feat(render): retain raw camera poses

This commit is contained in:
2026-07-18 14:03:18 +04:00
parent 50a55461e7
commit 0506a45d57
2 changed files with 76 additions and 0 deletions
+66
View File
@@ -22,6 +22,42 @@
use fparkan_world::OriginalObjectId; use fparkan_world::OriginalObjectId;
/// A 64-byte transform block returned by the original Terrain camera ABI.
///
/// The original uses two selector-dependent transform pointers. Their exact
/// matrix convention is still under recovery, so words are deliberately kept
/// losslessly rather than treated as a renderer-ready matrix. The three
/// translation words have been confirmed at indices 3, 7, and 11.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RawCameraTransform {
/// The exact 16 little-endian dwords returned by the legacy camera.
pub words: [u32; 16],
}
impl RawCameraTransform {
/// Indices of the confirmed X, Y, and Z translation floats.
pub const TRANSLATION_WORD_INDICES: [usize; 3] = [3, 7, 11];
/// Returns the confirmed legacy world position (X, Y, Z).
#[must_use]
pub fn translation(self) -> [f32; 3] {
Self::TRANSLATION_WORD_INDICES.map(|index| f32::from_bits(self.words[index]))
}
}
/// Raw camera state observed through the original Terrain camera interface.
///
/// Selector 0 supplies the currently active transform and selector 2 supplies
/// its paired transform. Keeping both lets a later compatibility layer derive
/// a view/projection convention without re-reading the legacy process.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RawCameraPose {
/// Transform returned by selector 0.
pub selector0: RawCameraTransform,
/// Transform returned by selector 2.
pub selector2: RawCameraTransform,
}
/// Immutable camera data visible to command generation. /// Immutable camera data visible to command generation.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct CameraSnapshot { pub struct CameraSnapshot {
@@ -29,6 +65,12 @@ pub struct CameraSnapshot {
pub view: [f32; 16], pub view: [f32; 16],
/// Projection matrix, row-major. /// Projection matrix, row-major.
pub projection: [f32; 16], pub projection: [f32; 16],
/// Optional unconverted source-camera state.
///
/// This does not alter rendering until the original matrix and projection
/// conventions have been recovered; it preserves the ABI boundary for the
/// runtime adapter and deterministic captures.
pub raw_pose: Option<RawCameraPose>,
} }
impl Default for CameraSnapshot { impl Default for CameraSnapshot {
@@ -36,6 +78,7 @@ impl Default for CameraSnapshot {
Self { Self {
view: identity_transform(), view: identity_transform(),
projection: identity_transform(), projection: identity_transform(),
raw_pose: None,
} }
} }
} }
@@ -702,6 +745,29 @@ fn identity_transform() -> [f32; 16] {
mod tests { mod tests {
use super::*; use super::*;
#[test]
fn raw_camera_pose_preserves_words_and_extracts_confirmed_translation() {
let mut active = [0_u32; 16];
active[0] = 0x7FC0_0001;
active[3] = 491.562_5_f32.to_bits();
active[7] = 761.550_8_f32.to_bits();
active[11] = 7.361_0_f32.to_bits();
let paired = [0xA5A5_5A5A; 16];
let pose = RawCameraPose {
selector0: RawCameraTransform { words: active },
selector2: RawCameraTransform { words: paired },
};
assert_eq!(pose.selector0.words, active);
assert_eq!(pose.selector2.words, paired);
assert_eq!(
pose.selector0.translation(),
[491.562_5, 761.550_8, 7.361_0]
);
assert_eq!(CameraSnapshot::default().raw_pose, None);
}
fn snapshot_draw( fn snapshot_draw(
id: u64, id: u64,
phase: RenderPhase, phase: RenderPhase,
+10
View File
@@ -1318,6 +1318,16 @@ is a proven orientation-angle path. Field names, angle order and matrix
handedness remain unassigned, but a backend-neutral camera pose may now retain handedness remain unassigned, but a backend-neutral camera pose may now retain
the raw affine transform and the exact translation triple without guessing them. the raw affine transform and the exact translation triple without guessing them.
The render contract now represents that boundary directly. `RawCameraTransform`
preserves each selector result as sixteen original `u32` words, so diagnostic
data is not rounded or silently assigned a row/column convention. Its only
typed accessor reinterprets the proven X/Y/Z words `3/7/11` as IEEE-754
floats. `RawCameraPose` retains selector `0` and selector `2` together, and
`CameraSnapshot::raw_pose` is optional. The present Vulkan renderer continues
to use its existing view/projection matrices: raw pose capture is intentionally
not converted into a view matrix until the original transform/projection
convention has evidence.
A fresh no-input launch of the canonical `iron_3d.exe` did create a responsive A fresh no-input launch of the canonical `iron_3d.exe` did create a responsive
window titled `Parkan. Железная Стратегия`. A read-only probe then requested window titled `Parkan. Железная Стратегия`. A read-only probe then requested
`PROCESS_QUERY_INFORMATION | PROCESS_VM_READ` and attempted to read the known `PROCESS_QUERY_INFORMATION | PROCESS_VM_READ` and attempted to read the known