feat(terrain): expose slot render dispatch

This commit is contained in:
2026-07-18 17:20:37 +04:00
parent 8b211f9a1c
commit 5577a04a40
2 changed files with 56 additions and 6 deletions
+47
View File
@@ -183,6 +183,33 @@ pub struct TerrainSlotTable {
pub slots_raw: Vec<[u8; SLOT_STRIDE]>,
}
/// The proven front fields of one 68-byte terrain slot record.
///
/// `Terrain.dll!CLandscape` supplies these fields to `GetShade`'s terrain
/// batch dispatcher: `pair_table_index` selects the pointer-table entry and
/// `pair_count` is the number of adjacent `u16` pairs to process. The pair
/// payload and the later material/blend interpretation remain external to the
/// disk record.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TerrainSlotRenderDispatch {
/// Index into the runtime pair-pointer table.
pub pair_table_index: u16,
/// Number of adjacent `u16` pairs in that selected payload.
pub pair_count: u16,
}
impl TerrainSlotTable {
/// Returns the render-dispatch header for one decoded slot record.
#[must_use]
pub fn render_dispatch(&self, slot_index: usize) -> Option<TerrainSlotRenderDispatch> {
let raw = self.slots_raw.get(slot_index)?;
Some(TerrainSlotRenderDispatch {
pair_table_index: u16::from_le_bytes([raw[0], raw[1]]),
pair_count: u16::from_le_bytes([raw[2], raw[3]]),
})
}
}
/// Land mesh document.
#[derive(Clone, Debug, PartialEq)]
pub struct LandMeshDocument {
@@ -1460,6 +1487,26 @@ mod tests {
);
}
#[test]
fn slot_render_dispatch_retains_pair_table_index_and_count() {
let mut raw = [0_u8; SLOT_STRIDE];
raw[..2].copy_from_slice(&7_u16.to_le_bytes());
raw[2..4].copy_from_slice(&3_u16.to_le_bytes());
let slots = TerrainSlotTable {
header_raw: SLOT_HEADER_ZERO.to_vec(),
slots_raw: vec![raw],
};
assert_eq!(
slots.render_dispatch(0),
Some(TerrainSlotRenderDispatch {
pair_table_index: 7,
pair_count: 3,
})
);
assert_eq!(slots.render_dispatch(1), None);
}
#[test]
fn terrain_material_tag_decodes_two_sidecar_selectors() {
let mut raw_face = face([0, 1, 2], [None, None, None]);
+9 -6
View File
@@ -1575,12 +1575,15 @@ an overlay, blend, or microtexture shader.
The terrain render traversal is now located at `Terrain.dll` RVA `0x11340`.
After visibility/cell work, it derives a 68-byte source-material record and
dispatches renderer-context slot `+16` with four arguments: zero, the record's
16-bit word at `+2`, a pointer selected from a four-byte table by its word at
`+0`, and zero. A preceding renderer-context slot `+60` receives the current
visibility mode. This is the concrete terrain-to-renderer handoff; it does not
call `GetMaterialPhase` directly and does not yet identify either word, the
four-byte payload, or the final blend operation.
dispatches `Terrain!GetShade()` slot `+16` with four arguments: zero, the
record's word at `+2`, a pointer selected from a four-byte table by its word at
`+0`, and zero. `GetShade` is a Terrain singleton, not `niGet3DRender`; the
preceding slot `+60` call sets its visibility mode. Slot `+16` consumes the
second word as the count of adjacent `u16` pairs and the first as the pair-table
index, then groups the pairs by material lookup before constructing draw work.
`TerrainSlotTable::render_dispatch` exposes exactly those two proven disk
fields. The pair payload, its material key, and the final blend operation remain
unassigned.
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