bevy_motiongfx 0.2.0

An implementation of the MotionGfx framework for Bevy.
Documentation
use bevy_app::prelude::*;
use bevy_ecs::component::Mutable;
use bevy_ecs::prelude::*;
use motiongfx::prelude::*;

use crate::pipeline::PipelineRegistryExt;
use crate::world::MotionGfxWorld;

// TODO: Move purely the recursive logic back to motiongfx and keep
// the registration logic here.

/// Recursively register fields.
///
/// # Example
///
/// ```
/// use bevy_ecs::entity::Entity;
/// use bevy_app::App;
/// use bevy_ecs::component::Component;
/// use bevy_motiongfx::BevyMotionGfxPlugin;
/// use bevy_motiongfx::prelude::*;
///
/// #[derive(Component, Default, Clone)]
/// struct Foo {
///     bar_x: Bar,
///     bar_y: Bar,
/// }
///
/// #[derive(Clone, Default)]
/// struct Bar {
///     cho_a: Cho,
///     cho_b: Cho,
/// }
///
/// #[derive(Clone, Default)]
/// struct Cho {
///     bo_c: Bo,
///     bo_d: Bo,
/// }
///
/// #[derive(Clone, Default)]
/// struct Bo(f32, u32);
///
/// let mut app = App::new();
/// app.add_plugins(BevyMotionGfxPlugin);
///
/// let a = &mut app;
/// register_fields!(
///     a.register_component_field(),
///     Foo,
///     (
///         bar_x(cho_a(bo_c(0, 1), bo_d(0, 1))),
///         bar_y(cho_b(bo_c(0, 1), bo_d(0, 1))),
///     )
/// );
///
/// let motiongfx = app.world().resource::<MotionGfxWorld>();
///
/// // Get accessor from the registry.
/// let key = field!(<Foo>::bar_x::cho_a::bo_c::0).untyped();
/// let accessor =
///     motiongfx.accessor_registry.get::<Foo, f32>(&key).unwrap();
///
/// let mut foo = Foo::default();
///
/// assert_eq!(accessor.get_ref(&foo), &foo.bar_x.cho_a.bo_c.0,);
///
/// *accessor.get_mut(&mut foo) = 2.0;
/// assert_eq!(accessor.get_ref(&foo), &2.0);
///
/// // Get pipeline from the registry.
/// let key = PipelineKey::new::<Entity, Foo, f32>();
/// let pipeline = motiongfx.pipeline_registry.get(&key).unwrap();
/// ```
#[macro_export]
macro_rules! register_fields {
    (
        $app:ident.$reg_func:ident(),
        $root:ty $(, $($rest:tt)*)?
    ) => {
        register_fields!(
            $app.$reg_func::<$root>(),
            $root $(, $($rest)*)?
        )
    };

    (
        $app:ident.$reg_func:ident::<$source:ty>(),
        $root:ty $(, $($rest:tt)*)?
    ) => {
        $crate::registry::FieldPathRegisterAppExt
        ::$reg_func::<$source, _>(
            $app,
            ::motiongfx::field_path::field::field!(<$root>),
            ::motiongfx::field_path::accessor::accessor!(<$root>),
        );

        register_fields!(
            @fields $app.$reg_func::<$source>, $root, []
            $(, $($rest)*)?
        );
    };

    // Recursively register all the nested fields!
    (
        @fields $app:ident.$reg_func:ident::<$source:ty>,
        $root:ty, [$(::$path:tt)*],
        (
            $field:tt $(( $($sub_field:tt)+ ))?
            $(,$($rest:tt)*)?
        )
    ) => {
        // Register the current field.
        // (translation(x, y, z), rotation, scale) => translation
        $crate::registry::FieldPathRegisterAppExt
        ::$reg_func::<$source, _>(
            $app,
            motiongfx::field_path::field::field!(<$root>$(::$path)*::$field),
            ::motiongfx::field_path::accessor::accessor!(<$root>$(::$path)*::$field),
        );

        // Register sub fields.
        // (translation(x, y, z), rotation, scale) => (x, y, z)
        register_fields!(
            @fields $app.$reg_func::<$source>,
            $root, [$(::$path)*::$field],
            $(( $($sub_field)+ ))?
        );

        // Register the rest of the fields.
        // (translation(x, y, z), rotation, scale) => (rotation, scale)
        register_fields!(
            @fields $app.$reg_func::<$source>,
            $root, [$(::$path)*],
            $(( $($rest)* ))?
        );
    };

    // There are no fields left!
    (
        @fields $app:ident.$reg_func:ident::<$source:ty>,
        $root:ty, [$(::$path:tt)*]
        $(,)? $(,())?
    ) => {};
}

pub trait FieldPathRegisterAppExt {
    fn register_component_field<S, T>(
        &mut self,
        field: Field<S, T>,
        accessor: Accessor<S, T>,
    ) -> &mut Self
    where
        S: Component<Mutability = Mutable>,
        T: Clone + ThreadSafe;

    #[cfg(feature = "asset")]
    fn register_asset_field<S, T>(
        &mut self,
        field: Field<S, T>,
        accessor: Accessor<S, T>,
    ) -> &mut Self
    where
        S: bevy_asset::Asset,
        T: Clone + ThreadSafe;
}

impl FieldPathRegisterAppExt for App {
    fn register_component_field<S, T>(
        &mut self,
        field: Field<S, T>,
        accessor: Accessor<S, T>,
    ) -> &mut Self
    where
        S: Component<Mutability = Mutable>,
        T: Clone + ThreadSafe,
    {
        let mut motiongfx =
            self.world_mut().resource_mut::<MotionGfxWorld>();

        motiongfx
            .accessor_registry
            .register(field.untyped(), accessor);
        motiongfx.pipeline_registry.register_component::<S, T>();

        self
    }

    #[cfg(feature = "asset")]
    fn register_asset_field<S, T>(
        &mut self,
        field: Field<S, T>,
        accessor: Accessor<S, T>,
    ) -> &mut Self
    where
        S: bevy_asset::Asset,
        T: Clone + ThreadSafe,
    {
        let mut motiongfx =
            self.world_mut().resource_mut::<MotionGfxWorld>();

        motiongfx
            .accessor_registry
            .register(field.untyped(), accessor);
        motiongfx.pipeline_registry.register_asset::<S, T>();

        self
    }
}