Skip to content

Commit 64b5322

Browse files
committed
Load morph targets from gltf
1 parent dff071c commit 64b5322

36 files changed

+1505
-187
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ anyhow = "1.0.4"
236236
rand = "0.8.0"
237237
ron = "0.8.0"
238238
serde = { version = "1", features = ["derive"] }
239+
serde_json = "1"
239240
bytemuck = "1.7"
240241
# Needed to poll Task examples
241242
futures-lite = "1.11.3"

crates/bevy_animation/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ bevy_asset = { path = "../bevy_asset", version = "0.11.0-dev" }
1515
bevy_core = { path = "../bevy_core", version = "0.11.0-dev" }
1616
bevy_math = { path = "../bevy_math", version = "0.11.0-dev" }
1717
bevy_reflect = { path = "../bevy_reflect", version = "0.11.0-dev", features = ["bevy"] }
18+
bevy_render = { path = "../bevy_render", version = "0.11.0-dev" }
1819
bevy_time = { path = "../bevy_time", version = "0.11.0-dev" }
1920
bevy_utils = { path = "../bevy_utils", version = "0.11.0-dev" }
2021
bevy_ecs = { path = "../bevy_ecs", version = "0.11.0-dev" }

crates/bevy_animation/src/lib.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use bevy_ecs::prelude::*;
1313
use bevy_hierarchy::{Children, Parent};
1414
use bevy_math::{Quat, Vec3};
1515
use bevy_reflect::{FromReflect, Reflect, TypeUuid};
16+
use bevy_render::mesh::morph::MorphWeights;
1617
use bevy_time::Time;
1718
use bevy_transform::{prelude::Transform, TransformSystem};
1819
use bevy_utils::{tracing::warn, HashMap};
@@ -34,6 +35,11 @@ pub enum Keyframes {
3435
Translation(Vec<Vec3>),
3536
/// Keyframes for scale.
3637
Scale(Vec<Vec3>),
38+
/// Keyframes for morph target weights.
39+
///
40+
/// Note that in `.0`, each contiguous `target_count` values is a single
41+
/// keyframe representing the weight values at given keyframe.
42+
Weights(Vec<f32>),
3743
}
3844

3945
/// Describes how an attribute of a [`Transform`] should be animated.
@@ -270,7 +276,7 @@ impl AnimationPlayer {
270276
}
271277
}
272278

