Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

459
vendor/bevy_mesh/src/conversions.rs vendored Normal file
View File

@@ -0,0 +1,459 @@
//! These implementations allow you to
//! convert `std::vec::Vec<T>` to `VertexAttributeValues::T` and back.
//!
//! # Examples
//!
//! ```
//! use bevy_mesh::VertexAttributeValues;
//!
//! // creating std::vec::Vec
//! let buffer = vec![[0_u32; 4]; 10];
//!
//! // converting std::vec::Vec to bevy_mesh::VertexAttributeValues
//! let values = VertexAttributeValues::from(buffer.clone());
//!
//! // converting bevy_mesh::VertexAttributeValues to std::vec::Vec with two ways
//! let result_into: Vec<[u32; 4]> = values.clone().try_into().unwrap();
//! let result_from: Vec<[u32; 4]> = Vec::try_from(values.clone()).unwrap();
//!
//! // getting an error when trying to convert incorrectly
//! let error: Result<Vec<u32>, _> = values.try_into();
//!
//! assert_eq!(buffer, result_into);
//! assert_eq!(buffer, result_from);
//! assert!(error.is_err());
//! ```
use super::VertexAttributeValues;
use bevy_math::{IVec2, IVec3, IVec4, UVec2, UVec3, UVec4, Vec2, Vec3, Vec3A, Vec4};
use thiserror::Error;
#[derive(Debug, Clone, Error)]
#[error("cannot convert VertexAttributeValues::{variant} to {into}")]
pub struct FromVertexAttributeError {
from: VertexAttributeValues,
variant: &'static str,
into: &'static str,
}
impl FromVertexAttributeError {
fn new<T: 'static>(from: VertexAttributeValues) -> Self {
Self {
variant: from.enum_variant_name(),
into: core::any::type_name::<T>(),
from,
}
}
}
macro_rules! impl_from {
($from:ty, $variant:tt) => {
impl From<Vec<$from>> for VertexAttributeValues {
fn from(vec: Vec<$from>) -> Self {
VertexAttributeValues::$variant(vec)
}
}
};
}
macro_rules! impl_from_into {
($from:ty, $variant:tt) => {
impl From<Vec<$from>> for VertexAttributeValues {
fn from(vec: Vec<$from>) -> Self {
let vec: Vec<_> = vec.into_iter().map(|t| t.into()).collect();
VertexAttributeValues::$variant(vec)
}
}
};
}
impl_from!(f32, Float32);
impl_from!([f32; 2], Float32x2);
impl_from_into!(Vec2, Float32x2);
impl_from!([f32; 3], Float32x3);
impl_from_into!(Vec3, Float32x3);
impl_from_into!(Vec3A, Float32x3);
impl_from!([f32; 4], Float32x4);
impl_from_into!(Vec4, Float32x4);
impl_from!(i32, Sint32);
impl_from!([i32; 2], Sint32x2);
impl_from_into!(IVec2, Sint32x2);
impl_from!([i32; 3], Sint32x3);
impl_from_into!(IVec3, Sint32x3);
impl_from!([i32; 4], Sint32x4);
impl_from_into!(IVec4, Sint32x4);
impl_from!(u32, Uint32);
impl_from!([u32; 2], Uint32x2);
impl_from_into!(UVec2, Uint32x2);
impl_from!([u32; 3], Uint32x3);
impl_from_into!(UVec3, Uint32x3);
impl_from!([u32; 4], Uint32x4);
impl_from_into!(UVec4, Uint32x4);
macro_rules! impl_try_from {
($into:ty, $($variant:tt), +) => {
impl TryFrom<VertexAttributeValues> for Vec<$into> {
type Error = FromVertexAttributeError;
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value {
$(VertexAttributeValues::$variant(value)) |+ => Ok(value),
_ => Err(FromVertexAttributeError::new::<Self>(value)),
}
}
}
};
}
macro_rules! impl_try_from_into {
($into:ty, $($variant:tt), +) => {
impl TryFrom<VertexAttributeValues> for Vec<$into> {
type Error = FromVertexAttributeError;
fn try_from(value: VertexAttributeValues) -> Result<Self, Self::Error> {
match value {
$(VertexAttributeValues::$variant(value)) |+ => {
Ok(value.into_iter().map(|t| t.into()).collect())
}
_ => Err(FromVertexAttributeError::new::<Self>(value)),
}
}
}
};
}
impl_try_from!(f32, Float32);
impl_try_from!([f32; 2], Float32x2);
impl_try_from_into!(Vec2, Float32x2);
impl_try_from!([f32; 3], Float32x3);
impl_try_from_into!(Vec3, Float32x3);
impl_try_from_into!(Vec3A, Float32x3);
impl_try_from!([f32; 4], Float32x4);
impl_try_from_into!(Vec4, Float32x4);
impl_try_from!(i32, Sint32);
impl_try_from!([i32; 2], Sint32x2);
impl_try_from_into!(IVec2, Sint32x2);
impl_try_from!([i32; 3], Sint32x3);
impl_try_from_into!(IVec3, Sint32x3);
impl_try_from!([i32; 4], Sint32x4);
impl_try_from_into!(IVec4, Sint32x4);
impl_try_from!(u32, Uint32);
impl_try_from!([u32; 2], Uint32x2);
impl_try_from_into!(UVec2, Uint32x2);
impl_try_from!([u32; 3], Uint32x3);
impl_try_from_into!(UVec3, Uint32x3);
impl_try_from!([u32; 4], Uint32x4);
impl_try_from_into!(UVec4, Uint32x4);
impl_try_from!([i8; 2], Sint8x2, Snorm8x2);
impl_try_from!([i8; 4], Sint8x4, Snorm8x4);
impl_try_from!([u8; 2], Uint8x2, Unorm8x2);
impl_try_from!([u8; 4], Uint8x4, Unorm8x4);
impl_try_from!([i16; 2], Sint16x2, Snorm16x2);
impl_try_from!([i16; 4], Sint16x4, Snorm16x4);
impl_try_from!([u16; 2], Uint16x2, Unorm16x2);
impl_try_from!([u16; 4], Uint16x4, Unorm16x4);
#[cfg(test)]
mod tests {
use bevy_math::{IVec2, IVec3, IVec4, UVec2, UVec3, UVec4, Vec2, Vec3, Vec3A, Vec4};
use super::VertexAttributeValues;
#[test]
fn f32() {
let buffer = vec![0.0; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<f32> = values.clone().try_into().unwrap();
let result_from: Vec<f32> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn i32() {
let buffer = vec![0; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<i32> = values.clone().try_into().unwrap();
let result_from: Vec<i32> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn u32() {
let buffer = vec![0_u32; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<u32> = values.clone().try_into().unwrap();
let result_from: Vec<u32> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<f32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn f32_2() {
let buffer = vec![[0.0; 2]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[f32; 2]> = values.clone().try_into().unwrap();
let result_from: Vec<[f32; 2]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn vec2() {
let buffer = vec![Vec2::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Float32x2(_)));
let result_into: Vec<Vec2> = values.clone().try_into().unwrap();
let result_from: Vec<Vec2> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn i32_2() {
let buffer = vec![[0; 2]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[i32; 2]> = values.clone().try_into().unwrap();
let result_from: Vec<[i32; 2]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn ivec2() {
let buffer = vec![IVec2::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Sint32x2(_)));
let result_into: Vec<IVec2> = values.clone().try_into().unwrap();
let result_from: Vec<IVec2> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn u32_2() {
let buffer = vec![[0_u32; 2]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[u32; 2]> = values.clone().try_into().unwrap();
let result_from: Vec<[u32; 2]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn uvec2() {
let buffer = vec![UVec2::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Uint32x2(_)));
let result_into: Vec<UVec2> = values.clone().try_into().unwrap();
let result_from: Vec<UVec2> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn f32_3() {
let buffer = vec![[0.0; 3]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[f32; 3]> = values.clone().try_into().unwrap();
let result_from: Vec<[f32; 3]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn vec3() {
let buffer = vec![Vec3::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Float32x3(_)));
let result_into: Vec<Vec3> = values.clone().try_into().unwrap();
let result_from: Vec<Vec3> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn vec3a() {
let buffer = vec![Vec3A::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Float32x3(_)));
let result_into: Vec<Vec3A> = values.clone().try_into().unwrap();
let result_from: Vec<Vec3A> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn i32_3() {
let buffer = vec![[0; 3]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[i32; 3]> = values.clone().try_into().unwrap();
let result_from: Vec<[i32; 3]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn ivec3() {
let buffer = vec![IVec3::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Sint32x3(_)));
let result_into: Vec<IVec3> = values.clone().try_into().unwrap();
let result_from: Vec<IVec3> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn u32_3() {
let buffer = vec![[0_u32; 3]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[u32; 3]> = values.clone().try_into().unwrap();
let result_from: Vec<[u32; 3]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn uvec3() {
let buffer = vec![UVec3::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Uint32x3(_)));
let result_into: Vec<UVec3> = values.clone().try_into().unwrap();
let result_from: Vec<UVec3> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn f32_4() {
let buffer = vec![[0.0; 4]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[f32; 4]> = values.clone().try_into().unwrap();
let result_from: Vec<[f32; 4]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn vec4() {
let buffer = vec![Vec4::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Float32x4(_)));
let result_into: Vec<Vec4> = values.clone().try_into().unwrap();
let result_from: Vec<Vec4> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn i32_4() {
let buffer = vec![[0; 4]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[i32; 4]> = values.clone().try_into().unwrap();
let result_from: Vec<[i32; 4]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn ivec4() {
let buffer = vec![IVec4::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Sint32x4(_)));
let result_into: Vec<IVec4> = values.clone().try_into().unwrap();
let result_from: Vec<IVec4> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn u32_4() {
let buffer = vec![[0_u32; 4]; 10];
let values = VertexAttributeValues::from(buffer.clone());
let result_into: Vec<[u32; 4]> = values.clone().try_into().unwrap();
let result_from: Vec<[u32; 4]> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn uvec4() {
let buffer = vec![UVec4::ZERO; 10];
let values = VertexAttributeValues::from(buffer.clone());
assert!(matches!(values, VertexAttributeValues::Uint32x4(_)));
let result_into: Vec<UVec4> = values.clone().try_into().unwrap();
let result_from: Vec<UVec4> = Vec::try_from(values.clone()).unwrap();
let error: Result<Vec<u32>, _> = values.try_into();
assert_eq!(buffer, result_into);
assert_eq!(buffer, result_from);
assert!(error.is_err());
}
#[test]
fn correct_message() {
let buffer = vec![[0_u32; 4]; 3];
let values = VertexAttributeValues::from(buffer);
let error_result: Result<Vec<u32>, _> = values.try_into();
let error = match error_result {
Ok(..) => unreachable!(),
Err(error) => error,
};
assert_eq!(
error.to_string(),
"cannot convert VertexAttributeValues::Uint32x4 to alloc::vec::Vec<u32>"
);
assert_eq!(format!("{error:?}"),
"FromVertexAttributeError { from: Uint32x4([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]), variant: \"Uint32x4\", into: \"alloc::vec::Vec<u32>\" }");
}
}

225
vendor/bevy_mesh/src/index.rs vendored Normal file
View File

@@ -0,0 +1,225 @@
use bevy_reflect::Reflect;
use core::iter;
use core::iter::FusedIterator;
use thiserror::Error;
use wgpu_types::IndexFormat;
/// A disjunction of four iterators. This is necessary to have a well-formed type for the output
/// of [`Mesh::triangles`](super::Mesh::triangles), which produces iterators of four different types depending on the
/// branch taken.
pub(crate) enum FourIterators<A, B, C, D> {
First(A),
Second(B),
Third(C),
Fourth(D),
}
impl<A, B, C, D, I> Iterator for FourIterators<A, B, C, D>
where
A: Iterator<Item = I>,
B: Iterator<Item = I>,
C: Iterator<Item = I>,
D: Iterator<Item = I>,
{
type Item = I;
fn next(&mut self) -> Option<Self::Item> {
match self {
FourIterators::First(iter) => iter.next(),
FourIterators::Second(iter) => iter.next(),
FourIterators::Third(iter) => iter.next(),
FourIterators::Fourth(iter) => iter.next(),
}
}
}
/// An error that occurred while trying to invert the winding of a [`Mesh`](super::Mesh).
#[derive(Debug, Error)]
pub enum MeshWindingInvertError {
/// This error occurs when you try to invert the winding for a mesh with [`PrimitiveTopology::PointList`](super::PrimitiveTopology::PointList).
#[error("Mesh winding inversion does not work for primitive topology `PointList`")]
WrongTopology,
/// This error occurs when you try to invert the winding for a mesh with
/// * [`PrimitiveTopology::TriangleList`](super::PrimitiveTopology::TriangleList), but the indices are not in chunks of 3.
/// * [`PrimitiveTopology::LineList`](super::PrimitiveTopology::LineList), but the indices are not in chunks of 2.
#[error("Indices weren't in chunks according to topology")]
AbruptIndicesEnd,
}
/// An error that occurred while trying to extract a collection of triangles from a [`Mesh`](super::Mesh).
#[derive(Debug, Error)]
pub enum MeshTrianglesError {
#[error("Source mesh does not have primitive topology TriangleList or TriangleStrip")]
WrongTopology,
#[error("Source mesh lacks position data")]
MissingPositions,
#[error("Source mesh position data is not Float32x3")]
PositionsFormat,
#[error("Source mesh lacks face index data")]
MissingIndices,
#[error("Face index data references vertices that do not exist")]
BadIndices,
}
/// An array of indices into the [`VertexAttributeValues`](super::VertexAttributeValues) for a mesh.
///
/// It describes the order in which the vertex attributes should be joined into faces.
#[derive(Debug, Clone, Reflect)]
#[reflect(Clone)]
pub enum Indices {
U16(Vec<u16>),
U32(Vec<u32>),
}
impl Indices {
/// Returns an iterator over the indices.
pub fn iter(&self) -> impl Iterator<Item = usize> + '_ {
match self {
Indices::U16(vec) => IndicesIter::U16(vec.iter()),
Indices::U32(vec) => IndicesIter::U32(vec.iter()),
}
}
/// Returns the number of indices.
pub fn len(&self) -> usize {
match self {
Indices::U16(vec) => vec.len(),
Indices::U32(vec) => vec.len(),
}
}
/// Returns `true` if there are no indices.
pub fn is_empty(&self) -> bool {
match self {
Indices::U16(vec) => vec.is_empty(),
Indices::U32(vec) => vec.is_empty(),
}
}
/// Add an index. If the index is greater than `u16::MAX`,
/// the storage will be converted to `u32`.
pub fn push(&mut self, index: u32) {
self.extend([index]);
}
}
/// Extend the indices with indices from an iterator.
/// Semantically equivalent to calling [`push`](Indices::push) for each element in the iterator,
/// but more efficient.
impl Extend<u32> for Indices {
fn extend<T: IntoIterator<Item = u32>>(&mut self, iter: T) {
let mut iter = iter.into_iter();
match self {
Indices::U32(indices) => indices.extend(iter),
Indices::U16(indices) => {
indices.reserve(iter.size_hint().0);
while let Some(index) = iter.next() {
match u16::try_from(index) {
Ok(index) => indices.push(index),
Err(_) => {
let new_vec = indices
.iter()
.map(|&index| u32::from(index))
.chain(iter::once(index))
.chain(iter)
.collect::<Vec<u32>>();
*self = Indices::U32(new_vec);
break;
}
}
}
}
}
}
}
/// An Iterator for the [`Indices`].
enum IndicesIter<'a> {
U16(core::slice::Iter<'a, u16>),
U32(core::slice::Iter<'a, u32>),
}
impl Iterator for IndicesIter<'_> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
match self {
IndicesIter::U16(iter) => iter.next().map(|val| *val as usize),
IndicesIter::U32(iter) => iter.next().map(|val| *val as usize),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
IndicesIter::U16(iter) => iter.size_hint(),
IndicesIter::U32(iter) => iter.size_hint(),
}
}
}
impl<'a> ExactSizeIterator for IndicesIter<'a> {}
impl<'a> FusedIterator for IndicesIter<'a> {}
impl From<&Indices> for IndexFormat {
fn from(indices: &Indices) -> Self {
match indices {
Indices::U16(_) => IndexFormat::Uint16,
Indices::U32(_) => IndexFormat::Uint32,
}
}
}
#[cfg(test)]
mod tests {
use crate::Indices;
use wgpu_types::IndexFormat;
#[test]
fn test_indices_push() {
let mut indices = Indices::U16(Vec::new());
indices.push(10);
assert_eq!(IndexFormat::Uint16, IndexFormat::from(&indices));
assert_eq!(vec![10], indices.iter().collect::<Vec<_>>());
// Add a value that is too large for `u16` so the storage should be converted to `U32`.
indices.push(0x10000);
assert_eq!(IndexFormat::Uint32, IndexFormat::from(&indices));
assert_eq!(vec![10, 0x10000], indices.iter().collect::<Vec<_>>());
indices.push(20);
indices.push(0x20000);
assert_eq!(IndexFormat::Uint32, IndexFormat::from(&indices));
assert_eq!(
vec![10, 0x10000, 20, 0x20000],
indices.iter().collect::<Vec<_>>()
);
}
#[test]
fn test_indices_extend() {
let mut indices = Indices::U16(Vec::new());
indices.extend([10, 11]);
assert_eq!(IndexFormat::Uint16, IndexFormat::from(&indices));
assert_eq!(vec![10, 11], indices.iter().collect::<Vec<_>>());
// Add a value that is too large for `u16` so the storage should be converted to `U32`.
indices.extend([12, 0x10013, 0x10014]);
assert_eq!(IndexFormat::Uint32, IndexFormat::from(&indices));
assert_eq!(
vec![10, 11, 12, 0x10013, 0x10014],
indices.iter().collect::<Vec<_>>()
);
indices.extend([15, 0x10016]);
assert_eq!(IndexFormat::Uint32, IndexFormat::from(&indices));
assert_eq!(
vec![10, 11, 12, 0x10013, 0x10014, 15, 0x10016],
indices.iter().collect::<Vec<_>>()
);
}
}

57
vendor/bevy_mesh/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,57 @@
#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
extern crate alloc;
extern crate core;
mod conversions;
mod index;
mod mesh;
mod mikktspace;
pub mod morph;
pub mod primitives;
pub mod skinning;
mod vertex;
use bitflags::bitflags;
pub use index::*;
pub use mesh::*;
pub use mikktspace::*;
pub use primitives::*;
pub use vertex::*;
pub use wgpu_types::VertexFormat;
bitflags! {
/// Our base mesh pipeline key bits start from the highest bit and go
/// downward. The PBR mesh pipeline key bits start from the lowest bit and
/// go upward. This allows the PBR bits in the downstream crate `bevy_pbr`
/// to coexist in the same field without any shifts.
#[derive(Clone, Debug)]
pub struct BaseMeshPipelineKey: u64 {
const MORPH_TARGETS = 1 << (u64::BITS - 1);
}
}
impl BaseMeshPipelineKey {
pub const PRIMITIVE_TOPOLOGY_MASK_BITS: u64 = 0b111;
pub const PRIMITIVE_TOPOLOGY_SHIFT_BITS: u64 =
(u64::BITS - 1 - Self::PRIMITIVE_TOPOLOGY_MASK_BITS.count_ones()) as u64;
pub fn from_primitive_topology(primitive_topology: PrimitiveTopology) -> Self {
let primitive_topology_bits = ((primitive_topology as u64)
& Self::PRIMITIVE_TOPOLOGY_MASK_BITS)
<< Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
Self::from_bits_retain(primitive_topology_bits)
}
pub fn primitive_topology(&self) -> PrimitiveTopology {
let primitive_topology_bits = (self.bits() >> Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS)
& Self::PRIMITIVE_TOPOLOGY_MASK_BITS;
match primitive_topology_bits {
x if x == PrimitiveTopology::PointList as u64 => PrimitiveTopology::PointList,
x if x == PrimitiveTopology::LineList as u64 => PrimitiveTopology::LineList,
x if x == PrimitiveTopology::LineStrip as u64 => PrimitiveTopology::LineStrip,
x if x == PrimitiveTopology::TriangleList as u64 => PrimitiveTopology::TriangleList,
x if x == PrimitiveTopology::TriangleStrip as u64 => PrimitiveTopology::TriangleStrip,
_ => PrimitiveTopology::default(),
}
}
}

1554
vendor/bevy_mesh/src/mesh.rs vendored Normal file

File diff suppressed because it is too large Load Diff

142
vendor/bevy_mesh/src/mikktspace.rs vendored Normal file
View File

@@ -0,0 +1,142 @@
use super::{Indices, Mesh, VertexAttributeValues};
use bevy_math::Vec3;
use thiserror::Error;
use wgpu_types::{PrimitiveTopology, VertexFormat};
struct MikktspaceGeometryHelper<'a> {
indices: Option<&'a Indices>,
positions: &'a Vec<[f32; 3]>,
normals: &'a Vec<[f32; 3]>,
uvs: &'a Vec<[f32; 2]>,
tangents: Vec<[f32; 4]>,
}
impl MikktspaceGeometryHelper<'_> {
fn index(&self, face: usize, vert: usize) -> usize {
let index_index = face * 3 + vert;
match self.indices {
Some(Indices::U16(indices)) => indices[index_index] as usize,
Some(Indices::U32(indices)) => indices[index_index] as usize,
None => index_index,
}
}
}
impl bevy_mikktspace::Geometry for MikktspaceGeometryHelper<'_> {
fn num_faces(&self) -> usize {
self.indices
.map(Indices::len)
.unwrap_or_else(|| self.positions.len())
/ 3
}
fn num_vertices_of_face(&self, _: usize) -> usize {
3
}
fn position(&self, face: usize, vert: usize) -> [f32; 3] {
self.positions[self.index(face, vert)]
}
fn normal(&self, face: usize, vert: usize) -> [f32; 3] {
self.normals[self.index(face, vert)]
}
fn tex_coord(&self, face: usize, vert: usize) -> [f32; 2] {
self.uvs[self.index(face, vert)]
}
fn set_tangent_encoded(&mut self, tangent: [f32; 4], face: usize, vert: usize) {
let idx = self.index(face, vert);
self.tangents[idx] = tangent;
}
}
#[derive(Error, Debug)]
/// Failed to generate tangents for the mesh.
pub enum GenerateTangentsError {
#[error("cannot generate tangents for {0:?}")]
UnsupportedTopology(PrimitiveTopology),
#[error("missing indices")]
MissingIndices,
#[error("missing vertex attributes '{0}'")]
MissingVertexAttribute(&'static str),
#[error("the '{0}' vertex attribute should have {1:?} format")]
InvalidVertexAttributeFormat(&'static str, VertexFormat),
#[error("mesh not suitable for tangent generation")]
MikktspaceError,
}
pub(crate) fn generate_tangents_for_mesh(
mesh: &Mesh,
) -> Result<Vec<[f32; 4]>, GenerateTangentsError> {
match mesh.primitive_topology() {
PrimitiveTopology::TriangleList => {}
other => return Err(GenerateTangentsError::UnsupportedTopology(other)),
};
let positions = mesh.attribute(Mesh::ATTRIBUTE_POSITION).ok_or(
GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_POSITION.name),
)?;
let VertexAttributeValues::Float32x3(positions) = positions else {
return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
Mesh::ATTRIBUTE_POSITION.name,
VertexFormat::Float32x3,
));
};
let normals = mesh.attribute(Mesh::ATTRIBUTE_NORMAL).ok_or(
GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_NORMAL.name),
)?;
let VertexAttributeValues::Float32x3(normals) = normals else {
return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
Mesh::ATTRIBUTE_NORMAL.name,
VertexFormat::Float32x3,
));
};
let uvs = mesh.attribute(Mesh::ATTRIBUTE_UV_0).ok_or(
GenerateTangentsError::MissingVertexAttribute(Mesh::ATTRIBUTE_UV_0.name),
)?;
let VertexAttributeValues::Float32x2(uvs) = uvs else {
return Err(GenerateTangentsError::InvalidVertexAttributeFormat(
Mesh::ATTRIBUTE_UV_0.name,
VertexFormat::Float32x2,
));
};
let len = positions.len();
let tangents = vec![[0., 0., 0., 0.]; len];
let mut mikktspace_mesh = MikktspaceGeometryHelper {
indices: mesh.indices(),
positions,
normals,
uvs,
tangents,
};
let success = bevy_mikktspace::generate_tangents(&mut mikktspace_mesh);
if !success {
return Err(GenerateTangentsError::MikktspaceError);
}
// mikktspace seems to assume left-handedness so we can flip the sign to correct for this
for tangent in &mut mikktspace_mesh.tangents {
tangent[3] = -tangent[3];
}
Ok(mikktspace_mesh.tangents)
}
/// Correctly scales and renormalizes an already normalized `normal` by the scale determined by its reciprocal `scale_recip`
pub(crate) fn scale_normal(normal: Vec3, scale_recip: Vec3) -> Vec3 {
// This is basically just `normal * scale_recip` but with the added rule that `0. * anything == 0.`
// This is necessary because components of `scale_recip` may be infinities, which do not multiply to zero
let n = Vec3::select(normal.cmpeq(Vec3::ZERO), Vec3::ZERO, normal * scale_recip);
// If n is finite, no component of `scale_recip` was infinite or the normal was perpendicular to the scale
// else the scale had at least one zero-component and the normal needs to point along the direction of that component
if n.is_finite() {
n.normalize_or_zero()
} else {
Vec3::select(n.abs().cmpeq(Vec3::INFINITY), n.signum(), Vec3::ZERO).normalize()
}
}

253
vendor/bevy_mesh/src/morph.rs vendored Normal file
View File

@@ -0,0 +1,253 @@
use super::Mesh;
use bevy_asset::{Handle, RenderAssetUsages};
use bevy_ecs::prelude::*;
use bevy_image::Image;
use bevy_math::Vec3;
use bevy_reflect::prelude::*;
use bytemuck::{Pod, Zeroable};
use thiserror::Error;
use wgpu_types::{Extent3d, TextureDimension, TextureFormat};
const MAX_TEXTURE_WIDTH: u32 = 2048;
// NOTE: "component" refers to the element count of math objects,
// Vec3 has 3 components, Mat2 has 4 components.
const MAX_COMPONENTS: u32 = MAX_TEXTURE_WIDTH * MAX_TEXTURE_WIDTH;
/// Max target count available for [morph targets](MorphWeights).
pub const MAX_MORPH_WEIGHTS: usize = 64;
#[derive(Error, Clone, Debug)]
pub enum MorphBuildError {
#[error(
"Too many vertex×components in morph target, max is {MAX_COMPONENTS}, \
got {vertex_count}×{component_count} = {}",
*vertex_count * *component_count as usize
)]
TooManyAttributes {
vertex_count: usize,
component_count: u32,
},
#[error(
"Bevy only supports up to {} morph targets (individual poses), tried to \
create a model with {target_count} morph targets",
MAX_MORPH_WEIGHTS
)]
TooManyTargets { target_count: usize },
}
/// An image formatted for use with [`MorphWeights`] for rendering the morph target.
#[derive(Debug)]
pub struct MorphTargetImage(pub Image);
impl MorphTargetImage {
/// Generate textures for each morph target.
///
/// This accepts an "iterator of [`MorphAttributes`] iterators". Each item iterated in the top level
/// iterator corresponds "the attributes of a specific morph target".
///
/// Each pixel of the texture is a component of morph target animated
/// attributes. So a set of 9 pixels is this morph's displacement for
/// position, normal and tangents of a single vertex (each taking 3 pixels).
pub fn new(
targets: impl ExactSizeIterator<Item = impl Iterator<Item = MorphAttributes>>,
vertex_count: usize,
asset_usage: RenderAssetUsages,
) -> Result<Self, MorphBuildError> {
let max = MAX_TEXTURE_WIDTH;
let target_count = targets.len();
if target_count > MAX_MORPH_WEIGHTS {
return Err(MorphBuildError::TooManyTargets { target_count });
}
let component_count = (vertex_count * MorphAttributes::COMPONENT_COUNT) as u32;
let Some((Rect(width, height), padding)) = lowest_2d(component_count, max) else {
return Err(MorphBuildError::TooManyAttributes {
vertex_count,
component_count,
});
};
let data = targets
.flat_map(|mut attributes| {
let layer_byte_count = (padding + component_count) as usize * size_of::<f32>();
let mut buffer = Vec::with_capacity(layer_byte_count);
for _ in 0..vertex_count {
let Some(to_add) = attributes.next() else {
break;
};
buffer.extend_from_slice(bytemuck::bytes_of(&to_add));
}
// Pad each layer so that they fit width * height
buffer.extend(core::iter::repeat_n(0, padding as usize * size_of::<f32>()));
debug_assert_eq!(buffer.len(), layer_byte_count);
buffer
})
.collect();
let extents = Extent3d {
width,
height,
depth_or_array_layers: target_count as u32,
};
let image = Image::new(
extents,
TextureDimension::D3,
data,
TextureFormat::R32Float,
asset_usage,
);
Ok(MorphTargetImage(image))
}
}
/// Controls the [morph targets] for all child `Mesh3d` entities. In most cases, [`MorphWeights`] should be considered
/// the "source of truth" when writing morph targets for meshes. However you can choose to write child [`MeshMorphWeights`]
/// if your situation requires more granularity. Just note that if you set [`MorphWeights`], it will overwrite child
/// [`MeshMorphWeights`] values.
///
/// This exists because Bevy's [`Mesh`] corresponds to a _single_ surface / material, whereas morph targets
/// as defined in the GLTF spec exist on "multi-primitive meshes" (where each primitive is its own surface with its own material).
/// Therefore in Bevy [`MorphWeights`] an a parent entity are the "canonical weights" from a GLTF perspective, which then
/// synchronized to child `Mesh3d` / [`MeshMorphWeights`] (which correspond to "primitives" / "surfaces" from a GLTF perspective).
///
/// Add this to the parent of one or more [`Entities`](`Entity`) with a `Mesh3d` with a [`MeshMorphWeights`].
///
/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
#[derive(Reflect, Default, Debug, Clone, Component)]
#[reflect(Debug, Component, Default, Clone)]
pub struct MorphWeights {
weights: Vec<f32>,
/// The first mesh primitive assigned to these weights
first_mesh: Option<Handle<Mesh>>,
}
impl MorphWeights {
pub fn new(
weights: Vec<f32>,
first_mesh: Option<Handle<Mesh>>,
) -> Result<Self, MorphBuildError> {
if weights.len() > MAX_MORPH_WEIGHTS {
let target_count = weights.len();
return Err(MorphBuildError::TooManyTargets { target_count });
}
Ok(MorphWeights {
weights,
first_mesh,
})
}
/// The first child `Mesh3d` primitive controlled by these weights.
/// This can be used to look up metadata information such as [`Mesh::morph_target_names`].
pub fn first_mesh(&self) -> Option<&Handle<Mesh>> {
self.first_mesh.as_ref()
}
pub fn weights(&self) -> &[f32] {
&self.weights
}
pub fn weights_mut(&mut self) -> &mut [f32] {
&mut self.weights
}
}
/// Control a specific [`Mesh`] instance's [morph targets]. These control the weights of
/// specific "mesh primitives" in scene formats like GLTF. They can be set manually, but
/// in most cases they should "automatically" synced by setting the [`MorphWeights`] component
/// on a parent entity.
///
/// See [`MorphWeights`] for more details on Bevy's morph target implementation.
///
/// Add this to an [`Entity`] with a `Mesh3d` with a [`MorphAttributes`] set
/// to control individual weights of each morph target.
///
/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
#[derive(Reflect, Default, Debug, Clone, Component)]
#[reflect(Debug, Component, Default, Clone)]
pub struct MeshMorphWeights {
weights: Vec<f32>,
}
impl MeshMorphWeights {
pub fn new(weights: Vec<f32>) -> Result<Self, MorphBuildError> {
if weights.len() > MAX_MORPH_WEIGHTS {
let target_count = weights.len();
return Err(MorphBuildError::TooManyTargets { target_count });
}
Ok(MeshMorphWeights { weights })
}
pub fn weights(&self) -> &[f32] {
&self.weights
}
pub fn weights_mut(&mut self) -> &mut [f32] {
&mut self.weights
}
pub fn clear_weights(&mut self) {
self.weights.clear();
}
pub fn extend_weights(&mut self, weights: &[f32]) {
self.weights.extend(weights);
}
}
/// Attributes **differences** used for morph targets.
///
/// See [`MorphTargetImage`] for more information.
#[derive(Copy, Clone, PartialEq, Pod, Zeroable, Default)]
#[repr(C)]
pub struct MorphAttributes {
/// The vertex position difference between base mesh and this target.
pub position: Vec3,
/// The vertex normal difference between base mesh and this target.
pub normal: Vec3,
/// The vertex tangent difference between base mesh and this target.
///
/// Note that tangents are a `Vec4`, but only the `xyz` components are
/// animated, as the `w` component is the sign and cannot be animated.
pub tangent: Vec3,
}
impl From<[Vec3; 3]> for MorphAttributes {
fn from([position, normal, tangent]: [Vec3; 3]) -> Self {
MorphAttributes {
position,
normal,
tangent,
}
}
}
impl MorphAttributes {
/// How many components `MorphAttributes` has.
///
/// Each `Vec3` has 3 components, we have 3 `Vec3`, for a total of 9.
pub const COMPONENT_COUNT: usize = 9;
pub fn new(position: Vec3, normal: Vec3, tangent: Vec3) -> Self {
MorphAttributes {
position,
normal,
tangent,
}
}
}
struct Rect(u32, u32);
/// Find the smallest rectangle of maximum edge size `max_edge` that contains
/// at least `min_includes` cells. `u32` is how many extra cells the rectangle
/// has.
///
/// The following rectangle contains 27 cells, and its longest edge is 9:
/// ```text
/// ----------------------------
/// |1 |2 |3 |4 |5 |6 |7 |8 |9 |
/// ----------------------------
/// |2 | | | | | | | | |
/// ----------------------------
/// |3 | | | | | | | | |
/// ----------------------------
/// ```
///
/// Returns `None` if `max_edge` is too small to build a rectangle
/// containing `min_includes` cells.
fn lowest_2d(min_includes: u32, max_edge: u32) -> Option<(Rect, u32)> {
(1..=max_edge)
.filter_map(|a| {
let b = min_includes.div_ceil(a);
let diff = (a * b).checked_sub(min_includes)?;
Some((Rect(a, b), diff))
})
.filter_map(|(rect, diff)| (rect.1 <= max_edge).then_some((rect, diff)))
.min_by_key(|(_, diff)| *diff)
}

1257
vendor/bevy_mesh/src/primitives/dim2.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,436 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{ops, primitives::Capsule3d, Vec2, Vec3};
use bevy_reflect::prelude::*;
/// Manner in which UV coordinates are distributed vertically.
#[derive(Clone, Copy, Debug, Default, Reflect)]
#[reflect(Default, Debug, Clone)]
pub enum CapsuleUvProfile {
/// UV space is distributed by how much of the capsule consists of the hemispheres.
#[default]
Aspect,
/// Hemispheres get UV space according to the ratio of latitudes to rings.
Uniform,
/// Upper third of the texture goes to the northern hemisphere, middle third to the cylinder
/// and lower third to the southern one.
Fixed,
}
/// A builder used for creating a [`Mesh`] with a [`Capsule3d`] shape.
#[derive(Clone, Copy, Debug, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct Capsule3dMeshBuilder {
/// The [`Capsule3d`] shape.
pub capsule: Capsule3d,
/// The number of horizontal lines subdividing the cylindrical part of the capsule.
/// The default is `0`.
pub rings: u32,
/// The number of vertical lines subdividing the hemispheres of the capsule.
/// The default is `32`.
pub longitudes: u32,
/// The number of horizontal lines subdividing the hemispheres of the capsule.
/// The default is `16`.
pub latitudes: u32,
/// The manner in which UV coordinates are distributed vertically.
/// The default is [`CapsuleUvProfile::Aspect`].
pub uv_profile: CapsuleUvProfile,
}
impl Default for Capsule3dMeshBuilder {
fn default() -> Self {
Self {
capsule: Capsule3d::default(),
rings: 0,
longitudes: 32,
latitudes: 16,
uv_profile: CapsuleUvProfile::default(),
}
}
}
impl Capsule3dMeshBuilder {
/// Creates a new [`Capsule3dMeshBuilder`] from a given radius, height, longitudes, and latitudes.
///
/// Note that `height` is the distance between the centers of the hemispheres.
/// `radius` will be added to both ends to get the real height of the mesh.
#[inline]
pub fn new(radius: f32, height: f32, longitudes: u32, latitudes: u32) -> Self {
Self {
capsule: Capsule3d::new(radius, height),
longitudes,
latitudes,
..Default::default()
}
}
/// Sets the number of horizontal lines subdividing the cylindrical part of the capsule.
#[inline]
pub const fn rings(mut self, rings: u32) -> Self {
self.rings = rings;
self
}
/// Sets the number of vertical lines subdividing the hemispheres of the capsule.
#[inline]
pub const fn longitudes(mut self, longitudes: u32) -> Self {
self.longitudes = longitudes;
self
}
/// Sets the number of horizontal lines subdividing the hemispheres of the capsule.
#[inline]
pub const fn latitudes(mut self, latitudes: u32) -> Self {
self.latitudes = latitudes;
self
}
/// Sets the manner in which UV coordinates are distributed vertically.
#[inline]
pub const fn uv_profile(mut self, uv_profile: CapsuleUvProfile) -> Self {
self.uv_profile = uv_profile;
self
}
}
impl MeshBuilder for Capsule3dMeshBuilder {
fn build(&self) -> Mesh {
// code adapted from https://behreajj.medium.com/making-a-capsule-mesh-via-script-in-five-3d-environments-c2214abf02db
let Capsule3dMeshBuilder {
capsule,
rings,
longitudes,
latitudes,
uv_profile,
} = *self;
let Capsule3d {
radius,
half_length,
} = capsule;
let calc_middle = rings > 0;
let half_lats = latitudes / 2;
let half_latsn1 = half_lats - 1;
let half_latsn2 = half_lats - 2;
let ringsp1 = rings + 1;
let lonsp1 = longitudes + 1;
let summit = half_length + radius;
// Vertex index offsets.
let vert_offset_north_hemi = longitudes;
let vert_offset_north_equator = vert_offset_north_hemi + lonsp1 * half_latsn1;
let vert_offset_cylinder = vert_offset_north_equator + lonsp1;
let vert_offset_south_equator = if calc_middle {
vert_offset_cylinder + lonsp1 * rings
} else {
vert_offset_cylinder
};
let vert_offset_south_hemi = vert_offset_south_equator + lonsp1;
let vert_offset_south_polar = vert_offset_south_hemi + lonsp1 * half_latsn2;
let vert_offset_south_cap = vert_offset_south_polar + lonsp1;
// Initialize arrays.
let vert_len = (vert_offset_south_cap + longitudes) as usize;
let mut vs: Vec<Vec3> = vec![Vec3::ZERO; vert_len];
let mut vts: Vec<Vec2> = vec![Vec2::ZERO; vert_len];
let mut vns: Vec<Vec3> = vec![Vec3::ZERO; vert_len];
let to_theta = 2.0 * core::f32::consts::PI / longitudes as f32;
let to_phi = core::f32::consts::PI / latitudes as f32;
let to_tex_horizontal = 1.0 / longitudes as f32;
let to_tex_vertical = 1.0 / half_lats as f32;
let vt_aspect_ratio = match uv_profile {
CapsuleUvProfile::Aspect => radius / (2.0 * half_length + radius + radius),
CapsuleUvProfile::Uniform => half_lats as f32 / (ringsp1 + latitudes) as f32,
CapsuleUvProfile::Fixed => 1.0 / 3.0,
};
let vt_aspect_north = 1.0 - vt_aspect_ratio;
let vt_aspect_south = vt_aspect_ratio;
let mut theta_cartesian: Vec<Vec2> = vec![Vec2::ZERO; longitudes as usize];
let mut rho_theta_cartesian: Vec<Vec2> = vec![Vec2::ZERO; longitudes as usize];
let mut s_texture_cache: Vec<f32> = vec![0.0; lonsp1 as usize];
for j in 0..longitudes as usize {
let jf = j as f32;
let s_texture_polar = 1.0 - ((jf + 0.5) * to_tex_horizontal);
let theta = jf * to_theta;
theta_cartesian[j] = Vec2::from_angle(theta);
rho_theta_cartesian[j] = radius * theta_cartesian[j];
// North.
vs[j] = Vec3::new(0.0, summit, 0.0);
vts[j] = Vec2::new(s_texture_polar, 1.0);
vns[j] = Vec3::Y;
// South.
let idx = vert_offset_south_cap as usize + j;
vs[idx] = Vec3::new(0.0, -summit, 0.0);
vts[idx] = Vec2::new(s_texture_polar, 0.0);
vns[idx] = Vec3::new(0.0, -1.0, 0.0);
}
// Equatorial vertices.
for (j, s_texture_cache_j) in s_texture_cache.iter_mut().enumerate().take(lonsp1 as usize) {
let s_texture = 1.0 - j as f32 * to_tex_horizontal;
*s_texture_cache_j = s_texture;
// Wrap to first element upon reaching last.
let j_mod = j % longitudes as usize;
let tc = theta_cartesian[j_mod];
let rtc = rho_theta_cartesian[j_mod];
// North equator.
let idxn = vert_offset_north_equator as usize + j;
vs[idxn] = Vec3::new(rtc.x, half_length, -rtc.y);
vts[idxn] = Vec2::new(s_texture, vt_aspect_north);
vns[idxn] = Vec3::new(tc.x, 0.0, -tc.y);
// South equator.
let idxs = vert_offset_south_equator as usize + j;
vs[idxs] = Vec3::new(rtc.x, -half_length, -rtc.y);
vts[idxs] = Vec2::new(s_texture, vt_aspect_south);
vns[idxs] = Vec3::new(tc.x, 0.0, -tc.y);
}
// Hemisphere vertices.
for i in 0..half_latsn1 {
let ip1f = i as f32 + 1.0;
let phi = ip1f * to_phi;
// For coordinates.
let (sin_phi_south, cos_phi_south) = ops::sin_cos(phi);
// Symmetrical hemispheres mean cosine and sine only needs
// to be calculated once.
let cos_phi_north = sin_phi_south;
let sin_phi_north = -cos_phi_south;
let rho_cos_phi_north = radius * cos_phi_north;
let rho_sin_phi_north = radius * sin_phi_north;
let z_offset_north = half_length - rho_sin_phi_north;
let rho_cos_phi_south = radius * cos_phi_south;
let rho_sin_phi_south = radius * sin_phi_south;
let z_offset_sout = -half_length - rho_sin_phi_south;
// For texture coordinates.
let t_tex_fac = ip1f * to_tex_vertical;
let cmpl_tex_fac = 1.0 - t_tex_fac;
let t_tex_north = cmpl_tex_fac + vt_aspect_north * t_tex_fac;
let t_tex_south = cmpl_tex_fac * vt_aspect_south;
let i_lonsp1 = i * lonsp1;
let vert_curr_lat_north = vert_offset_north_hemi + i_lonsp1;
let vert_curr_lat_south = vert_offset_south_hemi + i_lonsp1;
for (j, s_texture) in s_texture_cache.iter().enumerate().take(lonsp1 as usize) {
let j_mod = j % longitudes as usize;
let tc = theta_cartesian[j_mod];
// North hemisphere.
let idxn = vert_curr_lat_north as usize + j;
vs[idxn] = Vec3::new(
rho_cos_phi_north * tc.x,
z_offset_north,
-rho_cos_phi_north * tc.y,
);
vts[idxn] = Vec2::new(*s_texture, t_tex_north);
vns[idxn] = Vec3::new(cos_phi_north * tc.x, -sin_phi_north, -cos_phi_north * tc.y);
// South hemisphere.
let idxs = vert_curr_lat_south as usize + j;
vs[idxs] = Vec3::new(
rho_cos_phi_south * tc.x,
z_offset_sout,
-rho_cos_phi_south * tc.y,
);
vts[idxs] = Vec2::new(*s_texture, t_tex_south);
vns[idxs] = Vec3::new(cos_phi_south * tc.x, -sin_phi_south, -cos_phi_south * tc.y);
}
}
// Cylinder vertices.
if calc_middle {
// Exclude both origin and destination edges
// (North and South equators) from the interpolation.
let to_fac = 1.0 / ringsp1 as f32;
let mut idx_cyl_lat = vert_offset_cylinder as usize;
for h in 1..ringsp1 {
let fac = h as f32 * to_fac;
let cmpl_fac = 1.0 - fac;
let t_texture = cmpl_fac * vt_aspect_north + fac * vt_aspect_south;
let z = half_length - 2.0 * half_length * fac;
for (j, s_texture) in s_texture_cache.iter().enumerate().take(lonsp1 as usize) {
let j_mod = j % longitudes as usize;
let tc = theta_cartesian[j_mod];
let rtc = rho_theta_cartesian[j_mod];
vs[idx_cyl_lat] = Vec3::new(rtc.x, z, -rtc.y);
vts[idx_cyl_lat] = Vec2::new(*s_texture, t_texture);
vns[idx_cyl_lat] = Vec3::new(tc.x, 0.0, -tc.y);
idx_cyl_lat += 1;
}
}
}
// Triangle indices.
// Stride is 3 for polar triangles;
// stride is 6 for two triangles forming a quad.
let lons3 = longitudes * 3;
let lons6 = longitudes * 6;
let hemi_lons = half_latsn1 * lons6;
let tri_offset_north_hemi = lons3;
let tri_offset_cylinder = tri_offset_north_hemi + hemi_lons;
let tri_offset_south_hemi = tri_offset_cylinder + ringsp1 * lons6;
let tri_offset_south_cap = tri_offset_south_hemi + hemi_lons;
let fs_len = tri_offset_south_cap + lons3;
let mut tris: Vec<u32> = vec![0; fs_len as usize];
// Polar caps.
let mut i = 0;
let mut k = 0;
let mut m = tri_offset_south_cap as usize;
while i < longitudes {
// North.
tris[k] = i;
tris[k + 1] = vert_offset_north_hemi + i;
tris[k + 2] = vert_offset_north_hemi + i + 1;
// South.
tris[m] = vert_offset_south_cap + i;
tris[m + 1] = vert_offset_south_polar + i + 1;
tris[m + 2] = vert_offset_south_polar + i;
i += 1;
k += 3;
m += 3;
}
// Hemispheres.
let mut i = 0;
let mut k = tri_offset_north_hemi as usize;
let mut m = tri_offset_south_hemi as usize;
while i < half_latsn1 {
let i_lonsp1 = i * lonsp1;
let vert_curr_lat_north = vert_offset_north_hemi + i_lonsp1;
let vert_next_lat_north = vert_curr_lat_north + lonsp1;
let vert_curr_lat_south = vert_offset_south_equator + i_lonsp1;
let vert_next_lat_south = vert_curr_lat_south + lonsp1;
let mut j = 0;
while j < longitudes {
// North.
let north00 = vert_curr_lat_north + j;
let north01 = vert_next_lat_north + j;
let north11 = vert_next_lat_north + j + 1;
let north10 = vert_curr_lat_north + j + 1;
tris[k] = north00;
tris[k + 1] = north11;
tris[k + 2] = north10;
tris[k + 3] = north00;
tris[k + 4] = north01;
tris[k + 5] = north11;
// South.
let south00 = vert_curr_lat_south + j;
let south01 = vert_next_lat_south + j;
let south11 = vert_next_lat_south + j + 1;
let south10 = vert_curr_lat_south + j + 1;
tris[m] = south00;
tris[m + 1] = south11;
tris[m + 2] = south10;
tris[m + 3] = south00;
tris[m + 4] = south01;
tris[m + 5] = south11;
j += 1;
k += 6;
m += 6;
}
i += 1;
}
// Cylinder.
let mut i = 0;
let mut k = tri_offset_cylinder as usize;
while i < ringsp1 {
let vert_curr_lat = vert_offset_north_equator + i * lonsp1;
let vert_next_lat = vert_curr_lat + lonsp1;
let mut j = 0;
while j < longitudes {
let cy00 = vert_curr_lat + j;
let cy01 = vert_next_lat + j;
let cy11 = vert_next_lat + j + 1;
let cy10 = vert_curr_lat + j + 1;
tris[k] = cy00;
tris[k + 1] = cy11;
tris[k + 2] = cy10;
tris[k + 3] = cy00;
tris[k + 4] = cy01;
tris[k + 5] = cy11;
j += 1;
k += 6;
}
i += 1;
}
let vs: Vec<[f32; 3]> = vs.into_iter().map(Into::into).collect();
let vns: Vec<[f32; 3]> = vns.into_iter().map(Into::into).collect();
let vts: Vec<[f32; 2]> = vts.into_iter().map(Into::into).collect();
assert_eq!(vs.len(), vert_len);
assert_eq!(tris.len(), fs_len as usize);
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vs)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vns)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vts)
.with_inserted_indices(Indices::U32(tris))
}
}
impl Meshable for Capsule3d {
type Output = Capsule3dMeshBuilder;
fn mesh(&self) -> Self::Output {
Capsule3dMeshBuilder {
capsule: *self,
..Default::default()
}
}
}
impl From<Capsule3d> for Mesh {
fn from(capsule: Capsule3d) -> Self {
capsule.mesh().build()
}
}

View File

@@ -0,0 +1,271 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{ops, primitives::Cone, Vec3};
use bevy_reflect::prelude::*;
/// Anchoring options for [`ConeMeshBuilder`]
#[derive(Debug, Copy, Clone, Default, Reflect)]
#[reflect(Default, Debug, Clone)]
pub enum ConeAnchor {
#[default]
/// Midpoint between the tip of the cone and the center of its base.
MidPoint,
/// The Tip of the triangle
Tip,
/// The center of the base circle
Base,
}
/// A builder used for creating a [`Mesh`] with a [`Cone`] shape.
#[derive(Clone, Copy, Debug, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct ConeMeshBuilder {
/// The [`Cone`] shape.
pub cone: Cone,
/// The number of vertices used for the base of the cone.
///
/// The default is `32`.
pub resolution: u32,
/// The anchor point for the cone mesh, defaults to the midpoint between
/// the tip of the cone and the center of its base
pub anchor: ConeAnchor,
}
impl Default for ConeMeshBuilder {
fn default() -> Self {
Self {
cone: Cone::default(),
resolution: 32,
anchor: ConeAnchor::default(),
}
}
}
impl ConeMeshBuilder {
/// Creates a new [`ConeMeshBuilder`] from a given radius, height,
/// and number of vertices used for the base of the cone.
#[inline]
pub const fn new(radius: f32, height: f32, resolution: u32) -> Self {
Self {
cone: Cone { radius, height },
resolution,
anchor: ConeAnchor::MidPoint,
}
}
/// Sets the number of vertices used for the base of the cone.
#[inline]
pub const fn resolution(mut self, resolution: u32) -> Self {
self.resolution = resolution;
self
}
/// Sets a custom anchor point for the mesh
#[inline]
pub const fn anchor(mut self, anchor: ConeAnchor) -> Self {
self.anchor = anchor;
self
}
}
impl MeshBuilder for ConeMeshBuilder {
fn build(&self) -> Mesh {
let half_height = self.cone.height / 2.0;
// `resolution` vertices for the base, `resolution` vertices for the bottom of the lateral surface,
// and one vertex for the tip.
let num_vertices = self.resolution as usize * 2 + 1;
let num_indices = self.resolution as usize * 6 - 6;
let mut positions = Vec::with_capacity(num_vertices);
let mut normals = Vec::with_capacity(num_vertices);
let mut uvs = Vec::with_capacity(num_vertices);
let mut indices = Vec::with_capacity(num_indices);
// Tip
positions.push([0.0, half_height, 0.0]);
// The tip doesn't have a singular normal that works correctly.
// We use an invalid normal here so that it becomes NaN in the fragment shader
// and doesn't affect the overall shading. This might seem hacky, but it's one of
// the only ways to get perfectly smooth cones without creases or other shading artifacts.
//
// Note that this requires that normals are not normalized in the vertex shader,
// as that would make the entire triangle invalid and make the cone appear as black.
normals.push([0.0, 0.0, 0.0]);
// The UVs of the cone are in polar coordinates, so it's like projecting a circle texture from above.
// The center of the texture is at the center of the lateral surface, at the tip of the cone.
uvs.push([0.5, 0.5]);
// Now we build the lateral surface, the side of the cone.
// The vertex normals will be perpendicular to the surface.
//
// Here we get the slope of a normal and use it for computing
// the multiplicative inverse of the length of a vector in the direction
// of the normal. This allows us to normalize vertex normals efficiently.
let normal_slope = self.cone.radius / self.cone.height;
// Equivalent to Vec2::new(1.0, slope).length().recip()
let normalization_factor = (1.0 + normal_slope * normal_slope).sqrt().recip();
// How much the angle changes at each step
let step_theta = core::f32::consts::TAU / self.resolution as f32;
// Add vertices for the bottom of the lateral surface.
for segment in 0..self.resolution {
let theta = segment as f32 * step_theta;
let (sin, cos) = ops::sin_cos(theta);
// The vertex normal perpendicular to the side
let normal = Vec3::new(cos, normal_slope, sin) * normalization_factor;
positions.push([self.cone.radius * cos, -half_height, self.cone.radius * sin]);
normals.push(normal.to_array());
uvs.push([0.5 + cos * 0.5, 0.5 + sin * 0.5]);
}
// Add indices for the lateral surface. Each triangle is formed by the tip
// and two vertices at the base.
for j in 1..self.resolution {
indices.extend_from_slice(&[0, j + 1, j]);
}
// Close the surface with a triangle between the tip, first base vertex, and last base vertex.
indices.extend_from_slice(&[0, 1, self.resolution]);
// Now we build the actual base of the cone.
let index_offset = positions.len() as u32;
// Add base vertices.
for i in 0..self.resolution {
let theta = i as f32 * step_theta;
let (sin, cos) = ops::sin_cos(theta);
positions.push([cos * self.cone.radius, -half_height, sin * self.cone.radius]);
normals.push([0.0, -1.0, 0.0]);
uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);
}
// Add base indices.
for i in 1..(self.resolution - 1) {
indices.extend_from_slice(&[index_offset, index_offset + i, index_offset + i + 1]);
}
// Offset the vertex positions Y axis to match the anchor
match self.anchor {
ConeAnchor::Tip => positions.iter_mut().for_each(|p| p[1] -= half_height),
ConeAnchor::Base => positions.iter_mut().for_each(|p| p[1] += half_height),
ConeAnchor::MidPoint => (),
};
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(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)
}
}
impl Meshable for Cone {
type Output = ConeMeshBuilder;
fn mesh(&self) -> Self::Output {
ConeMeshBuilder {
cone: *self,
..Default::default()
}
}
}
impl From<Cone> for Mesh {
fn from(cone: Cone) -> Self {
cone.mesh().build()
}
}
#[cfg(test)]
mod tests {
use crate::{Mesh, MeshBuilder, Meshable, VertexAttributeValues};
use bevy_math::{primitives::Cone, Vec2};
/// Rounds floats to handle floating point error in tests.
fn round_floats<const N: usize>(points: &mut [[f32; N]]) {
for point in points.iter_mut() {
for coord in point.iter_mut() {
let round = (*coord * 100.0).round() / 100.0;
if (*coord - round).abs() < 0.00001 {
*coord = round;
}
}
}
}
#[test]
fn cone_mesh() {
let mut mesh = Cone {
radius: 0.5,
height: 1.0,
}
.mesh()
.resolution(4)
.build();
let Some(VertexAttributeValues::Float32x3(mut positions)) =
mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION)
else {
panic!("Expected positions f32x3");
};
let Some(VertexAttributeValues::Float32x3(mut normals)) =
mesh.remove_attribute(Mesh::ATTRIBUTE_NORMAL)
else {
panic!("Expected normals f32x3");
};
round_floats(&mut positions);
round_floats(&mut normals);
// Vertex positions
assert_eq!(
[
// Tip
[0.0, 0.5, 0.0],
// Lateral surface
[0.5, -0.5, 0.0],
[0.0, -0.5, 0.5],
[-0.5, -0.5, 0.0],
[0.0, -0.5, -0.5],
// Base
[0.5, -0.5, 0.0],
[0.0, -0.5, 0.5],
[-0.5, -0.5, 0.0],
[0.0, -0.5, -0.5],
],
&positions[..]
);
// Vertex normals
let [x, y] = Vec2::new(0.5, -1.0).perp().normalize().to_array();
assert_eq!(
&[
// Tip
[0.0, 0.0, 0.0],
// Lateral surface
[x, y, 0.0],
[0.0, y, x],
[-x, y, 0.0],
[0.0, y, -x],
// Base
[0.0, -1.0, 0.0],
[0.0, -1.0, 0.0],
[0.0, -1.0, 0.0],
[0.0, -1.0, 0.0],
],
&normals[..]
);
}
}

View File

@@ -0,0 +1,184 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{ops, primitives::ConicalFrustum, Vec3};
use bevy_reflect::prelude::*;
/// A builder used for creating a [`Mesh`] with a [`ConicalFrustum`] shape.
#[derive(Clone, Copy, Debug, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct ConicalFrustumMeshBuilder {
/// The [`ConicalFrustum`] shape.
pub frustum: ConicalFrustum,
/// The number of vertices used for the top and bottom of the conical frustum.
///
/// The default is `32`.
pub resolution: u32,
/// The number of horizontal lines subdividing the lateral surface of the conical frustum.
///
/// The default is `1`.
pub segments: u32,
}
impl Default for ConicalFrustumMeshBuilder {
fn default() -> Self {
Self {
frustum: ConicalFrustum::default(),
resolution: 32,
segments: 1,
}
}
}
impl ConicalFrustumMeshBuilder {
/// Creates a new [`ConicalFrustumMeshBuilder`] from the given top and bottom radii, a height,
/// and a resolution used for the top and bottom.
#[inline]
pub const fn new(radius_top: f32, radius_bottom: f32, height: f32, resolution: u32) -> Self {
Self {
frustum: ConicalFrustum {
radius_top,
radius_bottom,
height,
},
resolution,
segments: 1,
}
}
/// Sets the number of vertices used for the top and bottom of the conical frustum.
#[inline]
pub const fn resolution(mut self, resolution: u32) -> Self {
self.resolution = resolution;
self
}
/// Sets the number of horizontal lines subdividing the lateral surface of the conical frustum.
#[inline]
pub const fn segments(mut self, segments: u32) -> Self {
self.segments = segments;
self
}
}
impl MeshBuilder for ConicalFrustumMeshBuilder {
fn build(&self) -> Mesh {
debug_assert!(self.resolution > 2);
debug_assert!(self.segments > 0);
let ConicalFrustum {
radius_top,
radius_bottom,
height,
} = self.frustum;
let half_height = height / 2.0;
let num_rings = self.segments + 1;
let num_vertices = (self.resolution * 2 + num_rings * (self.resolution + 1)) as usize;
let num_faces = self.resolution * (num_rings - 2);
let num_indices = ((2 * num_faces + 2 * (self.resolution - 1) * 2) * 3) as usize;
let mut positions = Vec::with_capacity(num_vertices);
let mut normals = Vec::with_capacity(num_vertices);
let mut uvs = Vec::with_capacity(num_vertices);
let mut indices = Vec::with_capacity(num_indices);
let step_theta = core::f32::consts::TAU / self.resolution as f32;
let step_y = height / self.segments as f32;
let step_radius = (radius_top - radius_bottom) / self.segments as f32;
// Rings
for ring in 0..num_rings {
let y = -half_height + ring as f32 * step_y;
let radius = radius_bottom + ring as f32 * step_radius;
for segment in 0..=self.resolution {
let theta = segment as f32 * step_theta;
let (sin, cos) = ops::sin_cos(theta);
positions.push([radius * cos, y, radius * sin]);
normals.push(
Vec3::new(cos, (radius_bottom - radius_top) / height, sin)
.normalize()
.to_array(),
);
uvs.push([
segment as f32 / self.resolution as f32,
ring as f32 / self.segments as f32,
]);
}
}
// Lateral surface
for i in 0..self.segments {
let ring = i * (self.resolution + 1);
let next_ring = (i + 1) * (self.resolution + 1);
for j in 0..self.resolution {
indices.extend_from_slice(&[
ring + j,
next_ring + j,
ring + j + 1,
next_ring + j,
next_ring + j + 1,
ring + j + 1,
]);
}
}
// Caps
let mut build_cap = |top: bool, radius: f32| {
let offset = positions.len() as u32;
let (y, normal_y, winding) = if top {
(half_height, 1.0, (1, 0))
} else {
(-half_height, -1.0, (0, 1))
};
for i in 0..self.resolution {
let theta = i as f32 * step_theta;
let (sin, cos) = ops::sin_cos(theta);
positions.push([cos * radius, y, sin * radius]);
normals.push([0.0, normal_y, 0.0]);
uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);
}
for i in 1..(self.resolution - 1) {
indices.extend_from_slice(&[
offset,
offset + i + winding.0,
offset + i + winding.1,
]);
}
};
build_cap(true, radius_top);
build_cap(false, radius_bottom);
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(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)
}
}
impl Meshable for ConicalFrustum {
type Output = ConicalFrustumMeshBuilder;
fn mesh(&self) -> Self::Output {
ConicalFrustumMeshBuilder {
frustum: *self,
..Default::default()
}
}
}
impl From<ConicalFrustum> for Mesh {
fn from(frustum: ConicalFrustum) -> Self {
frustum.mesh().build()
}
}

View File

@@ -0,0 +1,99 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{primitives::Cuboid, Vec3};
use bevy_reflect::prelude::*;
/// A builder used for creating a [`Mesh`] with a [`Cuboid`] shape.
#[derive(Clone, Copy, Debug, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct CuboidMeshBuilder {
half_size: Vec3,
}
impl Default for CuboidMeshBuilder {
/// Returns the default [`CuboidMeshBuilder`] with a width, height, and depth of `1.0`.
fn default() -> Self {
Self {
half_size: Vec3::splat(0.5),
}
}
}
impl MeshBuilder for CuboidMeshBuilder {
fn build(&self) -> Mesh {
let min = -self.half_size;
let max = self.half_size;
// Suppose Y-up right hand, and camera look from +Z to -Z
let vertices = &[
// Front
([min.x, min.y, max.z], [0.0, 0.0, 1.0], [0.0, 0.0]),
([max.x, min.y, max.z], [0.0, 0.0, 1.0], [1.0, 0.0]),
([max.x, max.y, max.z], [0.0, 0.0, 1.0], [1.0, 1.0]),
([min.x, max.y, max.z], [0.0, 0.0, 1.0], [0.0, 1.0]),
// Back
([min.x, max.y, min.z], [0.0, 0.0, -1.0], [1.0, 0.0]),
([max.x, max.y, min.z], [0.0, 0.0, -1.0], [0.0, 0.0]),
([max.x, min.y, min.z], [0.0, 0.0, -1.0], [0.0, 1.0]),
([min.x, min.y, min.z], [0.0, 0.0, -1.0], [1.0, 1.0]),
// Right
([max.x, min.y, min.z], [1.0, 0.0, 0.0], [0.0, 0.0]),
([max.x, max.y, min.z], [1.0, 0.0, 0.0], [1.0, 0.0]),
([max.x, max.y, max.z], [1.0, 0.0, 0.0], [1.0, 1.0]),
([max.x, min.y, max.z], [1.0, 0.0, 0.0], [0.0, 1.0]),
// Left
([min.x, min.y, max.z], [-1.0, 0.0, 0.0], [1.0, 0.0]),
([min.x, max.y, max.z], [-1.0, 0.0, 0.0], [0.0, 0.0]),
([min.x, max.y, min.z], [-1.0, 0.0, 0.0], [0.0, 1.0]),
([min.x, min.y, min.z], [-1.0, 0.0, 0.0], [1.0, 1.0]),
// Top
([max.x, max.y, min.z], [0.0, 1.0, 0.0], [1.0, 0.0]),
([min.x, max.y, min.z], [0.0, 1.0, 0.0], [0.0, 0.0]),
([min.x, max.y, max.z], [0.0, 1.0, 0.0], [0.0, 1.0]),
([max.x, max.y, max.z], [0.0, 1.0, 0.0], [1.0, 1.0]),
// Bottom
([max.x, min.y, max.z], [0.0, -1.0, 0.0], [0.0, 0.0]),
([min.x, min.y, max.z], [0.0, -1.0, 0.0], [1.0, 0.0]),
([min.x, min.y, min.z], [0.0, -1.0, 0.0], [1.0, 1.0]),
([max.x, min.y, min.z], [0.0, -1.0, 0.0], [0.0, 1.0]),
];
let positions: Vec<_> = vertices.iter().map(|(p, _, _)| *p).collect();
let normals: Vec<_> = vertices.iter().map(|(_, n, _)| *n).collect();
let uvs: Vec<_> = vertices.iter().map(|(_, _, uv)| *uv).collect();
let indices = Indices::U32(vec![
0, 1, 2, 2, 3, 0, // front
4, 5, 6, 6, 7, 4, // back
8, 9, 10, 10, 11, 8, // right
12, 13, 14, 14, 15, 12, // left
16, 17, 18, 18, 19, 16, // top
20, 21, 22, 22, 23, 20, // bottom
]);
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
.with_inserted_indices(indices)
}
}
impl Meshable for Cuboid {
type Output = CuboidMeshBuilder;
fn mesh(&self) -> Self::Output {
CuboidMeshBuilder {
half_size: self.half_size,
}
}
}
impl From<Cuboid> for Mesh {
fn from(cuboid: Cuboid) -> Self {
cuboid.mesh().build()
}
}

