Skip to main content

viewport_lib/resources/mesh/
gpu_mesh.rs

1//! Per-mesh GPU buffers and bind group (`GpuMesh`).
2
3use crate::resources::types::*;
4
5// ---------------------------------------------------------------------------
6// GpuMesh: per-object GPU buffers
7// ---------------------------------------------------------------------------
8
9/// GPU buffers and bind group for a single mesh.
10pub struct GpuMesh {
11    /// Interleaved position + normal vertex buffer (Vertex layout).
12    pub vertex_buffer: wgpu::Buffer,
13    /// Triangle index buffer (for solid rendering).
14    pub index_buffer: wgpu::Buffer,
15    /// Number of indices in the triangle index buffer.
16    pub index_count: u32,
17    /// Edge index buffer (deduplicated pairs, for wireframe LineList rendering).
18    pub edge_index_buffer: wgpu::Buffer,
19    /// Number of indices in the edge index buffer.
20    pub edge_index_count: u32,
21    /// Vertex buffer for per-vertex normal visualization lines (LineList topology).
22    /// Each normal contributes two vertices: the vertex position and position + normal * 0.1.
23    /// None if no normal data is available.
24    pub normal_line_buffer: Option<wgpu::Buffer>,
25    /// Number of vertices in the normal line buffer (2 per normal line).
26    pub normal_line_count: u32,
27    /// Per-object uniform buffer (model matrix, material, selection state).
28    pub object_uniform_buf: wgpu::Buffer,
29    /// Bind group (group 1) combining `object_uniform_buf` with texture views.
30    /// Texture views are the fallback 1x1 textures by default; rebuilt when material
31    /// texture assignment changes (tracked via `last_tex_key`).
32    pub object_bind_group: wgpu::BindGroup,
33    /// Last texture/attribute key used to build `object_bind_group`. `u64::MAX` = fallback / none.
34    /// Fields: `(albedo, normal_map, ao_map, lut, attr_hash, matcap, warp_hash, metallic_roughness, emissive, position_override_gen, normal_override_gen)`.
35    /// The trailing two slots are bumped by `set_position_override_buffer`,
36    /// `set_normal_override_buffer`, and their `clear_*` counterparts, so the
37    /// next `update_mesh_texture_bind_group` call rebuilds with the real
38    /// override buffer (or the fallback) at bindings 13/14.
39    pub(crate) last_tex_key: (u64, u64, u64, u64, u64, u64, u64, u64, u64, u64, u64),
40    /// Per-named-attribute GPU storage buffers (f32 per vertex, STORAGE usage).
41    pub attribute_buffers: std::collections::HashMap<String, wgpu::Buffer>,
42    /// Scalar range `(min, max)` per attribute, computed at upload time.
43    pub attribute_ranges: std::collections::HashMap<String, (f32, f32)>,
44    /// Non-indexed vertex buffer containing 3xN expanded vertices for face-attribute rendering.
45    /// `None` if no `Face` or `FaceColour` attributes exist for this mesh.
46    pub face_vertex_buffer: Option<wgpu::Buffer>,
47    /// Named face scalar buffers: 3N `f32` entries (value replicated for all 3 vertices of each tri).
48    pub face_attribute_buffers: std::collections::HashMap<String, wgpu::Buffer>,
49    /// Named face colour buffers: 3N `[f32; 4]` entries (colour replicated for all 3 vertices of each tri).
50    pub face_colour_buffers: std::collections::HashMap<String, wgpu::Buffer>,
51    /// Per-vertex vector attribute buffers: flat `array<f32>` with 3 values per vertex.
52    /// Uploaded from `AttributeData::VertexVector`; used by the Surface LIC surface pass.
53    pub vector_attribute_buffers: std::collections::HashMap<String, wgpu::Buffer>,
54    /// Optional per-vertex position override buffer.
55    ///
56    /// When `Some`, the standard mesh pipeline reads positions from this buffer
57    /// (group 1 binding 13, an `array<vec3<f32>>`) instead of the vertex
58    /// buffer's position attribute. Set via
59    /// `DeviceResources::set_position_override_buffer` and consumed by a
60    /// `GpuPlugin`'s compute output. Mutually exclusive per frame with
61    /// `write_mesh_positions_normals`; the two paths race if both are used.
62    pub position_override_buffer: Option<wgpu::Buffer>,
63    /// Optional per-vertex normal override buffer. Same contract as
64    /// `position_override_buffer` but bound at group 1 binding 14.
65    pub normal_override_buffer: Option<wgpu::Buffer>,
66    /// Monotonic counter bumped each time `set_position_override_buffer` or
67    /// `clear_position_override` is called. Folded into `last_tex_key` so the
68    /// object bind group rebuilds on the next `prepare()` call.
69    pub(crate) position_override_gen: u64,
70    /// Same idea for normal override (re)bind tracking.
71    pub(crate) normal_override_gen: u64,
72    /// Uniform buffer for normal-line rendering: always has selected=0, wireframe=0.
73    /// Updated each frame in prepare() with the object's model matrix only.
74    pub normal_uniform_buf: wgpu::Buffer,
75    /// Bind group referencing `normal_uniform_buf` : used when drawing normal lines.
76    pub normal_bind_group: wgpu::BindGroup,
77    /// Local-space axis-aligned bounding box computed from vertex positions at upload time.
78    pub aabb: crate::scene::aabb::Aabb,
79    /// CPU-side positions retained for cap geometry generation (clip plane cross-section fill).
80    pub(crate) cpu_positions: Option<Vec<[f32; 3]>>,
81    /// CPU-side triangle indices retained for cap geometry generation.
82    pub(crate) cpu_indices: Option<Vec<u32>>,
83}
84
85impl GpuMesh {
86    /// Number of vertices in the mesh, derived from the vertex buffer size.
87    ///
88    /// Useful for sizing or validating a position/normal override buffer
89    /// against the mesh it will be bound to.
90    pub fn vertex_count(&self) -> usize {
91        (self.vertex_buffer.size() / std::mem::size_of::<Vertex>() as u64) as usize
92    }
93
94    /// Total GPU buffer bytes held by this mesh.
95    ///
96    /// Sums the geometry, attribute, override, and per-object uniform buffers.
97    /// Used for the resident-bytes accounting so freeing the mesh decrements the
98    /// running total by the same amount it added at upload.
99    pub(crate) fn gpu_byte_size(&self) -> u64 {
100        let mut bytes = self.vertex_buffer.size()
101            + self.index_buffer.size()
102            + self.edge_index_buffer.size()
103            + self.object_uniform_buf.size()
104            + self.normal_uniform_buf.size();
105        for buf in [
106            self.normal_line_buffer.as_ref(),
107            self.face_vertex_buffer.as_ref(),
108            self.position_override_buffer.as_ref(),
109            self.normal_override_buffer.as_ref(),
110        ]
111        .into_iter()
112        .flatten()
113        {
114            bytes += buf.size();
115        }
116        for map in [
117            &self.attribute_buffers,
118            &self.face_attribute_buffers,
119            &self.face_colour_buffers,
120            &self.vector_attribute_buffers,
121        ] {
122            bytes += map.values().map(|b| b.size()).sum::<u64>();
123        }
124        bytes
125    }
126}