-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Implement mesh skinning #2359
Implement mesh skinning #2359
Conversation
That's much simpler than I expected! Could we get an example or two of how this works in practice? I'm interested in how this handles both animations from the gltf as well as creating new animations from code. |
@@ -0,0 +1,24 @@ | |||
[package] | |||
name = "bevy_animation_rig" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO this should be the start of a general bevy_animation
crate instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original bevy_animation
is meant to animate the component properties. So I created this crate to separate the "skinning" from the "animation".
@@ -263,6 +311,24 @@ async fn load_gltf<'a, 'b>( | |||
if let Some(Err(err)) = err { | |||
return Err(err); | |||
} | |||
|
|||
for (&entity, &skin_index) in &entity_to_skin_index_map { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it looks like this grabs animation data from the .gltf file, creating one entity for each joint?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically, it's only skin data, without any animation clip. Entity joints are already created since they are part of the glTF nodes. This code just map the created entity to the joint defined in glTF.
crates/bevy_gltf/src/loader.rs
Outdated
@@ -142,6 +146,23 @@ async fn load_gltf<'a, 'b>( | |||
mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, vertex_attribute); | |||
} | |||
|
|||
if let Some(vertex_attribute) = reader.read_joints(0).map(|v| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new code in this function doesn't seem to be feature gated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is in bevy_gltf
crate, and it depends on the bevy_animation_rig
. Making bevy_animation_rig
optional in this crate is kinda hard as bevy_gltf
is also optional for bevy
. I don't know how to deal with this. Do you have any suggestion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very simple! I have a few nits, but mostly I want to see end-user examples of how this is used before I can comment further.
@alice-i-cecile Sure, I will sort these things out. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here a two very small thing.
I'll give this PR a more formal review tomorrow/this weekend :)
b018cad
to
280c755
Compare
} | ||
|
||
fn write_buffer_bytes(&self, buffer: &mut [u8]) { | ||
let transform_size = std::mem::size_of::<[f32; 16]>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're checking for the transform size, why are use using [f32; 16]
instead of Mat4
?
(I might not be understanding something so please correct me if I'm wrong haha :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got this std::mem::size_of::<[f32; 16]>()
from RenderResource
implementation for Mat4
. I'm not sure why as well. Maybe Mat4
may have internal states so we don't rely on the size of Mat4
?
8d9371b
to
ca83297
Compare
@alice-i-cecile I have added |
38ab695
to
28c6b74
Compare
3cd5ad1
to
c9edce7
Compare
|
||
/// Manually implement [`bevy_reflect::Reflect`] for [`SkinnedMeshJoint`] to work around an issue, | ||
/// where spawning a scene with a component containings a vector of structs would result in runtime panic. | ||
unsafe impl Reflect for SkinnedMeshJoint { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious, is deriving Reflect
on the SkinnedMeshJoint
enough?
(Instead of manually implementing it?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not just normal manual implementation but more like a monkey-patch. I patched the clone_value()
method to work around this issue.
c9edce7
to
242e7d9
Compare
242e7d9
to
366742c
Compare
Hi, just was wondering, are there any issues still blocking this? |
@alice-i-cecile I have left the comment. I haven't touch this for a while. Is there anything else I need to do to get this shipped? |
It's looking quite good! Code quality is solid, and I'd be happy to ship this as an initial building block. This PR is currently just waiting on editorial attention: animation wasn't a focus for the 0.6 release. I would expect it to receive attention during the 0.7 cycle; animation seems to be one of the bigger stumbling blocks now that rendering is cleaned up. |
Some quick notes (I'll do a review soon after 0.6 releases):
|
As someone who has been heavily reliant on this PR for my own prototypes (really thank you for your work!) I have a some comments regarding the API:
|
Now that you mention about it, this is a really good idea that I have never considered before. I will move this feature into However, there are a few things I need some advices on:
|
Hi, just wanted to check what still needs to be done for this? I need skinning for something that I'm working out so would like to help if possible. |
Closing this in favor for #4238, which is compatible with the new renderer. @Looooong is credited in that PR as it was started by merging this PR into For those who left a review here, please give a once-over for that PR. @nicopap: re: the @nickjbenson PR to this one could be a good start of an actual animation crate. I'd suggest that they open a PR with it after #4238 gets merged. |
# Objective Load skeletal weights and indices from GLTF files. Animate meshes. ## Solution - Load skeletal weights and indices from GLTF files. - Added `SkinnedMesh` component and ` SkinnedMeshInverseBindPose` asset - Added `extract_skinned_meshes` to extract joint matrices. - Added queue phase systems for enqueuing the buffer writes. Some notes: - This ports part of # #2359 to the current main. - This generates new `BufferVec`s and bind groups every frame. The expectation here is that the number of `Query::get` calls during extract is probably going to be the stronger bottleneck, with up to 256 calls per skinned mesh. Until that is optimized, caching buffers and bind groups is probably a non-concern. - Unfortunately, due to the uniform size requirements, this means a 16KB buffer is allocated for every skinned mesh every frame. There's probably a few ways to get around this, but most of them require either compute shaders or storage buffers, which are both incompatible with WebGL2. Co-authored-by: james7132 <contact@jamessliu.com> Co-authored-by: François <mockersf@gmail.com> Co-authored-by: James Liu <contact@jamessliu.com>
# Objective Load skeletal weights and indices from GLTF files. Animate meshes. ## Solution - Load skeletal weights and indices from GLTF files. - Added `SkinnedMesh` component and ` SkinnedMeshInverseBindPose` asset - Added `extract_skinned_meshes` to extract joint matrices. - Added queue phase systems for enqueuing the buffer writes. Some notes: - This ports part of # bevyengine#2359 to the current main. - This generates new `BufferVec`s and bind groups every frame. The expectation here is that the number of `Query::get` calls during extract is probably going to be the stronger bottleneck, with up to 256 calls per skinned mesh. Until that is optimized, caching buffers and bind groups is probably a non-concern. - Unfortunately, due to the uniform size requirements, this means a 16KB buffer is allocated for every skinned mesh every frame. There's probably a few ways to get around this, but most of them require either compute shaders or storage buffers, which are both incompatible with WebGL2. Co-authored-by: james7132 <contact@jamessliu.com> Co-authored-by: François <mockersf@gmail.com> Co-authored-by: James Liu <contact@jamessliu.com>
# Objective Load skeletal weights and indices from GLTF files. Animate meshes. ## Solution - Load skeletal weights and indices from GLTF files. - Added `SkinnedMesh` component and ` SkinnedMeshInverseBindPose` asset - Added `extract_skinned_meshes` to extract joint matrices. - Added queue phase systems for enqueuing the buffer writes. Some notes: - This ports part of # bevyengine#2359 to the current main. - This generates new `BufferVec`s and bind groups every frame. The expectation here is that the number of `Query::get` calls during extract is probably going to be the stronger bottleneck, with up to 256 calls per skinned mesh. Until that is optimized, caching buffers and bind groups is probably a non-concern. - Unfortunately, due to the uniform size requirements, this means a 16KB buffer is allocated for every skinned mesh every frame. There's probably a few ways to get around this, but most of them require either compute shaders or storage buffers, which are both incompatible with WebGL2. Co-authored-by: james7132 <contact@jamessliu.com> Co-authored-by: François <mockersf@gmail.com> Co-authored-by: James Liu <contact@jamessliu.com>
Objective
Solution
bevy_gltf
to extract skins information as well as the joint data in the mesh.SkinnedMesh
that stores:SkinnedMeshInverseBindposes
.SkinnedMeshInverseBindposes
to store inverse bindposes data which can be shared between multipleSkinnedMesh
component.SkinnedMesh
's joint transform matrices fromSkinnedMeshInverseBindposes
and joint entities'GlobalTransform
.RenderResource
andRenderResources
trait forSkinnedMesh
, then by addingRenderResourceNode<SkinnedMesh>
to render graph.Compare to the counterpart in #1429
RenderResourcesNode
instead ofAssetRenderResourcesNode
, it doesn't need to create custom material asset for each skinned mesh instance that shares the same mesh and skin.