View File

@@ -0,0 +1,222 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{ops, primitives::Cylinder};
use bevy_reflect::prelude::*;
/// Anchoring options for [`CylinderMeshBuilder`]
#[derive(Debug, Copy, Clone, Default, Reflect)]
#[reflect(Default, Debug, Clone)]
pub enum CylinderAnchor {
#[default]
/// Midpoint between the top and bottom caps of the cylinder
MidPoint,
/// The center of the top circle cap
Top,
/// The center of the bottom circle cap
Bottom,
}
/// A builder used for creating a [`Mesh`] with a [`Cylinder`] shape.
#[derive(Clone, Copy, Debug, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct CylinderMeshBuilder {
/// The [`Cylinder`] shape.
pub cylinder: Cylinder,
/// The number of vertices used for the top and bottom of the cylinder.
///
/// The default is `32`.
pub resolution: u32,
/// The number of segments along the height of the cylinder.
/// Must be greater than `0` for geometry to be generated.
///
/// The default is `1`.
pub segments: u32,
/// If set to `true`, the cylinder caps (flat circle faces) are built,
/// otherwise the mesh will be a shallow tube
pub caps: bool,
/// The anchor point for the cylinder mesh, defaults to the midpoint between
/// the top and bottom caps
pub anchor: CylinderAnchor,
}
impl Default for CylinderMeshBuilder {
fn default() -> Self {
Self {
cylinder: Cylinder::default(),
resolution: 32,
segments: 1,
caps: true,
anchor: CylinderAnchor::default(),
}
}
}
impl CylinderMeshBuilder {
/// Creates a new [`CylinderMeshBuilder`] from the given radius, a height,
/// and a resolution used for the top and bottom.
#[inline]
pub fn new(radius: f32, height: f32, resolution: u32) -> Self {
Self {
cylinder: Cylinder::new(radius, height),
resolution,
..Default::default()
}
}
/// Sets the number of vertices used for the top and bottom of the cylinder.
#[inline]
pub const fn resolution(mut self, resolution: u32) -> Self {
self.resolution = resolution;
self
}
/// Sets the number of segments along the height of the cylinder.
/// Must be greater than `0` for geometry to be generated.
#[inline]
pub const fn segments(mut self, segments: u32) -> Self {
self.segments = segments;
self
}
/// Ignore the cylinder caps, making the mesh a shallow tube instead
#[inline]
pub const fn without_caps(mut self) -> Self {
self.caps = false;
self
}
/// Sets a custom anchor point for the mesh
#[inline]
pub const fn anchor(mut self, anchor: CylinderAnchor) -> Self {
self.anchor = anchor;
self
}
}
impl MeshBuilder for CylinderMeshBuilder {
fn build(&self) -> Mesh {
let resolution = self.resolution;
let segments = self.segments;
debug_assert!(resolution > 2);
debug_assert!(segments > 0);
let num_rings = segments + 1;
let num_vertices = resolution * 2 + num_rings * (resolution + 1);
let num_faces = resolution * (num_rings - 2);
let num_indices = (2 * num_faces + 2 * (resolution - 1) * 2) * 3;
let mut positions = Vec::with_capacity(num_vertices as usize);
let mut normals = Vec::with_capacity(num_vertices as usize);
let mut uvs = Vec::with_capacity(num_vertices as usize);
let mut indices = Vec::with_capacity(num_indices as usize);
let step_theta = core::f32::consts::TAU / resolution as f32;
let step_y = 2.0 * self.cylinder.half_height / segments as f32;
// rings
for ring in 0..num_rings {
let y = -self.cylinder.half_height + ring as f32 * step_y;
for segment in 0..=resolution {
let theta = segment as f32 * step_theta;
let (sin, cos) = ops::sin_cos(theta);
positions.push([self.cylinder.radius * cos, y, self.cylinder.radius * sin]);
normals.push([cos, 0., sin]);
uvs.push([
segment as f32 / resolution as f32,
ring as f32 / segments as f32,
]);
}
}
// barrel skin
for i in 0..segments {
let ring = i * (resolution + 1);
let next_ring = (i + 1) * (resolution + 1);
for j in 0..resolution {
indices.extend_from_slice(&[
ring + j,
next_ring + j,
ring + j + 1,
next_ring + j,
next_ring + j + 1,
ring + j + 1,
]);
}
}
// caps
if self.caps {
let mut build_cap = |top: bool| {
let offset = positions.len() as u32;
let (y, normal_y, winding) = if top {
(self.cylinder.half_height, 1., (1, 0))
} else {
(-self.cylinder.half_height, -1., (0, 1))
};
for i in 0..self.resolution {
let theta = i as f32 * step_theta;
let (sin, cos) = ops::sin_cos(theta);
positions.push([cos * self.cylinder.radius, y, sin * self.cylinder.radius]);
normals.push([0.0, normal_y, 0.0]);
uvs.push([0.5 * (cos + 1.0), 1.0 - 0.5 * (sin + 1.0)]);
}
for i in 1..(self.resolution - 1) {
indices.extend_from_slice(&[
offset,
offset + i + winding.0,
offset + i + winding.1,
]);
}
};
build_cap(true);
build_cap(false);
}
// Offset the vertex positions Y axis to match the anchor
match self.anchor {
CylinderAnchor::Top => positions
.iter_mut()
.for_each(|p| p[1] -= self.cylinder.half_height),
CylinderAnchor::Bottom => positions
.iter_mut()
.for_each(|p| p[1] += self.cylinder.half_height),
CylinderAnchor::MidPoint => (),
};
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(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)
}
}
impl Meshable for Cylinder {
type Output = CylinderMeshBuilder;
fn mesh(&self) -> Self::Output {
CylinderMeshBuilder {
cylinder: *self,
..Default::default()
}
}
}
impl From<Cylinder> for Mesh {
fn from(cylinder: Cylinder) -> Self {
cylinder.mesh().build()
}
}

