pebble/graphics/pipeline/
mesh.rs1use crate::{
2 assets::{handle::Handle, storage::Assets, upload::{Asset, AssetSource}},
3 graphics::{
4 pipeline::buffers::{Buffer, BufferBuilder},
5 render::Backend,
6 types::{
7 VertexFormat, VertexStepMode,
8 flags::BufferUsages,
9 pipeline_state::{VertexAttribute, VertexBufferLayout},
10 },
11 },
12};
13
14#[repr(C)]
18#[derive(Copy, Clone, Default, bytemuck::Pod, bytemuck::Zeroable)]
19pub struct Vertex {
20 pub position: glam::Vec3,
21 pub tex_coords: glam::Vec2,
22 pub normal: glam::Vec3,
23 pub tangent: glam::Vec4,
24}
25
26impl Vertex {
27 pub fn new(position: glam::Vec3, tex_coords: glam::Vec2, normal: glam::Vec3, tangent: glam::Vec4) -> Self {
28 Self { position, tex_coords, normal, tangent }
29 }
30
31 pub fn layout() -> VertexBufferLayout {
33 VertexBufferLayout {
34 array_stride: std::mem::size_of::<Vertex>() as u64,
35 step_mode: VertexStepMode::Vertex,
36 attributes: vec![
37 VertexAttribute { format: VertexFormat::Float32x3, offset: 0, shader_location: 0 },
38 VertexAttribute { format: VertexFormat::Float32x2, offset: 12, shader_location: 1 },
39 VertexAttribute { format: VertexFormat::Float32x3, offset: 20, shader_location: 2 },
40 VertexAttribute { format: VertexFormat::Float32x4, offset: 32, shader_location: 3 },
41 ],
42 }
43 }
44}
45
46#[repr(C)]
49#[derive(Copy, Clone, Default, bytemuck::Pod, bytemuck::Zeroable)]
50pub struct InstanceVertex {
51 pub model: glam::Mat4,
52}
53
54impl InstanceVertex {
55 pub fn new(model: glam::Mat4) -> Self {
56 Self { model }
57 }
58
59 pub fn layout() -> VertexBufferLayout {
60 VertexBufferLayout {
61 array_stride: std::mem::size_of::<InstanceVertex>() as u64,
62 step_mode: VertexStepMode::Instance,
63 attributes: vec![
64 VertexAttribute { format: VertexFormat::Float32x4, offset: 0, shader_location: 4 },
65 VertexAttribute { format: VertexFormat::Float32x4, offset: 16, shader_location: 5 },
66 VertexAttribute { format: VertexFormat::Float32x4, offset: 32, shader_location: 6 },
67 VertexAttribute { format: VertexFormat::Float32x4, offset: 48, shader_location: 7 },
68 ],
69 }
70 }
71}
72
73pub struct Mesh<V: bytemuck::Pod = Vertex> {
76 vertices: Option<Vec<V>>,
77 indices: Option<Vec<u32>>,
78}
79
80impl<V: bytemuck::Pod> Mesh<V> {
81 pub fn new(vertices: Vec<V>, indices: Vec<u32>) -> Self {
82 Self { vertices: Some(vertices), indices: Some(indices) }
83 }
84
85 fn validate(&self) {
86 if self.vertices.as_ref().is_none_or(|v| v.is_empty()) {
87 tracing::warn!("Mesh::new(): no vertices — did you forget to pass them?");
88 }
89 if self.indices.as_ref().is_none_or(|i| i.is_empty()) {
90 tracing::warn!("Mesh::new(): no indices — did you forget to pass them?");
91 }
92 }
93
94 pub fn build_asset(self, name: &str, assets: &mut Assets<Mesh<V>>) -> Handle<Mesh<V>> {
95 self.validate();
96 assets.insert(name, self)
97 }
98
99 pub fn vertices(&self) -> Option<&[V]> {
103 self.vertices.as_deref()
104 }
105
106 pub fn indices(&self) -> Option<&[u32]> {
107 self.indices.as_deref()
108 }
109
110 pub fn release_cpu_data(&mut self) {
116 self.vertices = None;
117 self.indices = None;
118 }
119}
120
121pub struct GPUMesh {
123 pub vertex_buffer: Buffer,
124 pub index_buffer: Buffer,
125 pub index_count: u32,
126}
127
128impl<V: bytemuck::Pod + 'static> AssetSource for Mesh<V> {
129 type Processed = GPUMesh;
130}
131
132impl<V: bytemuck::Pod + 'static> Asset<Backend> for Mesh<V> {
133 type Deps<'a> = ();
134
135 fn upload<'a>(&self, backend: &Backend, _deps: &()) -> Option<GPUMesh> {
136 let (Some(vertices), Some(indices)) = (&self.vertices, &self.indices) else {
137 tracing::error!(
138 "Mesh::upload: CPU vertex/index data was released via release_cpu_data() and \
139 the GPU resource needs to be (re)built — this mesh can never become ready"
140 );
141 return None;
142 };
143
144 let vertex_buffer = BufferBuilder::with_data(bytemuck::cast_slice(vertices.as_slice()))
145 .with_label("Mesh Vertex Buffer")
146 .with_usage(BufferUsages::VERTEX)
147 .build(backend);
148 let index_buffer = BufferBuilder::with_data(bytemuck::cast_slice(indices))
149 .with_label("Mesh Index Buffer")
150 .with_usage(BufferUsages::INDEX)
151 .build(backend);
152 Some(GPUMesh {
153 vertex_buffer,
154 index_buffer,
155 index_count: indices.len() as u32,
156 })
157 }
158}