mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-17 00:13:17 +00:00
Reorganize scene hierarchy
This commit is contained in:
+140
-145
@@ -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::<NewGltf>()
|
||||
.and_then(gltf_loaded)
|
||||
.and_then(gltf_scene_spawned),
|
||||
),
|
||||
mark_scene_objects.run_if(resource_exists::<NewScene>().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<Gltf>);
|
||||
struct NewScene {
|
||||
#[deref]
|
||||
pub scene: Handle<Scene>,
|
||||
pub gltf: Option<Handle<Gltf>>,
|
||||
}
|
||||
|
||||
#[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<AssetServer>, gltf: Res<NewGltf>) -> bool {
|
||||
assets.get_load_state(&**gltf) == LoadState::Loaded
|
||||
}
|
||||
|
||||
fn gltf_scene_spawned(
|
||||
r_assets: Res<AssetServer>,
|
||||
r_gltf: Res<NewGltf>,
|
||||
ra_gltfs: Res<Assets<Gltf>>,
|
||||
q_scenes: Query<&Handle<Scene>, (With<Name>, With<Children>)>,
|
||||
) -> 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<SceneSpawner>, 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<NewGltf>,
|
||||
r_scene: Res<NewScene>,
|
||||
r_assets: Res<AssetServer>,
|
||||
ra_gltfs: Res<Assets<Gltf>>,
|
||||
ra_gltf_nodes: Res<Assets<gltf::GltfNode>>,
|
||||
ra_gltf_meshes: Res<Assets<gltf::GltfMesh>>,
|
||||
ra_gltf_nodes: Res<Assets<GltfNode>>,
|
||||
ra_gltf_meshes: Res<Assets<GltfMesh>>,
|
||||
q_scene_inst: Query<Entity, With<SceneInstance>>,
|
||||
q_parents: Query<(Entity, &Name, &Children), Without<Selectable>>,
|
||||
q_primitives: Query<&Handle<Mesh>>,
|
||||
) {
|
||||
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::<HashSet<_>>();
|
||||
// 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::<HashSet<_>>();
|
||||
|
||||
// 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::<HashSet<_>>();
|
||||
// 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::<HashSet<_>>();
|
||||
|
||||
// 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::<NewGltf>();
|
||||
cmd.remove_resource::<NewScene>();
|
||||
}
|
||||
|
||||
fn move_plane_with_camera(
|
||||
|
||||
Reference in New Issue
Block a user