honestly doesent make that much sense in most cases to grow capacity * 2 (slightly misunderstood some stuff on implementation) and in other cases dont need to manually check for resize
269 lines
6.3 KiB
C++
269 lines
6.3 KiB
C++
// 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 <vector>
|
|
|
|
#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<patch_t*>(rotsprite->patches[idx]);
|
|
}
|
|
|
|
rotsprite_t *RotatedPatch_Create(INT32 numangles)
|
|
{
|
|
rotsprite_t *rotsprite = static_cast<rotsprite_t*>(Z_Calloc(sizeof(rotsprite_t), PU_STATIC, NULL));
|
|
rotsprite->angles = numangles;
|
|
rotsprite->patches = static_cast<void**>(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<UINT16> 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
|