WIP affine patch drawing

will probably invert the matrix
This commit is contained in:
GenericHeroGuy 2025-12-31 14:49:36 +01:00
parent 2afd5d1ef0
commit bcef031c85
6 changed files with 360 additions and 0 deletions

View file

@ -265,6 +265,131 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p
HWD.pfnDrawPolygon(NULL, v, 4, flags|PF_Translucent);
}
void HWR_DrawAffinePatch(patch_t *gpatch, fixed_t x, fixed_t y, const affine_t *transform, INT32 option, const UINT8 *colormap)
{
//const cliprect_t *clip = V_GetClipRect();
// make patch ready in hardware cache
if (!colormap)
HWR_GetPatch(gpatch);
else
HWR_GetMappedPatch(gpatch, colormap);
// positions of the x, y, are between 0 and vid.width/vid.height now, we need them to be between -1 and 1
float fwidth = vid.width;// / vid.dupx;
float fheight = vid.height;// / vid.dupy;
float cx = -1.0f + (x / (fwidth/2));
float cy = 1.0f - (y / (fheight/2));
float fa = FIXED_TO_FLOAT(transform->a) / vid.dupx;
float fd = FIXED_TO_FLOAT(transform->d) / vid.dupx;
float fc = FIXED_TO_FLOAT(transform->c) / vid.dupy;
float fb = FIXED_TO_FLOAT(transform->b) / vid.dupy;
float fx = FIXED_TO_FLOAT(transform->ox);
float fy = FIXED_TO_FLOAT(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)
return;
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
FOutVector v[4] = {
[3] = { .x = ba * -fx + bb * -fy + fx,
.y = bc * -fx + bd * -fy + fy },
[2] = { .x = ba * (gpatch->width - fx) + bb * -fy + fx,
.y = bc * (gpatch->width - fx) + bd * -fy + fy },
[0] = { .x = ba * -fx + bb * (gpatch->height - fy) + fx,
.y = bc * -fx + bd * (gpatch->height - fy) + fy },
[1] = { .x = ba * (gpatch->width - fx) + bb * (gpatch->height - fy) + fx,
.y = bc * (gpatch->width - fx) + bd * (gpatch->height - fy) + fy },
};
// normalize to -1,1
v[0].x = cx + (v[0].x / fwidth*2);
v[1].x = cx + (v[1].x / fwidth*2);
v[2].x = cx + (v[2].x / fwidth*2);
v[3].x = cx + (v[3].x / fwidth*2);
v[0].y = cy - (v[0].y / fheight*2);
v[1].y = cy - (v[1].y / fheight*2);
v[2].y = cy - (v[2].y / fheight*2);
v[3].y = cy - (v[3].y / fheight*2);
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
const GLPatch_t *hwrPatch = ((GLPatch_t *)gpatch->hardware);
float s_min = 0.f, t_min = 0.f;
float s_max = hwrPatch->max_s, t_max = hwrPatch->max_t;
if (option & V_FLIP)
{
v[0].s = v[3].s = s_max;
v[2].s = v[1].s = s_min;
}
else
{
v[0].s = v[3].s = s_min;
v[2].s = v[1].s = s_max;
}
if (option & V_VFLIP)
{
v[0].t = v[1].t = t_min;
v[2].t = v[3].t = t_max;
}
else
{
v[0].t = v[1].t = t_max;
v[2].t = v[3].t = t_min;
}
FBITFIELD flags = PF_NoDepthTest;
// clip it since it is used for bunny scroll in doom I
UINT32 alphalevel = (option & V_ALPHAMASK) >> V_ALPHASHIFT;
UINT32 blendmode = (option & V_BLENDMASK) >> V_BLENDSHIFT;
if (blendmode)
blendmode++; // realign to constants
if (alphalevel || blendmode)
{
FSurfaceInfo Surf;
Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff;
flags |= HWR_GetBlendModeFlag(blendmode);
if (alphalevel == 13)
Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency];
else if (alphalevel == 14)
Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency];
else if (alphalevel == 15)
Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency];
else if (alphalevel < 10)
Surf.PolyColor.s.alpha = softwaretranstogl[min(max((10 - alphalevel), 0), 10)];
else
Surf.PolyColor.s.alpha = 0;
HWD.pfnDrawPolygon(&Surf, v, 4, flags|PF_Modulated);
}
else
HWD.pfnDrawPolygon(NULL, v, 4, flags|PF_Translucent);
}
void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, INT32 option, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h)
{
FOutVector v[4];

View file

@ -43,6 +43,7 @@ void HWR_InitTextureMapping(void);
void HWR_SetViewSize(void);
void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option, const UINT8 *colormap);
void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t scale, INT32 option, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h);
void HWR_DrawAffinePatch(patch_t *gpatch, fixed_t x, fixed_t y, const affine_t *transform, INT32 option, const UINT8 *colormap);
void HWR_MakePatch(const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipmap, boolean makebitmap);
void HWR_CreatePlanePolygons(INT32 bspnum);
void HWR_CreateStaticLightmaps(INT32 bspnum);

