
# Objective - This PR aims to make creating meshes a little bit more ergonomic, specifically by removing the need for intermediate mutable variables. ## Solution - We add methods that consume the `Mesh` and return a mesh with the specified changes, so that meshes can be entirely constructed via builder-style calls, without intermediate variables; - Methods are flagged with `#[must_use]` to ensure proper use; - Examples are updated to use the new methods where applicable. Some examples are kept with the mutating methods so that users can still easily discover them, and also where the new methods wouldn't really be an improvement. ## Examples Before: ```rust let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vs); mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vns); mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vts); mesh.set_indices(Some(Indices::U32(tris))); mesh ``` After: ```rust Mesh::new(PrimitiveTopology::TriangleList) .with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vs) .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vns) .with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vts) .with_indices(Some(Indices::U32(tris))) ``` Before: ```rust let mut cube = Mesh::from(shape::Cube { size: 1.0 }); cube.generate_tangents().unwrap(); PbrBundle { mesh: meshes.add(cube), ..default() } ``` After: ```rust PbrBundle { mesh: meshes.add( Mesh::from(shape::Cube { size: 1.0 }) .with_generated_tangents() .unwrap(), ), ..default() } ``` --- ## Changelog - Added consuming builder methods for more ergonomic `Mesh` creation: `with_inserted_attribute()`, `with_removed_attribute()`, `with_indices()`, `with_duplicated_vertices()`, `with_computed_flat_normals()`, `with_generated_tangents()`, `with_morph_targets()`, `with_morph_target_names()`.
94 lines
3.4 KiB
Rust
94 lines
3.4 KiB
Rust
use crate::mesh::{Indices, Mesh};
|
|
use bevy_math::Vec3;
|
|
use wgpu::PrimitiveTopology;
|
|
|
|
/// A torus (donut) shape.
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Torus {
|
|
pub radius: f32,
|
|
pub ring_radius: f32,
|
|
pub subdivisions_segments: usize,
|
|
pub subdivisions_sides: usize,
|
|
}
|
|
|
|
impl Default for Torus {
|
|
fn default() -> Self {
|
|
Torus {
|
|
radius: 1.0,
|
|
ring_radius: 0.5,
|
|
subdivisions_segments: 32,
|
|
subdivisions_sides: 24,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Torus> for Mesh {
|
|
fn from(torus: Torus) -> Self {
|
|
// code adapted from http://apparat-engine.blogspot.com/2013/04/procedural-meshes-torus.html
|
|
// (source code at https://github.com/SEilers/Apparat)
|
|
|
|
let n_vertices = (torus.subdivisions_segments + 1) * (torus.subdivisions_sides + 1);
|
|
let mut positions: Vec<[f32; 3]> = Vec::with_capacity(n_vertices);
|
|
let mut normals: Vec<[f32; 3]> = Vec::with_capacity(n_vertices);
|
|
let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(n_vertices);
|
|
|
|
let segment_stride = 2.0 * std::f32::consts::PI / torus.subdivisions_segments as f32;
|
|
let side_stride = 2.0 * std::f32::consts::PI / torus.subdivisions_sides as f32;
|
|
|
|
for segment in 0..=torus.subdivisions_segments {
|
|
let theta = segment_stride * segment as f32;
|
|
|
|
for side in 0..=torus.subdivisions_sides {
|
|
let phi = side_stride * side as f32;
|
|
|
|
let position = Vec3::new(
|
|
theta.cos() * (torus.radius + torus.ring_radius * phi.cos()),
|
|
torus.ring_radius * phi.sin(),
|
|
theta.sin() * (torus.radius + torus.ring_radius * phi.cos()),
|
|
);
|
|
|
|
let center = Vec3::new(torus.radius * theta.cos(), 0., torus.radius * theta.sin());
|
|
let normal = (position - center).normalize();
|
|
|
|
positions.push(position.into());
|
|
normals.push(normal.into());
|
|
uvs.push([
|
|
segment as f32 / torus.subdivisions_segments as f32,
|
|
side as f32 / torus.subdivisions_sides as f32,
|
|
]);
|
|
}
|
|
}
|
|
|
|
let n_faces = (torus.subdivisions_segments) * (torus.subdivisions_sides);
|
|
let n_triangles = n_faces * 2;
|
|
let n_indices = n_triangles * 3;
|
|
|
|
let mut indices: Vec<u32> = Vec::with_capacity(n_indices);
|
|
|
|
let n_vertices_per_row = torus.subdivisions_sides + 1;
|
|
for segment in 0..torus.subdivisions_segments {
|
|
for side in 0..torus.subdivisions_sides {
|
|
let lt = side + segment * n_vertices_per_row;
|
|
let rt = (side + 1) + segment * n_vertices_per_row;
|
|
|
|
let lb = side + (segment + 1) * n_vertices_per_row;
|
|
let rb = (side + 1) + (segment + 1) * n_vertices_per_row;
|
|
|
|
indices.push(lt as u32);
|
|
indices.push(rt as u32);
|
|
indices.push(lb as u32);
|
|
|
|
indices.push(rt as u32);
|
|
indices.push(rb as u32);
|
|
indices.push(lb as u32);
|
|
}
|
|
}
|
|
|
|
Mesh::new(PrimitiveTopology::TriangleList)
|
|
.with_indices(Some(Indices::U32(indices)))
|
|
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
|
|
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
|
|
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
|
|
}
|
|
}
|