1use crate::scene::hit::Hit;
19use crate::scene::material::Material;
20use crate::scene::mesh::Mesh;
21use crate::scene::ray::Ray;
22use crate::scene::triangle::Triangle;
23use glam::Vec3;
24use ndarray::Array1;
25use parry3d::query::RayCast;
26
27#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
30pub struct StaticMesh {
31 mesh: Mesh,
32 material_indices: Array1<usize>,
33 materials: Array1<Material>,
34}
35
36impl StaticMesh {
38 pub fn new(
39 vertices: Vec<Vec3>,
40 triangles: Vec<Triangle>,
41 material_indices: Vec<usize>,
42 materials: Vec<Material>,
43 ) -> Self {
44 Self {
45 mesh: Mesh::new(vertices, triangles),
46 material_indices: material_indices.into(),
47 materials: materials.into(),
48 }
49 }
50
51 pub fn new_static_mesh(
52 vertices: Vec<Vec3>,
53 triangles: Vec<[u32; 3]>,
54 material_indices: Vec<usize>,
55 materials: Vec<Material>,
56 ) -> Self {
57 let triangles = triangles
58 .iter()
59 .map(|triangle| Triangle {
60 indices: [
61 triangle[0] as usize,
62 triangle[1] as usize,
63 triangle[2] as usize,
64 ],
65 })
66 .collect();
67
68 Self {
69 mesh: Mesh::new(vertices, triangles),
70 material_indices: material_indices.into(),
71 materials: materials.into(),
72 }
73 }
74
75 pub fn new_from_mesh(
77 mesh: Mesh,
78 ) -> Self {
81 let material = Material {
82 absorption: [0.5, 0.3, 0.1],
83 scattering: 0.05,
84 transmission: [0.5, 0.3, 0.1],
85 };
86
87 let num_triangles = mesh.mesh.num_triangles();
88 let materials = vec![material];
89
90 Self {
91 mesh,
92 material_indices: Array1::zeros(num_triangles),
93 materials: materials.into(),
94 }
95 }
96
97 pub(crate) fn closest_hit(
98 &self,
99 ray: &Ray,
100 min_distance: f32,
101 max_distance: f32,
102 ) -> Option<Hit> {
103 let ray = Ray::new(ray.point_at_distance(min_distance), ray.direction());
104
105 let info = self
107 .mesh
108 .mesh
109 .cast_local_ray_and_get_normal(&ray.0, max_distance, false);
110
111 return if let Some(hit) = info {
112 let mut triangle_index = hit.feature.unwrap_face() as usize;
113
114 if triangle_index >= self.mesh.mesh.indices().len() {
115 triangle_index -= self.mesh.mesh.indices().len();
118 }
119
120 let material_index = self.material_indices[triangle_index];
121
122 Some(Hit {
123 distance: hit.toi,
124 triangle_index,
125 object_index: 0,
126 material_index,
127 normal: hit.normal.into(),
128 material: self.materials[material_index],
129 })
130 } else {
131 None
132 };
133 }
134
135 pub(crate) fn any_hit(&self, ray: &Ray, min_distance: f32, max_distance: f32) -> bool {
136 let ray = Ray::new(ray.point_at_distance(min_distance), ray.direction());
137
138 self.mesh
139 .mesh
140 .cast_local_ray(&ray.0, max_distance, false)
141 .is_some()
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn test_static_mesh() {
151 let vertices = vec![
152 Vec3::new(0.0, 0.0, 0.0),
153 Vec3::new(1.0, 0.0, 0.0),
154 Vec3::new(0.0, 1.0, 0.0),
155 Vec3::new(1.0, 1.0, 0.0),
156 Vec3::new(2.0, 0.0, 0.0),
157 ];
158
159 let triangle0 = Triangle { indices: [0, 1, 2] };
160 let triangle1 = Triangle { indices: [1, 2, 3] };
161 let triangle2 = Triangle { indices: [1, 3, 4] };
162 let triangles = vec![triangle0, triangle1, triangle2];
163
164 let material = Material {
165 absorption: [0.1, 0.1, 0.1],
166 scattering: 0.05,
167 transmission: [0.0, 0.0, 0.0],
168 };
169
170 let materials = vec![material];
171 let material_indices = vec![0, 0, 0];
172
173 let static_mesh = StaticMesh::new(vertices, triangles, material_indices, materials);
174
175 let ray0: Ray = Ray::new(Vec3::new(0.1, 0.1, -1.0), Vec3::new(0.0, 0.0, 1.0));
176 let ray1: Ray = Ray::new(Vec3::new(0.6, 0.6, -1.0), Vec3::new(0.0, 0.0, 1.0));
177 let ray2: Ray = Ray::new(Vec3::new(1.1, 0.1, -1.0), Vec3::new(0.0, 0.0, 1.0));
178
179 let ray_miss: Ray = Ray::new(Vec3::new(1.0, 0.0, -1.0), Vec3::new(0.0, 1.0, 0.0));
180
181 let hit0 = static_mesh.closest_hit(&ray0, 0.0, 10.0);
182 let hit1 = static_mesh.closest_hit(&ray1, 0.0, 10.0);
183 let hit2 = static_mesh.closest_hit(&ray2, 0.0, 10.0);
184
185 assert_eq!(hit0.unwrap().triangle_index, 0);
186 assert_eq!(hit1.unwrap().triangle_index, 1);
187 assert_eq!(hit2.unwrap().triangle_index, 2);
188
189 assert!(!static_mesh.any_hit(&ray_miss, 0.0, 10.0));
190 }
191}