aboutsummaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/fparkan-game/src/main.rs155
1 files changed, 150 insertions, 5 deletions
diff --git a/apps/fparkan-game/src/main.rs b/apps/fparkan-game/src/main.rs
index c974b6b..abfb6da 100644
--- a/apps/fparkan-game/src/main.rs
+++ b/apps/fparkan-game/src/main.rs
@@ -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,
@@ -6030,6 +6149,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));
assert_eq!(native_shadow_kind(Some(PROTOTYPE_TYPE_BTLU)), Some(4));
@@ -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];