diff --git a/Cargo.lock b/Cargo.lock index d1a98a7..c480f65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3787,7 +3787,7 @@ dependencies = [ [[package]] name = "space_bevy_xpbd_plugin" version = "0.3.1" -source = "git+https://github.com/rewin123/space_editor?rev=b56d81222cbf59448ecda5064bf1b37ca90b84f1#b56d81222cbf59448ecda5064bf1b37ca90b84f1" +source = "git+https://github.com/rewin123/space_editor?rev=ce83f86dc902787ab16213f3209276a784654df6#ce83f86dc902787ab16213f3209276a784654df6" dependencies = [ "bevy", "bevy_xpbd_3d", @@ -3797,7 +3797,7 @@ dependencies = [ [[package]] name = "space_editor" version = "0.3.1" -source = "git+https://github.com/rewin123/space_editor?rev=b56d81222cbf59448ecda5064bf1b37ca90b84f1#b56d81222cbf59448ecda5064bf1b37ca90b84f1" +source = "git+https://github.com/rewin123/space_editor?rev=ce83f86dc902787ab16213f3209276a784654df6#ce83f86dc902787ab16213f3209276a784654df6" dependencies = [ "bevy", "space_bevy_xpbd_plugin", @@ -3808,7 +3808,7 @@ dependencies = [ [[package]] name = "space_editor_core" version = "0.3.1" -source = "git+https://github.com/rewin123/space_editor?rev=b56d81222cbf59448ecda5064bf1b37ca90b84f1#b56d81222cbf59448ecda5064bf1b37ca90b84f1" +source = "git+https://github.com/rewin123/space_editor?rev=ce83f86dc902787ab16213f3209276a784654df6#ce83f86dc902787ab16213f3209276a784654df6" dependencies = [ "bevy", "space_persistence", @@ -3820,7 +3820,7 @@ dependencies = [ [[package]] name = "space_editor_ui" version = "0.3.1" -source = "git+https://github.com/rewin123/space_editor?rev=b56d81222cbf59448ecda5064bf1b37ca90b84f1#b56d81222cbf59448ecda5064bf1b37ca90b84f1" +source = "git+https://github.com/rewin123/space_editor?rev=ce83f86dc902787ab16213f3209276a784654df6#ce83f86dc902787ab16213f3209276a784654df6" dependencies = [ "bevy", "bevy_debug_grid", @@ -3841,7 +3841,7 @@ dependencies = [ [[package]] name = "space_persistence" version = "0.3.1" -source = "git+https://github.com/rewin123/space_editor?rev=b56d81222cbf59448ecda5064bf1b37ca90b84f1#b56d81222cbf59448ecda5064bf1b37ca90b84f1" +source = "git+https://github.com/rewin123/space_editor?rev=ce83f86dc902787ab16213f3209276a784654df6#ce83f86dc902787ab16213f3209276a784654df6" dependencies = [ "bevy", "ron", @@ -3851,7 +3851,7 @@ dependencies = [ [[package]] name = "space_prefab" version = "0.3.1" -source = "git+https://github.com/rewin123/space_editor?rev=b56d81222cbf59448ecda5064bf1b37ca90b84f1#b56d81222cbf59448ecda5064bf1b37ca90b84f1" +source = "git+https://github.com/rewin123/space_editor?rev=ce83f86dc902787ab16213f3209276a784654df6#ce83f86dc902787ab16213f3209276a784654df6" dependencies = [ "bevy", "bevy-inspector-egui", @@ -3865,7 +3865,7 @@ dependencies = [ [[package]] name = "space_shared" version = "0.3.1" -source = "git+https://github.com/rewin123/space_editor?rev=b56d81222cbf59448ecda5064bf1b37ca90b84f1#b56d81222cbf59448ecda5064bf1b37ca90b84f1" +source = "git+https://github.com/rewin123/space_editor?rev=ce83f86dc902787ab16213f3209276a784654df6#ce83f86dc902787ab16213f3209276a784654df6" dependencies = [ "bevy", "bevy-inspector-egui", @@ -3875,11 +3875,10 @@ dependencies = [ [[package]] name = "space_undo" version = "0.3.1" -source = "git+https://github.com/rewin123/space_editor?rev=b56d81222cbf59448ecda5064bf1b37ca90b84f1#b56d81222cbf59448ecda5064bf1b37ca90b84f1" +source = "git+https://github.com/rewin123/space_editor?rev=ce83f86dc902787ab16213f3209276a784654df6#ce83f86dc902787ab16213f3209276a784654df6" dependencies = [ "bevy", "pretty-type-name", - "space_shared", ] [[package]] diff --git a/studio/assets/gltf/sample-room.glb b/studio/assets/gltf/sample-room.glb new file mode 100644 index 0000000..f4f2217 Binary files /dev/null and b/studio/assets/gltf/sample-room.glb differ diff --git a/studio/src/debug.rs b/studio/src/debug.rs index a72199b..2c7d897 100644 --- a/studio/src/debug.rs +++ b/studio/src/debug.rs @@ -1,9 +1,15 @@ use std::fmt; use bevy::{prelude::*, window::PrimaryWindow}; -use bevy_inspector_egui::{quick::WorldInspectorPlugin, bevy_egui::EguiPlugin, DefaultInspectorConfigPlugin}; +use bevy_inspector_egui::{bevy_egui::EguiPlugin, DefaultInspectorConfigPlugin}; use bevy_mod_picking::debug::DebugPickingMode; -use space_editor::{space_editor_ui::{EditorPluginGroup, EditorPickingPlugin, EditorDefaultCameraPlugin, bottom_menu::BottomMenuPlugin, EditorUiCore, tools::gizmo::GizmoToolPlugin, ShowEditorUi}, prelude::{EditorCameraMarker, space_undo::UndoPlugin}}; +use space_editor::{ + prelude::EditorCameraMarker, + space_editor_ui::{ + bottom_menu::BottomMenuPlugin, tools::gizmo::GizmoToolPlugin, EditorDefaultCameraPlugin, + EditorPickingPlugin, EditorPluginGroup, EditorUiCore, ShowEditorUi, + }, +}; use crate::{ camera_controller::{CameraController, CameraMotionSystem}, @@ -46,14 +52,14 @@ impl Plugin for DebugPlugin { ), ); - //space_editor stuff // app.add_plugins(UndoPlugin); app.add_plugins( - EditorPluginGroup.build() + EditorPluginGroup + .build() .disable::() .add_before::(EditorUiCore { - disable_no_editor_cams: false + disable_no_editor_cams: false, }) .disable::() .disable::() @@ -62,18 +68,19 @@ impl Plugin for DebugPlugin { .disable::() .disable::() .set(space_editor::prelude::SelectedPlugin { - show_selected_wireframe: false - }) + show_selected_wireframe: false, + }), ); app.add_systems(Update, sync_editor_camera); - app.add_systems(Update, update_editor_ui_mode.run_if(resource_changed::())); + app.add_systems( + Update, + update_editor_ui_mode.run_if(resource_changed::()), + ); } } -fn toggle_debug_mode( - mut input: EventReader, - mut r_debug: ResMut,) { +fn toggle_debug_mode(mut input: EventReader, mut r_debug: ResMut) { if input .read() .any(|action| matches!(action, InputAction(Lifecycle::Stop, Action::ToggleDebug))) @@ -164,10 +171,10 @@ pub fn print_mat3(m: &Mat3, label: impl fmt::Display) -> fmt::Result { } fn sync_editor_camera( - mut cmd : Commands, - mut q_cam : Query, Without)> + mut cmd: Commands, + mut q_cam: Query, Without)>, ) { for ent in q_cam.iter_mut() { cmd.entity(ent).insert(EditorCameraMarker); } -} \ No newline at end of file +} diff --git a/studio/src/input.rs b/studio/src/input.rs index 552f402..30add81 100644 --- a/studio/src/input.rs +++ b/studio/src/input.rs @@ -320,12 +320,18 @@ fn read_mouse_delta( // TODO: Make it configurable fn send_undo_event(mut events: EventWriter, keyboard: Res>) { - if keyboard.pressed(KeyCode::ControlLeft) && keyboard.just_pressed(KeyCode::Z) && !keyboard.pressed(KeyCode::ShiftLeft) { + if keyboard.pressed(KeyCode::ControlLeft) + && keyboard.just_pressed(KeyCode::Z) + && !keyboard.pressed(KeyCode::ShiftLeft) + { events.send(UndoRedo::Undo); info!("Undo event sent"); } - if keyboard.pressed(KeyCode::ControlLeft) && keyboard.just_pressed(KeyCode::Z) && keyboard.pressed(KeyCode::ShiftLeft) { + if keyboard.pressed(KeyCode::ControlLeft) + && keyboard.just_pressed(KeyCode::Z) + && keyboard.pressed(KeyCode::ShiftLeft) + { events.send(UndoRedo::Redo); info!("Redo event sent"); } diff --git a/studio/src/interaction/gizmos/camera.rs b/studio/src/interaction/gizmos/camera.rs index f4bc999..4b6fde6 100644 --- a/studio/src/interaction/gizmos/camera.rs +++ b/studio/src/interaction/gizmos/camera.rs @@ -36,7 +36,7 @@ pub(super) fn spawn_gizmo_camera( }, RenderLayers::layer(GIZMO_RENDER_LAYER), Fxaa::default(), - NotShowCamera + NotShowCamera, )); }); } diff --git a/studio/src/interaction/selection.rs b/studio/src/interaction/selection.rs index defa2bf..99c3897 100644 --- a/studio/src/interaction/selection.rs +++ b/studio/src/interaction/selection.rs @@ -24,7 +24,7 @@ impl Plugin for SelectionPlugin { .chain() .after(SceneSpawnerSystem), ); - + app.add_systems(Update, tag_scene_elements); #[cfg(feature = "wasm")] @@ -35,10 +35,7 @@ impl Plugin for SelectionPlugin { .run_if(resource_changed_or_removed::()), ); - app.add_systems( - Update, - sync_editor_selected - ); + app.add_systems(Update, sync_editor_selected); } } @@ -56,11 +53,15 @@ fn sync_editor_selected( q_new_selected: Query, Without)>, q_editor_selected: Query>, q_selected: Query>, - q_added_editor_selected: Query, Added)>, + q_added_editor_selected: Query< + Entity, + (Without, Added), + >, ) { for ent in q_new_selected.iter() { for ed_ent in q_editor_selected.iter() { - cmd.entity(ed_ent).remove::(); + cmd.entity(ed_ent) + .remove::(); } cmd.entity(ent).insert(space_editor::prelude::Selected); } diff --git a/studio/src/lib.rs b/studio/src/lib.rs index 3c2d390..049b5bc 100644 --- a/studio/src/lib.rs +++ b/studio/src/lib.rs @@ -3,10 +3,6 @@ use bevy::{app::PluginGroupBuilder, prelude::*}; use bevy_mod_picking::DefaultPickingPlugins; -use space_editor::{ - prelude::{space_undo::UndoPlugin, PersistencePlugin}, - space_editor_ui::{EditorPluginGroup, ext::{bevy_egui::EguiPlugin, bevy_inspector_egui::DefaultInspectorConfigPlugin}, EditorPickingPlugin}, -}; #[cfg(feature = "wasm")] use wasm_bindgen::prelude::*; @@ -17,12 +13,12 @@ mod input; mod inspector; mod interaction; mod math; +mod persistance; mod scene; mod skybox; mod ui; mod viewport; mod world_grid; -mod persistance; use crate::{ camera_controller::CameraControllerPlugin, @@ -79,9 +75,7 @@ impl PluginGroup for CommonPlugins { struct EditorPlugins; impl PluginGroup for EditorPlugins { fn build(self) -> PluginGroupBuilder { - PluginGroupBuilder::start::() - .add(InteractionPlugin) - .add(UndoPlugin) + PluginGroupBuilder::start::().add(InteractionPlugin) } } @@ -125,7 +119,7 @@ pub fn init(viewport_size: ViewportSize) { SkyboxPlugin, UiLayerPlugin, WorldGridPlugin, - persistance::PersistancePlugin + persistance::PersistancePlugin, )) .add_systems( Startup, @@ -228,7 +222,8 @@ pub fn start_internal( CommonPlugins, )); - if matches!(studio_mode, StudioMode::Editor) { + if studio_mode == StudioMode::Editor { + info!("Starting in editor mode"); app.add_plugins((DefaultPickingPlugins, EditorPlugins)); // Only for registering components for undo diff --git a/studio/src/persistance/mod.rs b/studio/src/persistance/mod.rs index 405c1c0..6f5842c 100644 --- a/studio/src/persistance/mod.rs +++ b/studio/src/persistance/mod.rs @@ -6,17 +6,23 @@ pub struct PersistancePlugin; impl Plugin for PersistancePlugin { fn build(&self, app: &mut App) { info!("PersistancePlugin::build"); - app.editor_tab_by_trait(EditorTabName::Other("Persistance".to_string()), DebugPersistanceTab::default()); + app.editor_tab_by_trait( + EditorTabName::Other("Persistance".to_string()), + DebugPersistanceTab::default(), + ); } } #[derive(Resource, Default)] -pub struct DebugPersistanceTab { - -} +pub struct DebugPersistanceTab {} impl EditorTab for DebugPersistanceTab { - fn ui(&mut self, ui: &mut bevy_inspector_egui::egui::Ui, commands: &mut Commands, world: &mut World) { + fn ui( + &mut self, + ui: &mut bevy_inspector_egui::egui::Ui, + commands: &mut Commands, + world: &mut World, + ) { if ui.button("Save").clicked() { world.send_event(EditorEvent::Save(EditorPrefabPath::MemoryCahce)); } @@ -36,4 +42,4 @@ impl EditorTab for DebugPersistanceTab { fn title(&self) -> bevy_inspector_egui::egui::WidgetText { "Persistance".into() } -} \ No newline at end of file +} diff --git a/studio/src/scene.rs b/studio/src/scene.rs index de0c07d..7d051db 100644 --- a/studio/src/scene.rs +++ b/studio/src/scene.rs @@ -1,19 +1,19 @@ use bevy::{ - asset::AssetPath, + asset::{DependencyLoadState, LoadState, RecursiveDependencyLoadState}, core_pipeline::{ bloom::{BloomCompositeMode, BloomPrefilterSettings, BloomSettings}, fxaa::Fxaa, tonemapping::Tonemapping, }, - gltf::{Gltf, GltfLoader}, - math::{vec2, vec3}, + gltf::Gltf, + math::vec2, pbr::ShadowFilteringMethod, prelude::*, render::primitives::Aabb, scene::InstanceId, }; -use space_editor::{prelude::{space_undo::OneFrameUndoIgnore, PrefabMarker}, space_prefab::component::{SceneAutoChild, SceneAutoRoot}}; +use space_editor::{prelude::PrefabMarker, space_prefab::component::SceneAutoChild}; #[cfg(feature = "wasm")] use wasm_bindgen::prelude::wasm_bindgen; @@ -40,31 +40,33 @@ impl Plugin for ScenePlugin { Update, ( init_scene_loading, - tick_scene_loading.run_if(in_state(SceneState::Loading)), tick_scene_spawning.run_if(in_state(SceneState::Spawning)), ) .in_set(SceneSpawnerSystem), ) - .add_systems( - OnEnter(SceneState::Loading), - despawn_previous_scene.in_set(SceneSpawnerSystem), - ) - .add_systems( - OnEnter(SceneState::Spawning), - spawn_next_scene.in_set(SceneSpawnerSystem), - ) .add_systems( OnEnter(SceneState::Active), - ( - tag_scene_elements, - spawn_main_scene_light, - frame_scene_in_view, - ) - .in_set(SceneSpawnerSystem), + (tag_scene_elements, frame_scene_in_view).in_set(SceneSpawnerSystem), ); app.add_systems(Update, tag_scene_elements); + //Auto loading screen + //Register handles on which we will look to show loading screen + app.init_resource::(); + app.add_systems( + PostUpdate, + ( + check_loading, + auto_add_handle::, + auto_add_handle::, + auto_add_handle::, + ), + ); + + //setup default light + app.add_systems(Startup, minimal_scene_setup); + #[cfg(feature = "wasm")] app.add_systems( Update, @@ -78,6 +80,18 @@ impl Plugin for ScenePlugin { } } +fn minimal_scene_setup(mut cmd: Commands) { + cmd.spawn(DirectionalLightBundle { + directional_light: DirectionalLight { + shadows_enabled: false, + ..default() + }, + transform: Transform::default().looking_at(Vec3::NEG_ONE, Vec3::Y), + ..default() + }) + .insert(PrefabMarker); +} + #[cfg_attr(feature = "wasm", wasm_bindgen)] #[derive(States, Clone, Copy, Default, Debug, PartialEq, Eq, Hash)] pub enum SceneState { @@ -88,9 +102,6 @@ pub enum SceneState { Active, } -#[derive(Resource, Deref, DerefMut)] -struct PendingScene(pub Handle); - #[derive(Resource, Deref)] pub struct ActiveScene { #[deref] @@ -98,6 +109,51 @@ pub struct ActiveScene { pub source: Handle, } +#[derive(Resource, Default)] +pub struct LoadingQueue { + pub queue: Vec, +} + +fn check_loading( + mut loading_queue: ResMut, + mut st_next: ResMut>, + asset_server: Res, +) { + if !loading_queue.queue.is_empty() { + //filter queue to only include notloaded assets + loading_queue.queue.retain(|h| { + if let Some((load_state, dep_load_state, rec_load_state)) = + asset_server.get_load_states(h.id()) + { + let is_loaded = dep_load_state == DependencyLoadState::Loaded; + + let is_failed = dep_load_state == DependencyLoadState::Failed + || rec_load_state == RecursiveDependencyLoadState::Failed + || load_state == LoadState::Failed; + + !(is_failed || is_loaded) + } else { + false + } + }); + + info!("Loading {} assets", loading_queue.queue.len()); + + st_next.set(SceneState::Loading); + } else { + st_next.set(SceneState::Active); + } +} + +fn auto_add_handle( + mut queue: ResMut, + query: Query<&Handle, Added>>, +) { + for h in query.iter() { + queue.queue.push(h.clone().untyped()); + } +} + #[derive(Event, Deref)] pub struct LoadSceneEvent(pub String); @@ -115,7 +171,6 @@ pub struct SceneData { pub scene_path: String, } - #[derive(Component)] pub struct SceneRoot; @@ -152,8 +207,6 @@ fn initialize(mut cmd: Commands) { fn init_scene_loading( mut cmd: Commands, - assets: Res, - r_active_scene: Option>, mut er: EventReader, mut st_next: ResMut>, ) { @@ -168,65 +221,9 @@ fn init_scene_loading( scene: "Scene0".to_string(), }, PrefabMarker, - SceneRoot - )); - - } -} - -fn despawn_previous_scene(mut cmd: Commands, q_scene_root: Query>) { - if let Ok(scene_root) = q_scene_root.get_single() { - cmd.entity(scene_root).despawn_recursive(); - } -} - -fn tick_scene_loading( - r_pending_scene: Res, - ra_gltfs: Res>, - mut er_scene_events: EventReader>, - mut st_next: ResMut>, -) { - for event in er_scene_events.read() { - if let AssetEvent::LoadedWithDependencies { id } = event { - let gltf = ra_gltfs.get(&**r_pending_scene).unwrap(); - if gltf.default_scene.is_some() && gltf.default_scene.as_ref().unwrap().id() == *id { - st_next.set(SceneState::Spawning); - break; - } - } - } -} - -fn spawn_next_scene( - mut cmd: Commands, - ra_gltfs: Res>, - r_pending_scene: Res, - mut r_scene_spawner: ResMut, -) { - let gltf = ra_gltfs.get(&**r_pending_scene).unwrap(); - let scene = gltf - .default_scene - .as_ref() - .expect("Expected glTF to include a default scene"); - - let parent = cmd - .spawn(( - Name::new("Scene"), SceneRoot, - SceneElement, - SpatialBundle::default(), - PrefabMarker, - OneFrameUndoIgnore::default(), //To prevert undo try to register add event - )) - .id(); - - let instance = r_scene_spawner.spawn_as_child(scene.clone(), parent); - - cmd.remove_resource::(); - cmd.insert_resource(ActiveScene { - instance, - source: r_pending_scene.clone(), - }); + )); + } } fn tick_scene_spawning( @@ -251,152 +248,101 @@ fn tag_scene_elements( // because they often consist of stuff like a single eyeball, or just the // 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, With, Without>, Without)>, + q_named_non_primitives: Query< + Entity, + ( + With, + With, + Without>, + Without, + ), + >, ) { for loaded_part in q_named_non_primitives.iter() { cmd.entity(loaded_part).insert(SceneElement); } } -fn spawn_main_scene_light( +fn frame_scene_in_view( mut cmd: Commands, - r_scene_spawner: Res, - // r_active_scene: Res, - q_aabbs: Query<(&Aabb, &GlobalTransform)>, - q_scene_root: Query>, + q_aabbs: Query<(&Aabb, &GlobalTransform), With>, + mut q_camera: Query<(&mut CameraController, &mut Transform, &Projection)>, ) { - let Ok(scene_root) = q_scene_root.get_single() else { + let Ok((mut cam, mut cam_xform, cam_proj)) = q_camera.get_single_mut() else { + error!("Failed to get camera controller"); return; }; - // let Some(aabb) = Aabb::combine( - // r_scene_spawner - // .iter_instance_entities(**r_active_scene) - // .filter_map(|ent| q_aabbs.get(ent).ok()), - // ) else { - // return; - // }; + let Some(aabb) = Aabb::combine(q_aabbs.iter()) else { + return; + }; - // let light_offset_base = vec3(0.5, 1., 1.).normalize(); - // let size = f32::max( - // f32::max(aabb.half_extents.x, aabb.half_extents.y), - // aabb.half_extents.z, - // ) * 2.; - // let light_pos = Vec3::from(aabb.center) + light_offset_base * size * 2.; + let world2cam = cam_xform.compute_matrix().inverse(); + let plane_origin = Vec3::from(aabb.center); + let plane_normal = cam_xform.forward(); - // cmd.spawn(DebugPoint { - // point: light_pos, - // color: Color::WHITE, - // }); - // cmd.entity(scene_root).with_children(|cmd| { - // cmd.spawn(( - // Name::new("Light"), - // SceneElement, - // SpotLightBundle { - // spot_light: SpotLight { - // color: Color::WHITE, - // // TODO: This doesn't scale super well to very large objects - // intensity: 200. * size, - // radius: 0.1, - // shadows_enabled: true, - // shadow_normal_bias: 0.2 * size, - // outer_angle: f32::to_radians(150.), - // inner_angle: f32::to_radians(45.), - // ..default() - // }, - // transform: Transform::from_translation(light_pos) - // .looking_at(aabb.center.into(), Vec3::Y), - // ..default() - // }, - // )); - // }); -} + let Some((_depth, bounding_rect)) = aabb + .extrema() + .iter() + .copied() + .map(|mut p| { + cmd.spawn(DebugPoint { + point: p, + color: Color::CYAN, + }); -fn frame_scene_in_view( - // mut cmd: Commands, - // r_scene_spawner: Res, - // r_active_scene: Res, - // q_aabbs: Query<(&Aabb, &GlobalTransform)>, - // mut q_camera: Query<(&mut CameraController, &mut Transform, &Projection)>, -) { - // let Ok((mut cam, mut cam_xform, cam_proj)) = q_camera.get_single_mut() else { - // return; - // }; + // Inflate the combined AABB to give the model some breathing room + p *= 1.5; - // let Some(aabb) = Aabb::combine( - // r_scene_spawner - // .iter_instance_entities(**r_active_scene) - // .filter_map(|ent| q_aabbs.get(ent).ok()), - // ) else { - // return; - // }; + // Project each corner onto the camera plane + let v = p - plane_origin; + let proj = p - v.project_onto_normalized(plane_normal); - // let world2cam = cam_xform.compute_matrix().inverse(); - // let plane_origin = Vec3::from(aabb.center); - // let plane_normal = cam_xform.forward(); + cmd.spawn(DebugPoint { + point: proj, + color: Color::FUCHSIA, + }); - // let Some((_depth, bounding_rect)) = aabb - // .extrema() - // .iter() - // .copied() - // .map(|mut p| { - // cmd.spawn(DebugPoint { - // point: p, - // color: Color::CYAN, - // }); + // Transform into the camera's local space + world2cam.transform_point(proj) + }) + // Fold the camera-space plane-projected points into a bounding rect and view depth + .fold(None, |accum: Option<(f32, Rect)>, p: Vec3| match accum { + Some((depth, rect)) => Some((depth, rect.union_point(vec2(p.x, p.y)))), + None => { + let depth = p.z.abs(); + let p = vec2(p.x, p.y); + Some((depth, Rect::from_corners(p, p))) + } + }) + else { + return; + }; - // // Inflate the combined AABB to give the model some breathing room - // p *= 1.5; + let pproj = match cam_proj { + Projection::Perspective(p) => p, + _ => panic!("Expected a perspective projection"), + }; - // // Project each corner onto the camera plane - // let v = p - plane_origin; - // let proj = p - v.project_onto_normalized(plane_normal); + // Formula for the target camera distance + // (c/o https://stackoverflow.com/questions/2866350/move-camera-to-fit-3d-scene): + // + // d = (s/2) / tan(a/2) + // where: + // d = distance + // s = size of the largest camera-aligned axis in world coords + // a = field-of-view angle for the same axis + let Vec2 { + x: width, + y: height, + } = bounding_rect.size(); - // cmd.spawn(DebugPoint { - // point: proj, - // color: Color::FUCHSIA, - // }); - - // // Transform into the camera's local space - // world2cam.transform_point(proj) - // }) - // // Fold the camera-space plane-projected points into a bounding rect and view depth - // .fold(None, |accum: Option<(f32, Rect)>, p: Vec3| match accum { - // Some((depth, rect)) => Some((depth, rect.union_point(vec2(p.x, p.y)))), - // None => { - // let depth = p.z.abs(); - // let p = vec2(p.x, p.y); - // Some((depth, Rect::from_corners(p, p))) - // } - // }) - // else { - // return; - // }; - - // let pproj = match cam_proj { - // Projection::Perspective(p) => p, - // _ => panic!("Expected a perspective projection"), - // }; - - // // Formula for the target camera distance - // // (c/o https://stackoverflow.com/questions/2866350/move-camera-to-fit-3d-scene): - // // - // // d = (s/2) / tan(a/2) - // // where: - // // d = distance - // // s = size of the largest camera-aligned axis in world coords - // // a = field-of-view angle for the same axis - // let Vec2 { - // x: width, - // y: height, - // } = bounding_rect.size(); - - // let s = f32::max(width, height); - // let a = if s == height { - // pproj.fov - // } else { - // f32::atan(pproj.aspect_ratio * f32::tan(pproj.fov / 2.)) - // }; + let s = f32::max(width, height); + let a = if s == height { + pproj.fov + } else { + f32::atan(pproj.aspect_ratio * f32::tan(pproj.fov / 2.)) + }; // // TODO: // // According to "I eat babies" at the Ars Technica forums @@ -413,11 +359,11 @@ fn frame_scene_in_view( // // // // Unfortunately I'm embarrassingly bad at algebra, but there // // must be some room for simplification here. - // let radius = (s / 2.) / f32::tan(a / 2.); + let radius = (s / 2.) / f32::tan(a / 2.); - // cam.focus = aabb.center.into(); - // cam.radius = radius; - // cam_xform.translation = cam.focus + cam_xform.back() * radius; + cam.focus = aabb.center.into(); + cam.radius = radius; + cam_xform.translation = cam.focus + cam_xform.back() * radius; } #[cfg(feature = "wasm")]