mirror of
https://github.com/bevyengine/bevy
synced 2024-11-14 00:47:32 +00:00
Split out bevy_mesh from bevy_render (#15666)
# Objective - bevy_render is gargantuan ## Solution - Split out bevy_mesh ## Testing - Ran some examples, everything looks fine ## Migration Guide `bevy_render::mesh::morph::inherit_weights` is now `bevy_render::mesh::inherit_weights` if you were using `Mesh::compute_aabb`, you will need to `use bevy_render::mesh::MeshAabb;` now --------- Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
This commit is contained in:
parent
7c4a0683c7
commit
4a23dc4216
32 changed files with 1087 additions and 1042 deletions
|
@ -1244,7 +1244,7 @@ impl Plugin for AnimationPlugin {
|
|||
// `PostUpdate`. For now, we just disable ambiguity testing
|
||||
// for this system.
|
||||
animate_targets
|
||||
.after(bevy_render::mesh::morph::inherit_weights)
|
||||
.after(bevy_render::mesh::inherit_weights)
|
||||
.ambiguous_with_all(),
|
||||
trigger_untargeted_animation_events,
|
||||
expire_completed_transitions,
|
||||
|
|
37
crates/bevy_mesh/Cargo.toml
Normal file
37
crates/bevy_mesh/Cargo.toml
Normal file
|
@ -0,0 +1,37 @@
|
|||
[package]
|
||||
name = "bevy_mesh"
|
||||
version = "0.15.0-dev"
|
||||
edition = "2021"
|
||||
description = "Provides mesh types for Bevy Engine"
|
||||
homepage = "https://bevyengine.org"
|
||||
repository = "https://github.com/bevyengine/bevy"
|
||||
license = "MIT OR Apache-2.0"
|
||||
keywords = ["bevy"]
|
||||
|
||||
[dependencies]
|
||||
bevy_asset = { path = "../bevy_asset", version = "0.15.0-dev" }
|
||||
bevy_image = { path = "../bevy_image", version = "0.15.0-dev" }
|
||||
bevy_math = { path = "../bevy_math", version = "0.15.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [
|
||||
"bevy",
|
||||
] }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" }
|
||||
bevy_transform = { path = "../bevy_transform", version = "0.15.0-dev" }
|
||||
bevy_mikktspace = { path = "../bevy_mikktspace", version = "0.15.0-dev" }
|
||||
bevy_derive = { path = "../bevy_derive", version = "0.15.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev" }
|
||||
|
||||
# misc
|
||||
bitflags = { version = "2.3", features = ["serde"] }
|
||||
bytemuck = { version = "1.5" }
|
||||
wgpu = { version = "22", default-features = false }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
hexasphere = "15.0"
|
||||
thiserror = "1.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"]
|
||||
all-features = true
|
|
@ -4,15 +4,15 @@
|
|||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! use bevy_render::mesh::VertexAttributeValues;
|
||||
//! use bevy_mesh::VertexAttributeValues;
|
||||
//!
|
||||
//! // creating std::vec::Vec
|
||||
//! let buffer = vec![[0_u32; 4]; 10];
|
||||
//!
|
||||
//! // converting std::vec::Vec to bevy_render::mesh::VertexAttributeValues
|
||||
//! // converting std::vec::Vec to bevy_mesh::VertexAttributeValues
|
||||
//! let values = VertexAttributeValues::from(buffer.clone());
|
||||
//!
|
||||
//! // converting bevy_render::mesh::VertexAttributeValues to std::vec::Vec with two ways
|
||||
//! // 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();
|
||||
//!
|
||||
|
@ -24,7 +24,7 @@
|
|||
//! assert!(error.is_err());
|
||||
//! ```
|
||||
|
||||
use crate::mesh::VertexAttributeValues;
|
||||
use super::VertexAttributeValues;
|
||||
use bevy_math::{IVec2, IVec3, IVec4, UVec2, UVec3, UVec4, Vec2, Vec3, Vec3A, Vec4};
|
||||
use thiserror::Error;
|
||||
|
137
crates/bevy_mesh/src/index.rs
Normal file
137
crates/bevy_mesh/src/index.rs
Normal file
|
@ -0,0 +1,137 @@
|
|||
use bevy_reflect::Reflect;
|
||||
use core::iter::FusedIterator;
|
||||
use thiserror::Error;
|
||||
use wgpu::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 invertation 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)]
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
}
|
58
crates/bevy_mesh/src/lib.rs
Normal file
58
crates/bevy_mesh/src/lib.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
// FIXME(15321): solve CI failures, then replace with `#![expect()]`.
|
||||
#![allow(missing_docs, reason = "Not all docs are written yet, see #3492.")]
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
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::*;
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
142
crates/bevy_mesh/src/mikktspace.rs
Normal file
142
crates/bevy_mesh/src/mikktspace.rs
Normal file
|
@ -0,0 +1,142 @@
|
|||
use super::{Indices, Mesh, VertexAttributeValues};
|
||||
use bevy_math::Vec3;
|
||||
use thiserror::Error;
|
||||
use wgpu::{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()
|
||||
}
|
||||
}
|
|
@ -1,20 +1,13 @@
|
|||
use crate::{
|
||||
mesh::Mesh,
|
||||
render_asset::RenderAssetUsages,
|
||||
render_resource::{Extent3d, TextureDimension, TextureFormat},
|
||||
texture::Image,
|
||||
};
|
||||
use bevy_app::{Plugin, PostUpdate};
|
||||
use bevy_asset::Handle;
|
||||
use super::Mesh;
|
||||
use bevy_asset::{Handle, RenderAssetUsages};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_hierarchy::Children;
|
||||
use bevy_image::Image;
|
||||
use bevy_math::Vec3;
|
||||
use bevy_reflect::prelude::*;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use core::iter;
|
||||
use thiserror::Error;
|
||||
|
||||
use super::Mesh3d;
|
||||
use wgpu::{Extent3d, TextureDimension, TextureFormat};
|
||||
|
||||
const MAX_TEXTURE_WIDTH: u32 = 2048;
|
||||
// NOTE: "component" refers to the element count of math objects,
|
||||
|
@ -24,17 +17,6 @@ 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;
|
||||
|
||||
/// [Inherit weights](inherit_weights) from glTF mesh parent entity to direct
|
||||
/// bevy mesh child entities (ie: glTF primitive).
|
||||
pub struct MorphPlugin;
|
||||
impl Plugin for MorphPlugin {
|
||||
fn build(&self, app: &mut bevy_app::App) {
|
||||
app.register_type::<MorphWeights>()
|
||||
.register_type::<MeshMorphWeights>()
|
||||
.add_systems(PostUpdate, inherit_weights);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Clone, Debug)]
|
||||
pub enum MorphBuildError {
|
||||
#[error(
|
||||
|
@ -116,7 +98,7 @@ impl MorphTargetImage {
|
|||
}
|
||||
}
|
||||
|
||||
/// Controls the [morph targets] for all child [`Mesh3d`] entities. In most cases, [`MorphWeights`] should be considered
|
||||
/// 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.
|
||||
|
@ -124,9 +106,9 @@ impl MorphTargetImage {
|
|||
/// 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).
|
||||
/// 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`].
|
||||
/// 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)]
|
||||
|
@ -150,7 +132,7 @@ impl MorphWeights {
|
|||
first_mesh,
|
||||
})
|
||||
}
|
||||
/// The first child [`Mesh3d`] primitive controlled by these weights.
|
||||
/// 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()
|
||||
|
@ -170,7 +152,7 @@ impl MorphWeights {
|
|||
///
|
||||
/// See [`MorphWeights`] for more details on Bevy's morph target implementation.
|
||||
///
|
||||
/// Add this to an [`Entity`] with a [`Mesh3d`] with a [`MorphAttributes`] set
|
||||
/// 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
|
||||
|
@ -193,22 +175,11 @@ impl MeshMorphWeights {
|
|||
pub fn weights_mut(&mut self) -> &mut [f32] {
|
||||
&mut self.weights
|
||||
}
|
||||
}
|
||||
|
||||
/// Bevy meshes are gltf primitives, [`MorphWeights`] on the bevy node entity
|
||||
/// should be inherited by children meshes.
|
||||
///
|
||||
/// Only direct children are updated, to fulfill the expectations of glTF spec.
|
||||
pub fn inherit_weights(
|
||||
morph_nodes: Query<(&Children, &MorphWeights), (Without<Mesh3d>, Changed<MorphWeights>)>,
|
||||
mut morph_primitives: Query<&mut MeshMorphWeights, With<Mesh3d>>,
|
||||
) {
|
||||
for (children, parent_weights) in &morph_nodes {
|
||||
let mut iter = morph_primitives.iter_many_mut(children);
|
||||
while let Some(mut child_weight) = iter.fetch_next() {
|
||||
child_weight.weights.clear();
|
||||
child_weight.weights.extend(&parent_weights.weights);
|
||||
}
|
||||
pub fn clear_weights(&mut self) {
|
||||
self.weights.clear();
|
||||
}
|
||||
pub fn extend_weights(&mut self, weights: &[f32]) {
|
||||
self.weights.extend(weights);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
use core::f32::consts::FRAC_PI_2;
|
||||
|
||||
use crate::{
|
||||
mesh::{primitives::dim3::triangle3d, Indices, Mesh, PerimeterSegment},
|
||||
render_asset::RenderAssetUsages,
|
||||
};
|
||||
use crate::{primitives::dim3::triangle3d, Indices, Mesh, PerimeterSegment};
|
||||
use bevy_asset::RenderAssetUsages;
|
||||
|
||||
use super::{Extrudable, MeshBuilder, Meshable};
|
||||
use bevy_math::{
|
||||
|
@ -1009,7 +1007,7 @@ mod tests {
|
|||
use bevy_math::{prelude::Annulus, primitives::RegularPolygon, FloatOrd};
|
||||
use bevy_utils::HashSet;
|
||||
|
||||
use crate::mesh::{Mesh, MeshBuilder, Meshable, VertexAttributeValues};
|
||||
use crate::{Mesh, MeshBuilder, Meshable, VertexAttributeValues};
|
||||
|
||||
fn count_distinct_positions(points: &[[f32; 3]]) -> usize {
|
||||
let mut map = HashSet::new();
|
|
@ -1,7 +1,5 @@
|
|||
use crate::{
|
||||
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||
render_asset::RenderAssetUsages,
|
||||
};
|
||||
use crate::{Indices, Mesh, MeshBuilder, Meshable};
|
||||
use bevy_asset::RenderAssetUsages;
|
||||
use bevy_math::{ops, primitives::Capsule3d, Vec2, Vec3};
|
||||
use wgpu::PrimitiveTopology;
|
||||
|
|
@ -1,11 +1,8 @@
|
|||
use crate::{Indices, Mesh, MeshBuilder, Meshable};
|
||||
use bevy_asset::RenderAssetUsages;
|
||||
use bevy_math::{ops, primitives::Cone, Vec3};
|
||||
use wgpu::PrimitiveTopology;
|
||||
|
||||
use crate::{
|
||||
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||
render_asset::RenderAssetUsages,
|
||||
};
|
||||
|
||||
/// Anchoring options for [`ConeMeshBuilder`]
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub enum ConeAnchor {
|
||||
|
@ -191,10 +188,9 @@ impl From<Cone> for Mesh {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{Mesh, MeshBuilder, Meshable, VertexAttributeValues};
|
||||
use bevy_math::{primitives::Cone, Vec2};
|
||||
|
||||
use crate::mesh::{primitives::MeshBuilder, Mesh, Meshable, VertexAttributeValues};
|
||||
|
||||
/// 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() {
|
|
@ -1,7 +1,5 @@
|
|||
use crate::{
|
||||
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||
render_asset::RenderAssetUsages,
|
||||
};
|
||||
use crate::{Indices, Mesh, MeshBuilder, Meshable};
|
||||
use bevy_asset::RenderAssetUsages;
|
||||
use bevy_math::{ops, primitives::ConicalFrustum, Vec3};
|
||||
use wgpu::PrimitiveTopology;
|
||||
|
|
@ -1,11 +1,8 @@
|
|||
use crate::{Indices, Mesh, MeshBuilder, Meshable};
|
||||
use bevy_asset::RenderAssetUsages;
|
||||
use bevy_math::{primitives::Cuboid, Vec3};
|
||||
use wgpu::PrimitiveTopology;
|
||||
|
||||
use crate::{
|
||||
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||
render_asset::RenderAssetUsages,
|
||||
};
|
||||
|
||||
/// A builder used for creating a [`Mesh`] with a [`Cuboid`] shape.
|
||||
pub struct CuboidMeshBuilder {
|
||||
half_size: Vec3,
|
|
@ -1,11 +1,8 @@
|
|||
use crate::{Indices, Mesh, MeshBuilder, Meshable};
|
||||
use bevy_asset::RenderAssetUsages;
|
||||
use bevy_math::{ops, primitives::Cylinder};
|
||||
use wgpu::PrimitiveTopology;
|
||||
|
||||
use crate::{
|
||||
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||
render_asset::RenderAssetUsages,
|
||||
};
|
||||
|
||||
/// Anchoring options for [`CylinderMeshBuilder`]
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub enum CylinderAnchor {
|
|
@ -1,11 +1,8 @@
|
|||
use crate::{Indices, Mesh, MeshBuilder, Meshable};
|
||||
use bevy_asset::RenderAssetUsages;
|
||||
use bevy_math::{primitives::Plane3d, Dir3, Quat, Vec2, Vec3};
|
||||
use wgpu::PrimitiveTopology;
|
||||
|
||||
use crate::{
|
||||
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||
render_asset::RenderAssetUsages,
|
||||
};
|
||||
|
||||
/// A builder used for creating a [`Mesh`] with a [`Plane3d`] shape.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct PlaneMeshBuilder {
|
|
@ -1,10 +1,7 @@
|
|||
use core::f32::consts::PI;
|
||||
|
||||
use crate::{
|
||||
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||
render_asset::RenderAssetUsages,
|
||||
};
|
||||
use crate::{Indices, Mesh, MeshBuilder, Meshable};
|
||||
use bevy_asset::RenderAssetUsages;
|
||||
use bevy_math::{ops, primitives::Sphere};
|
||||
use core::f32::consts::PI;
|
||||
use hexasphere::shapes::IcoSphere;
|
||||
use thiserror::Error;
|
||||
use wgpu::PrimitiveTopology;
|
|
@ -1,8 +1,6 @@
|
|||
use super::triangle3d;
|
||||
use crate::{
|
||||
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||
render_asset::RenderAssetUsages,
|
||||
};
|
||||
use crate::{Indices, Mesh, MeshBuilder, Meshable};
|
||||
use bevy_asset::RenderAssetUsages;
|
||||
use bevy_math::primitives::{Tetrahedron, Triangle3d};
|
||||
use wgpu::PrimitiveTopology;
|
||||
|
|
@ -1,12 +1,9 @@
|
|||
use crate::{Indices, Mesh, MeshBuilder, Meshable};
|
||||
use bevy_asset::RenderAssetUsages;
|
||||
use bevy_math::{ops, primitives::Torus, Vec3};
|
||||
use core::ops::RangeInclusive;
|
||||
use wgpu::PrimitiveTopology;
|
||||
|
||||
use crate::{
|
||||
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||
render_asset::RenderAssetUsages,
|
||||
};
|
||||
|
||||
/// A builder used for creating a [`Mesh`] with a [`Torus`] shape.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TorusMeshBuilder {
|
|
@ -1,11 +1,8 @@
|
|||
use crate::{Indices, Mesh, MeshBuilder, Meshable};
|
||||
use bevy_asset::RenderAssetUsages;
|
||||
use bevy_math::{primitives::Triangle3d, Vec3};
|
||||
use wgpu::PrimitiveTopology;
|
||||
|
||||
use crate::{
|
||||
mesh::{Indices, Mesh, MeshBuilder, Meshable},
|
||||
render_asset::RenderAssetUsages,
|
||||
};
|
||||
|
||||
/// A builder used for creating a [`Mesh`] with a [`Triangle3d`] shape.
|
||||
pub struct Triangle3dMeshBuilder {
|
||||
triangle: Triangle3d,
|
|
@ -3,9 +3,8 @@ use bevy_math::{
|
|||
Vec2, Vec3,
|
||||
};
|
||||
|
||||
use crate::mesh::{Indices, Mesh, VertexAttributeValues};
|
||||
|
||||
use super::{MeshBuilder, Meshable};
|
||||
use crate::{Indices, Mesh, VertexAttributeValues};
|
||||
|
||||
/// A type representing a segment of the perimeter of an extrudable mesh.
|
||||
pub enum PerimeterSegment {
|
|
@ -8,7 +8,7 @@
|
|||
//! # use bevy_asset::Assets;
|
||||
//! # use bevy_ecs::prelude::ResMut;
|
||||
//! # use bevy_math::prelude::Circle;
|
||||
//! # use bevy_render::prelude::*;
|
||||
//! # use bevy_mesh::*;
|
||||
//! #
|
||||
//! # fn setup(mut meshes: ResMut<Assets<Mesh>>) {
|
||||
//! // Create circle mesh with default configuration
|
445
crates/bevy_mesh/src/vertex.rs
Normal file
445
crates/bevy_mesh/src/vertex.rs
Normal file
|
@ -0,0 +1,445 @@
|
|||
use alloc::sync::Arc;
|
||||
use bevy_derive::EnumVariantMeta;
|
||||
use bevy_ecs::system::Resource;
|
||||
use bevy_math::Vec3;
|
||||
use bevy_utils::HashSet;
|
||||
use bytemuck::cast_slice;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use thiserror::Error;
|
||||
use wgpu::{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,
|
||||
}
|
||||
|
||||
pub(crate) 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::Unorm10_10_10_2 => 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[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(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
|
@ -19,10 +19,10 @@ webp = ["image/webp", "bevy_image/webp"]
|
|||
dds = ["ddsfile", "bevy_image/dds"]
|
||||
pnm = ["image/pnm", "bevy_image/pnm"]
|
||||
|
||||
ddsfile = ["dep:ddsfile", "bevy_image/ddsfile"]
|
||||
ddsfile = ["bevy_image/ddsfile"]
|
||||
ktx2 = ["dep:ktx2", "bevy_image/ktx2"]
|
||||
flate2 = ["dep:flate2", "bevy_image/flate2"]
|
||||
ruzstd = ["dep:ruzstd", "bevy_image/ruzstd"]
|
||||
flate2 = ["bevy_image/flate2"]
|
||||
ruzstd = ["bevy_image/ruzstd"]
|
||||
basis-universal = ["dep:basis-universal", "bevy_image/basis-universal"]
|
||||
|
||||
multi_threaded = ["bevy_tasks/multi_threaded"]
|
||||
|
@ -59,7 +59,6 @@ bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" }
|
|||
bevy_encase_derive = { path = "../bevy_encase_derive", version = "0.15.0-dev" }
|
||||
bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.15.0-dev" }
|
||||
bevy_math = { path = "../bevy_math", version = "0.15.0-dev" }
|
||||
bevy_mikktspace = { path = "../bevy_mikktspace", version = "0.15.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [
|
||||
"bevy",
|
||||
] }
|
||||
|
@ -71,6 +70,7 @@ bevy_winit = { path = "../bevy_winit", version = "0.15.0-dev" }
|
|||
bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev" }
|
||||
bevy_tasks = { path = "../bevy_tasks", version = "0.15.0-dev" }
|
||||
bevy_image = { path = "../bevy_image", version = "0.15.0-dev" }
|
||||
bevy_mesh = { path = "../bevy_mesh", version = "0.15.0-dev" }
|
||||
|
||||
# rendering
|
||||
image = { version = "0.25.2", default-features = false }
|
||||
|
@ -90,17 +90,11 @@ wgpu = { version = "22", default-features = false, features = [
|
|||
] }
|
||||
naga = { version = "22", features = ["wgsl-in"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
bitflags = { version = "2.3", features = ["serde"] }
|
||||
bytemuck = { version = "1.5", features = ["derive", "must_cast"] }
|
||||
downcast-rs = "1.2.0"
|
||||
thiserror = "1.0"
|
||||
futures-lite = "2.0.1"
|
||||
hexasphere = "15.0"
|
||||
ddsfile = { version = "0.5.2", optional = true }
|
||||
ktx2 = { version = "0.3.0", optional = true }
|
||||
# For ktx2 supercompression
|
||||
flate2 = { version = "1.0.22", optional = true }
|
||||
ruzstd = { version = "0.7.0", optional = true }
|
||||
# For transcoding of UASTC/ETC1S universal formats, and for .basis file support
|
||||
basis-universal = { version = "0.3.0", optional = true }
|
||||
encase = { version = "0.10", features = ["glam"] }
|
||||
|
|
|
@ -84,7 +84,7 @@ use world_sync::{
|
|||
use crate::gpu_readback::GpuReadbackPlugin;
|
||||
use crate::{
|
||||
camera::CameraPlugin,
|
||||
mesh::{morph::MorphPlugin, MeshPlugin, RenderMesh},
|
||||
mesh::{MeshPlugin, MorphPlugin, RenderMesh},
|
||||
render_asset::prepare_assets,
|
||||
render_resource::{PipelineCache, Shader, ShaderLoader},
|
||||
renderer::{render_system, RenderInstance, WgpuWrapper},
|
||||
|
|
|
@ -1,23 +1,33 @@
|
|||
#[allow(clippy::module_inception)]
|
||||
mod mesh;
|
||||
|
||||
use bevy_hierarchy::Children;
|
||||
use bevy_math::Vec3;
|
||||
pub use bevy_mesh::*;
|
||||
use morph::{MeshMorphWeights, MorphWeights};
|
||||
pub mod allocator;
|
||||
mod components;
|
||||
pub mod morph;
|
||||
pub mod primitives;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use crate::{
|
||||
primitives::Aabb,
|
||||
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
|
||||
render_resource::TextureView,
|
||||
texture::GpuImage,
|
||||
RenderApp,
|
||||
};
|
||||
use allocator::MeshAllocatorPlugin;
|
||||
use bevy_utils::HashSet;
|
||||
use bevy_app::{App, Plugin, PostUpdate};
|
||||
use bevy_asset::{AssetApp, RenderAssetUsages};
|
||||
use bevy_ecs::{
|
||||
entity::Entity,
|
||||
query::{Changed, With},
|
||||
system::Query,
|
||||
};
|
||||
use bevy_ecs::{
|
||||
query::Without,
|
||||
system::{
|
||||
lifetimeless::{SRes, SResMut},
|
||||
SystemParamItem,
|
||||
},
|
||||
};
|
||||
pub use components::{Mesh2d, Mesh3d};
|
||||
use core::hash::{Hash, Hasher};
|
||||
pub use mesh::*;
|
||||
pub use primitives::*;
|
||||
|
||||
use crate::{render_asset::RenderAssetPlugin, texture::GpuImage, RenderApp};
|
||||
use bevy_app::{App, Plugin};
|
||||
use bevy_asset::AssetApp;
|
||||
use bevy_ecs::{entity::Entity, system::Resource};
|
||||
use wgpu::IndexFormat;
|
||||
|
||||
/// Adds the [`Mesh`] as an asset and makes sure that they are extracted and prepared for the GPU.
|
||||
pub struct MeshPlugin;
|
||||
|
@ -42,51 +52,158 @@ impl Plugin for MeshPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
/// Describes the layout of the mesh vertices in GPU memory.
|
||||
/// [Inherit weights](inherit_weights) from glTF mesh parent entity to direct
|
||||
/// bevy mesh child entities (ie: glTF primitive).
|
||||
pub struct MorphPlugin;
|
||||
impl Plugin for MorphPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.register_type::<MorphWeights>()
|
||||
.register_type::<MeshMorphWeights>()
|
||||
.add_systems(PostUpdate, inherit_weights);
|
||||
}
|
||||
}
|
||||
|
||||
/// Bevy meshes are gltf primitives, [`MorphWeights`] on the bevy node entity
|
||||
/// should be inherited by children meshes.
|
||||
///
|
||||
/// 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(),
|
||||
)
|
||||
/// Only direct children are updated, to fulfill the expectations of glTF spec.
|
||||
pub fn inherit_weights(
|
||||
morph_nodes: Query<(&Children, &MorphWeights), (Without<Mesh3d>, Changed<MorphWeights>)>,
|
||||
mut morph_primitives: Query<&mut MeshMorphWeights, With<Mesh3d>>,
|
||||
) {
|
||||
for (children, parent_weights) in &morph_nodes {
|
||||
let mut iter = morph_primitives.iter_many_mut(children);
|
||||
while let Some(mut child_weight) = iter.fetch_next() {
|
||||
child_weight.clear_weights();
|
||||
child_weight.extend_weights(parent_weights.weights());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for MeshVertexBufferLayoutRef {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Arc::ptr_eq(&self.0, &other.0)
|
||||
pub trait MeshAabb {
|
||||
/// Compute the Axis-Aligned Bounding Box of the mesh vertices in model space
|
||||
///
|
||||
/// Returns `None` if `self` doesn't have [`Mesh::ATTRIBUTE_POSITION`] of
|
||||
/// type [`VertexAttributeValues::Float32x3`], or if `self` doesn't have any vertices.
|
||||
fn compute_aabb(&self) -> Option<Aabb>;
|
||||
}
|
||||
|
||||
impl MeshAabb for Mesh {
|
||||
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)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for MeshVertexBufferLayoutRef {}
|
||||
/// The render world representation of a [`Mesh`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RenderMesh {
|
||||
/// The number of vertices in the mesh.
|
||||
pub vertex_count: u32,
|
||||
|
||||
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);
|
||||
/// Morph targets for the mesh, if present.
|
||||
pub morph_targets: Option<TextureView>,
|
||||
|
||||
/// Information about the mesh data buffers, including whether the mesh uses
|
||||
/// indices or not.
|
||||
pub buffer_info: RenderMeshBufferInfo,
|
||||
|
||||
/// Precomputed pipeline key bits for this mesh.
|
||||
pub key_bits: BaseMeshPipelineKey,
|
||||
|
||||
/// A reference to the vertex buffer layout.
|
||||
///
|
||||
/// Combined with [`RenderMesh::buffer_info`], this specifies the complete
|
||||
/// layout of the buffers associated with this mesh.
|
||||
pub layout: MeshVertexBufferLayoutRef,
|
||||
}
|
||||
|
||||
impl RenderMesh {
|
||||
/// Returns the primitive topology of this mesh (triangles, triangle strips,
|
||||
/// etc.)
|
||||
#[inline]
|
||||
pub fn primitive_topology(&self) -> PrimitiveTopology {
|
||||
self.key_bits.primitive_topology()
|
||||
}
|
||||
}
|
||||
|
||||
/// The index/vertex buffer info of a [`RenderMesh`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RenderMeshBufferInfo {
|
||||
Indexed {
|
||||
count: u32,
|
||||
index_format: IndexFormat,
|
||||
},
|
||||
NonIndexed,
|
||||
}
|
||||
|
||||
impl RenderAsset for RenderMesh {
|
||||
type SourceAsset = Mesh;
|
||||
type Param = (
|
||||
SRes<RenderAssets<GpuImage>>,
|
||||
SResMut<MeshVertexBufferLayouts>,
|
||||
);
|
||||
|
||||
#[inline]
|
||||
fn asset_usage(mesh: &Self::SourceAsset) -> RenderAssetUsages {
|
||||
mesh.asset_usage
|
||||
}
|
||||
|
||||
fn byte_len(mesh: &Self::SourceAsset) -> Option<usize> {
|
||||
let mut vertex_size = 0;
|
||||
for attribute_data in mesh.attributes() {
|
||||
let vertex_format = attribute_data.0.format;
|
||||
vertex_size += vertex_format.get_size() as usize;
|
||||
}
|
||||
|
||||
let vertex_count = mesh.count_vertices();
|
||||
let index_bytes = mesh.get_index_buffer_bytes().map(<[_]>::len).unwrap_or(0);
|
||||
Some(vertex_size * vertex_count + index_bytes)
|
||||
}
|
||||
|
||||
/// Converts the extracted mesh into a [`RenderMesh`].
|
||||
fn prepare_asset(
|
||||
mesh: Self::SourceAsset,
|
||||
(images, ref mut mesh_vertex_buffer_layouts): &mut SystemParamItem<Self::Param>,
|
||||
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
|
||||
let morph_targets = match mesh.morph_targets() {
|
||||
Some(mt) => {
|
||||
let Some(target_image) = images.get(mt) else {
|
||||
return Err(PrepareAssetError::RetryNextUpdate(mesh));
|
||||
};
|
||||
Some(target_image.texture_view.clone())
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let buffer_info = match mesh.indices() {
|
||||
Some(indices) => RenderMeshBufferInfo::Indexed {
|
||||
count: indices.len() as u32,
|
||||
index_format: indices.into(),
|
||||
},
|
||||
None => RenderMeshBufferInfo::NonIndexed,
|
||||
};
|
||||
|
||||
let mesh_vertex_buffer_layout =
|
||||
mesh.get_mesh_vertex_buffer_layout(mesh_vertex_buffer_layouts);
|
||||
|
||||
let mut key_bits = BaseMeshPipelineKey::from_primitive_topology(mesh.primitive_topology());
|
||||
key_bits.set(
|
||||
BaseMeshPipelineKey::MORPH_TARGETS,
|
||||
mesh.morph_targets().is_some(),
|
||||
);
|
||||
|
||||
Ok(RenderMesh {
|
||||
vertex_count: mesh.count_vertices() as u32,
|
||||
buffer_info,
|
||||
key_bits,
|
||||
layout: mesh_vertex_buffer_layout,
|
||||
morph_targets,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@ pub use wgpu::{
|
|||
VertexStepMode, COPY_BUFFER_ALIGNMENT,
|
||||
};
|
||||
|
||||
pub use crate::mesh::VertexBufferLayout;
|
||||
|
||||
pub mod encase {
|
||||
pub use bevy_encase_derive::ShaderType;
|
||||
pub use encase::*;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use super::ShaderDefVal;
|
||||
use crate::mesh::VertexBufferLayout;
|
||||
use crate::renderer::WgpuWrapper;
|
||||
use crate::{
|
||||
define_atomic_id,
|
||||
|
@ -9,8 +10,7 @@ use alloc::sync::Arc;
|
|||
use bevy_asset::Handle;
|
||||
use core::ops::Deref;
|
||||
use wgpu::{
|
||||
BufferAddress, ColorTargetState, DepthStencilState, MultisampleState, PrimitiveState,
|
||||
PushConstantRange, VertexAttribute, VertexFormat, VertexStepMode,
|
||||
ColorTargetState, DepthStencilState, MultisampleState, PrimitiveState, PushConstantRange,
|
||||
};
|
||||
|
||||
define_atomic_id!(RenderPipelineId);
|
||||
|
@ -122,54 +122,6 @@ pub struct VertexState {
|
|||
pub buffers: Vec<VertexBufferLayout>,
|
||||
}
|
||||
|
||||
/// 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 fragment process in a render pipeline.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct FragmentState {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::{
|
||||
mesh::{MeshVertexBufferLayoutRef, MissingVertexAttributeError},
|
||||
mesh::{MeshVertexBufferLayoutRef, MissingVertexAttributeError, VertexBufferLayout},
|
||||
render_resource::{
|
||||
CachedComputePipelineId, CachedRenderPipelineId, ComputePipelineDescriptor, PipelineCache,
|
||||
RenderPipelineDescriptor, VertexBufferLayout,
|
||||
RenderPipelineDescriptor,
|
||||
},
|
||||
};
|
||||
use bevy_ecs::system::Resource;
|
||||
|
|
|
@ -19,7 +19,7 @@ use bevy_utils::{Parallel, TypeIdMap};
|
|||
|
||||
use crate::{
|
||||
camera::{Camera, CameraProjection},
|
||||
mesh::{Mesh, Mesh3d},
|
||||
mesh::{Mesh, Mesh3d, MeshAabb},
|
||||
primitives::{Aabb, Frustum, Sphere},
|
||||
};
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ use bevy_core_pipeline::core_2d::Transparent2d;
|
|||
use bevy_ecs::{prelude::*, query::QueryItem};
|
||||
use bevy_render::{
|
||||
extract_component::{ExtractComponent, ExtractComponentPlugin},
|
||||
mesh::{Mesh, Mesh2d},
|
||||
mesh::{Mesh, Mesh2d, MeshAabb},
|
||||
primitives::Aabb,
|
||||
render_phase::AddRenderCommand,
|
||||
render_resource::{Shader, SpecializedRenderPipelines},
|
||||
|
|
Loading…
Reference in a new issue