mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
D195: restore G_TEXTURE_GEN envmap UV generation -- fixes black reflective materials game-wide
GE uses real N64 RSP environment-mapping (G_TEXTURE_GEN) for every shiny/reflective material: gold/silver weapon skins, chrome trim, and Control's reflective console glass. An earlier session (D72.1) removed the PD-inherited envmap UV-generation block wholesale from gfx_sp_vertex to fix the Rareware logo, reasoning "GE never generates texture coordinates from vertex normals" -- true for the logo (lit, but doesn't set G_TEXTURE_GEN) but false in general. Every envmap draw was left sampling whatever raw tc[] happened to be authored (near 0), landing on a fixed corner texel of the reflection texture regardless of view angle -- reading as solid black for a typically dark-edged reflection map. Restored the envmap UV generation (ported from the reference Perfect Dark PC port's already-correct implementation), gated specifically on rsp.geometry_mode & G_TEXTURE_GEN so D72.1's actual fix is untouched (the logo never sets this bit) while every other envmap draw gets real reflection coordinates back. User-verified live: console glass and gold/silver guns are now visibly reflective instead of flat black. The remaining "not translucent" complaint on the glass is confirmed NOT a bug -- it was never see-through on the original N64 either (reflective by design, not transparent). Full 21-level sweep crash-free post-fix. Claude-Session: https://claude.ai/code/session_016DQUKGLZxFtPy52MkKXXog
This commit is contained in:
1 parent
1f7f804733
commit
a088bb51e7
4 files changed
+70
-15
No files matched your search
@@ -21,7 +21,7 @@ D190,?,"Linux -level_45 SIGSEGV at load in add_ptr_to_objective (objective.c:64)
|
||||
D191,FIXED,Windows -O2 release-bundle crash:,FIXED + PLAYTEST-VERIFIED (M-79).
|
||||
D193,FIXED,NPC AI locomotion is too slow —,FIXED + user-playtest-verified (M-81) —
|
||||
D194,OPEN,Mouse input needs another tuning pass —,"OPEN — root cause and fix direction known, not implemented."
|
||||
D195,OPEN,"Transparency / alpha surfaces broken on Control (M-49, user playtest) —","OPEN — not investigated (M-49), needs a capture"
|
||||
D195,FIXED,"Transparency / alpha surfaces broken on Control (M-49, user playtest) —","FIXED (port/fast3d/gfx_pc.cpp, gfx_sp_vertex) —"
|
||||
D197,OPEN,"Character face/head textures wrap around the head, seen on Silo (M-49, user playtest).","OPEN — not investigated (M-49), needs a capture"
|
||||
D198,FIXED,alAdpcmPull/_decodeChunk used K0_TO_PHYS (an unconditional & 0x1FFFFFFF mask) on 64-bit heap pointers (src/libultra/audio/load.c).,FIXED (src/libultra/audio/load.c) —
|
||||
D199,PARTIAL,Phase-3 software audio mixer:,"LANDED (port/src/mixer.c, port/include/mixer.h, include/PR/abi.h) —"
|
||||
|
||||
|
@@ -394,7 +394,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
|
||||
| D191 | **Windows `-O2` release-bundle crash: SIGSEGV in `modelGetNodeRwData` (`src/game/model.c:478`) — the `root->Parent` node-tree walk (M-49, user playtests of the v0.1.0 win64 bundle).** **Two instances, identical fault PC `0x1400801b1`:** (1) fresh save, killed the first guard right after escaping the cell in Bunker ii; (2) Statue (`-level_49`?), right after the Trevelyan meeting cutscene. `ge007.crash.log` (auto-dump; symbols intact in the shipped exe, `addr2line` resolves): PC = `modelGetNodeRwData` at `model.c:478` — the `switch (root->Opcode & 0xff)`, i.e. `root` (a `ModelNode *`) is bad; `addr2line` puts it in the inlined self-recursion at `:532`, so the `while (root->Parent) { root = root->Parent; … }` walk (`model.c:527-540`) handed a garbage node into the recursive call. Instance 1: `Rdx = 0x70267bc000000000` — a `~0x70266xxx` heap address in the **high 32 bits, zero low half** = 32-bit pointer field read as 64-bit (or a byte-swapped `u32` pair). Instance 2: `Rdx = 0`, FAULT ADDR `0x0` — `root` (or `root->Parent`) resolved to NULL. Both: `Rcx=Rdi` = the `~0x7008xxxx`/`~0x7007xxxx` object ptr, `R8 = 0x1d` (29). So the `Parent` chain in some model contains a **truncated or NULL link** — pointer-width / byte-swap family (D3x, D119 `weapons_held[]->chr` type-pun, D122/D126/D132 propDef-stride). NOT weapon-fire-specific — common factor is `modelGetNodeRwData` running on a character/cutscene model whose node tree has a bad `Parent`. Crash logs saved: `scratchpad/crashlogs/`. **Repro aid:** shipped exe has full symbols; `debug-crash.ps1` in the bundle dir runs it under gdb, `GE_D51=1` → `d52rw.log` rwdata-pool trace. The crash.c EBP backtrace breaks immediately (`#01: 0x1d`) — need a real `bt` + `p *root` + `p *Objinst` at the fault. Not yet reproduced under a debugger; no fix. | FIXED + PLAYTEST-VERIFIED (M-79). User replayed Bunker ii (first-guard kill) and Statue (post-Trevelyan cutscene) on the fix build — **both no longer crash** → 21/21 solo levels crash-free (v0.2.0 gate met; Cradle's remaining issue is D193, not a crash). Build clean; bunker1 golden framediff 3/3. **Real gdb backtrace (M-79):** the fault is NOT in the `root->Parent` walk — `root` arrives *already* garbage (`0x70267a9000000000` = a half-word-swapped `u32` `0x70267a90`) from frame #1 `bondviewSelectCuff(model=0x70076808, header=0x700763a0, switchindex=29)`, called from `gunUpdateAndFire(GUNRIGHT)` on the frame the player fires. `bondviewSelectCuff` byte-indexes `header->Switches` (a `ModelNode *` array — **8-byte stride on PC**, widened per D43/D45) with `offset = switchindex << 2` (N64 4-byte stride), so every `base[N]` deref lands mid-slot and yields a misaligned half-pointer that is non-NULL by luck → `modelGetNodeRwData(root=garbage)`. **Exactly the D140 bug class** — D140 fixed the identical pattern in `gunfire.c:1740`; this call site was missed. Fix: PORT-only `offset = switchindex * (s32) sizeof(ModelNode *)`; the `base = (u8*)switches + offset` math and the `#else` N64 line are unchanged. ABI/pointer-width only (D3x class), no game-logic change. The Statue instance (`Rdx=0`, NULL fault addr) is the same site landing on a slot whose bytes read as 0. |
|
||||
| D193 | **NPC AI locomotion is too slow — characters don't move to their destinations at the original game's rate (M-49, user playtest; clarified after the FULL 21-level playthrough — "the biggest overall flaw").** Not (primarily) an animation-playback bug — the *travel speed* of scripted/AI movement is wrong: guards, named NPCs (Trevelyan, Ourumov) and friendlies (Natalya) all take noticeably longer to run/walk to their goal positions than on N64. General, not chr-specific. **Leading hypothesis — the D117/D134/D155/D156 wall-clock-timing family:** `chr.c` / `chrai.c` integrate movement as `pos += speed * g_GlobalTimerDelta` and step sim loops `while (i < g_ClockTimer)` / `-= g_ClockTimer` (`chr.c:1437,1446,1491-1502,1862,2359,2569`; `chrai.c:76`). On the port `osGetCount()` is free-running wall-clock (D117), `g_GlobalTimerDelta`/`g_ClockTimer` are derived from it, and D155's `FRAMETIMING_PORT_MAX_CATCHUP = 6` clamps `nextFrameTime`. If the effective per-second sum of `g_GlobalTimerDelta` (or the `g_ClockTimer` tick count) runs low — quantization, the catch-up clamp biting during normal play, or a units mismatch (`g_JP_GlobalTimerDelta` vs `g_GlobalTimerDelta`, `chr.c:1435/1437`) — every delta-scaled AI motion integrates slow while **player** movement (input-driven per frame) still feels right, which is exactly the reported split. Related: **D170** (Ourumov/Trevelyan "flee" looks wrong — flagged there as possibly the same locomotion-speed issue rather than an AI divergence). Also possible: `chrGoToBond/Chr(SPEED_RUN/WALK/SPRINT)` speed enum → path-follow step, or pathfinding advancing ≤1 waypoint per frame-quantized tick. **Instances seen (M-49 full playthrough):** (a) Facility — the post-scripted-scene beat with Ourumov + the soldier squad: the soldiers **walk in super slowly and take a long time to path into firing positions** before they start shooting (this is regular squad combat AI, not a named-NPC script — good evidence the slowdown is general delta-scaled `chr` locomotion / path-following, not one ailist); (b) the named-NPC flee behaviour on Silo (Ourumov) and Cradle (Trevelyan) — see D170; (c) **Natalya (the escort NPC) always walks really slow** — on every escort level (Bunker ii, Statue, Control, Archives) she trails far behind, forcing the player to wait. Escort follow-AI is the same `chrGoToChr`/path-follow path as (a). **WORST CASE — Cradle (`-level_51`) is "bugged out completely" due to Trevelyan's behaviour (M-49).** The final level is Trevelyan-fleeing-and-scripted end to end (he runs the antenna cradle, climbs, drops to the cradle floor, the helicopter beat); if his locomotion timing / path-following is off he never hits the trigger points and the level's scripted progression stalls or goes haywire. This makes D193 effectively a **can't-properly-finish-the-game** bug, not just cosmetic sluggishness. **Next:** log `g_GlobalTimerDelta` + `g_ClockTimer` per frame during normal play and check the 1-second sum vs the N64's ~60; time a guard's run across a known distance vs reference footage; then watch Trevelyan's chr state / current AI command on Cradle. | **FIXED + user-playtest-verified (M-81) — root cause is D209**: `act_ubytes.padding[45]`, a raw-byte union alias for `act_gopos.unk59` (the SPEED tier), lands on offset 45 which is `unk59` on N64 but is byte 5 of `waypoints[1]` at 64-bit — always `0x00`. So the locomotion-animation selector `get_sound_at_range()` always received tier 0 and bound `ANIM_DATA_walking`/`walking_unarmed` for **every AI chr in the game**, and since travel is anim-root-motion driven they all moved at walk pace no matter what the ailist commanded. User verified on Cradle: Trevelyan and the guards run. Full elimination trail below — **steady-state timing keystone MEASURED and the "sim sums low" hypothesis is CONTRADICTED (M-80).** Added a `GE_D193=1` per-wall-second probe in `waitForNextFrame()` (`src/game/frametiming.c`, `#ifdef PORT`, env-gated): logs render frames, sim ticks delivered (= sum of the `deltaFrames` fed to `updateFrameCounters` = sum of `g_ClockTimer`), catch-up clamp hits, and max raw pre-clamp delta. Built ntsc-final, ran `-level_09 / _20 / _27 / _34` idle-after-load, ~20 s each. **Every level, rock-steady: render = 30.0 fps, sim = 60.0 ticks/s, clamped = 0, maxraw = 2.** i.e. each rendered frame carries `g_ClockTimer = 2` and the per-second sum is *exactly* the N64's ~60 — the wall-clock timer is NOT running low in steady state, and `FRAMETIMING_PORT_MAX_CATCHUP` never bites in normal play. VSync on/off made no difference (still 30/60). So the B1 premise ("`g_GlobalTimerDelta`/`g_ClockTimer` sum low over a real second") is wrong for the common case; D193 must come from elsewhere. **Residual quantization risk (not yet observed):** `nextFrameTime = (dt_counts + 387937) / 775875` is integer-rounded per frame with no remainder carry, so a scene that renders at a *steady* rate between 30 and 60 that is not an integeLine truncated
|
||||
| D194 | **Mouse input needs another tuning pass — RMB aim over-sensitive + sensitivity couples to frame/sim rate (M-49, user playtest).** Two parts. **(a) Aim mode (RMB) is near bang-bang, not proportional** (`port/src/input.c:697-707`): any per-poll delta past `AIM_MOVE_THRESH` emits stick `m = clamp(61 + |Δ|·(MouseAimSpeed/100)·AIM_GAIN, 61, 60+AimBand)` = `[61,80]` — with the M-29 defaults (`MouseAimSpeed=16`, `AIM_GAIN=4.0`, `AimBand=20`) `m` is already 61 (fast) at ~1.5 px/poll and saturates at 80 by ~30 px/poll, so the usable proportional band is tiny and sits near the top of GE's aim range → "way too sensitive," turn rate barely tracks how fast you actually move the mouse. Needs a real response curve (low floor, wider proportional band, maybe a gamma), not a threshold+clamp. **(b) Overall sensitivity correlates with movement/frame rate** (user's words: "correlated with movement speed, does not feel good"): `input.c` emits raw stick counts **per controller poll** with no `g_GlobalTimerDelta` / polls-per-frame normalization (hipfire yaw `sx += edx·(MouseTurnSpeed/100)·6.0` at `:709`; aim as above). GE integrates the stick scaled by its sim delta, and the port's poll cadence vs render/sim cadence drifts with scene load — so effective sensitivity shifts when the framerate does (which reads as "when I'm moving"). Fix direction: accumulate mouse delta and convert to stick using a fixed reference dt (or feed the game a dt-consistent value), so a given hand motion = a given view rotation regardless of fps. Lineage: D118a (hipfire pitch digital vs analog), D165 (menu pointer), D166 (hipfire pitch pulses), D180-B3 (`MouseAimSpeed` 25→16). New adjacent QoL ask, root-caused → **D223** (mouse-wheel direction should reverse weapon cycle direction, currently both directions cycle forward). Config knobs exist (`Input.MouseAimSpeed/AimBand/MouseTurnSpeed/HipfirePitchSpeed`) but the *shape* of the mapping is the problem. **M-87 design-goal note (user):** overall mouse feel should move toward **Quake Remaster / the Perfect Dark PC port** — raw, direct, responsive 1:1 mouse-look — while explicitly **not losing GE's original aiming nuance** (the analog-stick aim-mode vs digital-hipfire split this whole D118/D194 lineage exists to preserve). Reframes the fix direction from "just widen the proportional band" to: normalize by a fixed reference dt first (part (b), the frame-rate coupling — this alone should fix most of "feels unresponsive"), then re-derive the aim-mode response curve, and treat raw hipfire mouse-look (PD/Quake-style, bypassing the N64 digital-stick emulation entirely when the player is *not* in aim mode) as a real design option to evaluate against "keep GE's aim-mode feel," not assume the N64 stick-emulation path is mandatory everywhere. **M-87 — found it: GE already has exactly this control scheme, unused by the port.** User asked why vertical mouse feels less responsive than horizontal; investigation found the port forces `controldef = CONTROLLER_CONFIG_HONEY` (1.1), under which hipfire pitch is genuinely digital-only (C-up/C-down) while yaw is analog — an intentional N64-era asymmetry (D166), not a port bug, but avoidable. **`CONTROLLER_CONFIG_SOLITARE` (1.2) / `GOODNIGHT` (1.4)** — user's own recollection, verified in code — redirect forward/back/strafe-L/R onto digital C-buttons/D-pad (`bondview2.c:5195-5253`, `moveData.digitalStep{Forward,Back,Left,Right}`), which frees the **entire primary analog stick** for continuous, unclamped pitch **and** yaw: `moveData.canNaturalPitch`/`canNaturalTurn` (set `!insightaimmode`, i.e. active outside aim mode, not gated behind holding aim) drive `analogPitch/70.0f` / `analogTurn/70.0f` directly (`bondview2.c:5950`/`6008`) — no threshold, no digital step, no `[61,80]`-style clamp band (that clamp is specific to the *aim-mode* zoom-turn path, a separate codepath, `insightaimmode && stick_y>60`). `analogPitch`/`analogTurn` default to the primary stick's raw X/Y for every 1.x scheme (`bondview2.c:4881-4882`) unless a 2.x dual-controller scheme overrides them from a second controller. **This is architecturally identical to a modern FPS mouselook**: continuous analog look on the stick, discrete step movement on buttons — a legitimate, unmodified original GE control scheme, not a port invention. Recommended fix: force the port's `controldef` to SOLITAIRE or GOODNIGHT instead of HONEY, map WASD to the digital step-movement buttons (already close to how keyboard hipfire movement works today) and mouse X/Y directly to the primary stick's continuous X/Y — this should resolve the vertical-vs-horizontal asymmetry completely (no more digital-pulse emulation needed at all, D166 becomes largely moot) and likely fixes much of "feels unresponsive" too, since it's direct continuous stick input rather than any thresholded emulation. The frame-rate-coupling issue (part b) is orthogonal and still needs fixing regardless of scheme. Cross-ref D166, D22Line truncated
|
||||
| D195 | **Transparency / alpha surfaces broken on Control (M-49, user playtest)** — Natalya's console room, the glass partitions + translucent projected wall displays. Failure mode not yet specified by the reporter (no `GE_PCDUMP` capture taken). Likely a fast3d render-mode / alpha-blend path defect, not a decode one — neighbours D161 (CI8/LUT decode), D172, D176(b) (both blend-adjacent) but those are texture-format bugs, this reads as a blend-state one; check D128 portal adjacency in case the glass geometry is portal-culled wrong instead. Logged in `GRAPHICS-BACKLOG.md`. **Next:** `GE_PCDUMP` capture on Control near the console room, compare RDP render-mode words against N64 `gmain.s` for the glass/display materials. | OPEN — not investigated (M-49), needs a capture |
|
||||
| D195 | **Transparency / alpha surfaces broken on Control (M-49, user playtest)** — Natalya's console room, the glass partitions + translucent projected wall displays. Originally reported as a blend-state defect. Reinvestigated this session (M-116) after the user reported the glass rendering **solid black** — traced to a real, separate, much bigger bug: `G_TEXTURE_GEN` (RSP environment-mapped texture coordinates, used by shiny/reflective materials game-wide — gold/silver weapon skins, chrome trim, and this glass) was never implemented in fast3d. D72.1 (an earlier session) removed the PD-inherited envmap UV-generation block wholesale to fix the Rareware logo, reasoning "GE never generates texture coordinates from vertex normals" — true for the logo (lit, but does not set `G_TEXTURE_GEN`) but false in general: every envmap draw was left sampling whatever raw `tc[]` happened to be authored (near 0), landing on a fixed corner texel of the reflection map every frame regardless of view angle — for a typically dark-edged reflection texture, a solid near-black surface. | **FIXED (`port/fast3d/gfx_pc.cpp`, `gfx_sp_vertex`) — restored envmap UV generation gated on the real `G_TEXTURE_GEN` bit** (ported from the reference Perfect Dark PC port's already-correct implementation), so D72.1's actual fix is untouched (the logo never sets this bit) while every other envmap draw gets real reflection coordinates. **User-verified live:** console glass and shiny gold/silver guns are now visibly reflective instead of flat black. Confirmed crash-free on a full 21-level sweep. **The remaining "not translucent" complaint is NOT a bug**: user confirmed after seeing the fixed reflective glass that it was never see-through on the original N64 either — it's a reflective (mirror-like) surface by design, not transparent glass. Traced (before the user's confirmation settled it) to the material's own combine formula, `alpha = (TEXEL0/1 LOD-crossfade alpha) * SHADE_alpha + PRIMITIVE_alpha` — an additive constant term that saturates alpha near 1.0 by design, matching "reflective, mostly opaque" intent, not a blend-state defect. **Closed.** Cross-ref: this same envmap fix likely also explains part of D228 (white NPC-weapon patches) if any of those are chrome/shiny weapon variants — worth checking before further D228 investigation. |
|
||||
| D197 | **Character face/head textures wrap around the head, seen on Silo (M-49, user playtest).** Reads as `G_TX_CLAMP` not honoured (or a non-power-of-two wrap period) on a head/face texture tile — the texture repeats around the mesh instead of clamping at the UV edge. Not yet isolated to a specific character or texture; no `GE_PCDUMP`/`GE_TEXDUMP` capture taken. Likely fast3d texture-tile setup (clamp/wrap mode bits from the tile descriptor not round-tripped) rather than a data bug, given it's cosmetic and geometry-independent. **Next:** `GE_TEXDUMP` the offending head texture's tile descriptor on Silo, diff clamp/mirror bits against `gmain.s`'s expectations for that material. | OPEN — not investigated (M-49), needs a capture |
|
||||
| D198 | **`alAdpcmPull`/`_decodeChunk` used `K0_TO_PHYS` (an unconditional `& 0x1FFFFFFF` mask) on 64-bit heap pointers (`src/libultra/audio/load.c`).** `K0_TO_PHYS` is the N64 kseg0→physical mask, valid only because N64 pointers fit in 32 bits; on PC's 64-bit heap it truncates any pointer whose value exceeds 512MB, corrupting the `aLoadADPCM`/`aSetLoop`/`aADPCMdec` DRAM addresses at 3 call sites (`load.c:71`, `:468`, `:474`). Every sibling call site in `env.c`/`resample.c`/`reverb.c` already resolves state pointers via `osVirtualToPhysical` (the port shim, `port/src/libultra.c`), which is bit-identical to `K0_TO_PHYS` for real N64 kseg0 addresses but correctly PC-shimmed. **Fix (ABI-layer, `#ifdef`-free — same call resolves correctly on both platforms):** replace the 3 `K0_TO_PHYS(...)` calls with `osVirtualToPhysical(...)`. No behavior change on N64 (identical result for kseg0 pointers); fixes PC. Surfaced while building the Phase-3 software mixer (D199) — the acmd list these opcodes feed now actually executes on PC instead of being ignored. | FIXED (`src/libultra/audio/load.c`) — part of the Phase-3 mixer landing, not yet build/runtime-verified in isolation |
|
||||
| D199 | **Phase-3 software audio mixer: macro-swap `aXxx` opcode execution + reverse-engineered `aSetBuffer` persistent-context semantics (`port/src/mixer.c`, `port/include/mixer.h`, `include/PR/abi.h`).** The RSP audio ucode (`aspMain`) that would normally execute GE's acmd list never runs on PC (`port/src/ucode.c`); adapting the Perfect Dark PC port's macro-swap trick (`docs/dev/AUDIO-PLAN.md`), `include/PR/abi.h` now `#include`s `port/include/mixer.h` under `#ifdef PORT`, which redefines every `aXxx` macro to call an `Impl` function in `mixer.c` immediately against a small software "DMEM" scratch buffer, instead of packing RSP command words for later execution. GE uses the classic IDO libaudio ABI (verified against every call site in `src/libultra/audio/*.c` + `src/libultrare/audio/{env,reverb}.c`), not PD's differently-shaped "naudio New" ABI — DSP math (ADPCM decode, linear resample, linear envelope mix) is the same Nintendo/SGI ucode PD's `mixer.c` already ported; only the ABI/addressing layer is GE-specific and new here. Several opcodes (`aADPCMdec`, `aResample`, `aEnvMixer`, `aLoadBuffer`, `aSaveBuffer`) take only a state/DRAM pointer — their DMEM source/dest addresses and byte counts come from the most recent `aSetBuffer` call(s), which there is no RSP disassembly to check against; the persistent-context shape (`sCtx.{in,out,count,dryR,wetL,wetR}`) was reconstructed purely from reading every `aSetBuffer`+opcode call-site pair across `resample.c`/`load.c`/`reverb.c`/`mainbus.c`/`save.c`/`env.c` (see the field comments on `sCtx` in `mixer.c:52-82`). `aSegmentImpl` is a no-op — GE's audio DRAM addresses are already resolved via `osVirtualToPhysical` before reaching the mixer (see D198), so segment/base never factor into DMEM addressing here. **Risk:** the `aSetBuffer` context reconstruction is an inference, not a verified spec — if a call site this session didn't audit sets the context differently, audio for that path will mix from the wrong DMEM offsets. **Verified:** `-level_09` now runs full music + SFX 120s+ crash-free (post-D200 fix); no cross-level or opcode-level audio-correctness verification done yet (no golden-audio comparison tooling exists). | LANDED (`port/src/mixer.c`, `port/include/mixer.h`, `include/PR/abi.h`) — functional on `-level_09`, context-inference risk noted, needs a wider level sweep |
|
||||
|
||||
@@ -1125,4 +1125,23 @@ and turns every such overrun into a fatal `*** stack smashing detected ***`
|
||||
geometry (viewport height/width, screen offset, etc.) back to its decomp
|
||||
origin — a visually surprising but *exactly reproduced* N64 quirk isn't a
|
||||
port defect, and the fix is to close the finding, not to write code.
|
||||
- **D195 — a targeted decomp-side fix (D72) blanket-disabled a whole PC-side
|
||||
feature instead of gating it on the GBI bit that actually distinguishes the
|
||||
broken case from the working one.** `gfx_sp_vertex()`'s PD-inherited
|
||||
envmap (`G_TEXTURE_GEN`) UV-generation block was removed wholesale to fix
|
||||
the Rareware logo (D72.1), on the reasoning "GE never generates texture
|
||||
coordinates from vertex normals." That's true for the logo specifically
|
||||
(lit, but doesn't set `G_TEXTURE_GEN`) but false for the engine in
|
||||
general: GE uses real RSP envmap generation for every shiny/reflective
|
||||
material (gold/silver weapon skins, chrome trim, reflective glass), all of
|
||||
which DO set the bit. Deleting the feature instead of gating it on
|
||||
`rsp.geometry_mode & G_TEXTURE_GEN` (which the reference PD port already
|
||||
does correctly) silently broke every one of those materials game-wide —
|
||||
they'd sample a fixed, usually-dark corner texel forever, reading as solid
|
||||
black. **Lesson:** when a PD-inherited block causes a wrong result for one
|
||||
specific draw, check what GBI bit *that draw* fails to gate on before
|
||||
assuming the whole mechanism doesn't apply to GE — a feature real N64
|
||||
hardware supports and GE's own assets exercise is not "PD-only" just
|
||||
because the first investigation happened to hit it on a draw that
|
||||
shouldn't have taken that path. §F **D195**.
|
||||
§F **D247**.
|
||||
+49
-13
@@ -1390,19 +1390,55 @@ static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx* verti
|
||||
d->color.g = g > 255 ? 255 : g;
|
||||
d->color.b = b > 255 ? 255 : b;
|
||||
|
||||
/* D72 (docs/internals.md): unlike the PD port, GE never
|
||||
* generates texture coordinates from vertex normals. This PD-
|
||||
* inherited block overwrote the authored tc[] UVs on every
|
||||
* lit+textured surface (lookat_enabled defaults to true here),
|
||||
* which smeared the Rareware-logo quads into a diagonal gold
|
||||
* wedge: their corner UVs (0x0010..0x03F0 = full 32x32 coverage)
|
||||
* were replaced by normal-derived values. GE's ground truth:
|
||||
* every DL that could env-map sets gsDPSetTextureLUT(G_TT_NONE),
|
||||
* stage rendering always has a camera lookat yet stage textures
|
||||
* are surface-fixed on hardware, and the logo vertex batches
|
||||
* carry authored atlas-slice UVs. So U/V stay as computed from
|
||||
* tc[] above. (gSPLookAt state is still tracked — it may matter
|
||||
* for lighting fidelity later.) */
|
||||
/* D195/D72 correction: GE DOES use RSP-generated (environment-
|
||||
* mapped) texture coordinates -- shiny gold/silver weapon skins
|
||||
* and Control's reflective console glass all set G_TEXTURE_GEN,
|
||||
* the real GBI bit that gates this on real hardware. D72.1
|
||||
* originally removed this whole PD-inherited block to fix the
|
||||
* Rareware logo (a lit surface that does NOT set G_TEXTURE_GEN,
|
||||
* so it was never supposed to hit this path in the first place)
|
||||
* but over-corrected: it deleted the feature instead of gating
|
||||
* it on the bit that actually distinguishes the two cases. With
|
||||
* no envmap UV generation, every G_TEXTURE_GEN draw sampled its
|
||||
* texture at whatever raw tc[] happened to be authored (usually
|
||||
* ~0), landing on the same corner texel every frame regardless
|
||||
* of view angle -- for a typical dark-edged reflection map, a
|
||||
* solid near-black surface instead of a shiny/reflective one.
|
||||
* Gating on G_TEXTURE_GEN preserves D72.1's actual fix (the logo
|
||||
* never sets this bit, so it's unaffected) while restoring the
|
||||
* envmap effect for the draws that do. */
|
||||
if (rsp.geometry_mode & G_TEXTURE_GEN) {
|
||||
float dotx = 0, doty = 0;
|
||||
if (rsp.lookat_enabled) {
|
||||
dotx += vcn->x * rsp.current_lookat_coeffs[0][0];
|
||||
dotx += vcn->y * rsp.current_lookat_coeffs[0][1];
|
||||
dotx += vcn->z * rsp.current_lookat_coeffs[0][2];
|
||||
doty += vcn->x * rsp.current_lookat_coeffs[1][0];
|
||||
doty += vcn->y * rsp.current_lookat_coeffs[1][1];
|
||||
doty += vcn->z * rsp.current_lookat_coeffs[1][2];
|
||||
dotx /= 127.0f;
|
||||
doty /= 127.0f;
|
||||
} else {
|
||||
float tvcn[3];
|
||||
calculate_normal_dir(vcn, tvcn);
|
||||
dotx = tvcn[0];
|
||||
doty = tvcn[1];
|
||||
}
|
||||
|
||||
dotx = clampf(dotx, -1.0f, 1.0f);
|
||||
doty = clampf(doty, -1.0f, 1.0f);
|
||||
|
||||
if (rsp.geometry_mode & G_TEXTURE_GEN_LINEAR) {
|
||||
dotx = acosf(-dotx) / 4.0f;
|
||||
doty = acosf(-doty) / 4.0f;
|
||||
} else {
|
||||
dotx = (dotx + 1.0f) / 4.0f;
|
||||
doty = (doty + 1.0f) / 4.0f;
|
||||
}
|
||||
|
||||
U = (int32_t)(dotx * rsp.texture_scaling_factor.s);
|
||||
V = (int32_t)(doty * rsp.texture_scaling_factor.t);
|
||||
}
|
||||
} else {
|
||||
d->color.r = vcn->r;
|
||||
d->color.g = vcn->g;
|
||||
|
||||
Reference in new issue
Block a user