View File

@@ -0,0 +1,21 @@
mod capsule;
mod cone;
mod conical_frustum;
mod cuboid;
mod cylinder;
mod plane;
mod sphere;
mod tetrahedron;
mod torus;
pub(crate) mod triangle3d;
pub use capsule::*;
pub use cone::*;
pub use conical_frustum::*;
pub use cuboid::*;
pub use cylinder::*;
pub use plane::*;
pub use sphere::*;
pub use tetrahedron::*;
pub use torus::*;
pub use triangle3d::*;

View File

@@ -0,0 +1,160 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{primitives::Plane3d, Dir3, Quat, Vec2, Vec3};
use bevy_reflect::prelude::*;
/// A builder used for creating a [`Mesh`] with a [`Plane3d`] shape.
#[derive(Clone, Copy, Debug, Default, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct PlaneMeshBuilder {
/// The [`Plane3d`] shape.
pub plane: Plane3d,
/// The number of subdivisions in the mesh.
///
/// 0 - is the original plane geometry, the 4 points in the XZ plane.
///
/// 1 - is split by 1 line in the middle of the plane on both the X axis and the Z axis, resulting in a plane with 4 quads / 8 triangles.
///
/// 2 - is a plane split by 2 lines on both the X and Z axes, subdividing the plane into 3 equal sections along each axis, resulting in a plane with 9 quads / 18 triangles.
///
/// and so on...
pub subdivisions: u32,
}
impl PlaneMeshBuilder {
/// Creates a new [`PlaneMeshBuilder`] from a given normal and size.
#[inline]
pub fn new(normal: Dir3, size: Vec2) -> Self {
Self {
plane: Plane3d {
normal,
half_size: size / 2.0,
},
subdivisions: 0,
}
}
/// Creates a new [`PlaneMeshBuilder`] from the given size, with the normal pointing upwards.
#[inline]
pub fn from_size(size: Vec2) -> Self {
Self {
plane: Plane3d {
half_size: size / 2.0,
..Default::default()
},
subdivisions: 0,
}
}
/// Creates a new [`PlaneMeshBuilder`] from the given length, with the normal pointing upwards,
/// and the resulting [`PlaneMeshBuilder`] being a square.
#[inline]
pub fn from_length(length: f32) -> Self {
Self {
plane: Plane3d {
half_size: Vec2::splat(length) / 2.0,
..Default::default()
},
subdivisions: 0,
}
}
/// Sets the normal of the plane, aka the direction the plane is facing.
#[inline]
#[doc(alias = "facing")]
pub fn normal(mut self, normal: Dir3) -> Self {
self.plane = Plane3d {
normal,
..self.plane
};
self
}
/// Sets the size of the plane mesh.
#[inline]
pub fn size(mut self, width: f32, height: f32) -> Self {
self.plane.half_size = Vec2::new(width, height) / 2.0;
self
}
/// Sets the subdivisions of the plane mesh.
///
/// 0 - is the original plane geometry, the 4 points in the XZ plane.
///
/// 1 - is split by 1 line in the middle of the plane on both the X axis and the Z axis,
/// resulting in a plane with 4 quads / 8 triangles.
///
/// 2 - is a plane split by 2 lines on both the X and Z axes, subdividing the plane into 3
/// equal sections along each axis, resulting in a plane with 9 quads / 18 triangles.
#[inline]
pub fn subdivisions(mut self, subdivisions: u32) -> Self {
self.subdivisions = subdivisions;
self
}
}
impl MeshBuilder for PlaneMeshBuilder {
fn build(&self) -> Mesh {
let z_vertex_count = self.subdivisions + 2;
let x_vertex_count = self.subdivisions + 2;
let num_vertices = (z_vertex_count * x_vertex_count) as usize;
let num_indices = ((z_vertex_count - 1) * (x_vertex_count - 1) * 6) as usize;
let mut positions: Vec<Vec3> = Vec::with_capacity(num_vertices);
let mut normals: Vec<[f32; 3]> = Vec::with_capacity(num_vertices);
let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(num_vertices);
let mut indices: Vec<u32> = Vec::with_capacity(num_indices);
let rotation = Quat::from_rotation_arc(Vec3::Y, *self.plane.normal);
let size = self.plane.half_size * 2.0;
for z in 0..z_vertex_count {
for x in 0..x_vertex_count {
let tx = x as f32 / (x_vertex_count - 1) as f32;
let tz = z as f32 / (z_vertex_count - 1) as f32;
let pos = rotation * Vec3::new((-0.5 + tx) * size.x, 0.0, (-0.5 + tz) * size.y);
positions.push(pos);
normals.push(self.plane.normal.to_array());
uvs.push([tx, tz]);
}
}
for z in 0..z_vertex_count - 1 {
for x in 0..x_vertex_count - 1 {
let quad = z * x_vertex_count + x;
indices.push(quad + x_vertex_count + 1);
indices.push(quad + 1);
indices.push(quad + x_vertex_count);
indices.push(quad);
indices.push(quad + x_vertex_count);
indices.push(quad + 1);
}
}
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(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)
}
}
impl Meshable for Plane3d {
type Output = PlaneMeshBuilder;
fn mesh(&self) -> Self::Output {
PlaneMeshBuilder {
plane: *self,
subdivisions: 0,
}
}
}
impl From<Plane3d> for Mesh {
fn from(plane: Plane3d) -> Self {
plane.mesh().build()
}
}

