Skip to content

Conversation

@hukasu
Copy link
Contributor

@hukasu hukasu commented Mar 11, 2025

Objective

Starts work on #17114

Solution

Create a new crate bevy_bone_attachments and implement a extension trait on EntityCommands to allow attaching a new Scene to an Entity. The command then collects all AnimationTargets of the entity and copies it to the new Scene.

This is very early and only includes the above method, the new crate can also be included on an existing crate.

Testing

  • New example

Showcase

Bone attachments allow you to attach a mesh to another, think a character holding a weapon, or customizable clothing.

Experimental: The current state only allows attaching new scenes into models, and requires that the parent entity is already fully loaded (if it is also a scene, after its SceneInstance is ready) and have matching AnimationTargetIds.

Screencast_20250312_094221.webm

Use Guide

use bevy_bone_attachments::scene::SceneAttachmentExt;

// This requires that the parent is already fully loaded, and there is only a warning if it is not
commands
    .entity(parent)
    .attach_scene(
        asset_server.load_with_settings(
            GltfAssetLabel::Scene(0).from_asset("attachment.glb"),
            |settings: &mut GltfLoaderSettings| { settings.include_animation_target_ids = true; }
    ));

@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible A-Animation Make things move and change over time M-Release-Note Work that should be called out in the blog due to impact X-Controversial There is active debate or serious implications around merging this PR S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Mar 11, 2025
@hukasu
Copy link
Contributor Author

hukasu commented Mar 11, 2025

@alice-i-cecile any template on how to make the release note?

@alice-i-cecile
Copy link
Member

@alice-i-cecile any template on how to make the release note?

We're kind of in flux right now, but for now, putting a # Showcase section in the PR would be really helpful. The key points to hit are:

  • what new feature was introduced?
  • why is it important?
  • very briefly, how do you use it?

Copy link
Contributor

@viridia viridia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good generally. Note to @alice-i-cecile : this PR solves one of the two use cases mentioned in the original issue, so unless we want to split it into two separate tickets, it should not close the issue once merged.

@hukasu
Copy link
Contributor Author

hukasu commented Mar 11, 2025

Looks good generally. Note to @alice-i-cecile : this PR solves one of the two use cases mentioned in the original issue, so unless we want to split it into two separate tickets, it should not close the issue once merged.

that is why i wrote Starts work on #17114 instead of Closes

@hukasu
Copy link
Contributor Author

hukasu commented Mar 11, 2025

@alice-i-cecile given how early this is I think it is best to add Needs design

@pcwalton
Copy link
Contributor

Can someone help me understand why we can't just use BSN overrides for this? BSN should have the ability to say "I want to load scene X, but add my own custom entity as a child of object Y". That's core to the concept of what overrides are. Once we do that, this feature should naturally fall out, no?

@alice-i-cecile alice-i-cecile added S-Needs-Design This issue requires design work to think about how it would best be accomplished and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Mar 12, 2025
@hukasu
Copy link
Contributor Author

hukasu commented Mar 12, 2025

@pcwalton for skinned meshes there are other things needed for it to work, not just parenting, I'm preparing another model to actually show that this works for skinned meshes

@hukasu hukasu changed the title Bone attachments 🧪 Bone attachments Apr 18, 2025
@Gaeric
Copy link

Gaeric commented Aug 18, 2025

@alice-i-cecile
Hello! I'd like to check if there are any outstanding tasks or pending reviews for this PR. Could you please share the expected timeline for merging?

This PR is important for me as it implements the ability for multiple skins to share a single armature scene.

@alice-i-cecile
Copy link
Member

alice-i-cecile commented Aug 18, 2025

This needs merge conflict resolution, and reviews by animation experts. I am not one, but I will socialize your PR and try to get opinions :)

It also needs an updated release note: we have a nice little folder for them now!

@viridia
Copy link
Contributor

viridia commented Aug 18, 2025

Can someone help me understand why we can't just use BSN overrides for this? BSN should have the ability to say "I want to load scene X, but add my own custom entity as a child of object Y". That's core to the concept of what overrides are. Once we do that, this feature should naturally fall out, no?

Well, the solution in this PR is based on GLTF scenes, not BSN. So that feature of BSN isn't going to be applicable, unless we are saying that BSN is going to be the one and only fully-supported scene format going forward.

@greeble-dev
Copy link
Contributor

My two cents as someone who's worked with similar features in Unreal:

