Path: blob/main/crates/bevy_mesh/src/primitives/dim3/torus.rs
6598 views
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};1use bevy_asset::RenderAssetUsages;2use bevy_math::{ops, primitives::Torus, Vec3};3use bevy_reflect::prelude::*;4use core::ops::RangeInclusive;56/// A builder used for creating a [`Mesh`] with a [`Torus`] shape.7#[derive(Clone, Debug, Reflect)]8#[reflect(Default, Debug, Clone)]9pub struct TorusMeshBuilder {10/// The [`Torus`] shape.11pub torus: Torus,12/// The number of vertices used for each circular segment13/// in the ring or tube of the torus.14///15/// The default is `24`.16pub minor_resolution: usize,17/// The number of segments used for the main ring of the torus.18///19/// A resolution of `4` would make the torus appear rectangular,20/// while a resolution of `32` resembles a circular ring.21///22/// The default is `32`.23pub major_resolution: usize,24/// Optional angle range in radians, defaults to a full circle (0.0..=2 * PI)25pub angle_range: RangeInclusive<f32>,26}2728impl Default for TorusMeshBuilder {29fn default() -> Self {30Self {31torus: Torus::default(),32minor_resolution: 24,33major_resolution: 32,34angle_range: (0.0..=2.0 * core::f32::consts::PI),35}36}37}3839impl TorusMeshBuilder {40/// Creates a new [`TorusMeshBuilder`] from an inner and outer radius.41///42/// The inner radius is the radius of the hole, and the outer radius43/// is the radius of the entire object.44#[inline]45pub fn new(inner_radius: f32, outer_radius: f32) -> Self {46Self {47torus: Torus::new(inner_radius, outer_radius),48..Default::default()49}50}5152/// Sets the number of vertices used for each circular segment53/// in the ring or tube of the torus.54#[inline]55pub const fn minor_resolution(mut self, resolution: usize) -> Self {56self.minor_resolution = resolution;57self58}5960/// Sets the number of segments used for the main ring of the torus.61///62/// A resolution of `4` would make the torus appear rectangular,63/// while a resolution of `32` resembles a circular ring.64#[inline]65pub const fn major_resolution(mut self, resolution: usize) -> Self {66self.major_resolution = resolution;67self68}6970/// Sets a custom angle range in radians instead of a full circle71#[inline]72pub const fn angle_range(mut self, range: RangeInclusive<f32>) -> Self {73self.angle_range = range;74self75}76}7778impl MeshBuilder for TorusMeshBuilder {79fn build(&self) -> Mesh {80// code adapted from http://apparat-engine.blogspot.com/2013/04/procedural-meshes-torus.html8182let n_vertices = (self.major_resolution + 1) * (self.minor_resolution + 1);83let mut positions: Vec<[f32; 3]> = Vec::with_capacity(n_vertices);84let mut normals: Vec<[f32; 3]> = Vec::with_capacity(n_vertices);85let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(n_vertices);8687let start_angle = self.angle_range.start();88let end_angle = self.angle_range.end();8990let segment_stride = (end_angle - start_angle) / self.major_resolution as f32;91let side_stride = 2.0 * core::f32::consts::PI / self.minor_resolution as f32;9293for segment in 0..=self.major_resolution {94let theta = start_angle + segment_stride * segment as f32;9596for side in 0..=self.minor_resolution {97let phi = side_stride * side as f32;98let (sin_theta, cos_theta) = ops::sin_cos(theta);99let (sin_phi, cos_phi) = ops::sin_cos(phi);100let radius = self.torus.major_radius + self.torus.minor_radius * cos_phi;101102let position = Vec3::new(103cos_theta * radius,104self.torus.minor_radius * sin_phi,105sin_theta * radius,106);107108let center = Vec3::new(109self.torus.major_radius * cos_theta,1100.,111self.torus.major_radius * sin_theta,112);113let normal = (position - center).normalize();114115positions.push(position.into());116normals.push(normal.into());117uvs.push([118segment as f32 / self.major_resolution as f32,119side as f32 / self.minor_resolution as f32,120]);121}122}123124let n_faces = (self.major_resolution) * (self.minor_resolution);125let n_triangles = n_faces * 2;126let n_indices = n_triangles * 3;127128let mut indices: Vec<u32> = Vec::with_capacity(n_indices);129130let n_vertices_per_row = self.minor_resolution + 1;131for segment in 0..self.major_resolution {132for side in 0..self.minor_resolution {133let lt = side + segment * n_vertices_per_row;134let rt = (side + 1) + segment * n_vertices_per_row;135136let lb = side + (segment + 1) * n_vertices_per_row;137let rb = (side + 1) + (segment + 1) * n_vertices_per_row;138139indices.push(lt as u32);140indices.push(rt as u32);141indices.push(lb as u32);142143indices.push(rt as u32);144indices.push(rb as u32);145indices.push(lb as u32);146}147}148149Mesh::new(150PrimitiveTopology::TriangleList,151RenderAssetUsages::default(),152)153.with_inserted_indices(Indices::U32(indices))154.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)155.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)156.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)157}158}159160impl Meshable for Torus {161type Output = TorusMeshBuilder;162163fn mesh(&self) -> Self::Output {164TorusMeshBuilder {165torus: *self,166..Default::default()167}168}169}170171impl From<Torus> for Mesh {172fn from(torus: Torus) -> Self {173torus.mesh().build()174}175}176177178