From 333c8a0c8ad3f9675c7d4cce1f9cd1c003427ccb Mon Sep 17 00:00:00 2001 From: Baplar Date: Sun, 31 May 2026 22:42:37 +0200 Subject: [PATCH] Use fallback vertex attribute types for multi-uv meshes Fixes #608 --- .../mesh/gpu_mesh/upload/extended_streams.rs | 12 +- .../src/assets/mesh/layout/streams.rs | 130 +++++++++++++----- 2 files changed, 98 insertions(+), 44 deletions(-) diff --git a/crates/renderide/src/assets/mesh/gpu_mesh/upload/extended_streams.rs b/crates/renderide/src/assets/mesh/gpu_mesh/upload/extended_streams.rs index 951a53ec8..9a730e319 100644 --- a/crates/renderide/src/assets/mesh/gpu_mesh/upload/extended_streams.rs +++ b/crates/renderide/src/assets/mesh/gpu_mesh/upload/extended_streams.rs @@ -79,14 +79,10 @@ pub(in crate::assets::mesh::gpu_mesh) struct UvVertexUploadSource<'a> { } fn float4_default_stream_bytes(vertex_count: usize, default: [f32; 4]) -> Vec { - let mut out = vec![0u8; vertex_count * 16]; - for chunk in out.chunks_exact_mut(16) { - for (component, value) in default.iter().enumerate() { - let o = component * 4; - chunk[o..o + 4].copy_from_slice(&value.to_le_bytes()); - } - } - out + default + .map(|f| f.to_le_bytes()) + .as_flattened() + .repeat(vertex_count) } fn float2_zero_stream_bytes(vertex_count: usize) -> Vec { diff --git a/crates/renderide/src/assets/mesh/layout/streams.rs b/crates/renderide/src/assets/mesh/layout/streams.rs index 9e1958313..733b61077 100644 --- a/crates/renderide/src/assets/mesh/layout/streams.rs +++ b/crates/renderide/src/assets/mesh/layout/streams.rs @@ -199,14 +199,90 @@ pub fn uv0_float2_stream_bytes( /// Dense `vec2` vertex stream for an arbitrary two-component attribute. /// -/// Missing or unsupported attributes return zeros so optional embedded shader streams can still -/// bind a stable vertex buffer slot. +/// Missing attributes with supported fallback types will use the first present attribute. +/// Missing or unsupported attributes return zeros so optional embedded shader streams +/// can still bind a stable vertex buffer slot. pub fn vertex_float2_stream_bytes( vertex_data: &[u8], vertex_count: usize, stride: usize, attrs: &[VertexAttributeDescriptor], target: VertexAttributeType, +) -> Option> { + vertex_float2_stream_bytes_fallback( + vertex_data, + vertex_count, + stride, + attrs, + &vertex_attribute_fallbacks(target), + ) +} + +/// Vector of attribute types that can be used in order of preference +/// to fill a requested vertex stream with fallback. +fn vertex_attribute_fallbacks(target: VertexAttributeType) -> Vec { + match target { + VertexAttributeType::UV1 => vec![VertexAttributeType::UV1, VertexAttributeType::UV0], + VertexAttributeType::UV2 => vec![ + VertexAttributeType::UV2, + VertexAttributeType::UV1, + VertexAttributeType::UV0, + ], + VertexAttributeType::UV3 => vec![ + VertexAttributeType::UV3, + VertexAttributeType::UV2, + VertexAttributeType::UV1, + VertexAttributeType::UV0, + ], + VertexAttributeType::UV4 => vec![ + VertexAttributeType::UV4, + VertexAttributeType::UV3, + VertexAttributeType::UV2, + VertexAttributeType::UV1, + VertexAttributeType::UV0, + ], + VertexAttributeType::UV5 => vec![ + VertexAttributeType::UV5, + VertexAttributeType::UV4, + VertexAttributeType::UV3, + VertexAttributeType::UV2, + VertexAttributeType::UV1, + VertexAttributeType::UV0, + ], + VertexAttributeType::UV6 => vec![ + VertexAttributeType::UV6, + VertexAttributeType::UV5, + VertexAttributeType::UV4, + VertexAttributeType::UV3, + VertexAttributeType::UV2, + VertexAttributeType::UV1, + VertexAttributeType::UV0, + ], + VertexAttributeType::UV7 => vec![ + VertexAttributeType::UV7, + VertexAttributeType::UV6, + VertexAttributeType::UV5, + VertexAttributeType::UV4, + VertexAttributeType::UV3, + VertexAttributeType::UV2, + VertexAttributeType::UV1, + VertexAttributeType::UV0, + ], + _ => vec![target], + } +} + +/// Dense `vec2` vertex stream for an arbitrary two-component attribute, +/// with a list of fallback targets to use in case of a missing attribute. +/// +/// Missing or unsupported attributes return zeros so optional embedded shader streams +/// can still bind a stable vertex buffer slot. +fn vertex_float2_stream_bytes_fallback( + vertex_data: &[u8], + vertex_count: usize, + stride: usize, + attrs: &[VertexAttributeDescriptor], + targets: &[VertexAttributeType], ) -> Option> { if vertex_count == 0 || stride == 0 { return None; @@ -216,15 +292,17 @@ pub fn vertex_float2_stream_bytes( return None; } let mut out = vec![0u8; vertex_count * 8]; - let Some(reader) = AttributeReader::from_attrs( - vertex_data, - vertex_count, - stride, - attrs, - target, - VertexDecodeKind::TexCoord, - 2, - ) else { + let Some(reader) = targets.iter().find_map(|&target| { + AttributeReader::from_attrs( + vertex_data, + vertex_count, + stride, + attrs, + target, + VertexDecodeKind::TexCoord, + 2, + ) + }) else { return Some(out); }; if should_parallelize_vertex_stream(vertex_count) { @@ -328,8 +406,10 @@ fn vertex_float4_stream_bytes_with_kind( if vertex_data.len() < need { return None; } - let mut out = vec![0u8; vertex_count * 16]; - fill_float4_stream_with_default(&mut out, default); + let mut out = default + .map(|f| f.to_le_bytes()) + .as_flattened() + .repeat(vertex_count); let Some(reader) = AttributeReader::from_attrs(vertex_data, vertex_count, stride, attrs, target, kind, 1) @@ -423,8 +503,7 @@ pub fn color_float4_stream_bytes( if vertex_data.len() < need { return None; } - let mut out = vec![0u8; vertex_count * 16]; - fill_color_stream_with_white(&mut out); + let mut out = 1.0f32.to_le_bytes().repeat(vertex_count * 4); let Some(reader) = AttributeReader::from_attrs( vertex_data, @@ -452,27 +531,6 @@ pub fn color_float4_stream_bytes( Some(out) } -fn fill_float4_stream_with_default(out: &mut [u8], default: [f32; 4]) { - let default = default.map(|value| value.to_le_bytes()); - let write_chunk = |chunk: &mut [u8]| { - for (component, value) in default.iter().enumerate() { - let offset = component * 4; - chunk[offset..offset + 4].copy_from_slice(value); - } - }; - if should_parallelize_vertex_stream(out.len() / 16) { - out.par_chunks_exact_mut(16) - .with_min_len(VERTEX_STREAM_PARALLEL_CHUNK_VERTICES) - .for_each(write_chunk); - } else { - out.chunks_exact_mut(16).for_each(write_chunk); - } -} - -fn fill_color_stream_with_white(out: &mut [u8]) { - fill_float4_stream_with_default(out, [1.0; 4]); -} - fn decode_vertex_scalar( vertex_data: &[u8], base: usize,