Path: blob/main/crates/bevy_mesh/src/primitives/dim3/conical_frustum.rs
6598 views
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};1use bevy_asset::RenderAssetUsages;2use bevy_math::{ops, primitives::ConicalFrustum, Vec3};3use bevy_reflect::prelude::*;45/// A builder used for creating a [`Mesh`] with a [`ConicalFrustum`] shape.6#[derive(Clone, Copy, Debug, Reflect)]7#[reflect(Default, Debug, Clone)]8pub struct ConicalFrustumMeshBuilder {9/// The [`ConicalFrustum`] shape.10pub frustum: ConicalFrustum,11/// The number of vertices used for the top and bottom of the conical frustum.12///13/// The default is `32`.14pub resolution: u32,15/// The number of horizontal lines subdividing the lateral surface of the conical frustum.16///17/// The default is `1`.18pub segments: u32,19}2021impl Default for ConicalFrustumMeshBuilder {22fn default() -> Self {23Self {24frustum: ConicalFrustum::default(),25resolution: 32,26segments: 1,27}28}29}3031impl ConicalFrustumMeshBuilder {32/// Creates a new [`ConicalFrustumMeshBuilder`] from the given top and bottom radii, a height,33/// and a resolution used for the top and bottom.34#[inline]35pub const fn new(radius_top: f32, radius_bottom: f32, height: f32, resolution: u32) -> Self {36Self {37frustum: ConicalFrustum {38radius_top,39radius_bottom,40height,41},42resolution,43segments: 1,44}45}4647/// Sets the number of vertices used for the top and bottom of the conical frustum.48#[inline]49pub const fn resolution(mut self, resolution: u32) -> Self {50self.resolution = resolution;51self52}5354/// Sets the number of horizontal lines subdividing the lateral surface of the conical frustum.55#[inline]56pub const fn segments(mut self, segments: u32) -> Self {57self.segments = segments;58self59}60}6162impl MeshBuilder for ConicalFrustumMeshBuilder {63fn build(&self) -> Mesh {64debug_assert!(self.resolution > 2);65debug_assert!(self.segments > 0);6667let ConicalFrustum {68radius_top,69radius_bottom,70height,71} = self.frustum;72let half_height = height / 2.0;7374let num_rings = self.segments + 1;75let num_vertices = (self.resolution * 2 + num_rings * (self.resolution + 1)) as usize;76let num_faces = self.resolution * (num_rings - 2);77let num_indices = ((2 * num_faces + 2 * (self.resolution - 1) * 2) * 3) as usize;7879let mut positions = Vec::with_capacity(num_vertices);80let mut normals = Vec::with_capacity(num_vertices);81let mut uvs = Vec::with_capacity(num_vertices);82let mut indices = Vec::with_capacity(num_indices);8384let step_theta = core::f32::consts::TAU / self.resolution as f32;85let step_y = height / self.segments as f32;86let step_radius = (radius_top - radius_bottom) / self.segments as f32;8788// Rings89for ring in 0..num_rings {90let y = -half_height + ring as f32 * step_y;91let radius = radius_bottom + ring as f32 * step_radius;9293for segment in 0..=self.resolution {94let theta = segment as f32 * step_theta;95let (sin, cos) = ops::sin_cos(theta);9697positions.push([radius * cos, y, radius * sin]);98normals.push(99Vec3::new(cos, (radius_bottom - radius_top) / height, sin)100.normalize()101.to_array(),102);103uvs.push([104segment as f32 / self.resolution as f32,105ring as f32 / self.segments as f32,106]);107}108}109110// Lateral surface111for i in 0..self.segments {112let ring = i * (self.resolution + 1);113let next_ring = (i + 1) * (self.resolution + 1);114115for j in 0..self.resolution {116indices.extend_from_slice(&[117ring + j,118next_ring + j,119ring + j + 1,120next_ring + j,121next_ring + j + 1,122ring + j + 1,123]);124}125}126127// Caps128let mut build_cap = |top: bool, radius: f32| {129let offset = positions.len() as u32;130let (y, normal_y, winding) = if top {131(half_height, 1.0, (1, 0))132} else {133(-half_height, -1.0, (0, 1))134};135136for i in 0..self.resolution {137let theta = i as f32 * step_theta;138let (sin, cos) = ops::sin_cos(theta);139140positions.push([cos * radius, y, sin * radius]);141normals.push([0.0, normal_y, 0.0]);142uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);143}144145for i in 1..(self.resolution - 1) {146indices.extend_from_slice(&[147offset,148offset + i + winding.0,149offset + i + winding.1,150]);151}152};153154build_cap(true, radius_top);155build_cap(false, radius_bottom);156157Mesh::new(158PrimitiveTopology::TriangleList,159RenderAssetUsages::default(),160)161.with_inserted_indices(Indices::U32(indices))162.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)163.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)164.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)165}166}167168impl Meshable for ConicalFrustum {169type Output = ConicalFrustumMeshBuilder;170171fn mesh(&self) -> Self::Output {172ConicalFrustumMeshBuilder {173frustum: *self,174..Default::default()175}176}177}178179impl From<ConicalFrustum> for Mesh {180fn from(frustum: ConicalFrustum) -> Self {181frustum.mesh().build()182}183}184185186