From fa7a35404750c09342514d533e1d1cd25c1d60ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Nore=CC=81n?= Date: Mon, 4 Sep 2023 13:43:08 +0200 Subject: [PATCH 1/6] Remove ref counting on animation nodes --- docs/src/reference/animations.md | 46 +++--- guest/rust/api_core/src/animation.rs | 156 ++++++++++++------ .../examples/basics/skinmesh/src/client.rs | 36 ++-- .../games/afps/core/fpsanim/src/server.rs | 48 +++--- .../games/afps/mods/zombie/src/server.rs | 11 +- 5 files changed, 173 insertions(+), 124 deletions(-) diff --git a/docs/src/reference/animations.md b/docs/src/reference/animations.md index 7bc62a7f8b..1fcda2d824 100644 --- a/docs/src/reference/animations.md +++ b/docs/src/reference/animations.md @@ -35,16 +35,16 @@ use packages::ambient_example_skinmesh::assets; ## Animation player -An `AnimationPlayer` is used to play animations. The player executes a graph of animation nodes; at present, -the two nodes that exist are `PlayClipFromUrlNode` and `BlendNode`. +An `AnimationPlayerRef` is used to play animations. The player executes a graph of animation nodes; at present, +the two nodes that exist are `PlayClipFromUrlNodeRef` and `BlendNodeRef`. Here's an example of how to set up a graph and play it for a single animation: ```rust -let clip = PlayClipFromUrlNode::new( +let clip = PlayClipFromUrlNodeRef::new( assets::url("Capoeira.fbx/animations/mixamo.com.anim") ); -let player = AnimationPlayer::new(&clip); +let player = AnimationPlayerRef::new(&clip); // Let's load a character model to apply the animation to. Entity::new() @@ -58,17 +58,17 @@ The same animation player can be attached to multiple models. ### Blending animations together -A `BlendNode` can be used to blend two animations together: +A `BlendNodeRef` can be used to blend two animations together: ```rust -let capoeira = PlayClipFromUrlNode::new( +let capoeira = PlayClipFromUrlNodeRef::new( assets::url("Capoeira.fbx/animations/mixamo.com.anim") ); -let robot = PlayClipFromUrlNode::new( +let robot = PlayClipFromUrlNodeRef::new( assets::url("Robot Hip Hop Dance.fbx/animations/mixamo.com.anim") ); -let blend = BlendNode::new(&capoeira, &robot, 0.3); -let anim_player = AnimationPlayer::new(&blend); +let blend = BlendNodeRef::new(&capoeira, &robot, 0.3); +let anim_player = AnimationPlayerRef::new(&blend); ``` This will blend `capoeira` (30%) and `robot` (70%) together to form one output animation. @@ -80,17 +80,17 @@ this is achieved using masking. Here's an example of how to blend two animations body: ```rust -let capoeira = PlayClipFromUrlNode::new( +let capoeira = PlayClipFromUrlNodeRef::new( assets::url("Capoeira.fbx/animations/mixamo.com.anim") ); -let robot = PlayClipFromUrlNode::new( +let robot = PlayClipFromUrlNodeRef::new( assets::url("Robot Hip Hop Dance.fbx/animations/mixamo.com.anim") ); -let blend = BlendNode::new(&capoeira, &robot, 0.0); +let blend = BlendNodeRef::new(&capoeira, &robot, 0.0); blend.set_mask_humanoid_lower_body(1.0); -let anim_player = AnimationPlayer::new(&blend); +let anim_player = AnimationPlayerRef::new(&blend); ``` This will play the `capoeira` at the upper body, and the `robot` dance for the lower body. @@ -98,7 +98,7 @@ The `set_mask_humanoid_lower_body` and `set_mask_humanoid_upper_body` functions functions for setting the mask for the upper and lower body. The blend node's weight is still relevant when used with masking, but can also be set per-bone using the mask. -Setting `BlendNode::new(&capoeira, &robot, 0.3)` and then `blend.set_mask_humanoid_lower_body(0.9)` will play all +Setting `BlendNodeRef::new(&capoeira, &robot, 0.3)` and then `blend.set_mask_humanoid_lower_body(0.9)` will play all nodes in the `capoeira` animation at 30%, except for the lower body, which will play it at 90%. If no mask is set, the weight is used for all bones. @@ -126,10 +126,10 @@ This will spawn a ball and attach it to the left foot of the character. ### Pre-loading animations -Animations can be pre-loaded by creating a `PlayClipFromUrlNode` node and waiting for it to load: +Animations can be pre-loaded by creating a `PlayClipFromUrlNodeRef` node and waiting for it to load: ```rust -let capoeira = PlayClipFromUrlNode::new( +let capoeira = PlayClipFromUrlNodeRef::new( assets::url("Capoeira.fbx/animations/mixamo.com.anim") ); capoeira.wait_for_load().await; @@ -143,18 +143,14 @@ It is possible to play an animation that was made for one character on another c Retargeting may be necessary to remap the animation from the original character's skeleton to your target character's skeleton. -To do this, `PlayClipFromUrlNode::set_retargeting` can be used to configure the retargeting for a given clip. -Additionally, `PlayClipFromUrlNode::apply_base_pose` may be necessary to change the origin of the animation +To do this, `PlayClipFromUrlNodeRef::set_retargeting` can be used to configure the retargeting for a given clip. +Additionally, `PlayClipFromUrlNodeRef::apply_base_pose` may be necessary to change the origin of the animation for correctness. If you're using Mixamo for animations, you can do retargeting through Mixamo itself to get the best results. ### Animation nodes lifetimes and ownership -The animation player and nodes all live in the ECS. The `AnimationPlayer`, `PlayClipFromUrlNode` and other nodes -are wrappers around an `EntityId`. To remove an animation player, call `player.despawn()` on it. - -The animation nodes are ref-counted, so they will survive while at least one of the following is true: - -- they are either being played by an animation player -- they are being referenced by your code (i.e. you have an `PlayClipFromUrlNode`). +The animation player and nodes all live in the ECS. The `AnimationPlayerRef`, `PlayClipFromUrlNodeRef` and other nodes +are wrappers around an `EntityId`. You are responsible for despawning them when you're done with them, by calling +`.despawn()`, which will remove the node and all the children. diff --git a/guest/rust/api_core/src/animation.rs b/guest/rust/api_core/src/animation.rs index 1ebe316d95..ffc49a6706 100644 --- a/guest/rust/api_core/src/animation.rs +++ b/guest/rust/api_core/src/animation.rs @@ -5,21 +5,25 @@ use crate::{ freeze_at_time, is_animation_player, looping, mask_bind_ids, mask_weights, play_clip_from_url, retarget_animation_scaled, retarget_model_from_url, start_time, }, - app::components::{name, ref_count}, + app::components::name, ecs::components::{children, parent}, }, entity, prelude::{epoch_time, Entity, EntityId}, }; +use std::time::Duration; /// This plays animations, and can handle blending and masking of animations together to create /// complex effects. A single animation player can be attached to multiple entities in the scene. +/// +/// This is just a reference to an entity which lives in the ecs. You need to call `despawn` to +/// remove it. #[derive(Debug, Clone, Copy)] -pub struct AnimationPlayer(pub EntityId); -impl AnimationPlayer { +pub struct AnimationPlayerRef(pub EntityId); +impl AnimationPlayerRef { /// Create a new animation player, with `root` as the currently playing node - pub fn new(root: impl AsRef) -> Self { - let root: &AnimationNode = root.as_ref(); + pub fn new(root: impl AsRef) -> Self { + let root: &AnimationNodeRef = root.as_ref(); let player = Entity::new() .with(is_animation_player(), ()) .with(children(), vec![root.0]) @@ -28,64 +32,87 @@ impl AnimationPlayer { entity::add_component(root.0, parent(), player); Self(player) } - fn root(&self) -> Option { + fn root(&self) -> Option { if let Some(children) = entity::get_component(self.0, children()) { - children.get(0).copied() + children.get(0).copied().map(|id| AnimationNodeRef(id)) } else { None } } - fn free_root(&self) { - if let Some(root) = self.root() { - entity::remove_component(root, parent()); - } - } /// Replaces the current root node of the animation player with a new node - pub fn play(&self, node: impl AsRef) { - self.free_root(); - let new_root: &AnimationNode = node.as_ref(); + pub fn play(&self, node: impl AsRef) -> Option { + let old_root = self.root(); + let new_root: &AnimationNodeRef = node.as_ref(); entity::add_component(self.0, children(), vec![new_root.0]); entity::add_component(new_root.0, parent(), self.0); + old_root } /// Despawn this animation player. /// Note that dropping this player won't despawn it automatically; only call this method will despawn it. pub fn despawn(self) { - self.free_root(); + if let Some(root) = self.root() { + root.despawn(); + } entity::despawn(self.0); } } -/// An animation node. Used in the animation player. It keeps an internal ref count. -#[derive(Debug)] -pub struct AnimationNode(EntityId); -impl AnimationNode { +/// An animation node. Used in the animation player. +#[derive(Debug, Clone, Copy)] +pub struct AnimationNodeRef(EntityId); +impl AnimationNodeRef { /// Get the entity id of this node pub fn get_entity_id(&self) -> EntityId { self.0 } /// Use an existing node pub fn from_entity(entity: EntityId) -> Self { - entity::mutate_component(entity, ref_count(), |x| *x += 1); Self(entity) } -} -impl Clone for AnimationNode { - fn clone(&self) -> Self { - entity::mutate_component(self.0, ref_count(), |x| *x += 1); - Self(self.0) + /// Delete this node and its children + pub fn despawn(self) { + for c in entity::get_component(self.0, children()).unwrap_or_default() { + Self::from_entity(c).despawn(); + } + entity::despawn(self.0); } } -impl Drop for AnimationNode { - fn drop(&mut self) { - entity::mutate_component(self.0, ref_count(), |x| *x -= 1); + +/// Play mode for animation nodes +pub enum PlayMode { + /// Play the animation + Play { + /// Global time for the start of the animation + start_time: Duration, + }, + /// Freeze the animation at time + FreezeAtTime { + /// Time in the animation we want to freeze at + time: f32, + }, + /// Freeze the animation at time = percentage * duration + FreezeAtPercentage { + /// Percentage into the animation that we want to freeze at + percentage: f32, + }, +} +impl PlayMode { + /// Play an animation + pub fn play() -> Self { + Self::Play { + start_time: epoch_time(), + } } } /// Play clip from url animation node. /// This is an animation node which can be plugged into an animation player or other animation nodes. -#[derive(Debug)] -pub struct PlayClipFromUrlNode(pub AnimationNode); -impl PlayClipFromUrlNode { +/// +/// This is just a reference to an entity which lives in the ecs. You need to call `despawn` to +/// remove it. +#[derive(Debug, Clone, Copy)] +pub struct PlayClipFromUrlNodeRef(pub AnimationNodeRef); +impl PlayClipFromUrlNodeRef { /// Create a new node. pub fn new(url: impl Into) -> Self { let node = Entity::new() @@ -93,25 +120,42 @@ impl PlayClipFromUrlNode { .with(name(), "Play clip from URL".to_string()) .with(looping(), true) .with(start_time(), epoch_time()) - .with(ref_count(), 1) .spawn(); - Self(AnimationNode(node)) + Self(AnimationNodeRef(node)) } /// Use an existing node pub fn from_entity(entity: EntityId) -> Self { - Self(AnimationNode::from_entity(entity)) + Self(AnimationNodeRef::from_entity(entity)) } /// Set if the animation should loop or not pub fn looping(&self, value: bool) { entity::add_component(self.0 .0, looping(), value); } - /// Freeze the animation at time - pub fn freeze_at_time(&self, time: f32) { - entity::add_component(self.0 .0, freeze_at_time(), time); + /// Set how the animation is played + pub fn set_play_mode(&self, mode: PlayMode) { + match mode { + PlayMode::Play { start_time: time } => { + entity::add_component(self.0 .0, start_time(), time); + entity::remove_component(self.0 .0, freeze_at_time()); + entity::remove_component(self.0 .0, freeze_at_percentage()); + } + PlayMode::FreezeAtTime { time } => { + entity::add_component(self.0 .0, freeze_at_time(), time); + entity::remove_component(self.0 .0, start_time()); + entity::remove_component(self.0 .0, freeze_at_percentage()); + } + PlayMode::FreezeAtPercentage { percentage } => { + entity::add_component(self.0 .0, freeze_at_percentage(), percentage); + entity::remove_component(self.0 .0, start_time()); + entity::remove_component(self.0 .0, freeze_at_time()); + } + } } - /// Freeze the animation at time = percentage * duration - pub fn freeze_at_percentage(&self, percentage: f32) { - entity::add_component(self.0 .0, freeze_at_percentage(), percentage); + /// Start playing the animation from the beginning + pub fn restart(&self) { + self.set_play_mode(PlayMode::Play { + start_time: epoch_time(), + }); } /// Set up retargeting pub fn set_retargeting(&self, retargeting: AnimationRetargeting) { @@ -174,42 +218,44 @@ impl PlayClipFromUrlNode { self.clip_duration().await; } } -impl AsRef for PlayClipFromUrlNode { - fn as_ref(&self) -> &AnimationNode { +impl AsRef for PlayClipFromUrlNodeRef { + fn as_ref(&self) -> &AnimationNodeRef { &self.0 } } /// Blend animation node. /// This is an animation node which can be plugged into an animation player or other animation nodes. -#[derive(Debug, Clone)] -pub struct BlendNode(pub AnimationNode); -impl BlendNode { +/// +/// This is just a reference to an entity which lives in the ecs. You need to call `despawn` to +/// remove it. +#[derive(Debug, Clone, Copy)] +pub struct BlendNodeRef(pub AnimationNodeRef); +impl BlendNodeRef { /// Create a new blend animation node. /// /// If the weight is 0, only the left animation will play. /// If the weight is 1, only the right animation will play. /// Values in between blend between the two animations. pub fn new( - left: impl AsRef, - right: impl AsRef, + left: impl AsRef, + right: impl AsRef, weight: f32, ) -> Self { - let left: &AnimationNode = left.as_ref(); - let right: &AnimationNode = right.as_ref(); + let left: &AnimationNodeRef = left.as_ref(); + let right: &AnimationNodeRef = right.as_ref(); let node = Entity::new() .with(blend(), weight) .with(name(), "Blend".to_string()) .with(children(), vec![left.0, right.0]) - .with(ref_count(), 1) .spawn(); entity::add_component(left.0, parent(), node); entity::add_component(right.0, parent(), node); - Self(AnimationNode(node)) + Self(AnimationNodeRef(node)) } /// Use an existing node pub fn from_entity(entity: EntityId) -> Self { - Self(AnimationNode::from_entity(entity)) + Self(AnimationNodeRef::from_entity(entity)) } /// Set the weight of this blend node. /// @@ -250,8 +296,8 @@ impl BlendNode { ); } } -impl AsRef for BlendNode { - fn as_ref(&self) -> &AnimationNode { +impl AsRef for BlendNodeRef { + fn as_ref(&self) -> &AnimationNodeRef { &self.0 } } diff --git a/guest/rust/examples/basics/skinmesh/src/client.rs b/guest/rust/examples/basics/skinmesh/src/client.rs index 44e0e36731..8dbf3bb05d 100644 --- a/guest/rust/examples/basics/skinmesh/src/client.rs +++ b/guest/rust/examples/basics/skinmesh/src/client.rs @@ -1,5 +1,5 @@ use ambient_api::{ - animation::{self, AnimationPlayer, BindId, BlendNode, PlayClipFromUrlNode}, + animation::{self, AnimationPlayerRef, BindId, BlendNodeRef, PlayClipFromUrlNodeRef, PlayMode}, core::{ animation::components::apply_animation_player, app::components::{main_scene, name}, @@ -45,12 +45,13 @@ pub async fn main() { .with(name(), "Peasant".to_string()) .spawn(); - let capoeira = PlayClipFromUrlNode::new(assets::url("Capoeira.fbx/animations/mixamo.com.anim")); - let robot = PlayClipFromUrlNode::new(assets::url( + let capoeira = + PlayClipFromUrlNodeRef::new(assets::url("Capoeira.fbx/animations/mixamo.com.anim")); + let robot = PlayClipFromUrlNodeRef::new(assets::url( "Robot Hip Hop Dance.fbx/animations/mixamo.com.anim", )); - let blend = BlendNode::new(&capoeira, &robot, 0.); - let anim_player = AnimationPlayer::new(&blend); + let blend = BlendNodeRef::new(&capoeira, &robot, 0.); + let anim_player = AnimationPlayerRef::new(&blend); entity::add_component(unit_id, apply_animation_player(), anim_player.0); println!("Robot duration: {} sec", robot.clip_duration().await); @@ -69,11 +70,21 @@ pub async fn main() { .spawn(); entity::add_child(left_foot, ball); - App::el(blend, anim_player).spawn_interactive() + let robot = PlayClipFromUrlNodeRef::new(assets::url( + "Robot Hip Hop Dance.fbx/animations/mixamo.com.anim", + )); + robot.looping(false); + + App::el(blend, anim_player, robot).spawn_interactive() } #[element_component] -fn App(hooks: &mut Hooks, blend_node: BlendNode, anim_player: AnimationPlayer) -> Element { +fn App( + hooks: &mut Hooks, + blend_node: BlendNodeRef, + anim_player: AnimationPlayerRef, + robot: PlayClipFromUrlNodeRef, +) -> Element { let (blend, set_blend) = hooks.use_state(0.0f32); let (masked, set_masked) = hooks.use_state(false); @@ -126,10 +137,8 @@ fn App(hooks: &mut Hooks, blend_node: BlendNode, anim_player: AnimationPlayer) - ]), FlowRow::el([ Button::new("Play single animation", move |_| { - let robot = PlayClipFromUrlNode::new(assets::url( - "Robot Hip Hop Dance.fbx/animations/mixamo.com.anim", - )); - robot.looping(false); + robot.looping(true); + robot.restart(); anim_player.play(robot); }) .el(), @@ -138,11 +147,8 @@ fn App(hooks: &mut Hooks, blend_node: BlendNode, anim_player: AnimationPlayer) - }) .el(), Button::new("Freeze animation", move |_| { - let robot = PlayClipFromUrlNode::new(assets::url( - "Robot Hip Hop Dance.fbx/animations/mixamo.com.anim", - )); robot.looping(false); - robot.freeze_at_percentage(0.5); + robot.set_play_mode(PlayMode::FreezeAtPercentage { percentage: 0.5 }); anim_player.play(robot); }) .el(), diff --git a/guest/rust/packages/games/afps/core/fpsanim/src/server.rs b/guest/rust/packages/games/afps/core/fpsanim/src/server.rs index d50e5509b0..9f6a9b0f1f 100644 --- a/guest/rust/packages/games/afps/core/fpsanim/src/server.rs +++ b/guest/rust/packages/games/afps/core/fpsanim/src/server.rs @@ -1,5 +1,5 @@ use ambient_api::{ - animation::{AnimationPlayer, BlendNode, PlayClipFromUrlNode}, + animation::{AnimationPlayerRef, BlendNodeRef, PlayClipFromUrlNodeRef}, core::{ animation::components::apply_animation_player, ecs::components::{children, parent}, @@ -51,23 +51,23 @@ fn add_anim_clip_and_blend_to_player(id: EntityId) { let run_bk_rt = make_node("Run Backward Right.fbx"); let idle = make_node("Rifle Aiming Idle.fbx"); - let blend1 = BlendNode::new(&walk_fd, &walk_bk, 0.0); - let blend2 = BlendNode::new(&blend1, &walk_lt, 0.0); - let blend3 = BlendNode::new(&blend2, &walk_rt, 0.0); - let blend4 = BlendNode::new(&blend3, &walk_fd_lt, 0.0); - let blend5 = BlendNode::new(&blend4, &walk_fd_rt, 0.0); - let blend6 = BlendNode::new(&blend5, &walk_bk_lt, 0.0); - let blend7 = BlendNode::new(&blend6, &walk_bk_rt, 0.0); - let blend8 = BlendNode::new(&blend7, &run_fd, 0.0); - let blend9 = BlendNode::new(&blend8, &run_bk, 0.0); - let blend10 = BlendNode::new(&blend9, &run_lt, 0.0); - let blend11 = BlendNode::new(&blend10, &run_rt, 0.0); - let blend12 = BlendNode::new(&blend11, &run_fd_lt, 0.0); - let blend13 = BlendNode::new(&blend12, &run_fd_rt, 0.0); - let blend14 = BlendNode::new(&blend13, &run_bk_lt, 0.0); - let blend15 = BlendNode::new(&blend14, &run_bk_rt, 0.0); - let blend16 = BlendNode::new(&blend15, &idle, 0.0); - let output = BlendNode::new(&blend16, &blend16, 0.0); // the right one is dummy + let blend1 = BlendNodeRef::new(&walk_fd, &walk_bk, 0.0); + let blend2 = BlendNodeRef::new(&blend1, &walk_lt, 0.0); + let blend3 = BlendNodeRef::new(&blend2, &walk_rt, 0.0); + let blend4 = BlendNodeRef::new(&blend3, &walk_fd_lt, 0.0); + let blend5 = BlendNodeRef::new(&blend4, &walk_fd_rt, 0.0); + let blend6 = BlendNodeRef::new(&blend5, &walk_bk_lt, 0.0); + let blend7 = BlendNodeRef::new(&blend6, &walk_bk_rt, 0.0); + let blend8 = BlendNodeRef::new(&blend7, &run_fd, 0.0); + let blend9 = BlendNodeRef::new(&blend8, &run_bk, 0.0); + let blend10 = BlendNodeRef::new(&blend9, &run_lt, 0.0); + let blend11 = BlendNodeRef::new(&blend10, &run_rt, 0.0); + let blend12 = BlendNodeRef::new(&blend11, &run_fd_lt, 0.0); + let blend13 = BlendNodeRef::new(&blend12, &run_fd_rt, 0.0); + let blend14 = BlendNodeRef::new(&blend13, &run_bk_lt, 0.0); + let blend15 = BlendNodeRef::new(&blend14, &run_bk_rt, 0.0); + let blend16 = BlendNodeRef::new(&blend15, &idle, 0.0); + let output = BlendNodeRef::new(&blend16, &blend16, 0.0); // the right one is dummy entity::add_component( id, components::player_output_blend_node(), @@ -104,14 +104,14 @@ fn add_anim_clip_and_blend_to_player(id: EntityId) { ); } -fn get_blend_node_for_playing(id: EntityId, index: usize) -> Option { +fn get_blend_node_for_playing(id: EntityId, index: usize) -> Option { let node = entity::get_component(id, components::player_anim_blend())?; if node.len() <= index { return None; } let init_node = node[index]; // let node = AnimationNode::from_entity(init_node); - let blend_node = BlendNode::from_entity(init_node); + let blend_node = BlendNodeRef::from_entity(init_node); Some(blend_node) } @@ -129,8 +129,8 @@ pub fn main() { add_anim_clip_and_blend_to_player(id); let output_blend_node = entity::get_component(id, components::player_output_blend_node()).unwrap(); - let blend_node = BlendNode::from_entity(output_blend_node); - let anim_player = AnimationPlayer::new(blend_node); + let blend_node = BlendNodeRef::from_entity(output_blend_node); + let anim_player = AnimationPlayerRef::new(blend_node); entity::add_component(model, apply_animation_player(), anim_player.0); entity::add_component(id, player_jumping(), false); } @@ -269,6 +269,6 @@ pub fn main() { }); } -fn make_node(url: &str) -> PlayClipFromUrlNode { - PlayClipFromUrlNode::new(assets::url(&format!("{url}/animations/mixamo.com.anim"))) +fn make_node(url: &str) -> PlayClipFromUrlNodeRef { + PlayClipFromUrlNodeRef::new(assets::url(&format!("{url}/animations/mixamo.com.anim"))) } diff --git a/guest/rust/packages/games/afps/mods/zombie/src/server.rs b/guest/rust/packages/games/afps/mods/zombie/src/server.rs index 327bd1e27c..f9a652872a 100644 --- a/guest/rust/packages/games/afps/mods/zombie/src/server.rs +++ b/guest/rust/packages/games/afps/mods/zombie/src/server.rs @@ -1,7 +1,7 @@ use std::f32::consts::FRAC_PI_2; use ambient_api::{ - animation::{AnimationPlayer, BlendNode, PlayClipFromUrlNode}, + animation::{AnimationPlayerRef, BlendNodeRef, PlayClipFromUrlNodeRef}, core::{ animation::components::apply_animation_player, ecs::components::{children, parent}, @@ -52,11 +52,12 @@ pub async fn main() { .with(components::is_zombie(), ()), ); - let run = - PlayClipFromUrlNode::new(assets::url("Zombie Run.fbx/animations/mixamo.com.anim")); + let run = PlayClipFromUrlNodeRef::new(assets::url( + "Zombie Run.fbx/animations/mixamo.com.anim", + )); - let blend = BlendNode::new(&run, &run, 0.); - let anim_player = AnimationPlayer::new(&blend); + let blend = BlendNodeRef::new(&run, &run, 0.); + let anim_player = AnimationPlayerRef::new(&blend); entity::add_component(model, apply_animation_player(), anim_player.0); sleep(random::()).await; From 23484f3f1cd4e9b3e788ebe325e72da94c297395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Nore=CC=81n?= Date: Tue, 5 Sep 2023 12:18:50 +0200 Subject: [PATCH 2/6] Peak load anim clips --- Cargo.lock | 429 +++++++++++++++++- crates/animation/src/player.rs | 55 ++- crates/ecs/src/generated.rs | 2 +- guest/rust/api_core/src/internal/generated.rs | 32 +- schema/schema/animation.toml | 32 +- 5 files changed, 490 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 142a928943..434824ec5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -159,6 +159,7 @@ dependencies = [ "clap 4.4.0", "convert_case 0.6.0", "env_logger 0.10.0", + "git-version", "glam 0.24.1", "image", "image_hasher", @@ -167,6 +168,8 @@ dependencies = [ "rpassword", "rustls-pemfile", "rusty-hook", + "sentry", + "sentry-rust-minidump", "serde", "serde_json", "time", @@ -2554,6 +2557,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "concurrent-queue" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "console" version = "0.15.7" @@ -2792,6 +2804,30 @@ dependencies = [ "wasmtime-types", ] +[[package]] +name = "crash-context" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b85cef661eeca0c6675116310936972c520ebb0a33ddef16fd7efc957f4c1288" +dependencies = [ + "cfg-if", + "libc", + "mach2", +] + +[[package]] +name = "crash-handler" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ddb8d457c6c6c6d0ebeeadb3b3f4b246c39ed83b0d3393f91bf06a5b79b05" +dependencies = [ + "cfg-if", + "crash-context", + "libc", + "mach2", + "parking_lot", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -3074,6 +3110,17 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum-primitive-derive" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c375b9c5eadb68d0a6efee2999fef292f45854c3444c86f09d8ab086ba942b0e" +dependencies = [ + "num-traits", + "quote", + "syn 1.0.109", +] + [[package]] name = "enumflags2" version = "0.6.4" @@ -3226,6 +3273,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "fbxcel" version = "0.9.0" @@ -3279,6 +3332,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + [[package]] name = "fixed-vec-deque" version = "0.1.11" @@ -3910,6 +3975,17 @@ dependencies = [ "xi-unicode", ] +[[package]] +name = "goblin" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27c1b4369c2cd341b5de549380158b105a04c331be5db9110eef7b6d2742134" +dependencies = [ + "log", + "plain", + "scroll", +] + [[package]] name = "gpu-alloc" version = "0.5.4" @@ -4019,7 +4095,7 @@ version = "0.0.2" source = "git+https://github.com/security-union/h3?branch=add-webtransport#fa956e0d44e66c04545741908fcb3690b0890be6" dependencies = [ "bytes", - "fastrand", + "fastrand 1.9.0", "futures-util", "http", "pin-project-lite", @@ -4185,6 +4261,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + [[package]] name = "hound" version = "3.5.0" @@ -4287,6 +4374,19 @@ dependencies = [ "tokio-io-timeout", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.57" @@ -4774,6 +4874,15 @@ dependencies = [ "libc", ] +[[package]] +name = "mach2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" +dependencies = [ + "libc", +] + [[package]] name = "macroquad" version = "0.3.24" @@ -4804,6 +4913,12 @@ dependencies = [ "libc", ] +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matchers" version = "0.1.0" @@ -4942,6 +5057,74 @@ dependencies = [ "unicase", ] +[[package]] +name = "minidump-common" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9114b15d86ee5e5c3e3b4d05821d17237adbf98c11dd07fc8f5a9b037a010ee5" +dependencies = [ + "bitflags 1.3.2", + "debugid", + "enum-primitive-derive", + "num-traits", + "range-map", + "scroll", + "smart-default", +] + +[[package]] +name = "minidump-writer" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9301e0f1b08ad0b310f40f92bffb40834461542a472a98814bc6cbb7f43f567" +dependencies = [ + "bitflags 2.4.0", + "byteorder", + "cfg-if", + "crash-context", + "goblin", + "libc", + "mach2", + "memmap2", + "memoffset 0.9.0", + "minidump-common", + "nix 0.26.2", + "procfs-core", + "scroll", + "tempfile", + "thiserror", +] + +[[package]] +name = "minidumper" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261916bd44ec98562f8573f5e882ca08b1fea717b20bee95b6b39ad024869f97" +dependencies = [ + "cfg-if", + "crash-context", + "libc", + "log", + "minidump-writer", + "parking_lot", + "polling", + "scroll", + "thiserror", + "uds", +] + +[[package]] +name = "minidumper-child" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "409df6a030ca1b1f3e64a08eb16282566621db3b9839db2fe323e67ddda7e54b" +dependencies = [ + "crash-handler", + "minidumper", + "thiserror", + "uuid", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -5059,6 +5242,24 @@ dependencies = [ "getrandom 0.2.10", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ndarray" version = "0.15.6" @@ -5626,6 +5827,17 @@ dependencies = [ "serde", ] +[[package]] +name = "os_info" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +dependencies = [ + "log", + "serde", + "winapi", +] + [[package]] name = "overload" version = "0.1.1" @@ -5768,6 +5980,12 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "png" version = "0.17.10" @@ -5781,6 +5999,22 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + [[package]] name = "pollster" version = "0.3.0" @@ -5843,6 +6077,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "procfs-core" +version = "0.16.0-RC1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee00a90a41543fce203e6a8771bad043bfd6d88de8fd4e3118435a233d0c3c4" +dependencies = [ + "bitflags 2.4.0", + "chrono", + "hex", +] + [[package]] name = "profiling" version = "1.0.9" @@ -6116,6 +6361,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" +[[package]] +name = "range-map" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12a5a2d6c7039059af621472a4389be1215a816df61aa4d531cfe85264aee95f" +dependencies = [ + "num-traits", +] + [[package]] name = "raw-window-handle" version = "0.5.2" @@ -6273,10 +6527,12 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -6286,6 +6542,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "tokio", + "tokio-native-tls", "tokio-rustls", "tower-service", "url", @@ -6600,6 +6857,26 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + [[package]] name = "sct" version = "0.7.0" @@ -6670,11 +6947,33 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "sentry" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e95efd0cefa32028cdb9766c96de71d96671072f9fb494dc9fb84c0ef93e52b" +dependencies = [ + "httpdate", + "native-tls", + "reqwest", + "rustls", + "sentry-anyhow", + "sentry-backtrace", + "sentry-contexts", + "sentry-core", + "sentry-debug-images", + "sentry-panic", + "sentry-tracing", + "tokio", + "ureq", + "webpki-roots 0.25.2", +] + [[package]] name = "sentry-anyhow" -version = "0.27.0" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d3479158a7396b4dfd346a1944d523427a225ff3c39102e8e985bc21cdfea8" +checksum = "27c30fb3ec036e3210a51d102890e734be62951d876e626205db8412a2a57c1a" dependencies = [ "anyhow", "sentry-backtrace", @@ -6683,9 +6982,9 @@ dependencies = [ [[package]] name = "sentry-backtrace" -version = "0.27.0" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49bafa55eefc6dbc04c7dac91e8c8ab9e89e9414f3193c105cabd991bbc75134" +checksum = "6ac2bac6f310c4c4c4bb094d1541d32ae497f8c5c23405e85492cefdfe0971a9" dependencies = [ "backtrace", "once_cell", @@ -6693,23 +6992,82 @@ dependencies = [ "sentry-core", ] +[[package]] +name = "sentry-contexts" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c3e17295cecdbacf66c5bd38d6e1147e09e1e9d824d2d5341f76638eda02a3a" +dependencies = [ + "hostname", + "libc", + "os_info", + "rustc_version 0.4.0", + "sentry-core", + "uname", +] + [[package]] name = "sentry-core" -version = "0.27.0" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a4591a2d128af73b1b819ab95f143bc6a2fbe48cd23a4c45e1ee32177e66ae6" +checksum = "8339474f587f36cb110fa1ed1b64229eea6d47b0b886375579297b7e47aeb055" dependencies = [ "once_cell", + "rand 0.8.5", "sentry-types", "serde", "serde_json", ] +[[package]] +name = "sentry-debug-images" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c11e7d2b809b06497a18a2e60f513206462ae2db27081dfb7be9ade1f329cc8" +dependencies = [ + "findshlibs", + "once_cell", + "sentry-core", +] + +[[package]] +name = "sentry-panic" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875b69f506da75bd664029eafb05f8934297d2990192896d17325f066bd665b7" +dependencies = [ + "sentry-backtrace", + "sentry-core", +] + +[[package]] +name = "sentry-rust-minidump" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac50b382465f5c6f26e7088e811ad791e835bacee951a0ea831a1cacd0a5cc15" +dependencies = [ + "minidumper-child", + "sentry", + "thiserror", +] + +[[package]] +name = "sentry-tracing" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89feead9bdd116f8035e89567651340fc382db29240b6c55ef412078b08d1aa3" +dependencies = [ + "sentry-backtrace", + "sentry-core", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "sentry-types" -version = "0.27.0" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823923ae5f54a729159d720aa12181673044ee5c79cbda3be09e56f885e5468f" +checksum = "99dc599bd6646884fc403d593cdcb9816dd67c50cff3271c01ff123617908dcd" dependencies = [ "debugid", "getrandom 0.2.10", @@ -6912,6 +7270,17 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +[[package]] +name = "smart-default" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "smithay-client-toolkit" version = "0.16.0" @@ -7259,6 +7628,19 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "tempfile" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +dependencies = [ + "cfg-if", + "fastrand 2.0.0", + "redox_syscall 0.3.5", + "rustix 0.38.9", + "windows-sys 0.48.0", +] + [[package]] name = "termcolor" version = "1.1.3" @@ -7442,6 +7824,16 @@ dependencies = [ "syn 2.0.29", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" @@ -7771,6 +8163,15 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "uds" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26095fa6d0208163cb66162d4b016c8f273226af3191ce651d6482ca760c31e7" +dependencies = [ + "libc", +] + [[package]] name = "ulid" version = "1.0.0" @@ -7781,6 +8182,15 @@ dependencies = [ "serde", ] +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + [[package]] name = "unicase" version = "2.7.0" @@ -7850,6 +8260,7 @@ dependencies = [ "base64 0.21.2", "flate2", "log", + "native-tls", "once_cell", "rustls", "rustls-webpki 0.100.2", diff --git a/crates/animation/src/player.rs b/crates/animation/src/player.rs index 355ecaf7ae..7906ec3d2c 100644 --- a/crates/animation/src/player.rs +++ b/crates/animation/src/player.rs @@ -10,9 +10,9 @@ use ambient_ecs::{ children, components, generated::animation::components::{ animation_errors, apply_animation_player, apply_base_pose, bind_ids, blend, clip_duration, - freeze_at_percentage, freeze_at_time, is_animation_player, looping, mask_bind_ids, - mask_weights, play_clip_from_url, retarget_animation_scaled, retarget_model_from_url, - speed, start_time, + clip_loaded, freeze_at_percentage, freeze_at_time, is_animation_player, looping, + mask_bind_ids, mask_weights, play_clip_from_url, retarget_animation_scaled, + retarget_model_from_url, speed, start_time, }, query, ComponentDesc, Debuggable, EntityId, SystemGroup, World, }; @@ -20,6 +20,7 @@ use ambient_model::{animation_binder, ModelFromUrl}; use ambient_native_std::{ asset_cache::{AssetCache, AsyncAssetKeyExt}, asset_url::{AnimationAssetType, TypedAssetUrl}, + download_asset::AssetResult, }; use anyhow::Context; use glam::{Quat, Vec3}; @@ -202,6 +203,7 @@ pub fn animation_player_systems() -> SystemGroup { query(play_clip_from_url().changed()).to_system(|q, world, qs, _| { let runtime = world.resource(runtime()).clone(); for (id, url) in q.collect_cloned(world, qs) { + world.remove_component(id, clip_loaded()).ok(); let async_run = world.resource(async_run()).clone(); let assets = world.resource(asset_cache()).clone(); let url = match TypedAssetUrl::::from_str(&url) { @@ -226,35 +228,40 @@ pub fn animation_player_systems() -> SystemGroup { } else { AnimationRetargeting::None }; - runtime.spawn(async move { - let clip = AnimationClipRetargetedFromModel { - clip: url, - translation_retargeting: retargeting, - retarget_model, - } - .get(&assets) - .await; - let duration = clip.as_ref().map(|clip| clip.duration()).unwrap_or(0.); - let binders = clip - .as_ref() - .map(|clip| { - clip.tracks + let clip_ref = AnimationClipRetargetedFromModel { + clip: url, + translation_retargeting: retargeting, + retarget_model, + }; + let apply_clip = + move |world: &mut World, clip: AssetResult>| { + if let Ok(clip) = clip { + world + .add_component(id, clip_duration(), clip.duration()) + .ok(); + let binders = clip + .tracks .iter() .filter_map(|x| match &x.target { AnimationTarget::BinderId(binder) => Some(binder.clone()), AnimationTarget::Entity(_entity) => None, }) - .collect::>() - }) - .unwrap_or_default(); - async_run.run(move |world| { - world.add_component(id, clip_duration(), duration).ok(); - world.add_component(id, bind_ids(), binders).ok(); - if let Ok(clip) = clip { + .collect::>(); + world.add_component(id, bind_ids(), binders).ok(); world.add_component(id, play_clip(), clip).ok(); } + world.add_component(id, clip_loaded(), ()).ok(); + }; + if let Some(clip) = clip_ref.peek(&assets) { + apply_clip(world, clip); + } else { + runtime.spawn(async move { + let clip = clip_ref.get(&assets).await; + async_run.run(move |world| { + apply_clip(world, clip); + }); }); - }); + } } }), query(play_clip_from_url().changed()) diff --git a/crates/ecs/src/generated.rs b/crates/ecs/src/generated.rs index 088fdb5fbf..c0660e859f 100644 --- a/crates/ecs/src/generated.rs +++ b/crates/ecs/src/generated.rs @@ -41,7 +41,7 @@ mod raw { }; use glam::{Mat4, Quat, UVec2, UVec3, UVec4, Vec2, Vec3, Vec4}; use std::time::Duration; - components ! ("animation" , { # [doc = "**Is animation player**: This entity is treated as an animation player. Attach an animation node as a child for it to play.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] @ [MaybeResource , Debuggable , Networked , Name ["Is animation player"] , Description ["This entity is treated as an animation player. Attach an animation node as a child for it to play."]] is_animation_player : () , # [doc = "**Animation errors**: A list of errors that were produced trying to play the animation.\n\n*Attributes*: MaybeResource, Debuggable"] @ [MaybeResource , Debuggable , Name ["Animation errors"] , Description ["A list of errors that were produced trying to play the animation."]] animation_errors : Vec :: < String > , # [doc = "**Apply animation player**: Apply the designated animation player to this entity and its sub-tree.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] @ [MaybeResource , Debuggable , Networked , Name ["Apply animation player"] , Description ["Apply the designated animation player to this entity and its sub-tree."]] apply_animation_player : EntityId , # [doc = "**Play clip from URL**: Make this entity a 'play animation clip' node. The value is the URL to the clip we'd like to play.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] @ [MaybeResource , Debuggable , Networked , Name ["Play clip from URL"] , Description ["Make this entity a 'play animation clip' node. The value is the URL to the clip we'd like to play."]] play_clip_from_url : String , # [doc = "**Looping**: When this is true, the animation clip will repeat infinitely.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] @ [MaybeResource , Debuggable , Networked , Name ["Looping"] , Description ["When this is true, the animation clip will repeat infinitely."]] looping : bool , # [doc = "**Speed**: Animation playback speed. Default is 1, higher values speeds up the animation.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] @ [MaybeResource , Debuggable , Networked , Name ["Speed"] , Description ["Animation playback speed. Default is 1, higher values speeds up the animation."]] speed : f32 , # [doc = "**Start time**: Start time of an animation node.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] @ [MaybeResource , Debuggable , Networked , Name ["Start time"] , Description ["Start time of an animation node."]] start_time : Duration , # [doc = "**Freeze at percentage**: Sample the input animation at a certain percentage of the animation track length.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] @ [MaybeResource , Debuggable , Networked , Name ["Freeze at percentage"] , Description ["Sample the input animation at a certain percentage of the animation track length."]] freeze_at_percentage : f32 , # [doc = "**Freeze at time**: Sample the input animation at a certain time (in seconds).\n\n*Attributes*: MaybeResource, Debuggable, Networked"] @ [MaybeResource , Debuggable , Networked , Name ["Freeze at time"] , Description ["Sample the input animation at a certain time (in seconds)."]] freeze_at_time : f32 , # [doc = "**Clip duration**: The clip duration is loaded from the clip, and then applied to the entity.\n\n*Attributes*: MaybeResource, Debuggable"] @ [MaybeResource , Debuggable , Name ["Clip duration"] , Description ["The clip duration is loaded from the clip, and then applied to the entity."]] clip_duration : f32 , # [doc = "**Blend**: Blend two animations together. The values is the blend weight. Use `children` to set the animations. Blend 0 means we only sample from the first animation, 1 means only the second one, and values in between blend between them.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] @ [MaybeResource , Debuggable , Networked , Name ["Blend"] , Description ["Blend two animations together. The values is the blend weight. Use `children` to set the animations. Blend 0 means we only sample from the first animation, 1 means only the second one, and values in between blend between them."]] blend : f32 , # [doc = "**Mask bind ids**: List of bind ids that will be masked.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] @ [MaybeResource , Debuggable , Networked , Name ["Mask bind ids"] , Description ["List of bind ids that will be masked."]] mask_bind_ids : Vec :: < String > , # [doc = "**Mask weights**: Weights for each bind id in `mask_bind_ids`.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] @ [MaybeResource , Debuggable , Networked , Name ["Mask weights"] , Description ["Weights for each bind id in `mask_bind_ids`."]] mask_weights : Vec :: < f32 > , # [doc = "**Retarget Model from URL**: Retarget the animation using the model at the given URL.\n\n*Attributes*: Debuggable, Networked, Store"] @ [Debuggable , Networked , Store , Name ["Retarget Model from URL"] , Description ["Retarget the animation using the model at the given URL."]] retarget_model_from_url : String , # [doc = "**Retarget animation scaled**: Retarget animation scaled. True means normalize hip.\n\n*Attributes*: Debuggable, Networked, Store"] @ [Debuggable , Networked , Store , Name ["Retarget animation scaled"] , Description ["Retarget animation scaled. True means normalize hip."]] retarget_animation_scaled : bool , # [doc = "**Apply base pose**: Apply the base pose to this clip.\n\n*Attributes*: Debuggable, Networked, Store"] @ [Debuggable , Networked , Store , Name ["Apply base pose"] , Description ["Apply the base pose to this clip."]] apply_base_pose : () , # [doc = "**Bind id**: Animation bind ID.\n\n*Attributes*: Debuggable, Networked, Store"] @ [Debuggable , Networked , Store , Name ["Bind id"] , Description ["Animation bind ID."]] bind_id : String , # [doc = "**Bind ids**: Animation bind IDs.\n\n*Attributes*: Debuggable, Store"] @ [Debuggable , Store , Name ["Bind ids"] , Description ["Animation bind IDs."]] bind_ids : Vec :: < String > , }); + components ! ("animation" , { # [doc = "**Is animation player**: This entity is treated as an animation player. Attach an animation node as a child for it to play.\n\n*Attributes*: Debuggable, Networked"] @ [Debuggable , Networked , Name ["Is animation player"] , Description ["This entity is treated as an animation player. Attach an animation node as a child for it to play."]] is_animation_player : () , # [doc = "**Animation errors**: A list of errors that were produced trying to play the animation.\n\n*Attributes*: Debuggable"] @ [Debuggable , Name ["Animation errors"] , Description ["A list of errors that were produced trying to play the animation."]] animation_errors : Vec :: < String > , # [doc = "**Apply animation player**: Apply the designated animation player to this entity and its sub-tree.\n\n*Attributes*: Debuggable, Networked"] @ [Debuggable , Networked , Name ["Apply animation player"] , Description ["Apply the designated animation player to this entity and its sub-tree."]] apply_animation_player : EntityId , # [doc = "**Play clip from URL**: Make this entity a 'play animation clip' node. The value is the URL to the clip we'd like to play.\n\n*Attributes*: Debuggable, Networked"] @ [Debuggable , Networked , Name ["Play clip from URL"] , Description ["Make this entity a 'play animation clip' node. The value is the URL to the clip we'd like to play."]] play_clip_from_url : String , # [doc = "**Looping**: When this is true, the animation clip will repeat infinitely.\n\n*Attributes*: Debuggable, Networked"] @ [Debuggable , Networked , Name ["Looping"] , Description ["When this is true, the animation clip will repeat infinitely."]] looping : bool , # [doc = "**Speed**: Animation playback speed. Default is 1, higher values speeds up the animation.\n\n*Attributes*: Debuggable, Networked"] @ [Debuggable , Networked , Name ["Speed"] , Description ["Animation playback speed. Default is 1, higher values speeds up the animation."]] speed : f32 , # [doc = "**Start time**: Start time of an animation node.\n\n*Attributes*: Debuggable, Networked"] @ [Debuggable , Networked , Name ["Start time"] , Description ["Start time of an animation node."]] start_time : Duration , # [doc = "**Freeze at percentage**: Sample the input animation at a certain percentage of the animation track length.\n\n*Attributes*: Debuggable, Networked"] @ [Debuggable , Networked , Name ["Freeze at percentage"] , Description ["Sample the input animation at a certain percentage of the animation track length."]] freeze_at_percentage : f32 , # [doc = "**Freeze at time**: Sample the input animation at a certain time (in seconds).\n\n*Attributes*: Debuggable, Networked"] @ [Debuggable , Networked , Name ["Freeze at time"] , Description ["Sample the input animation at a certain time (in seconds)."]] freeze_at_time : f32 , # [doc = "**Clip duration**: The clip duration is loaded from the clip, and then applied to the entity.\n\n*Attributes*: Debuggable"] @ [Debuggable , Name ["Clip duration"] , Description ["The clip duration is loaded from the clip, and then applied to the entity."]] clip_duration : f32 , # [doc = "**Clip loaded**: The clip has been loaded.\n\n*Attributes*: Debuggable"] @ [Debuggable , Name ["Clip loaded"] , Description ["The clip has been loaded."]] clip_loaded : () , # [doc = "**Blend**: Blend two animations together. The values is the blend weight. Use `children` to set the animations. Blend 0 means we only sample from the first animation, 1 means only the second one, and values in between blend between them.\n\n*Attributes*: Debuggable, Networked"] @ [Debuggable , Networked , Name ["Blend"] , Description ["Blend two animations together. The values is the blend weight. Use `children` to set the animations. Blend 0 means we only sample from the first animation, 1 means only the second one, and values in between blend between them."]] blend : f32 , # [doc = "**Mask bind ids**: List of bind ids that will be masked.\n\n*Attributes*: Debuggable, Networked"] @ [Debuggable , Networked , Name ["Mask bind ids"] , Description ["List of bind ids that will be masked."]] mask_bind_ids : Vec :: < String > , # [doc = "**Mask weights**: Weights for each bind id in `mask_bind_ids`.\n\n*Attributes*: Debuggable, Networked"] @ [Debuggable , Networked , Name ["Mask weights"] , Description ["Weights for each bind id in `mask_bind_ids`."]] mask_weights : Vec :: < f32 > , # [doc = "**Retarget Model from URL**: Retarget the animation using the model at the given URL.\n\n*Attributes*: Debuggable, Networked, Store"] @ [Debuggable , Networked , Store , Name ["Retarget Model from URL"] , Description ["Retarget the animation using the model at the given URL."]] retarget_model_from_url : String , # [doc = "**Retarget animation scaled**: Retarget animation scaled. True means normalize hip.\n\n*Attributes*: Debuggable, Networked, Store"] @ [Debuggable , Networked , Store , Name ["Retarget animation scaled"] , Description ["Retarget animation scaled. True means normalize hip."]] retarget_animation_scaled : bool , # [doc = "**Apply base pose**: Apply the base pose to this clip.\n\n*Attributes*: Debuggable, Networked, Store"] @ [Debuggable , Networked , Store , Name ["Apply base pose"] , Description ["Apply the base pose to this clip."]] apply_base_pose : () , # [doc = "**Bind id**: Animation bind ID.\n\n*Attributes*: Debuggable, Networked, Store"] @ [Debuggable , Networked , Store , Name ["Bind id"] , Description ["Animation bind ID."]] bind_id : String , # [doc = "**Bind ids**: Animation bind IDs.\n\n*Attributes*: Debuggable, Store"] @ [Debuggable , Store , Name ["Bind ids"] , Description ["Animation bind IDs."]] bind_ids : Vec :: < String > , }); } } #[allow(unused)] diff --git a/guest/rust/api_core/src/internal/generated.rs b/guest/rust/api_core/src/internal/generated.rs index 7239804e4b..2ca67a9bf2 100644 --- a/guest/rust/api_core/src/internal/generated.rs +++ b/guest/rust/api_core/src/internal/generated.rs @@ -16,86 +16,92 @@ mod raw { static IS_ANIMATION_PLAYER: Lazy> = Lazy::new(|| { __internal_get_component("ambient_core::animation::is_animation_player") }); - #[doc = "**Is animation player**: This entity is treated as an animation player. Attach an animation node as a child for it to play.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] + #[doc = "**Is animation player**: This entity is treated as an animation player. Attach an animation node as a child for it to play.\n\n*Attributes*: Debuggable, Networked"] pub fn is_animation_player() -> Component<()> { *IS_ANIMATION_PLAYER } static ANIMATION_ERRORS: Lazy>> = Lazy::new(|| { __internal_get_component("ambient_core::animation::animation_errors") }); - #[doc = "**Animation errors**: A list of errors that were produced trying to play the animation.\n\n*Attributes*: MaybeResource, Debuggable"] + #[doc = "**Animation errors**: A list of errors that were produced trying to play the animation.\n\n*Attributes*: Debuggable"] pub fn animation_errors() -> Component> { *ANIMATION_ERRORS } static APPLY_ANIMATION_PLAYER: Lazy> = Lazy::new(|| { __internal_get_component("ambient_core::animation::apply_animation_player") }); - #[doc = "**Apply animation player**: Apply the designated animation player to this entity and its sub-tree.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] + #[doc = "**Apply animation player**: Apply the designated animation player to this entity and its sub-tree.\n\n*Attributes*: Debuggable, Networked"] pub fn apply_animation_player() -> Component { *APPLY_ANIMATION_PLAYER } static PLAY_CLIP_FROM_URL: Lazy> = Lazy::new(|| { __internal_get_component("ambient_core::animation::play_clip_from_url") }); - #[doc = "**Play clip from URL**: Make this entity a 'play animation clip' node. The value is the URL to the clip we'd like to play.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] + #[doc = "**Play clip from URL**: Make this entity a 'play animation clip' node. The value is the URL to the clip we'd like to play.\n\n*Attributes*: Debuggable, Networked"] pub fn play_clip_from_url() -> Component { *PLAY_CLIP_FROM_URL } static LOOPING: Lazy> = Lazy::new(|| __internal_get_component("ambient_core::animation::looping")); - #[doc = "**Looping**: When this is true, the animation clip will repeat infinitely.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] + #[doc = "**Looping**: When this is true, the animation clip will repeat infinitely.\n\n*Attributes*: Debuggable, Networked"] pub fn looping() -> Component { *LOOPING } static SPEED: Lazy> = Lazy::new(|| __internal_get_component("ambient_core::animation::speed")); - #[doc = "**Speed**: Animation playback speed. Default is 1, higher values speeds up the animation.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] + #[doc = "**Speed**: Animation playback speed. Default is 1, higher values speeds up the animation.\n\n*Attributes*: Debuggable, Networked"] pub fn speed() -> Component { *SPEED } static START_TIME: Lazy> = Lazy::new(|| __internal_get_component("ambient_core::animation::start_time")); - #[doc = "**Start time**: Start time of an animation node.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] + #[doc = "**Start time**: Start time of an animation node.\n\n*Attributes*: Debuggable, Networked"] pub fn start_time() -> Component { *START_TIME } static FREEZE_AT_PERCENTAGE: Lazy> = Lazy::new(|| { __internal_get_component("ambient_core::animation::freeze_at_percentage") }); - #[doc = "**Freeze at percentage**: Sample the input animation at a certain percentage of the animation track length.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] + #[doc = "**Freeze at percentage**: Sample the input animation at a certain percentage of the animation track length.\n\n*Attributes*: Debuggable, Networked"] pub fn freeze_at_percentage() -> Component { *FREEZE_AT_PERCENTAGE } static FREEZE_AT_TIME: Lazy> = Lazy::new(|| { __internal_get_component("ambient_core::animation::freeze_at_time") }); - #[doc = "**Freeze at time**: Sample the input animation at a certain time (in seconds).\n\n*Attributes*: MaybeResource, Debuggable, Networked"] + #[doc = "**Freeze at time**: Sample the input animation at a certain time (in seconds).\n\n*Attributes*: Debuggable, Networked"] pub fn freeze_at_time() -> Component { *FREEZE_AT_TIME } static CLIP_DURATION: Lazy> = Lazy::new(|| { __internal_get_component("ambient_core::animation::clip_duration") }); - #[doc = "**Clip duration**: The clip duration is loaded from the clip, and then applied to the entity.\n\n*Attributes*: MaybeResource, Debuggable"] + #[doc = "**Clip duration**: The clip duration is loaded from the clip, and then applied to the entity.\n\n*Attributes*: Debuggable"] pub fn clip_duration() -> Component { *CLIP_DURATION } + static CLIP_LOADED: Lazy> = + Lazy::new(|| __internal_get_component("ambient_core::animation::clip_loaded")); + #[doc = "**Clip loaded**: The clip has been loaded.\n\n*Attributes*: Debuggable"] + pub fn clip_loaded() -> Component<()> { + *CLIP_LOADED + } static BLEND: Lazy> = Lazy::new(|| __internal_get_component("ambient_core::animation::blend")); - #[doc = "**Blend**: Blend two animations together. The values is the blend weight. Use `children` to set the animations. Blend 0 means we only sample from the first animation, 1 means only the second one, and values in between blend between them.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] + #[doc = "**Blend**: Blend two animations together. The values is the blend weight. Use `children` to set the animations. Blend 0 means we only sample from the first animation, 1 means only the second one, and values in between blend between them.\n\n*Attributes*: Debuggable, Networked"] pub fn blend() -> Component { *BLEND } static MASK_BIND_IDS: Lazy>> = Lazy::new(|| { __internal_get_component("ambient_core::animation::mask_bind_ids") }); - #[doc = "**Mask bind ids**: List of bind ids that will be masked.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] + #[doc = "**Mask bind ids**: List of bind ids that will be masked.\n\n*Attributes*: Debuggable, Networked"] pub fn mask_bind_ids() -> Component> { *MASK_BIND_IDS } static MASK_WEIGHTS: Lazy>> = Lazy::new(|| __internal_get_component("ambient_core::animation::mask_weights")); - #[doc = "**Mask weights**: Weights for each bind id in `mask_bind_ids`.\n\n*Attributes*: MaybeResource, Debuggable, Networked"] + #[doc = "**Mask weights**: Weights for each bind id in `mask_bind_ids`.\n\n*Attributes*: Debuggable, Networked"] pub fn mask_weights() -> Component> { *MASK_WEIGHTS } diff --git a/schema/schema/animation.toml b/schema/schema/animation.toml index 788b27bd88..9676d165b9 100644 --- a/schema/schema/animation.toml +++ b/schema/schema/animation.toml @@ -9,79 +9,85 @@ version = "0.3.0-dev" type = "Empty" name = "Is animation player" description = "This entity is treated as an animation player. Attach an animation node as a child for it to play." -attributes = ["MaybeResource", "Debuggable", "Networked"] +attributes = ["Debuggable", "Networked"] [components.animation_errors] type = { type = "Vec", element_type = "String" } name = "Animation errors" description = "A list of errors that were produced trying to play the animation." -attributes = ["MaybeResource", "Debuggable"] +attributes = ["Debuggable"] [components.apply_animation_player] type = "EntityId" name = "Apply animation player" description = "Apply the designated animation player to this entity and its sub-tree." -attributes = ["MaybeResource", "Debuggable", "Networked"] +attributes = ["Debuggable", "Networked"] [components.play_clip_from_url] type = "String" name = "Play clip from URL" description = "Make this entity a 'play animation clip' node. The value is the URL to the clip we'd like to play." -attributes = ["MaybeResource", "Debuggable", "Networked"] +attributes = ["Debuggable", "Networked"] [components.looping] type = "Bool" name = "Looping" description = "When this is true, the animation clip will repeat infinitely." -attributes = ["MaybeResource", "Debuggable", "Networked"] +attributes = ["Debuggable", "Networked"] [components.speed] type = "F32" name = "Speed" description = "Animation playback speed. Default is 1, higher values speeds up the animation." -attributes = ["MaybeResource", "Debuggable", "Networked"] +attributes = ["Debuggable", "Networked"] [components.start_time] type = "Duration" name = "Start time" description = "Start time of an animation node." -attributes = ["MaybeResource", "Debuggable", "Networked"] +attributes = ["Debuggable", "Networked"] [components.freeze_at_percentage] type = "F32" name = "Freeze at percentage" description = "Sample the input animation at a certain percentage of the animation track length." -attributes = ["MaybeResource", "Debuggable", "Networked"] +attributes = ["Debuggable", "Networked"] [components.freeze_at_time] type = "F32" name = "Freeze at time" description = "Sample the input animation at a certain time (in seconds)." -attributes = ["MaybeResource", "Debuggable", "Networked"] +attributes = ["Debuggable", "Networked"] [components.clip_duration] type = "F32" name = "Clip duration" description = "The clip duration is loaded from the clip, and then applied to the entity." -attributes = ["MaybeResource", "Debuggable"] +attributes = ["Debuggable"] + +[components.clip_loaded] +type = "Empty" +name = "Clip loaded" +description = "The clip has been loaded." +attributes = ["Debuggable"] [components.blend] type = "F32" name = "Blend" description = "Blend two animations together. The values is the blend weight. Use `children` to set the animations. Blend 0 means we only sample from the first animation, 1 means only the second one, and values in between blend between them." -attributes = ["MaybeResource", "Debuggable", "Networked"] +attributes = ["Debuggable", "Networked"] [components.mask_bind_ids] type = { type = "Vec", element_type = "String" } name = "Mask bind ids" description = "List of bind ids that will be masked." -attributes = ["MaybeResource", "Debuggable", "Networked"] +attributes = ["Debuggable", "Networked"] [components.mask_weights] type = { type = "Vec", element_type = "F32" } name = "Mask weights" description = "Weights for each bind id in `mask_bind_ids`." -attributes = ["MaybeResource", "Debuggable", "Networked"] +attributes = ["Debuggable", "Networked"] [components.retarget_model_from_url] type = "String" From b5459083cbb350a19fbdfad7897ca707532ee254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Nore=CC=81n?= Date: Tue, 5 Sep 2023 12:34:59 +0200 Subject: [PATCH 3/6] Animations using the element infra --- guest/rust/Cargo.lock | 10 + guest/rust/Cargo.toml | 1 + guest/rust/api/Cargo.toml | 1 + guest/rust/api/src/animation_element.rs | 141 +++++++ guest/rust/api/src/lib.rs | 2 + .../games/afps/core/fpsanim/src/server.rs | 384 +++++++----------- 6 files changed, 308 insertions(+), 231 deletions(-) create mode 100644 guest/rust/api/src/animation_element.rs diff --git a/guest/rust/Cargo.lock b/guest/rust/Cargo.lock index 8283e4564b..8e0dfe8a48 100644 --- a/guest/rust/Cargo.lock +++ b/guest/rust/Cargo.lock @@ -119,6 +119,7 @@ dependencies = [ "futures", "glam", "once_cell", + "ordered-float", "rand 0.8.5", ] @@ -1622,6 +1623,15 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "ordered-float" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a54938017eacd63036332b4ae5c8a49fc8c0c1d6d629893057e4f13609edd06" +dependencies = [ + "num-traits", +] + [[package]] name = "output_vt100" version = "0.1.3" diff --git a/guest/rust/Cargo.toml b/guest/rust/Cargo.toml index 95a98b79c6..41a20d6fc7 100644 --- a/guest/rust/Cargo.toml +++ b/guest/rust/Cargo.toml @@ -114,3 +114,4 @@ wit-bindgen = { version = "0.9.0", features = ["realloc"] } ulid = { version = "1.0.0", features = ["serde"] } indexmap = { version = "2.0", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } +ordered-float = "3.9.1" diff --git a/guest/rust/api/Cargo.toml b/guest/rust/api/Cargo.toml index a4067c412c..176768d21c 100644 --- a/guest/rust/api/Cargo.toml +++ b/guest/rust/api/Cargo.toml @@ -26,6 +26,7 @@ futures = { workspace = true } once_cell = { workspace = true } rand = { workspace = true } glam = { workspace = true } +ordered-float = { workspace = true } [features] client = [ diff --git a/guest/rust/api/src/animation_element.rs b/guest/rust/api/src/animation_element.rs new file mode 100644 index 0000000000..c9c50d92a4 --- /dev/null +++ b/guest/rust/api/src/animation_element.rs @@ -0,0 +1,141 @@ +use ambient_api_core::{ + core::{ + animation::{ + self, + components::{play_clip_from_url, start_time}, + }, + app::components::name, + }, + prelude::epoch_time, +}; +use ambient_element::{element_component, Element, ElementComponentExt, Hooks}; + +/// An animation player +#[element_component] +pub fn AnimationPlayer(_hooks: &mut Hooks, root: Element) -> Element { + Element::new() + .with(animation::components::is_animation_player(), ()) + .children(vec![root]) + .with(name(), "Animation player".to_string()) +} + +/// Play an animation clip from an URL +#[element_component] +pub fn PlayClipFromUrl( + _hooks: &mut Hooks, + /// Url to clip + url: String, + /// Loop the animation + looping: bool, +) -> Element { + Element::new() + .with(play_clip_from_url(), url.into()) + .with(name(), "Play clip from URL".to_string()) + .with(animation::components::looping(), looping) + .init(start_time(), epoch_time()) +} + +/// Blend animation clips together +#[element_component(without_el)] +pub fn BlendNode( + _hooks: &mut Hooks, + /// Left animation node + left: Element, + /// Right animation node + right: Element, + /// Weight (0 means left, 1 means right, 0.5 means half left and half right) + weight: f32, +) -> Element { + if weight <= 0. { + left + } else if weight >= 1. { + right + } else { + Element::new() + .with(animation::components::blend(), weight) + .with(name(), "Blend".to_string()) + .children(vec![left, right]) + } +} +impl BlendNode { + /// Create a blend node and turn it into an Element + pub fn el(left: Element, right: Element, weight: f32) -> Element { + if weight <= 0. { + left + } else if weight >= 1. { + right + } else { + Self { + left, + right, + weight, + } + .el() + } + } + /// Creates a tree of blend nodes where the weights are arbitrary, for example + /// `BlendNode::normalize_multiblend(vec![("a", 1.), ("b", 20.), ("c", 3.)])` will create a tree + /// where b is the strongest contribution + pub fn normalize_multiblend(items: Vec<(Element, f32)>) -> Element { + let total_weight = items.iter().map(|x| x.1).sum::(); + if total_weight <= 0. { + return Element::new(); + } + let mut items = items + .into_iter() + .map(|(a, w)| (a, w / total_weight)) + .collect::>(); + items.retain(|x| x.1 > 0.); + items.sort_by_key(|x| -ordered_float::OrderedFloat(x.1)); + for x in items.iter_mut() { + x.1 = 1. - x.1; + } + Self::multiblend(items) + } + /// Creates a tree of blend nodes, where each weight is the blend between that element and the next, + /// for example: + /// `BlendNode::multiblend(vec![("a", 0.5), ("b", 0.2), ("c", 0.)])` will create a tree where + /// b is first blended with c, using 20% of b and 80% of b + /// The result if that is then blended with a, using 50% of the result and 50% of a + pub fn multiblend(mut items: Vec<(Element, f32)>) -> Element { + if items.len() == 0 { + Element::new() + } else if items.len() == 1 { + items.pop().unwrap().0 + } else { + let item = items.remove(0); + Self::el(item.0, Self::multiblend(items), item.1) + } + } +} + +/// Transition between multiple animations +#[element_component] +pub fn Transition( + hooks: &mut Hooks, + /// The animations that can be transitioned between + animations: Vec, + /// The index of the active animation + active: usize, + /// The speed that the transitions happen at + speed: f32, +) -> Element { + let weights = hooks.use_ref_with(|_| { + animations + .iter() + .enumerate() + .map(|(i, _)| if i == active { 1. } else { 0. }) + .collect::>() + }); + let mut weights = weights.lock(); + for (i, weight) in weights.iter_mut().enumerate() { + let target = if i == active { 1. } else { 0. }; + *weight = *weight * (1. - speed) + target * speed; + } + BlendNode::normalize_multiblend( + animations + .into_iter() + .zip(weights.iter().cloned()) + .collect(), + ) +} diff --git a/guest/rust/api/src/lib.rs b/guest/rust/api/src/lib.rs index 83c48d5405..420890fbd9 100644 --- a/guest/rust/api/src/lib.rs +++ b/guest/rust/api/src/lib.rs @@ -18,6 +18,8 @@ pub use ambient_editor_derive as editor_derive; pub use ambient_element as element; pub use ambient_ui as ui; +pub mod animation_element; + pub mod prelude { pub use ambient_api_core::prelude::*; pub use ambient_editor_derive::ElementEditor; diff --git a/guest/rust/packages/games/afps/core/fpsanim/src/server.rs b/guest/rust/packages/games/afps/core/fpsanim/src/server.rs index 9f6a9b0f1f..d6e4b77201 100644 --- a/guest/rust/packages/games/afps/core/fpsanim/src/server.rs +++ b/guest/rust/packages/games/afps/core/fpsanim/src/server.rs @@ -1,8 +1,8 @@ use ambient_api::{ - animation::{AnimationPlayerRef, BlendNodeRef, PlayClipFromUrlNodeRef}, + animation::PlayClipFromUrlNodeRef, + animation_element::{AnimationPlayer, BlendNode, PlayClipFromUrl, Transition}, core::{ - animation::components::apply_animation_player, - ecs::components::{children, parent}, + animation::components::{apply_animation_player, start_time}, player::components::is_player, }, prelude::*, @@ -11,196 +11,163 @@ use packages::{ afps_schema::components::{ player_direction, player_health, player_jumping, player_model_ref, player_running, }, - this::{assets, components}, + this::assets, +}; +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, }; -fn calculate_blend_from_weight(weights: &[f32]) -> Vec { - assert!(weights.len() >= 2); - let mut blend = Vec::with_capacity(weights.len() - 1); - let total = weights.iter().sum::(); - // left weight is used to compare left and right - let mut left_weight = weights[0] / total; - for i in 0..weights.len() - 1 { - left_weight += weights[i + 1] / total; - let b: f32 = if left_weight != 0.0 { - weights[i + 1] / left_weight - } else { - 0.0 - }; - blend.push(b); - } - blend +fn anim_url(name: &str) -> String { + assets::url(&format!("{name}/animations/mixamo.com.anim")) } -fn add_anim_clip_and_blend_to_player(id: EntityId) { - let walk_fd = make_node("Walk Forward.fbx"); - let walk_bk = make_node("Walk Backward.fbx"); - let walk_lt = make_node("Walk Left.fbx"); - let walk_rt = make_node("Walk Right.fbx"); - let walk_fd_lt = make_node("Walk Forward Left.fbx"); - let walk_fd_rt = make_node("Walk Forward Right.fbx"); - let walk_bk_lt = make_node("Walk Backward Left.fbx"); - let walk_bk_rt = make_node("Walk Backward Right.fbx"); - let run_fd = make_node("Run Forward.fbx"); - let run_bk = make_node("Run Backward.fbx"); - let run_lt = make_node("Run Left.fbx"); - let run_rt = make_node("Run Right.fbx"); - let run_fd_lt = make_node("Run Forward Left.fbx"); - let run_fd_rt = make_node("Run Forward Right.fbx"); - let run_bk_lt = make_node("Run Backward Left.fbx"); - let run_bk_rt = make_node("Run Backward Right.fbx"); - - let idle = make_node("Rifle Aiming Idle.fbx"); - let blend1 = BlendNodeRef::new(&walk_fd, &walk_bk, 0.0); - let blend2 = BlendNodeRef::new(&blend1, &walk_lt, 0.0); - let blend3 = BlendNodeRef::new(&blend2, &walk_rt, 0.0); - let blend4 = BlendNodeRef::new(&blend3, &walk_fd_lt, 0.0); - let blend5 = BlendNodeRef::new(&blend4, &walk_fd_rt, 0.0); - let blend6 = BlendNodeRef::new(&blend5, &walk_bk_lt, 0.0); - let blend7 = BlendNodeRef::new(&blend6, &walk_bk_rt, 0.0); - let blend8 = BlendNodeRef::new(&blend7, &run_fd, 0.0); - let blend9 = BlendNodeRef::new(&blend8, &run_bk, 0.0); - let blend10 = BlendNodeRef::new(&blend9, &run_lt, 0.0); - let blend11 = BlendNodeRef::new(&blend10, &run_rt, 0.0); - let blend12 = BlendNodeRef::new(&blend11, &run_fd_lt, 0.0); - let blend13 = BlendNodeRef::new(&blend12, &run_fd_rt, 0.0); - let blend14 = BlendNodeRef::new(&blend13, &run_bk_lt, 0.0); - let blend15 = BlendNodeRef::new(&blend14, &run_bk_rt, 0.0); - let blend16 = BlendNodeRef::new(&blend15, &idle, 0.0); - let output = BlendNodeRef::new(&blend16, &blend16, 0.0); // the right one is dummy - entity::add_component( - id, - components::player_output_blend_node(), - output.0.get_entity_id(), - ); - - entity::add_component( - id, - components::player_persistant_anim_node(), - blend16.0.get_entity_id(), +#[element_component] +fn UnitAnimation( + _hooks: &mut Hooks, + direction: Vec2, + running: bool, + jumping: bool, + health: i32, +) -> Element { + println!( + "UnitAnimation running: {running}, jumping: {jumping}, health: {health} direction: {:?}", + direction ); - - entity::add_component( - id, - components::player_anim_blend(), - vec![ - blend1.0.get_entity_id(), - blend2.0.get_entity_id(), - blend3.0.get_entity_id(), - blend4.0.get_entity_id(), - blend5.0.get_entity_id(), - blend6.0.get_entity_id(), - blend7.0.get_entity_id(), - blend8.0.get_entity_id(), - blend9.0.get_entity_id(), - blend10.0.get_entity_id(), - blend11.0.get_entity_id(), - blend12.0.get_entity_id(), - blend13.0.get_entity_id(), - blend14.0.get_entity_id(), - blend15.0.get_entity_id(), - blend16.0.get_entity_id(), - ], - ); -} - -fn get_blend_node_for_playing(id: EntityId, index: usize) -> Option { - let node = entity::get_component(id, components::player_anim_blend())?; - if node.len() <= index { - return None; + AnimationPlayer { + root: Transition { + animations: vec![ + PlayClipFromUrl { + url: anim_url("Rifle Death.fbx"), + looping: false, + } + .el() + .key("death"), + PlayClipFromUrl { + url: anim_url("Rifle Jump.fbx"), + looping: false, + } + .el() + .key("jump"), + Walk { direction, running }.el(), + ], + active: if health <= 0 { + 0 + } else if jumping { + 1 + } else { + 2 + }, + speed: 0.3, + } + .el(), } - let init_node = node[index]; - // let node = AnimationNode::from_entity(init_node); - let blend_node = BlendNodeRef::from_entity(init_node); - Some(blend_node) + .el() } -fn set_blend_weights_on_entity(id: EntityId, blend_weights: Vec) { - for (i, weight) in blend_weights.iter().enumerate() { - let blend_node = get_blend_node_for_playing(id, i).unwrap(); - blend_node.set_weight(*weight); +#[element_component] +fn Walk(hooks: &mut Hooks, running: bool, direction: Vec2) -> Element { + let lagging_direction = hooks.use_ref_with(|_| Vec3::ZERO); + let mut lag_dir = lagging_direction.lock(); + *lag_dir = lag_dir.lerp(direction.extend(if running { 1. } else { 0. }), 0.1); + + fn walkblend(items: &[(&str, f32)]) -> Element { + BlendNode::normalize_multiblend( + items + .into_iter() + .map(|(a, w)| { + ( + PlayClipFromUrl { + url: anim_url(a), + looping: true, + } + .el() + .with(start_time(), Duration::ZERO), + 1. - w.clamp(0., 1.), + ) + }) + .collect(), + ) } + walkblend(&[ + ("Rifle Aiming Idle.fbx", lag_dir.xy().distance(Vec2::ZERO)), + ("Walk Forward.fbx", lag_dir.distance(vec3(0., -1., 0.))), + ( + "Walk Forward Left.fbx", + lag_dir.distance(vec3(-1., -1., 0.)), + ), + ( + "Walk Forward Right.fbx", + lag_dir.distance(vec3(1., -1., 0.)), + ), + ("Walk Backward.fbx", lag_dir.distance(vec3(0., 1., 0.))), + ( + "Walk Backward Left.fbx", + lag_dir.distance(vec3(-1., 1., 0.)), + ), + ( + "Walk Backward Right.fbx", + lag_dir.distance(vec3(1., 1., 0.)), + ), + ("Walk Left.fbx", lag_dir.distance(vec3(-1., 0., 0.))), + ("Walk Right.fbx", lag_dir.distance(vec3(1., 0., 0.))), + ("Run Forward.fbx", lag_dir.distance(vec3(0., -1., 1.))), + ("Run Forward Left.fbx", lag_dir.distance(vec3(-1., -1., 1.))), + ("Run Forward Right.fbx", lag_dir.distance(vec3(1., -1., 1.))), + ("Run Backward.fbx", lag_dir.distance(vec3(0., 1., 1.))), + ("Run Backward Left.fbx", lag_dir.distance(vec3(-1., 1., 1.))), + ("Run Backward Right.fbx", lag_dir.distance(vec3(1., 1., 1.))), + ("Run Left.fbx", lag_dir.distance(vec3(-1., 0., 1.))), + ("Run Right.fbx", lag_dir.distance(vec3(1., 0., 1.))), + ]) } #[main] pub fn main() { - spawn_query((is_player(), player_model_ref())).bind(move |v| { - for (id, (_, model)) in v { - add_anim_clip_and_blend_to_player(id); - let output_blend_node = - entity::get_component(id, components::player_output_blend_node()).unwrap(); - let blend_node = BlendNodeRef::from_entity(output_blend_node); - let anim_player = AnimationPlayerRef::new(blend_node); - entity::add_component(model, apply_animation_player(), anim_player.0); - entity::add_component(id, player_jumping(), false); - } - }); - - change_query(( - is_player(), - player_health(), - components::player_output_blend_node(), - components::player_persistant_anim_node(), - )) - .track_change(player_health()) - .bind(move |res| { - for (_, (_, health, output_node, persistant_node)) in res { - if health <= 0 { - let clip = make_node("Rifle Death.fbx"); - clip.looping(false); + fn preload(name: &str) { + PlayClipFromUrlNodeRef::new(anim_url(name)); + } + preload("Walk Forward.fbx"); + preload("Walk Backward.fbx"); + preload("Walk Left.fbx"); + preload("Walk Right.fbx"); + preload("Walk Forward Left.fbx"); + preload("Walk Forward Right.fbx"); + preload("Walk Backward Left.fbx"); + preload("Walk Backward Right.fbx"); + preload("Run Forward.fbx"); + preload("Run Backward.fbx"); + preload("Run Left.fbx"); + preload("Run Right.fbx"); + preload("Run Forward Left.fbx"); + preload("Run Forward Right.fbx"); + preload("Run Backward Left.fbx"); + preload("Run Backward Right.fbx"); + preload("Rifle Aiming Idle.fbx"); - entity::mutate_component(output_node, children(), |v| { - v.clear(); - v.push(clip.0.get_entity_id()); - v.push(clip.0.get_entity_id()); - }); - entity::add_component(clip.0.get_entity_id(), parent(), output_node); + let anims = Arc::new(Mutex::new(HashMap::::new())); - run_async(async move { - let clip = make_node("Rifle Death.fbx"); - clip.looping(false); - let dur = clip.clip_duration().await; - sleep(dur).await; - // after death animation, play the blend node again - entity::mutate_component(output_node, children(), |v| { - v.clear(); - v.push(persistant_node); - v.push(persistant_node); - }); - entity::add_component(persistant_node, parent(), output_node); - }); - }; - } - }); + spawn_query((is_player(), player_model_ref())).bind({ + let anims = anims.clone(); + move |v| { + let mut anims = anims.lock().unwrap(); + for (id, (_, model)) in v { + let tree = UnitAnimation { + direction: Vec2::ZERO, + running: false, + jumping: false, + health: 100, + } + .el() + .spawn_tree(); + entity::add_component(model, apply_animation_player(), tree.root_entity().unwrap()); + anims.insert(id, tree); - change_query(( - is_player(), - player_jumping(), - components::player_output_blend_node(), - components::player_persistant_anim_node(), - )) - .track_change(player_jumping()) - .bind(move |res| { - for (_, (_, is_jumping, output_node, persistant_node)) in res { - if is_jumping { - let clip = make_node("Rifle Jump.fbx"); - clip.looping(false); - entity::mutate_component(output_node, children(), |v| { - v.clear(); - v.push(clip.0.get_entity_id()); - v.push(clip.0.get_entity_id()); - }); - entity::add_component(clip.0.get_entity_id(), parent(), output_node); - } else { - entity::mutate_component(output_node, children(), |v| { - v.clear(); - v.push(persistant_node); - v.push(persistant_node); // we had to add the second one for blend node to work - }); - entity::add_component(persistant_node, parent(), output_node); + entity::add_component(id, player_jumping(), false); } } }); + query(( is_player(), player_model_ref(), @@ -210,65 +177,20 @@ pub fn main() { player_jumping(), )) .each_frame(move |res| { + let mut anims = anims.lock().unwrap(); for (player_id, (_, _model, dir, is_running, health, jump)) in res { - if health <= 0 { - continue; - } - if jump { - continue; - } - let mut weights = vec![0.0; 17]; - - let fd = dir.y == -1.0; - let bk = dir.y == 1.0; - let lt = dir.x == -1.0; - let rt = dir.x == 1.0; - - if !is_running { - if fd && !lt && !rt { - weights[0] = 1.0; - } else if bk && !lt && !rt { - weights[1] = 1.0; - } else if lt && !fd && !bk { - weights[2] = 1.0; - } else if rt && !fd && !bk { - weights[3] = 1.0; - } else if fd && lt { - weights[4] = 1.0; - } else if fd && rt { - weights[5] = 1.0; - } else if bk && lt { - weights[6] = 1.0; - } else if bk && rt { - weights[7] = 1.0; - } else { - weights[16] = 1.0; + let tree = anims.get_mut(&player_id).unwrap(); + tree.migrate_root( + &mut World, + UnitAnimation { + direction: dir, + running: is_running, + jumping: jump, + health, } - } else if fd && !lt && !rt { - weights[8] = 1.0; - } else if bk && !lt && !rt { - weights[9] = 1.0; - } else if lt && !fd && !bk { - weights[10] = 1.0; - } else if rt && !fd && !bk { - weights[11] = 1.0; - } else if fd && lt { - weights[12] = 1.0; - } else if fd && rt { - weights[13] = 1.0; - } else if bk && lt { - weights[14] = 1.0; - } else if bk && rt { - weights[15] = 1.0; - } else { - weights[16] = 1.0; - } - let blend_weight = calculate_blend_from_weight(&weights); - set_blend_weights_on_entity(player_id, blend_weight); + .el(), + ); + tree.update(&mut World); } }); } - -fn make_node(url: &str) -> PlayClipFromUrlNodeRef { - PlayClipFromUrlNodeRef::new(assets::url(&format!("{url}/animations/mixamo.com.anim"))) -} From d71d02d0c615039108ac19313caccfa30f5888d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Nore=CC=81n?= Date: Tue, 5 Sep 2023 13:45:08 +0200 Subject: [PATCH 4/6] Remove unused components --- .../games/afps/core/fpsanim/ambient.toml | 177 ------------------ 1 file changed, 177 deletions(-) diff --git a/guest/rust/packages/games/afps/core/fpsanim/ambient.toml b/guest/rust/packages/games/afps/core/fpsanim/ambient.toml index 51b2b2533e..0e127718f0 100644 --- a/guest/rust/packages/games/afps/core/fpsanim/ambient.toml +++ b/guest/rust/packages/games/afps/core/fpsanim/ambient.toml @@ -4,182 +4,5 @@ name = "AFPS Animation" version = "0.0.1" content = { type = "Asset", animations = true, code = true } -[components] -walk_fd = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -walk_bk = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -walk_lt = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -walk_rt = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -walk_fd_lt = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -walk_fd_rt = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -walk_bk_lt = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -walk_bk_rt = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - - -run_fd = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -run_bk = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -run_lt = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -run_rt = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -run_fd_lt = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -run_fd_rt = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -run_bk_lt = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -run_bk_rt = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - - -run = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -jump = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -hit = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -death = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - -fire = { type = { type = "Vec", element_type = "EntityId" }, name = "fire", description = "fire", attributes = [ - "Debuggable", - "Networked", -] } - -fd_rt = { type = { type = "Vec", element_type = "EntityId" }, name = "fd_rt", description = "fd_rt", attributes = [ - "Debuggable", - "Networked", -] } -fd_lt = { type = { type = "Vec", element_type = "EntityId" }, name = "fd_rt", description = "fd_rt", attributes = [ - "Debuggable", - "Networked", -] } -bk_lt = { type = { type = "Vec", element_type = "EntityId" }, name = "fd_rt", description = "fd_rt", attributes = [ - "Debuggable", - "Networked", -] } -bk_rt = { type = { type = "Vec", element_type = "EntityId" }, name = "fd_rt", description = "fd_rt", attributes = [ - "Debuggable", - "Networked", -] } -idle_fd = { type = { type = "Vec", element_type = "EntityId" }, name = "fd_rt", description = "fd_rt", attributes = [ - "Debuggable", - "Networked", -] } -idle_bk = { type = { type = "Vec", element_type = "EntityId" }, name = "fd_rt", description = "fd_rt", attributes = [ - "Debuggable", - "Networked", -] } -idle_lt = { type = { type = "Vec", element_type = "EntityId" }, name = "fd_rt", description = "fd_rt", attributes = [ - "Debuggable", - "Networked", -] } -idle_rt = { type = { type = "Vec", element_type = "EntityId" }, name = "fd_rt", description = "fd_rt", attributes = [ - "Debuggable", - "Networked", -] } -idle_fd_lt = { type = { type = "Vec", element_type = "EntityId" }, name = "fd_rt", description = "fd_rt", attributes = [ - "Debuggable", - "Networked", -] } -idle_fd_rt = { type = { type = "Vec", element_type = "EntityId" }, name = "fd_rt", description = "fd_rt", attributes = [ - "Debuggable", - "Networked", -] } -idle_bk_lt = { type = { type = "Vec", element_type = "EntityId" }, name = "fd_rt", description = "fd_rt", attributes = [ - "Debuggable", - "Networked", -] } -idle_bk_rt = { type = { type = "Vec", element_type = "EntityId" }, name = "fd_rt", description = "fd_rt", attributes = [ - "Debuggable", - "Networked", -] } - -player_persistant_anim_node = { type = "EntityId", attributes = [ - "Debuggable", - "Networked", -] } - -player_output_blend_node = { type = "EntityId", attributes = [ - "Debuggable", - "Networked", -] } - -player_anim_blend_weights = { type = { type = "Vec", element_type = "F32" }, attributes = [ - "Debuggable", - "Networked", -] } - -player_anim_blend = { type = { type = "Vec", element_type = "EntityId" }, attributes = [ - "Debuggable", - "Networked", -] } - [dependencies] afps_schema = { path = "../../schema", deployment = "4qcnuQpcxcMDogPdNsKl6s" } From d9f278cfe1a76edd3f1852590ef523be6aeac4d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Nore=CC=81n?= Date: Tue, 5 Sep 2023 14:00:51 +0200 Subject: [PATCH 5/6] Clippy --- guest/rust/api/src/animation_element.rs | 4 ++-- guest/rust/api_core/src/animation.rs | 2 +- guest/rust/examples/basics/skinmesh/src/client.rs | 6 +++--- guest/rust/packages/games/afps/core/fpsanim/src/server.rs | 2 +- guest/rust/packages/games/afps/mods/zombie/src/server.rs | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/guest/rust/api/src/animation_element.rs b/guest/rust/api/src/animation_element.rs index c9c50d92a4..a5136bc0c3 100644 --- a/guest/rust/api/src/animation_element.rs +++ b/guest/rust/api/src/animation_element.rs @@ -29,7 +29,7 @@ pub fn PlayClipFromUrl( looping: bool, ) -> Element { Element::new() - .with(play_clip_from_url(), url.into()) + .with(play_clip_from_url(), url) .with(name(), "Play clip from URL".to_string()) .with(animation::components::looping(), looping) .init(start_time(), epoch_time()) @@ -98,7 +98,7 @@ impl BlendNode { /// b is first blended with c, using 20% of b and 80% of b /// The result if that is then blended with a, using 50% of the result and 50% of a pub fn multiblend(mut items: Vec<(Element, f32)>) -> Element { - if items.len() == 0 { + if items.is_empty() { Element::new() } else if items.len() == 1 { items.pop().unwrap().0 diff --git a/guest/rust/api_core/src/animation.rs b/guest/rust/api_core/src/animation.rs index ffc49a6706..92c936775b 100644 --- a/guest/rust/api_core/src/animation.rs +++ b/guest/rust/api_core/src/animation.rs @@ -34,7 +34,7 @@ impl AnimationPlayerRef { } fn root(&self) -> Option { if let Some(children) = entity::get_component(self.0, children()) { - children.get(0).copied().map(|id| AnimationNodeRef(id)) + children.get(0).copied().map(AnimationNodeRef) } else { None } diff --git a/guest/rust/examples/basics/skinmesh/src/client.rs b/guest/rust/examples/basics/skinmesh/src/client.rs index 8dbf3bb05d..cb229a8794 100644 --- a/guest/rust/examples/basics/skinmesh/src/client.rs +++ b/guest/rust/examples/basics/skinmesh/src/client.rs @@ -50,8 +50,8 @@ pub async fn main() { let robot = PlayClipFromUrlNodeRef::new(assets::url( "Robot Hip Hop Dance.fbx/animations/mixamo.com.anim", )); - let blend = BlendNodeRef::new(&capoeira, &robot, 0.); - let anim_player = AnimationPlayerRef::new(&blend); + let blend = BlendNodeRef::new(capoeira, robot, 0.); + let anim_player = AnimationPlayerRef::new(blend); entity::add_component(unit_id, apply_animation_player(), anim_player.0); println!("Robot duration: {} sec", robot.clip_duration().await); @@ -143,7 +143,7 @@ fn App( }) .el(), Button::new("Play blend animation", move |_| { - anim_player.play(blend_node.clone()); + anim_player.play(blend_node); }) .el(), Button::new("Freeze animation", move |_| { diff --git a/guest/rust/packages/games/afps/core/fpsanim/src/server.rs b/guest/rust/packages/games/afps/core/fpsanim/src/server.rs index d6e4b77201..4b994b2b8b 100644 --- a/guest/rust/packages/games/afps/core/fpsanim/src/server.rs +++ b/guest/rust/packages/games/afps/core/fpsanim/src/server.rs @@ -74,7 +74,7 @@ fn Walk(hooks: &mut Hooks, running: bool, direction: Vec2) -> Element { fn walkblend(items: &[(&str, f32)]) -> Element { BlendNode::normalize_multiblend( items - .into_iter() + .iter() .map(|(a, w)| { ( PlayClipFromUrl { diff --git a/guest/rust/packages/games/afps/mods/zombie/src/server.rs b/guest/rust/packages/games/afps/mods/zombie/src/server.rs index f9a652872a..2abdd9e5e9 100644 --- a/guest/rust/packages/games/afps/mods/zombie/src/server.rs +++ b/guest/rust/packages/games/afps/mods/zombie/src/server.rs @@ -56,8 +56,8 @@ pub async fn main() { "Zombie Run.fbx/animations/mixamo.com.anim", )); - let blend = BlendNodeRef::new(&run, &run, 0.); - let anim_player = AnimationPlayerRef::new(&blend); + let blend = BlendNodeRef::new(run, run, 0.); + let anim_player = AnimationPlayerRef::new(blend); entity::add_component(model, apply_animation_player(), anim_player.0); sleep(random::()).await; From 7cc51d6dbb8bd7e390ed31b747046240211d1efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Nore=CC=81n?= Date: Tue, 5 Sep 2023 14:04:23 +0200 Subject: [PATCH 6/6] Remove println --- guest/rust/packages/games/afps/core/fpsanim/src/server.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/guest/rust/packages/games/afps/core/fpsanim/src/server.rs b/guest/rust/packages/games/afps/core/fpsanim/src/server.rs index 4b994b2b8b..61207898e3 100644 --- a/guest/rust/packages/games/afps/core/fpsanim/src/server.rs +++ b/guest/rust/packages/games/afps/core/fpsanim/src/server.rs @@ -30,10 +30,6 @@ fn UnitAnimation( jumping: bool, health: i32, ) -> Element { - println!( - "UnitAnimation running: {running}, jumping: {jumping}, health: {health} direction: {:?}", - direction - ); AnimationPlayer { root: Transition { animations: vec![