feat(terrain): separate shade material selections
This commit is contained in:
@@ -128,6 +128,12 @@ struct TerrainInspectOutput {
|
|||||||
faces: usize,
|
faces: usize,
|
||||||
slots: usize,
|
slots: usize,
|
||||||
material_tags: Vec<TerrainMaterialTagCount>,
|
material_tags: Vec<TerrainMaterialTagCount>,
|
||||||
|
shade_material_pairs: usize,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
shade_material_lookup_min: Option<u16>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
shade_material_lookup_max: Option<u16>,
|
||||||
|
shade_batch_boundaries: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
@@ -473,6 +479,22 @@ fn inspect_terrain(args: &[String]) -> Result<(), String> {
|
|||||||
.map(|(tag, faces)| TerrainMaterialTagCount { tag, faces })
|
.map(|(tag, faces)| TerrainMaterialTagCount { tag, faces })
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
material_tags.sort_unstable_by_key(|entry| entry.tag);
|
material_tags.sort_unstable_by_key(|entry| entry.tag);
|
||||||
|
let shade_material_pairs = (0..terrain.slots.slots_raw.len())
|
||||||
|
.filter_map(|slot_index| terrain.slot_material_pairs(slot_index))
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let shade_material_lookup_min = shade_material_pairs
|
||||||
|
.iter()
|
||||||
|
.map(|pair| pair.shade_selection().material_index)
|
||||||
|
.min();
|
||||||
|
let shade_material_lookup_max = shade_material_pairs
|
||||||
|
.iter()
|
||||||
|
.map(|pair| pair.shade_selection().material_index)
|
||||||
|
.max();
|
||||||
|
let shade_batch_boundaries = shade_material_pairs
|
||||||
|
.iter()
|
||||||
|
.filter(|pair| pair.flags & 0x0010 != 0)
|
||||||
|
.count();
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
serialize_json(&TerrainInspectOutput {
|
serialize_json(&TerrainInspectOutput {
|
||||||
@@ -484,6 +506,10 @@ fn inspect_terrain(args: &[String]) -> Result<(), String> {
|
|||||||
faces: terrain.faces.len(),
|
faces: terrain.faces.len(),
|
||||||
slots: terrain.slots.slots_raw.len(),
|
slots: terrain.slots.slots_raw.len(),
|
||||||
material_tags,
|
material_tags,
|
||||||
|
shade_material_pairs: shade_material_pairs.len(),
|
||||||
|
shade_material_lookup_min,
|
||||||
|
shade_material_lookup_max,
|
||||||
|
shade_batch_boundaries,
|
||||||
})?
|
})?
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -733,12 +759,16 @@ mod tests {
|
|||||||
TerrainMaterialTagCount { tag: 0, faces: 1 },
|
TerrainMaterialTagCount { tag: 0, faces: 1 },
|
||||||
TerrainMaterialTagCount { tag: 3, faces: 1 },
|
TerrainMaterialTagCount { tag: 3, faces: 1 },
|
||||||
],
|
],
|
||||||
|
shade_material_pairs: 2,
|
||||||
|
shade_material_lookup_min: Some(1),
|
||||||
|
shade_material_lookup_max: Some(2),
|
||||||
|
shade_batch_boundaries: 1,
|
||||||
})
|
})
|
||||||
.expect("serialize terrain inspection");
|
.expect("serialize terrain inspection");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
json,
|
json,
|
||||||
"{\"schema_version\":\"fparkan-terrain-inspect-v1\",\"path\":\"DATA/MAPS/AutoMAP/Land.msh\",\"positions\":3,\"min\":[-1.0,-2.0,-3.0],\"max\":[4.0,5.0,6.0],\"faces\":2,\"slots\":4,\"material_tags\":[{\"tag\":0,\"faces\":1},{\"tag\":3,\"faces\":1}]}"
|
"{\"schema_version\":\"fparkan-terrain-inspect-v1\",\"path\":\"DATA/MAPS/AutoMAP/Land.msh\",\"positions\":3,\"min\":[-1.0,-2.0,-3.0],\"max\":[4.0,5.0,6.0],\"faces\":2,\"slots\":4,\"material_tags\":[{\"tag\":0,\"faces\":1},{\"tag\":3,\"faces\":1}],\"shade_material_pairs\":2,\"shade_material_lookup_min\":1,\"shade_material_lookup_max\":2,\"shade_batch_boundaries\":1}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,6 +211,22 @@ pub struct TerrainMaterialPair {
|
|||||||
pub flags: u16,
|
pub flags: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TerrainMaterialPair {
|
||||||
|
/// Returns the material-manager selection used by `GetShade`.
|
||||||
|
///
|
||||||
|
/// `GetShade` owns a manager loaded only with `Shade.wea`, so its
|
||||||
|
/// 16-bit lookup occupies row bits of that manager's table zero. It is
|
||||||
|
/// deliberately separate from the `Land1.wea`/`Land2.wea` selections
|
||||||
|
/// carried by [`TerrainMaterialLayers`].
|
||||||
|
#[must_use]
|
||||||
|
pub const fn shade_selection(self) -> TerrainMaterialSelection {
|
||||||
|
TerrainMaterialSelection {
|
||||||
|
table_index: 0,
|
||||||
|
material_index: self.material_lookup,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TerrainSlotTable {
|
impl TerrainSlotTable {
|
||||||
/// Returns the render-dispatch header for one decoded slot record.
|
/// Returns the render-dispatch header for one decoded slot record.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@@ -1569,6 +1585,13 @@ mod tests {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
document.slot_material_pairs(0).expect("material pairs")[0].shade_selection(),
|
||||||
|
TerrainMaterialSelection {
|
||||||
|
table_index: 0,
|
||||||
|
material_index: 0x0102,
|
||||||
|
}
|
||||||
|
);
|
||||||
assert_eq!(document.slot_material_pairs(1), None);
|
assert_eq!(document.slot_material_pairs(1), None);
|
||||||
|
|
||||||
document.slots.slots_raw[0][2..4].copy_from_slice(&3_u16.to_le_bytes());
|
document.slots.slots_raw[0][2..4].copy_from_slice(&3_u16.to_le_bytes());
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ use std::collections::VecDeque;
|
|||||||
///
|
///
|
||||||
/// Applications access this semantic terrain API through this terrain crate
|
/// Applications access this semantic terrain API through this terrain crate
|
||||||
/// rather than taking a direct dependency on the binary-format parser.
|
/// rather than taking a direct dependency on the binary-format parser.
|
||||||
pub use fparkan_terrain_format::{TerrainMaterialLayers, TerrainMaterialSelection};
|
pub use fparkan_terrain_format::{
|
||||||
|
TerrainMaterialLayers, TerrainMaterialPair, TerrainMaterialSelection,
|
||||||
|
};
|
||||||
|
|
||||||
/// Terrain world.
|
/// Terrain world.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
|
|||||||
@@ -1584,9 +1584,14 @@ index, then groups the pairs by material lookup before constructing draw work.
|
|||||||
`TerrainSlotTable::render_dispatch` exposes exactly those two proven disk
|
`TerrainSlotTable::render_dispatch` exposes exactly those two proven disk
|
||||||
fields, while `LandMeshDocument::slot_material_pairs` decodes the selected
|
fields, while `LandMeshDocument::slot_material_pairs` decodes the selected
|
||||||
type-11 entries as `{ material_lookup: u16, flags: u16 }`. Flag `0x0010` is a
|
type-11 entries as `{ material_lookup: u16, flags: u16 }`. Flag `0x0010` is a
|
||||||
proven batch boundary in `GetShade`; the lookup's mapping to the two WEAR
|
proven batch boundary in `GetShade`. Crucially, this lookup does **not** select
|
||||||
tables, the meanings of its remaining flags, and the final blend operation
|
either map-local Land table: the `GetShade` constructor loads its own one-table
|
||||||
remain unassigned.
|
manager from `system.rlb` resource `Shade.wea`, so the 16-bit value is that
|
||||||
|
manager's table-zero row. `TerrainMaterialPair::shade_selection` records this
|
||||||
|
separate selector instead of conflating it with `Land1.wea`/`Land2.wea`. The
|
||||||
|
AutoDemo inspector now reports 128 slots, 3,174 addressed shade pairs, selector
|
||||||
|
range `0..3173`, and 392 `0x0010` batch boundaries. The meanings of remaining
|
||||||
|
flags and the final blend operation remain unassigned.
|
||||||
|
|
||||||
The static Vulkan bridge now carries each contiguous face run as a separate
|
The static Vulkan bridge now carries each contiguous face run as a separate
|
||||||
draw range keyed by the original packed `material_tag`; it neither reorders
|
draw range keyed by the original packed `material_tag`; it neither reorders
|
||||||
|
|||||||
Reference in New Issue
Block a user