D245: water tiled at the wrong size + s16 sky/water vertices; D242 closed

The IsWater 32x32 CI8 image (drawn through an RGBA16 tile, D229) was
imported with its mip chain as 32x43 and normalised as 16 wide, so the
pattern ran at 2x and wrapped every 43 rows instead of the N64's 32:
fold steps made it jump. Import the base level only and use CI8
geometry in the tri path. Sky/water fans now go through a port-only
G_FLOATVTX_EXT (float clip positions + S/T) instead of s16 Vtx, removing
the tc shift and wScale quantisation jitter. User-verified vs 1964;
golden 3/3; Win + Linux build. A/B: GE_D245_OLDVTX / GE_D245_OLDTEX.
This commit is contained in:
James Dansereau committed 2026-09-26 18:56:52 -04:00
1 parent d5f41d6a37
commit f242992360
7 files changed
+172 -5

No files matched your search

+1
View File
@@ -68,6 +68,7 @@ Two classes:
| `GE_D245_FIXEDSHIFT` | `src/game/sky.c:946` (`#ifdef PORT`) | D245 test override: pins the water tc shift `k` to the given integer (0..SKY_TC_MAX_SHIFT) instead of the computed per-vertex value, to A/B the allowShift=TRUE behavior. Off by default (unset = current behavior). | **live, test-only** (D245 OPEN) |
| `GE_D252` | `port/fast3d/gfx_pc.cpp:733` (`import_texture`, cached `s_d252`) | D252 rainbow-particle diag: one-shot per (address,size) import log of the tile state (tmem/fmt/siz/addr/size/line) for small textures so a suspect particle tile can be checked against its source bytes by hand. Remove once D252 is resolved. | **live** (D252 OPEN — non-reproducible) |
| `GE_D252POOL` | `src/game/image.c` (`texLoad`), `src/game/initmttex.c`, `port/src/gimgfixup.c` | **TEMP** (M-201): one line per new texture load (pool main/other, free bytes) + `POOL-FULL` events + stage pool range; `D252SYNC: N slots re-synced, M changed` per stage load (the D252 fix at work: M>0 from the 2nd stage on). Load-time only, no fps cost. | **TEMP** (D252 fixed; remove after release) |
| `GE_D245_OLDVTX=1` / `GE_D245_OLDTEX=1` | `src/game/sky.c` (`skyPortRenderPoly`) / `port/fast3d/gfx_pc.cpp` (CI8 import + tri-path dims) | **A/B** (M-201): restore the pre-fix s16 sky/water vertex path / the pre-fix 32x43, 16-wide water texture geometry. | **TEMP** (D245 fixed; remove post-release) |
| `GE_D207S` | `src/game/gunfire.c` (player shot-sound block, `#ifdef PORT`) | **TEMP** (M-201): one line per player shot-sound decision: `D207S t=<g_GlobalTimer> hand item rate -> H1|H2|SKIP` (SKIP = both per-hand sound handles still held). Read-only, uncapped, cheap; use instead of `GE_AUDIOTRACE` for live captures (that probe does file I/O on the audio path). | **TEMP** (D207/D241) |
| `GE_D306C="f0 f1 [wmax]"` | `port/fast3d/gfx_pc.cpp` (after `zf_note_emit`) | **TEMP** (M-201): depth-state census of every emitted triangle in frames f0..f1 (GL depth mode, geometry_mode, other_mode_l, tile-0 texture, NDC centroid, z/w range, min w); optional `wmax` keeps near geometry only. Cap 60000 lines. | **TEMP** (D306 closed; remove post-release) |
| `GE_INTROCAM=<n>` | `src/game/bondview_r.c` (`#ifdef PORT`, intro camera pick) | **TEMP/test-only**: force the level intro's first camera index (Dam truck shot = 5). The PRNG draw is still consumed. Logs `D306: intro camera index` when set (or with `GE_D306C`). | **TEMP** (useful repro tool) |
+2 -2
View File
@@ -139,9 +139,9 @@ D238,FIXED,"QoL ask (M-106, user QA):",FIXED (2026-09-26: covered by D337/D338 a
D239,CLOSED,"QoL ask (M-106, user QA):",CLOSED — executed in the v0.2.0 prep pass (port-only)…
D240,OPEN,"Player gunshot SFX timing/cadence reportedly does not match the N64 original's rate (M-106, user QA report).","OPEN — not root-caused; static call-site tracing only, no runtime verification possible in this environment."
D241,OPEN,Player gunshot SFX goes silent under some conditions —,"OPEN — not newly root-caused; high confidence this is D207 reproduced, not an independent bug."
D242,OPEN,Player-death sequence UX regression vs N64 original:,"OPEN — N64 reference obtained (2026-09-26, user on 1964:"
D242,FIXED,Player-death sequence UX regression vs N64 original:,"FIXED/CLOSED (2026-09-26, user:"
D243,FIXED,"Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject):","FIXED, live-confirmed by the user (M-189/M-190, 2026-09-18)."
D245,OPEN,IsWater water: two competing patterns with a visible seam that moves as the player looks around (mouse/aim) —,"OPEN — CONFIRMED PORT BUG (2026-09-26, user vs 1964:"
D245,FIXED,IsWater water: two competing patterns with a visible seam that moves as the player looks around (mouse/aim) —,"FIXED (M-201, 2026-09-26, user-verified vs 1964):"
D246,FIXED,"Tiny pixel strips at the left and right edges of the screen, ""like overscan"" —","FIXED (2026-09-18 session, port-only, Video.SafeAreaCrop)."
D247,FIXED,Black bars top and bottom of screen —,"FIXED (2026-09-18 session, port-only, Video.SafeAreaCrop)."
D248,FIXED,"Port runs a stable 30 FPS instead of 60 FPS game-wide (M-117, user asked to verify FPS after the D209 timing-keystone fixes).","FIXED, root-caused to a genuine pointer-width ABI bug in src/sched.c (compiled for the PC build, not excluded)."
1 # GENERATED by tools_pc/gen_findings_index.py -- do not hand-edit.
139 D239
140 D240
141 D241
142 D242
143 D243
144 D245
145 D246
146 D247
147 D248
+13 -2
View File
@@ -546,9 +546,9 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
| D239 | **QoL ask (M-106, user QA): click-to-lock vs always-grab parity.** — full `## D239` entry at file tail | CLOSED — executed in the v0.2.0 prep pass (port-only)… |
| D240 | **Player gunshot SFX timing/cadence reportedly does not match the N64 original's rate (M-106, user QA report).** — full `## D240` entry at file tail | **OPEN — not root-caused; static call-site tracing only, no runtime verification possible in this environment.** Confidence LOW-MEDIUM that this is a *new* bug distinct from the D155/D186/D193/D204/D209 timing-keystone family (all of which gate the identical `deltaFrames→speedgraphframes→g_ClockTimer→g_GlobalTimer` chain this SFX trigger consumes).… |
| D241 | **Player gunshot SFX goes silent under some conditions — reported on Bunker with the alarm sounding, "or some other factor" — but only for PP7 and AK47, Klobb…** — full `## D241` entry at file tail | **OPEN — not newly root-caused; high confidence this is D207 reproduced, not an independent bug.** Recommend folding this repro into D207 (or closing D241 as a duplicate once D207 is reopened) rather than tracking separately.… |
| D242 | **Player-death sequence UX regression vs N64 original: PC requires an LMB click to proceed to the mission-failed/file-select screen; user recalls N64 auto-plays…** — full `## D242` entry at file tail | OPEN — N64 reference obtained (2026-09-26, user on 1964: after death the game returns to mission select and shows the "killed in action" screen); PC behaviour still to validate. Earlier: OPEN — 1964 comparison owed (2026-09-26, user). Earlier: **NOT A PORT-LOGIC BUG as far as static analysis can tell.** Confidence HIGH that the LMB-click skip path is faithful original behavior (Z_TRIG-mapped, exists unmodified in `bondview2.c`), not a defect to remove.… |
| D242 | **Player-death sequence UX regression vs N64 original: PC requires an LMB click to proceed to the mission-failed/file-select screen; user recalls N64 auto-plays…** — full `## D242` entry at file tail | FIXED/CLOSED (2026-09-26, user: death -> mission failed flow works without input, matches N64). Earlier: OPEN — N64 reference obtained (2026-09-26, user on 1964: after death the game returns to mission select and shows the "killed in action" screen); PC behaviour still to validate. Earlier: OPEN — 1964 comparison owed (2026-09-26, user). Earlier: **NOT A PORT-LOGIC BUG as far as static analysis can tell.** Confidence HIGH that the LMB-click skip path is faithful original behavior (Z_TRIG-mapped, exists unmodified in `bondview2.c`), not a defect to remove.… |
| D243 | **Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject): the camera violently shakes/"spazzes" during each of the scripted camera-angle…** — full `## D243` entry at file tail | **FIXED, live-confirmed by the user (M-189/M-190, 2026-09-18).** Root cause found: `bondview2.c:598-599`'s manual buffer-layout math for Bond's third-person cutscene body model reserved space using a hardcoded 32-bit `sizeof(Model)` literal (`0xfb`) that never accounted for pointer widening — real 64-bit `sizeof(Model)` is 232 bytes, 44 more than reserved, so the animation-data buffer placed right after it overlapped the model's own `playspeed`/`animrate`/`unkac`/`unkb0`/`unkb4` fields, corrupting them (this is what M-183/M-185's clamps were band-aiding). Fixed by computing the real `sizeof(Model)` instead of the stale literal, matching the identical idiom already used one line above for `ModelFileHeader`. **M-190 follow-up:** M-170's camera-shake freeze was replaced with a filter re-seed-on-teleport (camera now tracks Bond continuously within each shot instead of holding a frozen angle), and an unrelated bug in the same review pass — mouse-look never gated during scripted/no-control camera states (D300's own documented-but-unimplemented safety gate) — was found and fixed in `port/src/input.c`. User confirmed both fixes live, full intro-flyover + Dam abseil playthrough: "ok yeah looks good." |
| D245 | **IsWater water: two competing patterns with a visible seam that moves as the player looks around (mouse/aim) — "looks almost exactly like skybox" (M-116, user…** — full `## D245` entry at file tail | OPEN — CONFIRMED PORT BUG (2026-09-26, user vs 1964: PC water displays glitched with a weird yellow line; view-dependent shift is subtle; animation otherwise matches). Earlier: OPEN — REOPENED, M-142 headless probe found the tc-shift `k` measurably flips during ordinary view rotation (new root-cause evidence); an opt-in `GE_D245_FIXEDSHIFT` candidate is build-verified but needs a live A/B, not shipped as default.… |
| D245 | **IsWater water: two competing patterns with a visible seam that moves as the player looks around (mouse/aim) — "looks almost exactly like skybox" (M-116, user…** — full `## D245` entry at file tail | FIXED (M-201, 2026-09-26, user-verified vs 1964): water texture imported/normalised at 32x43/16-wide instead of the 32x32 CI8 mask period (resets, 2x speed) + s16 sky/water vertices replaced by G_FLOATVTX_EXT floats (jitter). Earlier: OPEN — CONFIRMED PORT BUG (2026-09-26, user vs 1964: PC water displays glitched with a weird yellow line; view-dependent shift is subtle; animation otherwise matches). Earlier: OPEN — REOPENED, M-142 headless probe found the tc-shift `k` measurably flips during ordinary view rotation (new root-cause evidence); an opt-in `GE_D245_FIXEDSHIFT` candidate is build-verified but needs a live A/B, not shipped as default.… |
| D246 | **Tiny pixel strips at the left and right edges of the screen, "like overscan" — look like raw pixels, not 3D graphics (M-116, user QA report).** — full `## D246` entry at file tail | **FIXED (2026-09-18 session, port-only, `Video.SafeAreaCrop`).** Measured precisely (exactly 1.0 logical unit missing on both edges, every resolution); underlying clip-precision root cause not isolated, but `gfx_adjust_viewport_or_scissor` now trims a fixed 1-unit margin from both edges — same toggle/mechanism as D247. Live user playtest confirmed clean edges. |
| D247 | **Black bars top and bottom of screen — possible aspect-ratio defect (M-116, user QA report).** — full `## D247` entry at file tail | **FIXED (2026-09-18 session, port-only, `Video.SafeAreaCrop`).** Root cause: faithful, unmodified N64 TV-safe-area letterboxing that real CRT overscan used to hide; this port showed the full VI frame. Per user request, given a real fix (not left as "correct original behaviour"): `gfx_adjust_viewport_or_scissor` now remaps against the active gameplay viewport bounds so it fills the window edge-to-edge. New `Video.SafeAreaCrop` toggle (default on). Live user playtest confirmed clean edges. |
| D248 | **Port runs a stable 30 FPS instead of 60 FPS game-wide (M-117, user asked to verify FPS after the D209 timing-keystone fixes).** — full `## D248` entry at file tail | **FIXED, root-caused to a genuine pointer-width ABI bug in `src/sched.c` (compiled for the PC build, not excluded).** `osScAddClient()` (`src/sched.c:187-199`) stashes a per-client "present every retrace vs.… |
@@ -11613,6 +11613,17 @@ D173 (level-start floating), D292 (death sinking), and the Cradle spin (also und
**Untested candidate, explicitly NOT shipped as the default** (per this row's own "don't repeat the same mistake" lesson from the superseded `allowShift=TRUE` fix): `skyPortForceFanShift()` + `GE_D245_FIXEDSHIFT=<k>` env var (`sky.c`, `#ifdef PORT`) lets a live session force a single constant `k` for the whole water draw instead of the per-frame adaptive pick, eliminating the transition this session found — but at the cost of coarser tc quantization *always* (a fixed `k=5` collapses to whole-texel granularity, which could itself look worse on the shimmer cross-fade; that trade-off can only be judged by eyes on the actual water, not another headless capture). Build-verified crash-free with `GE_D245_FIXEDSHIFT=5` on Frigate (300+ frames); `verify.sh frigate`/`verify.sh "surface 2"` both PASS with the env var unset (default path byte-identical to the current reopened behaviour — nothing changes for players until someone opts in). **Next step for whoever has live Frigate/Surface-2 access:** play with `GE_D245_FIXEDSHIFT` unset (current behaviour, confirm the glitch still reproduces) vs. set to 3, 4, and 5 in turn, and report which (if any) looks better — that's the missing data point, not more static tracing.
**M-201 (2026-09-26, v0.4.0): FIXED — two port-layer causes; user-verified against 1964 ("looks accurate").**
User vs 1964: PC water jittered with player movement, "reset" even standing still, animated too fast (the earlier yellow line was not present on the current build).
1. **Texture geometry (the resets + speed).** The IsWater image is a 32x32 CI8 texture (mask 5 = 32-texel period) loaded with its mip chain (1400 B) and drawn through an RGBA16 tile (the D229 case). fast3d imported it CI8 as **32x43** (mip rows included) while the triangle path normalised UVs with the RGBA16 reading, **width 16** (line bytes / 2) and height 43 (`GE_D229` probe: `texw=16 texh=43`). So S ran at 2x frequency and T repeated every 43 rows, not 32; the port's per-fan S/T fold (multiples of 64 texels) is not a multiple of 43, so every fold step jumped the pattern. Fix (`gfx_pc.cpp`, D229 case only): import only the `line_size * (1<<maskt)` base level, and in the tri path use CI8 geometry (width = line bytes, height = mask period). Headless still-camera A/B (300 frames): old 71 jump spikes, new 0 and a steady per-frame change.
2. **s16 vertex quantisation (the jitter).** The N64 draws sky/water as CPU-built RDP triangles with 32-bit S/T; the port routed them through `Vtx` (s16 `tc` needing a 2^k shift, k=4 for water = half-texel steps; s16 `ob` scaled by a fan-wide `wScale` ≈ 7, ~8 px position steps near the camera). New port-only `G_FLOATVTX_EXT` (0x47, `port/include/floatvtx.h`, loader `gfx_sp_vertex_float` in `gfx_pc.cpp`) passes clip-space positions and folded S/T as floats; `skyPortRenderPoly` (`sky.c`, `#ifdef PORT`) uses it for every sky/water fan, so no tile shift or wScale.
Ruled out on the way: the animation phases advance by `g_ClockTimer` (rate-independent); `PRIM_LOD_FRAC` and per-tile `uls/ult` offsets are handled by fast3d; `WrapFix` pre-wrap is off by default. A/B switches kept: `GE_D245_OLDVTX=1`, `GE_D245_OLDTEX=1`. Golden gate 3/3; Windows + Linux builds clean.
**Status: FIXED (M-201, user-verified vs 1964, 2026-09-26).**
## D246 — Tiny pixel strips at the left and right edges of the screen, "like overscan" — look like raw pixels, not 3D graphics (M-116, user QA report).
**Tiny pixel strips at the left and right edges of the screen, "like overscan" — look like raw pixels, not 3D graphics (M-116, user QA report).** Uninvestigated. The port renders at native 640×480 (`GE_NATIVE_W/H`; `gfx_current_native_aspect` set in `port/src/video.c:253,570`) and scales to the window on present, so the leading candidate is a scaling/blit rounding artifact: non-integer scale factors leaving 1–2 columns of uncleared backbuffer content at the edges (or an off-by-one in the blit rect). Secondary candidates: an RDP fill-rect/scissor off-by-one in a game DL or the F10 overlay hook (`gfx_run()` seam, D184), or GL edge filtering on the scaled texture.
+3
View File
@@ -610,6 +610,9 @@ through a converter or a runtime bswap fixup reads scrambled.
Headless `-level_XX` boots hide this class (the first stage is the level
itself): repro through a real boot → title → level sequence.
- **A texture re-declared in another format must be normalised with the geometry it was IMPORTED with (D245).** GE's sky water loads a CI8 image and draws it through an RGBA16 tile; fast3d imports it as CI8 (D229) but its triangle path computed the UV divisor from the RGBA16 tile (half the width) and the whole mip-chain height (43 rows, not the 32-row mask period). Anything that changes the importer's format/extent must change the tri-path `tex_width/tex_height` with it, and wrap must equal the N64 mask period.
- **CPU-built RDP triangles need float vertices, not `Vtx` (D245).** Where GE builds RDP edge/texture coefficients on the CPU (sky/water), 32-bit S/T and positions are normal; squeezing them through s16 `tc`/`ob` costs visible precision on horizon-scale geometry. Use `G_FLOATVTX_EXT` (`port/include/floatvtx.h`).
## D. N64 hardware idioms fast3d does not emulate
- **Appending a port-owned 2D overlay to the game frame: hook inside
+69 -1
View File
@@ -1,5 +1,6 @@
#define NOMINMAX
#include "floatvtx.h" /* D245 */
#include <cmath>
#include <cstdint>
#include <cstdlib>
@@ -1336,7 +1337,23 @@ static void import_texture(int i, int tile, bool importReplacement) {
if (siz_eff == G_IM_SIZ_4b) {
import_texture_ci4(tile, loaded_texture, rdp.tex_lod);
} else if (siz_eff == G_IM_SIZ_8b) {
import_texture_ci8(tile, loaded_texture, rdp.tex_lod);
/* D245 (M-201): for the D229 case (RGBA16 tile over a CI8 load --
* the IsWater sky water), upload only the base level: the load
* carries the whole mip chain (32x32 base + mips = 1400 B), which
* made a 32x43 image whose GL REPEAT period (43 rows) differs from
* the N64's mask period (32). See the tri-path counterpart. */
static int d245_oldtex = -1;
if (d245_oldtex < 0) d245_oldtex = getenv("GE_D245_OLDTEX") != NULL;
const uint8_t maskt = rdp.texture_tile[tile].maskt;
if (!d245_oldtex && fmt == G_IM_FMT_RGBA && siz == G_IM_SIZ_16b && maskt > 0 && maskt < 12 &&
loaded_texture.line_size_bytes > 0 &&
loaded_texture.size_bytes > loaded_texture.line_size_bytes * (1u << maskt)) {
LoadedTexture lt = loaded_texture;
lt.size_bytes = loaded_texture.line_size_bytes * (1u << maskt);
import_texture_ci8(tile, lt, rdp.tex_lod);
} else {
import_texture_ci8(tile, loaded_texture, rdp.tex_lod);
}
} else {
sysFatalError("Bad size for CI texture in tile %d: %02x", tile, siz);
}
@@ -2415,6 +2432,22 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
break;
}
tex_width[i] = line_size;
/* D245 (M-201): D229 water (RGBA16 tile over a CI8 load) is
* imported as CI8 -- one byte per texel, base level only -- so the
* UV normalisation must use that geometry: width = line bytes
* (32, not 32/2 = 16, which doubled the S frequency) and height =
* the mask period (32, not 1400/32 = 43 rows of mip chain). */
{
static int d245_oldtex2 = -1;
if (d245_oldtex2 < 0) d245_oldtex2 = getenv("GE_D245_OLDTEX") != NULL;
const auto& ltx = rdp.loaded_texture[rdp.texture_tile[tile].tmem];
if (!d245_oldtex2 && rdp.texture_tile[tile].fmt == G_IM_FMT_RGBA &&
rdp.texture_tile[tile].siz == G_IM_SIZ_16b && ltx.src_fmt == G_IM_FMT_CI) {
const uint8_t mkt = rdp.texture_tile[tile].maskt;
tex_width[i] = rdp.texture_tile[tile].line_size_bytes;
if (mkt > 0 && mkt < 12 && tex_height[i] > (1u << mkt)) tex_height[i] = 1u << mkt;
}
}
tex_width2[i] = (rdp.texture_tile[tile].lrs - rdp.texture_tile[tile].uls + 4) / 4;
tex_height2[i] = (rdp.texture_tile[tile].lrt - rdp.texture_tile[tile].ult + 4) / 4;
@@ -3662,6 +3695,38 @@ static void gfx_dp_set_other_mode(uint32_t h, uint32_t l) {
rdp.other_mode_l = l;
}
/* D245 (M-201): G_FLOATVTX_EXT loader -- gfx_sp_vertex for pre-transformed
* clip-space float vertices with float S/T (see port/include/floatvtx.h).
* Only the unlit, unfogged path the sky/water fans use is supported. */
static void gfx_sp_vertex_float(size_t n_vertices, size_t dest_index, const PortFloatVtx* vertices) {
if (!fast3d_ptr_ok(vertices) || dest_index + n_vertices > MAX_VERTICES) {
return;
}
for (size_t i = 0; i < n_vertices; i++, dest_index++) {
const PortFloatVtx* v = &vertices[i];
struct LoadedVertex* d = &rsp.loaded_vertices[dest_index];
float x = gfx_adjust_x_for_aspect_ratio(v->x, v->w);
float y = v->y, z = v->z, w = v->w;
d->u = v->s * (float)rsp.texture_scaling_factor.s / 65536.0f;
d->v = v->t * (float)rsp.texture_scaling_factor.t / 65536.0f;
d->color.r = v->r;
d->color.g = v->g;
d->color.b = v->b;
d->color.a = v->a;
d->clip_rej = 0;
if (x < -w) d->clip_rej |= 1;
if (x > w) d->clip_rej |= 2;
if (y < -w) d->clip_rej |= 4;
if (y > w) d->clip_rej |= 8;
if (z > w) d->clip_rej |= 32;
d->x = x;
d->y = y;
d->z = z;
d->w = w;
d->fog = rdp.fog_color.a;
}
}
static inline void *seg_addr(uintptr_t w1) {
// GE model files reference GDLs (gSPDisplayList) and vertex arrays by raw
// VMA 0x05xxxxxx WITHOUT the LSB set; segment 5 is set per-render to the
@@ -3780,6 +3845,9 @@ static void gfx_run_dl(Gfx* cmd) {
case (uint8_t)G_CLEARGEOMETRYMODE:
gfx_sp_geometry_mode(cmd->words.w1, 0);
break;
case G_FLOATVTX_EXT: /* D245 */
gfx_sp_vertex_float(C0(0, 16), C0(16, 8), (const PortFloatVtx*)seg_addr(cmd->words.w1));
break;
case 0x46: /* G_HUDSCALE_EXT (D226): w0 low16 = scale*256 (0/256 = off), w1 = ax4<<16 | ay4 */
{
const uint32_t sc = C0(0, 16);
+33
View File
@@ -0,0 +1,33 @@
#ifndef PORT_FLOATVTX_H
#define PORT_FLOATVTX_H
/* D245 (M-201): port-only full-precision vertex load for the sky/water fans.
*
* On N64, sky.c builds these triangles directly as RDP edge/texture
* coefficients with 32-bit S/T, so a horizon-scale quad (the IsWater ocean
* spans ~300,000 S10.5 units, ~9,400 texels) is exact. Routed through an
* ordinary gSPVertex, both its texture coords (s16 tc: +-1,024 texels) and its
* positions (s16 ob, divided by a fan-wide wScale to fit camera-space w up to
* ~2e5) had to be quantised -- visible as water/sky jitter that slides with
* the view. G_FLOATVTX_EXT hands fast3d the clip-space position and S/T as
* floats instead; fast3d already stores every loaded vertex as floats.
*
* w0 = G_FLOATVTX_EXT << 24 | dest_index << 16 | count, w1 = PortFloatVtx *.
* s/t are in Vtx.tc units (S10.5, before gSPTexture scaling). */
#define G_FLOATVTX_EXT 0x47
typedef struct PortFloatVtx {
float x, y, z, w; /* clip space */
float s, t; /* tc units */
unsigned char r, g, b, a;
} PortFloatVtx;
#define gSPFloatVertexExt(pkt, ptr, n, v0) \
do { \
Gfx *_g = (Gfx *)(pkt); \
_g->words.w0 = ((uintptr_t)G_FLOATVTX_EXT << 24) | \
(((uintptr_t)(v0) & 0xff) << 16) | ((uintptr_t)(n) & 0xffff); \
_g->words.w1 = (uintptr_t)(ptr); \
} while (0)
#endif
+51
View File
@@ -5,6 +5,10 @@
#include <stdlib.h>
#endif
#include "sky.h"
#ifdef PORT
#include "floatvtx.h" /* D245: G_FLOATVTX_EXT */
#include "envflag.h"
#endif
#include "player.h"
#include "unk_092E50.h"
#include "bondview.h"
@@ -1996,6 +2000,53 @@ static Gfx *skyPortRenderPoly(Gfx *gdl, SkyRelated38 **v, s32 nverts)
wScale = (maxAbsW > 30000.0f) ? (maxAbsW / 30000.0f) : 1.0f;
}
/* D245 (M-201): full-precision path. Hand fast3d the clip-space position
* (ndcX*w, ndcY*w, 0, w) and the folded S/T as floats (G_FLOATVTX_EXT,
* port/include/floatvtx.h) instead of squeezing them through s16 Vtx
* ob/tc: no wScale position quantisation, no 2^k tc shift. The fold is
* still applied (a multiple of the repeat period, exact under wrapping)
* to keep the float magnitudes small. GE_D245_OLDVTX=1 restores the old
* s16 route for A/B. */
if (!GE_ENVFLAG("GE_D245_OLDVTX"))
{
PortFloatVtx *fv = (PortFloatVtx *) dynAllocateVertices(2 * (nverts < 3 ? 3 : nverts));
for (i = 0; i < nverts; i++)
{
f32 screenX = v[i]->unk28 * 0.25f;
f32 screenY = v[i]->unk2c * 0.25f;
f32 ndcX = 2.0f * ((screenX - l) / (r - l)) - 1.0f;
f32 ndcY = 1.0f - 2.0f * ((screenY - t) / (b - t));
f32 w = v[i]->unk0c;
fv[i].x = ndcX * w;
fv[i].y = ndcY * w;
fv[i].z = 0.0f;
fv[i].w = w;
fv[i].s = v[i]->unk20 - foldS;
fv[i].t = v[i]->unk24 - foldT;
fv[i].r = (u8) v[i]->r;
fv[i].g = (u8) v[i]->g;
fv[i].b = (u8) v[i]->b;
fv[i].a = (u8) v[i]->a;
}
gSPClearGeometryMode(gdl++, G_LIGHTING | G_CULL_BOTH | G_FOG);
gSPSetGeometryMode(gdl++, G_SHADE | G_SHADING_SMOOTH);
gSPFloatVertexExt(gdl++, fv, nverts, 0);
if (nverts >= 4)
{
gSP2Triangles(gdl++, 0, 1, 3, 0, 3, 2, 0, 0);
}
else
{
gSP1Triangle(gdl++, 0, 1, 2, 0);
}
return gdl;
}
guMtxIdentF(projf.m);
/* row = input axis (matches this codebase's row-vector * M convention,
* e.g. sub_GAME_7F097388 / gfx_pc.cpp's own MP_matrix use):