Add an option for mosaic affine rendering

Currently software only; would require a shader or more "involved" solution for OpenGL
This commit is contained in:
yamamama 2026-03-05 00:39:19 -05:00
parent cb578f423d
commit 951c43d1e8
9 changed files with 58 additions and 11 deletions

View file

@ -43,12 +43,6 @@ typedef struct
float z;
} polyvertex_t;
typedef struct
{
float x;
float y;
} f_vector2_t;
// a convex 'plane' polygon, clockwise order
typedef struct
{

View file

@ -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);

View file

@ -1168,6 +1168,7 @@ typedef struct
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;

View file

@ -635,19 +635,50 @@ static void R_DrawAffineColumnTemplate(drawcolumndata_t *dc)
//I_OutputMsg("xdiff: %f, ydiff: %f\n", FIXED_TO_FLOAT(xdiff), FIXED_TO_FLOAT(ydiff));
// 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, dc->frac) + cx;
fixed_t uy = FixedMul(d, -cyy) + FixedMul(c, -cxx) + FixedMul(c, dc->frac) + cy;
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);
ux += FixedMul(b, ystep_delta);
uy += FixedMul(d, ystep_delta);
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)
{

View file

@ -200,6 +200,7 @@ consvar_t cv_nulldrifttilt = CVAR_INIT ("nulldrifttilt", "On", CV_SAVE, CV_OnOff
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}};
@ -1813,6 +1814,7 @@ void R_RegisterEngineStuff(void)
CV_RegisterVar(&cv_nulldrifttilt);
CV_RegisterVar(&cv_fakerollangle);
CV_RegisterVar(&cv_affineprescale);
CV_RegisterVar(&cv_affinemosaic);
CV_RegisterVar(&cv_showhud);
CV_RegisterVar(&cv_translucenthud);

View file

@ -162,7 +162,7 @@ extern consvar_t cv_sliptidetilt;
extern consvar_t cv_nulldriftefx, cv_nulldrifttilt;
extern consvar_t cv_fakerollangle;
extern consvar_t cv_affineprescale;
extern consvar_t cv_affineprescale, cv_affinemosaic;
// debugging

View file

@ -1182,6 +1182,9 @@ static void R_DrawVisSprite(vissprite_t *vis)
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);
@ -2081,6 +2084,7 @@ static void R_ProjectSprite(mobj_t *thing)
vector2_t affine_scale = {0};
vector2_t affine_distscale = {0};
vector2_t affine_pivotoffsetdiff = {0};
f_vector2_t affine_mosaic = {.x = 1.0f, .y = 1.0f};
if (R_IsOverlayingSMonitorPlayer(thing))
{
@ -2355,6 +2359,9 @@ static void R_ProjectSprite(mobj_t *thing)
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);
@ -2940,6 +2947,9 @@ static void R_ProjectSprite(mobj_t *thing)
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);
}

View file

@ -220,6 +220,7 @@ struct vissprite_t
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.

View file

@ -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);