mirror of
https://github.com/bevyengine/bevy.git
synced 2026-05-06 06:06:42 -04:00
bevy_mesh optional morph (#21389)
# Objective - make morph support optional on bevy_mesh - unblock #20742 - another step towards #20867 ## Solution - feature ## Testing - morph targets example works
This commit is contained in:
@@ -166,6 +166,7 @@ default = [
|
||||
"default_font",
|
||||
"hdr",
|
||||
"ktx2",
|
||||
"morph",
|
||||
"multi_threaded",
|
||||
"png",
|
||||
"reflect_auto_register",
|
||||
@@ -441,6 +442,9 @@ bevy_ci_testing = ["bevy_internal/bevy_ci_testing"]
|
||||
# Enable animation support, and glTF animation loading
|
||||
animation = ["bevy_internal/animation", "bevy_animation"]
|
||||
|
||||
# Enables support for morph target weights in bevy_mesh
|
||||
morph = ["bevy_internal/morph"]
|
||||
|
||||
# Enable using a shared stdlib for cxx on Android
|
||||
android_shared_stdcxx = ["bevy_internal/android_shared_stdcxx"]
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@ bevy_asset = { path = "../bevy_asset", version = "0.18.0-dev" }
|
||||
bevy_color = { path = "../bevy_color", version = "0.18.0-dev" }
|
||||
bevy_derive = { path = "../bevy_derive", version = "0.18.0-dev" }
|
||||
bevy_math = { path = "../bevy_math", version = "0.18.0-dev" }
|
||||
bevy_mesh = { path = "../bevy_mesh", version = "0.18.0-dev" }
|
||||
bevy_mesh = { path = "../bevy_mesh", version = "0.18.0-dev", features = [
|
||||
"morph",
|
||||
] }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.18.0-dev", features = [
|
||||
"petgraph",
|
||||
] }
|
||||
|
||||
@@ -209,6 +209,9 @@ animation = [
|
||||
"bevy_gltf?/bevy_animation",
|
||||
]
|
||||
|
||||
# Enables support for morph target weights in bevy_mesh
|
||||
morph = ["bevy_mesh?/morph", "bevy_render?/morph"]
|
||||
|
||||
bevy_shader = ["dep:bevy_shader"]
|
||||
bevy_image = ["dep:bevy_image", "bevy_color", "bevy_asset"]
|
||||
bevy_sprite = ["dep:bevy_sprite", "bevy_camera"]
|
||||
|
||||
@@ -12,7 +12,7 @@ keywords = ["bevy"]
|
||||
# bevy
|
||||
bevy_app = { path = "../bevy_app", version = "0.18.0-dev" }
|
||||
bevy_asset = { path = "../bevy_asset", version = "0.18.0-dev" }
|
||||
bevy_image = { path = "../bevy_image", version = "0.18.0-dev" }
|
||||
bevy_image = { path = "../bevy_image", version = "0.18.0-dev", optional = true }
|
||||
bevy_math = { path = "../bevy_math", version = "0.18.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.18.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.18.0-dev" }
|
||||
@@ -43,6 +43,7 @@ serde_json = "1.0.140"
|
||||
default = []
|
||||
## Adds serialization support through `serde`.
|
||||
serialize = ["dep:serde", "wgpu-types/serde"]
|
||||
morph = ["dep:bevy_image"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -8,6 +8,7 @@ mod conversions;
|
||||
mod index;
|
||||
mod mesh;
|
||||
mod mikktspace;
|
||||
#[cfg(feature = "morph")]
|
||||
pub mod morph;
|
||||
pub mod primitives;
|
||||
pub mod skinning;
|
||||
@@ -28,10 +29,10 @@ pub use wgpu_types::VertexFormat;
|
||||
///
|
||||
/// This includes the most common types in this crate, re-exported for your convenience.
|
||||
pub mod prelude {
|
||||
#[cfg(feature = "morph")]
|
||||
pub use crate::morph::MorphWeights;
|
||||
#[doc(hidden)]
|
||||
pub use crate::{
|
||||
morph::MorphWeights, primitives::MeshBuilder, primitives::Meshable, Mesh, Mesh2d, Mesh3d,
|
||||
};
|
||||
pub use crate::{primitives::MeshBuilder, primitives::Meshable, Mesh, Mesh2d, Mesh3d};
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
|
||||
@@ -10,7 +10,10 @@ use super::{
|
||||
#[cfg(feature = "serialize")]
|
||||
use crate::SerializedMeshAttributeData;
|
||||
use alloc::collections::BTreeMap;
|
||||
use bevy_asset::{Asset, Handle, RenderAssetUsages};
|
||||
#[cfg(feature = "morph")]
|
||||
use bevy_asset::Handle;
|
||||
use bevy_asset::{Asset, RenderAssetUsages};
|
||||
#[cfg(feature = "morph")]
|
||||
use bevy_image::Image;
|
||||
use bevy_math::{primitives::Triangle3d, *};
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -127,7 +130,9 @@ pub struct Mesh {
|
||||
#[reflect(ignore, clone)]
|
||||
attributes: BTreeMap<MeshVertexAttributeId, MeshAttributeData>,
|
||||
indices: Option<Indices>,
|
||||
#[cfg(feature = "morph")]
|
||||
morph_targets: Option<Handle<Image>>,
|
||||
#[cfg(feature = "morph")]
|
||||
morph_target_names: Option<Vec<String>>,
|
||||
pub asset_usage: RenderAssetUsages,
|
||||
/// Whether or not to build a BLAS for use with `bevy_solari` raytracing.
|
||||
@@ -230,7 +235,9 @@ impl Mesh {
|
||||
primitive_topology,
|
||||
attributes: Default::default(),
|
||||
indices: None,
|
||||
#[cfg(feature = "morph")]
|
||||
morph_targets: None,
|
||||
#[cfg(feature = "morph")]
|
||||
morph_target_names: None,
|
||||
asset_usage,
|
||||
enable_raytracing: true,
|
||||
@@ -1231,55 +1238,6 @@ impl Mesh {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this mesh has morph targets.
|
||||
pub fn has_morph_targets(&self) -> bool {
|
||||
self.morph_targets.is_some()
|
||||
}
|
||||
|
||||
/// Set [morph targets] image for this mesh. This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.
|
||||
///
|
||||
/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
|
||||
pub fn set_morph_targets(&mut self, morph_targets: Handle<Image>) {
|
||||
self.morph_targets = Some(morph_targets);
|
||||
}
|
||||
|
||||
pub fn morph_targets(&self) -> Option<&Handle<Image>> {
|
||||
self.morph_targets.as_ref()
|
||||
}
|
||||
|
||||
/// Consumes the mesh and returns a mesh with the given [morph targets].
|
||||
///
|
||||
/// This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.
|
||||
///
|
||||
/// (Alternatively, you can use [`Mesh::set_morph_targets`] to mutate an existing mesh in-place)
|
||||
///
|
||||
/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
|
||||
#[must_use]
|
||||
pub fn with_morph_targets(mut self, morph_targets: Handle<Image>) -> Self {
|
||||
self.set_morph_targets(morph_targets);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the names of each morph target. This should correspond to the order of the morph targets in `set_morph_targets`.
|
||||
pub fn set_morph_target_names(&mut self, names: Vec<String>) {
|
||||
self.morph_target_names = Some(names);
|
||||
}
|
||||
|
||||
/// Consumes the mesh and returns a mesh with morph target names.
|
||||
/// Names should correspond to the order of the morph targets in `set_morph_targets`.
|
||||
///
|
||||
/// (Alternatively, you can use [`Mesh::set_morph_target_names`] to mutate an existing mesh in-place)
|
||||
#[must_use]
|
||||
pub fn with_morph_target_names(mut self, names: Vec<String>) -> Self {
|
||||
self.set_morph_target_names(names);
|
||||
self
|
||||
}
|
||||
|
||||
/// Gets a list of all morph target names, if they exist.
|
||||
pub fn morph_target_names(&self) -> Option<&[String]> {
|
||||
self.morph_target_names.as_deref()
|
||||
}
|
||||
|
||||
/// Normalize joint weights so they sum to 1.
|
||||
pub fn normalize_joint_weights(&mut self) {
|
||||
if let Some(joints) = self.attribute_mut(Self::ATTRIBUTE_JOINT_WEIGHT) {
|
||||
@@ -1404,6 +1362,58 @@ impl Mesh {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "morph")]
|
||||
impl Mesh {
|
||||
/// Whether this mesh has morph targets.
|
||||
pub fn has_morph_targets(&self) -> bool {
|
||||
self.morph_targets.is_some()
|
||||
}
|
||||
|
||||
/// Set [morph targets] image for this mesh. This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.
|
||||
///
|
||||
/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
|
||||
pub fn set_morph_targets(&mut self, morph_targets: Handle<Image>) {
|
||||
self.morph_targets = Some(morph_targets);
|
||||
}
|
||||
|
||||
pub fn morph_targets(&self) -> Option<&Handle<Image>> {
|
||||
self.morph_targets.as_ref()
|
||||
}
|
||||
|
||||
/// Consumes the mesh and returns a mesh with the given [morph targets].
|
||||
///
|
||||
/// This requires a "morph target image". See [`MorphTargetImage`](crate::morph::MorphTargetImage) for info.
|
||||
///
|
||||
/// (Alternatively, you can use [`Mesh::set_morph_targets`] to mutate an existing mesh in-place)
|
||||
///
|
||||
/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
|
||||
#[must_use]
|
||||
pub fn with_morph_targets(mut self, morph_targets: Handle<Image>) -> Self {
|
||||
self.set_morph_targets(morph_targets);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the names of each morph target. This should correspond to the order of the morph targets in `set_morph_targets`.
|
||||
pub fn set_morph_target_names(&mut self, names: Vec<String>) {
|
||||
self.morph_target_names = Some(names);
|
||||
}
|
||||
|
||||
/// Consumes the mesh and returns a mesh with morph target names.
|
||||
/// Names should correspond to the order of the morph targets in `set_morph_targets`.
|
||||
///
|
||||
/// (Alternatively, you can use [`Mesh::set_morph_target_names`] to mutate an existing mesh in-place)
|
||||
#[must_use]
|
||||
pub fn with_morph_target_names(mut self, names: Vec<String>) -> Self {
|
||||
self.set_morph_target_names(names);
|
||||
self
|
||||
}
|
||||
|
||||
/// Gets a list of all morph target names, if they exist.
|
||||
pub fn morph_target_names(&self) -> Option<&[String]> {
|
||||
self.morph_target_names.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Mul<Mesh> for Transform {
|
||||
type Output = Mesh;
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ decoupled_naga = ["bevy_shader/decoupled_naga"]
|
||||
|
||||
multi_threaded = ["bevy_tasks/multi_threaded"]
|
||||
|
||||
morph = ["bevy_mesh/morph"]
|
||||
|
||||
shader_format_spirv = ["bevy_shader/shader_format_spirv", "wgpu/spirv"]
|
||||
|
||||
# Enable SPIR-V shader passthrough
|
||||
|
||||
@@ -79,7 +79,7 @@ pub use extract_param::Extract;
|
||||
use crate::{
|
||||
camera::CameraPlugin,
|
||||
gpu_readback::GpuReadbackPlugin,
|
||||
mesh::{MeshRenderAssetPlugin, MorphPlugin, RenderMesh},
|
||||
mesh::{MeshRenderAssetPlugin, RenderMesh},
|
||||
render_asset::prepare_assets,
|
||||
render_resource::{init_empty_bind_group_layout, PipelineCache},
|
||||
renderer::{render_system, RenderAdapterInfo},
|
||||
@@ -368,7 +368,8 @@ impl Plugin for RenderPlugin {
|
||||
ViewPlugin,
|
||||
MeshRenderAssetPlugin,
|
||||
GlobalsPlugin,
|
||||
MorphPlugin,
|
||||
#[cfg(feature = "morph")]
|
||||
mesh::MorphPlugin,
|
||||
TexturePlugin,
|
||||
BatchingPlugin {
|
||||
debug_flags: self.debug_flags,
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
pub mod allocator;
|
||||
use crate::{
|
||||
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
|
||||
render_resource::TextureView,
|
||||
texture::GpuImage,
|
||||
RenderApp,
|
||||
};
|
||||
use allocator::MeshAllocatorPlugin;
|
||||
use bevy_app::{App, Plugin, PostUpdate};
|
||||
use bevy_app::{App, Plugin};
|
||||
use bevy_asset::{AssetId, RenderAssetUsages};
|
||||
use bevy_ecs::{
|
||||
prelude::*,
|
||||
@@ -15,6 +14,7 @@ use bevy_ecs::{
|
||||
SystemParamItem,
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "morph")]
|
||||
use bevy_mesh::morph::{MeshMorphWeights, MorphWeights};
|
||||
use bevy_mesh::*;
|
||||
use wgpu::IndexFormat;
|
||||
@@ -40,10 +40,15 @@ impl Plugin for MeshRenderAssetPlugin {
|
||||
|
||||
/// [Inherit weights](inherit_weights) from glTF mesh parent entity to direct
|
||||
/// bevy mesh child entities (ie: glTF primitive).
|
||||
#[cfg(feature = "morph")]
|
||||
pub struct MorphPlugin;
|
||||
#[cfg(feature = "morph")]
|
||||
impl Plugin for MorphPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(PostUpdate, inherit_weights.in_set(InheritWeightSystems));
|
||||
app.add_systems(
|
||||
bevy_app::PostUpdate,
|
||||
inherit_weights.in_set(InheritWeightSystems),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +56,7 @@ impl Plugin for MorphPlugin {
|
||||
/// should be inherited by children meshes.
|
||||
///
|
||||
/// Only direct children are updated, to fulfill the expectations of glTF spec.
|
||||
#[cfg(feature = "morph")]
|
||||
pub fn inherit_weights(
|
||||
morph_nodes: Query<(&Children, &MorphWeights), (Without<Mesh3d>, Changed<MorphWeights>)>,
|
||||
mut morph_primitives: Query<&mut MeshMorphWeights, With<Mesh3d>>,
|
||||
@@ -71,7 +77,8 @@ pub struct RenderMesh {
|
||||
pub vertex_count: u32,
|
||||
|
||||
/// Morph targets for the mesh, if present.
|
||||
pub morph_targets: Option<TextureView>,
|
||||
#[cfg(feature = "morph")]
|
||||
pub morph_targets: Option<crate::render_resource::TextureView>,
|
||||
|
||||
/// Information about the mesh data buffers, including whether the mesh uses
|
||||
/// indices or not.
|
||||
@@ -140,12 +147,13 @@ impl RenderAsset for RenderMesh {
|
||||
fn prepare_asset(
|
||||
mesh: Self::SourceAsset,
|
||||
_: AssetId<Self::SourceAsset>,
|
||||
(images, mesh_vertex_buffer_layouts): &mut SystemParamItem<Self::Param>,
|
||||
(_images, mesh_vertex_buffer_layouts): &mut SystemParamItem<Self::Param>,
|
||||
_: Option<&Self>,
|
||||
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
|
||||
#[cfg(feature = "morph")]
|
||||
let morph_targets = match mesh.morph_targets() {
|
||||
Some(mt) => {
|
||||
let Some(target_image) = images.get(mt) else {
|
||||
let Some(target_image) = _images.get(mt) else {
|
||||
return Err(PrepareAssetError::RetryNextUpdate(mesh));
|
||||
};
|
||||
Some(target_image.texture_view.clone())
|
||||
@@ -164,17 +172,20 @@ impl RenderAsset for RenderMesh {
|
||||
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(),
|
||||
);
|
||||
let key_bits = BaseMeshPipelineKey::from_primitive_topology(mesh.primitive_topology());
|
||||
#[cfg(feature = "morph")]
|
||||
let key_bits = if mesh.morph_targets().is_some() {
|
||||
key_bits | BaseMeshPipelineKey::MORPH_TARGETS
|
||||
} else {
|
||||
key_bits
|
||||
};
|
||||
|
||||
Ok(RenderMesh {
|
||||
vertex_count: mesh.count_vertices() as u32,
|
||||
buffer_info,
|
||||
key_bits,
|
||||
layout: mesh_vertex_buffer_layout,
|
||||
#[cfg(feature = "morph")]
|
||||
morph_targets,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ The default feature set enables most of the expected features of a game engine,
|
||||
|default_font|Include a default font, containing only ASCII characters, at the cost of a 20kB binary size increase|
|
||||
|hdr|HDR image format support|
|
||||
|ktx2|KTX2 compressed texture support|
|
||||
|morph|Enables support for morph target weights in bevy_mesh|
|
||||
|multi_threaded|Enables multithreaded parallelism in the engine. Disabling it forces all engine tasks to run on a single thread.|
|
||||
|png|PNG image format support|
|
||||
|reflect_auto_register|Enable automatic reflect registration|
|
||||
|
||||
Reference in New Issue
Block a user