fix(render): fade MSH portals with camera distance
Docs Deploy / Build and Deploy MkDocs (push) Successful in 36s

This commit is contained in:
Valentin Popov committed 2026-10-31 18:01:35 +04:00
1 parent 09fcd35023
commit c31e5b7342
7 files changed
+382 -44

No files matched your search

+81
View File
@@ -361,6 +361,53 @@ pub enum LegacyCullMode {
FrontFace,
}
/// CPU diffuse alpha for a colourless MSH batch carrying flag `0x0008`.
///
/// `packet_distance` is the distance to the packet's first accepted vertex,
/// after its object transform. Flag `0x0010` takes precedence over `0x0040`.
/// Returns `None` for invalid distance or projection inputs. Callers must keep
/// the material alpha for batches with a type-16 colour stream or without `0x0008`.
#[must_use]
pub fn legacy_msh_portal_alpha(
batch_flags: u16,
packet_distance: f32,
horizontal_fov: f32,
) -> Option<f32> {
if !packet_distance.is_finite()
|| packet_distance < 0.0
|| !horizontal_fov.is_finite()
|| horizontal_fov <= 0.0
{
return None;
}
let (distance, near, far, slope) = if batch_flags & 0x0010 != 0 {
(
packet_distance.sqrt(),
2.6 / horizontal_fov,
7.799_999_7 / horizontal_fov,
(1.0 / (7.799_999_7 - 2.6)) * horizontal_fov,
)
} else if batch_flags & 0x0040 != 0 {
return Some(0.0);
} else {
let near = 75.0 * 1.3;
let far = 95.0 * 1.3;
(
packet_distance,
horizontal_fov.recip() * near,
horizontal_fov.recip() * far,
(1.0 / (far - near)) * horizontal_fov,
)
};
Some(if distance < near {
0.0
} else if distance > far {
1.0
} else {
((distance - near) * slope).clamp(0.0, 1.0)
})
}
/// Raster depth adjustment applied to one pipeline variant.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum LegacyDepthBias {
@@ -463,6 +510,40 @@ impl From<LegacyPipelineState> for PipelineKey {
mod tests {
use super::*;
#[test]
fn portal_alpha_matches_native_packet_distance_vectors() {
// GOG Terrain+1B0F0 with its real +246A0/+247B0 packet methods,
// Ngi32 RSQRTSS dispatch, x87 CW=007F, FOV=1.04.
for (distance, flags, expected) in [
(0.499_938_96, 0x18, 0.0),
(4.999_542, 0x18, 0.0),
(9.999_084, 0x18, 0.132_426_6),
(25.000_572, 0x18, 0.500_011_44),
(59.999_084, 0x18, 1.0),
(85.012_48, 0x08, 0.0),
(109.994_125, 0x08, 0.649_765_2),
(25.000_572, 0x58, 0.500_011_44),
(110.0, 0x48, 0.0),
] {
let actual = legacy_msh_portal_alpha(flags, distance, 1.04).unwrap();
assert!(
(actual - expected).abs() < 0.000_002,
"{distance}: {actual}"
);
}
assert_eq!(legacy_msh_portal_alpha(8, 60.0, 1.3), Some(0.0));
assert!((legacy_msh_portal_alpha(8, 85.0, 1.3).unwrap() - 0.5).abs() < 0.000_001);
assert_eq!(legacy_msh_portal_alpha(8, 110.0, 1.3), Some(1.0));
for (distance, fov) in [
(f32::NAN, 1.3),
(-1.0, 1.3),
(1.0, 0.0),
(1.0, f32::INFINITY),
] {
assert_eq!(legacy_msh_portal_alpha(8, distance, fov), None);
}
}
fn multiply_row_major(left: [f32; 16], right: [f32; 16]) -> [f32; 16] {
let mut result = [0.0; 16];
for row in 0..4 {