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

+96 -25
View File
@@ -47,8 +47,9 @@ use fparkan_path::ResourceName;
use fparkan_platform_winit::{window_native_handles, WinitWindow, WinitWindowPlan};
use fparkan_prototype::{unit_component_tree, PROTOTYPE_TYPE_EXTO};
use fparkan_render::{
LegacyBlendMode, LegacyCullMode, LegacyD3d7Projection, LegacyDepthBias, LegacyDepthMode,
LegacyIron3dEulerTransform, LegacyPipelineState, RawCameraTransform,
legacy_msh_portal_alpha, LegacyBlendMode, LegacyCullMode, LegacyD3d7Projection,
LegacyDepthBias, LegacyDepthMode, LegacyIron3dEulerTransform, LegacyPipelineState,
RawCameraTransform,
};
use fparkan_render_vulkan::{
node38_pose, node38_pose_from_hierarchy, node38_pose_relative_to_root,
@@ -1334,11 +1335,29 @@ fn native_world_transparent(range: &VulkanStaticDrawRange, diffuse_alpha: f32) -
diffuse_alpha < 1.0 || (range.batch_flags & 0x108) != 0
}
fn native_msh_diffuse_alpha_override(batch_flags: u16, type16_present: bool) -> Option<f32> {
if type16_present || batch_flags & 0x0008 == 0 {
return None;
fn native_msh_diffuse_alpha_override(
mesh: &VulkanStaticMesh,
range_index: usize,
type16_present: bool,
eye_world: [f32; 3],
horizontal_fov: f32,
) -> Result<Option<f32>, String> {
let range = mesh
.draw_ranges
.get(range_index)
.ok_or_else(|| "portal draw range is out of bounds".to_string())?;
if type16_present || range.batch_flags & 0x0008 == 0 {
return Ok(None);
}
(batch_flags & 0x0040 != 0 && batch_flags & 0x0010 == 0).then_some(0.0)
let distance = if range.batch_flags & 0x0050 == 0x0040 {
0.0
} else {
// shortcut: use the first indexed vertex; replace with native CPU clipping selection when clipped-portal captures are available.
first_indexed_vertex_distance(mesh, range_index, eye_world)?
};
legacy_msh_portal_alpha(range.batch_flags, distance, horizontal_fov)
.map(Some)
.ok_or_else(|| "portal distance or field of view is invalid".to_string())
}
fn native_msh_handedness(transform: LegacyIron3dEulerTransform, scale: [f32; 3]) -> i8 {
@@ -9408,7 +9427,19 @@ impl StaticVulkanApp {
fn configure_world_draw_ranges(
&self,
renderer: &mut VulkanSmokeRenderer,
aspect: f32,
) -> Result<(), String> {
let approximate_portals = self
.world_range_indices
.iter()
.filter(|&&index| {
let flags = self.mesh.draw_ranges[index].batch_flags;
flags & 8 != 0
&& flags & 0x50 != 0x40
&& !self.type16_present_ranges.contains(&index)
})
.count();
eprintln!("render_coverage portal_fade_packet_selection_approx={approximate_portals} (first indexed vertex; native CPU clipping selection pending)");
for &range_index in &self.world_range_indices {
let range = self
.mesh
@@ -9420,9 +9451,12 @@ impl StaticVulkanApp {
.get(usize::from(range.material_index))
.map_or(1.0, |material| material.diffuse_alpha);
let alpha_override = native_msh_diffuse_alpha_override(
range.batch_flags,
&self.mesh,
range_index,
self.type16_present_ranges.contains(&range_index),
);
self.view_camera.position,
horizontal_fov(&self.view_camera, aspect),
)?;
let effective_alpha = alpha_override.unwrap_or(alpha);
renderer
.set_draw_range_diffuse_alpha_override(range_index, alpha_override)
@@ -9513,6 +9547,7 @@ impl StaticVulkanApp {
world_range_index_counts: &HashMap<usize, u32>,
native_handedness_by_range: &HashMap<usize, i8>,
eye_world: [f32; 3],
horizontal_fov: f32,
active_materials: &HashMap<usize, ActiveMaterialState>,
renderer: &mut VulkanSmokeRenderer,
) -> Result<(), String> {
@@ -9531,9 +9566,12 @@ impl StaticVulkanApp {
|state| state.diffuse_alpha,
);
let alpha_override = native_msh_diffuse_alpha_override(
range.batch_flags,
mesh,
range_index,
type16_present_ranges.contains(&range_index),
);
eye_world,
horizontal_fov,
)?;
let alpha = alpha_override.unwrap_or(material_alpha);
renderer
.set_draw_range_diffuse_alpha_override(range_index, alpha_override)
@@ -10120,6 +10158,7 @@ impl StaticVulkanApp {
&self.world_range_index_counts,
&self.native_handedness_by_range,
frame.camera.position,
horizontal_fov(&frame.camera, frame.aspect),
&frame.material_update.active_materials,
renderer,
)?;
@@ -10517,7 +10556,10 @@ impl ApplicationHandler for StaticVulkanApp {
event_loop.exit();
return;
}
if let Err(error) = self.configure_world_draw_ranges(&mut renderer) {
if let Err(error) = self.configure_world_draw_ranges(
&mut renderer,
size.width.max(1) as f32 / size.height.max(1) as f32,
) {
self.error = Some(error);
event_loop.exit();
return;
@@ -11331,23 +11373,32 @@ mod tests {
let (mesh, mut range) = facing_mesh(0x0148, &triangle);
let original_count = range.index_count;
let mut alpha_override = None;
let update = |range: &VulkanStaticDrawRange, alpha_override: &mut Option<f32>| {
*alpha_override = native_msh_diffuse_alpha_override(range.batch_flags, false);
let update = |range: &VulkanStaticDrawRange,
eye_world: [f32; 3],
alpha_override: &mut Option<f32>| {
let mut current_mesh = mesh.clone();
current_mesh.draw_ranges[0] = *range;
*alpha_override =
native_msh_diffuse_alpha_override(&current_mesh, 0, false, eye_world, 1.3).unwrap();
world_draw_range_index_count(
&mesh,
range,
original_count,
alpha_override.unwrap_or(1.0),
[0.0; 3],
eye_world,
None,
)
};
assert_eq!(update(&range, &mut alpha_override), 0);
assert_eq!(update(&range, [0.0; 3], &mut alpha_override), 0);
range.batch_flags = 0x0108;
assert_eq!(update(&range, &mut alpha_override), original_count);
assert_eq!(update(&range, [0.0; 3], &mut alpha_override), 0);
assert_eq!(
update(&range, [150.0; 3], &mut alpha_override),
original_count
);
range.batch_flags = 0x0148;
assert_eq!(update(&range, &mut alpha_override), 0);
assert_eq!(update(&range, [150.0; 3], &mut alpha_override), 0);
assert_eq!(original_count, 3);
}
@@ -13077,14 +13128,34 @@ mod tests {
}
#[test]
fn native_cpu_shaded_msh_alpha_override_uses_only_the_constant_batch_rule() {
assert_eq!(native_msh_diffuse_alpha_override(0x0148, false), Some(0.0));
assert_eq!(native_msh_diffuse_alpha_override(0x0108, false), None);
assert_eq!(native_msh_diffuse_alpha_override(0x0118, false), None);
assert_eq!(native_msh_diffuse_alpha_override(0x0058, false), None);
assert_eq!(native_msh_diffuse_alpha_override(0x0148, true), None);
assert_eq!(native_msh_diffuse_alpha_override(0x0058, true), None);
assert_eq!(native_msh_diffuse_alpha_override(0x0040, false), None);
fn cpu_msh_portal_alpha_updates_with_distance_and_preserves_type16() {
let mut mesh = VulkanStaticMesh::smoke_triangle();
for flags in [0x0148, 0x0108, 0x0118, 0x0058] {
mesh.draw_ranges[0].batch_flags = flags;
assert_eq!(
native_msh_diffuse_alpha_override(&mesh, 0, false, [0.0, -0.55, -1.0], 1.04),
Ok(Some(0.0))
);
assert_eq!(
native_msh_diffuse_alpha_override(&mesh, 0, true, [0.0, -0.55, -1.0], 1.04),
Ok(None)
);
}
mesh.draw_ranges[0].batch_flags = 0x0118;
let alpha = native_msh_diffuse_alpha_override(&mesh, 0, false, [0.0, -0.55, -25.0], 1.04)
.unwrap()
.unwrap();
assert!((alpha - 0.5).abs() < 0.000_001);
assert_eq!(
native_msh_diffuse_alpha_override(&mesh, 0, false, [0.0, -0.55, -60.0], 1.04),
Ok(Some(1.0))
);
assert!(native_msh_diffuse_alpha_override(&mesh, 0, false, [0.0; 3], 0.0).is_err());
mesh.draw_ranges[0].batch_flags = 0x0040;
assert_eq!(
native_msh_diffuse_alpha_override(&mesh, 0, false, [0.0; 3], 1.04),
Ok(None)
);
}
#[test]