Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full featured property based Animation System #1429

Closed
wants to merge 173 commits into from

Conversation

lassade
Copy link
Contributor

@lassade lassade commented Feb 11, 2021

Any type that implements bevy_reflect::Struct can have their attributes animated.

Has a safety restriction only attributes that are within the struct memory limits can be addressed and animated,
this means you can't animate items behind a Vec, array types could be animated but nested properties implementation
is missing.

The system main goal is to provide an low level api to play and blend animations.
That been said an animation state machine should be built by the user or provided by a external crate.

Features

  1. Property based animation
let mut clip = Clip::default();
/// A property is defined by the entity path followed by the component property
clip.add_curve_at_path("/Node1/Node2@Transform.translation", track_data);
  1. Animate any type using bevy_reflect
#[derive(Debug, Reflect)]
#[reflect(Component)]
struct CustomComponent {
    value: f32,
    point: Vec3,
    #[reflect(ignore)]
    non_animated: Vec3,
}

// During app startup
app_builder.register_animated_component::<bevy_transform::prelude::Transform>();

let mut clip = Clip::default();
clip.add_curve_at_path("@CustomComponent.value", ...);
clip.add_curve_at_path("@CustomComponent.point", ...);
  1. Animate assets by swapping handles or animate their properties
// During app startup
app_builder.register_animated_asset::<bevy_pbr::prelude::StandardMaterial>();

let mut clip = Clip::default();
// Swap material with
clip.add_curve_at_path("@Handle<StandardMaterial>", ...);
// Change the current material properties
clip.add_curve_at_path("@Handle<StandardMaterial>.albedo", ...);
clip.add_curve_at_path("@Handle<StandardMaterial>.albedo_texture", ...);
  1. GLTF support

You can load any animation clips embedded within a GLTF file

// Loads the skinned mesh ready to be animated with all of its clips (if any)
let char_scene_handle = asset_server.load("models/character_medium/character_medium.gltf");
// Returns a single clip from the GLTF
let clip_handle = asset_server.load("models/character_medium/idle.gltf#Anim0");
  1. Animation Blending

It's possible to blend bool, f32, Vec2, Vec3, Vec4, Quat, Handle<T>, Option<T> when T can also be blended, and more.

  1. Animation clips defined by a named hierarchy

Each Animator can only animate children entities, this is similar to other engines like Unity;

Each clip have it's own hierarchy space, this allows clips to be reused for different setups that
share some similarities.

Note that this isn't a humanoid retargeting system, but it could give you some good results is some particular situations;

  1. Mostly safe :p

This crate uses a couple unsafe's blocks, the safety rules of these blocks should be guaranteed
at within the scope of a function or file (in the worst case) this way changes can be more easily
made without the fear braking random safety conditions, as a bonus they should be asserted
by code whenever possible;

The unsafe code still needs a review

  1. Fast TM

a) Each component animation runs in parallel from each other
b) Keyframe cursor, allows for faster sample rates by remembering the last keyframe it was last frame
c) NLerp only and you wont notice the difference, hopefully ...

  1. Skinned Mesh support out of the box

Simple and naive implementation, replaces the vertex shader from the bevy_pbr::prelude::StandardMaterial.
Further work should done along side with the rendering, but hey it works!

  1. Additive Blending

Supports additive blending as defined by the ACL mode Additive 1, that can be found
here

  1. SIMD support (REMOVED for the time been)

Clips can now be packed, a packed clip will contain tracks that are sampled using wide types from the ultraviolet crate
thus resulting in a greater sampling throughput.

Missing Features

  1. Nested properties

Right now only the top most properties of each struct can be animated,
this means "@CustomComponent.point.x" isn't valid;

  1. Masks

  2. Animation events (simple and interval)

  3. Morph Targets

  4. Dedicated Transform animator system

Transform is the most common animated component in any game, its the only animated component registered
by default and it should receive a custom animator system to squeeze every single us that is possible,
this custom system should be also be capable of performing SIMD blending;

  1. Root Motion

Terminology and Internals

A Clip is made of a collection of Tracks, each Track animates a single property in a single entity; The animator works by layering multiple Clips on top of each other in order to evaluate the final animation; So each Layer have information about how to play a single Clip, like time and weight as well some other complicated stuff to make everything work;

Each Clip stores its own hierarchy of entities that should be animated, each each entity in this hierarchy can be associated with one or more properties; Each property is defined by a combination of path (Transform.translation) and type (Vec3)
and contains a list of all the samplers paired with their respective target entities;

