Skip to main content

he3d_core/
mesh.rs

1use alloc::vec;
2use alloc::vec::Vec;
3use he3d_math::scalar::{cos, sin};
4use he3d_math::{Vec2, Vec3};
5
6use crate::Error;
7
8/// Maximum number of vertices a mesh may declare.
9/// 一个网格可声明的最大顶点数。
10pub const MAX_VERTEX_COUNT: usize = 16_777_216;
11
12/// A triangle mesh owning its vertices, UVs, and per-triangle normals.
13/// 一个拥有顶点、UV 和逐三角形法线的三角形网格。
14///
15/// Storage is a flat vertex list where every three consecutive vertices form a triangle (no index buffer).
16/// 存储是一个扁平的顶点列表,每三个连续顶点构成一个三角形(没有索引缓冲)。
17/// `uvs.len() == vertices.len()` and `triangle_normals.len() == vertices.len()
18/// 3` are invariants maintained by [`Mesh::new`]. / `uvs.len() == vertices.len()` 与 `triangle_normals.len() == vertices.len() / 3` 是由 [`Mesh::new`] 维持的不变式。
19#[derive(Clone, Debug, PartialEq)]
20pub struct Mesh {
21   vertices: Vec<Vec3>,
22   uvs: Vec<Vec2>,
23   triangle_normals: Vec<Vec3>,
24}
25
26impl Mesh {
27   /// Construct a mesh, validating invariants and computing flat normals.
28   /// 构造一个网格,校验不变式并计算平面法线。
29   pub fn new(vertices: Vec<Vec3>, uvs: Vec<Vec2>) -> Result<Self, Error> {
30      if vertices.is_empty() || vertices.len() > MAX_VERTEX_COUNT {
31         return Err(Error::InvalidSize);
32      }
33      if vertices.len() % 3 != 0 {
34         return Err(Error::InvalidData);
35      }
36      if uvs.len() != vertices.len() {
37         return Err(Error::BufferMismatch);
38      }
39      let mut mesh = Self {
40         vertices,
41         uvs,
42         triangle_normals: Vec::new(),
43      };
44      mesh.recalculate_normals();
45      Ok(mesh)
46   }
47
48   /// Construct a mesh from vertices only, defaulting UVs to zero.
49   /// 仅从顶点构造网格,UV 默认置零。
50   pub fn from_vertices(vertices: Vec<Vec3>) -> Result<Self, Error> {
51      let count = vertices.len();
52      if count == 0 || count > MAX_VERTEX_COUNT {
53         return Err(Error::InvalidSize);
54      }
55      if count % 3 != 0 {
56         return Err(Error::InvalidData);
57      }
58      Self::new(vertices, vec![Vec2::ZERO; count])
59   }
60
61   pub fn vertex_count(&self) -> usize {
62      self.vertices.len()
63   }
64
65   pub fn triangle_count(&self) -> usize {
66      self.vertices.len() / 3
67   }
68
69   pub fn is_valid(&self) -> bool {
70      !self.vertices.is_empty() && self.vertices.len() % 3 == 0
71   }
72
73   pub fn vertices(&self) -> &[Vec3] {
74      &self.vertices
75   }
76
77   pub fn uvs(&self) -> &[Vec2] {
78      &self.uvs
79   }
80
81   pub fn triangle_normals(&self) -> &[Vec3] {
82      &self.triangle_normals
83   }
84
85   /// Recompute flat per-triangle normals from the current vertices.
86   /// 根据当前顶点重新计算逐三角形平面法线。
87   pub fn recalculate_normals(&mut self) {
88      let triangle_count = self.vertices.len() / 3;
89      self.triangle_normals.clear();
90      self.triangle_normals.reserve(triangle_count);
91      for tri in self.vertices.chunks_exact(3) {
92         let e1 = tri[1] - tri[0];
93         let e2 = tri[2] - tri[0];
94         self.triangle_normals.push(e1.cross(e2).normalize_or_zero());
95      }
96   }
97
98   /// A single triangle lying in the XY plane.
99   /// 一个位于 XY 平面上的三角形。
100   pub fn triangle(width: f32, height: f32) -> Result<Self, Error> {
101      if !positive_finite(width) || !positive_finite(height) {
102         return Err(Error::InvalidSize);
103      }
104      let hx = width * 0.5;
105      let hy = height * 0.5;
106      let vertices = vec![
107         Vec3::new(-hx, -hy, 0.0),
108         Vec3::new(0.0, hy, 0.0),
109         Vec3::new(hx, -hy, 0.0),
110      ];
111      let uvs = vec![
112         Vec2::new(0.0, 0.0),
113         Vec2::new(0.5, 1.0),
114         Vec2::new(1.0, 0.0),
115      ];
116      Self::new(vertices, uvs)
117   }
118
119   /// A flat quad on the XZ plane (two triangles).
120   /// XZ 平面上的一个平面四边形(两个三角形)。
121   pub fn plane(width: f32, depth: f32) -> Result<Self, Error> {
122      if !positive_finite(width) || !positive_finite(depth) {
123         return Err(Error::InvalidSize);
124      }
125      let hx = width * 0.5;
126      let hz = depth * 0.5;
127      let vertices = vec![
128         Vec3::new(-hx, 0.0, -hz),
129         Vec3::new(hx, 0.0, hz),
130         Vec3::new(hx, 0.0, -hz),
131         Vec3::new(-hx, 0.0, -hz),
132         Vec3::new(-hx, 0.0, hz),
133         Vec3::new(hx, 0.0, hz),
134      ];
135      let uvs = vec![
136         Vec2::new(0.0, 0.0),
137         Vec2::new(1.0, 1.0),
138         Vec2::new(1.0, 0.0),
139         Vec2::new(0.0, 0.0),
140         Vec2::new(0.0, 1.0),
141         Vec2::new(1.0, 1.0),
142      ];
143      Self::new(vertices, uvs)
144   }
145
146   /// An axis-aligned cube (12 triangles, 36 vertices).
147   /// 一个轴对齐的立方体(12 个三角形,36 个顶点)。
148   pub fn cube(width: f32, height: f32, depth: f32) -> Result<Self, Error> {
149      if !positive_finite(width) || !positive_finite(height) || !positive_finite(depth) {
150         return Err(Error::InvalidSize);
151      }
152      let hx = width * 0.5;
153      let hy = height * 0.5;
154      let hz = depth * 0.5;
155      let p = [
156         Vec3::new(-hx, -hy, -hz),
157         Vec3::new(hx, -hy, -hz),
158         Vec3::new(hx, hy, -hz),
159         Vec3::new(-hx, hy, -hz),
160         Vec3::new(-hx, -hy, hz),
161         Vec3::new(hx, -hy, hz),
162         Vec3::new(hx, hy, hz),
163         Vec3::new(-hx, hy, hz),
164      ];
165      let (uv0, uv1, uv2, uv3) = (
166         Vec2::new(0.0, 0.0),
167         Vec2::new(1.0, 0.0),
168         Vec2::new(1.0, 1.0),
169         Vec2::new(0.0, 1.0),
170      );
171
172      let mut vertices = Vec::with_capacity(36);
173      let mut uvs = Vec::with_capacity(36);
174      let mut emit = |a: usize, b: usize, c: usize, uv: [Vec2; 3]| {
175         push_tri(&mut vertices, &mut uvs, p[a], p[b], p[c], uv);
176      };
177      emit(4, 5, 6, [uv0, uv1, uv2]);
178      emit(4, 6, 7, [uv0, uv2, uv3]);
179      emit(1, 0, 3, [uv0, uv1, uv2]);
180      emit(1, 3, 2, [uv0, uv2, uv3]);
181      emit(0, 4, 7, [uv0, uv1, uv2]);
182      emit(0, 7, 3, [uv0, uv2, uv3]);
183      emit(5, 1, 2, [uv0, uv1, uv2]);
184      emit(5, 2, 6, [uv0, uv2, uv3]);
185      emit(3, 7, 6, [uv0, uv1, uv2]);
186      emit(3, 6, 2, [uv0, uv2, uv3]);
187      emit(0, 1, 5, [uv0, uv1, uv2]);
188      emit(0, 5, 4, [uv0, uv2, uv3]);
189
190      Self::new(vertices, uvs)
191   }
192
193   /// A UV sphere tessellated into `segments` × `rings` quads.
194   /// 一个被细分为 `segments` × `rings` 个四边形的 UV 球体。
195   ///
196   /// Sizing is checked: the total vertex count must fit within [`MAX_VERTEX_COUNT`] without overflowing.
197   /// 尺寸经过校验:顶点总数必须在不溢出的前提下落在 [`MAX_VERTEX_COUNT`] 之内。
198   pub fn sphere(radius: f32, segments: u32, rings: u32) -> Result<Self, Error> {
199      if !positive_finite(radius) {
200         return Err(Error::InvalidSize);
201      }
202      let segments = segments.max(3);
203      let rings = rings.max(2);
204      let total = (segments as usize)
205         .checked_mul(rings as usize)
206         .and_then(|v| v.checked_mul(6))
207         .ok_or(Error::Overflow)?;
208      if total == 0 || total > MAX_VERTEX_COUNT {
209         return Err(Error::InvalidSize);
210      }
211
212      let tau = core::f32::consts::TAU;
213      let pi = core::f32::consts::PI;
214      let mut vertices = Vec::with_capacity(total);
215      let mut uvs = Vec::with_capacity(total);
216
217      for ring in 0..rings {
218         let v0 = ring as f32 / rings as f32;
219         let v1 = (ring + 1) as f32 / rings as f32;
220         let lat0 = (v0 - 0.5) * pi;
221         let lat1 = (v1 - 0.5) * pi;
222         let y0 = sin(lat0) * radius;
223         let y1 = sin(lat1) * radius;
224         let r0 = cos(lat0) * radius;
225         let r1 = cos(lat1) * radius;
226
227         for segment in 0..segments {
228            let u0 = segment as f32 / segments as f32;
229            let u1 = (segment + 1) as f32 / segments as f32;
230            let lon0 = u0 * tau;
231            let lon1 = u1 * tau;
232
233            let p00 = Vec3::new(sin(lon0) * r0, y0, cos(lon0) * r0);
234            let p01 = Vec3::new(sin(lon1) * r0, y0, cos(lon1) * r0);
235            let p10 = Vec3::new(sin(lon0) * r1, y1, cos(lon0) * r1);
236            let p11 = Vec3::new(sin(lon1) * r1, y1, cos(lon1) * r1);
237
238            push_tri(
239               &mut vertices,
240               &mut uvs,
241               p00,
242               p11,
243               p10,
244               [Vec2::new(u0, v0), Vec2::new(u1, v1), Vec2::new(u0, v1)],
245            );
246            push_tri(
247               &mut vertices,
248               &mut uvs,
249               p00,
250               p01,
251               p11,
252               [Vec2::new(u0, v0), Vec2::new(u1, v0), Vec2::new(u1, v1)],
253            );
254         }
255      }
256
257      Self::new(vertices, uvs)
258   }
259}
260
261fn positive_finite(value: f32) -> bool {
262   value.is_finite() && value > 0.0
263}
264
265fn push_tri(
266   vertices: &mut Vec<Vec3>,
267   uvs: &mut Vec<Vec2>,
268   a: Vec3,
269   b: Vec3,
270   c: Vec3,
271   uv: [Vec2; 3],
272) {
273   vertices.extend([a, b, c]);
274   uvs.extend(uv);
275}
276
277#[cfg(test)]
278mod tests {
279   use alloc::vec;
280
281   use super::*;
282
283   #[test]
284   fn triangle_is_valid() {
285      let mesh = Mesh::triangle(1.0, 1.0).unwrap();
286      assert_eq!(mesh.vertex_count(), 3);
287      assert_eq!(mesh.triangle_count(), 1);
288      assert!(mesh.is_valid());
289   }
290
291   #[test]
292   fn cube_has_twelve_triangles() {
293      let mesh = Mesh::cube(1.0, 1.0, 1.0).unwrap();
294      assert_eq!(mesh.vertex_count(), 36);
295      assert_eq!(mesh.triangle_count(), 12);
296   }
297
298   #[test]
299   fn rejects_empty_mesh() {
300      assert_eq!(Mesh::new(Vec::new(), Vec::new()), Err(Error::InvalidSize));
301   }
302
303   #[test]
304   fn from_vertices_empty_is_invalid_size() {
305      assert_eq!(Mesh::from_vertices(Vec::new()), Err(Error::InvalidSize));
306   }
307
308   #[test]
309   fn rejects_non_triangle_vertex_count() {
310      let v = vec![Vec3::ZERO, Vec3::X, Vec3::Y, Vec3::Z];
311      assert_eq!(Mesh::from_vertices(v), Err(Error::InvalidData));
312   }
313
314   #[test]
315   fn rejects_uv_length_mismatch() {
316      let v = vec![Vec3::ZERO, Vec3::X, Vec3::Y];
317      let uv = vec![Vec2::ZERO];
318      assert_eq!(Mesh::new(v, uv), Err(Error::BufferMismatch));
319   }
320
321   #[test]
322   fn rejects_zero_or_negative_primitive_sizes() {
323      assert_eq!(Mesh::triangle(0.0, 1.0), Err(Error::InvalidSize));
324      assert_eq!(Mesh::plane(-1.0, 1.0), Err(Error::InvalidSize));
325      assert_eq!(Mesh::cube(1.0, 1.0, f32::NAN), Err(Error::InvalidSize));
326      assert_eq!(Mesh::sphere(0.0, 8, 4), Err(Error::InvalidSize));
327   }
328
329   #[test]
330   fn sphere_rejects_overflow() {
331      assert!(Mesh::sphere(1.0, u32::MAX, u32::MAX).is_err());
332   }
333
334   #[test]
335   fn sphere_vertex_count_matches_tessellation() {
336      let segments = 16u32;
337      let rings = 8u32;
338      let mesh = Mesh::sphere(0.5, segments, rings).unwrap();
339      assert_eq!(mesh.vertex_count(), (segments * rings * 6) as usize);
340      assert_eq!(mesh.triangle_normals().len(), mesh.triangle_count());
341   }
342}