1use std::path::Path;
2use std::sync::Arc;
3
4use crate::renderer::extras::ModelVertex;
5use crate::renderer::types::{BufferInitDescriptor, BufferUsage};
6use crate::renderer::{Renderer, Texture};
7use crate::transform::Transformable;
8use crate::types::Color;
9
10#[repr(C)]
14#[derive(Debug, Clone, Copy)]
15pub(crate) struct ModelUniform {
16 pub(crate) model: [[f32; 4]; 4],
17 pub(crate) highlight: [f32; 4],
18}
19
20#[derive(Clone)]
21pub struct Model {
22 pub meshes: Vec<Mesh>,
23 pub materials: Vec<Material>,
24 pub transform: glam::Mat4,
27 pub highlight: Color,
32 pub bounding_center: glam::Vec3,
34 pub bounding_radius: f32,
37 pub(crate) transform_buffer: wgpu::Buffer,
38 pub(crate) transform_bind_group: wgpu::BindGroup,
39}
40
41#[derive(Clone)]
42pub struct Mesh {
43 pub name: String,
44 pub vertex_buffer: wgpu::Buffer,
45 pub index_buffer: wgpu::Buffer,
46 pub num_indices: u32,
47 pub material: usize,
48 pub positions: Vec<glam::Vec3>,
52 pub indices: Vec<u32>,
54}
55
56#[derive(Clone)]
57pub struct Material {
58 pub name: String,
59 pub diffuse_texture: Arc<Texture>,
60}
61
62impl Model {
63 pub fn load(renderer: &Renderer, path: impl AsRef<Path>) -> anyhow::Result<Self> {
66 let path = crate::util::resolve_resource_path(path);
67 let parent = path.parent().unwrap_or_else(|| Path::new(""));
68
69 let (obj_models, obj_materials) = tobj::load_obj(
70 &path,
71 &tobj::LoadOptions {
72 triangulate: true,
73 single_index: true,
74 ..Default::default()
75 },
76 )?;
77 let obj_materials = obj_materials?;
78
79 let mut materials: Vec<Material> = obj_materials
80 .into_iter()
81 .map(|mat| {
82 let diffuse_texture = match &mat.diffuse_texture {
83 Some(texture_file) => {
84 Texture::from_path(renderer, parent.join(texture_file)).map_err(|e| anyhow::anyhow!(e))?
85 }
86 None => Texture::from_color(renderer, [255, 255, 255, 255]).map_err(|e| anyhow::anyhow!(e))?,
87 };
88 Ok(Material {
89 name: mat.name,
90 diffuse_texture: Arc::new(diffuse_texture),
91 })
92 })
93 .collect::<anyhow::Result<_>>()?;
94
95 if materials.is_empty() {
96 let diffuse_texture = Texture::from_color(renderer, [255, 255, 255, 255]).map_err(|e| anyhow::anyhow!(e))?;
97 materials.push(Material {
98 name: "default".to_string(),
99 diffuse_texture: Arc::new(diffuse_texture),
100 });
101 }
102 let fallback_material = materials.len() - 1;
103
104 let mut bounds_min = glam::Vec3::splat(f32::INFINITY);
105 let mut bounds_max = glam::Vec3::splat(f32::NEG_INFINITY);
106
107 let meshes = obj_models
108 .into_iter()
109 .map(|obj_model| {
110 let mesh = obj_model.mesh;
111 let vertex_count = mesh.positions.len() / 3;
112 let vertices: Vec<ModelVertex> = (0..vertex_count)
113 .map(|i| {
114 let position = [mesh.positions[i * 3], mesh.positions[i * 3 + 1], mesh.positions[i * 3 + 2]];
115 let p = glam::Vec3::from_array(position);
116 bounds_min = bounds_min.min(p);
117 bounds_max = bounds_max.max(p);
118 ModelVertex {
119 position,
120 tex_coords: if mesh.texcoords.is_empty() {
121 [0.0, 0.0]
122 } else {
123 [mesh.texcoords[i * 2], 1.0 - mesh.texcoords[i * 2 + 1]]
125 },
126 normal: if mesh.normals.is_empty() {
127 [0.0, 0.0, 0.0]
128 } else {
129 [mesh.normals[i * 3], mesh.normals[i * 3 + 1], mesh.normals[i * 3 + 2]]
130 },
131 }
132 })
133 .collect();
134
135 let vertex_buffer = renderer.create_buffer_init(&BufferInitDescriptor {
136 label: Some(&format!("{} Vertex Buffer", obj_model.name)),
137 contents: crate::util::slice_to_bytes(&vertices),
138 usage: BufferUsage::Vertex,
139 });
140 let index_buffer = renderer.create_buffer_init(&BufferInitDescriptor {
141 label: Some(&format!("{} Index Buffer", obj_model.name)),
142 contents: crate::util::slice_to_bytes(&mesh.indices),
143 usage: BufferUsage::Index,
144 });
145
146 let positions: Vec<glam::Vec3> = vertices.iter().map(|v| glam::Vec3::from_array(v.position)).collect();
147
148 Mesh {
149 name: obj_model.name,
150 vertex_buffer,
151 index_buffer,
152 num_indices: mesh.indices.len() as u32,
153 material: mesh.material_id.unwrap_or(fallback_material).min(fallback_material),
154 positions,
155 indices: mesh.indices,
156 }
157 })
158 .collect();
159
160 let (bounding_center, bounding_radius) = if bounds_min.x.is_finite() {
161 ((bounds_min + bounds_max) * 0.5, (bounds_max - bounds_min).length() * 0.5)
162 } else {
163 (glam::Vec3::ZERO, 0.0)
164 };
165
166 let transform_buffer = renderer.device.create_buffer(&wgpu::BufferDescriptor {
167 label: Some("Model Transform Buffer"),
168 size: std::mem::size_of::<ModelUniform>() as u64,
169 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
170 mapped_at_creation: false,
171 });
172 renderer.queue.write_buffer(
173 &transform_buffer,
174 0,
175 crate::util::slice_to_bytes(&[ModelUniform {
176 model: glam::Mat4::IDENTITY.to_cols_array_2d(),
177 highlight: [1.0, 1.0, 1.0, 0.0],
178 }]),
179 );
180 let transform_bind_group = renderer.device.create_bind_group(&wgpu::BindGroupDescriptor {
181 layout: &renderer.model_transform_bind_group_layout,
182 entries: &[wgpu::BindGroupEntry {
183 binding: 0,
184 resource: transform_buffer.as_entire_binding(),
185 }],
186 label: Some("model_transform_bind_group"),
187 });
188
189 Ok(Self {
190 meshes,
191 materials,
192 transform: glam::Mat4::IDENTITY,
193 highlight: Color::new(1.0, 1.0, 1.0, 0.0),
194 bounding_center,
195 bounding_radius,
196 transform_buffer,
197 transform_bind_group,
198 })
199 }
200}
201
202impl Transformable for Model {
203 fn transform(&self) -> glam::Mat4 {
204 self.transform
205 }
206
207 fn transform_mut(&mut self) -> &mut glam::Mat4 {
208 &mut self.transform
209 }
210}