mod conversions;
pub mod skinning;
use bevy_transform::components::Transform;
pub use wgpu::PrimitiveTopology;
use crate::{
prelude::Image,
primitives::Aabb,
render_asset::{PrepareAssetError, RenderAsset, RenderAssetUsages, RenderAssets},
render_resource::{Buffer, TextureView, VertexBufferLayout},
renderer::RenderDevice,
};
use bevy_asset::{Asset, Handle};
use bevy_core::cast_slice;
use bevy_derive::EnumVariantMeta;
use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
use bevy_log::warn;
use bevy_math::*;
use bevy_reflect::Reflect;
use bevy_utils::{tracing::error, Hashed};
use std::{collections::BTreeMap, hash::Hash, iter::FusedIterator};
use thiserror::Error;
use wgpu::{
util::BufferInitDescriptor, BufferUsages, IndexFormat, VertexAttribute, VertexFormat,
VertexStepMode,
};
pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;
pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
#[derive(Asset, Debug, Clone, Reflect)]
pub struct Mesh {
#[reflect(ignore)]
primitive_topology: PrimitiveTopology,
#[reflect(ignore)]
attributes: BTreeMap<MeshVertexAttributeId, MeshAttributeData>,
indices: Option<Indices>,
morph_targets: Option<Handle<Image>>,
morph_target_names: Option<Vec<String>>,
pub asset_usage: RenderAssetUsages,
}
impl Mesh {
pub const ATTRIBUTE_POSITION: MeshVertexAttribute =
MeshVertexAttribute::new("Vertex_Position", 0, VertexFormat::Float32x3);
pub const ATTRIBUTE_NORMAL: MeshVertexAttribute =
MeshVertexAttribute::new("Vertex_Normal", 1, VertexFormat::Float32x3);
pub const ATTRIBUTE_UV_0: MeshVertexAttribute =
MeshVertexAttribute::new("Vertex_Uv", 2, VertexFormat::Float32x2);
pub const ATTRIBUTE_UV_1: MeshVertexAttribute =
MeshVertexAttribute::new("Vertex_Uv_1", 3, VertexFormat::Float32x2);
pub const ATTRIBUTE_TANGENT: MeshVertexAttribute =
MeshVertexAttribute::new("Vertex_Tangent", 4, VertexFormat::Float32x4);
pub const ATTRIBUTE_COLOR: MeshVertexAttribute =
MeshVertexAttribute::new("Vertex_Color", 5, VertexFormat::Float32x4);
pub const ATTRIBUTE_JOINT_WEIGHT: MeshVertexAttribute =
MeshVertexAttribute::new("Vertex_JointWeight", 6, VertexFormat::Float32x4);
pub const ATTRIBUTE_JOINT_INDEX: MeshVertexAttribute =
MeshVertexAttribute::new("Vertex_JointIndex", 7, VertexFormat::Uint16x4);
pub fn new(primitive_topology: PrimitiveTopology, asset_usage: RenderAssetUsages) -> Self {
Mesh {
primitive_topology,
attributes: Default::default(),
indices: None,
morph_targets: None,
morph_target_names: None,
asset_usage,
}
}
pub fn primitive_topology(&self) -> PrimitiveTopology {
self.primitive_topology
}
#[inline]
pub fn insert_attribute(
&mut self,
attribute: MeshVertexAttribute,
values: impl Into<VertexAttributeValues>,
) {
let values = values.into();
let values_format = VertexFormat::from(&values);
if values_format != attribute.format {
panic!(
"Failed to insert attribute. Invalid attribute format for {}. Given format is {values_format:?} but expected {:?}",
attribute.name, attribute.format
);
}
self.attributes
.insert(attribute.id, MeshAttributeData { attribute, values });
}
#[must_use]
#[inline]
pub fn with_inserted_attribute(
mut self,
attribute: MeshVertexAttribute,
values: impl Into<VertexAttributeValues>,
) -> Self {
self.insert_attribute(attribute, values);
self
}
pub fn remove_attribute(
&mut self,
attribute: impl Into<MeshVertexAttributeId>,
) -> Option<VertexAttributeValues> {
self.attributes
.remove(&attribute.into())
.map(|data| data.values)
}
#[must_use]
pub fn with_removed_attribute(mut self, attribute: impl Into<MeshVertexAttributeId>) -> Self {
self.remove_attribute(attribute);
self
}
#[inline]
pub fn contains_attribute(&self, id: impl Into<MeshVertexAttributeId>) -> bool {
self.attributes.contains_key(&id.into())
}
#[inline]
pub fn attribute(
&self,
id: impl Into<MeshVertexAttributeId>,
) -> Option<&VertexAttributeValues> {
self.attributes.get(&id.into()).map(|data| &data.values)
}
#[inline]
pub fn attribute_mut(
&mut self,
id: impl Into<MeshVertexAttributeId>,
) -> Option<&mut VertexAttributeValues> {
self.attributes
.get_mut(&id.into())
.map(|data| &mut data.values)
}
pub fn attributes(
&self,
) -> impl Iterator<Item = (MeshVertexAttributeId, &VertexAttributeValues)> {
self.attributes.iter().map(|(id, data)| (*id, &data.values))
}
pub fn attributes_mut(
&mut self,
) -> impl Iterator<Item = (MeshVertexAttributeId, &mut VertexAttributeValues)> {
self.attributes
.iter_mut()
.map(|(id, data)| (*id, &mut data.values))
}
#[inline]
pub fn insert_indices(&mut self, indices: Indices) {
self.indices = Some(indices);
}
#[must_use]
#[inline]
pub fn with_inserted_indices(mut self, indices: Indices) -> Self {
self.insert_indices(indices);
self
}
#[inline]
pub fn indices(&self) -> Option<&Indices> {
self.indices.as_ref()
}
#[inline]
pub fn indices_mut(&mut self) -> Option<&mut Indices> {
self.indices.as_mut()
}
#[inline]
pub fn remove_indices(&mut self) -> Option<Indices> {
std::mem::take(&mut self.indices)
}
#[must_use]
pub fn with_removed_indices(mut self) -> Self {
self.remove_indices();
self
}
pub fn get_index_buffer_bytes(&self) -> Option<&[u8]> {
self.indices.as_ref().map(|indices| match &indices {
Indices::U16(indices) => cast_slice(&indices[..]),
Indices::U32(indices) => cast_slice(&indices[..]),
})
}
pub fn get_mesh_vertex_buffer_layout(&self) -> MeshVertexBufferLayout {
let mut attributes = Vec::with_capacity(self.attributes.len());
let mut attribute_ids = Vec::with_capacity(self.attributes.len());
let mut accumulated_offset = 0;
for (index, data) in self.attributes.values().enumerate() {
attribute_ids.push(data.attribute.id);
attributes.push(VertexAttribute {
offset: accumulated_offset,
format: data.attribute.format,
shader_location: index as u32,
});
accumulated_offset += data.attribute.format.get_size();
}
MeshVertexBufferLayout::new(InnerMeshVertexBufferLayout {
layout: VertexBufferLayout {
array_stride: accumulated_offset,
step_mode: VertexStepMode::Vertex,
attributes,
},
attribute_ids,
})
}
pub fn count_vertices(&self) -> usize {
let mut vertex_count: Option<usize> = None;
for (attribute_id, attribute_data) in &self.attributes {
let attribute_len = attribute_data.values.len();
if let Some(previous_vertex_count) = vertex_count {
if previous_vertex_count != attribute_len {
let name = self
.attributes
.get(attribute_id)
.map(|data| data.attribute.name.to_string())
.unwrap_or_else(|| format!("{attribute_id:?}"));
warn!("{name} has a different vertex count ({attribute_len}) than other attributes ({previous_vertex_count}) in this mesh, \
all attributes will be truncated to match the smallest.");
vertex_count = Some(std::cmp::min(previous_vertex_count, attribute_len));
}
} else {
vertex_count = Some(attribute_len);
}
}
vertex_count.unwrap_or(0)
}
pub fn get_vertex_buffer_data(&self) -> Vec<u8> {
let mut vertex_size = 0;
for attribute_data in self.attributes.values() {
let vertex_format = attribute_data.attribute.format;
vertex_size += vertex_format.get_size() as usize;
}
let vertex_count = self.count_vertices();
let mut attributes_interleaved_buffer = vec![0; vertex_count * vertex_size];
let mut attribute_offset = 0;
for attribute_data in self.attributes.values() {
let attribute_size = attribute_data.attribute.format.get_size() as usize;
let attributes_bytes = attribute_data.values.get_bytes();
for (vertex_index, attribute_bytes) in attributes_bytes
.chunks_exact(attribute_size)
.take(vertex_count)
.enumerate()
{
let offset = vertex_index * vertex_size + attribute_offset;
attributes_interleaved_buffer[offset..offset + attribute_size]
.copy_from_slice(attribute_bytes);
}
attribute_offset += attribute_size;
}
attributes_interleaved_buffer
}
#[allow(clippy::match_same_arms)]
pub fn duplicate_vertices(&mut self) {
fn duplicate<T: Copy>(values: &[T], indices: impl Iterator<Item = usize>) -> Vec<T> {
indices.map(|i| values[i]).collect()
}
let Some(indices) = self.indices.take() else {
return;
};
for attributes in self.attributes.values_mut() {
let indices = indices.iter();
match &mut attributes.values {
VertexAttributeValues::Float32(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Sint32(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Uint32(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Float32x2(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Sint32x2(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Uint32x2(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Float32x3(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Sint32x3(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Uint32x3(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Sint32x4(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Uint32x4(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Float32x4(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Sint16x2(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Snorm16x2(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Uint16x2(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Unorm16x2(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Sint16x4(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Snorm16x4(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Uint16x4(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Unorm16x4(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Sint8x2(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Snorm8x2(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Uint8x2(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Unorm8x2(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Sint8x4(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Snorm8x4(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Uint8x4(vec) => *vec = duplicate(vec, indices),
VertexAttributeValues::Unorm8x4(vec) => *vec = duplicate(vec, indices),
}
}
}
#[must_use]
pub fn with_duplicated_vertices(mut self) -> Self {
self.duplicate_vertices();
self
}
pub fn compute_flat_normals(&mut self) {
assert!(self.indices().is_none(), "`compute_flat_normals` can't work on indexed geometry. Consider calling `Mesh::duplicate_vertices`.");
assert!(
matches!(self.primitive_topology, PrimitiveTopology::TriangleList),
"`compute_flat_normals` can only work on `TriangleList`s"
);
let positions = self
.attribute(Mesh::ATTRIBUTE_POSITION)
.unwrap()
.as_float3()
.expect("`Mesh::ATTRIBUTE_POSITION` vertex attributes should be of type `float3`");
let normals: Vec<_> = positions
.chunks_exact(3)
.map(|p| face_normal(p[0], p[1], p[2]))
.flat_map(|normal| [normal; 3])
.collect();
self.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
}
#[must_use]
pub fn with_computed_flat_normals(mut self) -> Self {
self.compute_flat_normals();
self
}
pub fn generate_tangents(&mut self) -> Result<(), GenerateTangentsError> {
let tangents = generate_tangents_for_mesh(self)?;
self.insert_attribute(Mesh::ATTRIBUTE_TANGENT, tangents);
Ok(())
}
pub fn with_generated_tangents(mut self) -> Result<Mesh, GenerateTangentsError> {
self.generate_tangents()?;
Ok(self)
}
#[allow(clippy::match_same_arms)]
pub fn merge(&mut self, other: Mesh) {
use VertexAttributeValues::*;
let index_offset = self
.attribute(Mesh::ATTRIBUTE_POSITION)
.get_or_insert(&VertexAttributeValues::Float32x3(Vec::default()))
.len();
for (id, values) in self.attributes_mut() {
let enum_variant_name = values.enum_variant_name();
if let Some(other_values) = other.attribute(id) {
match (values, other_values) {
(Float32(vec1), Float32(vec2)) => vec1.extend(vec2),
(Sint32(vec1), Sint32(vec2)) => vec1.extend(vec2),
(Uint32(vec1), Uint32(vec2)) => vec1.extend(vec2),
(Float32x2(vec1), Float32x2(vec2)) => vec1.extend(vec2),
(Sint32x2(vec1), Sint32x2(vec2)) => vec1.extend(vec2),
(Uint32x2(vec1), Uint32x2(vec2)) => vec1.extend(vec2),
(Float32x3(vec1), Float32x3(vec2)) => vec1.extend(vec2),
(Sint32x3(vec1), Sint32x3(vec2)) => vec1.extend(vec2),
(Uint32x3(vec1), Uint32x3(vec2)) => vec1.extend(vec2),
(Sint32x4(vec1), Sint32x4(vec2)) => vec1.extend(vec2),
(Uint32x4(vec1), Uint32x4(vec2)) => vec1.extend(vec2),
(Float32x4(vec1), Float32x4(vec2)) => vec1.extend(vec2),
(Sint16x2(vec1), Sint16x2(vec2)) => vec1.extend(vec2),
(Snorm16x2(vec1), Snorm16x2(vec2)) => vec1.extend(vec2),
(Uint16x2(vec1), Uint16x2(vec2)) => vec1.extend(vec2),
(Unorm16x2(vec1), Unorm16x2(vec2)) => vec1.extend(vec2),
(Sint16x4(vec1), Sint16x4(vec2)) => vec1.extend(vec2),
(Snorm16x4(vec1), Snorm16x4(vec2)) => vec1.extend(vec2),
(Uint16x4(vec1), Uint16x4(vec2)) => vec1.extend(vec2),
(Unorm16x4(vec1), Unorm16x4(vec2)) => vec1.extend(vec2),
(Sint8x2(vec1), Sint8x2(vec2)) => vec1.extend(vec2),
(Snorm8x2(vec1), Snorm8x2(vec2)) => vec1.extend(vec2),
(Uint8x2(vec1), Uint8x2(vec2)) => vec1.extend(vec2),
(Unorm8x2(vec1), Unorm8x2(vec2)) => vec1.extend(vec2),
(Sint8x4(vec1), Sint8x4(vec2)) => vec1.extend(vec2),
(Snorm8x4(vec1), Snorm8x4(vec2)) => vec1.extend(vec2),
(Uint8x4(vec1), Uint8x4(vec2)) => vec1.extend(vec2),
(Unorm8x4(vec1), Unorm8x4(vec2)) => vec1.extend(vec2),
_ => panic!(
"Incompatible vertex attribute types {} and {}",
enum_variant_name,
other_values.enum_variant_name()
),
}
}
}
if let (Some(indices), Some(other_indices)) = (self.indices_mut(), other.indices()) {
match (indices, other_indices) {
(Indices::U16(i1), Indices::U16(i2)) => {
i1.extend(i2.iter().map(|i| *i + index_offset as u16));
}
(Indices::U32(i1), Indices::U32(i2)) => {
i1.extend(i2.iter().map(|i| *i + index_offset as u32));
}
(Indices::U16(i1), Indices::U32(i2)) => {
i1.extend(i2.iter().map(|i| *i as u16 + index_offset as u16));
}
(Indices::U32(i1), Indices::U16(i2)) => {
i1.extend(i2.iter().map(|i| *i as u32 + index_offset as u32));
}
}
}
}
pub fn transformed_by(mut self, transform: Transform) -> Self {
self.transform_by(transform);
self
}
pub fn transform_by(&mut self, transform: Transform) {
let covector_scale = transform.scale.yzx() * transform.scale.zxy();
debug_assert!(
covector_scale != Vec3::ZERO,
"mesh transform scale cannot be zero on more than one axis"
);
if let Some(VertexAttributeValues::Float32x3(ref mut positions)) =
self.attribute_mut(Mesh::ATTRIBUTE_POSITION)
{
positions
.iter_mut()
.for_each(|pos| *pos = transform.transform_point(Vec3::from_slice(pos)).to_array());
}
if transform.rotation.is_near_identity()
&& transform.scale.x == transform.scale.y
&& transform.scale.y == transform.scale.z
{
return;
}
if let Some(VertexAttributeValues::Float32x3(ref mut normals)) =
self.attribute_mut(Mesh::ATTRIBUTE_NORMAL)
{
normals.iter_mut().for_each(|normal| {
let scaled_normal = Vec3::from_slice(normal) * covector_scale;
*normal = (transform.rotation * scaled_normal.normalize_or_zero()).to_array();
});
}
if let Some(VertexAttributeValues::Float32x3(ref mut tangents)) =
self.attribute_mut(Mesh::ATTRIBUTE_TANGENT)
{
tangents.iter_mut().for_each(|tangent| {
let scaled_tangent = Vec3::from_slice(tangent) * covector_scale;
*tangent = (transform.rotation * scaled_tangent.normalize_or_zero()).to_array();
});
}
}
pub fn translated_by(mut self, translation: Vec3) -> Self {
self.translate_by(translation);
self
}
pub fn translate_by(&mut self, translation: Vec3) {
if translation == Vec3::ZERO {
return;
}
if let Some(VertexAttributeValues::Float32x3(ref mut positions)) =
self.attribute_mut(Mesh::ATTRIBUTE_POSITION)
{
positions
.iter_mut()
.for_each(|pos| *pos = (Vec3::from_slice(pos) + translation).to_array());
}
}
pub fn rotated_by(mut self, rotation: Quat) -> Self {
self.rotate_by(rotation);
self
}
pub fn rotate_by(&mut self, rotation: Quat) {
if let Some(VertexAttributeValues::Float32x3(ref mut positions)) =
self.attribute_mut(Mesh::ATTRIBUTE_POSITION)
{
positions
.iter_mut()
.for_each(|pos| *pos = (rotation * Vec3::from_slice(pos)).to_array());
}
if rotation.is_near_identity() {
return;
}
if let Some(VertexAttributeValues::Float32x3(ref mut normals)) =
self.attribute_mut(Mesh::ATTRIBUTE_NORMAL)
{
normals.iter_mut().for_each(|normal| {
*normal = (rotation * Vec3::from_slice(normal).normalize_or_zero()).to_array();
});
}
if let Some(VertexAttributeValues::Float32x3(ref mut tangents)) =
self.attribute_mut(Mesh::ATTRIBUTE_TANGENT)
{
tangents.iter_mut().for_each(|tangent| {
*tangent = (rotation * Vec3::from_slice(tangent).normalize_or_zero()).to_array();
});
}
}
pub fn scaled_by(mut self, scale: Vec3) -> Self {
self.scale_by(scale);
self
}
pub fn scale_by(&mut self, scale: Vec3) {
let covector_scale = scale.yzx() * scale.zxy();
debug_assert!(
covector_scale != Vec3::ZERO,
"mesh transform scale cannot be zero on more than one axis"
);
if let Some(VertexAttributeValues::Float32x3(ref mut positions)) =
self.attribute_mut(Mesh::ATTRIBUTE_POSITION)
{
positions
.iter_mut()
.for_each(|pos| *pos = (scale * Vec3::from_slice(pos)).to_array());
}
if scale.x == scale.y && scale.y == scale.z {
return;
}
if let Some(VertexAttributeValues::Float32x3(ref mut normals)) =
self.attribute_mut(Mesh::ATTRIBUTE_NORMAL)
{
normals.iter_mut().for_each(|normal| {
let scaled_normal = Vec3::from_slice(normal) * covector_scale;
*normal = scaled_normal.normalize_or_zero().to_array();
});
}
if let Some(VertexAttributeValues::Float32x3(ref mut tangents)) =
self.attribute_mut(Mesh::ATTRIBUTE_TANGENT)
{
tangents.iter_mut().for_each(|tangent| {
let scaled_tangent = Vec3::from_slice(tangent) * covector_scale;
*tangent = scaled_tangent.normalize_or_zero().to_array();
});
}
}
pub fn compute_aabb(&self) -> Option<Aabb> {
let Some(VertexAttributeValues::Float32x3(values)) =
self.attribute(Mesh::ATTRIBUTE_POSITION)
else {
return None;
};
Aabb::enclosing(values.iter().map(|p| Vec3::from_slice(p)))
}
pub fn has_morph_targets(&self) -> bool {
self.morph_targets.is_some()
}
pub fn set_morph_targets(&mut self, morph_targets: Handle<Image>) {
self.morph_targets = Some(morph_targets);
}
#[must_use]
pub fn with_morph_targets(mut self, morph_targets: Handle<Image>) -> Self {
self.set_morph_targets(morph_targets);
self
}
pub fn set_morph_target_names(&mut self, names: Vec<String>) {
self.morph_target_names = Some(names);
}
#[must_use]
pub fn with_morph_target_names(mut self, names: Vec<String>) -> Self {
self.set_morph_target_names(names);
self
}
pub fn morph_target_names(&self) -> Option<&[String]> {
self.morph_target_names.as_deref()
}
pub fn normalize_joint_weights(&mut self) {
if let Some(joints) = self.attribute_mut(Self::ATTRIBUTE_JOINT_WEIGHT) {
let VertexAttributeValues::Float32x4(ref mut joints) = joints else {
panic!("unexpected joint weight format");
};
for weights in joints.iter_mut() {
weights.iter_mut().for_each(|w| *w = w.max(0.0));
let sum: f32 = weights.iter().sum();
if sum == 0.0 {
weights[0] = 1.0;
} else {
let recip = sum.recip();
for weight in weights.iter_mut() {
*weight *= recip;
}
}
}
}
}
}
impl core::ops::Mul<Mesh> for Transform {
type Output = Mesh;
fn mul(self, rhs: Mesh) -> Self::Output {
rhs.transformed_by(self)
}
}
#[derive(Debug, Clone)]
pub struct MeshVertexAttribute {
pub name: &'static str,
pub id: MeshVertexAttributeId,
pub format: VertexFormat,
}
impl MeshVertexAttribute {
pub const fn new(name: &'static str, id: usize, 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(usize);
impl From<MeshVertexAttribute> for MeshVertexAttributeId {
fn from(attribute: MeshVertexAttribute) -> Self {
attribute.id
}
}
pub type MeshVertexBufferLayout = Hashed<InnerMeshVertexBufferLayout>;
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct InnerMeshVertexBufferLayout {
attribute_ids: Vec<MeshVertexAttributeId>,
layout: VertexBufferLayout,
}
impl InnerMeshVertexBufferLayout {
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(crate) 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)]
struct MeshAttributeData {
attribute: MeshVertexAttribute,
values: VertexAttributeValues,
}
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()
}
pub trait VertexFormatSize {
fn get_size(self) -> u64;
}
impl VertexFormatSize for VertexFormat {
#[allow(clippy::match_same_arms)]
fn get_size(self) -> u64 {
match self {
VertexFormat::Uint8x2 => 2,
VertexFormat::Uint8x4 => 4,
VertexFormat::Sint8x2 => 2,
VertexFormat::Sint8x4 => 4,
VertexFormat::Unorm8x2 => 2,
VertexFormat::Unorm8x4 => 4,
VertexFormat::Snorm8x2 => 2,
VertexFormat::Snorm8x4 => 4,
VertexFormat::Uint16x2 => 2 * 2,
VertexFormat::Uint16x4 => 2 * 4,
VertexFormat::Sint16x2 => 2 * 2,
VertexFormat::Sint16x4 => 2 * 4,
VertexFormat::Unorm16x2 => 2 * 2,
VertexFormat::Unorm16x4 => 2 * 4,
VertexFormat::Snorm16x2 => 2 * 2,
VertexFormat::Snorm16x4 => 2 * 4,
VertexFormat::Float16x2 => 2 * 2,
VertexFormat::Float16x4 => 2 * 4,
VertexFormat::Float32 => 4,
VertexFormat::Float32x2 => 4 * 2,
VertexFormat::Float32x3 => 4 * 3,
VertexFormat::Float32x4 => 4 * 4,
VertexFormat::Uint32 => 4,
VertexFormat::Uint32x2 => 4 * 2,
VertexFormat::Uint32x3 => 4 * 3,
VertexFormat::Uint32x4 => 4 * 4,
VertexFormat::Sint32 => 4,
VertexFormat::Sint32x2 => 4 * 2,
VertexFormat::Sint32x3 => 4 * 3,
VertexFormat::Sint32x4 => 4 * 4,
VertexFormat::Float64 => 8,
VertexFormat::Float64x2 => 8 * 2,
VertexFormat::Float64x3 => 8 * 3,
VertexFormat::Float64x4 => 8 * 4,
}
}
}
#[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 {
#[allow(clippy::match_same_arms)]
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(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn as_float3(&self) -> Option<&[[f32; 3]]> {
match self {
VertexAttributeValues::Float32x3(values) => Some(values),
_ => None,
}
}
#[allow(clippy::match_same_arms)]
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,
}
}
}
#[derive(Debug, Clone, Reflect)]
pub enum Indices {
U16(Vec<u16>),
U32(Vec<u32>),
}
impl 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()),
}
}
pub fn len(&self) -> usize {
match self {
Indices::U16(vec) => vec.len(),
Indices::U32(vec) => vec.len(),
}
}
pub fn is_empty(&self) -> bool {
match self {
Indices::U16(vec) => vec.is_empty(),
Indices::U32(vec) => vec.is_empty(),
}
}
}
enum IndicesIter<'a> {
U16(std::slice::Iter<'a, u16>),
U32(std::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,
}
}
}
#[derive(Debug, Clone)]
pub struct GpuMesh {
pub vertex_buffer: Buffer,
pub vertex_count: u32,
pub morph_targets: Option<TextureView>,
pub buffer_info: GpuBufferInfo,
pub primitive_topology: PrimitiveTopology,
pub layout: MeshVertexBufferLayout,
}
#[derive(Debug, Clone)]
pub enum GpuBufferInfo {
Indexed {
buffer: Buffer,
count: u32,
index_format: IndexFormat,
},
NonIndexed,
}
impl RenderAsset for Mesh {
type PreparedAsset = GpuMesh;
type Param = (SRes<RenderDevice>, SRes<RenderAssets<Image>>);
fn asset_usage(&self) -> RenderAssetUsages {
self.asset_usage
}
fn prepare_asset(
self,
(render_device, images): &mut SystemParamItem<Self::Param>,
) -> Result<Self::PreparedAsset, PrepareAssetError<Self>> {
let vertex_buffer_data = self.get_vertex_buffer_data();
let vertex_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
usage: BufferUsages::VERTEX,
label: Some("Mesh Vertex Buffer"),
contents: &vertex_buffer_data,
});
let buffer_info = if let Some(data) = self.get_index_buffer_bytes() {
GpuBufferInfo::Indexed {
buffer: render_device.create_buffer_with_data(&BufferInitDescriptor {
usage: BufferUsages::INDEX,
contents: data,
label: Some("Mesh Index Buffer"),
}),
count: self.indices().unwrap().len() as u32,
index_format: self.indices().unwrap().into(),
}
} else {
GpuBufferInfo::NonIndexed
};
let mesh_vertex_buffer_layout = self.get_mesh_vertex_buffer_layout();
Ok(GpuMesh {
vertex_buffer,
vertex_count: self.count_vertices() as u32,
buffer_info,
primitive_topology: self.primitive_topology(),
layout: mesh_vertex_buffer_layout,
morph_targets: self
.morph_targets
.and_then(|mt| images.get(&mt).map(|i| i.texture_view.clone())),
})
}
}
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)]
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,
}
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);
}
for tangent in &mut mikktspace_mesh.tangents {
tangent[3] = -tangent[3];
}
Ok(mikktspace_mesh.tangents)
}
#[cfg(test)]
mod tests {
use super::Mesh;
use crate::render_asset::RenderAssetUsages;
use wgpu::PrimitiveTopology;
#[test]
#[should_panic]
fn panic_invalid_format() {
let _mesh = Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0, 0.0]]);
}
}