diff --git a/README.md b/README.md index d6b1312..79f9897 100644 --- a/README.md +++ b/README.md @@ -387,6 +387,29 @@ which the desktop build does not apply. the sprite from where it should sit, since it shares its coordinate system with this patch's own size override rather than with the bitmap's real pixel dimensions. + + `g3_bitmap_common()`'s world-space size and its texture-coordinate range + are computed from the same `bm_w`/`bm_h` locals, but only the former + should ever use this patch's override - the pixels actually being + sampled are still the bitmap's own real (possibly upscaled) buffer, + regardless of what on-screen size it's drawn at. Overriding both left the + texture-coordinate range capped at the smaller, un-upscaled size while + the real bitmap underneath it was larger, so the renderer only ever + sampled that smaller top-left region of it - cropping off anything past + it, such as an upscaled critter's limbs extending beyond its original + sprite's bounding box. OpenGL never showed this: `opengl_bitmap()` + ignores `g3_bitmap_common()`'s computed `u`/`v` entirely and always maps + the *whole* bound texture over the quad via fixed `0.0`-`1.0` corners. + The software renderer's own scanline texture mapper (`h_map()`, + `gentm.c`) has no such shortcut - it takes `u`/`v` as literal pixel + coordinates into the bitmap and interpolates between exactly the values + it's given, so a capped range there directly caps how much of the real + bitmap it ever reads. Fixed by splitting `g3_bitmap_common()`'s single + `bm_w`/`bm_h` into the override-aware pair (world-space size only) and a + `tex_w`/`tex_h` pair that always holds the bitmap's own real dimensions, + used for every texture-coordinate computation (including the later + blend-buffer-size check, which sizes a buffer against the same real + pixel data these coordinates sample from). - **`05-desktop-fix-texture-cache-collision.patch`** - `OpenGL.cc`'s per-bitmap GL texture cache (`texturesByBitsPtr`) was keyed by `bm->bits | (w << 32) | (h << 48)`, a plain bitwise OR rather than a @@ -443,15 +466,28 @@ included in `make dist`/`make package`. numbered the same way as `engine-patches/` (section 4.4's own process applies: scratch copy, `patch -p1`, regenerate via `diff -u` rather than hand-editing hunks), applied on top of the regular `engine-patches/` - series, never in place of it. Currently one patch, adding a debug dump - hotkey: pressing **K** in-game dumps every frame of the - currently/most-recently-drawn critter's animation to `dumps/` (created - next to the running binary) as raw palette-index pixel data - 2-byte LE - width, 2-byte LE height, then width\*height index bytes, one file per - frame, named `id_frame.raw`. Deliberately independent of - the renderer's own texture/GL state, so a frame already corrupted in the - dump is a decode-time bug, while a frame that's clean in the dump but - wrong on screen is a rendering-time one. + series, never in place of it. Two patches so far: + - **`01`** - a debug dump hotkey: pressing **K** in-game dumps every + frame of the currently/most-recently-drawn critter's animation to + `dumps/` (created next to the running binary) as raw palette-index + pixel data - 2-byte LE width, 2-byte LE height, then width\*height + index bytes, one file per frame, named `id_frame.raw`. + Deliberately independent of the renderer's own texture/GL state, so a + frame already corrupted in the dump is a decode-time bug, while a + frame that's clean in the dump but wrong on screen is a rendering-time + one. + - **`02`** - RSD8 decode tracing: `gr_rsd8_convert()`/`gr_rsd8_unpack()` + (`engine/src/Libraries/2D/Source/RSD/`) print a `[debug rsd8]` summary + per frame (decode path, source/destination dimensions, bytes written + versus the bitmap's own area) and a `[debug rsd8 op]` line per RLE + opcode consumed, gated by a flag `debug_dump_critter_animation()` (01, + above) sets only around its own decode calls - tracing covers exactly + the frames the K hotkey dumps, not every RSD8 decode in the game. Each + frame is also decoded a second time back to back and logged as + `identical`/`DIFFERS` against the first, to tell a decode result that's + deterministic given the compressed source (a bad source stream, or a + decoder bug it triggers) apart from one that depends on something else, + such as a resource-cache slot being reused mid-decode. - `engine-debug-tools/dump2png/` - converts those `.raw` dumps into viewable PNGs using the real game palette (the dump format has no palette of its own, to keep the in-game dump path from depending on the diff --git a/engine-patches/04-desktop-fix-critter-sprite-scale.patch b/engine-patches/04-desktop-fix-critter-sprite-scale.patch index 424b604..98e3d74 100644 --- a/engine-patches/04-desktop-fix-critter-sprite-scale.patch +++ b/engine-patches/04-desktop-fix-critter-sprite-scale.patch @@ -200,7 +200,15 @@ _g3d_bitmap_clut = (p->i & 0x00ff00) + grd_screen->ltab; _g3d_bitmap_poly = vpl; return (g3_bitmap_common(bm, p)); -@@ -185,6 +196,15 @@ +@@ -176,6 +187,7 @@ + grs_vertex *tempG1, *tempG2; + fix tempResult; + int32_t bm_w, bm_h; ++ int32_t tex_w, tex_h; + fix dx, dy; + + // MLA- these were globals, I made them locals for PPC speed, they aren't +@@ -185,6 +197,15 @@ long rm2; long rm3; @@ -216,7 +224,7 @@ #ifdef stereo_on if (_g3d_stereo & 1) { -@@ -209,9 +229,11 @@ +@@ -209,9 +230,23 @@ if ((p->codes & CC_CLIP_OVERFLOW) != 0) return (0L); @@ -225,12 +233,71 @@ - bm_h = bm->h; + // copy a few things into locals - w_override/h_override, if set (see + // _g3d_bitmap_w_override's own comment), take the place of the bitmap's -+ // own pixel dimensions here. ++ // own pixel dimensions here, for the on-screen quad's world-space size ++ // only. tex_w/tex_h stay the bitmap's own real dimensions always: the ++ // quad's u/v bounds (below) are pixel coordinates into bm's own actual ++ // pixel data - the software renderer's scanline texture mapper ++ // (h_map(), gentm.c) interpolates and samples them literally, so ++ // overriding them to a smaller size here would make it only ever read ++ // part of a bitmap bigger than that override (such as an upscaled mod's ++ // replacement art), cropping the rest. OpenGL doesn't read u/v from here ++ // at all (opengl_bitmap() always maps the whole real texture over the ++ // quad), which is why this only ever showed up in the software ++ // renderer. + bm_w = w_override ? w_override : bm->w; + bm_h = h_override ? h_override : bm->h; ++ tex_w = bm->w; ++ tex_h = bm->h; tempL = p->gZ; // mov eax,[edi].z tempL <<= 8; // sal eax,8 ;should be +@@ -299,7 +334,7 @@ + tempG1->y = tempL2; + + // vpl[1][2] = bitmap.w-1 +- tempG1->u = (bm_w - 1) << 16; ++ tempG1->u = (tex_w - 1) << 16; + + // vpl[1][3] = 0 + tempG1->v = 0; +@@ -326,10 +361,10 @@ + tempG1->y = tempL2; + + // vpl[2][2] = bitmap.w-1 +- tempG1->u = (bm_w - 1) << 16; ++ tempG1->u = (tex_w - 1) << 16; + + // vpl[2][3] = bitmap.h-1 +- tempG1->v = (bm_h - 1) << 16; ++ tempG1->v = (tex_h - 1) << 16; + + // vpl[2][4] undefined + +@@ -356,7 +391,7 @@ + tempG1->u = 0; + + // vpl[3][3] = bitmap.h +- tempG1->v = bm_h << 16; ++ tempG1->v = tex_h << 16; + + // vpl[3][4] undefined + +@@ -380,11 +415,13 @@ + if (dy > dx) + dx = dy; // get max value in dx + +- // shift down because bm_w isn't fixed, and we only double when twice the size ++ // shift down because tex_w isn't fixed, and we only double when twice the size + dx >>= 0x11; + + // make sure doubled bitmap won't overflow unpack buffer (that's roughly 1/5 of 64k) +- if ((bm_w <= dx) && (bm_w * bm_h <= 12000)) { ++ // - against the bitmap's own real size (tex_w/tex_h), same as the u/v this doubles ++ // just below: both operate on bm's actual pixel data, not the on-screen override. ++ if ((tex_w <= dx) && (tex_w * tex_h <= 12000)) { + // first fix all u's and v's + for (tempL = 0; tempL < 4; tempL++) { + _g3d_bitmap_poly[tempL]->u <<= 1; --- a/src/Libraries/3D/Source/3d.h +++ b/src/Libraries/3D/Source/3d.h @@ -720,7 +720,8 @@