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}