diff --git a/studio/assets/gltf/Alien.glb b/studio/assets/gltf/Alien.glb new file mode 100644 index 0000000..3f71218 Binary files /dev/null and b/studio/assets/gltf/Alien.glb differ diff --git a/studio/assets/gltf/Basilisk.glb b/studio/assets/gltf/Basilisk.glb new file mode 100644 index 0000000..3f71218 Binary files /dev/null and b/studio/assets/gltf/Basilisk.glb differ diff --git a/studio/src/bvh/mod.rs b/studio/src/bvh/mod.rs index 808709b..6463c34 100644 --- a/studio/src/bvh/mod.rs +++ b/studio/src/bvh/mod.rs @@ -13,13 +13,14 @@ use bevy::{ utils::hashbrown::HashMap, }; use bevy_inspector_egui::egui::TextBuffer; +use bevy_mod_picking::prelude::EntityEvent; use bevy_rapier3d::parry::transformation; use studio_bvh::{frames::Frame, joint}; mod error; mod loader; -use crate::scene::SceneState; +use crate::{debug::print_mat3, scene::SceneState}; use self::loader::BvhLoader; @@ -31,7 +32,10 @@ impl Plugin for BvhPlugin { .register_asset_loader(BvhLoader) .add_systems(Update, animate_bvh.run_if(in_state(SceneState::Active))) .register_type::() - .register_type::(); + .register_type::() + //Events + .add_event::() + .add_systems(PreUpdate, update_bvh_anim); } } @@ -49,6 +53,8 @@ pub struct BvhAnim { should_play: bool, frame: usize, #[reflect(ignore)] + retarget: Option, + #[reflect(ignore)] timer: Timer, #[reflect(ignore)] last_frame: usize, @@ -99,49 +105,51 @@ fn apply_frame( return; }; - let joint_data = bvh_file - .get_joint(joint_entity.joint.0) - .expect("Invalid BVH format: the joint index is out of bounds"); + if let Some(joint) = joint_entity.joint { + let joint_data = bvh_file + .get_joint(joint.0) + .expect("Invalid BVH format: the joint index is out of bounds"); - //new Positions and rotations - let mut new_pos = joint_entity.transform.translation; - let mut new_rot: Vec3 = joint_entity - .transform - .rotation - .to_euler(EulerRot::ZXY) - .into(); + //new Positions and rotations + let mut new_pos = joint_entity.transform.translation; + let mut new_rot: Vec3 = joint_entity + .transform + .rotation + .to_euler(EulerRot::ZXY) + .into(); - for channel in joint_data.channels() { - // info!("Channel {}: {:?} : {}", channel.motion_index(), channel.channel_type(), frame.as_slice()[channel.motion_index()]); - match channel.channel_type() { - studio_bvh::ChannelType::PositionX => { - new_pos.x = frame.as_slice()[channel.motion_index()] - } - studio_bvh::ChannelType::PositionY => { - new_pos.y = frame.as_slice()[channel.motion_index()] - } - studio_bvh::ChannelType::PositionZ => { - new_pos.z = frame.as_slice()[channel.motion_index()] - } - studio_bvh::ChannelType::RotationX => { - new_rot.x = frame.as_slice()[channel.motion_index()] - } - studio_bvh::ChannelType::RotationY => { - new_rot.y = frame.as_slice()[channel.motion_index()] - } - studio_bvh::ChannelType::RotationZ => { - new_rot.z = frame.as_slice()[channel.motion_index()] + for channel in joint_data.channels() { + // info!("Channel {}: {:?} : {}", channel.motion_index(), channel.channel_type(), frame.as_slice()[channel.motion_index()]); + match channel.channel_type() { + studio_bvh::ChannelType::PositionX => { + new_pos.x = frame.as_slice()[channel.motion_index()] + } + studio_bvh::ChannelType::PositionY => { + new_pos.y = frame.as_slice()[channel.motion_index()] + } + studio_bvh::ChannelType::PositionZ => { + new_pos.z = frame.as_slice()[channel.motion_index()] + } + studio_bvh::ChannelType::RotationX => { + new_rot.x = frame.as_slice()[channel.motion_index()] + } + studio_bvh::ChannelType::RotationY => { + new_rot.y = frame.as_slice()[channel.motion_index()] + } + studio_bvh::ChannelType::RotationZ => { + new_rot.z = frame.as_slice()[channel.motion_index()] + } } } - } - joint_entity.transform.translation = new_pos; - joint_entity.transform.rotation = Quat::from_euler( - EulerRot::ZXY, - new_rot.z.to_radians(), - new_rot.x.to_radians(), - new_rot.y.to_radians(), - ); + joint_entity.transform.translation = new_pos; + joint_entity.transform.rotation = Quat::from_euler( + EulerRot::ZXY, + new_rot.z.to_radians(), + new_rot.x.to_radians(), + new_rot.y.to_radians(), + ); + } if joint_entity.children.is_some() { let children = joint_entity @@ -171,10 +179,9 @@ pub struct BvhJoint(usize); #[derive(Debug, WorldQuery)] #[world_query(mutable)] pub struct JointQuery { - pub name: &'static Name, - pub joint: &'static BvhJoint, - pub transform: &'static mut Transform, + pub joint: Option<&'static BvhJoint>, pub children: Option<&'static mut Children>, + pub transform: &'static mut Transform, } //============================================================================== @@ -261,13 +268,25 @@ impl Command for SpawnBvhCommand { panic!("Could not create the BVH entity because the bvh file has no root joint") }; - if let Some((skinned_model, RetargetMap)) = self.3 { - let entity = world - .query::<&Children>() - .get(world, skinned_model) - .unwrap(); + if let Some((skinned_model, retarget_map)) = self.3 { + world.entity_mut(skinned_model).insert(( + BvhAnim { + bvh: self.1, + frame: 0, + last_frame: 0, + retarget: Some(retarget_map), + should_play: true, + timer: Timer::from_seconds( + bvh.0.frame_time().as_secs_f32(), + TimerMode::Repeating, + ), + }, + Name::new("BVH Anim"), + )); - println!("Entity: {:?}", entity); + world.send_event(UpdateBVHAnim { + entity: skinned_model, + }); } else { let entity = world.spawn_empty().id(); spawn_bvh_recursive(world, &entity, root); @@ -279,23 +298,112 @@ impl Command for SpawnBvhCommand { frame: 0, last_frame: 0, should_play: true, + retarget: None, timer: Timer::from_seconds( bvh.0.frame_time().as_secs_f32(), TimerMode::Repeating, ), }, SpatialBundle::from_transform(self.2), - Name::new("BVH Skeliton"), + Name::new("BVH Anim"), )) .add_child(entity); } } } +//============================================================================== +// Update BVH anim Event Functions +//============================================================================== + +#[derive(Event)] +pub struct UpdateBVHAnim { + pub entity: Entity, +} + +fn update_bvh_anim( + mut commands: Commands, + mut bvh_anims: Query<(Entity, &BvhAnim, Option<&Children>)>, + mut update_events: EventReader, + bvh_assets: Res>, +) { + for event in update_events.read() { + // println!("Updating BVHs"); + // println!("{:?} | {:?}", event.entity, bvh_anims.single().0); + // for anim in bvh_anims.iter() { + // println!("Children {:?}", anim); + // }; + + if let Ok(bvh_anim) = bvh_anims.get(event.entity) { + let Some(bvh) = bvh_assets.get(bvh_anim.1.bvh.id()) else { + continue; + }; + }; + } +} + +fn update_bvh_recursive(current_node: Entity, commands: &mut Commands, bvh_anim: &Bvh) {} + +//============================================================================== +// Utility Functions +//============================================================================== + +fn spawn_bvh_recursive(world: &mut World, entity: &Entity, joint: studio_bvh::Joint) { + let offset = joint.data().offset(); + let transform = Transform::from_xyz(offset[0], offset[1], offset[2]); + + // Save the name of the joint + let name = String::from_utf8_lossy(joint.data().name()).to_string(); + + world.entity_mut(*entity).insert(( + SpatialBundle::from_transform(transform), + BvhJoint(joint.data().index()), + Name::new(name), + )); + + for child in joint.children() { + let mut child_entity = world.spawn_empty().id(); + spawn_bvh_recursive(world, &child_entity, child); + world.entity_mut(*entity).push_children(&[child_entity]); + } +} + +// fn locate_rig(world: &mut World, current_entity: Entity) -> Option { +// let Ok(query) = world +// .query::<(Option<&Children>, Option<&Name>)>() +// .get(world, current_entity) +// else { +// return None; +// }; + +// if let Some(name) = query.1 { +// println!("Name: {}", name.as_str()); +// if name.as_str() == "Rig" { +// return Some(current_entity); +// } +// } + +// let children = if let Some(children) = query.0 { +// children.iter().map(|x| x.clone()).collect::>() +// } else { +// return None; +// }; + +// for child in children.iter() { +// println!("Child: {:?}", child); +// if let Some(entity) = locate_rig(world, *child) { +// return Some(entity); +// } +// } + +// None +// } + //============================================================================== // RetargetMap //============================================================================== +#[derive(Debug)] pub struct RetargetMap { pub map: HashMap, } @@ -375,58 +483,3 @@ impl RetargetMap { false } } - -//============================================================================== -// Utility Functions -//============================================================================== - -fn spawn_bvh_recursive(world: &mut World, entity: &Entity, joint: studio_bvh::Joint) { - let offset = joint.data().offset(); - let transform = Transform::from_xyz(offset[0], offset[1], offset[2]); - - // Save the name of the joint - let name = String::from_utf8_lossy(joint.data().name()).to_string(); - - world.entity_mut(*entity).insert(( - SpatialBundle::from_transform(transform), - BvhJoint(joint.data().index()), - Name::new(name), - )); - - for child in joint.children() { - let mut child_entity = world.spawn_empty().id(); - spawn_bvh_recursive(world, &child_entity, child); - world.entity_mut(*entity).push_children(&[child_entity]); - } -} - -fn locate_rig(world: &mut World, current_entity: Entity) -> Option { - let Ok(query) = world - .query::<(Option<&Children>, Option<&Name>)>() - .get(world, current_entity) - else { - return None; - }; - - if let Some(name) = query.1 { - println!("Name: {}", name.as_str()); - if name.as_str() == "Rig" { - return Some(current_entity); - } - } - - let children = if let Some(children) = query.0 { - children.iter().map(|x| x.clone()).collect::>() - } else { - return None; - }; - - for child in children.iter() { - println!("Child: {:?}", child); - if let Some(entity) = locate_rig(world, *child) { - return Some(entity); - } - } - - None -} diff --git a/studio/src/lib.rs b/studio/src/lib.rs index 548c23b..cc0f604 100644 --- a/studio/src/lib.rs +++ b/studio/src/lib.rs @@ -87,7 +87,7 @@ pub fn init(viewport_size: ViewportSize) { Startup, (move |mut cmd: Commands, mut ew: EventWriter| { cmd.insert_resource(SkyboxId("gum_trees_4k".to_string())); - ew.send(LoadSceneEvent("base-human-female".to_string())); + ew.send(LoadSceneEvent("Alien".to_string())); }) ) // .add_systems( @@ -96,8 +96,6 @@ pub fn init(viewport_size: ViewportSize) { // ) ; - - #[cfg(feature = "wasm")] wasm::initialize(); diff --git a/studio/src/scene.rs b/studio/src/scene.rs index be9bd4b..b8badfe 100644 --- a/studio/src/scene.rs +++ b/studio/src/scene.rs @@ -43,8 +43,8 @@ impl Plugin for ScenePlugin { init_scene_loading, tick_scene_loading.run_if(in_state(SceneState::Loading)), tick_scene_spawning.run_if(in_state(SceneState::Spawning)), - ).in_set(SceneSpawnerSystem) - + ) + .in_set(SceneSpawnerSystem), ) .add_systems( OnEnter(SceneState::Loading), @@ -158,11 +158,19 @@ fn spawn_test_bvh( mut commands: Commands, mut er_bvh_events: EventReader>, r_bvh: Res, - mut asset_server : ResMut + mut asset_server: ResMut, ) { for event in er_bvh_events.read() { if matches!(event, AssetEvent::LoadedWithDependencies { .. }) { - commands.spawn_skined_bvh_with_transform(&r_bvh.0, asset_server.load("gltf/base-human-female.gltf#Scene0"), RetargetMap::mocap_to_rigify(), Transform::from_scale(Vec3::splat(1.0)).with_translation(Vec3::new(0., 1.0, -0.024))); + commands.spawn_skined_bvh_with_transform( + &r_bvh.0, + asset_server.load("gltf/base-human-female.gltf#Scene0"), + RetargetMap::mocap_to_rigify(), + Transform::from_scale(Vec3::splat(1.0)) + .with_translation(Vec3::new(0., 1.0, -0.024)), + ); + + commands.spawn_bvh_with_transform(&r_bvh.0, Transform::from_xyz(0.0, 1.0, -0.024)); } } } @@ -176,7 +184,7 @@ fn init_scene_loading( ) { if let Some(id) = er.read().last() { let name = &**id; - let path = format!("gltf/{name}.gltf"); + let path = format!("gltf/{name}.glb"); // Avoid trying to re-load this scene if it's already active if let Some(active_scene_path) =