Fix material categories and transparent background ordering
Docs Deploy / Build and Deploy MkDocs (push) Successful in 40s

This commit is contained in:
2026-10-11 21:41:37 +04:00
parent bfe4db1611
commit c90ec80dc0
6 changed files with 496 additions and 53 deletions
+150 -5
View File
@@ -657,6 +657,7 @@ impl MaterialPhaseBinding {
struct MaterialVariantSet {
phase_material_indices: Vec<usize>,
range_indices: Vec<usize>,
pipeline_category: u8,
}
#[derive(Clone, Debug)]
@@ -676,6 +677,19 @@ struct ActiveMaterialState {
diffuse_alpha: f32,
}
fn selected_material_phase(
variant: &MaterialVariantSet,
phase_index: usize,
pipeline_category_for: impl FnOnce(usize) -> Option<u8>,
) -> Result<(usize, u8), String> {
let material_index = selected_material_variant_index(variant, phase_index)
.ok_or_else(|| "MAT0 material animation has no texture variant".to_string())?;
let pipeline_category = pipeline_category_for(material_index).ok_or_else(|| {
format!("MAT0 material animation selector {material_index} is out of bounds")
})?;
Ok((material_index, pipeline_category))
}
fn update_material_animations(
bindings: &mut [MaterialAnimationBinding],
materials: &[VulkanStaticMaterial],
@@ -706,9 +720,13 @@ fn update_material_animations(
.map(|overlay_detail| overlay_detail.sample(binding.initial_phase_index, clock_ms))
.transpose()?;
let mut updated_materials = HashSet::new();
for variant in &binding.variants {
let material_index = selected_material_variant_index(variant, base_sample.phase_index)
.ok_or_else(|| "MAT0 material animation has no texture variant".to_string())?;
for variant in &mut binding.variants {
let (material_index, pipeline_category) =
selected_material_phase(variant, base_sample.phase_index, |index| {
materials
.get(index)
.map(|material| material.pipeline_category)
})?;
for &range_index in &variant.range_indices {
active_materials.insert(
range_index,
@@ -720,7 +738,15 @@ fn update_material_animations(
renderer
.set_draw_range_material(range_index, material_index)
.map_err(|error| format!("update MAT0 material draw selector: {error}"))?;
if variant.pipeline_category != pipeline_category {
renderer
.set_draw_range_material_category(range_index, pipeline_category)
.map_err(|error| {
format!("update MAT0 material pipeline category: {error}")
})?;
}
}
variant.pipeline_category = pipeline_category;
if !updated_materials.insert(material_index) {
continue;
}
@@ -887,6 +913,37 @@ fn native_world_transparent(range: &VulkanStaticDrawRange, diffuse_alpha: f32) -
diffuse_alpha < 1.0 || (range.batch_flags & 0x108) != 0
}
fn configure_world_material_categories(
mesh: &mut VulkanStaticMesh,
materials: &[VulkanStaticMaterial],
world_range_indices: &[usize],
) -> Result<(), String> {
for &range_index in world_range_indices {
let range = mesh
.draw_ranges
.get_mut(range_index)
.ok_or_else(|| "world material range index is out of bounds".to_string())?;
let material = materials
.get(usize::from(range.material_index))
.ok_or_else(|| {
format!(
"world material selector {} is out of bounds",
range.material_index
)
})?;
range.pipeline_state = range
.pipeline_state
.with_material_category(material.pipeline_category)
.ok_or_else(|| {
format!(
"world material selector {} has unsupported native pipeline category {}",
range.material_index, material.pipeline_category
)
})?;
}
Ok(())
}
fn register_material_range(
bindings: &mut [MaterialAnimationBinding],
material_index: usize,
@@ -1164,6 +1221,7 @@ fn append_environment_material(
})?;
materials.push(VulkanStaticMaterial {
material_index,
pipeline_category: phase.pipeline_category,
texture: asset
.phase_textures
.get(phase_index)
@@ -1201,6 +1259,7 @@ fn append_environment_material(
variants: vec![MaterialVariantSet {
phase_material_indices,
range_indices: Vec::new(),
pipeline_category: phases[0].pipeline_category,
}],
base: MaterialPhaseBinding {
document,
@@ -1228,6 +1287,7 @@ fn append_environment_gradient_material(
})?;
materials.push(VulkanStaticMaterial {
material_index,
pipeline_category: 0,
texture: solid_environment_texture([255, 255, 255, 255]),
directional_rgb: [1.0; 3],
additive_rgb: [0.0; 3],
@@ -1275,6 +1335,7 @@ fn append_shadow_material(materials: &mut Vec<VulkanStaticMaterial>) -> Result<u
let index = materials.len();
materials.push(VulkanStaticMaterial {
material_index,
pipeline_category: 0,
texture: shadow_atlas_texture(),
directional_rgb: [1.0; 3],
additive_rgb: [0.0; 3],
@@ -1859,6 +1920,19 @@ fn screen_gradient_quad_vertices(
}
impl EnvironmentGpuScene {
fn transparent_background_range_indices(&self) -> Vec<usize> {
self.sky_layers
.iter()
.filter(|layer| {
matches!(
layer.kind,
SkyPassKind::NebulaStars | SkyPassKind::DomeGradient | SkyPassKind::Clouds
)
})
.map(|layer| layer.range.range_index)
.collect()
}
fn fixed_range_indices(&self) -> HashSet<usize> {
self.sky_layers
.iter()
@@ -2289,9 +2363,9 @@ impl EnvironmentGpuScene {
&& *index != cloud_index
}));
old_order.push(*shadow_range_index);
old_order.extend([nebula_index, dome_index]);
old_order.extend([nebula_index, dome_index, cloud_index]);
old_order.extend(sprite_indices.iter().copied());
old_order.extend([cloud_index, rain_index, snow_index, lightning_index]);
old_order.extend([rain_index, snow_index, lightning_index]);
let remap = reorder_draw_ranges(mesh, &old_order)?;
for layer in &mut scene.sky_layers {
layer.range.range_index = *remap
@@ -4165,6 +4239,7 @@ fn static_preview_terrain_base_materials(
})?;
materials.push(VulkanStaticMaterial {
material_index: preview_selector,
pipeline_category: base_phase.pipeline_category,
texture: base.clone(),
detail_texture: Some(detail.clone()),
overlay_texture: overlay.map(|(texture, _)| texture.clone()),
@@ -4257,6 +4332,7 @@ fn static_preview_terrain_base_materials(
variants: vec![MaterialVariantSet {
phase_material_indices,
range_indices: Vec::new(),
pipeline_category: base_phases[initial_phase_index].1.pipeline_category,
}],
base: MaterialPhaseBinding {
document: base_document,
@@ -4369,6 +4445,7 @@ fn static_preview_component_materials(
})?;
materials.push(VulkanStaticMaterial {
material_index: preview_selector,
pipeline_category: material.pipeline_category,
texture,
detail_texture: None,
overlay_texture: None,
@@ -4405,6 +4482,7 @@ fn static_preview_component_materials(
variants: vec![MaterialVariantSet {
phase_material_indices,
range_indices: Vec::new(),
pipeline_category: material.pipeline_category,
}],
base: MaterialPhaseBinding {
document: material.mat0.clone(),
@@ -4551,6 +4629,7 @@ fn append_static_preview_component_with_lightmaps(
"static preview material selector {source_material_index} has no MAT0 animation binding"
)
})?;
let initial_phase_index = bindings[binding_index].initial_phase_index;
let lightmap = lightmap_textures
.get(usize::from(range.lightmap_index))
.expect("lightmap presence checked above");
@@ -4590,6 +4669,16 @@ fn append_static_preview_component_with_lightmaps(
materials.push(lightmap_material);
phase_material_indices.push(usize::from(index));
}
let initial_selector = phase_material_indices
.get(initial_phase_index)
.copied()
.ok_or_else(|| "static preview lightmap phase table is empty".to_string())?;
let pipeline_category = materials
.get(initial_selector)
.map(|material| material.pipeline_category)
.ok_or_else(|| {
"static preview lightmap material selector is unavailable".to_string()
})?;
let bindings = material_animations.as_deref_mut().ok_or_else(|| {
"static preview lightmap animation storage is unavailable".to_string()
})?;
@@ -4597,6 +4686,7 @@ fn append_static_preview_component_with_lightmaps(
bindings[binding_index].variants.push(MaterialVariantSet {
phase_material_indices: phase_material_indices.clone(),
range_indices: Vec::new(),
pipeline_category,
});
let value = (phase_material_indices, binding_index, variant_index);
lightmap_materials.insert(key, value.clone());
@@ -4913,6 +5003,16 @@ impl StaticVulkanApp {
renderer
.set_draw_range_transparency(range_index, transparent)
.map_err(|error| format!("configure world draw range {range_index}: {error}"))?;
let category = self
.materials
.get(usize::from(range.material_index))
.ok_or_else(|| "world draw range has no material selector".to_string())?
.pipeline_category;
renderer
.set_draw_range_material_category(range_index, category)
.map_err(|error| {
format!("configure world material category {range_index}: {error}")
})?;
renderer
.set_draw_range_index_count(
range_index,
@@ -5381,6 +5481,15 @@ impl ApplicationHandler for StaticVulkanApp {
return;
};
let gpu_initialization_started = Instant::now();
if let Err(error) = configure_world_material_categories(
&mut self.mesh,
&self.materials,
&self.world_range_indices,
) {
self.error = Some(error);
event_loop.exit();
return;
}
let mut renderer = match VulkanSmokeRenderer::new(&VulkanSmokeRendererCreateInfo {
application_name: "fparkan-game".to_string(),
native_handles,
@@ -5399,6 +5508,15 @@ impl ApplicationHandler for StaticVulkanApp {
return;
}
};
let background_ranges = self.environment_gpu.as_ref().map_or_else(
Vec::new,
EnvironmentGpuScene::transparent_background_range_indices,
);
if let Err(error) = renderer.set_world_transparent_background_ranges(&background_ranges) {
self.error = Some(format!("configure world background ranges: {error}"));
event_loop.exit();
return;
}
if let Err(error) = self.configure_world_draw_ranges(&mut renderer) {
self.error = Some(error);
event_loop.exit();
@@ -5975,6 +6093,7 @@ mod tests {
variants: vec![MaterialVariantSet {
phase_material_indices: vec![10],
range_indices: vec![0, 2],
pipeline_category: 0,
}],
base: empty_binding(),
detail: None,
@@ -6029,6 +6148,30 @@ mod tests {
));
}
#[test]
fn material_animation_selects_pipeline_category_with_active_phase() {
let variant = MaterialVariantSet {
phase_material_indices: vec![10, 11],
range_indices: vec![3],
pipeline_category: 1,
};
let category_for = |material_index| match material_index {
10 => Some(1),
11 => Some(2),
_ => None,
};
assert_eq!(
selected_material_phase(&variant, 0, category_for),
Ok((10, 1))
);
assert_eq!(
selected_material_phase(&variant, 1, category_for),
Ok((11, 2))
);
assert!(selected_material_phase(&variant, 2, category_for).is_err());
}
#[test]
fn native_sun_object_source_types_map_to_world_kinds() {
assert_eq!(native_shadow_kind(Some(PROTOTYPE_TYPE_FORT)), Some(3));
@@ -6187,6 +6330,7 @@ mod tests {
variants: vec![MaterialVariantSet {
phase_material_indices: vec![10, 11],
range_indices: Vec::new(),
pipeline_category: 0,
}],
base: MaterialPhaseBinding {
document: empty_mat0,
@@ -6207,6 +6351,7 @@ mod tests {
bindings[binding_index].variants.push(MaterialVariantSet {
phase_material_indices: vec![20, 21],
range_indices: vec![7],
pipeline_category: 0,
});
let lightmap_variant = &bindings[binding_index].variants[1];