diff --git a/crates/gc-extract/tests/disc.rs b/crates/gc-extract/tests/disc.rs index f903d69..6200ccb 100644 --- a/crates/gc-extract/tests/disc.rs +++ b/crates/gc-extract/tests/disc.rs @@ -7914,7 +7914,7 @@ fn moby_classes_enrichment_survives_the_correct_mapping() { } } -/// The camera transform is three orthonormal matrices with a shared translation. +/// The camera region is nine orthonormal affine matrices sharing a translation. /// /// Read live over PINE: two EE RAM dumps taken seconds apart, then the region at /// `0x18aa70` is checked to be a 4x4 matrix whose upper 3x3 is orthonormal and @@ -7944,11 +7944,12 @@ fn camera_transform_is_orthonormal_with_a_moving_translation() { ] }; - for (name, base) in [ - ("m1", 0x0018_AA70usize), - ("m2", 0x0018_AA_B0), - ("m3", 0x0018_AA_F0), - ] { + // Nine, contiguous at 64-byte stride from 0x18aa70, ending at 0x18acb0 where + // the structure breaks (row norms 0, 0, 0.97 and w = 0). `dcd4dd6` said three + // and was wrong: it stopped looking after three because the first three + // happened to read as a sensible family. + for name in 0..9usize { + let base = 0x0018_AA70usize + name * 64; for row in 0..3 { let r = m(&a, base, row); let norm = (r[0] * r[0] + r[1] * r[1] + r[2] * r[2]).sqrt(); @@ -7971,8 +7972,8 @@ fn camera_transform_is_orthonormal_with_a_moving_translation() { assert_eq!(ta[3], 1.0, "{name} fourth row is not affine (w != 1)"); let moved = (ta[0] - tb[0]).hypot(ta[1] - tb[1]).hypot(ta[2] - tb[2]); eprintln!( - "camera {name} at [{:.3}, {:.3}, {:.3}] moved {:.4} in 1.5s", - ta[0], ta[1], ta[2], moved + "camera m{} at [{:.3}, {:.3}, {:.3}] moved {:.4} in 1.5s", + name, ta[0], ta[1], ta[2], moved ); assert!( moved > 0.0, diff --git a/docs/plan/level-assets.md b/docs/plan/level-assets.md index 2007652..452946b 100644 --- a/docs/plan/level-assets.md +++ b/docs/plan/level-assets.md @@ -2774,7 +2774,7 @@ the fixed-point convention and is **not** established, and the clip-cube scale o 64 is likewise R&C1's documented value, unverified. Both are noted in the module doc so they are not mistaken for measured facts. -## The camera transform is at 0x18aa70 -- found live, verified by orthonormality +## The camera region is at 0x18aa70 -- found live, verified by orthonormality With PCSX2 on Aranos, the savestate problem went away: **PINE reads EE RAM live**, so two dumps seconds apart are consecutive frames by construction, which is exactly @@ -2788,7 +2788,8 @@ moving 4-5 units. Two shapes stood out: 0x18aaa0 [247.299, 193.133, 50.453] -> ... moved 0.26 over 2s 0x18aa70..0x18ab2c unit vectors that reoriented -The second region is **three 4x4 matrices at 64-byte stride**: +The second region is **nine 4x4 matrices at 64-byte stride** (this note first said +three; see the correction below): 0x18aa70 0.0338 -0.1983 0.9796 0.0000 -0.0275 -0.9799 -0.1974 0.0000 @@ -2821,3 +2822,42 @@ the one step 1 wants, whether the translation is world or camera space (the guard-band algorithm multiplies by 1024, which implies the disc's own 12.4 fixed point, so the float translation may need converting first), and the frustum planes, which the guard band needs and which are not in this structure. + +### Correction: it is nine matrices, not three + +`dcd4dd6` reported "three 4x4 matrices at 64-byte stride" and read them as a +view / modelview / lighting family. **There are nine.** They are contiguous from +`0x18aa70` and the structure breaks at `0x18acb0`, where the row norms are +`0, 0, 0.97` and `w = 0`. + + m0 247.299 193.133 50.451 m3 247.122 193.125 50.140 m6 247.211 193.074 50.517 + m1 247.239 193.102 50.496 m4 247.030 193.092 50.799 m7 247.041 193.091 50.457 + m2 246.940 193.157 50.150 m5 247.033 193.062 50.633 m8 247.033 193.062 50.633 + +All nine are orthonormal to 1e-6, all nine are affine, all nine carry a +translation inside a 0.4-unit box, and all nine translate smoothly between live +frames (0.003-0.008 in 1.5s). Nine different rotations about one position is not a +view/projection/lighting family -- it is a **table indexed by orientation**, and +the index is what is unknown. + +So `dcd4dd6` was right that the region holds the camera's position and wrong that +three was the extent. It stopped at three because three happened to read as a +sensible story, which is the failure this project already documents in rule 4 and +that I have now committed twice in this investigation. + +**What survives, unchanged:** the translation is the camera position, it is +(247.3, 193.1, 50.5), and it moves smoothly between consecutive live frames while +the rotation is changing much faster. That is still the identification, and it is +still verified by orthonormality plus the smooth-motion control. + +**What is now open, and is larger than it looked:** which entry of the nine is the +camera the guard-band test wants. Not "pick m0" -- nine orientations about one point +is a table with an index, and the index is what step 1's `R` has to be. There is a +plausible reading that these are the nine legal camera orientations on a spherical +planet, which would be a genuinely interesting find and is **not** established by +anything measured here. + +Two cheap checks that would settle it, both needing only the live emulator: +move the camera and see whether all nine translations move together or only one +(the former says "one camera, nine cached frames"; the latter says "nine things"); +and change the camera's pitch to see whether the nine rotations reindex.