View file

@ -705,6 +705,57 @@ static int libd_drawStretched(lua_State *L)
return 0;
}
static int libd_drawAffine(lua_State *L)
{
HUDONLY
fixed_t x = luaL_checkfixed(L, 1);
fixed_t y = luaL_checkfixed(L, 2);
affine_t transform = {};
luaL_checktype(L, 3, LUA_TTABLE);
patch_t *patch = *((patch_t **)luaL_checkudata(L, 4, META_PATCH));
INT32 flags = luaL_optinteger(L, 5, 0);
UINT8 *colormap = NULL;
if (!lua_isnoneornil(L, 6))
colormap = *((UINT8 **)luaL_checkudata(L, 6, META_COLORMAP));
lua_pushnil(L);
while (lua_next(L, 3))
{
INT32 i = 0;
const char *k = NULL;
if (lua_isnumber(L, -2))
i = lua_tointeger(L, -2);
else if (lua_isstring(L, -2))
k = lua_tostring(L, -2);
if (i == 1 || (k && fasticmp(k, "a")))
transform.a = luaL_checkfixed(L, -1);
else if (i == 2 || (k && fasticmp(k, "b")))
transform.b = luaL_checkfixed(L, -1);
else if (i == 3 || (k && fasticmp(k, "c")))
transform.c = luaL_checkfixed(L, -1);
else if (i == 4 || (k && fasticmp(k, "d")))
transform.d = luaL_checkfixed(L, -1);
else if (i == 5 || (k && fasticmp(k, "ox")))
transform.ox = luaL_checkfixed(L, -1);
else if (i == 6 || (k && fasticmp(k, "oy")))
transform.oy = luaL_checkfixed(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
lua_getfield(L, LUA_REGISTRYINDEX, "HUD_DRAW_LIST");
huddrawlist_h list = lua_touserdata(L, -1);
lua_pop(L, 1);
//if (LUA_HUD_IsDrawListValid(list))
//LUA_HUD_AddDrawStretched(list, x, y, hscale, vscale, patch, flags, colormap);
//else
V_DrawAffinePatch(x, y, &transform, flags, patch, colormap);
return 0;
}
// KART: draw patch on minimap from x, y coordinates on the map
static int libd_drawOnMinimap(lua_State *L)
{
@ -1536,6 +1587,7 @@ static luaL_Reg lib_draw[] = {
{"draw", libd_draw},
{"drawScaled", libd_drawScaled},
{"drawStretched", libd_drawStretched},
{"drawAffine", libd_drawAffine},
{"drawNum", libd_drawNum},
{"drawPaddedNum", libd_drawPaddedNum},
{"drawPingNum", libd_drawPingNum},

View file

@ -419,6 +419,7 @@ TYPEDEF (taggroup_t);
// v_video.h
TYPEDEF (colorlookup_t);
TYPEDEF (cliprect_t);
TYPEDEF (affine_t);
// w_wad.h
TYPEDEF (filelump_t);

View file

@ -31,6 +31,7 @@
#include "m_random.h"
#include "doomstat.h"
#include "r_fps.h"
#include "qs22j.h"
#ifdef HWRENDER
#include "hardware/hw_glob.h"
@ -797,6 +798,173 @@ static const UINT8 *v_translevel = NULL;
#define TRANSLUCENTDRAW 3
#define TRANSMAPPEDDRAW 4
/*
static int sortcoords(const void *a, const void *b)
{
return *(const fixed_t *)a - *(const fixed_t *)b;
}
*/
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)
return;
INT32 dupx, dupy;
switch (scrn & V_SCALEPATCHMASK)
{
case V_NOSCALEPATCH:
dupx = dupy = 1;
break;
case V_SMALLSCALEPATCH:
dupx = vid.smalldupx;
dupy = vid.smalldupy;
break;
case V_MEDSCALEPATCH:
dupx = vid.meddupx;
dupy = vid.meddupy;
break;
default:
dupx = vid.dupx;
dupy = vid.dupy;
break;
}
if (scrn & V_NOSCALESTART)
{
x >>= FRACBITS;
y >>= FRACBITS;
}
else
{
x = FixedMul(x,dupx<<FRACBITS);
y = FixedMul(y,dupy<<FRACBITS);
x += transform->ox * (dupx-1);
y += transform->oy * (dupy-1);
x >>= FRACBITS;
y >>= FRACBITS;
if (!(scrn & V_SCALEPATCHMASK)) // Center it if necessary
V_AdjustXYWithSnap(&x, &y, scrn, dupx, dupy);
}
#ifdef HWRENDER
if (rendermode == render_opengl)
{
HWR_DrawAffinePatch(patch, x, y, transform, scrn, colormap);
return;
}
#endif
Patch_GenerateFlat(patch, 0);
const UINT16 *src = patch->flats[0];
if (src == NULL)
return;
const fixed_t a = transform->a / dupx;
const fixed_t b = transform->b / dupx;
const fixed_t c = transform->c / dupy;
const fixed_t d = transform->d / dupy;
const fixed_t cx = transform->ox;
const fixed_t cy = transform->oy;
const INT32 scrwidth = vid.width;
const INT32 pw = patch->width, ph = patch->height;
/*
fixed_t determinant = FixedMul(a, d) - FixedMul(b, c);
if (determinant == 0)
return;
fixed_t ba = FixedDiv(d, determinant);
fixed_t bb = FixedDiv(-b, determinant);
fixed_t bc = FixedDiv(-c, determinant);
fixed_t bd = FixedDiv(a, determinant);
fixed_t x1 = FixedMul(ba, -cx) + FixedMul(bb, -cy) + cx;
fixed_t y1 = FixedMul(bc, -cx) + FixedMul(bd, -cy) + cy;
fixed_t x2 = FixedMul(ba, pw*FRACUNIT - cx) + FixedMul(bb, -cy) + cx;
fixed_t y2 = FixedMul(bc, pw*FRACUNIT - cx) + FixedMul(bd, -cy) + cy;
fixed_t x3 = FixedMul(ba, -cx) + FixedMul(bb, ph*FRACUNIT - cy) + cx;
fixed_t y3 = FixedMul(bc, -cx) + FixedMul(bd, ph*FRACUNIT - cy) + cy;
fixed_t x4 = FixedMul(ba, pw*FRACUNIT - cx) + FixedMul(bb, ph*FRACUNIT - cy) + cx;
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;
for (dy = 0; dy < ph; 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;
for (dx = 0; dx < pw; dx++, dest++)
{
const INT32 srcx = ux >> FRACBITS;
const INT32 srcy = uy >> FRACBITS;
ux += a;
uy += c;
if (srcx < 0 || srcx >= pw || srcy < 0 || srcy >= ph)
continue;
const UINT16 pixel = src[srcy * pw + srcx];
if (pixel < 0xff00)
continue;
if (colormap != NULL)
*dest = colormap[pixel & 0xff];
else
*dest = (UINT8)(pixel & 0xff);
}
}
/*
fixed_t xsort[4] = { x1, x2, x3, x4 };
fixed_t ysort[4] = { y1, y2, y3, y4 };
qs22j(xsort, 4, sizeof(*xsort), sortcoords);
qs22j(ysort, 4, sizeof(*ysort), sortcoords);
fixed_t dx1, dx2;
dx1 = FixedDiv(x1 - x2, y2 - y1);
dx2 = FixedDiv(x1 - x3, y3 - y1);
fixed_t px1 = x*FRACUNIT + x1;
fixed_t px2 = x*FRACUNIT + x2;
fixed_t py = y*FRACUNIT + y1;
for (UINT32 i = 0; i < 10; i++)
{
for (fixed_t A = px1; A < px2; A += FRACUNIT)
V_DrawFixedFill(A, py + i*FRACUNIT, FRACUNIT, FRACUNIT, V_NOSCALESTART | (leveltime&0xff));
px1 += dx1;
px2 += dx2;
}
V_DrawFixedFill(x*FRACUNIT + x1, y*FRACUNIT + y1, FRACUNIT, FRACUNIT, V_NOSCALESTART | (leveltime&0xff));
V_DrawFixedFill(x*FRACUNIT + x2, y*FRACUNIT + y2, FRACUNIT, FRACUNIT, V_NOSCALESTART | (leveltime&0xff));
V_DrawFixedFill(x*FRACUNIT + x3, y*FRACUNIT + y3, FRACUNIT, FRACUNIT, V_NOSCALESTART | (leveltime&0xff));
V_DrawFixedFill(x*FRACUNIT + x4, y*FRACUNIT + y4, FRACUNIT, FRACUNIT, V_NOSCALESTART | (leveltime&0xff));
*/
};
// draws a patch rotated counter-clockwise by angle degrees
// note that scaling is applied BEFORE rotation!
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)
{
fixed_t sa = FSIN(angle), ca = FCOS(angle);
affine_t t = {
.a = FixedDiv(ca, pscale),
.b = FixedDiv(-sa, pscale),
.c = FixedDiv(sa, vscale),
.d = FixedDiv(ca, vscale),
.ox = (patch->pivot.x + patch->leftoffset) * FRACUNIT,
.oy = (patch->pivot.y + patch->topoffset) * FRACUNIT,
};
V_DrawAffinePatch(x, y, &t, scrn, patch, colormap);
}
// Draws a patch scaled to arbitrary size.
void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 scrn, patch_t *patch, const UINT8 *colormap)
{

View file

@ -215,6 +215,19 @@ 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)
};
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);
void V_DrawContinueIcon(INT32 x, INT32 y, INT32 flags, INT32 skinnum, UINT16 skincolor);
// Draw a linear block of pixels into the view buffer.