A sampler evaluates the state of a property at any given time. The Track is the only sampler currently implemented
and comes in 3 flavors.

  1. TrackFixed performant in any operation range, requires a keyframe for every frame of the animation duration;

  2. TrackVariableLinear, can be used with linear keyframe reduction technique to save memory at runtime,
    most performant when sampled at a higher fps than the track sample rate.

  3. TrackVariable very similar to TrackVariableLinear but beyond linear it supports step and catmull-rom as interpolations methods,
    because of that it will have a lower performance and require and extra runtime memory to store the keyframes tangents;
    This track is similar to AnimationCurve from Unity engine;

Safety status

Assume that bevy_reflect::Struct was correctly implemented

For that to happen unsafe blocks are used, but their safety constrains should be
self contained within the same file or even with the same function block, with one
single exception.

Asside from that an animator system can be implemented with only safe code for any
type that you may want, its a lot of code but mostly is boiler plates. Optionally you can
add two extra unsafe blocks to improve performance (with very little safety conditions);

Other Considerations

Why Hierarchy and not Entity List?

I wanted that the animation system to be the sole responsible of finding and assigning any animated entities, so I opted for using a Hierarchy instead of a entity list; This of course will make the animation system for simple to use, further more here a list of pros and cons of using an entity list:

Pros

  • Can limit to only the entities that we really want to animate, from deep nested hierarchies
  • Entities names don't matter
  • Easer to design a LayerMask around it
  • Bit less memory

Cons

  • Harder to modify and manipulate clips
  • Harder to find missing entities
  • Harder to mix clips with different hierarchies
  • Hierarchy don't matter which can lead to a giant mess, and makes impossible update many animators and their system in parallel (using hierarchy will be harder but not impossible)

Update Apr 11

To simplify the work as suggested to me (sry forgot who) I broke this into a couple of smaller PR's:

  1. Removed SIMD support to keep it simple
  2. I will try to push this for bevy 0.6
  3. I need to do a bit of clean up, organize a bit, some huge files to break down before making this a full pr,

lassade and others added 30 commits November 7, 2020 17:22
@cart cart added the S-Pre-Relicense This PR was made before Bevy added the Apache license. Cannot be merged or used for other work label Jul 23, 2021
ostwilkens pushed a commit to ostwilkens/bevy that referenced this pull request Jul 27, 2021
Required by bevyengine#1429,

- Adds the `Ushort4` vertex attribute for joint indices
- `Mesh::ATTRIBUTE_JOINT_WEIGHT` and `Mesh::ATTRIBUTE_JOINT_INDEX` to import vertex attributes related to skinning from GLTF
- impl `Default` for `Mesh` a empty triangle mesh is created (needed by reflect)
- impl `Reflect` for `Mesh` all attributes are ignored (needed by the animation system)
@DJMcNab DJMcNab removed the S-Pre-Relicense This PR was made before Bevy added the Apache license. Cannot be merged or used for other work label Aug 7, 2021
vabrador pushed a commit to vabrador/bevy that referenced this pull request Sep 15, 2021
Required by bevyengine#1429,

- Adds the `Ushort4` vertex attribute for joint indices
- `Mesh::ATTRIBUTE_JOINT_WEIGHT` and `Mesh::ATTRIBUTE_JOINT_INDEX` to import vertex attributes related to skinning from GLTF
- impl `Default` for `Mesh` a empty triangle mesh is created (needed by reflect)
- impl `Reflect` for `Mesh` all attributes are ignored (needed by the animation system)
setzer22 pushed a commit to Cakefish/bevy that referenced this pull request Nov 17, 2021
Required by bevyengine#1429,

- Adds the `Ushort4` vertex attribute for joint indices
- `Mesh::ATTRIBUTE_JOINT_WEIGHT` and `Mesh::ATTRIBUTE_JOINT_INDEX` to import vertex attributes related to skinning from GLTF
- impl `Default` for `Mesh` a empty triangle mesh is created (needed by reflect)
- impl `Reflect` for `Mesh` all attributes are ignored (needed by the animation system)
@james7132 james7132 mentioned this pull request Feb 23, 2022
14 tasks
@alice-i-cecile
Copy link
Member

Closing this out; this has been incredibly valuable to lay the design foundations of our animation solutions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Animation Make things move and change over time A-Rendering Drawing game state to the screen C-Feature A new feature, making something new possible S-Needs-Design-Doc This issue or PR is particularly complex, and needs an approved design doc before it can be merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants