// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 2020-2023 by Lactozilla. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file r_patchrotation.c /// \brief Patch rotation. #include "doomdef.h" #include "r_fps.h" #include "r_patch.h" #include "r_patchrotation.h" #include "z_zone.h" #include "w_wad.h" #include #ifdef ROTSPRITE // // R_GetRollAngle // // Angles precalculated in R_InitSprites. // fixed_t rollcosang[ROTANGLES] = {}; fixed_t rollsinang[ROTANGLES] = {}; INT32 R_GetRollAngle(angle_t rollangle) { INT32 ra = AngleFixed(rollangle)>>FRACBITS; #if (ROTANGDIFF > 1) ra += (ROTANGDIFF/2); #endif ra /= ROTANGDIFF; ra %= ROTANGLES; return ra; } angle_t R_RotationAngle(angle_t ang, angle_t camang, interpmobjstate_t *interp) { return FixedMul(FINECOSINE((ang) >> ANGLETOFINESHIFT), interp->roll) + FixedMul(FINESINE((ang) >> ANGLETOFINESHIFT), interp->pitch) + FixedMul(FINECOSINE((camang) >> ANGLETOFINESHIFT), interp->sloperoll) + FixedMul(FINESINE((camang) >> ANGLETOFINESHIFT), interp->slopepitch); } patch_t *Patch_GetRotatedSprite(spriteframe_t *sprite, size_t frame, size_t spriteangle, boolean flip, void *info, INT32 rotationangle) { rotsprite_t *rotsprite; spriteinfo_t *sprinfo = (spriteinfo_t *)info; INT32 idx = rotationangle; if (rotationangle < 1 || rotationangle >= ROTANGLES) return NULL; rotsprite = sprite->rotated[spriteangle]; if (rotsprite == NULL) { rotsprite = RotatedPatch_Create(ROTANGLES); sprite->rotated[spriteangle] = rotsprite; } if (flip) idx += rotsprite->angles; if (rotsprite->patches[idx] == NULL) { patch_t *patch; INT32 xpivot = 0, ypivot = 0; lumpnum_t lump = sprite->lumppat[spriteangle]; if (lump == LUMPERROR) return NULL; patch = (patch_t *)W_CachePatchNum(lump, PU_SPRITE); if (sprinfo->available) { xpivot = sprinfo->pivot[frame].x; ypivot = sprinfo->pivot[frame].y; } else { xpivot = patch->leftoffset; ypivot = patch->height / 2; } RotatedPatch_DoRotation(rotsprite, patch, rotationangle, xpivot, ypivot, flip); } return static_cast(rotsprite->patches[idx]); } rotsprite_t *RotatedPatch_Create(INT32 numangles) { rotsprite_t *rotsprite = static_cast(Z_Calloc(sizeof(rotsprite_t), PU_STATIC, NULL)); rotsprite->angles = numangles; rotsprite->patches = static_cast(Z_Calloc(rotsprite->angles * 2 * sizeof(void *), PU_STATIC, NULL)); return rotsprite; } static void RotatedPatch_CalculateDimensions( INT32 width, INT32 height, fixed_t ca, fixed_t sa, INT32 *newwidth, INT32 *newheight) { fixed_t fixedwidth = (width * FRACUNIT); fixed_t fixedheight = (height * FRACUNIT); fixed_t w1 = abs(FixedMul(fixedwidth, ca) - FixedMul(fixedheight, sa)); fixed_t w2 = abs(FixedMul(-fixedwidth, ca) - FixedMul(fixedheight, sa)); fixed_t h1 = abs(FixedMul(fixedwidth, sa) + FixedMul(fixedheight, ca)); fixed_t h2 = abs(FixedMul(-fixedwidth, sa) + FixedMul(fixedheight, ca)); w1 = FixedInt(FixedCeil(w1 + (FRACUNIT/2))); w2 = FixedInt(FixedCeil(w2 + (FRACUNIT/2))); h1 = FixedInt(FixedCeil(h1 + (FRACUNIT/2))); h2 = FixedInt(FixedCeil(h2 + (FRACUNIT/2))); *newwidth = std::max(width, std::max(w1, w2)); *newheight = std::max(height, std::max(h1, h2)); } void RotatedPatch_DoRotation(rotsprite_t *rotsprite, patch_t *patch, INT32 angle, INT32 xpivot, INT32 ypivot, boolean flip) { UINT32 i; patch_t *rotated; static std::vector rawdst(512 * 1024), rawconv(512 * 1024); // 1 MB should be enough for most cases UINT16 *rawout; size_t size; INT32 bflip = (flip != 0x00); INT32 width = patch->width; INT32 height = patch->height; INT32 leftoffset = patch->leftoffset; INT32 newwidth, newheight; fixed_t ca = rollcosang[angle]; fixed_t sa = rollsinang[angle]; fixed_t xcenter, ycenter; INT32 idx = angle; INT32 x, y; INT32 sx, sy; INT32 dx, dy; INT32 ox, oy; INT32 minx, miny, maxx, maxy; // Don't cache angle = 0 if (angle < 1 || angle >= ROTANGLES) return; if (flip) { idx += rotsprite->angles; xpivot = width - xpivot; leftoffset = width - leftoffset; } if (rotsprite->patches[idx]) return; // Find the dimensions of the rotated patch. RotatedPatch_CalculateDimensions(width, height, ca, sa, &newwidth, &newheight); xcenter = (xpivot * FRACUNIT); ycenter = (ypivot * FRACUNIT); if (xpivot != width / 2 || ypivot != height / 2) { newwidth *= 2; newheight *= 2; } minx = newwidth; miny = newheight; maxx = 0; maxy = 0; // Draw the rotated sprite to a temporary buffer. size = (newwidth * newheight); if (!size) size = (width * height); rawdst.resize(size); for (i = 0; i < size; i++) rawdst[i] = 0xFF00; for (dy = 0; dy < newheight; dy++) { for (dx = 0; dx < newwidth; dx++) { x = (dx - (newwidth / 2)) * FRACUNIT; y = (dy - (newheight / 2)) * FRACUNIT; sx = FixedMul(x, ca) + FixedMul(y, sa) + xcenter; sy = -FixedMul(x, sa) + FixedMul(y, ca) + ycenter; sx >>= FRACBITS; sy >>= FRACBITS; if (sx >= 0 && sy >= 0 && sx < width && sy < height) { rawdst[(dy * newwidth) + dx] = R_GetPatchPixel(patch, sx, sy, bflip); if (dx < minx) minx = dx; if (dy < miny) miny = dy; if (dx > maxx) maxx = dx; if (dy > maxy) maxy = dy; } } } ox = (newwidth / 2) + (leftoffset - xpivot); oy = (newheight / 2) + (patch->topoffset - ypivot); width = (maxx - minx); height = (maxy - miny); if ((unsigned)(width * height) > size) { UINT16 *src, *dest; size = (width * height); rawconv.resize(size); memset(rawconv.data(), 0, size * sizeof(UINT16)); src = &rawdst[(miny * newwidth) + minx]; dest = rawconv.data(); dy = height; while (dy--) { memcpy(dest, src, width * sizeof(UINT16)); dest += width; src += newwidth; } ox -= minx; oy -= miny; rawout = rawconv.data(); } else { width = newwidth; height = newheight; rawout = rawdst.data(); } // make patch rotated = (patch_t *)R_MaskedFlatToPatch(rawout, width, height, 0, 0, &size); Z_ChangeTag(rotated, PU_PATCH_ROTATED); Z_SetUser(rotated, (void **)(&rotsprite->patches[idx])); rotated->leftoffset = ox; rotated->topoffset = oy; } #endif