Path: blob/main/crates/bevy_mesh/src/primitives/dim3/cylinder.rs
6598 views
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};1use bevy_asset::RenderAssetUsages;2use bevy_math::{ops, primitives::Cylinder};3use bevy_reflect::prelude::*;45/// Anchoring options for [`CylinderMeshBuilder`]6#[derive(Debug, Copy, Clone, Default, Reflect)]7#[reflect(Default, Debug, Clone)]8pub enum CylinderAnchor {9#[default]10/// Midpoint between the top and bottom caps of the cylinder11MidPoint,12/// The center of the top circle cap13Top,14/// The center of the bottom circle cap15Bottom,16}1718/// A builder used for creating a [`Mesh`] with a [`Cylinder`] shape.19#[derive(Clone, Copy, Debug, Reflect)]20#[reflect(Default, Debug, Clone)]21pub struct CylinderMeshBuilder {22/// The [`Cylinder`] shape.23pub cylinder: Cylinder,24/// The number of vertices used for the top and bottom of the cylinder.25///26/// The default is `32`.27pub resolution: u32,28/// The number of segments along the height of the cylinder.29/// Must be greater than `0` for geometry to be generated.30///31/// The default is `1`.32pub segments: u32,33/// If set to `true`, the cylinder caps (flat circle faces) are built,34/// otherwise the mesh will be a shallow tube35pub caps: bool,36/// The anchor point for the cylinder mesh, defaults to the midpoint between37/// the top and bottom caps38pub anchor: CylinderAnchor,39}4041impl Default for CylinderMeshBuilder {42fn default() -> Self {43Self {44cylinder: Cylinder::default(),45resolution: 32,46segments: 1,47caps: true,48anchor: CylinderAnchor::default(),49}50}51}5253impl CylinderMeshBuilder {54/// Creates a new [`CylinderMeshBuilder`] from the given radius, a height,55/// and a resolution used for the top and bottom.56#[inline]57pub fn new(radius: f32, height: f32, resolution: u32) -> Self {58Self {59cylinder: Cylinder::new(radius, height),60resolution,61..Default::default()62}63}6465/// Sets the number of vertices used for the top and bottom of the cylinder.66#[inline]67pub const fn resolution(mut self, resolution: u32) -> Self {68self.resolution = resolution;69self70}7172/// Sets the number of segments along the height of the cylinder.73/// Must be greater than `0` for geometry to be generated.74#[inline]75pub const fn segments(mut self, segments: u32) -> Self {76self.segments = segments;77self78}7980/// Ignore the cylinder caps, making the mesh a shallow tube instead81#[inline]82pub const fn without_caps(mut self) -> Self {83self.caps = false;84self85}8687/// Sets a custom anchor point for the mesh88#[inline]89pub const fn anchor(mut self, anchor: CylinderAnchor) -> Self {90self.anchor = anchor;91self92}93}9495impl MeshBuilder for CylinderMeshBuilder {96fn build(&self) -> Mesh {97let resolution = self.resolution;98let segments = self.segments;99100debug_assert!(resolution > 2);101debug_assert!(segments > 0);102103let num_rings = segments + 1;104let num_vertices = resolution * 2 + num_rings * (resolution + 1);105let num_faces = resolution * (num_rings - 2);106let num_indices = (2 * num_faces + 2 * (resolution - 1) * 2) * 3;107108let mut positions = Vec::with_capacity(num_vertices as usize);109let mut normals = Vec::with_capacity(num_vertices as usize);110let mut uvs = Vec::with_capacity(num_vertices as usize);111let mut indices = Vec::with_capacity(num_indices as usize);112113let step_theta = core::f32::consts::TAU / resolution as f32;114let step_y = 2.0 * self.cylinder.half_height / segments as f32;115116// rings117118for ring in 0..num_rings {119let y = -self.cylinder.half_height + ring as f32 * step_y;120121for segment in 0..=resolution {122let theta = segment as f32 * step_theta;123let (sin, cos) = ops::sin_cos(theta);124125positions.push([self.cylinder.radius * cos, y, self.cylinder.radius * sin]);126normals.push([cos, 0., sin]);127uvs.push([128segment as f32 / resolution as f32,129ring as f32 / segments as f32,130]);131}132}133134// barrel skin135136for i in 0..segments {137let ring = i * (resolution + 1);138let next_ring = (i + 1) * (resolution + 1);139140for j in 0..resolution {141indices.extend_from_slice(&[142ring + j,143next_ring + j,144ring + j + 1,145next_ring + j,146next_ring + j + 1,147ring + j + 1,148]);149}150}151152// caps153if self.caps {154let mut build_cap = |top: bool| {155let offset = positions.len() as u32;156let (y, normal_y, winding) = if top {157(self.cylinder.half_height, 1., (1, 0))158} else {159(-self.cylinder.half_height, -1., (0, 1))160};161162for i in 0..self.resolution {163let theta = i as f32 * step_theta;164let (sin, cos) = ops::sin_cos(theta);165166positions.push([cos * self.cylinder.radius, y, sin * self.cylinder.radius]);167normals.push([0.0, normal_y, 0.0]);168uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);169}170171for i in 1..(self.resolution - 1) {172indices.extend_from_slice(&[173offset,174offset + i + winding.0,175offset + i + winding.1,176]);177}178};179180build_cap(true);181build_cap(false);182}183184// Offset the vertex positions Y axis to match the anchor185match self.anchor {186CylinderAnchor::Top => positions187.iter_mut()188.for_each(|p| p[1] -= self.cylinder.half_height),189CylinderAnchor::Bottom => positions190.iter_mut()191.for_each(|p| p[1] += self.cylinder.half_height),192CylinderAnchor::MidPoint => (),193};194195Mesh::new(196PrimitiveTopology::TriangleList,197RenderAssetUsages::default(),198)199.with_inserted_indices(Indices::U32(indices))200.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)201.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)202.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)203}204}205206impl Meshable for Cylinder {207type Output = CylinderMeshBuilder;208209fn mesh(&self) -> Self::Output {210CylinderMeshBuilder {211cylinder: *self,212..Default::default()213}214}215}216217impl From<Cylinder> for Mesh {218fn from(cylinder: Cylinder) -> Self {219cylinder.mesh().build()220}221}222223224