bevy/crates/bevy_internal/src/default_plugins.rs
Patrick Walton 5f1dd3918b
Rework animation to be done in two phases. (#11707)
# Objective

Bevy's animation system currently does tree traversals based on `Name`
that aren't necessary. Not only do they require in unsafe code because
tree traversals are awkward with parallelism, but they are also somewhat
slow, brittle, and complex, which manifested itself as way too many
queries in #11670.

# Solution

Divide animation into two phases: animation *advancement* and animation
*evaluation*, which run after one another. *Advancement* operates on the
`AnimationPlayer` and sets the current animation time to match the game
time. *Evaluation* operates on all animation bones in the scene in
parallel and sets the transforms and/or morph weights based on the time
and the clip.

To do this, we introduce a new component, `AnimationTarget`, which the
asset loader places on every bone. It contains the ID of the entity
containing the `AnimationPlayer`, as well as a UUID that identifies
which bone in the animation the target corresponds to. In the case of
glTF, the UUID is derived from the full path name to the bone. The rule
that `AnimationTarget`s are descendants of the entity containing
`AnimationPlayer` is now just a convention, not a requirement; this
allows us to eliminate the unsafe code.

# Migration guide

* `AnimationClip` now uses UUIDs instead of hierarchical paths based on
the `Name` component to refer to bones. This has several consequences:
- A new component, `AnimationTarget`, should be placed on each bone that
you wish to animate, in order to specify its UUID and the associated
`AnimationPlayer`. The glTF loader automatically creates these
components as necessary, so most uses of glTF rigs shouldn't need to
change.
- Moving a bone around the tree, or renaming it, no longer prevents an
`AnimationPlayer` from affecting it.
- Dynamically changing the `AnimationPlayer` component will likely
require manual updating of the `AnimationTarget` components.
* Entities with `AnimationPlayer` components may now possess descendants
that also have `AnimationPlayer` components. They may not, however,
animate the same bones.
* As they aren't specific to `TypeId`s,
`bevy_reflect::utility::NoOpTypeIdHash` and
`bevy_reflect::utility::NoOpTypeIdHasher` have been renamed to
`bevy_reflect::utility::NoOpHash` and
`bevy_reflect::utility::NoOpHasher` respectively.
2024-02-19 14:59:54 +00:00

209 lines
8.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use bevy_app::{Plugin, PluginGroup, PluginGroupBuilder};
/// This plugin group will add all the default plugins for a *Bevy* application:
/// * [`LogPlugin`](crate::log::LogPlugin)
/// * [`TaskPoolPlugin`](crate::core::TaskPoolPlugin)
/// * [`TypeRegistrationPlugin`](crate::core::TypeRegistrationPlugin)
/// * [`FrameCountPlugin`](crate::core::FrameCountPlugin)
/// * [`TimePlugin`](crate::time::TimePlugin)
/// * [`TransformPlugin`](crate::transform::TransformPlugin)
/// * [`HierarchyPlugin`](crate::hierarchy::HierarchyPlugin)
/// * [`DiagnosticsPlugin`](crate::diagnostic::DiagnosticsPlugin)
/// * [`InputPlugin`](crate::input::InputPlugin)
/// * [`WindowPlugin`](crate::window::WindowPlugin)
/// * [`AccessibilityPlugin`](crate::a11y::AccessibilityPlugin)
/// * [`AssetPlugin`](crate::asset::AssetPlugin) - with feature `bevy_asset`
/// * [`ScenePlugin`](crate::scene::ScenePlugin) - with feature `bevy_scene`
/// * [`WinitPlugin`](crate::winit::WinitPlugin) - with feature `bevy_winit`
/// * [`RenderPlugin`](crate::render::RenderPlugin) - with feature `bevy_render`
/// * [`ImagePlugin`](crate::render::texture::ImagePlugin) - with feature `bevy_render`
/// * [`PipelinedRenderingPlugin`](crate::render::pipelined_rendering::PipelinedRenderingPlugin) - with feature `bevy_render` when not targeting `wasm32`
/// * [`CorePipelinePlugin`](crate::core_pipeline::CorePipelinePlugin) - with feature `bevy_core_pipeline`
/// * [`SpritePlugin`](crate::sprite::SpritePlugin) - with feature `bevy_sprite`
/// * [`TextPlugin`](crate::text::TextPlugin) - with feature `bevy_text`
/// * [`UiPlugin`](crate::ui::UiPlugin) - with feature `bevy_ui`
/// * [`PbrPlugin`](crate::pbr::PbrPlugin) - with feature `bevy_pbr`
/// * [`GltfPlugin`](crate::gltf::GltfPlugin) - with feature `bevy_gltf`
/// * [`AudioPlugin`](crate::audio::AudioPlugin) - with feature `bevy_audio`
/// * [`GilrsPlugin`](crate::gilrs::GilrsPlugin) - with feature `bevy_gilrs`
/// * [`AnimationPlugin`](crate::animation::AnimationPlugin) - with feature `bevy_animation`
///
/// [`DefaultPlugins`] obeys *Cargo* *feature* flags. Users may exert control over this plugin group
/// by disabling `default-features` in their `Cargo.toml` and enabling only those features
/// that they wish to use.
///
/// [`DefaultPlugins`] contains all the plugins typically required to build
/// a *Bevy* application which includes a *window* and presentation components.
/// For *headless* cases without a *window* or presentation, see [`MinimalPlugins`].
pub struct DefaultPlugins;
impl PluginGroup for DefaultPlugins {
fn build(self) -> PluginGroupBuilder {
let mut group = PluginGroupBuilder::start::<Self>();
group = group
.add(bevy_log::LogPlugin::default())
.add(bevy_core::TaskPoolPlugin::default())
.add(bevy_core::TypeRegistrationPlugin)
.add(bevy_core::FrameCountPlugin)
.add(bevy_time::TimePlugin)
.add(bevy_transform::TransformPlugin)
.add(bevy_hierarchy::HierarchyPlugin)
.add(bevy_diagnostic::DiagnosticsPlugin)
.add(bevy_input::InputPlugin)
.add(bevy_window::WindowPlugin::default())
.add(bevy_a11y::AccessibilityPlugin);
#[cfg(feature = "bevy_asset")]
{
group = group.add(bevy_asset::AssetPlugin::default());
}
#[cfg(feature = "bevy_scene")]
{
group = group.add(bevy_scene::ScenePlugin);
}
#[cfg(feature = "bevy_winit")]
{
group = group.add(bevy_winit::WinitPlugin::default());
}
#[cfg(feature = "bevy_render")]
{
group = group
.add(bevy_render::RenderPlugin::default())
// NOTE: Load this after renderer initialization so that it knows about the supported
// compressed texture formats
.add(bevy_render::texture::ImagePlugin::default());
#[cfg(all(not(target_arch = "wasm32"), feature = "multi-threaded"))]
{
group = group.add(bevy_render::pipelined_rendering::PipelinedRenderingPlugin);
}
}
#[cfg(feature = "bevy_core_pipeline")]
{
group = group.add(bevy_core_pipeline::CorePipelinePlugin);
}
#[cfg(feature = "bevy_sprite")]
{
group = group.add(bevy_sprite::SpritePlugin);
}
#[cfg(feature = "bevy_text")]
{
group = group.add(bevy_text::TextPlugin);
}
#[cfg(feature = "bevy_ui")]
{
group = group.add(bevy_ui::UiPlugin);
}
#[cfg(feature = "bevy_pbr")]
{
group = group.add(bevy_pbr::PbrPlugin::default());
}
// NOTE: Load this after renderer initialization so that it knows about the supported
// compressed texture formats
#[cfg(feature = "bevy_gltf")]
{
group = group.add(bevy_gltf::GltfPlugin::default());
}
#[cfg(feature = "bevy_audio")]
{
group = group.add(bevy_audio::AudioPlugin::default());
}
#[cfg(feature = "bevy_gilrs")]
{
group = group.add(bevy_gilrs::GilrsPlugin);
}
#[cfg(feature = "bevy_animation")]
{
group = group.add(bevy_animation::AnimationPlugin);
}
#[cfg(feature = "bevy_gizmos")]
{
group = group.add(bevy_gizmos::GizmoPlugin);
}
group = group.add(IgnoreAmbiguitiesPlugin);
group
}
}
struct IgnoreAmbiguitiesPlugin;
impl Plugin for IgnoreAmbiguitiesPlugin {
#[allow(unused_variables)] // Variables are used depending on enabled features
fn build(&self, app: &mut bevy_app::App) {
// bevy_ui owns the Transform and cannot be animated
#[cfg(all(feature = "bevy_animation", feature = "bevy_ui"))]
app.ignore_ambiguity(
bevy_app::PostUpdate,
bevy_animation::advance_animations,
bevy_ui::ui_layout_system,
);
#[cfg(feature = "bevy_render")]
if let Ok(render_app) = app.get_sub_app_mut(bevy_render::RenderApp) {
#[cfg(all(feature = "bevy_gizmos", feature = "bevy_sprite"))]
{
render_app.ignore_ambiguity(
bevy_render::Render,
bevy_gizmos::GizmoRenderSystem::QueueLineGizmos2d,
bevy_sprite::queue_sprites,
);
render_app.ignore_ambiguity(
bevy_render::Render,
bevy_gizmos::GizmoRenderSystem::QueueLineGizmos2d,
bevy_sprite::queue_material2d_meshes::<bevy_sprite::ColorMaterial>,
);
}
#[cfg(all(feature = "bevy_gizmos", feature = "bevy_pbr"))]
{
render_app.ignore_ambiguity(
bevy_render::Render,
bevy_gizmos::GizmoRenderSystem::QueueLineGizmos3d,
bevy_pbr::queue_material_meshes::<bevy_pbr::StandardMaterial>,
);
}
}
}
}
/// This plugin group will add the minimal plugins for a *Bevy* application:
/// * [`TaskPoolPlugin`](crate::core::TaskPoolPlugin)
/// * [`TypeRegistrationPlugin`](crate::core::TypeRegistrationPlugin)
/// * [`FrameCountPlugin`](crate::core::FrameCountPlugin)
/// * [`TimePlugin`](crate::time::TimePlugin)
/// * [`ScheduleRunnerPlugin`](crate::app::ScheduleRunnerPlugin)
///
/// This group of plugins is intended for use for minimal, *headless* programs
/// see the [*Bevy* *headless* example](https://github.com/bevyengine/bevy/blob/main/examples/app/headless.rs)
/// and includes a [schedule runner (`ScheduleRunnerPlugin`)](crate::app::ScheduleRunnerPlugin)
/// to provide functionality that would otherwise be driven by a windowed application's
/// *event loop* or *message loop*.
///
/// Windowed applications that wish to use a reduced set of plugins should consider the
/// [`DefaultPlugins`] plugin group which can be controlled with *Cargo* *feature* flags.
pub struct MinimalPlugins;
impl PluginGroup for MinimalPlugins {
fn build(self) -> PluginGroupBuilder {
PluginGroupBuilder::start::<Self>()
.add(bevy_core::TaskPoolPlugin::default())
.add(bevy_core::TypeRegistrationPlugin)
.add(bevy_core::FrameCountPlugin)
.add(bevy_time::TimePlugin)
.add(bevy_app::ScheduleRunnerPlugin::default())
}
}