View File

@@ -0,0 +1,264 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{ops, primitives::Sphere};
use bevy_reflect::prelude::*;
use core::f32::consts::PI;
use hexasphere::shapes::IcoSphere;
use thiserror::Error;
/// An error when creating an icosphere [`Mesh`] from a [`SphereMeshBuilder`].
#[derive(Clone, Copy, Debug, Error)]
pub enum IcosphereError {
/// The icosphere has too many vertices.
#[error("Cannot create an icosphere of {subdivisions} subdivisions due to there being too many vertices being generated: {number_of_resulting_points}. (Limited to 65535 vertices or 79 subdivisions)")]
TooManyVertices {
/// The number of subdivisions used. 79 is the largest allowed value for a mesh to be generated.
subdivisions: u32,
/// The number of vertices generated. 65535 is the largest allowed value for a mesh to be generated.
number_of_resulting_points: u32,
},
}
/// A type of sphere mesh.
#[derive(Clone, Copy, Debug, Reflect)]
#[reflect(Default, Debug, Clone)]
pub enum SphereKind {
/// An icosphere, a spherical mesh that consists of similar sized triangles.
Ico {
/// The number of subdivisions applied.
/// The number of faces quadruples with each subdivision.
subdivisions: u32,
},
/// A UV sphere, a spherical mesh that consists of quadrilaterals
/// apart from triangles at the top and bottom.
Uv {
/// The number of longitudinal sectors, aka the horizontal resolution.
#[doc(alias = "horizontal_resolution")]
sectors: u32,
/// The number of latitudinal stacks, aka the vertical resolution.
#[doc(alias = "vertical_resolution")]
stacks: u32,
},
}
impl Default for SphereKind {
fn default() -> Self {
Self::Ico { subdivisions: 5 }
}
}
/// A builder used for creating a [`Mesh`] with an [`Sphere`] shape.
#[derive(Clone, Copy, Debug, Default, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct SphereMeshBuilder {
/// The [`Sphere`] shape.
pub sphere: Sphere,
/// The type of sphere mesh that will be built.
pub kind: SphereKind,
}
impl SphereMeshBuilder {
/// Creates a new [`SphereMeshBuilder`] from a radius and [`SphereKind`].
#[inline]
pub const fn new(radius: f32, kind: SphereKind) -> Self {
Self {
sphere: Sphere { radius },
kind,
}
}
/// Sets the [`SphereKind`] that will be used for building the mesh.
#[inline]
pub const fn kind(mut self, kind: SphereKind) -> Self {
self.kind = kind;
self
}
/// Creates an icosphere mesh with the given number of subdivisions.
///
/// The number of faces quadruples with each subdivision.
/// If there are `80` or more subdivisions, the vertex count will be too large,
/// and an [`IcosphereError`] is returned.
///
/// A good default is `5` subdivisions.
pub fn ico(&self, subdivisions: u32) -> Result<Mesh, IcosphereError> {
if subdivisions >= 80 {
/*
Number of triangles:
N = 20
Number of edges:
E = 30
Number of vertices:
V = 12
Number of points within a triangle (triangular numbers):
inner(s) = (s^2 + s) / 2
Number of points on an edge:
edges(s) = s
Add up all vertices on the surface:
vertices(s) = edges(s) * E + inner(s - 1) * N + V
Expand and simplify. Notice that the triangular number formula has roots at -1, and 0, so translating it one to the right fixes it.
subdivisions(s) = 30s + 20((s^2 - 2s + 1 + s - 1) / 2) + 12
subdivisions(s) = 30s + 10s^2 - 10s + 12
subdivisions(s) = 10(s^2 + 2s) + 12
Factor an (s + 1) term to simplify in terms of calculation
subdivisions(s) = 10(s + 1)^2 + 12 - 10
resulting_vertices(s) = 10(s + 1)^2 + 2
*/
let temp = subdivisions + 1;
let number_of_resulting_points = temp * temp * 10 + 2;
return Err(IcosphereError::TooManyVertices {
subdivisions,
number_of_resulting_points,
});
}
let generated = IcoSphere::new(subdivisions as usize, |point| {
let inclination = ops::acos(point.y);
let azimuth = ops::atan2(point.z, point.x);
let norm_inclination = inclination / PI;
let norm_azimuth = 0.5 - (azimuth / core::f32::consts::TAU);
[norm_azimuth, norm_inclination]
});
let raw_points = generated.raw_points();
let points = raw_points
.iter()
.map(|&p| (p * self.sphere.radius).into())
.collect::<Vec<[f32; 3]>>();
let normals = raw_points
.iter()
.copied()
.map(Into::into)
.collect::<Vec<[f32; 3]>>();
let uvs = generated.raw_data().to_owned();
let mut indices = Vec::with_capacity(generated.indices_per_main_triangle() * 20);
for i in 0..20 {
generated.get_indices(i, &mut indices);
}
let indices = Indices::U32(indices);
Ok(Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(indices)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, points)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs))
}
/// Creates a UV sphere [`Mesh`] with the given number of
/// longitudinal sectors and latitudinal stacks, aka horizontal and vertical resolution.
///
/// A good default is `32` sectors and `18` stacks.
pub fn uv(&self, sectors: u32, stacks: u32) -> Mesh {
// Largely inspired from http://www.songho.ca/opengl/gl_sphere.html
let sectors_f32 = sectors as f32;
let stacks_f32 = stacks as f32;
let length_inv = 1. / self.sphere.radius;
let sector_step = 2. * PI / sectors_f32;
let stack_step = PI / stacks_f32;
let n_vertices = (stacks * sectors) as usize;
let mut vertices: 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 mut indices: Vec<u32> = Vec::with_capacity(n_vertices * 2 * 3);
for i in 0..stacks + 1 {
let stack_angle = PI / 2. - (i as f32) * stack_step;
let xy = self.sphere.radius * ops::cos(stack_angle);
let z = self.sphere.radius * ops::sin(stack_angle);
for j in 0..sectors + 1 {
let sector_angle = (j as f32) * sector_step;
let x = xy * ops::cos(sector_angle);
let y = xy * ops::sin(sector_angle);
vertices.push([x, y, z]);
normals.push([x * length_inv, y * length_inv, z * length_inv]);
uvs.push([(j as f32) / sectors_f32, (i as f32) / stacks_f32]);
}
}
// indices
// k1--k1+1
// | / |
// | / |
// k2--k2+1
for i in 0..stacks {
let mut k1 = i * (sectors + 1);
let mut k2 = k1 + sectors + 1;
for _j in 0..sectors {
if i != 0 {
indices.push(k1);
indices.push(k2);
indices.push(k1 + 1);
}
if i != stacks - 1 {
indices.push(k1 + 1);
indices.push(k2);
indices.push(k2 + 1);
}
k1 += 1;
k2 += 1;
}
}
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(Indices::U32(indices))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
}
}
impl MeshBuilder for SphereMeshBuilder {
/// Builds a [`Mesh`] according to the configuration in `self`.
///
/// # Panics
///
/// Panics if the sphere is a [`SphereKind::Ico`] with a subdivision count
/// that is greater than or equal to `80` because there will be too many vertices.
fn build(&self) -> Mesh {
match self.kind {
SphereKind::Ico { subdivisions } => self.ico(subdivisions).unwrap(),
SphereKind::Uv { sectors, stacks } => self.uv(sectors, stacks),
}
}
}
impl Meshable for Sphere {
type Output = SphereMeshBuilder;
fn mesh(&self) -> Self::Output {
SphereMeshBuilder {
sphere: *self,
..Default::default()
}
}
}
impl From<Sphere> for Mesh {
fn from(sphere: Sphere) -> Self {
sphere.mesh().build()
}
}

