forked from Ports/goldeneye-pc-port
D120: emit the opcode-0x18 PointUsage[] s16 chain (was zero-filled)
d43_emit.py reserved the PointUsage region (add_region) during layout but the emit pass never wrote it -> all zeros -> chr.c's blood-decal walk 'index = PointUsage[index]' cycled 0->0 forever (capped by the #ifdef PORT guard at chr.c:3328, so decals were missing/wrong, not a hang). Now byteswaps each s16 index (numVertices entries, negative-terminated, no remap). Full regen chain re-run, round-trip validation passes. The chr.c guard stays as a safety net (SMALL-FIXES B3). Also: retract RC4 (palette off-by-one) in TEXTURE-GLITCH-ANALYSIS -- the current RGBA5551 decode is correct; the doc's 'spec' was ARGB1555.
This commit is contained in:
1 parent
d0b17f1788
commit
742f2f820e
2 files changed
+30
-8
No files matched your search
@@ -63,19 +63,24 @@
|
||||
`G_TX_WRAP == 0` (`include/PR/gbi.h:391`) — dead code (already parked in
|
||||
`docs/GRAPHICS-BACKLOG.md`).
|
||||
|
||||
### RC4 — Palette off-by-one (subtle global hue shift)
|
||||
### RC4 — Palette off-by-one — **RETRACTED (M-30): the current code is correct.**
|
||||
|
||||
`port/fast3d/gfx_pc.cpp:835`:
|
||||
The "N64 RGBA16 spec" line below is **ARGB1555**, which is *not* what the N64 uses.
|
||||
N64 `G_IM_FMT_RGBA` / `G_IM_SIZ_16b` is **RGBA5551**: `RRRRR GGGGG BBBBB A`
|
||||
(R = bits 15-11, G = 10-6, B = 5-1, A = bit 0).
|
||||
|
||||
```c
|
||||
// fast3d (current): r = v>>11; g = (v>>6)&0x1f; b = (v>>1)&0x1f; a = v&1
|
||||
// N64 RGBA16 spec: r = (v>>10)&0x1f; g = (v>>5)&0x1f; b = v&0x1f; a = v>>15
|
||||
// fast3d (current, CORRECT): r = v>>11; g = (v>>6)&0x1f; b = (v>>1)&0x1f; a = v&1
|
||||
// the "spec" line here was wrong: r = (v>>10)&0x1f; g = (v>>5)&0x1f; b = v&0x1f; a = v>>15
|
||||
```
|
||||
|
||||
Channels are read one bit off (alpha treated as LSB). Grays (r≈g≈b) survive nearly intact —
|
||||
which is why BUNKER1 looks "mostly right" (it's gray/brown) — but saturated hues shift.
|
||||
Format mapping itself was verified correct: `TEXFORMAT_RGBA16_CI8/CI4` → `G_TT_RGBA16`,
|
||||
`IA16_CI8/CI4` → `G_TT_IA16` (`src/game/image.c:71–104`).
|
||||
Both `palette_to_rgba32` (`gfx_pc.cpp:~835`) and `import_texture_rgba16`
|
||||
(`~651`) use the same RGBA5551 decode and agree with each other. The palette is
|
||||
`PD_BE16`-swapped at load (`gfx_dp_load_tlut`), so `palentry` is already
|
||||
correctly ordered. **Applying the "fix" would rotate every channel and move
|
||||
alpha to the wrong bit — do not do it.** If a hue problem is ever confirmed
|
||||
in-game, it is upstream (TLUT byte order at the converter, or a wrong
|
||||
`rdp.palette_fmt`), not this function.
|
||||
|
||||
## 3. Depot-specific analysis ("why blue ground, only there?")
|
||||
|
||||
|
||||
@@ -341,6 +341,7 @@ def process(name):
|
||||
vtx_regions.append((cvo, ncv)); dstpos = add_region(cvo, 16 * ncv, dstpos)
|
||||
if nv and puo:
|
||||
dstpos = add_region(puo, 2 * nv, dstpos)
|
||||
op24_pointusage[puo] = nv
|
||||
elif op == 22:
|
||||
nv = struct.unpack_from(">i", src, data)[0]; vo = be32o(src, data + 4)
|
||||
if nv and vo:
|
||||
@@ -629,6 +630,16 @@ def process(name):
|
||||
else:
|
||||
emit_main_vtx(vo, nv, npos)
|
||||
|
||||
# D120: PointUsage[] -- an `s16` next-index chain (numVertices entries,
|
||||
# negative-terminated). Plain byteswap, no remap.
|
||||
for puo, punv in op24_pointusage.items():
|
||||
pnpos = remap(puo)
|
||||
if pnpos is None:
|
||||
errors.append(f"{name}: PointUsage {puo:#x} not in region map")
|
||||
continue
|
||||
for k in range(punv):
|
||||
put_s16(pnpos + 2 * k, be16(src, puo + 2 * k))
|
||||
|
||||
# GDLs (pack order: unvisited first, then visit order), 16B LE slots
|
||||
for g in all_gdls:
|
||||
end = gdl_end(src, D, g)
|
||||
@@ -675,6 +686,11 @@ def process(name):
|
||||
|
||||
# collision-vertex set (filled during layout; emit pass needs it)
|
||||
op24_is_collision = {}
|
||||
# D120: opcode-0x18 PointUsage arrays -> {old_off: numVertices}. Reserved in
|
||||
# layout (add_region) but the emit pass must byteswap the s16 index chain or the
|
||||
# runtime blood-decal walk (chr.c PointUsage[index]) cycles (guarded, not fixed,
|
||||
# until now).
|
||||
op24_pointusage = {}
|
||||
|
||||
def validate(name, src, D, NS, NT, nodes, R0, placed, all_gdls, buf, D_PC,
|
||||
regions, node_newoff, rec_newoff, gdl_newoff, remap, inv_remap):
|
||||
@@ -764,6 +780,7 @@ stats["table_models"] = len(model_names)
|
||||
for name in model_names:
|
||||
# reset per-file collision set before layout (populated below)
|
||||
op24_is_collision.clear()
|
||||
op24_pointusage.clear()
|
||||
process(name)
|
||||
|
||||
if errors:
|
||||
|
||||
Reference in new issue
Block a user