diff options
| author | Valentin Popov <valentin@popov.link> | 2026-10-11 17:25:33 +0300 |
|---|---|---|
| committer | Valentin Popov <valentin@popov.link> | 2026-10-11 17:25:33 +0300 |
| commit | 45fcf0eb50b4e93cc3900671859cb20d8b1c34d0 (patch) | |
| tree | 247232b4bf0c602ae23faa499e852270959cf501 /apps/fparkan-game | |
| parent | 85441bd2bfb2ae49d10a90b2d897813bc087970d (diff) | |
| download | fparkan-45fcf0eb50b4e93cc3900671859cb20d8b1c34d0.tar.xz fparkan-45fcf0eb50b4e93cc3900671859cb20d8b1c34d0.zip | |
Use native heading for sky gradient
Diffstat (limited to 'apps/fparkan-game')
| -rw-r--r-- | apps/fparkan-game/src/main.rs | 87 |
1 files changed, 84 insertions, 3 deletions
diff --git a/apps/fparkan-game/src/main.rs b/apps/fparkan-game/src/main.rs index c284115..c974b6b 100644 --- a/apps/fparkan-game/src/main.rs +++ b/apps/fparkan-game/src/main.rs @@ -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 { @@ -6261,6 +6262,86 @@ mod tests { } #[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]); } |
