aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--adapters/fparkan-render-vulkan/src/asset_mesh.rs16
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/resources.rs2
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/smoke.rs2
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs12
-rw-r--r--apps/fparkan-game/src/main.rs39
-rw-r--r--docs/tomes/05-render.md12
6 files changed, 57 insertions, 26 deletions
diff --git a/adapters/fparkan-render-vulkan/src/asset_mesh.rs b/adapters/fparkan-render-vulkan/src/asset_mesh.rs
index 7a2f8ba..d74e79c 100644
--- a/adapters/fparkan-render-vulkan/src/asset_mesh.rs
+++ b/adapters/fparkan-render-vulkan/src/asset_mesh.rs
@@ -251,7 +251,7 @@ pub fn project_land_msh_to_static_mesh(
.ok_or(VulkanAssetMeshError::IndexOutOfRange)?,
);
for face in &terrain.faces {
- indices.extend(face.vertices);
+ indices.extend(face.vertices.map(u32::from));
}
if indices.is_empty() {
return Err(VulkanAssetMeshError::EmptyGeometry);
@@ -280,7 +280,7 @@ pub fn project_land_msh_to_static_mesh_in_xy_frame(
.ok_or(VulkanAssetMeshError::IndexOutOfRange)?,
);
for face in &terrain.faces {
- indices.extend(face.vertices);
+ indices.extend(face.vertices.map(u32::from));
}
if indices.is_empty() {
return Err(VulkanAssetMeshError::EmptyGeometry);
@@ -357,7 +357,7 @@ pub fn project_land_msh_to_static_mesh_in_world_space(
})
}
-fn static_terrain_indices(terrain: &LandMeshDocument) -> Result<Vec<u16>, VulkanAssetMeshError> {
+fn static_terrain_indices(terrain: &LandMeshDocument) -> Result<Vec<u32>, VulkanAssetMeshError> {
let mut indices = Vec::with_capacity(
terrain
.faces
@@ -366,7 +366,7 @@ fn static_terrain_indices(terrain: &LandMeshDocument) -> Result<Vec<u16>, Vulkan
.ok_or(VulkanAssetMeshError::IndexOutOfRange)?,
);
for face in &terrain.faces {
- indices.extend(face.vertices);
+ indices.extend(face.vertices.map(u32::from));
}
if indices.is_empty() {
return Err(VulkanAssetMeshError::EmptyGeometry);
@@ -391,7 +391,7 @@ fn static_terrain_draw_ranges(
fn static_model_indices_and_ranges(
model: &ModelAsset,
-) -> Result<(Vec<u16>, Vec<VulkanStaticDrawRange>), VulkanAssetMeshError> {
+) -> Result<(Vec<u32>, Vec<VulkanStaticDrawRange>), VulkanAssetMeshError> {
let mut indices = Vec::new();
let mut draw_ranges = Vec::with_capacity(model.batches.len());
for batch in &model.batches {
@@ -411,7 +411,7 @@ fn static_model_indices_and_ranges(
.base_vertex
.checked_add(u32::from(raw_index))
.ok_or(VulkanAssetMeshError::IndexOutOfRange)?;
- indices.push(u16::try_from(index).map_err(|_| VulkanAssetMeshError::IndexOutOfRange)?);
+ indices.push(index);
}
draw_ranges.push(VulkanStaticDrawRange {
first_index,
@@ -429,7 +429,7 @@ fn static_model_indices_and_ranges(
fn static_mesh_xy_bounds(
positions: &[[f32; 3]],
- indices: &[u16],
+ indices: &[u32],
) -> Result<(f32, f32, f32, f32), VulkanAssetMeshError> {
let mut min_x = f32::INFINITY;
let mut max_x = f32::NEG_INFINITY;
@@ -437,7 +437,7 @@ fn static_mesh_xy_bounds(
let mut max_y = f32::NEG_INFINITY;
for &index in indices {
let position = positions
- .get(usize::from(index))
+ .get(usize::try_from(index).map_err(|_| VulkanAssetMeshError::IndexOutOfRange)?)
.ok_or(VulkanAssetMeshError::IndexOutOfRange)?;
if !position.iter().all(|value| value.is_finite()) {
return Err(VulkanAssetMeshError::NonFinitePosition);
diff --git a/adapters/fparkan-render-vulkan/src/ffi/resources.rs b/adapters/fparkan-render-vulkan/src/ffi/resources.rs
index b7e84bd..1d38503 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/resources.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/resources.rs
@@ -236,7 +236,7 @@ pub(super) fn create_static_mesh_index_buffer(
device: &VulkanLogicalDeviceProbe,
mesh: &VulkanStaticMesh,
) -> Result<VulkanAllocatedBuffer, VulkanSmokeRendererError> {
- let mut bytes = Vec::with_capacity(mesh.indices.len() * std::mem::size_of::<u16>());
+ let mut bytes = Vec::with_capacity(mesh.indices.len() * std::mem::size_of::<u32>());
for &index in &mesh.indices {
bytes.extend_from_slice(&index.to_ne_bytes());
}
diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
index 63066b4..f7c72b6 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
@@ -701,7 +701,7 @@ impl VulkanSmokeRenderer {
command_buffer,
self.index_buffer_ref()?.buffer,
0,
- vk::IndexType::UINT16,
+ vk::IndexType::UINT32,
);
let clip_from_world = matrix_bytes(self.camera.clip_from_world);
device.device().cmd_push_constants(
diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
index 47cab7d..2ca517b 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
@@ -126,7 +126,7 @@ pub struct VulkanStaticMesh {
/// Vertex data in pipeline order.
pub vertices: Vec<VulkanStaticVertex>,
/// Triangle-list indices into [`Self::vertices`].
- pub indices: Vec<u16>,
+ pub indices: Vec<u32>,
/// Source-preserving triangle draw ranges in [`Self::indices`].
pub draw_ranges: Vec<VulkanStaticDrawRange>,
}
@@ -296,11 +296,11 @@ impl VulkanStaticMesh {
if usize::try_from(expected_first).ok() != Some(self.indices.len()) {
return Err("static mesh draw ranges must cover all indices");
}
- if self
- .indices
- .iter()
- .any(|&index| usize::from(index) >= self.vertices.len())
- {
+ if self.indices.iter().any(|&index| {
+ usize::try_from(index)
+ .ok()
+ .is_none_or(|index| index >= self.vertices.len())
+ }) {
return Err("static mesh index exceeds vertex count");
}
Ok(())
diff --git a/apps/fparkan-game/src/main.rs b/apps/fparkan-game/src/main.rs
index af5d7ed..850b509 100644
--- a/apps/fparkan-game/src/main.rs
+++ b/apps/fparkan-game/src/main.rs
@@ -465,28 +465,24 @@ fn append_static_preview_component(
component: VulkanStaticMesh,
selector_remap: &[(u16, u16)],
) -> Result<(), String> {
- let vertex_base = u16::try_from(target.vertices.len()).map_err(|_| {
- "static preview exceeds the available 16-bit vertex index space".to_string()
- })?;
+ let vertex_base = u32::try_from(target.vertices.len())
+ .map_err(|_| "static preview vertex count exceeds u32".to_string())?;
let first_index_base = u32::try_from(target.indices.len())
.map_err(|_| "static preview index count exceeds u32".to_string())?;
target
.vertices
.len()
.checked_add(component.vertices.len())
- .filter(|count| *count <= usize::from(u16::MAX) + 1)
- .ok_or_else(|| {
- "static preview exceeds the available 16-bit vertex index space".to_string()
- })?;
+ .ok_or_else(|| "static preview vertex count exceeds addressable memory".to_string())?;
target.vertices.extend(component.vertices);
target.indices.extend(
component
.indices
.into_iter()
.map(|index| {
- index.checked_add(vertex_base).ok_or_else(|| {
- "static preview exceeds the available 16-bit vertex index space".to_string()
- })
+ index
+ .checked_add(vertex_base)
+ .ok_or_else(|| "static preview vertex index exceeds u32".to_string())
})
.collect::<Result<Vec<_>, _>>()?,
);
@@ -1293,6 +1289,29 @@ mod tests {
}
#[test]
+ fn static_preview_component_merge_keeps_indices_above_u16() -> Result<(), String> {
+ let vertex = fparkan_render_vulkan::VulkanStaticVertex {
+ position: [0.0, 0.0, 0.0],
+ color: [1.0, 1.0, 1.0],
+ uv: [0.0, 0.0],
+ };
+ let mut merged = VulkanStaticMesh {
+ vertices: vec![vertex; usize::from(u16::MAX) + 1],
+ indices: Vec::new(),
+ draw_ranges: Vec::new(),
+ };
+
+ append_static_preview_component(
+ &mut merged,
+ VulkanStaticMesh::smoke_triangle(),
+ &[(0, 0)],
+ )?;
+
+ assert_eq!(merged.indices, vec![65_536, 65_537, 65_538]);
+ Ok(())
+ }
+
+ #[test]
fn render_commands_follow_snapshot_order() -> Result<(), String> {
let snapshot = WorldSnapshot {
tick: Tick(7),
diff --git a/docs/tomes/05-render.md b/docs/tomes/05-render.md
index 543af27..e24db94 100644
--- a/docs/tomes/05-render.md
+++ b/docs/tomes/05-render.md
@@ -1475,6 +1475,18 @@ is a real original-asset rendering milestone, but not pixel parity or gameplay
camera parity: it intentionally renders the bounded first preview root and
uses a one-time offline camera capture.
+Expanding that same preview to all eight AutoDemo mission objects exposed a
+different, renderer-local limit: the merged static scene crossed 65,535
+vertices before any Vulkan command was recorded. `VulkanStaticMesh` now uses a
+32-bit GPU index buffer (`VK_INDEX_TYPE_UINT32`), while source MSH/TerrainFace28
+indices remain decoded in their original 16-bit representations and are widened
+only at the static-render bridge. A regression test crosses the former boundary.
+The full captured-camera AutoDemo preview completed at 13.369 seconds and
+rendered three 1280×720 native frames: 8 mission objects, 66 mesh components,
+67 material descriptors, and zero Vulkan validation warnings or errors. It is
+still a static source-world preview: raw mission orientation, terrain material
+selection, lighting and animation have not yet been claimed as parity.
+
A fresh no-input launch of the canonical `iron_3d.exe` did create a responsive
window titled `Parkan. Железная Стратегия`. A read-only probe then requested
`PROCESS_QUERY_INFORMATION | PROCESS_VM_READ` and attempted to read the known