aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-07-18 16:20:37 +0300
committerValentin Popov <valentin@popov.link>2026-07-18 16:20:37 +0300
commit5577a04a4093a529d2c64a24e1ab4c72941b5ff1 (patch)
tree48a647126370ee94aed31ac603f1383511ead536
parent8b211f9a1cdd562488f66ab0be1c9a96291c7944 (diff)
downloadfparkan-5577a04a4093a529d2c64a24e1ab4c72941b5ff1.tar.xz
fparkan-5577a04a4093a529d2c64a24e1ab4c72941b5ff1.zip
feat(terrain): expose slot render dispatch
-rw-r--r--crates/fparkan-terrain-format/src/lib.rs47
-rw-r--r--docs/tomes/05-render.md15
2 files changed, 56 insertions, 6 deletions
diff --git a/crates/fparkan-terrain-format/src/lib.rs b/crates/fparkan-terrain-format/src/lib.rs
index 6e018c0..0051b82 100644
--- a/crates/fparkan-terrain-format/src/lib.rs
+++ b/crates/fparkan-terrain-format/src/lib.rs
@@ -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 {
@@ -1461,6 +1488,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]);
raw_face[4..6].copy_from_slice(&0x0102_u16.to_le_bytes());
diff --git a/docs/tomes/05-render.md b/docs/tomes/05-render.md
index 749d073..29319ed 100644
--- a/docs/tomes/05-render.md
+++ b/docs/tomes/05-render.md
@@ -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