use std::ops::{Deref, DerefMut}; use bevy::{ app::{App, Plugin, PostUpdate, Update}, asset::{Asset, AssetApp, AssetEvent, Assets, Handle}, core::Name, ecs::{ component::Component, entity::Entity, event::{Event, EventReader, EventWriter}, query::{QueryData, QueryFilter, With, Without, WorldQuery}, schedule::{apply_deferred, IntoSystemConfigs}, system::{Command, Commands, Query, Res, Resource}, world::World, }, hierarchy::{BuildWorldChildren, Children}, math::{EulerRot, Quat, Vec3}, reflect::{Reflect, TypePath}, render::color::Color, time::{Time, Timer, TimerMode}, transform::{ components::{GlobalTransform, Transform}, TransformSystem, }, }; use space_editor::space_prefab::ext::{in_state, resource_exists, Deref, DerefMut, SpatialBundle}; use studio_bvh::{frames::Frame, ChannelType}; mod error; mod loader; use crate::{ anim::{BindPoseGlobal, BindPoseLocal, InvBindPose, RetargetSource, SkeletalJoint}, math::Lerp, scene::SceneState, }; use self::loader::BvhLoader; pub struct BvhPlugin; impl Plugin for BvhPlugin { fn build(&self, app: &mut App) { app.init_asset::() .add_event::() .register_asset_loader(BvhLoader) // FIXME: Need to add a state for this module (similar to scene // module's `SceneState`) to manage the order/frequency at // which these systems run instead of just running them all // every frame .add_systems(Update, spawn_test_bvh.run_if(resource_exists::)) .add_systems( PostUpdate, (compute_inverse_bindposes, apply_deferred, animate_bvh) .chain() .run_if(in_state(SceneState::Active)) .after(TransformSystem::TransformPropagate), ) .register_type::() .register_type::(); } } // TODO: Rename & reorganize #[derive(Resource, Deref, DerefMut)] pub struct TestBvh(pub Handle); // TODO: Rename & reorganize fn spawn_test_bvh( mut cmd: Commands, mut er_bvh_events: EventReader>, r_bvh: Res, ) { for event in er_bvh_events.read() { if matches!(event, AssetEvent::LoadedWithDependencies { .. }) { let ent = cmd.spawn_bvh_with_transform( &r_bvh.0, Transform::from_scale(Vec3::splat(0.06)) .with_translation(Vec3::new(0., 1.0, 0.524)), ); cmd.entity(ent).insert(RetargetSource); } } } //============================================================================== // Bvh Asset //============================================================================== #[derive(Clone, Debug, Default, Asset, TypePath)] pub struct Bvh(studio_bvh::Bvh); //============================================================================== // Bvh Anim Component //============================================================================== #[derive(Component, Debug, Reflect)] pub struct BvhAnim { #[reflect(ignore)] bvh: Handle, should_play: bool, frame: usize, #[reflect(ignore)] timer: Timer, } // TODO: Rename fn compute_inverse_bindposes( mut cmd: Commands, q_bvh_joints: Query< (Entity, &Transform, &GlobalTransform), (With, Without), >, ) { for (joint, xform, world_xform) in q_bvh_joints.iter() { cmd.entity(joint).insert(( BindPoseLocal(*xform), BindPoseGlobal(world_xform.affine()), InvBindPose(world_xform.affine().inverse()), )); } } pub fn animate_bvh( mut q_bvh_anims: Query<(&mut BvhAnim, &Children)>, mut q_bvh_joints: Query>, mut bvh_event_writer: EventWriter, r_bvh_assets: Res>, r_time: Res