Skip to main content

box3d_rust/mesh/
factory.rs

1//! Mesh factory helpers: grid, wave, torus, box, hollow box, platform.
2//!
3//! SPDX-FileCopyrightText: 2026 Erin Catto
4//! SPDX-License-Identifier: MIT
5
6use super::create::create_mesh;
7use super::types::{MeshData, MeshDef};
8use crate::math_functions::{add, cos, sin, Vec3, PI, TWO_PI};
9
10/// Create a grid mesh along the x and z axes. (b3CreateGridMesh)
11pub fn create_grid_mesh(
12    x_count: i32,
13    z_count: i32,
14    cell_width: f32,
15    material_count: i32,
16    identify_edges: bool,
17) -> Option<MeshData> {
18    debug_assert!((0..=u8::MAX as i32).contains(&material_count));
19
20    let vertex_count = (x_count + 1) * (z_count + 1);
21    let mut vertices = vec![Vec3::default(); vertex_count as usize];
22    let mut index = 0usize;
23
24    let x_width = cell_width * (x_count as f32);
25    let z_width = cell_width * (z_count as f32);
26
27    let mut x = -0.5 * x_width;
28    for _ix in 0..=x_count {
29        let mut z = -0.5 * z_width;
30        for _iz in 0..=z_count {
31            vertices[index] = Vec3 { x, y: 0.0, z };
32            z += cell_width;
33            index += 1;
34        }
35        x += cell_width;
36    }
37    debug_assert!(index == vertex_count as usize);
38
39    let triangle_count = 2 * x_count * z_count;
40    let mut indices = vec![0i32; (3 * triangle_count) as usize];
41    let mut material_indices = vec![0u8; triangle_count as usize];
42
43    let mut material_index = 0i32;
44    index = 0;
45    for ix in 0..x_count {
46        for iz in 0..z_count {
47            let index1 = iz + (z_count + 1) * ix;
48            let index2 = index1 + 1;
49            let index3 = index2 + (z_count + 1);
50            let index4 = index3 - 1;
51
52            debug_assert!(index1 < vertex_count);
53            debug_assert!(index2 < vertex_count);
54            debug_assert!(index3 < vertex_count);
55            debug_assert!(index4 < vertex_count);
56
57            indices[index] = index1;
58            indices[index + 1] = index2;
59            indices[index + 2] = index3;
60            indices[index + 3] = index3;
61            indices[index + 4] = index4;
62            indices[index + 5] = index1;
63
64            if material_count > 0 {
65                material_indices[(2 * material_index) as usize] =
66                    (material_index % material_count) as u8;
67                material_indices[(2 * material_index + 1) as usize] =
68                    (material_index % material_count) as u8;
69            }
70
71            material_index += 1;
72            index += 6;
73        }
74    }
75    debug_assert!(index == (3 * triangle_count) as usize);
76
77    let def = MeshDef {
78        vertices,
79        indices,
80        material_indices: if material_count > 0 {
81            material_indices
82        } else {
83            Vec::new()
84        },
85        use_median_split: true,
86        identify_edges,
87        ..Default::default()
88    };
89
90    create_mesh(&def, None)
91}
92
93/// Create a wave mesh along the x and z axes. (b3CreateWaveMesh)
94///
95/// Uses `f32::sin` (C `sinf`) for heights — not the deterministic `b3Sin`.
96pub fn create_wave_mesh(
97    x_count: i32,
98    z_count: i32,
99    cell_width: f32,
100    amplitude: f32,
101    row_frequency: f32,
102    column_frequency: f32,
103) -> Option<MeshData> {
104    let vertex_count = (x_count + 1) * (z_count + 1);
105    let mut vertices = vec![Vec3::default(); vertex_count as usize];
106    let mut index = 0usize;
107
108    let x_width = cell_width * (x_count as f32);
109    let z_width = cell_width * (z_count as f32);
110
111    let omega_z = 2.0 * PI * row_frequency * cell_width;
112    let omega_x = 2.0 * PI * column_frequency * cell_width;
113
114    let mut x = -0.5 * x_width;
115    for ix in 0..=x_count {
116        let row_height = (omega_x * (ix as f32)).sin();
117        let mut z = -0.5 * z_width;
118        for iz in 0..=z_count {
119            let column_height = (omega_z * (iz as f32)).sin();
120            let y = amplitude * row_height * column_height;
121            vertices[index] = Vec3 { x, y, z };
122            z += cell_width;
123            index += 1;
124        }
125        x += cell_width;
126    }
127    debug_assert!(index == vertex_count as usize);
128
129    let triangle_count = 2 * x_count * z_count;
130    let mut indices = vec![0i32; (3 * triangle_count) as usize];
131    index = 0;
132    for ix in 0..x_count {
133        for iz in 0..z_count {
134            let index1 = iz + (z_count + 1) * ix;
135            let index2 = index1 + 1;
136            let index3 = index2 + (z_count + 1);
137            let index4 = index3 - 1;
138
139            indices[index] = index1;
140            indices[index + 1] = index2;
141            indices[index + 2] = index3;
142            indices[index + 3] = index3;
143            indices[index + 4] = index4;
144            indices[index + 5] = index1;
145            index += 6;
146        }
147    }
148    debug_assert!(index == (3 * triangle_count) as usize);
149
150    let def = MeshDef {
151        vertices,
152        indices,
153        use_median_split: true,
154        identify_edges: true,
155        ..Default::default()
156    };
157
158    create_mesh(&def, None)
159}
160
161/// Create a torus mesh. (b3CreateTorusMesh)
162pub fn create_torus_mesh(
163    radial_resolution: i32,
164    tubular_resolution: i32,
165    radius: f32,
166    thickness: f32,
167) -> Option<MeshData> {
168    let mut vertices = Vec::new();
169
170    for radial_index in 0..radial_resolution {
171        for tubular_index in 0..tubular_resolution {
172            let u = (tubular_index as f32) / (tubular_resolution as f32) * TWO_PI;
173            let v = (radial_index as f32) / (radial_resolution as f32) * TWO_PI;
174
175            let x = (radius + thickness * cos(v)) * cos(u);
176            let y = (radius + thickness * cos(v)) * sin(u);
177            let z = thickness * sin(v);
178
179            vertices.push(Vec3 { x, y, z });
180        }
181    }
182
183    let mut indices = Vec::new();
184    for radial_index1 in 0..radial_resolution {
185        let radial_index2 = (radial_index1 + 1) % radial_resolution;
186        for tubular_index1 in 0..tubular_resolution {
187            let tubular_index2 = (tubular_index1 + 1) % tubular_resolution;
188            let index1 = radial_index1 * tubular_resolution + tubular_index1;
189            let index2 = radial_index1 * tubular_resolution + tubular_index2;
190            let index3 = radial_index2 * tubular_resolution + tubular_index2;
191            let index4 = radial_index2 * tubular_resolution + tubular_index1;
192
193            indices.push(index1);
194            indices.push(index2);
195            indices.push(index3);
196            indices.push(index3);
197            indices.push(index4);
198            indices.push(index1);
199        }
200    }
201
202    let def = MeshDef {
203        vertices,
204        indices,
205        use_median_split: false,
206        identify_edges: true,
207        ..Default::default()
208    };
209
210    create_mesh(&def, None)
211}
212
213/// Create a box mesh. (b3CreateBoxMesh)
214pub fn create_box_mesh(center: Vec3, extent: Vec3, identify_edges: bool) -> Option<MeshData> {
215    let x = extent.x;
216    let y = extent.y;
217    let z = extent.z;
218    let mut vertices = [
219        Vec3 { x, y, z },
220        Vec3 { x: -x, y, z },
221        Vec3 { x: -x, y: -y, z },
222        Vec3 { x, y: -y, z },
223        Vec3 { x, y, z: -z },
224        Vec3 { x: -x, y, z: -z },
225        Vec3 {
226            x: -x,
227            y: -y,
228            z: -z,
229        },
230        Vec3 { x, y: -y, z: -z },
231    ];
232
233    for v in &mut vertices {
234        *v = add(*v, center);
235    }
236
237    let indices = [
238        0, 1, 3, 1, 2, 3, // front
239        0, 4, 1, 1, 4, 5, // top
240        0, 3, 7, 4, 0, 7, // right
241        4, 7, 5, 6, 5, 7, // back
242        1, 5, 2, 6, 2, 5, // left
243        3, 2, 7, 6, 7, 2, // bottom
244    ];
245
246    let def = MeshDef {
247        vertices: vertices.to_vec(),
248        indices: indices.to_vec(),
249        use_median_split: false,
250        identify_edges,
251        ..Default::default()
252    };
253
254    create_mesh(&def, None)
255}
256
257/// Create a hollow (inward-facing) box mesh. (b3CreateHollowBoxMesh)
258pub fn create_hollow_box_mesh(center: Vec3, extent: Vec3) -> Option<MeshData> {
259    let x = extent.x;
260    let y = extent.y;
261    let z = extent.z;
262    let mut vertices = [
263        Vec3 { x, y, z },
264        Vec3 { x: -x, y, z },
265        Vec3 { x: -x, y: -y, z },
266        Vec3 { x, y: -y, z },
267        Vec3 { x, y, z: -z },
268        Vec3 { x: -x, y, z: -z },
269        Vec3 {
270            x: -x,
271            y: -y,
272            z: -z,
273        },
274        Vec3 { x, y: -y, z: -z },
275    ];
276
277    for v in &mut vertices {
278        *v = add(*v, center);
279    }
280
281    let indices = [
282        3, 1, 0, 3, 2, 1, // front
283        1, 4, 0, 5, 4, 1, // top
284        7, 3, 0, 7, 0, 4, // right
285        5, 7, 4, 7, 5, 6, // back
286        2, 5, 1, 5, 2, 6, // left
287        7, 2, 3, 2, 7, 6, // bottom
288    ];
289
290    let def = MeshDef {
291        vertices: vertices.to_vec(),
292        indices: indices.to_vec(),
293        use_median_split: false,
294        identify_edges: true,
295        ..Default::default()
296    };
297
298    create_mesh(&def, None)
299}
300
301/// Create a platform mesh (truncated pyramid). (b3CreatePlatformMesh)
302pub fn create_platform_mesh(
303    center: Vec3,
304    height: f32,
305    top_width: f32,
306    bottom_width: f32,
307) -> Option<MeshData> {
308    let hb = 0.5 * bottom_width;
309    let ht = 0.5 * top_width;
310    let hy = 0.5 * height;
311    let mut vertices = [
312        Vec3 {
313            x: ht,
314            y: hy,
315            z: ht,
316        },
317        Vec3 {
318            x: -ht,
319            y: hy,
320            z: ht,
321        },
322        Vec3 {
323            x: -hb,
324            y: -hy,
325            z: hb,
326        },
327        Vec3 {
328            x: hb,
329            y: -hy,
330            z: hb,
331        },
332        Vec3 {
333            x: ht,
334            y: hy,
335            z: -ht,
336        },
337        Vec3 {
338            x: -ht,
339            y: hy,
340            z: -ht,
341        },
342        Vec3 {
343            x: -hb,
344            y: -hy,
345            z: -hb,
346        },
347        Vec3 {
348            x: hb,
349            y: -hy,
350            z: -hb,
351        },
352    ];
353
354    for v in &mut vertices {
355        *v = add(*v, center);
356    }
357
358    let indices = [
359        0, 1, 3, 1, 2, 3, // front
360        0, 4, 1, 1, 4, 5, // top
361        0, 3, 7, 4, 0, 7, // right
362        4, 7, 5, 6, 5, 7, // back
363        1, 5, 2, 6, 2, 5, // left
364        3, 2, 7, 6, 7, 2, // bottom
365    ];
366
367    let def = MeshDef {
368        vertices: vertices.to_vec(),
369        indices: indices.to_vec(),
370        use_median_split: true,
371        identify_edges: true,
372        ..Default::default()
373    };
374
375    create_mesh(&def, None)
376}