From aec433803ebb83e98306c97312f3fa6d20d69344 Mon Sep 17 00:00:00 2001 From: Danny McGee Date: Wed, 15 Nov 2023 22:06:30 -0500 Subject: [PATCH] Reorganize scene hierarchy --- studio/src/scene/mod.rs | 285 ++++++++++++++++++++-------------------- 1 file changed, 140 insertions(+), 145 deletions(-) diff --git a/studio/src/scene/mod.rs b/studio/src/scene/mod.rs index 4c14f02..032b03a 100644 --- a/studio/src/scene/mod.rs +++ b/studio/src/scene/mod.rs @@ -1,16 +1,16 @@ use bevy::{ - asset::LoadState, core_pipeline::{ bloom::{BloomCompositeMode, BloomPrefilterSettings, BloomSettings}, fxaa::Fxaa, tonemapping::Tonemapping, }, - gltf::{self, Gltf}, + gltf::{Gltf, GltfMesh, GltfNode}, math::vec3, pbr::NotShadowCaster, prelude::*, reflect::{TypePath, TypeUuid}, render::render_resource::{AsBindGroup, ShaderRef}, + scene::SceneInstance, utils::HashSet, }; use bevy_mod_picking::PickableBundle; @@ -35,11 +35,7 @@ impl Plugin for ScenePlugin { .add_systems(Startup, initialize) .add_systems( Update, - mark_scene_objects.run_if( - resource_exists::() - .and_then(gltf_loaded) - .and_then(gltf_scene_spawned), - ), + mark_scene_objects.run_if(resource_exists::().and_then(scene_inst_ready)), ) .add_systems(PostUpdate, move_plane_with_camera.after(CameraMotionSystem)); @@ -58,7 +54,11 @@ impl Plugin for ScenePlugin { struct WorldGridPlane; #[derive(Resource, Deref, DerefMut)] -struct NewGltf(pub Handle); +struct NewScene { + #[deref] + pub scene: Handle, + pub gltf: Option>, +} #[derive(Component)] struct Inspectable; @@ -73,80 +73,47 @@ fn initialize( cmd.insert_resource(SkyboxId("test_scene".into())); // glTF scene - cmd.insert_resource(NewGltf(assets.load("gltf/sample-room.gltf"))); + let gltf = assets.load("gltf/sample-room.gltf"); + let scene = assets.load("gltf/sample-room.gltf#Scene0"); + cmd.insert_resource(NewScene { + gltf: Some(gltf.clone()), + scene: scene.clone(), + }); cmd.spawn(( Name::new("Scene"), Inspectable, - SceneBundle { - scene: assets.load("gltf/sample-room.gltf#Scene0"), - ..default() - }, - )); - - // Camera, graphics settings - let focus = vec3(0.35011, 0.5, -0.99576); - let camera_xform = Transform::from_xyz(-2.5, 4.5, 5.0).looking_at(focus, Vec3::Y); - cmd.spawn(( - Name::new("Main Camera"), - MainCamera, - Camera3dBundle { - transform: camera_xform, - tonemapping: Tonemapping::BlenderFilmic, - camera: Camera { - hdr: true, + SceneBundle { scene, ..default() }, + )) + .with_children(|cmd| { + // Camera, graphics settings + let focus = vec3(0.35011, 0.5, -0.99576); + let camera_xform = Transform::from_xyz(-2.5, 4.5, 5.0).looking_at(focus, Vec3::Y); + cmd.spawn(( + Name::new("Main Camera"), + MainCamera, + Inspectable, + Camera3dBundle { + transform: camera_xform, + tonemapping: Tonemapping::BlenderFilmic, + camera: Camera { + hdr: true, + ..default() + }, ..default() }, - ..default() - }, - CameraControllerBundle::new(focus, camera_xform.translation), - BloomSettings { - composite_mode: BloomCompositeMode::EnergyConserving, - intensity: 0.25, - prefilter_settings: BloomPrefilterSettings { - threshold: 0.8, - threshold_softness: 0.5, - }, - ..default() - }, - Fxaa::default(), - )); - - // Main light - // TODO: get Blender to export enough metadata to automate light placements - cmd.spawn(( - Name::new("Ceiling Light"), - Selectable, - Inspectable, - PointLightBundle { - point_light: PointLight { - color: Color::hex("FFE5B4").unwrap(), - intensity: 200., - radius: 0.1, - shadows_enabled: true, + CameraControllerBundle::new(focus, camera_xform.translation), + BloomSettings { + composite_mode: BloomCompositeMode::EnergyConserving, + intensity: 0.25, + prefilter_settings: BloomPrefilterSettings { + threshold: 0.8, + threshold_softness: 0.5, + }, ..default() }, - transform: Transform::from_xyz(0.35263, 2.4348, 0.19601), - ..default() - }, - )); - - // Monitor glow - cmd.spawn(( - Name::new("Monitor Glow"), - Selectable, - Inspectable, - PointLightBundle { - point_light: PointLight { - color: Color::WHITE, - intensity: 6., - radius: 0.25, - shadows_enabled: false, - ..default() - }, - transform: Transform::from_xyz(0.42615, 1.0706, -2.0705), - ..default() - }, - )); + Fxaa::default(), + )); + }); // Plane let plane = ra_meshes.add(Mesh::from(shape::Plane { @@ -182,101 +149,129 @@ fn initialize( }); } -fn gltf_loaded(assets: Res, gltf: Res) -> bool { - assets.get_load_state(&**gltf) == LoadState::Loaded -} - -fn gltf_scene_spawned( - r_assets: Res, - r_gltf: Res, - ra_gltfs: Res>, - q_scenes: Query<&Handle, (With, With)>, -) -> bool { - let gltf = ra_gltfs.get(&r_gltf).unwrap(); - let Some(gltf_scene_path) = gltf - .default_scene - .as_ref() - .and_then(|handle| r_assets.get_handle_path(handle)) - else { +fn scene_inst_ready(r_scene_mgr: Res, q_scene_inst: Query<&SceneInstance>) -> bool { + let Ok(scene_inst) = q_scene_inst.get_single() else { return false; }; - q_scenes.iter().any(|handle| { - let Some(path) = r_assets.get_handle_path(handle) else { - return false; - }; - path == gltf_scene_path - }) + r_scene_mgr.instance_is_ready(**scene_inst) } fn mark_scene_objects( mut cmd: Commands, - r_gltf: Res, + r_scene: Res, r_assets: Res, ra_gltfs: Res>, - ra_gltf_nodes: Res>, - ra_gltf_meshes: Res>, + ra_gltf_nodes: Res>, + ra_gltf_meshes: Res>, + q_scene_inst: Query>, q_parents: Query<(Entity, &Name, &Children), Without>, q_primitives: Query<&Handle>, ) { - let gltf = ra_gltfs.get(&r_gltf).unwrap(); + let scene_ent = q_scene_inst.single(); - for (ent_parent, name, children) in q_parents.iter() { - if gltf.named_nodes.contains_key(&**name) { - cmd.entity(ent_parent).insert(Inspectable); - } + // Spawn lights + cmd.entity(scene_ent).with_children(|cmd| { + // Main light + // TODO: get Blender to export enough metadata to automate light placements + cmd.spawn(( + Name::new("Ceiling Light"), + Selectable, + Inspectable, + PointLightBundle { + point_light: PointLight { + color: Color::hex("FFE5B4").unwrap(), + intensity: 200., + radius: 0.1, + shadows_enabled: true, + ..default() + }, + transform: Transform::from_xyz(0.35263, 2.4348, 0.19601), + ..default() + }, + )); - // See if this name matches any of the meshes or nodes in our glTF scene - let Some(gltf_mesh) = gltf - .named_meshes - .get(&**name) - .cloned() - .or_else(|| { - gltf.named_nodes.get(&**name).and_then(|node_handle| { - ra_gltf_nodes - .get(node_handle) - .and_then(|node| node.mesh.clone()) + // Monitor glow + cmd.spawn(( + Name::new("Monitor Glow"), + Selectable, + Inspectable, + PointLightBundle { + point_light: PointLight { + color: Color::WHITE, + intensity: 6., + radius: 0.25, + shadows_enabled: false, + ..default() + }, + transform: Transform::from_xyz(0.42615, 1.0706, -2.0705), + ..default() + }, + )); + }); + + if let Some(gltf) = r_scene + .gltf + .as_ref() + .and_then(|handle| ra_gltfs.get(handle)) + { + for (ent_parent, name, children) in q_parents.iter() { + if gltf.named_nodes.contains_key(&**name) { + cmd.entity(ent_parent).insert(Inspectable); + } + + // See if this name matches any of the meshes or nodes in our glTF scene + let Some(gltf_mesh) = gltf + .named_meshes + .get(&**name) + .cloned() + .or_else(|| { + gltf.named_nodes.get(&**name).and_then(|node_handle| { + ra_gltf_nodes + .get(node_handle) + .and_then(|node| node.mesh.clone()) + }) }) - }) - .and_then(|handle| ra_gltf_meshes.get(&handle)) - else { - continue; - }; + .and_then(|handle| ra_gltf_meshes.get(&handle)) + else { + continue; + }; - // FIXME: These hashsets don't actually contain anything. Ever. -_- - // This has "worked" this whole time because {} == {} + // FIXME: These hashsets don't actually contain anything. Ever. -_- + // This has "worked" this whole time because {} == {} - // Collect all of the mesh handles in the glTF mesh - let prims_from_gltf = gltf_mesh - .primitives - .iter() - .filter_map(|prim| r_assets.get_handle_path(&prim.mesh)) - .collect::>(); + // Collect all of the mesh handles in the glTF mesh + let prims_from_gltf = gltf_mesh + .primitives + .iter() + .filter_map(|prim| r_assets.get_handle_path(&prim.mesh)) + .collect::>(); - // Collect all of the mesh handles in this entity's children - let prims_from_ecs = children - .iter() - .filter_map(|ent_child| { - q_primitives - .get(*ent_child) - .ok() - .and_then(|handle| r_assets.get_handle_path(handle)) - }) - .collect::>(); + // Collect all of the mesh handles in this entity's children + let prims_from_ecs = children + .iter() + .filter_map(|ent_child| { + q_primitives + .get(*ent_child) + .ok() + .and_then(|handle| r_assets.get_handle_path(handle)) + }) + .collect::>(); - // If the sets match, this is the entity that represents the glTF mesh - if prims_from_gltf == prims_from_ecs { - cmd.entity(ent_parent).insert(Selectable); + // If the sets match, this is the entity that represents the glTF mesh + if prims_from_gltf == prims_from_ecs { + cmd.entity(ent_parent).insert(Selectable); - for &ent_child in children.iter() { - if q_primitives.contains(ent_child) { - cmd.entity(ent_child).insert(PickableBundle::default()); + for &ent_child in children.iter() { + if q_primitives.contains(ent_child) { + cmd.entity(ent_child).insert(PickableBundle::default()); + } } } } } - cmd.remove_resource::(); + cmd.remove_resource::(); } fn move_plane_with_camera(