There's really two parts to this change - adding the option to make the glTF loader calculate AnimationTargetIds for all nodes, and adding joint rebinding.

The glTF loader changes could be useful for other features. One example is loading a glTF with a skinned mesh but no animations, then binding the mesh to animations loaded from a second glTF. There's even an argument that the loader should do this by default for convenience.

I do think the code could be simplified if AnimationTarget was refactored a bit (proof of concept). I'm open to filing that as a separate PR - it would make this PR less controversial.

The second part is binding the joints of one skinned mesh to another mesh's animation player. Note that this is not just a rigid attachment, so as far I can tell it's not doable through simple parent/child relationships or BSN patches - there needs to be logic to find and match up joint entities. There's various other ways to do similar things but with different trade-offs. I think it's fine for this PR to start with one option, even if it might not fit all users.

The feature is clunky as it has to wait for one scene to spawn first, and then the other scene has to be spawned and tweaked. I don't think there's a better way to do this within the current Bevy asset/scene pipeline. As the pipeline evolves I'd guess this feature would move into a more robust transform step within the pipeline.

So the options as I see them are:

  1. Accept this PR. It's a bit clunky now, but it can evolve alongside the asset/scene pipeline.
  2. Accept just the glTF changes, possibly with tweaks (I can file a PR for this). The joint rebinding can be a third party crate, or it could be redone as an engine example - something that's easy for users to copy and paste. Once the pipeline has evolved it can be redone as an engine feature.

@hukasu
Copy link
Contributor Author

hukasu commented Aug 22, 2025

i'm in favor of doing the AnimationTarget refactor first, my solution in this PR is hella clunky and even i kinda dislike it, that is why i marked it as a experiment with the 🧪

@greeble-dev
Copy link
Contributor

Ok, I'll take a crack at a PR with the AnimationTarget refactor and glTf importer options. It's also fine if this PR lands first and I can do the refactor afterwards.

@alice-i-cecile
Copy link
Member

I'd be very happy to see that refactor in 0.18 :)

github-merge-queue bot pushed a commit that referenced this pull request Oct 1, 2025
## Objective

Add flexibility by refactoring `AnimationTarget` into two separate
components. This will smooth the path for future animation features.

## Background

`bevy_animation` animates entities by assigning them `AnimationTarget`
components:

```rust
struct AnimationTarget {
    player: Entity,
    id: AnimationTargetId,
}
```

- `player: Entity` links to an entity that contains an `AnimationPlayer`
component. An `AnimationPlayer` plays `AnimationClip` assets.
- `id: AnimationTargetId` identifies which tracks in an `AnimationClip`
apply to the target entity.

When loading a glTF these components are automatically created. They can
also be created manually.

## Problem

The two parts of `AnimationTarget` often go together but sometimes would
be better separated:

1. I might want to calculate the `AnimationTargetId` first, but not link
it up to an `AnimationPlayer` until later (see #18262 for an example).
2. I might want to use `AnimationTargetId` but not use `AnimationPlayer`
- maybe I've got a different component that plays `AnimationClip`s.

In theory `player` could be left as `Entity::PLACEHOLDER`, but that's
messy and will trigger a warning in `animate_targets`.

## Solution

This PR splits `AnimationTarget` into two components:

1. `AnimationTargetId` is just the original struct with a component
derive.
2. `AnimationPlayerTarget` is a new unit struct `(Entity)`.

I'm not convinced `AnimationPlayerTarget` is a good name, but it does
fit the usual source/target naming for entity relationships.
`AnimationPlayerRef` was another candidate.

`AnimationPlayerTarget` could be a relationship target, but there would
be a performance cost from making `AnimationPlayer` a relationship
source. Maybe it's still a good idea, but that's probably best left to
another PR.

### Performance

Profiled on `many_foxes` - difference was negligible.

### Testing

Examples `animated_mesh`, `animated_transform`, `animated_ui`,
`animation_masks`, `eased_motion`, `scene_viewer`.

## Future

If this PR lands then I'll probably file a follow up that adds more
flexibility to the the glTF loader creation of `AnimationTargetId` and
`AnimationPlayer`. This will help #18262 and enable some other features.
@greeble-dev
Copy link
Contributor

I've submitted two PRs for the refactor I proposed in my previous comment.

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 C-Feature A new feature, making something new possible M-Release-Note Work that should be called out in the blog due to impact S-Needs-Design This issue requires design work to think about how it would best be accomplished X-Controversial There is active debate or serious implications around merging this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants