Skip to content

Commit d657f73

Browse files
committed
Load morph targets from gltf
1 parent e22572d commit d657f73

36 files changed

+1502
-187
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ anyhow = "1.0.4"
226226
rand = "0.8.0"
227227
ron = "0.8.0"
228228
serde = { version = "1", features = ["derive"] }
229+
serde_json = "1"
229230
bytemuck = "1.7"
230231
# Needed to poll Task examples
231232
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
@@ -12,6 +12,7 @@ use bevy_ecs::prelude::*;
1212
use bevy_hierarchy::{Children, Parent};
1313
use bevy_math::{Quat, Vec3};
1414
use bevy_reflect::{FromReflect, Reflect, TypeUuid};
15+
use bevy_render::mesh::morph::MorphWeights;
1516
use bevy_time::Time;
1617
use bevy_transform::{prelude::Transform, TransformSystem};
1718
use bevy_utils::{tracing::warn, HashMap};
@@ -33,6 +34,11 @@ pub enum Keyframes {
3334
Translation(Vec<Vec3>),
3435
/// Keyframes for scale.
3536
Scale(Vec<Vec3>),
37+
/// Keyframes for morph target weights.
38+
///
39+
/// Note that in `.0`, each contiguous `target_count` values is a single
40+
/// keyframe representing the weight values at given keyframe.
41+
Weights(Vec<f32>),
3642
}
3743

3844
/// Describes how an attribute of a [`Transform`] should be animated.
@@ -269,7 +275,7 @@ impl AnimationPlayer {
269275
}
270276
}
271277

272-
fn find_bone(
278+
fn entity_from_path(
273279
root: Entity,
274280
path: &EntityPath,
275281
children: &Query<&Children>,
@@ -335,12 +341,14 @@ fn verify_no_ancestor_player(
335341

336342
/// System that will play all animations, using any entity with a [`AnimationPlayer`]
337343
/// and a [`Handle<AnimationClip>`] as an animation root
344+
#[allow(clippy::too_many_arguments)]
338345
pub fn animation_player(
339346
time: Res<Time>,
340347
animations: Res<Assets<AnimationClip>>,
341348
children: Query<&Children>,
342349
names: Query<&Name>,
343350
transforms: Query<&mut Transform>,
351+
morphs: Query<&mut MorphWeights>,
344352
parents: Query<(Option<With<AnimationPlayer>>, Option<&Parent>)>,
345353
mut animation_players: Query<(Entity, Option<&Parent>, &mut AnimationPlayer)>,
346354
) {
@@ -355,6 +363,7 @@ pub fn animation_player(
355363
&animations,
356364
&names,
357365
&transforms,
366+
&morphs,
358367
maybe_parent,
359368
&parents,
360369
&children,
@@ -370,6 +379,7 @@ fn run_animation_player(
370379
animations: &Assets<AnimationClip>,
371380
names: &Query<&Name>,
372381
transforms: &Query<&mut Transform>,
382+
morphs: &Query<&mut MorphWeights>,
373383
maybe_parent: Option<&Parent>,
374384
parents: &Query<(Option<With<AnimationPlayer>>, Option<&Parent>)>,
375385
children: &Query<&Children>,
@@ -391,6 +401,7 @@ fn run_animation_player(
391401
animations,
392402
names,
393403
transforms,
404+
morphs,
394405
maybe_parent,
395406
parents,
396407
children,
@@ -412,13 +423,39 @@ fn run_animation_player(
412423
animations,
413424
names,
414425
transforms,
426+
morphs,
415427
maybe_parent,
416428
parents,
417429
children,
418430
);
419431
}
420432
}
421433

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

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
@@ -21,6 +21,7 @@ impl Plugin for GltfPlugin {
2121
fn build(&self, app: &mut App) {
2222
app.init_asset_loader::<GltfLoader>()
2323
.register_type::<GltfExtras>()
24+
.register_type::<GltfMeshExtras>()
2425
.add_asset::<Gltf>()
2526
.add_asset::<GltfNode>()
2627
.add_asset::<GltfPrimitive>()
@@ -82,3 +83,11 @@ pub struct GltfPrimitive {
8283
pub struct GltfExtras {
8384
pub value: String,
8485
}
86+
/// Gltf `extras` field present in the gltf `mesh` of this node.
87+
///
88+
/// This allows accessing the `extras` field of a mesh as a component.
89+
#[derive(Clone, Debug, Reflect, Default, Component)]
90+
#[reflect(Component)]
91+
pub struct GltfMeshExtras {
92+
pub value: String,
93+
}

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)