273-
fn find_bone(
279+
fn entity_from_path(
274280
root: Entity,
275281
path: &EntityPath,
276282
children: &Query<&Children>,
@@ -336,12 +342,14 @@ fn verify_no_ancestor_player(
336342

337343
/// System that will play all animations, using any entity with a [`AnimationPlayer`]
338344
/// and a [`Handle<AnimationClip>`] as an animation root
345+
#[allow(clippy::too_many_arguments)]
339346
pub fn animation_player(
340347
time: Res<Time>,
341348
animations: Res<Assets<AnimationClip>>,
342349
children: Query<&Children>,
343350
names: Query<&Name>,
344351
transforms: Query<&mut Transform>,
352+
morphs: Query<&mut MorphWeights>,
345353
parents: Query<(Option<With<AnimationPlayer>>, Option<&Parent>)>,
346354
mut animation_players: Query<(Entity, Option<&Parent>, &mut AnimationPlayer)>,
347355
) {
@@ -356,6 +364,7 @@ pub fn animation_player(
356364
&animations,
357365
&names,
358366
&transforms,
367+
&morphs,
359368
maybe_parent,
360369
&parents,
361370
&children,
@@ -371,6 +380,7 @@ fn run_animation_player(
371380
animations: &Assets<AnimationClip>,
372381
names: &Query<&Name>,
373382
transforms: &Query<&mut Transform>,
383+
morphs: &Query<&mut MorphWeights>,
374384
maybe_parent: Option<&Parent>,
375385
parents: &Query<(Option<With<AnimationPlayer>>, Option<&Parent>)>,
376386
children: &Query<&Children>,
@@ -392,6 +402,7 @@ fn run_animation_player(
392402
animations,
393403
names,
394404
transforms,
405+
morphs,
395406
maybe_parent,
396407
parents,
397408
children,
@@ -413,13 +424,39 @@ fn run_animation_player(
413424
animations,
414425
names,
415426
transforms,
427+
morphs,
416428
maybe_parent,
417429
parents,
418430
children,
419431
);
420432
}
421433
}
422434

435+
/// # Safety
436+
///
437+
/// No other reference to a `MorphWeights` accessible through `morphs` should
438+
/// exist when this function is called.
439+
unsafe fn morph_primitives(
440+
children: &Children,
441+
morphs: &Query<&mut MorphWeights>,
442+
weight: f32,
443+
keyframes: &[f32],
444+
offset: usize,
445+
) {
446+
for child in children {
447+
// SAFETY: ensured by function's safety invariants.
448+
let Ok(mut morph) = (unsafe { morphs.get_unchecked(*child) }) else { continue };
449+
let target_count = morph.weights().len();
450+
let start = target_count * offset;
451+
let end = target_count * (offset + 1);
452+
let zipped = morph.weights_mut().iter_mut().zip(&keyframes[start..end]);
453+
for (morph_weight, keyframe) in zipped {
454+
let minus_weight = 1.0 - weight;
455+
*morph_weight = (*morph_weight * minus_weight) + (keyframe * weight);
456+
}
457+
}
458+
}
459+
423460
#[allow(clippy::too_many_arguments)]
424461
fn apply_animation(
425462
weight: f32,
@@ -430,6 +467,7 @@ fn apply_animation(
430467
animations: &Assets<AnimationClip>,
431468
names: &Query<&Name>,
432469
transforms: &Query<&mut Transform>,
470+
morphs: &Query<&mut MorphWeights>,
433471
maybe_parent: Option<&Parent>,
434472
parents: &Query<(Option<With<AnimationPlayer>>, Option<&Parent>)>,
435473
children: &Query<&Children>,
@@ -456,7 +494,7 @@ fn apply_animation(
456494
for (path, bone_id) in &animation_clip.paths {
457495
let cached_path = &mut animation.path_cache[*bone_id];
458496
let curves = animation_clip.get_curves(*bone_id).unwrap();
459-
let Some(target) = find_bone(root, path, children, names, cached_path) else { continue };
497+
let Some(target) = entity_from_path(root, path, children, names, cached_path) else { continue };
460498
// SAFETY: The verify_no_ancestor_player check above ensures that two animation players cannot alias
461499
// any of their descendant Transforms.
462500
//
@@ -484,6 +522,13 @@ fn apply_animation(
484522
Keyframes::Scale(keyframes) => {
485523
transform.scale = transform.scale.lerp(keyframes[0], weight);
486524
}
525+
Keyframes::Weights(keyframes) => {
526+
let Ok(children) = children.get(target) else { continue; };
527+
// SAFETY: Same as above
528+
unsafe {
529+
morph_primitives(children, morphs, weight, keyframes, 0);
530+
};
531+
}
487532
}
488533
continue;
489534
}
@@ -529,6 +574,13 @@ fn apply_animation(
529574
let result = scale_start.lerp(scale_end, lerp);
530575
transform.scale = transform.scale.lerp(result, weight);
531576
}
577+
Keyframes::Weights(keyframes) => {
578+
let Ok(children) = children.get(target) else { continue; };
579+
// SAFETY: Same as above
580+
unsafe {
581+
morph_primitives(children, morphs, weight, keyframes, step_start)
582+
};
583+
}
532584
}
533585
}
534586
}

crates/bevy_gltf/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,8 @@ gltf = { version = "1.0.0", default-features = false, features = [
3737
] }
3838
thiserror = "1.0"
3939
anyhow = "1.0.4"
40+
# For Zeroable and Pod traits used to generate morph target buffer
41+
bytemuck = { version = "1.5", features = ["derive"] }
42+
4043
base64 = "0.13.0"
4144
percent-encoding = "2.1"

crates/bevy_gltf/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ impl Plugin for GltfPlugin {
2323
fn build(&self, app: &mut App) {
2424
app.init_asset_loader::<GltfLoader>()
2525
.register_type::<GltfExtras>()
26+
.register_type::<GltfMeshExtras>()
2627
.add_asset::<Gltf>()
2728
.add_asset::<GltfNode>()
2829
.add_asset::<GltfPrimitive>()
@@ -84,3 +85,11 @@ pub struct GltfPrimitive {
8485
pub struct GltfExtras {
8586
pub value: String,
8687
}
88+
/// Gltf `extras` field present in the gltf `mesh` of this node.
89+
///
90+
/// This allows accessing the `extras` field of a mesh as a component.
91+
#[derive(Clone, Debug, Reflect, Default, Component)]
92+
#[reflect(Component)]
93+
pub struct GltfMeshExtras {
94+
pub value: String,
95+
}

crates/bevy_gltf/src/loader.rs

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mod morph;
2+
13
use anyhow::Result;
24
use bevy_asset::{
35
AssetIoError, AssetLoader, AssetPath, BoxedFuture, Handle, LoadContext, LoadedAsset,
@@ -16,6 +18,7 @@ use bevy_render::{
1618
camera::{Camera, OrthographicProjection, PerspectiveProjection, Projection, ScalingMode},
1719
color::Color,
1820
mesh::{
21+
morph::MorphWeights,
1922
skinning::{SkinnedMesh, SkinnedMeshInverseBindposes},
2023
Indices, Mesh, VertexAttributeValues,
2124
},
@@ -64,6 +67,8 @@ pub enum GltfError {
6467
MissingAnimationSampler(usize),
6568
#[error("failed to generate tangents: {0}")]
6669
GenerateTangentsError(#[from] bevy_render::mesh::GenerateTangentsError),
70+
#[error("failed to generate morph targets: {0}")]
71+
MorphTarget(#[from] bevy_render::mesh::morph::MorphTargetsGenerationError),
6772
}
6873

6974
/// Loads glTF files with all of their data as their corresponding bevy representations.
@@ -146,6 +151,7 @@ async fn load_gltf<'a, 'b>(
146151

147152
#[cfg(feature = "bevy_animation")]
148153
let (animations, named_animations, animation_roots) = {
154+
use gltf::animation::util::ReadOutputs;
149155
let mut animations = vec![];
150156
let mut named_animations = HashMap::default();
151157
let mut animation_roots = HashSet::default();
@@ -176,20 +182,17 @@ async fn load_gltf<'a, 'b>(
176182

177183
let keyframes = if let Some(outputs) = reader.read_outputs() {
178184
match outputs {
179-
gltf::animation::util::ReadOutputs::Translations(tr) => {
185+
ReadOutputs::Translations(tr) => {
180186
bevy_animation::Keyframes::Translation(tr.map(Vec3::from).collect())
181187
}
182-
gltf::animation::util::ReadOutputs::Rotations(rots) => {
183-
bevy_animation::Keyframes::Rotation(
184-
rots.into_f32().map(bevy_math::Quat::from_array).collect(),
185-
)
186-
}
187-
gltf::animation::util::ReadOutputs::Scales(scale) => {
188+
ReadOutputs::Rotations(rots) => bevy_animation::Keyframes::Rotation(
189+
rots.into_f32().map(bevy_math::Quat::from_array).collect(),
190+
),
191+
ReadOutputs::Scales(scale) => {
188192
bevy_animation::Keyframes::Scale(scale.map(Vec3::from).collect())
189193
}
190-
gltf::animation::util::ReadOutputs::MorphTargetWeights(_) => {
191-
warn!("Morph animation property not yet supported");
192-
continue;
194+
ReadOutputs::MorphTargetWeights(weights) => {
195+
bevy_animation::Keyframes::Weights(weights.into_f32().collect())
193196
}
194197
}
195198
} else {
@@ -233,6 +236,7 @@ async fn load_gltf<'a, 'b>(
233236
let mut primitives = vec![];
234237
for primitive in mesh.primitives() {
235238
let primitive_label = primitive_label(&mesh, &primitive);
239+
let morph_targets_label = morph_targets_label(&mesh, &primitive);
236240
let reader = primitive.reader(|buffer| Some(&buffer_data[buffer.index()]));
237241
let primitive_topology = get_primitive_topology(primitive.mode())?;
238242

@@ -282,6 +286,15 @@ async fn load_gltf<'a, 'b>(
282286
mesh.set_indices(Some(Indices::U32(indices.into_u32().collect())));
283287
};
284288

289+
let target_count = reader.read_morph_targets().len();
290+
if target_count != 0 {
291+
let walker = morph::PrimitiveMorphTargets::new(&reader);
292+
let store = |image| {
293+
load_context.set_labeled_asset(&morph_targets_label, LoadedAsset::new(image))
294+
};
295+
mesh.set_morph_targets(walker, store)?;
296+
}
297+
285298
if mesh.attribute(Mesh::ATTRIBUTE_NORMAL).is_none()
286299
&& matches!(mesh.primitive_topology(), PrimitiveTopology::TriangleList)
287300
{
@@ -767,6 +780,16 @@ fn load_node(
767780
// Map node index to entity
768781
node_index_to_entity_map.insert(gltf_node.index(), node.id());
769782

783+
if let Some(mesh) = gltf_node.mesh() {
784+
if let Some(extras) = mesh.extras().as_ref() {
785+
node.insert(super::GltfMeshExtras {
786+
value: extras.get().to_string(),
787+
});
788+
}
789+
if let Some(weights) = mesh.weights() {
790+
node.insert(MorphWeights::new(weights.to_vec()));
791+
}
792+
};
770793
node.with_children(|parent| {
771794
if let Some(mesh) = gltf_node.mesh() {
772795
// append primitives
@@ -788,27 +811,35 @@ fn load_node(
788811
let material_asset_path =
789812
AssetPath::new_ref(load_context.path(), Some(&material_label));
790813

791-
let mut mesh_entity = parent.spawn(PbrBundle {
814+
let mut primitive_entity = parent.spawn(PbrBundle {
792815
mesh: load_context.get_handle(mesh_asset_path),
793816
material: load_context.get_handle(material_asset_path),
794817
..Default::default()
795818
});
796-
mesh_entity.insert(Aabb::from_min_max(
819+
let target_count = primitive.morph_targets().len();
820+
if target_count != 0 {
821+
let weights = match mesh.weights() {
822+
Some(weights) => weights.to_vec(),
823+
None => vec![0.0; target_count],
824+
};
825+
primitive_entity.insert(MorphWeights::new(weights));
826+
}
827+
primitive_entity.insert(Aabb::from_min_max(
797828
Vec3::from_slice(&bounds.min),
798829
Vec3::from_slice(&bounds.max),
799830
));
800831

801832
if let Some(extras) = primitive.extras() {
802-
mesh_entity.insert(super::GltfExtras {
833+
primitive_entity.insert(super::GltfExtras {
803834
value: extras.get().to_string(),
804835
});
805836
}
806837
if let Some(name) = mesh.name() {
807-
mesh_entity.insert(Name::new(name.to_string()));
838+
primitive_entity.insert(Name::new(name.to_string()));
808839
}
809840
// Mark for adding skinned mesh
810841
if let Some(skin) = gltf_node.skin() {
811-
entity_to_skin_index_map.insert(mesh_entity.id(), skin.index());
842+
entity_to_skin_index_map.insert(primitive_entity.id(), skin.index());
812843
}
813844
}
814845
}
@@ -921,6 +952,15 @@ fn primitive_label(mesh: &gltf::Mesh, primitive: &Primitive) -> String {
921952
format!("Mesh{}/Primitive{}", mesh.index(), primitive.index())
922953
}
923954

955+
/// Returns the label for the `mesh` and `primitive`.
956+
fn morph_targets_label(mesh: &gltf::Mesh, primitive: &Primitive) -> String {
957+
format!(
958+
"Mesh{}/Primitive{}/MorphTargets",
959+
mesh.index(),
960+
primitive.index()
961+
)
962+
}
963+
924964
/// Returns the label for the `material`.
925965
fn material_label(material: &gltf::Material) -> String {
926966
if let Some(index) = material.index() {

0 commit comments

Comments
 (0)