Merge pull request '[FEAT] Affine sprite rendering' (#223) from softwarehell into next
Reviewed-on: https://codeberg.org/NepDisk/blankart/pulls/223
This commit is contained in:
commit
6e99c9b5cd
23 changed files with 1495 additions and 129 deletions
|
|
@ -948,6 +948,8 @@ struct int_const_s const INT_CONST[] = {
|
|||
{"FF_HORIZONTALFLIP",FF_HORIZONTALFLIP},
|
||||
{"FF_PAPERSPRITE",FF_PAPERSPRITE},
|
||||
{"FF_FLOORSPRITE",FF_FLOORSPRITE},
|
||||
{"FF_NOAFFINE",FF_NOAFFINE},
|
||||
{"FF_AFFINEPAPER",FF_AFFINEPAPER},
|
||||
{"FF_BLENDMASK",FF_BLENDMASK},
|
||||
{"FF_BLENDSHIFT",FF_BLENDSHIFT},
|
||||
{"FF_ADD",FF_ADD},
|
||||
|
|
@ -1032,6 +1034,7 @@ struct int_const_s const INT_CONST[] = {
|
|||
#else
|
||||
{"RF_NOMODEL",0},
|
||||
#endif
|
||||
{"RF_NOAFFINE",RF_NOAFFINE},
|
||||
{"RF_DONTDRAW",RF_DONTDRAW},
|
||||
{"RF_DONTDRAWP1",RF_DONTDRAWP1},
|
||||
{"RF_DONTDRAWP2",RF_DONTDRAWP2},
|
||||
|
|
@ -1044,6 +1047,7 @@ struct int_const_s const INT_CONST[] = {
|
|||
{"RF_REVERSESUBTRACT",RF_REVERSESUBTRACT},
|
||||
{"RF_MODULATE",RF_MODULATE},
|
||||
{"RF_OVERLAY",RF_OVERLAY},
|
||||
{"RF_AFFINEPRESCALE",RF_AFFINEPRESCALE},
|
||||
{"RF_TRANSMASK",RF_TRANSMASK},
|
||||
{"RF_TRANSSHIFT",RF_TRANSSHIFT},
|
||||
{"RF_TRANS10",RF_TRANS10},
|
||||
|
|
|
|||
|
|
@ -84,6 +84,28 @@ typedef struct gl_vissprite_s
|
|||
|
||||
angle_t angle; // for splats
|
||||
|
||||
// Affine nonsense
|
||||
struct {
|
||||
// Vertex points for the affine sprite to be drawn to.
|
||||
//
|
||||
// 4--3
|
||||
// | /|
|
||||
// |/ |
|
||||
// 1--2
|
||||
f_vector2_t p1, p2, p3, p4;
|
||||
|
||||
// The "root", or center, of the affine sprite
|
||||
polyvertex_t root;
|
||||
|
||||
// Bounding point (leftmost and highest point), to resolve clipping
|
||||
polyvertex_t bounding_point;
|
||||
|
||||
// Sine/cosine multiplier, used for billboarding.
|
||||
float patchsin, patchcos;
|
||||
|
||||
affine_t transform; // The actual affine transformation.
|
||||
} affine;
|
||||
|
||||
//Hurdler: 25/04/2000: now support colormap in hardware mode
|
||||
UINT8 *colormap;
|
||||
INT32 dispoffset; // copy of info->dispoffset, affects ordering but not drawing
|
||||
|
|
|
|||
|
|
@ -3343,6 +3343,8 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale)
|
|||
// This is expecting a pointer to an array containing 4 wallVerts for a sprite
|
||||
static void HWR_RotateSpritePolyToAim(gl_vissprite_t *spr, FOutVector *wallVerts, const boolean precip)
|
||||
{
|
||||
const boolean affine = ((spr->mobj && !P_MobjWasRemoved(spr->mobj)) && ((spr->mobj->player != NULL) || R_ThingIsAffineSprite(spr->mobj)));
|
||||
|
||||
if (cv_glspritebillboarding.value
|
||||
&& spr && spr->mobj && !R_ThingIsPaperSprite(spr->mobj)
|
||||
&& wallVerts)
|
||||
|
|
@ -3370,20 +3372,42 @@ static void HWR_RotateSpritePolyToAim(gl_vissprite_t *spr, FOutVector *wallVerts
|
|||
// X, Y, AND Z need to be manipulated for the polys to rotate around the
|
||||
// origin, because of how the origin setting works I believe that should
|
||||
// be mobj->z or mobj->z + mobj->height
|
||||
wallVerts[2].y = wallVerts[3].y = (spr->gzt - basey) * gl_viewludsin + basey;
|
||||
wallVerts[0].y = wallVerts[1].y = (lowy - basey) * gl_viewludsin + basey;
|
||||
// translate back to be around 0 before translating back
|
||||
wallVerts[3].x += ((spr->gzt - basey) * gl_viewludcos) * gl_viewcos;
|
||||
wallVerts[2].x += ((spr->gzt - basey) * gl_viewludcos) * gl_viewcos;
|
||||
|
||||
wallVerts[0].x += ((lowy - basey) * gl_viewludcos) * gl_viewcos;
|
||||
wallVerts[1].x += ((lowy - basey) * gl_viewludcos) * gl_viewcos;
|
||||
if (affine)
|
||||
{
|
||||
wallVerts[0].y = (spr->affine.p1.y * gl_viewludsin) + spr->affine.root.y;
|
||||
wallVerts[0].x += (spr->affine.p1.y * gl_viewludcos) * gl_viewcos;
|
||||
wallVerts[0].z += (spr->affine.p1.y * gl_viewludcos) * gl_viewsin;
|
||||
|
||||
wallVerts[3].z += ((spr->gzt - basey) * gl_viewludcos) * gl_viewsin;
|
||||
wallVerts[2].z += ((spr->gzt - basey) * gl_viewludcos) * gl_viewsin;
|
||||
wallVerts[1].y = (spr->affine.p2.y * gl_viewludsin) + spr->affine.root.y;
|
||||
wallVerts[1].x += (spr->affine.p2.y * gl_viewludcos) * gl_viewcos;
|
||||
wallVerts[1].z += (spr->affine.p2.y * gl_viewludcos) * gl_viewsin;
|
||||
|
||||
wallVerts[0].z += ((lowy - basey) * gl_viewludcos) * gl_viewsin;
|
||||
wallVerts[1].z += ((lowy - basey) * gl_viewludcos) * gl_viewsin;
|
||||
wallVerts[2].y = (spr->affine.p3.y * gl_viewludsin) + spr->affine.root.y;
|
||||
wallVerts[2].x += (spr->affine.p3.y * gl_viewludcos) * gl_viewcos;
|
||||
wallVerts[2].z += (spr->affine.p3.y * gl_viewludcos) * gl_viewsin;
|
||||
|
||||
wallVerts[3].y = (spr->affine.p4.y * gl_viewludsin) + spr->affine.root.y;
|
||||
wallVerts[3].x += (spr->affine.p4.y * gl_viewludcos) * gl_viewcos;
|
||||
wallVerts[3].z += (spr->affine.p4.y * gl_viewludcos) * gl_viewsin;
|
||||
}
|
||||
else
|
||||
{
|
||||
wallVerts[2].y = wallVerts[3].y = (spr->gzt - basey) * gl_viewludsin + basey;
|
||||
wallVerts[0].y = wallVerts[1].y = (lowy - basey) * gl_viewludsin + basey;
|
||||
// translate back to be around 0 before translating back
|
||||
wallVerts[3].x += ((spr->gzt - basey) * gl_viewludcos) * gl_viewcos;
|
||||
wallVerts[2].x += ((spr->gzt - basey) * gl_viewludcos) * gl_viewcos;
|
||||
|
||||
wallVerts[0].x += ((lowy - basey) * gl_viewludcos) * gl_viewcos;
|
||||
wallVerts[1].x += ((lowy - basey) * gl_viewludcos) * gl_viewcos;
|
||||
|
||||
wallVerts[3].z += ((spr->gzt - basey) * gl_viewludcos) * gl_viewsin;
|
||||
wallVerts[2].z += ((spr->gzt - basey) * gl_viewludcos) * gl_viewsin;
|
||||
|
||||
wallVerts[0].z += ((lowy - basey) * gl_viewludcos) * gl_viewsin;
|
||||
wallVerts[1].z += ((lowy - basey) * gl_viewludcos) * gl_viewsin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3468,7 +3492,14 @@ static void HWR_SplitSprite(gl_vissprite_t *spr)
|
|||
HWR_RotateSpritePolyToAim(spr, baseWallVerts, false);
|
||||
|
||||
// push it toward the camera to mitigate floor-clipping sprites
|
||||
float sprdist = sqrtf((spr->x1 - gl_viewx)*(spr->x1 - gl_viewx) + (spr->z1 - gl_viewy)*(spr->z1 - gl_viewy) + (spr->gzt - gl_viewz)*(spr->gzt - gl_viewz));
|
||||
float sprdist = 0;
|
||||
|
||||
float f_xdiff = (spr->x1 - gl_viewx);
|
||||
float f_ydiff = (spr->z1 - gl_viewy);
|
||||
float f_zdiff = (spr->gzt - gl_viewz);
|
||||
|
||||
sprdist = sqrtf((f_xdiff*f_xdiff) + (f_ydiff*f_ydiff) + (f_zdiff*f_zdiff));
|
||||
|
||||
float distfact = ((2.0f*spr->dispoffset) + 20.0f) / sprdist;
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
|
|
@ -3746,6 +3777,7 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
|
|||
patch_t *gpatch;
|
||||
GLPatch_t *hwrpatch;
|
||||
FSurfaceInfo Surf = {};
|
||||
const boolean affine = ((spr->mobj && !P_MobjWasRemoved(spr->mobj)) && ((spr->mobj->player != NULL) || R_ThingIsAffineSprite(spr->mobj)));
|
||||
const boolean splat = R_ThingIsFloorSprite(spr->mobj);
|
||||
|
||||
if (!spr->mobj || !spr->mobj->subsector)
|
||||
|
|
@ -3753,7 +3785,7 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
|
|||
|
||||
if (spr->mobj->subsector->sector->numlights
|
||||
&& (spr->mobj->renderflags & RF_ABSOLUTELIGHTLEVEL) == 0
|
||||
&& !splat)
|
||||
&& !splat && !affine)
|
||||
{
|
||||
HWR_SplitSprite(spr);
|
||||
return;
|
||||
|
|
@ -3886,6 +3918,29 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
|
|||
wallVerts[i].y = FIXED_TO_FLOAT(spr->gz) + zoffset;
|
||||
}
|
||||
}
|
||||
else if (affine)
|
||||
{
|
||||
// Affines are weird; we've generated the transformations for them,
|
||||
// so now we need to piece together each wall vertex.
|
||||
const float _cos = spr->affine.patchcos;
|
||||
const float _sin = spr->affine.patchsin;
|
||||
|
||||
wallVerts[0].x = spr->affine.root.x + (spr->affine.p1.x * _cos);
|
||||
wallVerts[0].z = spr->affine.root.z + (spr->affine.p1.x * _sin);
|
||||
wallVerts[0].y = spr->affine.root.y + spr->affine.p1.y;
|
||||
|
||||
wallVerts[1].x = spr->affine.root.x + (spr->affine.p2.x * _cos);
|
||||
wallVerts[1].z = spr->affine.root.z + (spr->affine.p2.x * _sin);
|
||||
wallVerts[1].y = spr->affine.root.y + spr->affine.p2.y;
|
||||
|
||||
wallVerts[2].x = spr->affine.root.x + (spr->affine.p3.x * _cos);
|
||||
wallVerts[2].z = spr->affine.root.z + (spr->affine.p3.x * _sin);
|
||||
wallVerts[2].y = spr->affine.root.y + spr->affine.p3.y;
|
||||
|
||||
wallVerts[3].x = spr->affine.root.x + (spr->affine.p4.x * _cos);
|
||||
wallVerts[3].z = spr->affine.root.z + (spr->affine.p4.x * _sin);
|
||||
wallVerts[3].y = spr->affine.root.y + spr->affine.p4.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// these were already scaled in HWR_ProjectSprite
|
||||
|
|
@ -3939,7 +3994,27 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
|
|||
HWR_RotateSpritePolyToAim(spr, wallVerts, false);
|
||||
|
||||
// push it toward the camera to mitigate floor-clipping sprites
|
||||
sprdist = sqrtf((spr->x1 - gl_viewx)*(spr->x1 - gl_viewx) + (spr->z1 - gl_viewy)*(spr->z1 -gl_viewy)+ (spr->gzt - gl_viewz)*(spr->gzt - gl_viewz));
|
||||
polyvertex_t pushpoint = {0};
|
||||
|
||||
if (affine)
|
||||
{
|
||||
pushpoint.x = spr->affine.bounding_point.x;
|
||||
pushpoint.y = spr->affine.bounding_point.y;
|
||||
pushpoint.z = spr->affine.bounding_point.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
pushpoint.x = spr->x1;
|
||||
pushpoint.y = spr->z1;
|
||||
pushpoint.z = spr->gzt;
|
||||
}
|
||||
|
||||
float f_xdiff = (pushpoint.x - gl_viewx);
|
||||
float f_ydiff = (pushpoint.y - gl_viewy);
|
||||
float f_zdiff = (pushpoint.z - gl_viewz);
|
||||
|
||||
sprdist = sqrtf((f_xdiff*f_xdiff) + (f_ydiff*f_ydiff) + (f_zdiff*f_zdiff));
|
||||
|
||||
distfact = ((2.0f* spr->dispoffset) + 20.0f) / sprdist;
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
|
|
@ -3976,9 +4051,21 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
|
|||
if (!R_ThingIsFullBright(spr->mobj) && !(spr->mobj->renderflags & RF_NOCOLORMAPS))
|
||||
colormap = sector->extra_colormap;
|
||||
|
||||
if (splat && sector->numlights)
|
||||
if ((splat||affine) && sector->numlights)
|
||||
{
|
||||
INT32 light = R_GetPlaneLight(sector, spr->mobj->z, false);
|
||||
// Affine splitting is going to require more advanced tricks
|
||||
// (GLSL fragment shader, or generating a "split" polygon on the fly).
|
||||
// I've already spent too much time on this damn thing, so I'm just doing
|
||||
// what precipitation (and, by extension, MD2) does.
|
||||
|
||||
fixed_t _zoffs = 0;
|
||||
|
||||
if (affine)
|
||||
{
|
||||
_zoffs = spr->mobj->height; // Always use the light at the top instead of whatever I was doing before
|
||||
}
|
||||
|
||||
INT32 light = R_GetPlaneLight(sector, spr->mobj->z + _zoffs, false);
|
||||
|
||||
if (!lightset)
|
||||
lightlevel = *sector->lightlist[light].lightlevel > 255 ? 255 : *sector->lightlist[light].lightlevel;
|
||||
|
|
@ -4823,6 +4910,16 @@ static void HWR_ProjectSprite(mobj_t *thing)
|
|||
vector2_t visoffs, rotoffset;
|
||||
#endif
|
||||
|
||||
// Affines
|
||||
boolean affinesprite = ((thing->player != NULL) || R_ThingIsAffineSprite(thing));
|
||||
affine_t affine_transform = {0};
|
||||
vector2_t affine_scale = {0};
|
||||
f_vector2_t affine_pivotoffsetdiff = {0};
|
||||
|
||||
polyvertex_t affine_rootpoint = {0};
|
||||
f_vector2_t affine_point[4] = {0};
|
||||
float leftbound = 0, backbound = 0, topbound = 0;
|
||||
|
||||
// uncapped/interpolation
|
||||
interpmobjstate_t interp = {0};
|
||||
mobj_t *interptarg;
|
||||
|
|
@ -4991,11 +5088,14 @@ static void HWR_ProjectSprite(mobj_t *thing)
|
|||
spr_offset = spritecachedinfo[lumpoff].offset;
|
||||
spr_topoffset = spritecachedinfo[lumpoff].topoffset;
|
||||
|
||||
float base_topoffs = FIXED_TO_FLOAT(spr_topoffset);
|
||||
|
||||
#ifdef ROTSPRITE
|
||||
spriterotangle = R_SpriteRotationAngle(thing, NULL, &interp);
|
||||
spriterotangle = R_SpriteRotationAngle(thing, NULL, &interp, false);
|
||||
|
||||
if (spriterotangle != 0
|
||||
&& !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE)))
|
||||
&& !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE))
|
||||
&& (!affinesprite)) // Affines are capable of rotation; this is redundant
|
||||
{
|
||||
rollangle = R_GetRollAngle(papersprite == vflip
|
||||
? spriterotangle : InvAngle(spriterotangle));
|
||||
|
|
@ -5027,6 +5127,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
|
|||
R_RotateSpriteOffsetsByPitchRoll(interptarg,
|
||||
vflip,
|
||||
hflip,
|
||||
false,
|
||||
&interp,
|
||||
&visoffs,
|
||||
&rotoffset);
|
||||
|
|
@ -5035,6 +5136,64 @@ static void HWR_ProjectSprite(mobj_t *thing)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (affinesprite)
|
||||
{
|
||||
vector2_t affine_pivot = {0};
|
||||
|
||||
vector2_t patch_defaultpivot = {.x = spr_offset, .y = (spr_height / 2)};
|
||||
|
||||
R_GetPivotVectorFromSpriteInfo(&affine_pivot,
|
||||
&patch_defaultpivot,
|
||||
sprinfo,
|
||||
(thing->frame & FF_FRAMEMASK));
|
||||
|
||||
affine_pivotoffsetdiff.x = FIXED_TO_FLOAT(affine_pivot.x - spr_offset);
|
||||
affine_pivotoffsetdiff.y = FIXED_TO_FLOAT(affine_pivot.y - spr_topoffset);
|
||||
|
||||
affine_scale.x = FLOAT_TO_FIXED(spritexscale * this_scale);
|
||||
affine_scale.y = FLOAT_TO_FIXED(spriteyscale * this_scale);
|
||||
|
||||
angle_t angle;
|
||||
|
||||
INT32 flipsign = ((flip && papersprite) ? -1 : 1); // Flip OGL affine papersprites for Software parity
|
||||
|
||||
angle = R_ConvToRollAngle(spriterotangle) * flipsign;
|
||||
|
||||
const fixed_t rolloffs_x = FixedDiv(interptarg->rollingxoffset * FRACUNIT, highresscale) * (((thing->renderflags & RF_FLIPOFFSETS) && flip) ? -1 : 1);
|
||||
const fixed_t rolloffs_y = FixedDiv(interptarg->rollingyoffset * FRACUNIT, highresscale);
|
||||
fixed_t y_piv = affine_pivot.y;
|
||||
|
||||
if (vflip)
|
||||
{
|
||||
// Flip the upper offset, and use *that* as the pivot
|
||||
y_piv = spr_height - y_piv;
|
||||
}
|
||||
|
||||
fixed_t sa = FSIN(angle), ca = FCOS(angle);
|
||||
|
||||
if (R_AffinePreScale(interptarg))
|
||||
{
|
||||
affine_transform.a = FixedDiv(ca, affine_scale.x);
|
||||
affine_transform.b = FixedDiv(-sa, affine_scale.x);
|
||||
affine_transform.c = FixedDiv(sa, affine_scale.y);
|
||||
affine_transform.d = FixedDiv(ca, affine_scale.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Simulate shitty SRB2 "scale after rotate" nonsense
|
||||
affine_transform.a = FixedDiv(ca, affine_scale.x);
|
||||
affine_transform.b = FixedDiv(-sa, affine_scale.y);
|
||||
affine_transform.c = FixedDiv(sa, affine_scale.x);
|
||||
affine_transform.d = FixedDiv(ca, affine_scale.y);
|
||||
}
|
||||
|
||||
affine_transform.ox = affine_pivot.x - rolloffs_x;
|
||||
affine_transform.oy = y_piv - rolloffs_y;
|
||||
|
||||
spritexscale = 1.0;
|
||||
spriteyscale = 1.0;
|
||||
}
|
||||
|
||||
if (thing->renderflags & RF_ABSOLUTEOFFSETS)
|
||||
{
|
||||
spr_offset = FixedDiv(interp.spritexoffset,highresscale);
|
||||
|
|
@ -5114,23 +5273,168 @@ static void HWR_ProjectSprite(mobj_t *thing)
|
|||
visoffs.x = (FixedDiv((visoffs.x * FRACUNIT), mapobjectscale));
|
||||
}
|
||||
#endif
|
||||
if (flip)
|
||||
if (affinesprite)
|
||||
{
|
||||
x1 = x2 = z1 = z2 = 0.0f;
|
||||
|
||||
float f_offs, f_width, f_height;
|
||||
|
||||
f_offs = FIXED_TO_FLOAT(spr_offset);
|
||||
f_width = FIXED_TO_FLOAT(spr_width);
|
||||
f_height = FIXED_TO_FLOAT(spr_height);
|
||||
|
||||
// From HWR_DrawAffinePatch
|
||||
|
||||
float fa = FIXED_TO_FLOAT(affine_transform.a);
|
||||
float fd = FIXED_TO_FLOAT(affine_transform.d);
|
||||
float fc = FIXED_TO_FLOAT(affine_transform.c);
|
||||
float fb = FIXED_TO_FLOAT(affine_transform.b);
|
||||
float fx = FIXED_TO_FLOAT(affine_transform.ox);
|
||||
float fy = FIXED_TO_FLOAT(affine_transform.oy);
|
||||
|
||||
// now, the matrix passed to this function maps screen coordinates to texel coordinates...
|
||||
// but to translate this from software to GL, we have to figure out where each corner
|
||||
// (or vertex) of the patch should end up on the screen.
|
||||
// which means we have to map texel coordinates to screen coordinates.
|
||||
// which means we have to invert the matrix.
|
||||
// how do you invert a matrix?
|
||||
// ...
|
||||
// i don't fucking know, i spent a day on this and got absolutely nowhere, but this guy knows:
|
||||
// https://nigeltao.github.io/blog/2021/inverting-3x2-affine-transformation-matrix.html
|
||||
float determinant = fa * fd - fb * fc;
|
||||
if (determinant > 0.0f)
|
||||
{
|
||||
float ba = fd / determinant;
|
||||
float bb = -fb / determinant;
|
||||
float bc = -fc / determinant;
|
||||
float bd = fa / determinant;
|
||||
|
||||
// set the polygon vertices to the right positions
|
||||
// 3--2
|
||||
// | /|
|
||||
// |/ |
|
||||
// 0--1
|
||||
affine_point[3].x = (ba * -fx + bb * -fy + fx);
|
||||
affine_point[3].y = (bc * -fx + bd * -fy + fy);
|
||||
|
||||
affine_point[2].x = ba * (f_width - fx) + bb * -fy + fx;
|
||||
affine_point[2].y = bc * (f_width - fx) + bd * -fy + fy;
|
||||
|
||||
affine_point[0].x = ba * -fx + bb * (f_height - fy) + fx;
|
||||
affine_point[0].y = bc * -fx + bd * (f_height - fy) + fy;
|
||||
|
||||
affine_point[1].x = ba * (f_width - fx) + bb * (f_height - fy) + fx;
|
||||
affine_point[1].y = bc * (f_width - fx) + bd * (f_height - fy) + fy;
|
||||
|
||||
// Focus each point on the center.
|
||||
affine_point[0].x = fx - affine_point[0].x;
|
||||
affine_point[1].x = fx - affine_point[1].x;
|
||||
affine_point[2].x = fx - affine_point[2].x;
|
||||
affine_point[3].x = fx - affine_point[3].x;
|
||||
|
||||
affine_point[1].y -= fy;
|
||||
affine_point[2].y -= fy;
|
||||
affine_point[0].y -= fy;
|
||||
affine_point[3].y -= fy;
|
||||
|
||||
// ...okay, now comb through all four vertices and set the output bounds based on this.
|
||||
// "Why not a loop?" According to SM64 programming wizard Kaze Emanuar,
|
||||
// loops take more processing time. If we can help it, it's better to just cut corners.
|
||||
float f_left = 0;
|
||||
float f_bottom = 0;
|
||||
float f_right = 0;
|
||||
float f_top = 0;
|
||||
float f_xlen = 0;
|
||||
float f_ylen = 0;
|
||||
#define BOUNDCHECK(i) \
|
||||
{ \
|
||||
f_left = min(affine_point[i].x, f_left); \
|
||||
f_bottom = max(affine_point[i].y, f_bottom); \
|
||||
f_top = min(affine_point[i].y, f_top); \
|
||||
f_right = max(affine_point[i].x, f_right); \
|
||||
f_xlen = (f_right - f_left); \
|
||||
f_ylen = (f_bottom - f_top); \
|
||||
}
|
||||
|
||||
BOUNDCHECK(0);
|
||||
BOUNDCHECK(1);
|
||||
BOUNDCHECK(2);
|
||||
BOUNDCHECK(3);
|
||||
|
||||
#undef BOUNDCHECK
|
||||
|
||||
backbound = (f_left * rightsin);
|
||||
leftbound = (f_left * rightcos);
|
||||
topbound = f_bottom * -1;
|
||||
|
||||
// Invert the Y values.
|
||||
affine_point[0].y *= -1.0f;
|
||||
affine_point[1].y *= -1.0f;
|
||||
affine_point[2].y *= -1.0f;
|
||||
affine_point[3].y *= -1.0f;
|
||||
|
||||
affine_rootpoint.x = tr_x;
|
||||
affine_rootpoint.z = tr_y;
|
||||
|
||||
// Rescale X and Y so we can multiply the pivot offset differences by them.
|
||||
const float f_affinexscale = f_xlen / (FIXED_TO_FLOAT(spr_width));
|
||||
const float f_affineyscale = f_ylen / (FIXED_TO_FLOAT(spr_height));
|
||||
|
||||
affine_pivotoffsetdiff.x *= f_affinexscale;
|
||||
affine_pivotoffsetdiff.y *= f_affineyscale;
|
||||
|
||||
affine_rootpoint.x -= (affine_pivotoffsetdiff.x * rightcos);
|
||||
affine_rootpoint.z -= (affine_pivotoffsetdiff.x * rightsin);
|
||||
|
||||
#ifdef ROTSPRITE
|
||||
spr_offset -= visoffs.x;
|
||||
spr_offset -= rotoffset.x;
|
||||
const float xrescale = this_xscale / FIXED_TO_FLOAT(mapobjectscale);
|
||||
float f_visx = (FIXED_TO_FLOAT(FixedMul(visoffs.x, mapobjectscale))) * xrescale;
|
||||
if (flip)
|
||||
{
|
||||
spr_offset -= visoffs.x;
|
||||
affine_rootpoint.x -= (f_visx * rightcos);
|
||||
affine_rootpoint.z -= (f_visx * rightsin);
|
||||
}
|
||||
else
|
||||
{
|
||||
spr_offset += visoffs.x;
|
||||
affine_rootpoint.x += (f_visx * rightcos);
|
||||
affine_rootpoint.z += (f_visx * rightsin);
|
||||
}
|
||||
#endif
|
||||
x1 = (FIXED_TO_FLOAT((spr_width - spr_offset)) * this_xscale);
|
||||
x2 = (FIXED_TO_FLOAT(spr_offset) * this_xscale);
|
||||
}
|
||||
else
|
||||
{
|
||||
goto nodeterminant;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeterminant:
|
||||
if (flip)
|
||||
{
|
||||
#ifdef ROTSPRITE
|
||||
spr_offset += visoffs.x;
|
||||
spr_offset += rotoffset.x;
|
||||
spr_offset -= visoffs.x;
|
||||
spr_offset -= rotoffset.x;
|
||||
#endif
|
||||
x1 = (FIXED_TO_FLOAT(spr_offset) * this_xscale);
|
||||
x2 = (FIXED_TO_FLOAT(spr_width - spr_offset) * this_xscale);
|
||||
x1 = (FIXED_TO_FLOAT((spr_width - spr_offset)) * this_xscale);
|
||||
x2 = (FIXED_TO_FLOAT(spr_offset) * this_xscale);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ROTSPRITE
|
||||
spr_offset += visoffs.x;
|
||||
spr_offset += rotoffset.x;
|
||||
#endif
|
||||
x1 = (FIXED_TO_FLOAT(spr_offset) * this_xscale);
|
||||
x2 = (FIXED_TO_FLOAT(spr_width - spr_offset) * this_xscale);
|
||||
}
|
||||
|
||||
z1 = tr_y + x1 * rightsin;
|
||||
z2 = tr_y - x2 * rightsin;
|
||||
x1 = tr_x + x1 * rightcos;
|
||||
x2 = tr_x - x2 * rightcos;
|
||||
}
|
||||
// test if too close
|
||||
/*
|
||||
|
|
@ -5144,23 +5448,46 @@ static void HWR_ProjectSprite(mobj_t *thing)
|
|||
}
|
||||
*/
|
||||
|
||||
z1 = tr_y + x1 * rightsin;
|
||||
z2 = tr_y - x2 * rightsin;
|
||||
x1 = tr_x + x1 * rightcos;
|
||||
x2 = tr_x - x2 * rightcos;
|
||||
|
||||
if (thing->terrain && thing->terrain->floorClip)
|
||||
spr_topoffset -= thing->terrain->floorClip;
|
||||
spr_topoffset -= thing->terrain->floorClip;
|
||||
|
||||
affine_rootpoint.y = 0;
|
||||
|
||||
const float f_topoffs = (FIXED_TO_FLOAT(spr_topoffset));
|
||||
|
||||
if (vflip)
|
||||
{
|
||||
gz = FIXED_TO_FLOAT(interp.z + thing->height) - (FIXED_TO_FLOAT(spr_topoffset) * this_yscale);
|
||||
gzt = gz + (FIXED_TO_FLOAT(spr_height) * this_yscale);
|
||||
affine_rootpoint.y = (FIXED_TO_FLOAT(interp.z + thing->height) + affine_pivotoffsetdiff.y) - (f_topoffs * this_yscale);
|
||||
}
|
||||
else
|
||||
{
|
||||
gzt = FIXED_TO_FLOAT(interp.z) + (FIXED_TO_FLOAT(spr_topoffset) * this_yscale);
|
||||
gz = gzt - (FIXED_TO_FLOAT(spr_height) * this_yscale);
|
||||
affine_rootpoint.y = (FIXED_TO_FLOAT(interp.z) - affine_pivotoffsetdiff.y) + (f_topoffs * this_yscale);
|
||||
}
|
||||
|
||||
if (!affinesprite)
|
||||
{
|
||||
if (vflip)
|
||||
{
|
||||
gz = affine_rootpoint.y;
|
||||
gzt = gz + (FIXED_TO_FLOAT(spr_height) * this_yscale);
|
||||
}
|
||||
else
|
||||
{
|
||||
gzt = affine_rootpoint.y;
|
||||
gz = gzt - (FIXED_TO_FLOAT(spr_height) * this_yscale);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vflip)
|
||||
{
|
||||
affine_rootpoint.y += (base_topoffs * this_yscale);
|
||||
}
|
||||
else
|
||||
{
|
||||
affine_rootpoint.y -= (base_topoffs * this_yscale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5226,10 +5553,50 @@ static void HWR_ProjectSprite(mobj_t *thing)
|
|||
|
||||
// store information in a vissprite
|
||||
vis = HWR_NewVisSprite();
|
||||
vis->x1 = x1;
|
||||
vis->x2 = x2;
|
||||
vis->z1 = z1;
|
||||
vis->z2 = z2;
|
||||
|
||||
if (affinesprite)
|
||||
{
|
||||
vis->affine.p1.x = affine_point[0].x;
|
||||
vis->affine.p1.y = affine_point[0].y;
|
||||
|
||||
vis->affine.p2.x = affine_point[1].x;
|
||||
vis->affine.p2.y = affine_point[1].y;
|
||||
|
||||
vis->affine.p3.x = affine_point[2].x;
|
||||
vis->affine.p3.y = affine_point[2].y;
|
||||
|
||||
vis->affine.p4.x = affine_point[3].x;
|
||||
vis->affine.p4.y = affine_point[3].y;
|
||||
|
||||
vis->affine.root.x = affine_rootpoint.x;
|
||||
vis->affine.root.y = affine_rootpoint.y;
|
||||
vis->affine.root.z = affine_rootpoint.z;
|
||||
|
||||
vis->affine.patchcos = rightcos;
|
||||
vis->affine.patchsin = rightsin;
|
||||
|
||||
vis->affine.transform.a = affine_transform.a;
|
||||
vis->affine.transform.c = affine_transform.b;
|
||||
vis->affine.transform.b = affine_transform.c;
|
||||
vis->affine.transform.d = affine_transform.d;
|
||||
vis->affine.transform.ox = affine_transform.ox;
|
||||
vis->affine.transform.oy = affine_transform.oy;
|
||||
|
||||
vis->affine.bounding_point.x = affine_rootpoint.x + leftbound;
|
||||
vis->affine.bounding_point.y = affine_rootpoint.z + backbound;
|
||||
vis->affine.bounding_point.z = affine_rootpoint.y + topbound;
|
||||
|
||||
// We're using this info to draw the affine sprite; zero-out "standard" draw info.
|
||||
vis->x1 = vis->x2 = vis->z1 = vis->z2 = 0;
|
||||
vis->gz = vis->gzt = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
vis->x1 = x1;
|
||||
vis->x2 = x2;
|
||||
vis->z1 = z1;
|
||||
vis->z2 = z2;
|
||||
}
|
||||
|
||||
vis->tz = tz; // Keep tz for the simple sprite sorting that happens
|
||||
vis->tracertz = tracertz;
|
||||
|
|
@ -5302,9 +5669,12 @@ static void HWR_ProjectSprite(mobj_t *thing)
|
|||
vis->colormap += COLORMAP_REMAPOFFSET;
|
||||
}
|
||||
|
||||
// set top/bottom coords
|
||||
vis->gzt = gzt;
|
||||
vis->gz = gz;
|
||||
if (!affinesprite)
|
||||
{
|
||||
// set top/bottom coords
|
||||
vis->gzt = gzt;
|
||||
vis->gz = gz;
|
||||
}
|
||||
|
||||
//CONS_Debug(DBG_RENDER, "------------------\nH: sprite : %d\nH: frame : %x\nH: type : %d\nH: sname : %s\n\n",
|
||||
// thing->sprite, thing->frame, thing->type, sprnames[thing->sprite]);
|
||||
|
|
|
|||
|
|
@ -1678,7 +1678,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
|
|||
fixedAngY = 0;
|
||||
|
||||
{
|
||||
fixed_t anglef = AngleFixed(R_SpriteRotationAngle(spr->mobj, NULL, &interp));
|
||||
fixed_t anglef = AngleFixed(R_SpriteRotationAngle(spr->mobj, NULL, &interp, false));
|
||||
|
||||
p.rollangle = 0.0f;
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,9 @@ enum mobj_e {
|
|||
mobj_flags2,
|
||||
mobj_eflags,
|
||||
mobj_renderflags,
|
||||
#if 0
|
||||
mobj_renderflags2,
|
||||
#endif
|
||||
mobj_skin,
|
||||
mobj_voice,
|
||||
mobj_color,
|
||||
|
|
@ -160,6 +163,9 @@ static const char *const mobj_opt[] = {
|
|||
"flags2",
|
||||
"eflags",
|
||||
"renderflags",
|
||||
#if 0
|
||||
"renderflags2",
|
||||
#endif
|
||||
"skin",
|
||||
"voice",
|
||||
"color",
|
||||
|
|
@ -429,6 +435,11 @@ static int mobj_get(lua_State *L)
|
|||
case mobj_renderflags:
|
||||
lua_pushinteger(L, mo->renderflags);
|
||||
break;
|
||||
#if 0
|
||||
case mobj_renderflags2:
|
||||
lua_pushinteger(L, mo->renderflags2);
|
||||
break;
|
||||
#endif
|
||||
case mobj_skin: // skin name or nil, not struct
|
||||
if (!mo->skin)
|
||||
return 0;
|
||||
|
|
@ -901,6 +912,11 @@ static int mobj_set(lua_State *L)
|
|||
case mobj_renderflags:
|
||||
mo->renderflags = (UINT32)luaL_checkinteger(L, 3);
|
||||
break;
|
||||
#if 0
|
||||
case mobj_renderflags2:
|
||||
mo->renderflags2 = (UINT32)luaL_checkinteger(L, 3);
|
||||
break;
|
||||
#endif
|
||||
case mobj_skin: // set skin by name
|
||||
{
|
||||
INT32 i;
|
||||
|
|
|
|||
|
|
@ -351,6 +351,13 @@ struct vector2_t
|
|||
fixed_t y;
|
||||
};
|
||||
|
||||
// Floating-point version, used sparingly, primarily in rendering
|
||||
struct f_vector2_t
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
vector2_t *FV2_Load(vector2_t *vec, fixed_t x, fixed_t y);
|
||||
vector2_t *FV2_UnLoad(vector2_t *vec, fixed_t *x, fixed_t *y);
|
||||
vector2_t *FV2_Copy(vector2_t *a_o, const vector2_t *a_i);
|
||||
|
|
|
|||
|
|
@ -306,6 +306,9 @@ struct mobj_t
|
|||
UINT16 anim_duration; // for FF_ANIMATE states
|
||||
|
||||
UINT32 renderflags; // render flags
|
||||
#if 0
|
||||
UINT32 renderflags2; // More render flags
|
||||
#endif
|
||||
fixed_t spritexscale, spriteyscale;
|
||||
fixed_t spritexoffset, spriteyoffset;
|
||||
fixed_t old_spritexscale, old_spriteyscale;
|
||||
|
|
@ -495,6 +498,9 @@ struct precipmobj_t
|
|||
UINT16 anim_duration; // for FF_ANIMATE states
|
||||
|
||||
UINT32 renderflags; // render flags
|
||||
#if 0
|
||||
UINT32 renderflags2; // More render flags
|
||||
#endif
|
||||
fixed_t spritexscale, spriteyscale;
|
||||
fixed_t spritexoffset, spriteyoffset;
|
||||
fixed_t old_spritexscale, old_spriteyscale;
|
||||
|
|
|
|||
|
|
@ -90,6 +90,12 @@ extern "C" {
|
|||
/// \brief Frame flags: Flip sprite horizontally
|
||||
#define FF_HORIZONTALFLIP 0x02000000
|
||||
|
||||
/// \brief Frame flags: Turns off Mode 7-style scaling and rotation
|
||||
#define FF_NOAFFINE 0x04000000
|
||||
|
||||
/// \brief Frame flags: Enables "affine papersprites"
|
||||
#define FF_AFFINEPAPER 0x08000000
|
||||
|
||||
/// \brief Frame flags - Animate: Simple stateless animation
|
||||
#define FF_ANIMATE 0x10000000
|
||||
/// \brief Frame flags - Animate: Sync animation to global timer (mutually exclusive with below, currently takes priority)
|
||||
|
|
|
|||
|
|
@ -2046,7 +2046,7 @@ static void DiffMobj(const mobj_t *mobj, UINT32 diff[])
|
|||
DIFF(mobj->mirrored, MD2_MIRRORED);
|
||||
DIFF(mobj->rollangle, MD2_ROLLANGLE);
|
||||
DIFF(mobj->shadowscale, MD2_SHADOWSCALE);
|
||||
DIFF(mobj->renderflags, MD2_RENDERFLAGS);
|
||||
DIFF(mobj->renderflags /*|| mobj->renderflags2*/, MD2_RENDERFLAGS);
|
||||
DIFF(mobj->tid != 0, MD2_TID);
|
||||
DIFF(mobj->spritexscale != FRACUNIT || mobj->spriteyscale != FRACUNIT, MD2_SPRITESCALE);
|
||||
DIFF(mobj->spritexoffset || mobj->spriteyoffset || mobj->rollingxoffset || mobj->rollingyoffset, MD2_SPRITEOFFSET);
|
||||
|
|
@ -2324,6 +2324,9 @@ static thinker_t *SyncMobjThinker(savebuffer_t *save, actionf_p1 thinker, thinke
|
|||
mobj->renderflags = READUINT32(save->p);
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
SYNCF(MD2_RENDERFLAGS, mobj->renderflags2);
|
||||
#endif
|
||||
SYNCF(MD2_TID, mobj->tid);
|
||||
SYNCDEF(MD2_SPRITESCALE, mobj->spritexscale, FRACUNIT);
|
||||
SYNCDEF(MD2_SPRITESCALE, mobj->spriteyscale, FRACUNIT);
|
||||
|
|
|
|||
13
src/p_user.c
13
src/p_user.c
|
|
@ -1261,12 +1261,15 @@ mobj_t *P_SpawnGhostMobjEX(mobj_t *mobj, boolean legacy)
|
|||
if (!(mobj->flags & MF_DONTENCOREMAP))
|
||||
ghost->flags &= ~MF_DONTENCOREMAP;
|
||||
|
||||
if (mobj->player && mobj->player->followmobj)
|
||||
if (mobj->player)
|
||||
{
|
||||
mobj_t *ghost2 = P_SpawnGhostMobj(mobj->player->followmobj);
|
||||
P_SetTarget(&ghost2->tracer, ghost);
|
||||
P_SetTarget(&ghost->tracer, ghost2);
|
||||
ghost2->flags2 |= (mobj->player->followmobj->flags2 & MF2_LINKDRAW);
|
||||
if(mobj->player->followmobj)
|
||||
{
|
||||
mobj_t *ghost2 = P_SpawnGhostMobj(mobj->player->followmobj);
|
||||
P_SetTarget(&ghost2->tracer, ghost);
|
||||
P_SetTarget(&ghost->tracer, ghost2);
|
||||
ghost2->flags2 |= (mobj->player->followmobj->flags2 & MF2_LINKDRAW);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy interpolation data :)
|
||||
|
|
|
|||
47
src/r_defs.h
47
src/r_defs.h
|
|
@ -1004,6 +1004,7 @@ typedef enum
|
|||
#ifdef HWRENDER
|
||||
RF_NOMODEL = 0x00040000, // do not draw a model for this mobj in opengl, use its sprite instead
|
||||
#endif
|
||||
RF_NOAFFINE = 0x00080000, // Disables affine drawing for this sprite
|
||||
|
||||
RF_HIDESHIFT = (20),
|
||||
RF_DONTDRAW = 0x0F << RF_HIDESHIFT, // --Don't generate a vissprite
|
||||
|
|
@ -1021,6 +1022,8 @@ typedef enum
|
|||
RF_MODULATE = ((AST_MODULATE-1)<<RF_BLENDSHIFT),
|
||||
RF_OVERLAY = ((AST_OVERLAY-1)<<RF_BLENDSHIFT),
|
||||
|
||||
RF_AFFINEPRESCALE = 0x08000000, // Makes affines scale before rotating, instead of rotating before scaling
|
||||
|
||||
RF_TRANSMASK = (INT32)0xF0000000, // --Transparency override
|
||||
RF_TRANSSHIFT = (7*4),
|
||||
RF_TRANS10 = (1<<RF_TRANSSHIFT), // 10%
|
||||
|
|
@ -1036,6 +1039,14 @@ typedef enum
|
|||
RF_GHOSTLYMASK = (RF_TRANSMASK | RF_FULLBRIGHT),
|
||||
} renderflags_t;
|
||||
|
||||
#if 0
|
||||
typedef enum
|
||||
{
|
||||
RF2_NOAFFINE = 0x00000001, // Disables affine drawing for this sprite
|
||||
RF2_AFFINEPRESCALE = 0x00000002, // Makes affines scale before rotating, instead of rotating before scaling
|
||||
} renderflags2_t;
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SRF_SINGLE = 0, // 0-angle for all rotations
|
||||
|
|
@ -1090,6 +1101,35 @@ struct spritedef_t
|
|||
spriteframe_t *spriteframes;
|
||||
};
|
||||
|
||||
// Affine transformation bounding positions, in pixels. Used for Software to determine
|
||||
// the dimensions of an affine patch.
|
||||
struct affine_bounding_t
|
||||
{
|
||||
vector2_t pivot;
|
||||
|
||||
// Differences between the "pivot" of the bound and each corner position.
|
||||
INT32 xleft, yup, xright, ydown;
|
||||
|
||||
// General length between each bounding position
|
||||
INT32 xlen, ylen;
|
||||
|
||||
INT32 l; // Leftmost bounding
|
||||
INT32 r; // Rightmost bounding
|
||||
|
||||
INT32 t; // Highest bounding
|
||||
INT32 b; // Lowest bounding
|
||||
};
|
||||
|
||||
// an affine transformation matrix. maps screen coordinates to texture coordinates
|
||||
struct affine_t
|
||||
{
|
||||
fixed_t a; // horizontal step per pixel (M7A)
|
||||
fixed_t b; // horizontal step per scanline (M7B)
|
||||
fixed_t c; // vertical step per pixel (M7C)
|
||||
fixed_t d; // vertical step per scanline (M7D)
|
||||
fixed_t ox, oy; // transform origin in texture coordinates (M7X/M7Y)
|
||||
};
|
||||
|
||||
// Column and span drawing data bundles
|
||||
|
||||
typedef struct
|
||||
|
|
@ -1123,6 +1163,13 @@ typedef struct
|
|||
INT32 sourcelength;
|
||||
|
||||
UINT8 r8_flatcolor;
|
||||
|
||||
affine_bounding_t affinebound;
|
||||
affine_t affine;
|
||||
vector2_t affineoffset;
|
||||
fixed_t affineystep;
|
||||
f_vector2_t affinemosaic; // Truncates how columndrawers "move" across the screen
|
||||
fixed_t frac;
|
||||
} drawcolumndata_t;
|
||||
|
||||
extern drawcolumndata_t g_dc;
|
||||
|
|
|
|||
13
src/r_draw.h
13
src/r_draw.h
|
|
@ -74,6 +74,10 @@ enum
|
|||
COLDRAWFUNC_TWOSMULTIPATCHTRANS,
|
||||
COLDRAWFUNC_FOG,
|
||||
COLDRAWFUNC_DROPSHADOW,
|
||||
COLDRAWFUNC_AFFINE,
|
||||
COLDRAWFUNC_AFFINETRANS,
|
||||
COLDRAWFUNC_AFFINEFUZZY,
|
||||
COLDRAWFUNC_AFFINETRANSTRANS,
|
||||
|
||||
COLDRAWFUNC_MAX
|
||||
};
|
||||
|
|
@ -201,6 +205,15 @@ void R_Draw2sMultiPatchTranslucentColumn_Flush(drawcolumndata_t* dc);
|
|||
|
||||
void R_DrawFogColumn(drawcolumndata_t* dc);
|
||||
void R_DrawColumnShadowed(drawcolumndata_t* dc);
|
||||
void R_DrawAffineColumn(drawcolumndata_t* dc);
|
||||
void R_DrawTranslatedAffineColumn(drawcolumndata_t* dc);
|
||||
void R_DrawTranslucentAffineColumn(drawcolumndata_t* dc);
|
||||
void R_DrawTranslatedTranslucentAffineColumn(drawcolumndata_t* dc);
|
||||
|
||||
void R_DrawAffineColumn_Brightmap(drawcolumndata_t* dc);
|
||||
void R_DrawTranslatedAffineColumn_Brightmap(drawcolumndata_t* dc);
|
||||
void R_DrawTranslucentAffineColumn_Brightmap(drawcolumndata_t* dc);
|
||||
void R_DrawTranslatedTranslucentAffineColumn_Brightmap(drawcolumndata_t* dc);
|
||||
|
||||
void R_DrawColumn_Brightmap(drawcolumndata_t* dc);
|
||||
void R_DrawTranslucentColumn_Brightmap(drawcolumndata_t* dc);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
// a has a constant z depth from top to bottom.
|
||||
//
|
||||
|
||||
#include "r_main.h"
|
||||
#include "r_draw.h"
|
||||
#include <tracy/tracy/Tracy.hpp>
|
||||
|
||||
|
|
@ -100,6 +101,72 @@ FUNCINLINE static ATTRINLINE constexpr UINT8 R_DrawColumnPixel(drawcolumndata_t*
|
|||
return R_GetColumnTranslucent<Type>(dc, dest, bit, col);
|
||||
}
|
||||
|
||||
// Function flow: Holes -> Translation -> Brightmap -> Colormap -> Translucency
|
||||
// "Why are these in nested functions for standard columns?" Uhhhhhh iunno lol
|
||||
// I make-a da code-a
|
||||
|
||||
template<DrawColumnType Type>
|
||||
FUNCINLINE static ATTRINLINE constexpr UINT16 R_DrawColumnAffinePixel(drawcolumndata_t* dc, UINT8 *dest, INT32 bit)
|
||||
{
|
||||
const UINT16 pixel = reinterpret_cast<UINT16 *>(dc->source)[bit];
|
||||
|
||||
UINT8 col = (UINT8)(pixel & 0xff);
|
||||
|
||||
if (pixel < 0xff00)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if constexpr (Type & DrawColumnType::DC_HOLES)
|
||||
{
|
||||
if (col == TRANSPARENTPIXEL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (Type & DrawColumnType::DC_COLORMAP)
|
||||
{
|
||||
// Remap to the current translation
|
||||
col = dc->translation[col];
|
||||
}
|
||||
|
||||
boolean was_brightmapped = false;
|
||||
|
||||
if constexpr (Type & DrawColumnType::DC_BRIGHTMAP)
|
||||
{
|
||||
// For affine drawers, dc->brightmap points to a *flat*, and not a *column*.
|
||||
// Keep that in mind before you do something that causes a bunch of segfaults!
|
||||
|
||||
if (dc->brightmap)
|
||||
{
|
||||
const UINT16 bmpixel = reinterpret_cast<UINT16 *>(dc->brightmap)[bit];
|
||||
|
||||
if ((bmpixel & 0xff) == BRIGHTPIXEL)
|
||||
{
|
||||
// Pixel is part of the brightmap
|
||||
col = dc->fullbright[col];
|
||||
was_brightmapped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!was_brightmapped)
|
||||
{
|
||||
col = dc->colormap[col];
|
||||
}
|
||||
|
||||
if constexpr (Type & DrawColumnType::DC_TRANSMAP)
|
||||
{
|
||||
// Pixel is translucent
|
||||
col = *(dc->transmap + (col << 8) + (*dest));
|
||||
}
|
||||
|
||||
*dest = col;
|
||||
|
||||
return (0xff00 | col);
|
||||
}
|
||||
|
||||
/** \brief The R_DrawColumn function
|
||||
Experiment to make software go faster. Taken from the Boom source
|
||||
*/
|
||||
|
|
@ -483,3 +550,165 @@ void R_DrawColumn_Flat(drawcolumndata_t *dc)
|
|||
}
|
||||
while (--count);
|
||||
}
|
||||
|
||||
template<DrawColumnType Type>
|
||||
static void R_DrawAffineColumnTemplate(drawcolumndata_t *dc)
|
||||
{
|
||||
INT32 count;
|
||||
const INT32 vidheight = vid.height;
|
||||
|
||||
// leban 1/17/99:
|
||||
// removed the + 1 here, adjusted the if test, and added an increment
|
||||
// later. this helps a compiler pipeline a bit better. the x86
|
||||
// assembler also does this.
|
||||
count = dc->yh - dc->yl;
|
||||
|
||||
// leban 1/17/99:
|
||||
// this case isn't executed too often. depending on how many instructions
|
||||
// there are between here and the second if test below, this case could
|
||||
// be moved down and might save instructions overall. since there are
|
||||
// probably different wads that favor one way or the other, i'll leave
|
||||
// this alone for now.
|
||||
if (count < 0) // Zero length, column does not exceed a pixel.
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((unsigned)dc->x >= (unsigned)vid.width || dc->yl < 0 || dc->yh >= vidheight)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// Inner loop that does the actual texture mapping,
|
||||
// e.g. a DDA-lile scaling.
|
||||
// This is as fast as it gets. (Yeah, right!!! -- killough)
|
||||
//
|
||||
// killough 2/1/98: more performance tuning
|
||||
|
||||
intptr_t frac;
|
||||
// Looks familiar.
|
||||
const intptr_t fracstep = dc->iscale;
|
||||
const intptr_t heightmask = dc->sourcelength-1; // CPhipps - specify type
|
||||
constexpr INT32 npow2min = -1;
|
||||
const INT32 npow2max = dc->sourcelength;
|
||||
|
||||
// Framebuffer destination address.
|
||||
// SoM: MAGIC
|
||||
UINT8 * restrict dest;
|
||||
|
||||
if constexpr (Type & DrawColumnType::DC_DIRECT)
|
||||
dest = R_Address(dc->x, dc->yl);
|
||||
else if constexpr ((Type & (DrawColumnType::DC_COLORMAP | DrawColumnType::DC_TRANSMAP))
|
||||
== (DrawColumnType::DC_COLORMAP | DrawColumnType::DC_TRANSMAP))
|
||||
dest = R_GetBufferColormapTrans(dc);
|
||||
else if constexpr (Type & DrawColumnType::DC_TRANSMAP)
|
||||
dest = R_GetBufferTrans(dc);
|
||||
else if constexpr (Type & DrawColumnType::DC_COLORMAP)
|
||||
dest = R_GetBufferColormap(dc);
|
||||
else
|
||||
dest = R_GetBufferOpaque(dc);
|
||||
|
||||
const INT32 stride = vid.width; // SoM: Oh, Oh it's MAGIC! You know...
|
||||
|
||||
count++;
|
||||
|
||||
// Determine scaling, which is the only mapping to be done.
|
||||
frac = (dc->texturemid + FixedMul((dc->yl << FRACBITS) - centeryfrac, fracstep));
|
||||
|
||||
const affine_t *transform = &dc->affine;
|
||||
const affine_bounding_t *bounds = &dc->affinebound;
|
||||
|
||||
const fixed_t a = transform->a;
|
||||
const fixed_t b = transform->b;
|
||||
const fixed_t c = transform->c;
|
||||
const fixed_t d = transform->d;
|
||||
fixed_t cx = transform->ox;
|
||||
fixed_t cy = transform->oy;
|
||||
|
||||
const INT32 pw = dc->sourcelength, ph = dc->texheight;
|
||||
|
||||
const boolean vflip = (dc->affineystep < 0);
|
||||
const fixed_t ystep_delta = abs(dc->affineystep);
|
||||
|
||||
fixed_t ydiff = (bounds->yup * FRACUNIT) - cy;
|
||||
fixed_t xdiff = (bounds->xleft * FRACUNIT) - cx;
|
||||
|
||||
xdiff -= (xdiff ? FRACUNIT : 0);
|
||||
ydiff -= (ydiff ? FRACUNIT : 0);
|
||||
|
||||
// Offset our X and Y positions by the bounding differences.
|
||||
fixed_t cxx = cx + xdiff + dc->affineoffset.x;
|
||||
fixed_t cyy = cy + ydiff + dc->affineoffset.y;
|
||||
|
||||
// If we're smaller than usual, mosaic isn't necessary.
|
||||
|
||||
const float y_mosaic = std::max(1.0f, dc->affinemosaic.y);
|
||||
const float x_mosaic = std::max(1.0f, dc->affinemosaic.x);
|
||||
|
||||
const boolean mosaic_on = cv_affinemosaic.value;
|
||||
const boolean xmosaic_on = (FLOAT_TO_FIXED(x_mosaic) > FRACUNIT);
|
||||
const boolean ymosaic_on = (FLOAT_TO_FIXED(y_mosaic) > FRACUNIT);
|
||||
|
||||
fixed_t usefrac = dc->frac;
|
||||
|
||||
if (mosaic_on && xmosaic_on)
|
||||
{
|
||||
usefrac = FLOAT_TO_FIXED(static_cast<INT32>(FIXED_TO_FLOAT(dc->frac) / x_mosaic) * x_mosaic);
|
||||
}
|
||||
|
||||
float ypx = 0.0f;
|
||||
|
||||
// yoinked from NovaSquirrel's mode 7 0preview
|
||||
// ...which is in turn yoinked from Mesen's S-PPU code
|
||||
// i can't do matrix math to save my life :face_holding_back_tears:
|
||||
// (m7xofs and m7yofs are already factored in by destbase)
|
||||
fixed_t ux = FixedMul(b, -cyy) + FixedMul(a, -cxx) + FixedMul(a, usefrac) + cx;
|
||||
fixed_t uy = FixedMul(d, -cyy) + FixedMul(c, -cxx) + FixedMul(c, usefrac) + cy;
|
||||
float ux_base = FIXED_TO_FLOAT(ux);
|
||||
float uy_base = FIXED_TO_FLOAT(uy);
|
||||
float f_b = FIXED_TO_FLOAT(FixedMul(b, ystep_delta));
|
||||
float f_d = FIXED_TO_FLOAT(FixedMul(d, ystep_delta));
|
||||
|
||||
for (; count > 0; dest += stride, --count)
|
||||
{
|
||||
ypx += 1.0f;
|
||||
|
||||
const INT32 srcx = ux >> FRACBITS;
|
||||
const INT32 srcy = (vflip) ? (ph - (uy >> FRACBITS)) : (uy >> FRACBITS);
|
||||
INT32 y_real = static_cast<INT32>(ypx);
|
||||
|
||||
if (mosaic_on && ymosaic_on)
|
||||
{
|
||||
y_real = (static_cast<INT32>(ypx / y_mosaic) * y_mosaic);
|
||||
}
|
||||
|
||||
ux = FLOAT_TO_FIXED(ux_base + (f_b * y_real));
|
||||
uy = FLOAT_TO_FIXED(uy_base + (f_d * y_real));
|
||||
|
||||
if (srcx < 0 || srcx >= pw || srcy < 0 || srcy >= ph)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
R_DrawColumnAffinePixel<Type>(dc, dest, srcy * pw + srcx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define DEFINE_AFFINE_COLUMN_FUNC(name, flags) \
|
||||
void name(drawcolumndata_t *dc) \
|
||||
{ \
|
||||
ZoneScoped; \
|
||||
constexpr DrawColumnType opt = static_cast<DrawColumnType>(flags); \
|
||||
R_DrawAffineColumnTemplate<opt>(dc); \
|
||||
}
|
||||
|
||||
#define DEFINE_AFFINE_COLUMN_SETUP(name, flags) \
|
||||
DEFINE_AFFINE_COLUMN_FUNC(name, flags|DC_DIRECT) \
|
||||
DEFINE_AFFINE_COLUMN_FUNC(name ## _Brightmap, flags|DC_DIRECT|DC_BRIGHTMAP)
|
||||
|
||||
DEFINE_AFFINE_COLUMN_SETUP(R_DrawAffineColumn, DC_BASIC);
|
||||
DEFINE_AFFINE_COLUMN_SETUP(R_DrawTranslatedAffineColumn, DC_COLORMAP);
|
||||
DEFINE_AFFINE_COLUMN_SETUP(R_DrawTranslucentAffineColumn, DC_TRANSMAP);
|
||||
DEFINE_AFFINE_COLUMN_SETUP(R_DrawTranslatedTranslucentAffineColumn, DC_COLORMAP|DC_TRANSMAP);
|
||||
|
|
|
|||
|
|
@ -197,6 +197,14 @@ consvar_t cv_sliptidetilt = CVAR_INIT ("sliptidetilt", "On", CV_SAVE, CV_OnOff,
|
|||
consvar_t cv_nulldriftefx = CVAR_INIT ("nulldriftefx", "On", CV_SAVE, CV_OnOff, NULL);
|
||||
consvar_t cv_nulldrifttilt = CVAR_INIT ("nulldrifttilt", "On", CV_SAVE, CV_OnOff, NULL);
|
||||
|
||||
consvar_t cv_fakerollangle = CVAR_INIT ("fakerollangle", "Off", CV_SAVE, CV_OnOff, NULL);
|
||||
|
||||
consvar_t cv_affineprescale = CVAR_INIT ("affineprescale", "Off", CV_SAVE, CV_OnOff, NULL);
|
||||
consvar_t cv_affinemosaic = CVAR_INIT ("affinemosaic", "Off", CV_SAVE, CV_OnOff, NULL);
|
||||
|
||||
static CV_PossibleValue_t affineangle_cons_t[] = {{0, "MIN"}, {360 * FRACUNIT, "MAX"}, {0, NULL}};
|
||||
static CV_PossibleValue_t affinetest_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Auto"}, {0, NULL}};
|
||||
|
||||
void SplitScreen_OnChange(void)
|
||||
{
|
||||
UINT8 i;
|
||||
|
|
@ -1804,6 +1812,9 @@ void R_RegisterEngineStuff(void)
|
|||
CV_RegisterVar(&cv_sliptidetilt);
|
||||
CV_RegisterVar(&cv_nulldriftefx);
|
||||
CV_RegisterVar(&cv_nulldrifttilt);
|
||||
CV_RegisterVar(&cv_fakerollangle);
|
||||
CV_RegisterVar(&cv_affineprescale);
|
||||
CV_RegisterVar(&cv_affinemosaic);
|
||||
|
||||
CV_RegisterVar(&cv_showhud);
|
||||
CV_RegisterVar(&cv_translucenthud);
|
||||
|
|
|
|||
|
|
@ -161,6 +161,9 @@ extern consvar_t cv_sloperoll;
|
|||
extern consvar_t cv_sliptidetilt;
|
||||
extern consvar_t cv_nulldriftefx, cv_nulldrifttilt;
|
||||
|
||||
extern consvar_t cv_fakerollangle;
|
||||
extern consvar_t cv_affineprescale, cv_affinemosaic;
|
||||
|
||||
// debugging
|
||||
|
||||
typedef enum {
|
||||
|
|
|
|||
|
|
@ -48,15 +48,17 @@ patch_t *Patch_GetRotatedSprite(
|
|||
INT32 R_GetRollAngle(angle_t rollangle);
|
||||
boolean R_IsOverlayingSMonitorPlayer(mobj_t* mobj);
|
||||
angle_t R_GetPitchRollAngle(mobj_t *mobj, player_t *viewPlayer, interpmobjstate_t *interp);
|
||||
angle_t R_ModelRotationAngle(mobj_t *mobj, player_t *viewPlayer);
|
||||
angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer, interpmobjstate_t *interp);
|
||||
angle_t R_ModelRotationAngle(mobj_t *mobj, player_t *viewPlayer, boolean fliptilt);
|
||||
angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer, interpmobjstate_t *interp, boolean fliptilt);
|
||||
vector2_t* R_RotateSpriteOffsetsByPitchRoll(
|
||||
mobj_t* mobj,
|
||||
boolean vflip,
|
||||
boolean hflip,
|
||||
boolean affine,
|
||||
interpmobjstate_t *interp,
|
||||
vector2_t* out,
|
||||
vector2_t* rolloffs);
|
||||
angle_t R_ConvToRollAngle(angle_t ang);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ angle_t R_GetPitchRollAngle(mobj_t *mobj, player_t *viewPlayer, interpmobjstate_
|
|||
return rollOrPitch;
|
||||
}
|
||||
|
||||
static angle_t R_PlayerSpriteRotation(player_t *player, player_t *viewPlayer)
|
||||
static angle_t R_PlayerSpriteRotation(player_t *player, player_t *viewPlayer, boolean fliptilt)
|
||||
{
|
||||
angle_t viewingAngle = R_PointToAnglePlayer(viewPlayer, player->mo->x, player->mo->y);
|
||||
angle_t angleDelta = (viewingAngle - player->mo->angle);
|
||||
|
|
@ -70,6 +70,12 @@ static angle_t R_PlayerSpriteRotation(player_t *player, player_t *viewPlayer)
|
|||
// Sliptide tilt: Nulldrifts take priority if and ONLY if they're the larger value compared to sliptides.
|
||||
angle_t sliptideLift = max(abs(aiztilt), abs(nulltilt)) * liftsign;
|
||||
|
||||
if (fliptilt)
|
||||
{
|
||||
// Vertical flip affine hack: invert the angle so it still looks right.
|
||||
sliptideLift *= -1;
|
||||
}
|
||||
|
||||
angle_t rollAngle = 0;
|
||||
|
||||
if (sliptideLift)
|
||||
|
|
@ -97,23 +103,23 @@ boolean R_IsOverlayingSMonitorPlayer(mobj_t* mobj)
|
|||
(mobj->target->player->smonitortimer));
|
||||
}
|
||||
|
||||
angle_t R_ModelRotationAngle(mobj_t *mobj, player_t *viewPlayer)
|
||||
angle_t R_ModelRotationAngle(mobj_t *mobj, player_t *viewPlayer, boolean fliptilt)
|
||||
{
|
||||
angle_t rollAngle = mobj->rollangle;
|
||||
|
||||
if (mobj->player)
|
||||
{
|
||||
rollAngle += R_PlayerSpriteRotation(mobj->player, viewPlayer);
|
||||
rollAngle += R_PlayerSpriteRotation(mobj->player, viewPlayer, fliptilt);
|
||||
}
|
||||
else if (R_IsOverlayingSMonitorPlayer(mobj))
|
||||
{
|
||||
rollAngle += R_PlayerSpriteRotation(mobj->target->player, viewPlayer);
|
||||
rollAngle += R_PlayerSpriteRotation(mobj->target->player, viewPlayer, fliptilt);
|
||||
}
|
||||
|
||||
return rollAngle;
|
||||
}
|
||||
|
||||
angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer, interpmobjstate_t *interp)
|
||||
angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer, interpmobjstate_t *interp, boolean fliptilt)
|
||||
{
|
||||
angle_t rollOrPitch;
|
||||
|
||||
|
|
@ -125,7 +131,7 @@ angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer, interpmobjstat
|
|||
{
|
||||
rollOrPitch = R_GetPitchRollAngle(mobj, viewPlayer, interp);
|
||||
}
|
||||
return (rollOrPitch + R_ModelRotationAngle(mobj, viewPlayer));
|
||||
return (rollOrPitch + R_ModelRotationAngle(mobj, viewPlayer, fliptilt));
|
||||
}
|
||||
|
||||
INT32 R_GetRollAngle(angle_t rollangle)
|
||||
|
|
@ -141,10 +147,17 @@ INT32 R_GetRollAngle(angle_t rollangle)
|
|||
|
||||
#define VISROTMUL (ANG1 * ROTANGDIFF)
|
||||
|
||||
// Simulates "rollangle" angling
|
||||
angle_t R_ConvToRollAngle(angle_t ang)
|
||||
{
|
||||
return (cv_fakerollangle.value) ? (R_GetRollAngle(ang) * VISROTMUL) : ang;
|
||||
}
|
||||
|
||||
vector2_t* R_RotateSpriteOffsetsByPitchRoll(
|
||||
mobj_t* mobj,
|
||||
boolean vflip,
|
||||
boolean hflip,
|
||||
boolean affine,
|
||||
interpmobjstate_t *interp,
|
||||
vector2_t* out,
|
||||
vector2_t* rolloffs)
|
||||
|
|
@ -166,7 +179,13 @@ vector2_t* R_RotateSpriteOffsetsByPitchRoll(
|
|||
angle_t viewingAngle = R_PointToAngle(mobj->x, mobj->y);
|
||||
|
||||
// rotate ourselves entirely by the sprite's own rotation angle
|
||||
angle_t visrot = R_SpriteRotationAngle(mobj, NULL, interp);
|
||||
angle_t visrot = R_SpriteRotationAngle(mobj, NULL, interp, false);
|
||||
|
||||
if (vflip)
|
||||
{
|
||||
// Invert the angle for vertically flipped sprites.
|
||||
visrot = InvAngle(visrot);
|
||||
}
|
||||
|
||||
// xoffs = (-cos(xoff) + sin(yoff))
|
||||
xoffs =
|
||||
|
|
|
|||
539
src/r_things.cpp
539
src/r_things.cpp
|
|
@ -39,6 +39,7 @@
|
|||
#include "d_netfil.h" // blargh. for nameonly().
|
||||
#include "m_cheat.h" // objectplace
|
||||
#include "p_local.h" // stplyr
|
||||
#include "v_video.h" // V_GetAffineBounds
|
||||
#include "core/thread_pool.h"
|
||||
#ifdef HWRENDER
|
||||
#include "hardware/hw_md2.h"
|
||||
|
|
@ -946,6 +947,25 @@ UINT32 R_GetThingTransTable(fixed_t alpha, UINT32 transmap)
|
|||
return (20*(FRACUNIT - ((alpha * (10 - transmap))/10) - 1) + FRACUNIT) >> (FRACBITS+1);
|
||||
}
|
||||
|
||||
static void R_CopyAffineBounds(affine_bounding_t *src, affine_bounding_t *dest)
|
||||
{
|
||||
// Bounding points
|
||||
dest->l = src->l;
|
||||
dest->r = src->r;
|
||||
dest->t = src->t;
|
||||
dest->b = src->b;
|
||||
|
||||
// Differences from pivot point
|
||||
dest->xleft = src->xleft;
|
||||
dest->xright = src->xright;
|
||||
dest->yup = src->yup;
|
||||
dest->ydown = src->ydown;
|
||||
|
||||
// Length values
|
||||
dest->xlen = src->xlen;
|
||||
dest->ylen = src->ylen;
|
||||
}
|
||||
|
||||
//
|
||||
// R_DrawVisSprite
|
||||
// mfloorclip and mceilingclip should also be set.
|
||||
|
|
@ -966,6 +986,8 @@ static void R_DrawVisSprite(vissprite_t *vis)
|
|||
drawcolumndata_t dc {0};
|
||||
const INT32 vidwidth = vid.width;
|
||||
|
||||
fixed_t maxpatchwidth = (patch->width << FRACBITS);
|
||||
|
||||
if (!patch)
|
||||
return;
|
||||
|
||||
|
|
@ -981,10 +1003,16 @@ static void R_DrawVisSprite(vissprite_t *vis)
|
|||
if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) return; // ditto
|
||||
}
|
||||
|
||||
if (vis->cut & SC_AFFINE)
|
||||
{
|
||||
// Resize the max horizontal bounds so we can draw the whole affine patch.
|
||||
maxpatchwidth = std::max(maxpatchwidth, vis->affine.bounds.xlen * FRACUNIT);
|
||||
}
|
||||
|
||||
// TODO This check should not be necessary. But Papersprites near to the camera will sometimes create invalid values
|
||||
// for the vissprite's startfrac. This happens because they are not depth culled like other sprites.
|
||||
// Someone who is more familiar with papersprites pls check and try to fix <3
|
||||
if (vis->startfrac < 0 || vis->startfrac > (patch->width << FRACBITS))
|
||||
if (vis->startfrac < 0 || vis->startfrac > maxpatchwidth)
|
||||
{
|
||||
// never draw vissprites with startfrac out of patch range
|
||||
return;
|
||||
|
|
@ -1010,10 +1038,37 @@ static void R_DrawVisSprite(vissprite_t *vis)
|
|||
dc.fullbright = colormaps;
|
||||
dc.translation = R_GetSpriteTranslation(vis);
|
||||
|
||||
if (vis->cut & SC_AFFINE)
|
||||
{
|
||||
// Specialized affine drawing functions, primarily for player sprites.
|
||||
boolean affinebrightmap = (bmpatch != NULL);
|
||||
|
||||
if (dc.translation) // translate green skin to another color
|
||||
{
|
||||
if (vis->transmap)
|
||||
{
|
||||
R_SetColumnFunc(COLDRAWFUNC_AFFINETRANSTRANS, affinebrightmap);
|
||||
dc.transmap = vis->transmap;
|
||||
}
|
||||
else
|
||||
{
|
||||
R_SetColumnFunc(COLDRAWFUNC_AFFINETRANS, affinebrightmap);
|
||||
}
|
||||
}
|
||||
else if (vis->transmap)
|
||||
{
|
||||
R_SetColumnFunc(COLDRAWFUNC_AFFINEFUZZY, affinebrightmap);
|
||||
dc.transmap = vis->transmap;
|
||||
}
|
||||
else
|
||||
{
|
||||
R_SetColumnFunc(COLDRAWFUNC_AFFINE, affinebrightmap);
|
||||
}
|
||||
}
|
||||
// Hack: Use a special column function for drop shadows that bypasses
|
||||
// invalid memory access crashes caused by R_ProjectDropShadow putting wrong values
|
||||
// in dc_texturemid and dc_iscale when the shadow is sloped.
|
||||
if (vis->cut & SC_SHADOW)
|
||||
else if (vis->cut & SC_SHADOW)
|
||||
{
|
||||
R_SetColumnFunc(COLDRAWFUNC_DROPSHADOW, false);
|
||||
dc.transmap = vis->transmap;
|
||||
|
|
@ -1112,8 +1167,175 @@ static void R_DrawVisSprite(vissprite_t *vis)
|
|||
localcolfunc = (vis->cut & SC_VFLIP) ? R_DrawFlippedMaskedColumn : R_DrawMaskedColumn;
|
||||
lengthcol = patch->height;
|
||||
|
||||
if (vis->cut & SC_AFFINE)
|
||||
{
|
||||
Patch_GenerateFlat(patch, static_cast<pictureflags_t>(0));
|
||||
dc.source = static_cast<UINT8 *>(patch->flats[0]);
|
||||
dc.sourcelength = patch->width;
|
||||
|
||||
if (bmpatch)
|
||||
{
|
||||
Patch_GenerateFlat(bmpatch, static_cast<pictureflags_t>(0));
|
||||
|
||||
// The column is a flat because fuck you
|
||||
dc.brightmap = static_cast<UINT8 *>(bmpatch->flats[0]);
|
||||
}
|
||||
|
||||
dc.affine.a = vis->affine.transform.a;
|
||||
dc.affine.b = vis->affine.transform.b;
|
||||
dc.affine.c = vis->affine.transform.c;
|
||||
dc.affine.d = vis->affine.transform.d;
|
||||
dc.affine.ox = vis->affine.transform.ox;
|
||||
dc.affine.oy = vis->affine.transform.oy;
|
||||
|
||||
dc.affineoffset.x = vis->affine.offset.x;
|
||||
dc.affineoffset.y = vis->affine.offset.y;
|
||||
|
||||
dc.affinemosaic.x = vis->affine.mosaic.x;
|
||||
dc.affinemosaic.y = vis->affine.mosaic.y;
|
||||
|
||||
R_CopyAffineBounds(&vis->affine.bounds, &dc.affinebound);
|
||||
|
||||
fixed_t fixed_ylen = (FRACUNIT*dc.affinebound.ylen);
|
||||
|
||||
if (vis->floorclip)
|
||||
{
|
||||
sprbotscreen = sprtopscreen + fixed_ylen;
|
||||
}
|
||||
|
||||
fixed_t xstep = intsign(vis->xiscale) * FRACUNIT;
|
||||
|
||||
dc.affineystep = (vis->cut & SC_VFLIP) ? -FRACUNIT : FRACUNIT;
|
||||
|
||||
const fixed_t beforeloopoffset = dc.affineoffset.y;
|
||||
|
||||
if (vis->scalestep)
|
||||
{
|
||||
// Convert these to floats, and feed them into the affine drawer
|
||||
float f_yscale_const = FIXED_TO_FLOAT(spryscale);
|
||||
float f_yscale = FIXED_TO_FLOAT(spryscale);
|
||||
float f_scalestep = FIXED_TO_FLOAT(FixedMul(vis->scalestep, vis->spriteyscale));
|
||||
float f_pixel = (vis->cut & SC_VFLIP) ? -1.0f : 1.0f;
|
||||
|
||||
// Papersprite drawing loop
|
||||
for (dc.x = vis->x1; dc.x <= vis->x2; dc.x++, f_yscale += f_scalestep)
|
||||
{
|
||||
float _ysc = (f_yscale / f_yscale_const);
|
||||
|
||||
if (_ysc < 0.001f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
dc.affineystep = FLOAT_TO_FIXED(f_pixel / _ysc);
|
||||
|
||||
angle_t angle = ((vis->centerangle + xtoviewangle[viewssnum][dc.x]) >> ANGLETOFINESHIFT) & 0xFFF;
|
||||
dc.frac = FixedMul((vis->paperoffset - FixedMul(FINETANGENT(angle), vis->paperdistance)),
|
||||
vis->affine.distscale.x);
|
||||
|
||||
sprtopscreen = (centeryfrac - FixedMul(dc.texturemid, FLOAT_TO_FIXED(f_yscale)));
|
||||
float f_ylen = static_cast<float>(dc.affinebound.ylen);
|
||||
|
||||
dc.yl = (sprtopscreen+FRACUNIT-1)>>FRACBITS;
|
||||
dc.yh = ((sprtopscreen + FLOAT_TO_FIXED(f_ylen * _ysc))-1)>>FRACBITS;
|
||||
|
||||
INT32 topscreen = sprtopscreen;
|
||||
INT32 bottomscreen = sprbotscreen == INT32_MAX ? topscreen + FLOAT_TO_FIXED(f_ylen * _ysc)
|
||||
: sprbotscreen + FLOAT_TO_FIXED(f_ylen * _ysc);
|
||||
|
||||
dc.yl = (topscreen+FRACUNIT-1)>>FRACBITS;
|
||||
dc.yh = (bottomscreen-1)>>FRACBITS;
|
||||
|
||||
if (windowtop != INT32_MAX && windowbottom != INT32_MAX)
|
||||
{
|
||||
if (windowtop > topscreen)
|
||||
dc.yl = (windowtop + FRACUNIT - 1)>>FRACBITS;
|
||||
if (windowbottom < bottomscreen)
|
||||
dc.yh = (windowbottom - 1)>>FRACBITS;
|
||||
}
|
||||
|
||||
if (dc.yh >= mfloorclip[dc.x])
|
||||
dc.yh = mfloorclip[dc.x]-1;
|
||||
if (dc.yl <= mceilingclip[dc.x])
|
||||
{
|
||||
// Prevent nasty shearing by getting the difference between yl and the ceiling clip.
|
||||
const INT32 clipdiff = (mceilingclip[dc.x]+1) - dc.yl;
|
||||
dc.yl += clipdiff;
|
||||
dc.affineoffset.y = beforeloopoffset - (clipdiff * dc.affineystep);
|
||||
}
|
||||
else
|
||||
{
|
||||
dc.affineoffset.y = beforeloopoffset;
|
||||
}
|
||||
|
||||
if (dc.yl < 0)
|
||||
dc.yl = 0;
|
||||
if (dc.yh >= vid.height) // dc_yl must be < vid.height, so reduces number of checks in tight loop
|
||||
dc.yh = vid.height - 1;
|
||||
|
||||
if (dc.yh >= baseclip && baseclip != -1)
|
||||
dc.yh = baseclip;
|
||||
|
||||
coldrawfunc_t* colfunccopy = colfunc;
|
||||
colfunccopy(&dc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-paper drawing loop
|
||||
for (dc.x = vis->x1; dc.x <= vis->x2; dc.x++, frac += xstep, sprtopscreen += vis->shear.tan)
|
||||
{
|
||||
INT32 topscreen = sprtopscreen + spryscale*0;
|
||||
INT32 bottomscreen = sprbotscreen == INT32_MAX ? topscreen + fixed_ylen
|
||||
: sprbotscreen + fixed_ylen;
|
||||
|
||||
dc.yl = (topscreen+FRACUNIT-1)>>FRACBITS;
|
||||
dc.yh = (bottomscreen-1)>>FRACBITS;
|
||||
|
||||
if (windowtop != INT32_MAX && windowbottom != INT32_MAX)
|
||||
{
|
||||
if (windowtop > topscreen)
|
||||
dc.yl = (windowtop + FRACUNIT - 1)>>FRACBITS;
|
||||
if (windowbottom < bottomscreen)
|
||||
dc.yh = (windowbottom - 1)>>FRACBITS;
|
||||
}
|
||||
|
||||
if (dc.yh >= mfloorclip[dc.x])
|
||||
dc.yh = mfloorclip[dc.x]-1;
|
||||
if (dc.yl <= mceilingclip[dc.x])
|
||||
{
|
||||
// Prevent nasty shearing by getting the difference between yl and the ceiling clip.
|
||||
const INT32 clipdiff = (mceilingclip[dc.x]+1) - dc.yl;
|
||||
dc.yl += clipdiff;
|
||||
dc.affineoffset.y = beforeloopoffset - (clipdiff * FRACUNIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
dc.affineoffset.y = beforeloopoffset;
|
||||
}
|
||||
|
||||
if (dc.yl < 0)
|
||||
dc.yl = 0;
|
||||
if (dc.yh >= vid.height) // dc_yl must be < vid.height, so reduces number of checks in tight loop
|
||||
dc.yh = vid.height - 1;
|
||||
|
||||
if (dc.yh >= baseclip && baseclip != -1)
|
||||
dc.yh = baseclip;
|
||||
|
||||
if (dc.yl <= dc.yh && dc.yh > 0 && fixed_ylen != 0)
|
||||
{
|
||||
dc.frac = frac;
|
||||
|
||||
coldrawfunc_t* colfunccopy = colfunc;
|
||||
colfunccopy(&dc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// Split drawing loops for paper and non-paper to reduce conditional checks per sprite
|
||||
if (vis->scalestep)
|
||||
else if (vis->scalestep)
|
||||
{
|
||||
fixed_t horzscale = FixedMul(vis->spritexscale, this_scale);
|
||||
fixed_t scalestep = FixedMul(vis->scalestep, vis->spriteyscale);
|
||||
|
|
@ -1162,40 +1384,38 @@ static void R_DrawVisSprite(vissprite_t *vis)
|
|||
}
|
||||
else
|
||||
{
|
||||
|
||||
#if 0
|
||||
#if 0
|
||||
if (vis->x1test && vis->x2test)
|
||||
{
|
||||
INT32 x1test = vis->x1test;
|
||||
INT32 x2test = vis->x2test;
|
||||
INT32 x1test = vis->x1test;
|
||||
INT32 x2test = vis->x2test;
|
||||
|
||||
if (x1test < 0)
|
||||
x1test = 0;
|
||||
if (x1test < 0)
|
||||
x1test = 0;
|
||||
|
||||
if (x2test >= vidwidth)
|
||||
x2test = vidwidth-1;
|
||||
if (x2test >= vidwidth)
|
||||
x2test = vidwidth-1;
|
||||
|
||||
const INT32 t = (vis->startfrac + (vis->xiscale * (x2test - x1test))) >> FRACBITS;
|
||||
const INT32 t = (vis->startfrac + (vis->xiscale * (x2test - x1test))) >> FRACBITS;
|
||||
|
||||
if (x1test <= x2test && (t < 0 || t >= patch->width))
|
||||
if (x1test <= x2test && (t < 0 || t >= patch->width))
|
||||
{
|
||||
CONS_Printf("THE GAME WOULD HAVE CRASHED, %d (old) vs %d (new)\n", (x2test - x1test), (vis->x2 - vis->x1));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Non-paper drawing loop
|
||||
for (dc.x = vis->x1; dc.x <= vis->x2; dc.x++, frac += vis->xiscale, sprtopscreen += vis->shear.tan)
|
||||
{
|
||||
CONS_Printf("THE GAME WOULD HAVE CRASHED, %d (old) vs %d (new)\n", (x2test - x1test), (vis->x2 - vis->x1));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
texturecolumn = std::clamp<fixed_t>(frac >> FRACBITS, 0, patch->width - 1);
|
||||
|
||||
// Non-paper drawing loop
|
||||
for (dc.x = vis->x1; dc.x <= vis->x2; dc.x++, frac += vis->xiscale, sprtopscreen += vis->shear.tan)
|
||||
{
|
||||
texturecolumn = std::clamp<fixed_t>(frac >> FRACBITS, 0, patch->width - 1);
|
||||
column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
|
||||
|
||||
column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
|
||||
if (bmpatch)
|
||||
bmcol = (column_t *)((UINT8 *)bmpatch->columns + (bmpatch->columnofs[texturecolumn]));
|
||||
|
||||
if (bmpatch)
|
||||
bmcol = (column_t *)((UINT8 *)bmpatch->columns + (bmpatch->columnofs[texturecolumn]));
|
||||
|
||||
localcolfunc (&dc, column, bmcol, baseclip);
|
||||
}
|
||||
localcolfunc (&dc, column, bmcol, baseclip);
|
||||
}
|
||||
}
|
||||
|
||||
R_SetColumnFunc(BASEDRAWFUNC, false);
|
||||
|
|
@ -1603,8 +1823,6 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
|
|||
shadow->gzt = (isflipped ? shadow->pzt : shadow->pz) + patch->height * shadowyscale / 2;
|
||||
shadow->gz = shadow->gzt - patch->height * shadowyscale;
|
||||
shadow->texturemid = FixedMul(interp.scale, FixedDiv(shadow->gzt - viewz, shadowyscale));
|
||||
if (thing->skin && ((skin_t *)thing->skin)->highresscale != FRACUNIT)
|
||||
shadow->texturemid = FixedMul(shadow->texturemid, ((skin_t *)thing->skin)->highresscale);
|
||||
shadow->scalestep = 0;
|
||||
shadow->shear.tan = shadowskew; // repurposed variable
|
||||
|
||||
|
|
@ -1755,6 +1973,28 @@ fixed_t R_GetSpriteDirectionalLighting(angle_t angle)
|
|||
return extralight;
|
||||
}
|
||||
|
||||
void R_GetPivotVectorFromSpriteInfo(vector2_t* out,
|
||||
vector2_t* defaultpiv,
|
||||
spriteinfo_t* sprinfo,
|
||||
size_t frame)
|
||||
{
|
||||
if (in_bit_array(sprinfo->available, frame))
|
||||
{
|
||||
out->x = (sprinfo->pivot[frame].x * FRACUNIT);
|
||||
out->y = (sprinfo->pivot[frame].y * FRACUNIT);
|
||||
}
|
||||
else if (in_bit_array(sprinfo->available, SPRINFO_DEFAULT_PIVOT))
|
||||
{
|
||||
out->x = (sprinfo->pivot[SPRINFO_DEFAULT_PIVOT].x * FRACUNIT);
|
||||
out->y = (sprinfo->pivot[SPRINFO_DEFAULT_PIVOT].y * FRACUNIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
out->x = defaultpiv->x;
|
||||
out->y = defaultpiv->y;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// R_ProjectSprite
|
||||
// Generates a vissprite for a thing
|
||||
|
|
@ -1838,6 +2078,15 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
interpmobjstate_t interp = {0};
|
||||
mobj_t *interptarg = thing;
|
||||
|
||||
// Affines
|
||||
boolean affinesprite = ((thing->player != NULL) || R_ThingIsAffineSprite(thing));
|
||||
affine_t affine_transform = {0};
|
||||
affine_bounding_t affine_bounds = {0};
|
||||
vector2_t affine_scale = {0};
|
||||
vector2_t affine_distscale = {0};
|
||||
f_vector2_t affine_pivotoffsetdiff = {0};
|
||||
f_vector2_t affine_mosaic = {.x = 1.0f, .y = 1.0f};
|
||||
|
||||
if (R_IsOverlayingSMonitorPlayer(thing))
|
||||
{
|
||||
// Kill overlay misalignment
|
||||
|
|
@ -1848,8 +2097,6 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
R_InterpolateMobjState(interptarg, R_GetTimeFrac(RTF_LEVEL), &interp);
|
||||
|
||||
this_scale = interp.scale;
|
||||
if (thing->skin && ((skin_t *)thing->skin)->highresscale != FRACUNIT)
|
||||
this_scale = FixedMul(this_scale, ((skin_t *)thing->skin)->highresscale);
|
||||
|
||||
// sprite offset
|
||||
interp.x += thing->sprxoff;
|
||||
|
|
@ -1876,8 +2123,20 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
return;
|
||||
|
||||
// aspect ratio stuff
|
||||
xscale = FixedDiv(projection[viewssnum], tz);
|
||||
sortscale = FixedDiv(projectiony[viewssnum], tz);
|
||||
if (affinesprite)
|
||||
{
|
||||
affine_distscale.x = affine_scale.x = FixedDiv(projection[viewssnum], tz);
|
||||
affine_distscale.y = affine_scale.y = FixedDiv(projectiony[viewssnum], tz);
|
||||
|
||||
xscale = affine_scale.x;
|
||||
sortscale = affine_scale.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
xscale = FixedDiv(projection[viewssnum], tz);
|
||||
sortscale = FixedDiv(projectiony[viewssnum], tz);
|
||||
}
|
||||
|
||||
|
||||
// decide which patch to use for sprite relative to player
|
||||
#ifdef RANGECHECK
|
||||
|
|
@ -2000,10 +2259,11 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
patch = static_cast<patch_t*>(W_CachePatchNum(sprframe->lumppat[rot], PU_SPRITE));
|
||||
|
||||
#ifdef ROTSPRITE
|
||||
spriterotangle = R_SpriteRotationAngle(thing, NULL, &interp);
|
||||
spriterotangle = R_SpriteRotationAngle(thing, NULL, &interp, (affinesprite && vflip));
|
||||
|
||||
if (spriterotangle
|
||||
&& !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE)))
|
||||
&& !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE))
|
||||
&& (!affinesprite)) // Affines are capable of rotation; this is redundant
|
||||
{
|
||||
if ((papersprite && ang >= ANGLE_180) != vflip)
|
||||
{
|
||||
|
|
@ -2030,6 +2290,12 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
flip = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (affinesprite && ((papersprite && ang >= ANGLE_180) != vflip))
|
||||
{
|
||||
// Parity with the standard rollangle system
|
||||
//spriterotangle = InvAngle(spriterotangle);
|
||||
}
|
||||
#endif
|
||||
|
||||
flip = !flip != !hflip;
|
||||
|
|
@ -2041,8 +2307,7 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
if (thing->skin && ((skin_t *)thing->skin)->highresscale != FRACUNIT)
|
||||
{
|
||||
fixed_t high_res = ((skin_t *)thing->skin)->highresscale;
|
||||
spritexscale = FixedMul(spritexscale, high_res);
|
||||
spriteyscale = FixedMul(spriteyscale, high_res);
|
||||
this_scale = FixedMul(this_scale, high_res);
|
||||
highresscale = high_res;
|
||||
}
|
||||
else
|
||||
|
|
@ -2060,6 +2325,8 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
rotoffset.x = 0;
|
||||
rotoffset.y = 0;
|
||||
|
||||
const fixed_t visoffs_xsc = (affinesprite) ? xscale : FixedDiv(FRACUNIT, mapobjectscale);
|
||||
|
||||
const fixed_t visoffymul = (vflip ? -FRACUNIT : FRACUNIT);
|
||||
|
||||
if (R_ThingIsUsingBakedOffsets(interptarg))
|
||||
|
|
@ -2067,6 +2334,7 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
R_RotateSpriteOffsetsByPitchRoll(interptarg,
|
||||
vflip,
|
||||
hflip,
|
||||
affinesprite,
|
||||
&interp,
|
||||
&visoffs,
|
||||
&rotoffset);
|
||||
|
|
@ -2075,11 +2343,87 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (affinesprite)
|
||||
{
|
||||
vector2_t affine_pivot = {0};
|
||||
|
||||
vector2_t patch_defaultpivot = {.x = spr_offset, .y = (spr_height / 2)};
|
||||
|
||||
R_GetPivotVectorFromSpriteInfo(&affine_pivot,
|
||||
&patch_defaultpivot,
|
||||
sprinfo,
|
||||
(thing->frame & FF_FRAMEMASK));
|
||||
|
||||
affine_pivotoffsetdiff.x = FIXED_TO_FLOAT(affine_pivot.x - spr_offset);
|
||||
affine_pivotoffsetdiff.y = FIXED_TO_FLOAT(affine_pivot.y - spr_topoffset);
|
||||
|
||||
affine_scale.x = FixedMul(affine_scale.x, FixedMul(spritexscale, this_scale));
|
||||
affine_scale.y = FixedMul(affine_scale.y, FixedMul(spriteyscale, this_scale));
|
||||
|
||||
affine_mosaic.x = FIXED_TO_FLOAT(FixedMul(affine_distscale.x, this_scale));
|
||||
affine_mosaic.y = FIXED_TO_FLOAT(FixedMul(affine_distscale.y, this_scale));
|
||||
|
||||
angle_t angle;
|
||||
|
||||
INT32 flipsign = ((flip) ? -1 : 1);
|
||||
|
||||
angle = R_ConvToRollAngle(spriterotangle) * flipsign;
|
||||
|
||||
const boolean renderflip = ((thing->renderflags & RF_FLIPOFFSETS) == RF_FLIPOFFSETS);
|
||||
const fixed_t rolloffs_x = FixedDiv(interptarg->rollingxoffset * FRACUNIT, highresscale) * (((!renderflip) && flip) ? -1 : 1);
|
||||
const fixed_t rolloffs_y = FixedDiv(interptarg->rollingyoffset * FRACUNIT, highresscale) * (((!renderflip) && vflip) ? -1 : 1);
|
||||
fixed_t y_piv = affine_pivot.y;
|
||||
|
||||
if (vflip)
|
||||
{
|
||||
// Flip the upper offset, and use *that* as the pivot
|
||||
y_piv = (patch->height * FRACUNIT) - y_piv;
|
||||
}
|
||||
|
||||
fixed_t sa = FSIN(angle), ca = FCOS(angle);
|
||||
|
||||
if (R_AffinePreScale(interptarg))
|
||||
{
|
||||
affine_transform.a = FixedDiv(ca, affine_scale.x);
|
||||
affine_transform.b = FixedDiv(-sa, affine_scale.x);
|
||||
affine_transform.c = FixedDiv(sa, affine_scale.y);
|
||||
affine_transform.d = FixedDiv(ca, affine_scale.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Simulate shitty SRB2 "scale after rotate" nonsense
|
||||
affine_transform.a = FixedDiv(ca, affine_scale.x);
|
||||
affine_transform.b = FixedDiv(-sa, affine_scale.y);
|
||||
affine_transform.c = FixedDiv(sa, affine_scale.x);
|
||||
affine_transform.d = FixedDiv(ca, affine_scale.y);
|
||||
}
|
||||
|
||||
affine_transform.ox = affine_pivot.x - rolloffs_x;
|
||||
affine_transform.oy = y_piv - rolloffs_y;
|
||||
|
||||
V_GetAffineBounds(&affine_transform, patch, FRACUNIT, &affine_bounds, true);
|
||||
|
||||
// Rescale X and Y so we can multiply the pivot offset differences by them.
|
||||
const float f_affinexscale = static_cast<float>(affine_bounds.xlen) / static_cast<float>(patch->width);
|
||||
const float f_affineyscale = static_cast<float>(affine_bounds.ylen) / static_cast<float>(patch->height);
|
||||
|
||||
affine_pivotoffsetdiff.x *= f_affinexscale;
|
||||
affine_pivotoffsetdiff.y *= f_affineyscale;
|
||||
|
||||
spr_width = (affine_bounds.xlen * FRACUNIT);
|
||||
spr_offset = (affine_bounds.xleft * FRACUNIT) - FLOAT_TO_FIXED(affine_pivotoffsetdiff.x);
|
||||
|
||||
spritexscale = FRACUNIT;
|
||||
spriteyscale = FRACUNIT;
|
||||
}
|
||||
|
||||
fixed_t thingyoffset = (FixedDiv(interp.spriteyoffset, highresscale) + FixedDiv((visoffs.y * visoffymul), mapobjectscale) + ((affinesprite) ? 0 : (rotoffset.y * visoffymul)));
|
||||
|
||||
if (thing->renderflags & RF_ABSOLUTEOFFSETS)
|
||||
{
|
||||
spr_offset = FixedDiv(interp.spritexoffset, highresscale);
|
||||
#ifdef ROTSPRITE
|
||||
spr_topoffset = (FixedDiv(interp.spriteyoffset, highresscale) + FixedDiv((visoffs.y * visoffymul), mapobjectscale) + (rotoffset.y * visoffymul));
|
||||
spr_topoffset = thingyoffset;
|
||||
#else
|
||||
spr_topoffset = FixedDiv(interp.spriteyoffset, highresscale);
|
||||
#endif
|
||||
|
|
@ -2093,7 +2437,12 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
|
||||
spr_offset += FixedDiv(interp.spritexoffset, highresscale) * flipoffset;
|
||||
#ifdef ROTSPRITE
|
||||
spr_topoffset += (FixedDiv(interp.spriteyoffset, highresscale) + FixedDiv((visoffs.y * visoffymul), mapobjectscale) + (rotoffset.y * visoffymul)) * flipoffset;
|
||||
thingyoffset *= flipoffset;
|
||||
|
||||
if (vflip && affinesprite)
|
||||
thingyoffset *= -1;
|
||||
|
||||
spr_topoffset += thingyoffset;
|
||||
#else
|
||||
spr_topoffset += FixedDiv(interp.spriteyoffset, highresscale) * flipoffset;
|
||||
#endif
|
||||
|
|
@ -2107,10 +2456,10 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
#ifdef ROTSPRITE
|
||||
if (visoffs.x)
|
||||
{
|
||||
offset -= FixedDiv((visoffs.x * FRACUNIT), mapobjectscale);
|
||||
offset -= (visoffs.x * visoffs_xsc);
|
||||
}
|
||||
|
||||
if (rotoffset.x)
|
||||
if ((rotoffset.x) && (!affinesprite))
|
||||
{
|
||||
offset -= rotoffset.x;
|
||||
}
|
||||
|
|
@ -2124,6 +2473,12 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
fixed_t xscale2, yscale2, cosmul, sinmul, tx2, tz2;
|
||||
INT32 range;
|
||||
|
||||
if (affinesprite)
|
||||
{
|
||||
offset = FixedDiv(offset, xscale);
|
||||
offset2 = FixedDiv(offset2, xscale);
|
||||
}
|
||||
|
||||
if (ang >= ANGLE_180)
|
||||
{
|
||||
offset *= -1;
|
||||
|
|
@ -2135,6 +2490,7 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
|
||||
tr_x += FixedMul(offset, cosmul);
|
||||
tr_y += FixedMul(offset, sinmul);
|
||||
|
||||
tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin);
|
||||
|
||||
tx = FixedMul(tr_x, viewsin) - FixedMul(tr_y, viewcos);
|
||||
|
|
@ -2151,6 +2507,7 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
|
||||
tr_x += FixedMul(offset2, cosmul);
|
||||
tr_y += FixedMul(offset2, sinmul);
|
||||
|
||||
tz2 = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin);
|
||||
|
||||
tx2 = FixedMul(tr_x, viewsin) - FixedMul(tr_y, viewcos);
|
||||
|
|
@ -2223,14 +2580,17 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
{
|
||||
scalestep = 0;
|
||||
yscale = sortscale;
|
||||
tx += offset;
|
||||
|
||||
// This is, frankly, a ridiculous solution to a problem that never existed until I touched the renderer
|
||||
// We blow up the scale of the offsets so that the RESCALED positions are still 1:1
|
||||
tx += (affinesprite) ? FixedDiv(offset,FixedMul(xscale, this_scale)) : offset;
|
||||
x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS;
|
||||
|
||||
// off the right side?
|
||||
if (x1 > viewwidth)
|
||||
return;
|
||||
|
||||
tx += offset2;
|
||||
tx += (affinesprite) ? FixedDiv(offset2,FixedMul(xscale, this_scale)) : offset2;
|
||||
x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--;
|
||||
|
||||
// off the left side
|
||||
|
|
@ -2385,18 +2745,47 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
if (!shadowskew)
|
||||
{
|
||||
//SoM: 3/17/2000: Disregard sprites that are out of view..
|
||||
if (vflip)
|
||||
|
||||
fixed_t useoffset = 0, useheight = 0;
|
||||
|
||||
if (affinesprite)
|
||||
{
|
||||
// This is an equally terrible hack, but it gets the job done with alignment.
|
||||
// I'm writing this after pulling an all nighter and trying tons of other solutions;
|
||||
// I cannot be assed to care about how elegant a solution is.
|
||||
|
||||
// spriteyscale is only ever 1.0 with affines; we have to recursively find the true
|
||||
// spriteyscale by doing this.
|
||||
fixed_t rawyscale = FixedDiv(affine_scale.y, FixedMul(this_scale, sortscale));
|
||||
|
||||
const float affineyscale = FIXED_TO_FLOAT(affine_scale.y);
|
||||
const float pivydiff = affine_pivotoffsetdiff.y * ((vflip) ? -1.0f : 1.0f);
|
||||
const fixed_t real_topoffset = FLOAT_TO_FIXED((static_cast<float>(affine_bounds.yup) - pivydiff) / affineyscale) + thingyoffset;
|
||||
const fixed_t real_height = FLOAT_TO_FIXED(static_cast<float>(affine_bounds.ylen) / affineyscale);
|
||||
|
||||
useoffset = FixedMul(real_topoffset, FixedMul(rawyscale, this_scale));
|
||||
useheight = FixedMul(real_height, FixedMul(rawyscale, this_scale));
|
||||
}
|
||||
else
|
||||
{
|
||||
useoffset = FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale));
|
||||
useheight = FixedMul(spr_height, FixedMul(spriteyscale, this_scale));
|
||||
}
|
||||
|
||||
const boolean vflipaffine = (vflip && (affinesprite));
|
||||
|
||||
if (vflip && (!affinesprite))
|
||||
{
|
||||
// When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned.
|
||||
// sprite height - sprite topoffset is the proper inverse of the vertical offset, of course.
|
||||
// remember gz and gzt should be seperated by sprite height, not thing height - thing height can be shorter than the sprite itself sometimes!
|
||||
gz = interp.z + interp.height - FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale));
|
||||
gzt = gz + FixedMul(spr_height, FixedMul(spriteyscale, this_scale));
|
||||
gz = interp.z + interp.height - useoffset;
|
||||
gzt = gz + useheight;
|
||||
}
|
||||
else
|
||||
{
|
||||
gzt = interp.z + FixedMul(spr_topoffset, FixedMul(spriteyscale, this_scale));
|
||||
gz = gzt - FixedMul(spr_height, FixedMul(spriteyscale, this_scale));
|
||||
gzt = interp.z + useoffset + ((vflipaffine) ? interp.height : 0);
|
||||
gz = gzt - useheight;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2539,6 +2928,29 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
vis->viewpoint.z = viewz;
|
||||
vis->viewpoint.angle = viewangle;
|
||||
|
||||
vis->affine = {0};
|
||||
|
||||
if (affinesprite)
|
||||
{
|
||||
vis->affine.scaling.x = affine_scale.x;
|
||||
vis->affine.scaling.y = affine_scale.y;
|
||||
vis->affine.distscale.x = affine_distscale.x;
|
||||
vis->affine.distscale.y = affine_distscale.y;
|
||||
vis->affine.rollangle = spriterotangle;
|
||||
|
||||
vis->affine.transform.a = affine_transform.a;
|
||||
vis->affine.transform.b = affine_transform.b;
|
||||
vis->affine.transform.c = affine_transform.c;
|
||||
vis->affine.transform.d = affine_transform.d;
|
||||
vis->affine.transform.ox = affine_transform.ox;
|
||||
vis->affine.transform.oy = affine_transform.oy;
|
||||
|
||||
vis->affine.mosaic.x = affine_mosaic.x;
|
||||
vis->affine.mosaic.y = affine_mosaic.y;
|
||||
|
||||
R_CopyAffineBounds(&affine_bounds, &vis->affine.bounds);
|
||||
}
|
||||
|
||||
vis->mobj = thing; // Easy access! Tails 06-07-2002
|
||||
|
||||
vis->x1 = x1 < portalclipstart ? portalclipstart : x1;
|
||||
|
|
@ -2558,7 +2970,7 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
|
||||
vis->xscale = FixedMul(spritexscale, xscale); //SoM: 4/17/2000
|
||||
vis->scale = FixedMul(spriteyscale, yscale); //<<detailshift;
|
||||
vis->thingscale = this_scale;
|
||||
vis->thingscale = (affinesprite) ? FRACUNIT : this_scale;
|
||||
|
||||
vis->spritexscale = spritexscale;
|
||||
vis->spriteyscale = spriteyscale;
|
||||
|
|
@ -2589,7 +3001,16 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
|
||||
if (vis->x1 > x1)
|
||||
{
|
||||
vis->startfrac += FixedDiv(vis->xiscale, this_scale) * (vis->x1 - x1);
|
||||
fixed_t xpush = FixedDiv(vis->xiscale, this_scale);
|
||||
|
||||
if (affinesprite)
|
||||
{
|
||||
// Affines are, code-wise, "1-to-1 scale", so we always move in whole movements for them.
|
||||
xpush = intsign(vis->xiscale) * FRACUNIT;
|
||||
}
|
||||
|
||||
|
||||
vis->startfrac += xpush * (vis->x1 - x1);
|
||||
vis->scale += FixedMul(scalestep, spriteyscale) * (vis->x1 - x1);
|
||||
}
|
||||
|
||||
|
|
@ -2632,6 +3053,8 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
vis->cut = static_cast<spritecut_e>(vis->cut | SC_VFLIP);
|
||||
if (splat)
|
||||
vis->cut = static_cast<spritecut_e>(vis->cut | SC_SPLAT); // I like ya cut g
|
||||
if (affinesprite)
|
||||
vis->cut = static_cast<spritecut_e>(vis->cut | SC_AFFINE); // *smack* AIEEEEEEEEEEEE
|
||||
|
||||
vis->patch = patch;
|
||||
vis->bright = R_CacheSpriteBrightMap(sprinfo, frame);
|
||||
|
|
@ -3939,6 +4362,17 @@ boolean R_ThingIsPaperSprite(mobj_t *thing)
|
|||
return (thing->frame & FF_PAPERSPRITE || thing->renderflags & RF_PAPERSPRITE);
|
||||
}
|
||||
|
||||
boolean R_ThingIsAffineSprite(mobj_t *thing)
|
||||
{
|
||||
// Affine papersprites are messy in software rendering.
|
||||
// Yes, I'm lazy; I've been at this for TWO WEEKS.
|
||||
boolean papersprite = (R_ThingIsPaperSprite(thing));
|
||||
|
||||
boolean notaffine = ((thing->frame & FF_NOAFFINE || thing->renderflags & RF_NOAFFINE) || papersprite);
|
||||
|
||||
return (!notaffine);
|
||||
}
|
||||
|
||||
boolean R_ThingIsFloorSprite(mobj_t *thing)
|
||||
{
|
||||
return (thing->flags2 & MF2_SPLAT || thing->frame & FF_FLOORSPRITE || thing->renderflags & RF_FLOORSPRITE);
|
||||
|
|
@ -3965,6 +4399,11 @@ boolean R_ThingIsFullDark(mobj_t *thing)
|
|||
return ((thing->frame & FF_BRIGHTMASK) == FF_FULLDARK);
|
||||
}
|
||||
|
||||
boolean R_AffinePreScale(mobj_t *thing)
|
||||
{
|
||||
return (cv_affineprescale.value || thing->renderflags & RF_AFFINEPRESCALE);
|
||||
}
|
||||
|
||||
//
|
||||
// R_DrawMasked
|
||||
//
|
||||
|
|
|
|||
|
|
@ -92,10 +92,18 @@ boolean R_ThingVerticallyFlipped (mobj_t *thing);
|
|||
boolean R_ThingIsPaperSprite (mobj_t *thing);
|
||||
boolean R_ThingIsFloorSprite (mobj_t *thing);
|
||||
|
||||
|
||||
boolean R_ThingIsFullBright (mobj_t *thing);
|
||||
boolean R_ThingIsSemiBright (mobj_t *thing);
|
||||
boolean R_ThingIsFullDark (mobj_t *thing);
|
||||
|
||||
boolean R_ThingIsAffineSprite (mobj_t *thing);
|
||||
boolean R_AffinePreScale (mobj_t *thing);
|
||||
void R_GetPivotVectorFromSpriteInfo(vector2_t* out,
|
||||
vector2_t* defaultpiv,
|
||||
spriteinfo_t* sprinfo,
|
||||
size_t frame);
|
||||
|
||||
boolean R_ThingIsFlashing(mobj_t *thing);
|
||||
|
||||
boolean R_ThingIsUsingBakedOffsets(mobj_t *thing);
|
||||
|
|
@ -159,6 +167,7 @@ typedef enum
|
|||
SC_SEMIBRIGHT = 1<<12,
|
||||
SC_BBOX = 1<<13,
|
||||
SC_CULL = 1<<14,
|
||||
SC_AFFINE = 1<<15,
|
||||
// masks
|
||||
SC_CUTMASK = SC_TOP|SC_BOTTOM,
|
||||
SC_FLAGMASK = ~SC_CUTMASK
|
||||
|
|
@ -185,6 +194,7 @@ struct vissprite_t
|
|||
|
||||
fixed_t startfrac; // horizontal position of x1
|
||||
fixed_t xscale, scale; // projected horizontal and vertical scales
|
||||
fixed_t ypush_scale; // Scale for downwards Y movement. Likely only relevant for affines.
|
||||
fixed_t thingscale; // the object's scale
|
||||
fixed_t sortscale; // sortscale only differs from scale for paper sprites and floor sprites
|
||||
fixed_t sortsplat; // the sortscale from behind the floor sprite
|
||||
|
|
@ -206,6 +216,16 @@ struct vissprite_t
|
|||
INT32 offset; // The center of the shearing location offset from x1
|
||||
} shear;
|
||||
|
||||
struct {
|
||||
vector2_t scaling; // Affine scaling
|
||||
vector2_t distscale; // X/Y scale based on camera distance
|
||||
vector2_t offset; // Per-pixel offset
|
||||
f_vector2_t mosaic; // Truncates how columndrawers "move" across the screen
|
||||
angle_t rollangle; // Affine rotation angle
|
||||
affine_t transform; // The actual affine transformation.
|
||||
affine_bounding_t bounds; // The "bounding box" (draw area) of the affine sprite.
|
||||
} affine;
|
||||
|
||||
fixed_t texturemid;
|
||||
patch_t *patch;
|
||||
patch_t *bright;
|
||||
|
|
|
|||
15
src/screen.c
15
src/screen.c
|
|
@ -133,6 +133,14 @@ void SCR_SetDrawFuncs(enum columncontext_e _columncontext)
|
|||
colfuncs[COLDRAWFUNC_FOG] = R_DrawFogColumn;
|
||||
colfuncs[COLDRAWFUNC_DROPSHADOW] = R_DrawDropShadowColumn;
|
||||
|
||||
// Affine functions; the ones here are only relevant for sprites.
|
||||
// We can't rotate texture columns normally, can we?
|
||||
|
||||
colfuncs[COLDRAWFUNC_AFFINE] = R_DrawAffineColumn;
|
||||
colfuncs[COLDRAWFUNC_AFFINETRANS] = R_DrawTranslatedAffineColumn;
|
||||
colfuncs[COLDRAWFUNC_AFFINEFUZZY] = R_DrawTranslucentAffineColumn;
|
||||
colfuncs[COLDRAWFUNC_AFFINETRANSTRANS] = R_DrawTranslatedTranslucentAffineColumn;
|
||||
|
||||
colfuncs_bm[BASEDRAWFUNC] = R_DrawColumn_Brightmap;
|
||||
colfuncs_bm[COLDRAWFUNC_FUZZY] = R_DrawTranslucentColumn_Brightmap;
|
||||
colfuncs_bm[COLDRAWFUNC_TRANS] = R_DrawTranslatedColumn_Brightmap;
|
||||
|
|
@ -143,6 +151,13 @@ void SCR_SetDrawFuncs(enum columncontext_e _columncontext)
|
|||
colfuncs_bm[COLDRAWFUNC_FOG] = NULL; // Not needed
|
||||
colfuncs_bm[COLDRAWFUNC_DROPSHADOW] = NULL; // Not needed
|
||||
|
||||
// Affine brightmap functions; needed but I don't care at the moment
|
||||
|
||||
colfuncs_bm[COLDRAWFUNC_AFFINE] = R_DrawAffineColumn_Brightmap;
|
||||
colfuncs_bm[COLDRAWFUNC_AFFINETRANS] = R_DrawTranslatedAffineColumn_Brightmap;
|
||||
colfuncs_bm[COLDRAWFUNC_AFFINEFUZZY] = R_DrawTranslucentAffineColumn_Brightmap;
|
||||
colfuncs_bm[COLDRAWFUNC_AFFINETRANSTRANS] = R_DrawTranslatedTranslucentAffineColumn_Brightmap;
|
||||
|
||||
spanfuncs[BASEDRAWFUNC] = R_DrawSpan;
|
||||
spanfuncs[SPANDRAWFUNC_TRANS] = R_DrawTranslucentSpan;
|
||||
spanfuncs[SPANDRAWFUNC_TILTED] = R_DrawSpan_Tilted;
|
||||
|
|
|
|||
|
|
@ -232,6 +232,7 @@ TYPEDEF (mdllistitem_t);
|
|||
|
||||
// m_fixed.h
|
||||
TYPEDEF (vector2_t);
|
||||
TYPEDEF (f_vector2_t);
|
||||
TYPEDEF (vector3_t);
|
||||
TYPEDEF (vector4_t);
|
||||
TYPEDEF (matrix_t);
|
||||
|
|
@ -358,6 +359,8 @@ TYPEDEF (msecnode_t);
|
|||
TYPEDEF (mprecipsecnode_t);
|
||||
TYPEDEF (lightmap_t);
|
||||
TYPEDEF (seg_t);
|
||||
TYPEDEF (affine_t);
|
||||
TYPEDEF (affine_bounding_t);
|
||||
|
||||
// r_fps.h
|
||||
TYPEDEF (viewvars_t);
|
||||
|
|
@ -419,7 +422,6 @@ TYPEDEF (taggroup_t);
|
|||
// v_video.h
|
||||
TYPEDEF (colorlookup_t);
|
||||
TYPEDEF (cliprect_t);
|
||||
TYPEDEF (affine_t);
|
||||
|
||||
// w_wad.h
|
||||
TYPEDEF (filelump_t);
|
||||
|
|
|
|||
152
src/v_video.c
152
src/v_video.c
|
|
@ -810,6 +810,114 @@ static int sortcoords(const void *a, const void *b)
|
|||
}
|
||||
*/
|
||||
|
||||
affine_bounding_t* V_GetAffineBounds(const affine_t* transform,
|
||||
patch_t* patch,
|
||||
fixed_t divisor,
|
||||
affine_bounding_t* out,
|
||||
boolean unrestrict_bound)
|
||||
{
|
||||
// A decent chunk of this code is just fixedpointizing stuff
|
||||
// from HWR_GetAffinePatch; That system's near flawless, why fix what isn't broken?
|
||||
// Kudos to Generic for making my life easier 🥹
|
||||
|
||||
// First, let's set our output to a bunch of dummy values.
|
||||
|
||||
out->l = INT32_MAX;
|
||||
out->t = INT32_MAX;
|
||||
out->r = INT32_MIN;
|
||||
out->b = INT32_MIN;
|
||||
|
||||
fixed_t fa = FixedDiv(transform->a, divisor);
|
||||
fixed_t fd = FixedDiv(transform->d, divisor);
|
||||
fixed_t fc = FixedDiv(transform->c, divisor);
|
||||
fixed_t fb = FixedDiv(transform->b, divisor);
|
||||
fixed_t fx = (transform->ox);
|
||||
fixed_t fy = (transform->oy);
|
||||
|
||||
// now, the matrix passed to this function maps screen coordinates to texel coordinates...
|
||||
// but to translate this from software to GL, we have to figure out where each corner
|
||||
// (or vertex) of the patch should end up on the screen.
|
||||
// which means we have to map texel coordinates to screen coordinates.
|
||||
// which means we have to invert the matrix.
|
||||
// how do you invert a matrix?
|
||||
// ...
|
||||
// i don't fucking know, i spent a day on this and got absolutely nowhere, but this guy
|
||||
// knows:
|
||||
// https://nigeltao.github.io/blog/2021/inverting-3x2-affine-transformation-matrix.html
|
||||
|
||||
// ...so I thought this would be standard fare order-of-operations stuff
|
||||
// NOPE!
|
||||
// The compiled assembly code does THIS:
|
||||
// (fa * fd) - (fb * fc)
|
||||
fixed_t determinant = FixedMul(fa, fd) - FixedMul(fb, fc);
|
||||
|
||||
if (determinant == 0)
|
||||
return out;
|
||||
|
||||
fixed_t ba = FixedDiv(fd, determinant);
|
||||
fixed_t bb = FixedDiv(-fb, determinant);
|
||||
fixed_t bc = FixedDiv(-fc, determinant);
|
||||
fixed_t bd = FixedDiv(fa, determinant);
|
||||
|
||||
// set the polygon vertices to the right positions
|
||||
// 3--2
|
||||
// | /|
|
||||
// |/ |
|
||||
// 0--1
|
||||
fixed_t pw = (patch->width << FRACBITS);
|
||||
fixed_t ph = (patch->height << FRACBITS);
|
||||
vector2_t v[4] = {
|
||||
[3] = {.x = (FixedMul(ba, -fx) - FixedMul(bb, fy)) + fx,
|
||||
.y = (FixedMul(bc, -fx) - FixedMul(bd, fy)) + fy },
|
||||
[2] = { .x = (FixedMul(ba, (pw - fx)) - FixedMul(bb, fy)) + fx,
|
||||
.y = (FixedMul(bc, (pw - fx)) - FixedMul(bd, fy)) + fy },
|
||||
[0] = { .x = (FixedMul(ba, -fx) + FixedMul(bb, (ph - fy))) + fx,
|
||||
.y = (FixedMul(bc, -fx) + FixedMul(bd, (ph - fy))) + fy },
|
||||
[1] = { .x = (FixedMul(ba, (pw - fx)) + FixedMul(bb, (ph - fy))) + fx,
|
||||
.y = (FixedMul(bc, (pw - fx)) + FixedMul(bd, (ph - fy))) + fy },
|
||||
};
|
||||
|
||||
// Get the leftmost and uppermost bounds of the current resolution.
|
||||
INT32 vw = vid.width, vh = vid.height;
|
||||
INT32 leftmost = ((BASEVIDWIDTH - vw) / 2), uppermost = ((BASEVIDHEIGHT - vh) / 2);
|
||||
|
||||
if (unrestrict_bound)
|
||||
{
|
||||
vw = vh = INT32_MAX;
|
||||
leftmost = uppermost = INT32_MIN;
|
||||
}
|
||||
|
||||
// ...okay, now comb through all four vertices and set the output bounds based on this.
|
||||
// "Why not a loop?" According to SM64 programming wizard Kaze Emanuar,
|
||||
// loops take more processing time. If we can help it, it's better to just cut corners.
|
||||
#define BOUNDCHECK(i) \
|
||||
{ \
|
||||
out->l = max(leftmost, min(v[i].x >> FRACBITS, out->l)); \
|
||||
out->r = min(vw, max(v[i].x >> FRACBITS, out->r)); \
|
||||
out->t = max(uppermost, min(v[i].y >> FRACBITS, out->t)); \
|
||||
out->b = min(vh, max(v[i].y >> FRACBITS, out->b)); \
|
||||
}
|
||||
|
||||
BOUNDCHECK(0);
|
||||
BOUNDCHECK(1);
|
||||
BOUNDCHECK(2);
|
||||
BOUNDCHECK(3);
|
||||
|
||||
#undef BOUNDCHECK
|
||||
|
||||
// Cool, we have our bounds. Get the diffs so the loops have something to reference.
|
||||
out->xlen = abs(out->r - out->l);
|
||||
out->ylen = abs(out->b - out->t);
|
||||
|
||||
out->xleft = (fx >> FRACBITS) - out->l;
|
||||
out->xright = out->r - (fx >> FRACBITS);
|
||||
|
||||
out->yup = (fy >> FRACBITS) - out->t;
|
||||
out->ydown = out->b - (fy >> FRACBITS);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void V_DrawAffinePatch(fixed_t x, fixed_t y, const affine_t *transform, INT32 scrn, patch_t *patch, const UINT8 *colormap)
|
||||
{
|
||||
if (rendermode == render_none)
|
||||
|
|
@ -858,6 +966,9 @@ void V_DrawAffinePatch(fixed_t x, fixed_t y, const affine_t *transform, INT32 sc
|
|||
}
|
||||
#endif
|
||||
|
||||
affine_bounding_t bounds = {0};
|
||||
V_GetAffineBounds(transform, patch, vid.dup * FRACUNIT, &bounds, false);
|
||||
|
||||
Patch_GenerateFlat(patch, 0);
|
||||
const UINT16 *src = patch->flats[0];
|
||||
if (src == NULL)
|
||||
|
|
@ -867,8 +978,8 @@ void V_DrawAffinePatch(fixed_t x, fixed_t y, const affine_t *transform, INT32 sc
|
|||
const fixed_t b = transform->b / dup;
|
||||
const fixed_t c = transform->c / dup;
|
||||
const fixed_t d = transform->d / dup;
|
||||
const fixed_t cx = transform->ox;
|
||||
const fixed_t cy = transform->oy;
|
||||
fixed_t cx = transform->ox;
|
||||
fixed_t cy = transform->oy;
|
||||
|
||||
const INT32 scrwidth = vid.width;
|
||||
const INT32 pw = patch->width, ph = patch->height;
|
||||
|
|
@ -892,20 +1003,43 @@ void V_DrawAffinePatch(fixed_t x, fixed_t y, const affine_t *transform, INT32 sc
|
|||
fixed_t y4 = FixedMul(bc, pw*FRACUNIT - cx) + FixedMul(bd, ph*FRACUNIT - cy) + cy;
|
||||
*/
|
||||
|
||||
UINT8 * const destbase = vid.screens[0] + y * vid.width + x;
|
||||
INT32 dx = 0, dy = 0;
|
||||
INT32 ydiff = (bounds.yup - (transform->oy >> FRACBITS));
|
||||
INT32 xdiff = (bounds.xleft - (transform->ox >> FRACBITS));
|
||||
|
||||
for (dy = 0; dy < ph; dy++)
|
||||
// Get the clipping values, since that can vary per-resolution.
|
||||
INT32 yclip = min(y - ydiff, 0) * -1;
|
||||
INT32 xclip = min(x - xdiff, 0) * -1;
|
||||
|
||||
ydiff -= yclip;
|
||||
xdiff -= xclip;
|
||||
|
||||
// Get the leftmost and uppermost bounds of the current resolution.
|
||||
const INT32 vw = vid.width, vh = vid.height;
|
||||
|
||||
INT32 yy = CLAMP(y - ydiff, 0, vh);
|
||||
INT32 xx = CLAMP(x - xdiff, 0, vw);
|
||||
|
||||
INT32 xmax = max(bounds.xlen - xclip, pw), ymax = max(bounds.ylen - yclip, ph);
|
||||
|
||||
// Offset our X and Y positions by the bounding differences.
|
||||
fixed_t cxx = cx + (xdiff * FRACUNIT);
|
||||
fixed_t cyy = cy + (ydiff * FRACUNIT);
|
||||
|
||||
intptr_t dest_y = (intptr_t)(vid.screens[0]) + yy * vw;
|
||||
|
||||
UINT8 * const destbase = (UINT8 * const)(dest_y + xx);
|
||||
INT32 dx = 0, dy = 0;
|
||||
for (dy = 0; dy < ymax; dy++)
|
||||
{
|
||||
// yoinked from NovaSquirrel's mode 7 preview
|
||||
// ...which is in turn yoinked from Mesen's S-PPU code
|
||||
// i can't do matrix math to save my life :face_holding_back_tears:
|
||||
// (m7xofs and m7yofs are already factored in by destbase)
|
||||
fixed_t ux = FixedMul(a, -cx) + FixedMul(b, -cy) + b*dy + cx;
|
||||
fixed_t uy = FixedMul(c, -cx) + FixedMul(d, -cy) + d*dy + cy;
|
||||
UINT8 *dest = destbase + dy * scrwidth;
|
||||
fixed_t ux = (FixedMul(a, -cxx) + FixedMul(b, -cyy) + b*(dy) + cx);
|
||||
fixed_t uy = FixedMul(c, -cxx) + FixedMul(d, -cyy) + d*(dy) + cy;
|
||||
UINT8 *dest = (destbase) + (dy) * scrwidth;
|
||||
|
||||
for (dx = 0; dx < pw; dx++, dest++)
|
||||
for (dx = 0; dx < xmax; dx++, dest++)
|
||||
{
|
||||
const INT32 srcx = ux >> FRACBITS;
|
||||
const INT32 srcy = uy >> FRACBITS;
|
||||
|
|
|
|||
|
|
@ -218,16 +218,11 @@ void V_ClearClipRect(void);
|
|||
void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 scrn, patch_t *patch, const UINT8 *colormap);
|
||||
void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t *patch, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h);
|
||||
|
||||
// an affine transformation matrix. maps screen coordinates to texture coordinates
|
||||
struct affine_t
|
||||
{
|
||||
fixed_t a; // horizontal step per pixel (M7A)
|
||||
fixed_t b; // horizontal step per scanline (M7B)
|
||||
fixed_t c; // vertical step per pixel (M7C)
|
||||
fixed_t d; // vertical step per scanline (M7D)
|
||||
fixed_t ox, oy; // transform origin in texture coordinates (M7X/M7Y)
|
||||
};
|
||||
|
||||
affine_bounding_t* V_GetAffineBounds(const affine_t* transform,
|
||||
patch_t* patch,
|
||||
fixed_t divisor,
|
||||
affine_bounding_t* out,
|
||||
boolean unrestrict_bound);
|
||||
void V_DrawAffinePatch(fixed_t x, fixed_t y, const affine_t *transform, INT32 scrn, patch_t *patch, const UINT8 *colormap);
|
||||
void V_DrawRotatedPatch(fixed_t x, fixed_t y, angle_t angle, fixed_t pscale, fixed_t vscale, INT32 scrn, patch_t *patch, const UINT8 *colormap);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue