This commit is contained in:
@@ -525,8 +525,9 @@ impl PreviewCameraFrame {
|
||||
self.up
|
||||
}
|
||||
|
||||
fn yaw(self) -> f32 {
|
||||
self.forward[1].atan2(self.forward[0])
|
||||
fn native_sky_heading(self) -> f32 {
|
||||
// Terrain+0x4F9C0 uses atan2(forward.x, forward.y); CSky+0x481C5 adds pi later.
|
||||
self.forward[0].atan2(self.forward[1])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2374,7 +2375,7 @@ impl EnvironmentGpuScene {
|
||||
(optics.primary_color[index] - sky_frame.sun.color[index]).max(0.0)
|
||||
})
|
||||
});
|
||||
let gradient = sky_frame.screen_gradient(camera.yaw(), glare_rgb_delta);
|
||||
let gradient = sky_frame.screen_gradient(camera.native_sky_heading(), glare_rgb_delta);
|
||||
if let Some(optics) = optics {
|
||||
let mut uniforms = renderer.frame_uniforms();
|
||||
if sky_frame.sun.active {
|
||||
@@ -6260,6 +6261,86 @@ mod tests {
|
||||
nearly_equal(far[2] / far[3], 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_sky_heading_selects_matching_gradient_sectors_for_both_camera_origins() {
|
||||
use fparkan_fx::atmosphere::{PackedColor, SkySample, SKY_COLOR_COUNT};
|
||||
|
||||
let mut colors = [PackedColor(0xFF00_0000); SKY_COLOR_COUNT];
|
||||
colors[..4].copy_from_slice(&[
|
||||
PackedColor(0xFF11_0000),
|
||||
PackedColor(0xFF00_2200),
|
||||
PackedColor(0xFF00_0033),
|
||||
PackedColor(0xFF44_4400),
|
||||
]);
|
||||
let sample = SkySample {
|
||||
colors,
|
||||
values: [0.0; 2],
|
||||
packed: [PackedColor(0); 2],
|
||||
};
|
||||
let projection = LegacyD3d7Projection {
|
||||
viewport: [0, 0, 1280, 720],
|
||||
near_plane: 0.5,
|
||||
far_plane: 700.0,
|
||||
field_of_view_radians: 1.596_851_4,
|
||||
};
|
||||
|
||||
let cardinal_forwards: [([f32; 3], usize); 4] = [
|
||||
([0.0, 1.0, 0.0], 0),
|
||||
([1.0, 0.0, 0.0], 1),
|
||||
([0.0, -1.0, 0.0], 2),
|
||||
([-1.0, 0.0, 0.0], 3),
|
||||
];
|
||||
for (forward, sector) in cardinal_forwards {
|
||||
let free_flight = FreeFlightCamera {
|
||||
position: [0.0; 3],
|
||||
yaw: forward[1].atan2(forward[0]),
|
||||
pitch: 0.0,
|
||||
vertical_fov: std::f32::consts::FRAC_PI_3,
|
||||
near_plane: projection.near_plane,
|
||||
far_plane: projection.far_plane,
|
||||
move_speed: 1.0,
|
||||
}
|
||||
.preview_frame(1280.0 / 720.0);
|
||||
|
||||
let right = normalize3(cross3(forward, [0.0, 0.0, 1.0])).expect("camera right");
|
||||
let up = normalize3(cross3(right, forward)).expect("camera up");
|
||||
let raw_transform = RawCameraTransform {
|
||||
words: [
|
||||
forward[0].to_bits(),
|
||||
(-right[0]).to_bits(),
|
||||
up[0].to_bits(),
|
||||
0.0_f32.to_bits(),
|
||||
forward[1].to_bits(),
|
||||
(-right[1]).to_bits(),
|
||||
up[1].to_bits(),
|
||||
0.0_f32.to_bits(),
|
||||
forward[2].to_bits(),
|
||||
(-right[2]).to_bits(),
|
||||
up[2].to_bits(),
|
||||
0.0_f32.to_bits(),
|
||||
0.0_f32.to_bits(),
|
||||
0.0_f32.to_bits(),
|
||||
0.0_f32.to_bits(),
|
||||
1.0_f32.to_bits(),
|
||||
],
|
||||
};
|
||||
let captured = PreviewCameraFrame::from_legacy_d3d7(raw_transform, projection)
|
||||
.expect("captured camera frame");
|
||||
|
||||
for (origin, camera) in [("free flight", free_flight), ("captured", captured)] {
|
||||
let gradient = fparkan_fx::sky::screen_gradient_frame(
|
||||
&sample,
|
||||
camera.native_sky_heading(),
|
||||
[0.0; 3],
|
||||
);
|
||||
assert_eq!(
|
||||
gradient.color, sample.colors[sector],
|
||||
"{origin} camera heading for forward {forward:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sky_normals_keep_native_signed_byte_scale_without_renormalizing() {
|
||||
assert_eq!(sky_normal([-128, 127, -64]), [-1.0, 127.0 / 128.0, -0.5]);
|
||||
|
||||
@@ -100,6 +100,15 @@ receives real frame time. `simulation_time_seconds`, weather, RNG and complete
|
||||
mission state remain unknown, and a camera/phase match alone does not establish
|
||||
visual parity with a native frame.
|
||||
|
||||
The preview's sky heading is `atan2(forward.x, forward.y)`, matching the second
|
||||
output calculated at `Terrain.dll` RVA `0x4F9C0`; `CSky` adds π at RVA `0x481C5`
|
||||
when selecting screen-gradient sectors. This mapping is applied only at the sky
|
||||
boundary and does not change `FreeFlightCamera` yaw or movement controls.
|
||||
Matched same-camera, same-phase three-frame captures at phases 56.227 and
|
||||
998.893 showed zero changed pixels before and after this correction; the
|
||||
remaining sky and color mismatch is unresolved, so this source-level match does
|
||||
not establish visual parity.
|
||||
|
||||
## Parity risks
|
||||
|
||||
`VulkanStaticCamera::from_legacy_d3d7` keeps the D3D7 view and projection
|
||||
|
||||
Reference in New Issue
Block a user