View File

@@ -0,0 +1,66 @@
use super::triangle3d;
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::primitives::{Tetrahedron, Triangle3d};
use bevy_reflect::prelude::*;
/// A builder used for creating a [`Mesh`] with a [`Tetrahedron`] shape.
#[derive(Clone, Copy, Debug, Default, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct TetrahedronMeshBuilder {
tetrahedron: Tetrahedron,
}
impl MeshBuilder for TetrahedronMeshBuilder {
fn build(&self) -> Mesh {
let mut faces: Vec<_> = self.tetrahedron.faces().into();
// If the tetrahedron has negative orientation, reverse all the triangles so that
// they still face outward.
if self.tetrahedron.signed_volume().is_sign_negative() {
faces.iter_mut().for_each(Triangle3d::reverse);
}
let mut positions = vec![];
let mut normals = vec![];
let mut uvs = vec![];
// Each face is meshed as a `Triangle3d`, and we just shove the data into the
// vertex attributes sequentially.
for face in faces {
positions.extend(face.vertices);
let face_normal = triangle3d::normal_vec(&face);
normals.extend(vec![face_normal; 3]);
let face_uvs = triangle3d::uv_coords(&face);
uvs.extend(face_uvs);
}
// There are four faces and none of them share vertices.
let indices = Indices::U32(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(indices)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
}
}
impl Meshable for Tetrahedron {
type Output = TetrahedronMeshBuilder;
fn mesh(&self) -> Self::Output {
TetrahedronMeshBuilder { tetrahedron: *self }
}
}
impl From<Tetrahedron> for Mesh {
fn from(tetrahedron: Tetrahedron) -> Self {
tetrahedron.mesh().build()
}
}

View File

@@ -0,0 +1,176 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{ops, primitives::Torus, Vec3};
use bevy_reflect::prelude::*;
use core::ops::RangeInclusive;
/// A builder used for creating a [`Mesh`] with a [`Torus`] shape.
#[derive(Clone, Debug, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct TorusMeshBuilder {
/// The [`Torus`] shape.
pub torus: Torus,
/// The number of vertices used for each circular segment
/// in the ring or tube of the torus.
///
/// The default is `24`.
pub minor_resolution: usize,
/// The number of segments used for the main ring of the torus.
///
/// A resolution of `4` would make the torus appear rectangular,
/// while a resolution of `32` resembles a circular ring.
///
/// The default is `32`.
pub major_resolution: usize,
/// Optional angle range in radians, defaults to a full circle (0.0..=2 * PI)
pub angle_range: RangeInclusive<f32>,
}
impl Default for TorusMeshBuilder {
fn default() -> Self {
Self {
torus: Torus::default(),
minor_resolution: 24,
major_resolution: 32,
angle_range: (0.0..=2.0 * core::f32::consts::PI),
}
}
}
impl TorusMeshBuilder {
/// Creates a new [`TorusMeshBuilder`] from an inner and outer radius.
///
/// The inner radius is the radius of the hole, and the outer radius
/// is the radius of the entire object.
#[inline]
pub fn new(inner_radius: f32, outer_radius: f32) -> Self {
Self {
torus: Torus::new(inner_radius, outer_radius),
..Default::default()
}
}
/// Sets the number of vertices used for each circular segment
/// in the ring or tube of the torus.
#[inline]
pub const fn minor_resolution(mut self, resolution: usize) -> Self {
self.minor_resolution = resolution;
self
}
/// Sets the number of segments used for the main ring of the torus.
///
/// A resolution of `4` would make the torus appear rectangular,
/// while a resolution of `32` resembles a circular ring.
#[inline]
pub const fn major_resolution(mut self, resolution: usize) -> Self {
self.major_resolution = resolution;
self
}
/// Sets a custom angle range in radians instead of a full circle
#[inline]
pub const fn angle_range(mut self, range: RangeInclusive<f32>) -> Self {
self.angle_range = range;
self
}
}
impl MeshBuilder for TorusMeshBuilder {
fn build(&self) -> Mesh {
// code adapted from http://apparat-engine.blogspot.com/2013/04/procedural-meshes-torus.html
let n_vertices = (self.major_resolution + 1) * (self.minor_resolution + 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 start_angle = self.angle_range.start();
let end_angle = self.angle_range.end();
let segment_stride = (end_angle - start_angle) / self.major_resolution as f32;
let side_stride = 2.0 * core::f32::consts::PI / self.minor_resolution as f32;
for segment in 0..=self.major_resolution {
let theta = start_angle + segment_stride * segment as f32;
for side in 0..=self.minor_resolution {
let phi = side_stride * side as f32;
let (sin_theta, cos_theta) = ops::sin_cos(theta);
let (sin_phi, cos_phi) = ops::sin_cos(phi);
let radius = self.torus.major_radius + self.torus.minor_radius * cos_phi;
let position = Vec3::new(
cos_theta * radius,
self.torus.minor_radius * sin_phi,
sin_theta * radius,
);
let center = Vec3::new(
self.torus.major_radius * cos_theta,
0.,
self.torus.major_radius * sin_theta,
);
let normal = (position - center).normalize();
positions.push(position.into());
normals.push(normal.into());
uvs.push([
segment as f32 / self.major_resolution as f32,
side as f32 / self.minor_resolution as f32,
]);
}
}
let n_faces = (self.major_resolution) * (self.minor_resolution);
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 = self.minor_resolution + 1;
for segment in 0..self.major_resolution {
for side in 0..self.minor_resolution {
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,
RenderAssetUsages::default(),
)
.with_inserted_indices(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)
}
}
impl Meshable for Torus {
type Output = TorusMeshBuilder;
fn mesh(&self) -> Self::Output {
TorusMeshBuilder {
torus: *self,
..Default::default()
}
}
}
impl From<Torus> for Mesh {
fn from(torus: Torus) -> Self {
torus.mesh().build()
}
}

View File

@@ -0,0 +1,130 @@
use crate::{Indices, Mesh, MeshBuilder, Meshable, PrimitiveTopology};
use bevy_asset::RenderAssetUsages;
use bevy_math::{primitives::Triangle3d, Vec3};
use bevy_reflect::prelude::*;
/// A builder used for creating a [`Mesh`] with a [`Triangle3d`] shape.
#[derive(Clone, Copy, Debug, Default, Reflect)]
#[reflect(Default, Debug, Clone)]
pub struct Triangle3dMeshBuilder {
triangle: Triangle3d,
}
impl MeshBuilder for Triangle3dMeshBuilder {
fn build(&self) -> Mesh {
let positions: Vec<_> = self.triangle.vertices.into();
let uvs: Vec<_> = uv_coords(&self.triangle).into();
// Every vertex has the normal of the face of the triangle (or zero if the triangle is degenerate).
let normal: Vec3 = normal_vec(&self.triangle);
let normals = vec![normal; 3];
let indices = Indices::U32(vec![0, 1, 2]);
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_indices(indices)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
}
}
impl Meshable for Triangle3d {
type Output = Triangle3dMeshBuilder;
fn mesh(&self) -> Self::Output {
Triangle3dMeshBuilder { triangle: *self }
}
}
/// The normal of a [`Triangle3d`] with zeroing so that a [`Vec3`] is always obtained for meshing.
#[inline]
pub(crate) fn normal_vec(triangle: &Triangle3d) -> Vec3 {
triangle.normal().map_or(Vec3::ZERO, Into::into)
}
/// Unskewed uv-coordinates for a [`Triangle3d`].
#[inline]
pub(crate) fn uv_coords(triangle: &Triangle3d) -> [[f32; 2]; 3] {
let [a, b, c] = triangle.vertices;
let main_length = a.distance(b);
let Some(x) = (b - a).try_normalize() else {
return [[0., 0.], [1., 0.], [0., 1.]];
};
let y = c - a;
// `x` corresponds to one of the axes in uv-coordinates;
// to uv-map the triangle without skewing, we use the orthogonalization
// of `y` with respect to `x` as the second direction and construct a rectangle that
// contains `triangle`.
let y_proj = y.project_onto_normalized(x);
// `offset` represents the x-coordinate of the point `c`; note that x has been shrunk by a
// factor of `main_length`, so `offset` follows it.
let offset = y_proj.dot(x) / main_length;
// Obtuse triangle leaning to the left => x direction extends to the left, shifting a from 0.
if offset < 0. {
let total_length = 1. - offset;
let a_uv = [offset.abs() / total_length, 0.];
let b_uv = [1., 0.];
let c_uv = [0., 1.];
[a_uv, b_uv, c_uv]
}
// Obtuse triangle leaning to the right => x direction extends to the right, shifting b from 1.
else if offset > 1. {
let a_uv = [0., 0.];
let b_uv = [1. / offset, 0.];
let c_uv = [1., 1.];
[a_uv, b_uv, c_uv]
}
// Acute triangle => no extending necessary; a remains at 0 and b remains at 1.
else {
let a_uv = [0., 0.];
let b_uv = [1., 0.];
let c_uv = [offset, 1.];
[a_uv, b_uv, c_uv]
}
}
impl From<Triangle3d> for Mesh {
fn from(triangle: Triangle3d) -> Self {
triangle.mesh().build()
}
}
#[cfg(test)]
mod tests {
use super::uv_coords;
use bevy_math::primitives::Triangle3d;
#[test]
fn uv_test() {
use bevy_math::vec3;
let mut triangle = Triangle3d::new(vec3(0., 0., 0.), vec3(2., 0., 0.), vec3(-1., 1., 0.));
let [a_uv, b_uv, c_uv] = uv_coords(&triangle);
assert_eq!(a_uv, [1. / 3., 0.]);
assert_eq!(b_uv, [1., 0.]);
assert_eq!(c_uv, [0., 1.]);
triangle.vertices[2] = vec3(3., 1., 0.);
let [a_uv, b_uv, c_uv] = uv_coords(&triangle);
assert_eq!(a_uv, [0., 0.]);
assert_eq!(b_uv, [2. / 3., 0.]);
assert_eq!(c_uv, [1., 1.]);
triangle.vertices[2] = vec3(2., 1., 0.);
let [a_uv, b_uv, c_uv] = uv_coords(&triangle);
assert_eq!(a_uv, [0., 0.]);
assert_eq!(b_uv, [1., 0.]);
assert_eq!(c_uv, [1., 1.]);
}
}

View File

@@ -0,0 +1,425 @@
use bevy_math::{
primitives::{Annulus, Capsule2d, Circle, Ellipse, Extrusion, Primitive2d},
Vec2, Vec3,
};
use super::{MeshBuilder, Meshable};
use crate::{Indices, Mesh, PrimitiveTopology, VertexAttributeValues};
/// A type representing a segment of the perimeter of an extrudable mesh.
pub enum PerimeterSegment {
/// This segment of the perimeter will be shaded smooth.
///
/// This has the effect of rendering the segment's faces with softened edges, so it is appropriate for curved shapes.
///
/// The normals for the vertices that are part of this segment will be calculated based on the positions of their neighbors.
/// Each normal is interpolated between the normals of the two line segments connecting it with its neighbors.
/// Closer vertices have a stronger effect on the normal than more distant ones.
///
/// Since the vertices corresponding to the first and last indices do not have two neighboring vertices, their normals must be provided manually.
Smooth {
/// The normal of the first vertex.
first_normal: Vec2,
/// The normal of the last vertex.
last_normal: Vec2,
/// A list of indices representing this segment of the perimeter of the mesh.
///
/// The indices must be ordered such that the *outside* of the mesh is to the right
/// when walking along the vertices of the mesh in the order provided by the indices.
///
/// For geometry to be rendered, you must provide at least two indices.
indices: Vec<u32>,
},
/// This segment of the perimeter will be shaded flat.
///
/// This has the effect of rendering the segment's faces with hard edges.
Flat {
/// A list of indices representing this segment of the perimeter of the mesh.
///
/// The indices must be ordered such that the *outside* of the mesh is to the right
/// when walking along the vertices of the mesh in the order provided by indices.
///
/// For geometry to be rendered, you must provide at least two indices.
indices: Vec<u32>,
},
}
impl PerimeterSegment {
/// Returns the amount of vertices each 'layer' of the extrusion should include for this perimeter segment.
///
/// A layer is the set of vertices sharing a common Z value or depth.
fn vertices_per_layer(&self) -> u32 {
match self {
PerimeterSegment::Smooth { indices, .. } => indices.len() as u32,
PerimeterSegment::Flat { indices } => 2 * (indices.len() as u32 - 1),
}
}
/// Returns the amount of indices each 'segment' of the extrusion should include for this perimeter segment.
///
/// A segment is the set of faces on the mantel of the extrusion between two layers of vertices.
fn indices_per_segment(&self) -> usize {
match self {
PerimeterSegment::Smooth { indices, .. } | PerimeterSegment::Flat { indices } => {
6 * (indices.len() - 1)
}
}
}
}
/// A trait required for implementing `Meshable` for `Extrusion<T>`.
///
/// ## Warning
///
/// By implementing this trait you guarantee that the `primitive_topology` of the mesh returned by
/// this builder is [`PrimitiveTopology::TriangleList`]
/// and that your mesh has a [`Mesh::ATTRIBUTE_POSITION`] attribute.
pub trait Extrudable: MeshBuilder {
/// A list of the indices each representing a part of the perimeter of the mesh.
fn perimeter(&self) -> Vec<PerimeterSegment>;
}
impl<P> Meshable for Extrusion<P>
where
P: Primitive2d + Meshable,
P::Output: Extrudable,
{
type Output = ExtrusionBuilder<P>;
fn mesh(&self) -> Self::Output {
ExtrusionBuilder {
base_builder: self.base_shape.mesh(),
half_depth: self.half_depth,
segments: 1,
}
}
}
/// A builder used for creating a [`Mesh`] with an [`Extrusion`] shape.
pub struct ExtrusionBuilder<P>
where
P: Primitive2d + Meshable,
P::Output: Extrudable,
{
pub base_builder: P::Output,
pub half_depth: f32,
pub segments: usize,
}
impl<P> ExtrusionBuilder<P>
where
P: Primitive2d + Meshable,
P::Output: Extrudable,
{
/// Create a new `ExtrusionBuilder<P>` from a given `base_shape` and the full `depth` of the extrusion.
pub fn new(base_shape: &P, depth: f32) -> Self {
Self {
base_builder: base_shape.mesh(),
half_depth: depth / 2.,
segments: 1,
}
}
/// Sets the number of segments along the depth of the extrusion.
/// Must be greater than `0` for the geometry of the mantel to be generated.
pub fn segments(mut self, segments: usize) -> Self {
self.segments = segments;
self
}
}
impl ExtrusionBuilder<Circle> {
/// Sets the number of vertices used for the circle mesh at each end of the extrusion.
pub fn resolution(mut self, resolution: u32) -> Self {
self.base_builder.resolution = resolution;
self
}
}
impl ExtrusionBuilder<Ellipse> {
/// Sets the number of vertices used for the ellipse mesh at each end of the extrusion.
pub fn resolution(mut self, resolution: u32) -> Self {
self.base_builder.resolution = resolution;
self
}
}
impl ExtrusionBuilder<Annulus> {
/// Sets the number of vertices used in constructing the concentric circles of the annulus mesh at each end of the extrusion.
pub fn resolution(mut self, resolution: u32) -> Self {
self.base_builder.resolution = resolution;
self
}
}
impl ExtrusionBuilder<Capsule2d> {
/// Sets the number of vertices used for each hemicircle at the ends of the extrusion.
pub fn resolution(mut self, resolution: u32) -> Self {
self.base_builder.resolution = resolution;
self
}
}
impl<P> MeshBuilder for ExtrusionBuilder<P>
where
P: Primitive2d + Meshable,
P::Output: Extrudable,
{
fn build(&self) -> Mesh {
// Create and move the base mesh to the front
let mut front_face =
self.base_builder
.build()
.translated_by(Vec3::new(0., 0., self.half_depth));
// Move the uvs of the front face to be between (0., 0.) and (0.5, 0.5)
if let Some(VertexAttributeValues::Float32x2(uvs)) =
front_face.attribute_mut(Mesh::ATTRIBUTE_UV_0)
{
for uv in uvs {
*uv = uv.map(|coord| coord * 0.5);
}
}
let back_face = {
let topology = front_face.primitive_topology();
// Flip the normals, etc. and move mesh to the back
let mut back_face = front_face.clone().scaled_by(Vec3::new(1., 1., -1.));
// Move the uvs of the back face to be between (0.5, 0.) and (1., 0.5)
if let Some(VertexAttributeValues::Float32x2(uvs)) =
back_face.attribute_mut(Mesh::ATTRIBUTE_UV_0)
{
for uv in uvs {
*uv = [uv[0] + 0.5, uv[1]];
}
}
// By swapping the first and second indices of each triangle we invert the winding order thus making the mesh visible from the other side
if let Some(indices) = back_face.indices_mut() {
match topology {
PrimitiveTopology::TriangleList => match indices {
Indices::U16(indices) => {
indices.chunks_exact_mut(3).for_each(|arr| arr.swap(1, 0));
}
Indices::U32(indices) => {
indices.chunks_exact_mut(3).for_each(|arr| arr.swap(1, 0));
}
},
_ => {
panic!("Meshes used with Extrusions must have a primitive topology of `PrimitiveTopology::TriangleList`");
}
};
}
back_face
};
// An extrusion of depth 0 does not need a mantel
if self.half_depth == 0. {
front_face.merge(&back_face).unwrap();
return front_face;
}
let mantel = {
let Some(VertexAttributeValues::Float32x3(cap_verts)) =
front_face.attribute(Mesh::ATTRIBUTE_POSITION)
else {
panic!("The base mesh did not have vertex positions");
};
debug_assert!(self.segments > 0);
let layers = self.segments + 1;
let layer_depth_delta = self.half_depth * 2.0 / self.segments as f32;
let perimeter = self.base_builder.perimeter();
let (vert_count, index_count) =
perimeter
.iter()
.fold((0, 0), |(verts, indices), perimeter| {
(
verts + layers * perimeter.vertices_per_layer() as usize,
indices + self.segments * perimeter.indices_per_segment(),
)
});
let mut positions = Vec::with_capacity(vert_count);
let mut normals = Vec::with_capacity(vert_count);
let mut indices = Vec::with_capacity(index_count);
let mut uvs = Vec::with_capacity(vert_count);
// Compute the amount of horizontal space allocated to each segment of the perimeter.
let uv_segment_delta = 1. / perimeter.len() as f32;
for (i, segment) in perimeter.into_iter().enumerate() {
// The start of the x range of the area of the current perimeter-segment.
let uv_start = i as f32 * uv_segment_delta;
match segment {
PerimeterSegment::Flat {
indices: segment_indices,
} => {
let uv_delta = uv_segment_delta / (segment_indices.len() - 1) as f32;
for i in 0..(segment_indices.len() - 1) {
let uv_x = uv_start + uv_delta * i as f32;
// Get the positions for the current and the next index.
let a = cap_verts[segment_indices[i] as usize];
let b = cap_verts[segment_indices[i + 1] as usize];
// Get the index of the next vertex added to the mantel.
let index = positions.len() as u32;
// Push the positions of the two indices and their equivalent points on each layer.
for i in 0..layers {
let i = i as f32;
let z = a[2] - layer_depth_delta * i;
positions.push([a[0], a[1], z]);
positions.push([b[0], b[1], z]);
// UVs for the mantel are between (0, 0.5) and (1, 1).
let uv_y = 0.5 + 0.5 * i / self.segments as f32;
uvs.push([uv_x, uv_y]);
uvs.push([uv_x + uv_delta, uv_y]);
}
// The normal is calculated to be the normal of the line segment connecting a and b.
let n = Vec3::from_array([b[1] - a[1], a[0] - b[0], 0.])
.normalize_or_zero()
.to_array();
normals.extend_from_slice(&vec![n; 2 * layers]);
// Add the indices for the vertices created above to the mesh.
for i in 0..self.segments as u32 {
let base_index = index + 2 * i;
indices.extend_from_slice(&[
base_index,
base_index + 2,
base_index + 1,
base_index + 1,
base_index + 2,
base_index + 3,
]);
}
}
}
PerimeterSegment::Smooth {
first_normal,
last_normal,
indices: segment_indices,
} => {
let uv_delta = uv_segment_delta / (segment_indices.len() - 1) as f32;
// Since the indices for this segment will be added after its vertices have been added,
// we need to store the index of the first vertex that is part of this segment.
let base_index = positions.len() as u32;
// If there is a first vertex, we need to add it and its counterparts on each layer.
// The normal is provided by `segment.first_normal`.
if let Some(i) = segment_indices.first() {
let p = cap_verts[*i as usize];
for i in 0..layers {
let i = i as f32;
let z = p[2] - layer_depth_delta * i;
positions.push([p[0], p[1], z]);
let uv_y = 0.5 + 0.5 * i / self.segments as f32;
uvs.push([uv_start, uv_y]);
}
normals.extend_from_slice(&vec![
first_normal.extend(0.).to_array();
layers
]);
}
// For all points inbetween the first and last vertices, we can automatically compute the normals.
for i in 1..(segment_indices.len() - 1) {
let uv_x = uv_start + uv_delta * i as f32;
// Get the positions for the last, current and the next index.
let a = cap_verts[segment_indices[i - 1] as usize];
let b = cap_verts[segment_indices[i] as usize];
let c = cap_verts[segment_indices[i + 1] as usize];
// Add the current vertex and its counterparts on each layer.
for i in 0..layers {
let i = i as f32;
let z = b[2] - layer_depth_delta * i;
positions.push([b[0], b[1], z]);
let uv_y = 0.5 + 0.5 * i / self.segments as f32;
uvs.push([uv_x, uv_y]);
}
// The normal for the current vertices can be calculated based on the two neighboring vertices.
// The normal is interpolated between the normals of the two line segments connecting the current vertex with its neighbors.
// Closer vertices have a stronger effect on the normal than more distant ones.
let n = {
let ab = Vec2::from_slice(&b) - Vec2::from_slice(&a);
let bc = Vec2::from_slice(&c) - Vec2::from_slice(&b);
let n = ab.normalize_or_zero() + bc.normalize_or_zero();
Vec2::new(n.y, -n.x)
.normalize_or_zero()
.extend(0.)
.to_array()
};
normals.extend_from_slice(&vec![n; layers]);
}
// If there is a last vertex, we need to add it and its counterparts on each layer.
// The normal is provided by `segment.last_normal`.
if let Some(i) = segment_indices.last() {
let p = cap_verts[*i as usize];
for i in 0..layers {
let i = i as f32;
let z = p[2] - layer_depth_delta * i;
positions.push([p[0], p[1], z]);
let uv_y = 0.5 + 0.5 * i / self.segments as f32;
uvs.push([uv_start + uv_segment_delta, uv_y]);
}
normals.extend_from_slice(&vec![
last_normal.extend(0.).to_array();
layers
]);
}
let columns = segment_indices.len() as u32;
let segments = self.segments as u32;
let layers = segments + 1;
for s in 0..segments {
for column in 0..(columns - 1) {
let index = base_index + s + column * layers;
indices.extend_from_slice(&[
index,
index + 1,
index + layers,
index + layers,
index + 1,
index + layers + 1,
]);
}
}
}
}
}
Mesh::new(PrimitiveTopology::TriangleList, front_face.asset_usage)
.with_inserted_indices(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)
};
front_face.merge(&back_face).unwrap();
front_face.merge(&mantel).unwrap();
front_face
}
}
impl<P> From<Extrusion<P>> for Mesh
where
P: Primitive2d + Meshable,
P::Output: Extrudable,
{
fn from(value: Extrusion<P>) -> Self {
value.mesh().build()
}
}

52
vendor/bevy_mesh/src/primitives/mod.rs vendored Normal file
View File

@@ -0,0 +1,52 @@
//! Mesh generation for [primitive shapes](bevy_math::primitives).
//!
//! Primitives that support meshing implement the [`Meshable`] trait.
//! Calling [`mesh`](Meshable::mesh) will return either a [`Mesh`] or a builder
//! that can be used to specify shape-specific configuration for creating the [`Mesh`].
//!
//! ```
//! # use bevy_asset::Assets;
//! # use bevy_ecs::prelude::ResMut;
//! # use bevy_math::prelude::Circle;
//! # use bevy_mesh::*;
//! #
//! # fn setup(mut meshes: ResMut<Assets<Mesh>>) {
//! // Create circle mesh with default configuration
//! let circle = meshes.add(Circle { radius: 25.0 });
//!
//! // Specify number of vertices
//! let circle = meshes.add(Circle { radius: 25.0 }.mesh().resolution(64));
//! # }
//! ```
mod dim2;
pub use dim2::*;
mod dim3;
pub use dim3::*;
mod extrusion;
pub use extrusion::*;
use super::Mesh;
/// A trait for shapes that can be turned into a [`Mesh`].
pub trait Meshable {
/// The output of [`Self::mesh`]. This will be a [`MeshBuilder`] used for creating a [`Mesh`].
type Output: MeshBuilder;
/// Creates a [`Mesh`] for a shape.
fn mesh(&self) -> Self::Output;
}
/// A trait used to build [`Mesh`]es from a configuration
pub trait MeshBuilder {
/// Builds a [`Mesh`] based on the configuration in `self`.
fn build(&self) -> Mesh;
}
impl<T: MeshBuilder> From<T> for Mesh {
fn from(builder: T) -> Self {
builder.build()
}
}

39
vendor/bevy_mesh/src/skinning.rs vendored Normal file
View File

@@ -0,0 +1,39 @@
use bevy_asset::{AsAssetId, Asset, AssetId, Handle};
use bevy_ecs::{component::Component, entity::Entity, prelude::ReflectComponent};
use bevy_math::Mat4;
use bevy_reflect::prelude::*;
use core::ops::Deref;
#[derive(Component, Debug, Default, Clone, Reflect)]
#[reflect(Component, Default, Debug, Clone)]
pub struct SkinnedMesh {
pub inverse_bindposes: Handle<SkinnedMeshInverseBindposes>,
#[entities]
pub joints: Vec<Entity>,
}
impl AsAssetId for SkinnedMesh {
type Asset = SkinnedMeshInverseBindposes;
// We implement this so that `AssetChanged` will work to pick up any changes
// to `SkinnedMeshInverseBindposes`.
fn as_asset_id(&self) -> AssetId<Self::Asset> {
self.inverse_bindposes.id()
}
}
#[derive(Asset, TypePath, Debug)]
pub struct SkinnedMeshInverseBindposes(Box<[Mat4]>);
impl From<Vec<Mat4>> for SkinnedMeshInverseBindposes {
fn from(value: Vec<Mat4>) -> Self {
Self(value.into_boxed_slice())
}
}
impl Deref for SkinnedMeshInverseBindposes {
type Target = [Mat4];
fn deref(&self) -> &Self::Target {
&self.0
}
}

426
vendor/bevy_mesh/src/vertex.rs vendored Normal file
View File

@@ -0,0 +1,426 @@
use alloc::sync::Arc;
use bevy_derive::EnumVariantMeta;
use bevy_ecs::resource::Resource;
use bevy_math::Vec3;
use bevy_platform::collections::HashSet;
use bytemuck::cast_slice;
use core::hash::{Hash, Hasher};
use thiserror::Error;
use wgpu_types::{BufferAddress, VertexAttribute, VertexFormat, VertexStepMode};
#[derive(Debug, Clone, Copy)]
pub struct MeshVertexAttribute {
/// The friendly name of the vertex attribute
pub name: &'static str,
/// The _unique_ id of the vertex attribute. This will also determine sort ordering
/// when generating vertex buffers. Built-in / standard attributes will use "close to zero"
/// indices. When in doubt, use a random / very large u64 to avoid conflicts.
pub id: MeshVertexAttributeId,
/// The format of the vertex attribute.
pub format: VertexFormat,
}
impl MeshVertexAttribute {
pub const fn new(name: &'static str, id: u64, format: VertexFormat) -> Self {
Self {
name,
id: MeshVertexAttributeId(id),
format,
}
}
pub const fn at_shader_location(&self, shader_location: u32) -> VertexAttributeDescriptor {
VertexAttributeDescriptor::new(shader_location, self.id, self.name)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct MeshVertexAttributeId(u64);
impl From<MeshVertexAttribute> for MeshVertexAttributeId {
fn from(attribute: MeshVertexAttribute) -> Self {
attribute.id
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct MeshVertexBufferLayout {
pub(crate) attribute_ids: Vec<MeshVertexAttributeId>,
pub(crate) layout: VertexBufferLayout,
}
impl MeshVertexBufferLayout {
pub fn new(attribute_ids: Vec<MeshVertexAttributeId>, layout: VertexBufferLayout) -> Self {
Self {
attribute_ids,
layout,
}
}
#[inline]
pub fn contains(&self, attribute_id: impl Into<MeshVertexAttributeId>) -> bool {
self.attribute_ids.contains(&attribute_id.into())
}
#[inline]
pub fn attribute_ids(&self) -> &[MeshVertexAttributeId] {
&self.attribute_ids
}
#[inline]
pub fn layout(&self) -> &VertexBufferLayout {
&self.layout
}
pub fn get_layout(
&self,
attribute_descriptors: &[VertexAttributeDescriptor],
) -> Result<VertexBufferLayout, MissingVertexAttributeError> {
let mut attributes = Vec::with_capacity(attribute_descriptors.len());
for attribute_descriptor in attribute_descriptors {
if let Some(index) = self
.attribute_ids
.iter()
.position(|id| *id == attribute_descriptor.id)
{
let layout_attribute = &self.layout.attributes[index];
attributes.push(VertexAttribute {
format: layout_attribute.format,
offset: layout_attribute.offset,
shader_location: attribute_descriptor.shader_location,
});
} else {
return Err(MissingVertexAttributeError {
id: attribute_descriptor.id,
name: attribute_descriptor.name,
pipeline_type: None,
});
}
}
Ok(VertexBufferLayout {
array_stride: self.layout.array_stride,
step_mode: self.layout.step_mode,
attributes,
})
}
}
#[derive(Error, Debug)]
#[error("Mesh is missing requested attribute: {name} ({id:?}, pipeline type: {pipeline_type:?})")]
pub struct MissingVertexAttributeError {
pub pipeline_type: Option<&'static str>,
id: MeshVertexAttributeId,
name: &'static str,
}
pub struct VertexAttributeDescriptor {
pub shader_location: u32,
pub id: MeshVertexAttributeId,
name: &'static str,
}
impl VertexAttributeDescriptor {
pub const fn new(shader_location: u32, id: MeshVertexAttributeId, name: &'static str) -> Self {
Self {
shader_location,
id,
name,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct MeshAttributeData {
pub(crate) attribute: MeshVertexAttribute,
pub(crate) values: VertexAttributeValues,
}
/// Compute a vector whose direction is the normal of the triangle formed by
/// points a, b, c, and whose magnitude is double the area of the triangle. This
/// is useful for computing smooth normals where the contributing normals are
/// proportionate to the areas of the triangles as [discussed
/// here](https://iquilezles.org/articles/normals/).
///
/// Question: Why double the area? Because the area of a triangle _A_ is
/// determined by this equation:
///
/// _A = |(b - a) x (c - a)| / 2_
///
/// By computing _2 A_ we avoid a division operation, and when calculating the
/// the sum of these vectors which are then normalized, a constant multiple has
/// no effect.
#[inline]
pub fn face_area_normal(a: [f32; 3], b: [f32; 3], c: [f32; 3]) -> [f32; 3] {
let (a, b, c) = (Vec3::from(a), Vec3::from(b), Vec3::from(c));
(b - a).cross(c - a).into()
}
/// Compute the normal of a face made of three points: a, b, and c.
#[inline]
pub fn face_normal(a: [f32; 3], b: [f32; 3], c: [f32; 3]) -> [f32; 3] {
let (a, b, c) = (Vec3::from(a), Vec3::from(b), Vec3::from(c));
(b - a).cross(c - a).normalize().into()
}
/// Contains an array where each entry describes a property of a single vertex.
/// Matches the [`VertexFormats`](VertexFormat).
#[derive(Clone, Debug, EnumVariantMeta)]
pub enum VertexAttributeValues {
Float32(Vec<f32>),
Sint32(Vec<i32>),
Uint32(Vec<u32>),
Float32x2(Vec<[f32; 2]>),
Sint32x2(Vec<[i32; 2]>),
Uint32x2(Vec<[u32; 2]>),
Float32x3(Vec<[f32; 3]>),
Sint32x3(Vec<[i32; 3]>),
Uint32x3(Vec<[u32; 3]>),
Float32x4(Vec<[f32; 4]>),
Sint32x4(Vec<[i32; 4]>),
Uint32x4(Vec<[u32; 4]>),
Sint16x2(Vec<[i16; 2]>),
Snorm16x2(Vec<[i16; 2]>),
Uint16x2(Vec<[u16; 2]>),
Unorm16x2(Vec<[u16; 2]>),
Sint16x4(Vec<[i16; 4]>),
Snorm16x4(Vec<[i16; 4]>),
Uint16x4(Vec<[u16; 4]>),
Unorm16x4(Vec<[u16; 4]>),
Sint8x2(Vec<[i8; 2]>),
Snorm8x2(Vec<[i8; 2]>),
Uint8x2(Vec<[u8; 2]>),
Unorm8x2(Vec<[u8; 2]>),
Sint8x4(Vec<[i8; 4]>),
Snorm8x4(Vec<[i8; 4]>),
Uint8x4(Vec<[u8; 4]>),
Unorm8x4(Vec<[u8; 4]>),
}
impl VertexAttributeValues {
/// Returns the number of vertices in this [`VertexAttributeValues`]. For a single
/// mesh, all of the [`VertexAttributeValues`] must have the same length.
#[expect(
clippy::match_same_arms,
reason = "Although the `values` binding on some match arms may have matching types, each variant has different semantics; thus it's not guaranteed that they will use the same type forever."
)]
pub fn len(&self) -> usize {
match self {
VertexAttributeValues::Float32(values) => values.len(),
VertexAttributeValues::Sint32(values) => values.len(),
VertexAttributeValues::Uint32(values) => values.len(),
VertexAttributeValues::Float32x2(values) => values.len(),
VertexAttributeValues::Sint32x2(values) => values.len(),
VertexAttributeValues::Uint32x2(values) => values.len(),
VertexAttributeValues::Float32x3(values) => values.len(),
VertexAttributeValues::Sint32x3(values) => values.len(),
VertexAttributeValues::Uint32x3(values) => values.len(),
VertexAttributeValues::Float32x4(values) => values.len(),
VertexAttributeValues::Sint32x4(values) => values.len(),
VertexAttributeValues::Uint32x4(values) => values.len(),
VertexAttributeValues::Sint16x2(values) => values.len(),
VertexAttributeValues::Snorm16x2(values) => values.len(),
VertexAttributeValues::Uint16x2(values) => values.len(),
VertexAttributeValues::Unorm16x2(values) => values.len(),
VertexAttributeValues::Sint16x4(values) => values.len(),
VertexAttributeValues::Snorm16x4(values) => values.len(),
VertexAttributeValues::Uint16x4(values) => values.len(),
VertexAttributeValues::Unorm16x4(values) => values.len(),
VertexAttributeValues::Sint8x2(values) => values.len(),
VertexAttributeValues::Snorm8x2(values) => values.len(),
VertexAttributeValues::Uint8x2(values) => values.len(),
VertexAttributeValues::Unorm8x2(values) => values.len(),
VertexAttributeValues::Sint8x4(values) => values.len(),
VertexAttributeValues::Snorm8x4(values) => values.len(),
VertexAttributeValues::Uint8x4(values) => values.len(),
VertexAttributeValues::Unorm8x4(values) => values.len(),
}
}
/// Returns `true` if there are no vertices in this [`VertexAttributeValues`].
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns the values as float triples if possible.
pub fn as_float3(&self) -> Option<&[[f32; 3]]> {
match self {
VertexAttributeValues::Float32x3(values) => Some(values),
_ => None,
}
}
// TODO: add vertex format as parameter here and perform type conversions
/// Flattens the [`VertexAttributeValues`] into a sequence of bytes. This is
/// useful for serialization and sending to the GPU.
#[expect(
clippy::match_same_arms,
reason = "Although the `values` binding on some match arms may have matching types, each variant has different semantics; thus it's not guaranteed that they will use the same type forever."
)]
pub fn get_bytes(&self) -> &[u8] {
match self {
VertexAttributeValues::Float32(values) => cast_slice(values),
VertexAttributeValues::Sint32(values) => cast_slice(values),
VertexAttributeValues::Uint32(values) => cast_slice(values),
VertexAttributeValues::Float32x2(values) => cast_slice(values),
VertexAttributeValues::Sint32x2(values) => cast_slice(values),
VertexAttributeValues::Uint32x2(values) => cast_slice(values),
VertexAttributeValues::Float32x3(values) => cast_slice(values),
VertexAttributeValues::Sint32x3(values) => cast_slice(values),
VertexAttributeValues::Uint32x3(values) => cast_slice(values),
VertexAttributeValues::Float32x4(values) => cast_slice(values),
VertexAttributeValues::Sint32x4(values) => cast_slice(values),
VertexAttributeValues::Uint32x4(values) => cast_slice(values),
VertexAttributeValues::Sint16x2(values) => cast_slice(values),
VertexAttributeValues::Snorm16x2(values) => cast_slice(values),
VertexAttributeValues::Uint16x2(values) => cast_slice(values),
VertexAttributeValues::Unorm16x2(values) => cast_slice(values),
VertexAttributeValues::Sint16x4(values) => cast_slice(values),
VertexAttributeValues::Snorm16x4(values) => cast_slice(values),
VertexAttributeValues::Uint16x4(values) => cast_slice(values),
VertexAttributeValues::Unorm16x4(values) => cast_slice(values),
VertexAttributeValues::Sint8x2(values) => cast_slice(values),
VertexAttributeValues::Snorm8x2(values) => cast_slice(values),
VertexAttributeValues::Uint8x2(values) => cast_slice(values),
VertexAttributeValues::Unorm8x2(values) => cast_slice(values),
VertexAttributeValues::Sint8x4(values) => cast_slice(values),
VertexAttributeValues::Snorm8x4(values) => cast_slice(values),
VertexAttributeValues::Uint8x4(values) => cast_slice(values),
VertexAttributeValues::Unorm8x4(values) => cast_slice(values),
}
}
}
impl From<&VertexAttributeValues> for VertexFormat {
fn from(values: &VertexAttributeValues) -> Self {
match values {
VertexAttributeValues::Float32(_) => VertexFormat::Float32,
VertexAttributeValues::Sint32(_) => VertexFormat::Sint32,
VertexAttributeValues::Uint32(_) => VertexFormat::Uint32,
VertexAttributeValues::Float32x2(_) => VertexFormat::Float32x2,
VertexAttributeValues::Sint32x2(_) => VertexFormat::Sint32x2,
VertexAttributeValues::Uint32x2(_) => VertexFormat::Uint32x2,
VertexAttributeValues::Float32x3(_) => VertexFormat::Float32x3,
VertexAttributeValues::Sint32x3(_) => VertexFormat::Sint32x3,
VertexAttributeValues::Uint32x3(_) => VertexFormat::Uint32x3,
VertexAttributeValues::Float32x4(_) => VertexFormat::Float32x4,
VertexAttributeValues::Sint32x4(_) => VertexFormat::Sint32x4,
VertexAttributeValues::Uint32x4(_) => VertexFormat::Uint32x4,
VertexAttributeValues::Sint16x2(_) => VertexFormat::Sint16x2,
VertexAttributeValues::Snorm16x2(_) => VertexFormat::Snorm16x2,
VertexAttributeValues::Uint16x2(_) => VertexFormat::Uint16x2,
VertexAttributeValues::Unorm16x2(_) => VertexFormat::Unorm16x2,
VertexAttributeValues::Sint16x4(_) => VertexFormat::Sint16x4,
VertexAttributeValues::Snorm16x4(_) => VertexFormat::Snorm16x4,
VertexAttributeValues::Uint16x4(_) => VertexFormat::Uint16x4,
VertexAttributeValues::Unorm16x4(_) => VertexFormat::Unorm16x4,
VertexAttributeValues::Sint8x2(_) => VertexFormat::Sint8x2,
VertexAttributeValues::Snorm8x2(_) => VertexFormat::Snorm8x2,
VertexAttributeValues::Uint8x2(_) => VertexFormat::Uint8x2,
VertexAttributeValues::Unorm8x2(_) => VertexFormat::Unorm8x2,
VertexAttributeValues::Sint8x4(_) => VertexFormat::Sint8x4,
VertexAttributeValues::Snorm8x4(_) => VertexFormat::Snorm8x4,
VertexAttributeValues::Uint8x4(_) => VertexFormat::Uint8x4,
VertexAttributeValues::Unorm8x4(_) => VertexFormat::Unorm8x4,
}
}
}
/// Describes how the vertex buffer is interpreted.
#[derive(Default, Clone, Debug, Hash, Eq, PartialEq)]
pub struct VertexBufferLayout {
/// The stride, in bytes, between elements of this buffer.
pub array_stride: BufferAddress,
/// How often this vertex buffer is "stepped" forward.
pub step_mode: VertexStepMode,
/// The list of attributes which comprise a single vertex.
pub attributes: Vec<VertexAttribute>,
}
impl VertexBufferLayout {
/// Creates a new densely packed [`VertexBufferLayout`] from an iterator of vertex formats.
/// Iteration order determines the `shader_location` and `offset` of the [`VertexAttributes`](VertexAttribute).
/// The first iterated item will have a `shader_location` and `offset` of zero.
/// The `array_stride` is the sum of the size of the iterated [`VertexFormats`](VertexFormat) (in bytes).
pub fn from_vertex_formats<T: IntoIterator<Item = VertexFormat>>(
step_mode: VertexStepMode,
vertex_formats: T,
) -> Self {
let mut offset = 0;
let mut attributes = Vec::new();
for (shader_location, format) in vertex_formats.into_iter().enumerate() {
attributes.push(VertexAttribute {
format,
offset,
shader_location: shader_location as u32,
});
offset += format.size();
}
VertexBufferLayout {
array_stride: offset,
step_mode,
attributes,
}
}
/// Returns a [`VertexBufferLayout`] with the shader location of every attribute offset by
/// `location`.
pub fn offset_locations_by(mut self, location: u32) -> Self {
self.attributes.iter_mut().for_each(|attr| {
attr.shader_location += location;
});
self
}
}
/// Describes the layout of the mesh vertices in GPU memory.
///
/// At most one copy of a mesh vertex buffer layout ever exists in GPU memory at
/// once. Therefore, comparing these for equality requires only a single pointer
/// comparison, and this type's [`PartialEq`] and [`Hash`] implementations take
/// advantage of this. To that end, this type doesn't implement
/// [`bevy_derive::Deref`] or [`bevy_derive::DerefMut`] in order to reduce the
/// possibility of accidental deep comparisons, which would be needlessly
/// expensive.
#[derive(Clone, Debug)]
pub struct MeshVertexBufferLayoutRef(pub Arc<MeshVertexBufferLayout>);
/// Stores the single copy of each mesh vertex buffer layout.
#[derive(Clone, Default, Resource)]
pub struct MeshVertexBufferLayouts(HashSet<Arc<MeshVertexBufferLayout>>);
impl MeshVertexBufferLayouts {
/// Inserts a new mesh vertex buffer layout in the store and returns a
/// reference to it, reusing the existing reference if this mesh vertex
/// buffer layout was already in the store.
pub fn insert(&mut self, layout: MeshVertexBufferLayout) -> MeshVertexBufferLayoutRef {
// Because the special `PartialEq` and `Hash` implementations that
// compare by pointer are on `MeshVertexBufferLayoutRef`, not on
// `Arc<MeshVertexBufferLayout>`, this compares the mesh vertex buffer
// structurally, not by pointer.
MeshVertexBufferLayoutRef(
self.0
.get_or_insert_with(&layout, |layout| Arc::new(layout.clone()))
.clone(),
)
}
}
impl PartialEq for MeshVertexBufferLayoutRef {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for MeshVertexBufferLayoutRef {}
impl Hash for MeshVertexBufferLayoutRef {
fn hash<H: Hasher>(&self, state: &mut H) {
// Hash the address of the underlying data, so two layouts that share the same
// `MeshVertexBufferLayout` will have the same hash.
(Arc::as_ptr(&self.0) as usize).hash(state);
}
}