From 951c43d1e8fe0340337f154209845d5afcb72dcb Mon Sep 17 00:00:00 2001 From: yamamama Date: Thu, 5 Mar 2026 00:39:19 -0500 Subject: [PATCH] Add an option for mosaic affine rendering Currently software only; would require a shader or more "involved" solution for OpenGL --- src/hardware/hw_glob.h | 6 ------ src/m_fixed.h | 7 +++++++ src/r_defs.h | 1 + src/r_draw_column.cpp | 39 +++++++++++++++++++++++++++++++++++---- src/r_main.cpp | 2 ++ src/r_main.h | 2 +- src/r_things.cpp | 10 ++++++++++ src/r_things.h | 1 + src/typedef.h | 1 + 9 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h index a1d668f80..39d1af744 100644 --- a/src/hardware/hw_glob.h +++ b/src/hardware/hw_glob.h @@ -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 { diff --git a/src/m_fixed.h b/src/m_fixed.h index 74b2f29b6..d97f3747f 100644 --- a/src/m_fixed.h +++ b/src/m_fixed.h @@ -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); diff --git a/src/r_defs.h b/src/r_defs.h index 7856a1473..e5bfd2771 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -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; diff --git a/src/r_draw_column.cpp b/src/r_draw_column.cpp index 1f5eb2014..ca90caf41 100644 --- a/src/r_draw_column.cpp +++ b/src/r_draw_column.cpp @@ -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(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(ypx); + + if (mosaic_on && ymosaic_on) + { + y_real = (static_cast(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) { diff --git a/src/r_main.cpp b/src/r_main.cpp index be7a995ed..46a367007 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -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); diff --git a/src/r_main.h b/src/r_main.h index b1f066705..26bcecf97 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -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 diff --git a/src/r_things.cpp b/src/r_things.cpp index db92a857f..fcf796998 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -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); } diff --git a/src/r_things.h b/src/r_things.h index 977c2a211..2914058c1 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -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. diff --git a/src/typedef.h b/src/typedef.h index c7590d27d..c5f78200a 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -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);