mesh_tools/builder_primitives.rs
1//! # Primitive Shape Generation Implementation
2//!
3//! This module implements the primitive shape generation methods for the `GltfBuilder` struct.
4//! It provides functionality for creating standard 3D shapes such as boxes, spheres, planes,
5//! cylinders, cones, tori, and more.
6//!
7//! Each shape generation method:
8//! 1. Generates the geometry data (vertices, indices, normals, UVs)
9//! 2. Creates the necessary buffer views and accessors
10//! 3. Creates a mesh with the appropriate primitives
11//! 4. Returns the index of the created mesh
12//!
13//! These methods are the high-level interface for the low-level geometry generation
14//! functions in the `primitives` module.
15
16use crate::builder::GltfBuilder;
17use crate::constants::{accessor_type, buffer_view_target, component_type};
18use crate::models::Primitive;
19use crate::primitives;
20use std::collections::HashMap;
21use crate::compat::{Point3, Vector2, Vector3};
22
23/// A triangle represented by three vertex indices
24///
25/// Uses u32 indices.
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct Triangle {
28 /// First vertex index
29 pub a: u32,
30 /// Second vertex index
31 pub b: u32,
32 /// Third vertex index
33 pub c: u32,
34}
35
36impl Triangle {
37 /// Create a new triangle with the given vertex indices
38 ///
39 /// # Arguments
40 /// * `a` - First vertex index
41 /// * `b` - Second vertex index
42 /// * `c` - Third vertex index
43 ///
44 /// # Returns
45 /// A new Triangle instance
46 pub fn new(a: u32, b: u32, c: u32) -> Self {
47 Self { a, b, c }
48 }
49}
50
51impl GltfBuilder {
52 /// Create a simple cubic box mesh with the specified size
53 ///
54 /// This method creates a cube centered at the origin with equal dimensions on all sides.
55 /// The cube has properly generated normals and texture coordinates for each face.
56 ///
57 /// # Parameters
58 /// * `size` - The length of each side of the cube
59 ///
60 /// # Returns
61 /// The index of the created mesh in the glTF document's meshes array
62 ///
63 /// # Example
64 /// ```
65 /// use mesh_tools::GltfBuilder;
66 /// let mut builder = GltfBuilder::new();
67 /// let box_mesh = builder.create_box(2.0); // Creates a 2x2x2 cube
68 /// ```
69 pub fn create_box(&mut self, size: f32) -> usize {
70 // Box centered at origin with given size
71 let half_size = size / 2.0;
72
73 // 8 vertices for a cube (8 corners) using Point3
74 let positions = vec![
75 // Front face (z+)
76 crate::compat::point3::new(-half_size, -half_size, half_size), // 0: bottom-left-front
77 crate::compat::point3::new( half_size, -half_size, half_size), // 1: bottom-right-front
78 crate::compat::point3::new( half_size, half_size, half_size), // 2: top-right-front
79 crate::compat::point3::new(-half_size, half_size, half_size), // 3: top-left-front
80
81 // Back face (z-)
82 crate::compat::point3::new(-half_size, -half_size, -half_size), // 4: bottom-left-back
83 crate::compat::point3::new( half_size, -half_size, -half_size), // 5: bottom-right-back
84 crate::compat::point3::new( half_size, half_size, -half_size), // 6: top-right-back
85 crate::compat::point3::new(-half_size, half_size, -half_size), // 7: top-left-back
86 ];
87
88 // 12 triangles (2 per face * 6 faces) using Triangle structs
89 let indices = vec![
90 // Front face (z+)
91 Triangle { a: 0, b: 1, c: 2 }, Triangle { a: 0, b: 2, c: 3 },
92
93 // Back face (z-)
94 Triangle { a: 5, b: 4, c: 7 }, Triangle { a: 5, b: 7, c: 6 },
95
96 // Top face (y+)
97 Triangle { a: 3, b: 2, c: 6 }, Triangle { a: 3, b: 6, c: 7 },
98
99 // Bottom face (y-)
100 Triangle { a: 4, b: 5, c: 1 }, Triangle { a: 4, b: 1, c: 0 },
101
102 // Right face (x+)
103 Triangle { a: 1, b: 5, c: 6 }, Triangle { a: 1, b: 6, c: 2 },
104
105 // Left face (x-)
106 Triangle { a: 4, b: 0, c: 3 }, Triangle { a: 4, b: 3, c: 7 },
107 ];
108
109 // Normals for each vertex using Vector3
110 let normals = vec![
111 // Front face (z+)
112 crate::compat::vector3::new(0.0, 0.0, 1.0),
113 crate::compat::vector3::new(0.0, 0.0, 1.0),
114 crate::compat::vector3::new(0.0, 0.0, 1.0),
115 crate::compat::vector3::new(0.0, 0.0, 1.0),
116
117 // Back face (z-)
118 crate::compat::vector3::new(0.0, 0.0, -1.0),
119 crate::compat::vector3::new(0.0, 0.0, -1.0),
120 crate::compat::vector3::new(0.0, 0.0, -1.0),
121 crate::compat::vector3::new(0.0, 0.0, -1.0),
122
123 // This is simplified for example purposes
124 // In a real implementation we would need more vertices with unique normals
125 // or use a better normal calculation strategy
126 ];
127
128 // Simple UV mapping using Vector2
129 let uvs = vec![
130 // Front face
131 crate::compat::vector2::new(0.0, 1.0),
132 crate::compat::vector2::new(1.0, 1.0),
133 crate::compat::vector2::new(1.0, 0.0),
134 crate::compat::vector2::new(0.0, 0.0),
135
136 // Back face
137 crate::compat::vector2::new(1.0, 1.0),
138 crate::compat::vector2::new(0.0, 1.0),
139 crate::compat::vector2::new(0.0, 0.0),
140 crate::compat::vector2::new(1.0, 0.0),
141 ];
142
143 self.create_simple_mesh(None, &positions, &indices, Some(normals), Some(uvs), None)
144 }
145
146 /// Create a box with the specified material
147 pub fn create_box_with_material(&mut self, size: f32, material: Option<usize>) -> usize {
148 // Box centered at origin with given size
149 let half_size = size / 2.0;
150
151 // For a proper cube with separate normals per face, we need to duplicate vertices
152 // 24 vertices for a cube (4 per face * 6 faces) using Point3
153 let positions = vec![
154 // Front face (z+)
155 crate::compat::point3::new(-half_size, -half_size, half_size),
156 crate::compat::point3::new( half_size, -half_size, half_size),
157 crate::compat::point3::new( half_size, half_size, half_size),
158 crate::compat::point3::new(-half_size, half_size, half_size),
159
160 // Back face (z-)
161 crate::compat::point3::new( half_size, -half_size, -half_size),
162 crate::compat::point3::new(-half_size, -half_size, -half_size),
163 crate::compat::point3::new(-half_size, half_size, -half_size),
164 crate::compat::point3::new( half_size, half_size, -half_size),
165
166 // Top face (y+)
167 crate::compat::point3::new(-half_size, half_size, half_size),
168 crate::compat::point3::new( half_size, half_size, half_size),
169 crate::compat::point3::new( half_size, half_size, -half_size),
170 crate::compat::point3::new(-half_size, half_size, -half_size),
171
172 // Bottom face (y-)
173 crate::compat::point3::new( half_size, -half_size, half_size),
174 crate::compat::point3::new(-half_size, -half_size, half_size),
175 crate::compat::point3::new(-half_size, -half_size, -half_size),
176 crate::compat::point3::new( half_size, -half_size, -half_size),
177
178 // Right face (x+)
179 crate::compat::point3::new( half_size, -half_size, half_size),
180 crate::compat::point3::new( half_size, -half_size, -half_size),
181 crate::compat::point3::new( half_size, half_size, -half_size),
182 crate::compat::point3::new( half_size, half_size, half_size),
183
184 // Left face (x-)
185 crate::compat::point3::new(-half_size, -half_size, -half_size),
186 crate::compat::point3::new(-half_size, -half_size, half_size),
187 crate::compat::point3::new(-half_size, half_size, half_size),
188 crate::compat::point3::new(-half_size, half_size, -half_size),
189 ];
190
191 // Convert positions to flat array for create_simple_mesh
192
193 // Triangle indices (6 faces * 2 triangles * 3 vertices = 36 indices)
194 let indices = vec![
195 // Front face
196 Triangle { a: 0, b: 1, c: 2 },
197 Triangle { a: 0, b: 2, c: 3 },
198
199 // Back face
200 Triangle { a: 4, b: 5, c: 6 },
201 Triangle { a: 4, b: 6, c: 7 },
202
203 // Top face
204 Triangle { a: 8, b: 9, c: 10 },
205 Triangle { a: 8, b: 10, c: 11 },
206
207 // Bottom face
208 Triangle { a: 12, b: 13, c: 14 },
209 Triangle { a: 12, b: 14, c: 15 },
210
211 // Right face
212 Triangle { a: 16, b: 17, c: 18 },
213 Triangle { a: 16, b: 18, c: 19 },
214
215 // Left face
216 Triangle { a: 20, b: 21, c: 22 },
217 Triangle { a: 20, b: 22, c: 23 },
218 ];
219
220 // Convert indices to u16 for create_simple_mesh
221
222 // Normals for each vertex
223 let normals = vec![
224 // Front face (z+)
225 crate::compat::vector3::new(0.0, 0.0, 1.0),
226 crate::compat::vector3::new(0.0, 0.0, 1.0),
227 crate::compat::vector3::new(0.0, 0.0, 1.0),
228 crate::compat::vector3::new(0.0, 0.0, 1.0),
229
230 // Back face (z-)
231 crate::compat::vector3::new(0.0, 0.0, -1.0),
232 crate::compat::vector3::new(0.0, 0.0, -1.0),
233 crate::compat::vector3::new(0.0, 0.0, -1.0),
234 crate::compat::vector3::new(0.0, 0.0, -1.0),
235
236 // Top face (y+)
237 crate::compat::vector3::new(0.0, 1.0, 0.0),
238 crate::compat::vector3::new(0.0, 1.0, 0.0),
239 crate::compat::vector3::new(0.0, 1.0, 0.0),
240 crate::compat::vector3::new(0.0, 1.0, 0.0),
241
242 // Bottom face (y-)
243 crate::compat::vector3::new(0.0, -1.0, 0.0),
244 crate::compat::vector3::new(0.0, -1.0, 0.0),
245 crate::compat::vector3::new(0.0, -1.0, 0.0),
246 crate::compat::vector3::new(0.0, -1.0, 0.0),
247
248 // Right face (x+)
249 crate::compat::vector3::new(1.0, 0.0, 0.0),
250 crate::compat::vector3::new(1.0, 0.0, 0.0),
251 crate::compat::vector3::new(1.0, 0.0, 0.0),
252 crate::compat::vector3::new(1.0, 0.0, 0.0),
253
254 // Left face (x-)
255 crate::compat::vector3::new(-1.0, 0.0, 0.0),
256 crate::compat::vector3::new(-1.0, 0.0, 0.0),
257 crate::compat::vector3::new(-1.0, 0.0, 0.0),
258 crate::compat::vector3::new(-1.0, 0.0, 0.0),
259 ];
260
261 // Convert normals to flat array for create_simple_mesh
262
263 // UVs for each face using Vector2
264 let uvs = vec![
265 // Front face
266 crate::compat::vector2::new(0.0, 1.0),
267 crate::compat::vector2::new(1.0, 1.0),
268 crate::compat::vector2::new(1.0, 0.0),
269 crate::compat::vector2::new(0.0, 0.0),
270
271 // Back face
272 crate::compat::vector2::new(1.0, 1.0),
273 crate::compat::vector2::new(1.0, 0.0),
274 crate::compat::vector2::new(0.0, 0.0),
275 crate::compat::vector2::new(0.0, 1.0),
276
277 // Top face
278 crate::compat::vector2::new(0.0, 1.0),
279 crate::compat::vector2::new(0.0, 0.0),
280 crate::compat::vector2::new(1.0, 0.0),
281 crate::compat::vector2::new(1.0, 1.0),
282
283 // Bottom face
284 crate::compat::vector2::new(1.0, 1.0),
285 crate::compat::vector2::new(0.0, 1.0),
286 crate::compat::vector2::new(0.0, 0.0),
287 crate::compat::vector2::new(1.0, 0.0),
288
289 // Right face
290 crate::compat::vector2::new(1.0, 1.0),
291 crate::compat::vector2::new(1.0, 0.0),
292 crate::compat::vector2::new(0.0, 0.0),
293 crate::compat::vector2::new(0.0, 1.0),
294
295 // Left face
296 crate::compat::vector2::new(0.0, 1.0),
297 crate::compat::vector2::new(1.0, 1.0),
298 crate::compat::vector2::new(1.0, 0.0),
299 crate::compat::vector2::new(0.0, 0.0),
300 ];
301
302 self.create_simple_mesh(None, &positions, &indices, Some(normals), Some(uvs), material)
303 }
304
305 /// Create a mesh with custom geometry and UV mapping
306 ///
307 /// # Parameters
308 /// * `name` - Optional name for the mesh
309 /// * `positions` - Vertex positions as Vec<Point3<f32>>
310 /// * `indices` - List of triangles, each containing three vertex indices
311 /// * `normals` - Optional vertex normals as Vec<Vector3<f32>>
312 /// * `texcoords` - Optional array of UV coordinate sets, each as Vec<Vector2<f32>>.
313 /// The first set becomes TEXCOORD_0, the second TEXCOORD_1, etc.
314 /// * `material` - Optional material index to use for the mesh
315 ///
316 /// # Returns
317 /// The index of the created mesh
318 pub fn create_custom_mesh(&mut self,
319 name: Option<String>,
320 positions: &[Point3<f32>],
321 indices: &[Triangle],
322 normals: Option<Vec<Vector3<f32>>>,
323 texcoords: Option<Vec<Vec<Vector2<f32>>>>,
324 material: Option<usize>) -> usize {
325 // Calculate bounds for the positions
326 let (min_point, max_point) = if !positions.is_empty() {
327 let mut min = crate::compat::point3::new(f32::MAX, f32::MAX, f32::MAX);
328 let mut max = crate::compat::point3::new(f32::MIN, f32::MIN, f32::MIN);
329
330 for point in positions {
331 min.x = min.x.min(point.x);
332 min.y = min.y.min(point.y);
333 min.z = min.z.min(point.z);
334
335 max.x = max.x.max(point.x);
336 max.y = max.y.max(point.y);
337 max.z = max.z.max(point.z);
338 }
339
340 (Some(min), Some(max))
341 } else {
342 (None, None)
343 };
344
345 // Convert Point3 min/max to Vec<f32> for accessor
346 let min = min_point.map(|p| vec![p.x, p.y, p.z]);
347 let max = max_point.map(|p| vec![p.x, p.y, p.z]);
348
349 // Convert positions from Point3 to flat array for buffer
350 let flat_positions: Vec<f32> = positions.iter().flat_map(|p| vec![p.x, p.y, p.z]).collect();
351
352 // Add position data to buffer
353 let pos_bytes = unsafe {
354 std::slice::from_raw_parts(
355 flat_positions.as_ptr() as *const u8,
356 flat_positions.len() * std::mem::size_of::<f32>()
357 )
358 };
359 let (pos_offset, pos_length) = self.add_buffer_data(pos_bytes);
360 let pos_buffer_view = self.add_buffer_view(pos_offset, pos_length, Some(buffer_view_target::ARRAY_BUFFER));
361
362 // Add position accessor
363 let vertex_count = positions.len();
364 let pos_accessor = self.add_accessor(
365 pos_buffer_view,
366 component_type::FLOAT,
367 vertex_count,
368 accessor_type::VEC3.to_string(),
369 None,
370 min,
371 max
372 );
373
374 // Flatten the Triangle structs into a flat list of indices
375 let flat_indices: Vec<u32> = indices.iter()
376 .flat_map(|triangle| vec![triangle.a, triangle.b, triangle.c])
377 .collect();
378
379 // Add index data to buffer
380 let idx_bytes = unsafe {
381 std::slice::from_raw_parts(
382 flat_indices.as_ptr() as *const u8,
383 flat_indices.len() * std::mem::size_of::<u32>()
384 )
385 };
386 let (idx_offset, idx_length) = self.add_buffer_data(idx_bytes);
387 let idx_buffer_view = self.add_buffer_view(idx_offset, idx_length, Some(buffer_view_target::ELEMENT_ARRAY_BUFFER));
388
389 // Add index accessor
390 let idx_accessor = self.add_accessor(
391 idx_buffer_view,
392 component_type::UNSIGNED_INT, // Use UNSIGNED_INT for u32 indices
393 flat_indices.len(),
394 accessor_type::SCALAR.to_string(),
395 None,
396 None,
397 None
398 );
399
400 // Build attributes map
401 let mut attributes = HashMap::new();
402 attributes.insert("POSITION".to_string(), pos_accessor);
403
404 // Add normals if provided
405 if let Some(normal_data) = normals {
406 // Convert normals from Vector3 to flat array for buffer
407 let flat_normals: Vec<f32> = normal_data.iter().flat_map(|n| vec![n.x, n.y, n.z]).collect();
408
409 let norm_bytes = unsafe {
410 std::slice::from_raw_parts(
411 flat_normals.as_ptr() as *const u8,
412 flat_normals.len() * std::mem::size_of::<f32>()
413 )
414 };
415 let (norm_offset, norm_length) = self.add_buffer_data(norm_bytes);
416 let norm_buffer_view = self.add_buffer_view(norm_offset, norm_length, Some(buffer_view_target::ARRAY_BUFFER));
417
418 let normal_accessor = self.add_accessor(
419 norm_buffer_view,
420 component_type::FLOAT,
421 normal_data.len(),
422 accessor_type::VEC3.to_string(),
423 None,
424 None,
425 None
426 );
427
428 attributes.insert("NORMAL".to_string(), normal_accessor);
429 }
430
431 // Add texture coordinates if provided
432 let mut texcoord_accessors = Vec::new();
433 if let Some(texcoord_sets) = texcoords {
434 for (i, texcoord_data) in texcoord_sets.iter().enumerate() {
435 // Convert Vector2 to flat array for buffer
436 let flat_texcoords: Vec<f32> = texcoord_data.iter().flat_map(|uv| vec![uv.x, uv.y]).collect();
437
438 let tc_bytes = unsafe {
439 std::slice::from_raw_parts(
440 flat_texcoords.as_ptr() as *const u8,
441 flat_texcoords.len() * std::mem::size_of::<f32>()
442 )
443 };
444 let (tc_offset, tc_length) = self.add_buffer_data(tc_bytes);
445 let tc_buffer_view = self.add_buffer_view(tc_offset, tc_length, Some(buffer_view_target::ARRAY_BUFFER));
446
447 let tc_accessor = self.add_accessor(
448 tc_buffer_view,
449 component_type::FLOAT,
450 texcoord_data.len(), // Number of Vector2 elements
451 accessor_type::VEC2.to_string(),
452 None,
453 None,
454 None
455 );
456
457 attributes.insert(format!("TEXCOORD_{}", i), tc_accessor);
458 texcoord_accessors.push(tc_accessor);
459 }
460 }
461
462 // Create primitive
463 let primitive = Primitive {
464 attributes,
465 indices: Some(idx_accessor),
466 material,
467 mode: None, // Default mode (triangles)
468 };
469
470 // Create and add mesh
471 self.add_mesh(name, vec![primitive])
472 }
473
474
475 /// Create a mesh with custom geometry and single UV channel using types
476 ///
477 /// Simplified version of create_custom_mesh for the common case of a single UV channel,
478 /// but using proper 3D math types instead of raw float arrays.
479 ///
480 /// # Parameters
481 /// * `name` - Optional name for the mesh
482 /// * `positions` - Vertex positions as &[Point3<f32>]
483 /// * `indices` - List of triangles using the Triangle struct
484 /// * `normals` - Optional vertex normals as Vec<Vector3<f32>>
485 /// * `texcoords` - Optional UV coordinates as Vec<Vector2<f32>>
486 /// * `material` - Optional material index to use for the mesh
487 ///
488 /// # Returns
489 /// The index of the created mesh
490 pub fn create_simple_mesh(&mut self,
491 name: Option<String>,
492 positions: &[Point3<f32>],
493 indices: &[Triangle],
494 normals: Option<Vec<Vector3<f32>>>,
495 texcoords: Option<Vec<Vector2<f32>>>,
496 material: Option<usize>) -> usize {
497 // If we have texture coordinates, create a texcoord set for the mesh
498 let texcoord_sets = if let Some(uvs) = texcoords {
499 let mut sets = Vec::new();
500 sets.push(uvs);
501 Some(sets)
502 } else {
503 None
504 };
505
506 self.create_custom_mesh(name, positions, indices, normals, texcoord_sets, material)
507 }
508
509 /// Create a flat plane mesh with subdivisions
510 ///
511 /// This method creates a flat rectangular plane on the XZ plane (with Y as up).
512 /// The plane is centered at the origin and can be subdivided into a grid of triangles.
513 /// Subdividing the plane is useful for terrain or deformation effects.
514 ///
515 /// # Parameters
516 /// * `width` - Width of the plane along X axis
517 /// * `depth` - Depth of the plane along Z axis
518 /// * `width_segments` - Number of subdivisions along width (min: 1)
519 /// * `depth_segments` - Number of subdivisions along depth (min: 1)
520 /// * `material` - Optional material index to use for the mesh
521 ///
522 /// # Returns
523 /// The index of the created mesh in the glTF document's meshes array
524 ///
525 /// # Example
526 /// ```
527 /// use mesh_tools::GltfBuilder;
528 /// let mut builder = GltfBuilder::new();
529 ///
530 /// // Create a material
531 /// let ground_material = builder.create_basic_material(
532 /// Some("Ground".to_string()),
533 /// [0.5, 0.5, 0.5, 1.0]
534 /// );
535 ///
536 /// // Create a 10x10 ground plane with 20x20 grid subdivisions
537 /// let ground_mesh = builder.create_plane(10.0, 10.0, 20, 20, Some(ground_material));
538 /// ```
539 pub fn create_plane(&mut self,
540 width: f32,
541 depth: f32,
542 width_segments: usize,
543 depth_segments: usize,
544 material: Option<usize>) -> usize {
545 // Get the mesh data directly as types
546 let (positions, indices, normals, uvs) = primitives::generate_plane(
547 width, depth, width_segments, depth_segments
548 );
549
550 // Create the mesh using the mint types returned by the primitives module
551 self.create_simple_mesh(None, &positions, &indices, Some(normals), Some(uvs), material)
552 }
553
554 /// Create a sphere mesh with specified radius and resolution
555 ///
556 /// This method creates a UV-mapped sphere centered at the origin. The sphere is generated
557 /// using latitude/longitude segmentation, with vertices distributed evenly around the surface.
558 ///
559 /// # Parameters
560 /// * `radius` - Radius of the sphere
561 /// * `width_segments` - Number of horizontal subdivisions (longitude lines, min: 3)
562 /// * `height_segments` - Number of vertical subdivisions (latitude lines, min: 2)
563 /// * `material` - Optional material index to use for the mesh
564 ///
565 /// # Returns
566 /// The index of the created mesh in the glTF document's meshes array
567 ///
568 /// # Example
569 /// ```
570 /// use mesh_tools::GltfBuilder;
571 /// let mut builder = GltfBuilder::new();
572 ///
573 /// // Create a red material
574 /// let red_material = builder.create_basic_material(
575 /// Some("Red".to_string()),
576 /// [1.0, 0.0, 0.0, 1.0]
577 /// );
578 ///
579 /// // Create a high-detail red sphere with radius 2.0
580 /// let sphere_mesh = builder.create_sphere(2.0, 32, 16, Some(red_material));
581 /// ```
582 pub fn create_sphere(&mut self,
583 radius: f32,
584 width_segments: usize,
585 height_segments: usize,
586 material: Option<usize>) -> usize {
587 // Get the mesh data directly as mint types
588 let (positions, indices, normals, uvs) = primitives::generate_sphere(
589 radius, width_segments, height_segments
590 );
591
592 // Create the mesh using the mint types returned by the primitives module
593 self.create_simple_mesh(None, &positions, &indices, Some(normals), Some(uvs), material)
594 }
595
596 /// Create a cylinder mesh with customizable dimensions
597 ///
598 /// This method creates a cylinder or a truncated cone (when top and bottom radii differ).
599 /// The cylinder is centered at the origin and extends along the Y axis.
600 /// The cylinder can be open-ended (without caps) or closed with caps.
601 ///
602 /// # Parameters
603 /// * `radius_top` - Radius at the top of the cylinder
604 /// * `radius_bottom` - Radius at the bottom of the cylinder
605 /// * `height` - Height of the cylinder along the Y axis
606 /// * `radial_segments` - Number of subdivisions around the circumference (min: 3)
607 /// * `height_segments` - Number of subdivisions along the height (min: 1)
608 /// * `open_ended` - When `true`, the cylinder has no top or bottom caps
609 /// * `material` - Optional material index to use for the mesh
610 ///
611 /// # Returns
612 /// The index of the created mesh in the glTF document's meshes array
613 ///
614 /// # Example
615 /// ```
616 /// use mesh_tools::GltfBuilder;
617 /// let mut builder = GltfBuilder::new();
618 ///
619 /// // Create a blue material
620 /// let blue_material = builder.create_basic_material(
621 /// Some("Blue".to_string()),
622 /// [0.0, 0.0, 0.8, 1.0]
623 /// );
624 ///
625 /// // Create a cylinder with different top and bottom radii (truncated cone)
626 /// let cylinder_mesh = builder.create_cylinder(
627 /// 0.5, // radius top
628 /// 1.0, // radius bottom
629 /// 2.0, // height
630 /// 16, // radial segments
631 /// 1, // height segments
632 /// false, // closed with caps
633 /// Some(blue_material)
634 /// );
635 /// ```
636 pub fn create_cylinder(&mut self,
637 radius_top: f32,
638 radius_bottom: f32,
639 height: f32,
640 radial_segments: usize,
641 height_segments: usize,
642 open_ended: bool,
643 material: Option<usize>) -> usize {
644 // Get the mesh data directly as types
645 let (positions, indices, normals, uvs) = primitives::generate_cylinder(
646 radius_top, radius_bottom, height, radial_segments, height_segments, open_ended
647 );
648
649 // Create the mesh using the types returned by the primitives module
650 self.create_simple_mesh(None, &positions, &indices, Some(normals), Some(uvs), material)
651 }
652
653 /// Create a cone mesh
654 ///
655 /// # Parameters
656 /// * `radius` - Radius at the base of the cone
657 /// * `height` - Height of the cone
658 /// * `radial_segments` - Number of subdivisions around the circumference
659 /// * `height_segments` - Number of subdivisions along the height
660 /// * `open_ended` - Whether to include the base cap
661 /// * `material` - Optional material index to use for the mesh
662 ///
663 /// # Returns
664 /// The index of the created mesh
665 pub fn create_cone(&mut self,
666 radius: f32,
667 height: f32,
668 radial_segments: usize,
669 height_segments: usize,
670 open_ended: bool,
671 material: Option<usize>) -> usize {
672 // Get the mesh data directly as types
673 let (positions, indices, normals, uvs) = primitives::generate_cone(
674 radius, height, radial_segments, height_segments, open_ended
675 );
676
677 // Create the mesh using the types returned by the primitives module
678 self.create_simple_mesh(None, &positions, &indices, Some(normals), Some(uvs), material)
679 }
680
681 /// Create a torus (donut shape) mesh
682 ///
683 /// # Parameters
684 /// * `radius` - Distance from the center of the tube to the center of the torus
685 /// * `tube` - Radius of the tube
686 /// * `radial_segments` - Number of subdivisions around the main circle
687 /// * `tubular_segments` - Number of subdivisions around the tube
688 /// * `material` - Optional material index to use for the mesh
689 ///
690 /// # Returns
691 /// The index of the created mesh
692 pub fn create_torus(&mut self,
693 radius: f32,
694 tube: f32,
695 radial_segments: usize,
696 tubular_segments: usize,
697 material: Option<usize>) -> usize {
698 // Get the mesh data directly as types
699 let (positions, indices, normals, uvs) = primitives::generate_torus(
700 radius, tube, radial_segments, tubular_segments
701 );
702
703 // Create the mesh using the types returned by the primitives module
704 self.create_simple_mesh(None, &positions, &indices, Some(normals), Some(uvs), material)
705 }
706
707 /// Create an icosahedron (20-sided polyhedron) mesh
708 ///
709 /// # Parameters
710 /// * `radius` - Radius of the circumscribed sphere
711 /// * `material` - Optional material index to use for the mesh
712 ///
713 /// # Returns
714 /// The index of the created mesh
715 pub fn create_icosahedron(&mut self,
716 radius: f32,
717 material: Option<usize>) -> usize {
718 // Get the mesh data directly as types
719 let (positions, indices, normals, uvs) = primitives::generate_icosahedron(radius);
720
721 // Create the mesh using the types returned by the primitives module
722 self.create_simple_mesh(None, &positions, &indices, Some(normals), Some(uvs), material)
723 }
724}