diff --git a/studio-frontend/src/app/app.element.ts b/studio-frontend/src/app/app.element.ts index 7197e56..1d1e4c8 100644 --- a/studio-frontend/src/app/app.element.ts +++ b/studio-frontend/src/app/app.element.ts @@ -192,7 +192,7 @@ export class AppElement extends LitElement { try { studio.startViewer( "base-human-female", - "meadow_4k", + "test_scene", "#studio_canvas", "Storyteller Studio", new ViewportSize(width, height), diff --git a/studio/src/debug.rs b/studio/src/debug.rs index c1a0d07..03bc7a5 100644 --- a/studio/src/debug.rs +++ b/studio/src/debug.rs @@ -7,6 +7,7 @@ use bevy_mod_picking::debug::DebugPickingMode; use crate::{ camera_controller::{CameraController, CameraMotionSystem}, input::{Action, InputAction, Lifecycle}, + scene::SkeletalJoint, MainCamera, }; @@ -38,7 +39,7 @@ impl Plugin for DebugPlugin { toggle_debug_mode, update_debug_picking_mode.run_if(resource_changed::()), update_gizmo_line_thickness, - (draw_world_axes, draw_debug_points) + (draw_world_axes, draw_debug_points, draw_skeletal_joints) .after(CameraMotionSystem) .run_if(is_debug_mode), ), @@ -108,6 +109,26 @@ fn draw_debug_points( } } +fn draw_skeletal_joints( + mut gizmos: Gizmos, + q_joints: Query<(&GlobalTransform, Option<&Parent>), With>, +) { + for (world_xform, parent) in q_joints.iter() { + let (_, rotation, xlation) = world_xform.to_scale_rotation_translation(); + + if let Some((parent_xform, _)) = parent.and_then(|parent| q_joints.get(**parent).ok()) { + let parent_xlation = parent_xform.translation(); + let bone_len = Vec3::distance(xlation, parent_xlation); + let joint_radius = f32::min(0.01, bone_len / 6.); + + gizmos.sphere(xlation, rotation, joint_radius, Color::BLACK); + gizmos.line(xlation, parent_xlation, Color::BLACK); + } else { + gizmos.sphere(xlation, rotation, 0.01, Color::BLACK); + } + } +} + #[rustfmt::skip] #[allow(dead_code)] pub fn write_mat3(w: &mut impl fmt::Write, m: &Mat3) -> fmt::Result { diff --git a/studio/src/scene.rs b/studio/src/scene.rs index b4cc5c0..3d47585 100644 --- a/studio/src/scene.rs +++ b/studio/src/scene.rs @@ -9,7 +9,7 @@ use bevy::{ math::{vec2, vec3}, pbr::ShadowFilteringMethod, prelude::*, - render::primitives::Aabb, + render::{mesh::skinning::SkinnedMesh, primitives::Aabb}, scene::InstanceId, }; @@ -107,6 +107,9 @@ pub struct LoadSceneEvent(pub String); #[derive(Component)] pub struct SceneElement; +#[derive(Component)] +pub struct SkeletalJoint; + #[derive(Component)] pub struct SceneRoot; @@ -245,18 +248,30 @@ fn tag_scene_elements( // leafy parts of a plant. Here we only want to tag the elements that a user // might want to individually address when arranging or animating a scene. q_named_non_primitives: Query<&Name, Without>>, + q_skinned_meshes: Query<&SkinnedMesh>, ) { let gltf = ra_gltfs.get(&r_active_scene.source).unwrap(); - for ent in r_scene_spawner.iter_instance_entities(**r_active_scene) { - let Ok(name) = q_named_non_primitives.get(ent) else { - continue; - }; - + for (ent, name) in r_scene_spawner + .iter_instance_entities(**r_active_scene) + .filter_map(|ent| match q_named_non_primitives.get(ent) { + Ok(name) => Some((ent, name)), + _ => None, + }) + { if gltf.named_nodes.contains_key(&**name) { cmd.entity(ent).insert(SceneElement); } } + + for skinned_mesh in r_scene_spawner + .iter_instance_entities(**r_active_scene) + .filter_map(|ent| q_skinned_meshes.get(ent).ok()) + { + for &joint in skinned_mesh.joints.iter() { + cmd.entity(joint).insert(SkeletalJoint); + } + } } fn spawn_main_scene_light(