1use crate::{
21 asset::item::AssetItem,
22 audio::AudioBusSelection,
23 camera::{CameraController, PickingOptions},
24 command::{Command, CommandGroup, CommandStack},
25 fyrox::{
26 asset::manager::ResourceManager,
27 core::{
28 algebra::{Matrix4, Vector2, Vector3},
29 color::Color,
30 define_as_any_trait,
31 futures::executor::block_on,
32 log::Log,
33 make_relative_path,
34 math::{aabb::AxisAlignedBoundingBox, plane::Plane, Rect},
35 pool::{ErasedHandle, Handle},
36 reflect::Reflect,
37 visitor::Visitor,
38 Uuid,
39 },
40 engine::{Engine, SerializationContext},
41 fxhash::FxHashSet,
42 graph::{SceneGraph, SceneGraphNode},
43 gui::{
44 inspector::PropertyChanged,
45 message::UiMessage,
46 message::{KeyCode, MessageDirection, MouseButton},
47 UiNode,
48 },
49 material::{
50 shader::ShaderResource, shader::ShaderResourceExtension, Material, MaterialResource,
51 },
52 resource::{
53 model::{Model, ModelResourceExtension},
54 texture::{Texture, TextureKind, TextureResource, TextureResourceExtension},
55 },
56 scene::{
57 base::BaseBuilder,
58 camera::{Camera, Projection},
59 debug::{Line, SceneDrawingContext},
60 graph::{Graph, GraphUpdateSwitches},
61 light::{point::PointLight, spot::SpotLight},
62 mesh::RenderPath,
63 mesh::{
64 surface::{SurfaceBuilder, SurfaceData, SurfaceResource},
65 Mesh, MeshBuilder,
66 },
67 navmesh::NavigationalMesh,
68 node::Node,
69 pivot::PivotBuilder,
70 terrain::Terrain,
71 Scene, SceneContainer,
72 },
73 },
74 highlight::HighlightRenderPass,
75 interaction::navmesh::selection::NavmeshSelection,
76 message::MessageSender,
77 plugins::{
78 absm::selection::AbsmSelection,
79 animation::selection::AnimationSelection,
80 inspector::editors::handle::{
81 HandlePropertyEditorHierarchyMessage, HandlePropertyEditorNameMessage,
82 },
83 inspector::handlers::node::SceneNodePropertyChangedHandler,
84 },
85 scene::{
86 clipboard::Clipboard,
87 commands::{
88 graph::AddModelCommand, mesh::SetMeshTextureCommand, ChangeSelectionCommand,
89 GameSceneContext,
90 },
91 controller::SceneController,
92 selector::HierarchyNode,
93 },
94 settings::{keys::KeyBindings, SettingsMessage},
95 ui_scene::selection::UiSelection,
96 world::selection::GraphSelection,
97 Message, Settings,
98};
99use fyrox::engine::GraphicsContext;
100use fyrox::gui::file_browser::FileType;
101use fyrox::scene::collider::BitMask;
102use fyrox::scene::pivot::Pivot;
103use std::{
104 cell::RefCell,
105 fmt::Debug,
106 fs::File,
107 io::Write,
108 path::Path,
109 rc::Rc,
110 sync::{
111 mpsc::{self, Receiver},
112 Arc, LazyLock,
113 },
114};
115
116pub mod clipboard;
117pub mod dialog;
118mod nullscene;
119pub mod property;
120pub mod selector;
121pub mod settings;
122
123#[macro_use]
124pub mod commands;
125pub mod container;
126pub mod controller;
127
128pub struct PreviewInstance {
129 pub instance: Handle<Node>,
130 pub nodes: FxHashSet<Handle<Node>>,
131}
132
133pub struct GameScene {
134 pub scene: Handle<Scene>,
135 pub editor_objects_root: Handle<Pivot>,
137 pub scene_content_root: Handle<Node>,
138 pub clipboard: Clipboard,
139 pub camera_controller: CameraController,
140 pub preview_camera: Handle<Node>,
141 pub graph_switches: GraphUpdateSwitches,
142 pub preview_instance: Option<PreviewInstance>,
143 pub sender: MessageSender,
144 pub camera_state: Vec<(Handle<Node>, bool)>,
145 pub node_property_changed_handler: SceneNodePropertyChangedHandler,
146 pub highlighter: Option<Rc<RefCell<HighlightRenderPass>>>,
147 pub resource_manager: ResourceManager,
148 pub serialization_context: Arc<SerializationContext>,
149 pub grid: Handle<Mesh>,
150 pub grid_material: MaterialResource,
151 pub settings_receiver: Receiver<SettingsMessage>,
152}
153
154static GRID_SHADER: LazyLock<ShaderResource> = LazyLock::new(|| {
155 ShaderResource::from_str(
156 Uuid::new_v4(),
157 include_str!("../../resources/shaders/grid.shader"),
158 Default::default(),
159 )
160 .unwrap()
161});
162
163fn make_grid_material() -> MaterialResource {
164 let material = Material::from_shader(GRID_SHADER.clone());
165 MaterialResource::new_embedded(material)
166}
167
168impl GameScene {
169 pub const EDITOR_OBJECTS_MASK: BitMask = BitMask(0b1000_0000_0000_0000);
170
171 pub fn from_native_scene(
172 mut scene: Scene,
173 engine: &mut Engine,
174 path: Option<&Path>,
175 settings: &mut Settings,
176 sender: MessageSender,
177 highlighter: Option<Rc<RefCell<HighlightRenderPass>>>,
178 ) -> Self {
179 scene.rendering_options.render_target = Some(TextureResource::new_render_target(0, 0));
180
181 let scene_content_root = scene.graph.get_root();
182
183 scene
184 .graph
185 .change_root_node(PivotBuilder::new(BaseBuilder::new()).build_node());
186
187 let editor_objects_root = PivotBuilder::new(BaseBuilder::new()).build(&mut scene.graph);
188
189 let grid_material = make_grid_material();
190 let grid = MeshBuilder::new(
191 BaseBuilder::new()
192 .with_frustum_culling(false)
193 .with_visibility(settings.graphics.draw_grid),
194 )
195 .with_surfaces(vec![SurfaceBuilder::new(SurfaceResource::new_embedded(
196 SurfaceData::make_quad(&Matrix4::new_scaling(2.0)),
197 ))
198 .with_material(grid_material.clone())
199 .build()])
200 .with_render_path(RenderPath::Forward)
201 .build(&mut scene.graph);
202
203 let (settings_sender, settings_receiver) = mpsc::channel();
204 settings.subscribers.push(settings_sender);
205
206 let camera_controller = CameraController::new(
207 &mut scene.graph,
208 editor_objects_root,
209 settings,
210 path.as_ref()
211 .and_then(|p| {
212 settings
213 .scene_settings
214 .get(*p)
215 .map(|s| s.camera_settings.clone())
216 })
217 .unwrap_or_default(),
218 grid,
219 editor_objects_root,
220 scene_content_root,
221 );
222
223 GameScene {
224 editor_objects_root,
225 scene_content_root,
226 camera_controller,
227 preview_instance: None,
228 scene: engine.scenes.add(scene),
229 clipboard: Default::default(),
230 preview_camera: Default::default(),
231 graph_switches: GraphUpdateSwitches {
232 physics_dt: false,
234 physics2d: true,
235 physics: true,
236 delete_dead_nodes: false,
239 node_overrides: Some(Default::default()),
241 paused: false,
242 },
243 sender,
244 camera_state: Default::default(),
245 node_property_changed_handler: SceneNodePropertyChangedHandler,
246 highlighter,
247 resource_manager: engine.resource_manager.clone(),
248 serialization_context: engine.serialization_context.clone(),
249 grid,
250 grid_material,
251 settings_receiver,
252 }
253 }
254
255 pub fn make_purified_scene(&self, engine: &mut Engine) -> Scene {
256 let scene = &mut engine.scenes[self.scene];
257
258 let editor_root = self.editor_objects_root;
259 let (pure_scene, _) = scene.clone_ex(
260 self.scene_content_root,
261 true,
262 &mut |node, _| node != editor_root,
263 &mut |_, _| {},
264 &mut |_, _, _| {},
265 );
266
267 pure_scene
268 }
269
270 pub fn save(
271 &mut self,
272 path: &Path,
273 settings: &Settings,
274 engine: &mut Engine,
275 ) -> Result<String, String> {
276 let mut pure_scene = self.make_purified_scene(engine);
277
278 let mut visitor = Visitor::new();
279 pure_scene.save("Scene", &mut visitor).unwrap();
280
281 if let Err(e) = visitor.save_ascii_to_file(path) {
282 Err(format!(
283 "Failed to save scene {}! Reason: {e}",
284 path.display()
285 ))
286 } else {
287 if settings.debugging.save_scene_in_text_form {
288 let text = visitor.save_ascii_to_string();
289 let mut path = path.to_path_buf();
290 path.set_extension("txt");
291 if let Ok(mut file) = File::create(path) {
292 Log::verify(file.write_all(text.as_bytes()));
293 }
294 }
295
296 Ok(format!("Scene {} was successfully saved!", path.display()))
297 }
298 }
299
300 pub fn draw_auxiliary_geometry(
301 &mut self,
302 editor_selection: &Selection,
303 engine: &mut Engine,
304 settings: &Settings,
305 ) {
306 let debug_settings = &settings.debugging;
307 let scene = &mut engine.scenes[self.scene];
308
309 scene.drawing_context.clear_lines();
310
311 if let Some(selection) = editor_selection.as_graph() {
312 for &node in selection.nodes() {
313 if let Ok(node) = scene.graph.try_get_node(node) {
314 scene.drawing_context.draw_oob(
315 &node.local_bounding_box(),
316 node.global_transform(),
317 Color::GREEN,
318 );
319 }
320 }
321 }
322
323 if debug_settings.show_physics {
324 scene.graph.physics.draw(&mut scene.drawing_context);
325 scene.graph.physics2d.draw(&mut scene.drawing_context);
326 }
327
328 fn draw_recursively(
329 node: Handle<Node>,
330 graph: &Graph,
331 ctx: &mut SceneDrawingContext,
332 editor_selection: &Selection,
333 game_scene: &GameScene,
334 settings: &Settings,
335 ) {
336 if node == game_scene.editor_objects_root {
338 return;
339 }
340
341 let node = &graph[node];
342
343 if settings.debugging.show_bounds {
344 ctx.draw_oob(
345 &AxisAlignedBoundingBox::unit(),
346 node.global_transform(),
347 Color::opaque(255, 127, 39),
348 );
349 }
350
351 if node.cast::<Mesh>().is_some() {
352 if settings.debugging.show_tbn {
353 node.debug_draw(ctx);
354 }
355 } else if node.component_ref::<Camera>().is_some() {
356 if settings.debugging.show_camera_bounds {
357 node.debug_draw(ctx);
358 }
359 } else if node.component_ref::<PointLight>().is_some()
360 || node.component_ref::<SpotLight>().is_some()
361 {
362 if settings.debugging.show_light_bounds {
363 node.debug_draw(ctx);
364 }
365 } else if node.component_ref::<Terrain>().is_some() {
366 if settings.debugging.show_terrains {
367 node.debug_draw(ctx);
368 }
369 } else if let Some(navmesh) = node.component_ref::<NavigationalMesh>() {
370 if settings.navmesh.draw_all {
371 let selection = editor_selection.as_navmesh();
372
373 for (index, vertex) in navmesh.navmesh_ref().vertices().iter().enumerate() {
374 ctx.draw_sphere(
375 *vertex,
376 10,
377 10,
378 settings.navmesh.vertex_radius,
379 selection.map_or(Color::GREEN, |s| {
380 if s.unique_vertices().contains(&index) {
381 Color::RED
382 } else {
383 Color::GREEN
384 }
385 }),
386 );
387 }
388
389 for triangle in navmesh.navmesh_ref().triangles().iter() {
390 for edge in &triangle.edges() {
391 ctx.add_line(Line {
392 begin: navmesh.navmesh_ref().vertices()[edge.a as usize],
393 end: navmesh.navmesh_ref().vertices()[edge.b as usize],
394 color: selection.map_or(Color::GREEN, |s| {
395 if s.contains_edge(*edge) {
396 Color::RED
397 } else {
398 Color::GREEN
399 }
400 }),
401 });
402 }
403 }
404 }
405 } else {
406 node.debug_draw(ctx);
407 }
408
409 for &child in node.children() {
410 draw_recursively(child, graph, ctx, editor_selection, game_scene, settings)
411 }
412 }
413
414 draw_recursively(
416 self.scene_content_root,
417 &scene.graph,
418 &mut scene.drawing_context,
419 editor_selection,
420 self,
421 settings,
422 );
423 }
424
425 pub fn is_current_selection_has_external_refs(
427 &self,
428 editor_selection: &Selection,
429 graph: &Graph,
430 ) -> bool {
431 if let Some(selection) = editor_selection.as_graph() {
432 for node in selection.nodes() {
433 for (descendant_handle, _) in graph.traverse_iter(*node) {
434 for reference in graph.find_references_to(descendant_handle) {
435 if !selection.contains(reference) {
436 return true;
437 }
438 }
439 }
440 }
441 }
442 false
443 }
444
445 fn try_save_selection_as_prefab(&self, path: &Path, selection: &Selection, engine: &Engine) {
446 let source_scene = &engine.scenes[self.scene];
447 let mut dest_scene = Scene::new();
448 if let Some(graph_selection) = selection.as_graph() {
449 for root_node in graph_selection.root_nodes(&source_scene.graph) {
450 source_scene.graph.copy_node(
451 root_node,
452 &mut dest_scene.graph,
453 false,
454 &mut |_, _| true,
455 &mut |_, _| {},
456 &mut |_, _, _| {},
457 );
458 }
459
460 let mut visitor = Visitor::new();
461 match dest_scene.save("Scene", &mut visitor) {
462 Err(e) => Log::err(format!("Failed to save selection as prefab! Reason: {e:?}")),
463 Ok(_) => {
464 if let Err(e) = visitor.save_ascii_to_file(path) {
465 Log::err(format!("Failed to save selection as prefab! Reason: {e:?}"));
466 } else {
467 Log::info(format!(
468 "Selection was successfully saved as prefab to {path:?}!"
469 ))
470 }
471 }
472 }
473 } else {
474 Log::warn("Unable to selection to prefab, because selection is not scene selection!");
475 }
476 }
477
478 fn select_object(&mut self, handle: ErasedHandle, engine: &Engine) {
479 let handle = Handle::<Node>::from(handle);
480 if engine.scenes[self.scene].graph.is_valid_handle(handle) {
481 self.sender
482 .do_command(ChangeSelectionCommand::new(Selection::new(
483 GraphSelection::single_or_empty(handle),
484 )))
485 }
486 }
487}
488
489impl SceneController for GameScene {
490 fn on_key_up(
491 &mut self,
492 key: KeyCode,
493 _engine: &mut Engine,
494 key_bindings: &KeyBindings,
495 ) -> bool {
496 if self.camera_controller.on_key_up(key_bindings, key) {
497 return true;
498 }
499
500 false
501 }
502
503 fn on_key_down(
504 &mut self,
505 key: KeyCode,
506 _engine: &mut Engine,
507 key_bindings: &KeyBindings,
508 ) -> bool {
509 if self.camera_controller.on_key_down(key_bindings, key) {
510 return true;
511 }
512
513 false
514 }
515
516 fn on_mouse_move(
517 &mut self,
518 pos: Vector2<f32>,
519 offset: Vector2<f32>,
520 _screen_bounds: Rect<f32>,
521 engine: &mut Engine,
522 _settings: &Settings,
523 ) {
524 self.camera_controller.on_mouse_move(
525 &mut engine.scenes[self.scene].graph,
526 pos,
527 _screen_bounds.size,
528 offset,
529 _settings,
530 );
531 }
532
533 fn on_mouse_up(
534 &mut self,
535 button: MouseButton,
536 _pos: Vector2<f32>,
537 _screen_bounds: Rect<f32>,
538 engine: &mut Engine,
539 _settings: &Settings,
540 ) {
541 self.camera_controller
542 .on_mouse_button_up(button, &mut engine.scenes[self.scene].graph);
543 }
544
545 fn on_mouse_down(
546 &mut self,
547 button: MouseButton,
548 pos: Vector2<f32>,
549 _screen_bounds: Rect<f32>,
550 engine: &mut Engine,
551 _settings: &Settings,
552 ) {
553 self.camera_controller.on_mouse_button_down(
554 pos,
555 button,
556 engine.user_interfaces.first_mut().keyboard_modifiers(),
557 &mut engine.scenes[self.scene].graph,
558 );
559 }
560
561 fn on_mouse_wheel(&mut self, amount: f32, engine: &mut Engine, settings: &Settings) {
562 self.camera_controller.on_mouse_wheel(
563 amount * settings.camera.zoom_speed,
564 &mut engine.scenes[self.scene].graph,
565 settings,
566 );
567 }
568
569 fn on_mouse_leave(&mut self, engine: &mut Engine, _settings: &Settings) {
570 if let Some(preview) = self.preview_instance.take() {
571 let scene = &mut engine.scenes[self.scene];
572
573 scene.graph.remove_node(preview.instance);
574 }
575 }
576
577 fn on_drag_over(
578 &mut self,
579 handle: Handle<UiNode>,
580 screen_bounds: Rect<f32>,
581 engine: &mut Engine,
582 settings: &Settings,
583 ) {
584 match self.preview_instance.as_ref() {
585 None => {
586 if let Some(item) = engine
587 .user_interfaces
588 .first_mut()
589 .node(handle)
590 .cast::<AssetItem>()
591 {
592 if let Ok(relative_path) = make_relative_path(&item.path) {
595 if let Some(model) = engine
597 .resource_manager
598 .try_request::<Model>(relative_path)
599 .and_then(|m| block_on(m).ok())
600 {
601 let scene = &mut engine.scenes[self.scene];
602
603 let instance = model.instantiate(scene);
605
606 scene.graph.link_nodes(instance, self.scene_content_root);
607
608 scene.graph[instance]
609 .local_transform_mut()
610 .set_scale(settings.model.instantiation_scale);
611
612 let nodes = scene
613 .graph
614 .traverse_iter(instance)
615 .map(|(handle, _)| handle)
616 .collect::<FxHashSet<Handle<Node>>>();
617
618 self.preview_instance = Some(PreviewInstance { instance, nodes });
619 }
620 }
621 }
622 }
623 Some(preview) => {
624 let frame_size = screen_bounds.size;
625 let cursor_pos = engine.user_interfaces.first_mut().cursor_position();
626 let rel_pos = cursor_pos - screen_bounds.position;
627 let graph = &mut engine.scenes[self.scene].graph;
628
629 let position = if let Some(result) = self.camera_controller.pick(
630 graph,
631 PickingOptions {
632 cursor_pos: rel_pos,
633 editor_only: false,
634 filter: Some(&mut |handle, _| !preview.nodes.contains(&handle)),
635 ignore_back_faces: settings.selection.ignore_back_faces,
636 use_picking_loop: false,
638 method: Default::default(),
639 settings: &settings.selection,
640 },
641 ) {
642 Some(result.position)
643 } else {
644 let camera = &graph[self.camera_controller.camera];
646
647 let normal = match camera.projection() {
648 Projection::Perspective(_) => Vector3::new(0.0, 1.0, 0.0),
649 Projection::Orthographic(_) => Vector3::new(0.0, 0.0, 1.0),
650 };
651
652 let plane = Plane::from_normal_and_point(&normal, &Default::default())
653 .unwrap_or_default();
654
655 let ray = camera.make_ray(rel_pos, frame_size);
656
657 ray.plane_intersection_point(&plane)
658 };
659
660 if let Some(position) = position {
661 graph[preview.instance].local_transform_mut().set_position(
662 settings
663 .move_mode_settings
664 .try_snap_vector_to_grid(position),
665 );
666 }
667 }
668 }
669 }
670
671 fn on_drop(
672 &mut self,
673 handle: Handle<UiNode>,
674 screen_bounds: Rect<f32>,
675 engine: &mut Engine,
676 settings: &Settings,
677 ) {
678 if handle.is_none() {
679 return;
680 }
681
682 if let Some(item) = engine
683 .user_interfaces
684 .first_mut()
685 .node(handle)
686 .cast::<AssetItem>()
687 {
688 if let Ok(relative_path) = make_relative_path(&item.path) {
691 if let Some(preview) = self.preview_instance.take() {
692 let scene = &mut engine.scenes[self.scene];
693
694 let sub_graph = scene.graph.take_reserve_sub_graph(preview.instance);
697
698 let group = vec![
699 Command::new(AddModelCommand::new(sub_graph)),
700 Command::new(ChangeSelectionCommand::new(Selection::new(
702 GraphSelection::single_or_empty(preview.instance),
703 ))),
704 ];
705
706 self.sender.do_command(CommandGroup::from(group));
707 } else if let Some(tex) = engine
708 .resource_manager
709 .try_request::<Texture>(relative_path)
710 .and_then(|t| block_on(t).ok())
711 {
712 let cursor_pos = engine.user_interfaces.first_mut().cursor_position();
713 let rel_pos = cursor_pos - screen_bounds.position;
714 let graph = &engine.scenes[self.scene].graph;
715 if let Some(result) = self.camera_controller.pick(
716 graph,
717 PickingOptions {
718 cursor_pos: rel_pos,
719 editor_only: false,
720 filter: None,
721 ignore_back_faces: settings.selection.ignore_back_faces,
722 use_picking_loop: true,
723 method: Default::default(),
724 settings: &settings.selection,
725 },
726 ) {
727 let texture = tex.clone();
728 let mut texture = texture.state();
729 if texture.data().is_some() {
730 let node = &mut engine.scenes[self.scene].graph[result.node];
731
732 if node.is_mesh() {
733 self.sender
734 .do_command(SetMeshTextureCommand::new(result.node, tex));
735 }
736 }
737 }
738 }
739 }
740 }
741 }
742
743 fn render_target(&self, engine: &Engine) -> Option<TextureResource> {
744 engine.scenes[self.scene]
745 .rendering_options
746 .render_target
747 .clone()
748 }
749
750 fn file_type(&self) -> FileType {
751 FileType {
752 description: "Game Scene".to_string(),
753 extension: "rgs".to_string(),
754 }
755 }
756
757 fn save(
758 &mut self,
759 path: &Path,
760 settings: &Settings,
761 engine: &mut Engine,
762 ) -> Result<String, String> {
763 self.save(path, settings, engine)
764 }
765
766 fn do_command(
767 &mut self,
768 command_stack: &mut CommandStack,
769 command: Command,
770 selection: &mut Selection,
771 engine: &mut Engine,
772 ) {
773 GameSceneContext::exec(
774 selection,
775 &mut engine.scenes[self.scene],
776 &mut self.scene_content_root,
777 &mut self.clipboard,
778 self.sender.clone(),
779 engine.resource_manager.clone(),
780 engine.serialization_context.clone(),
781 |ctx| command_stack.do_command(command, ctx),
782 )
783 }
784
785 fn undo(
786 &mut self,
787 command_stack: &mut CommandStack,
788 selection: &mut Selection,
789 engine: &mut Engine,
790 ) {
791 GameSceneContext::exec(
792 selection,
793 &mut engine.scenes[self.scene],
794 &mut self.scene_content_root,
795 &mut self.clipboard,
796 self.sender.clone(),
797 engine.resource_manager.clone(),
798 engine.serialization_context.clone(),
799 |ctx| command_stack.undo(ctx),
800 );
801 }
802
803 fn redo(
804 &mut self,
805 command_stack: &mut CommandStack,
806 selection: &mut Selection,
807 engine: &mut Engine,
808 ) {
809 GameSceneContext::exec(
810 selection,
811 &mut engine.scenes[self.scene],
812 &mut self.scene_content_root,
813 &mut self.clipboard,
814 self.sender.clone(),
815 engine.resource_manager.clone(),
816 engine.serialization_context.clone(),
817 |ctx| command_stack.redo(ctx),
818 );
819 }
820
821 fn clear_command_stack(
822 &mut self,
823 command_stack: &mut CommandStack,
824 selection: &mut Selection,
825 scenes: &mut SceneContainer,
826 ) {
827 GameSceneContext::exec(
828 selection,
829 &mut scenes[self.scene],
830 &mut self.scene_content_root,
831 &mut self.clipboard,
832 self.sender.clone(),
833 self.resource_manager.clone(),
834 self.serialization_context.clone(),
835 |ctx| command_stack.clear(ctx),
836 );
837 }
838
839 fn on_before_render(&mut self, _editor_selection: &Selection, engine: &mut Engine) {
840 let scene = &mut engine.scenes[self.scene];
841
842 for handle in scene
844 .graph
845 .traverse_handle_iter(self.editor_objects_root)
846 .collect::<Vec<_>>()
847 {
848 scene.graph[handle]
849 .render_mask
850 .set_value_and_mark_modified(Self::EDITOR_OBJECTS_MASK);
851 }
852
853 for (handle, camera) in scene.graph.pair_iter_mut().filter_map(|(h, n)| {
857 if h == self.camera_controller.camera || h == self.preview_camera {
858 None
859 } else {
860 n.cast_mut::<Camera>().map(|c| (h, c))
861 }
862 }) {
863 self.camera_state.push((handle, camera.is_enabled()));
864 camera.set_enabled(false);
865 }
866 }
867
868 fn on_after_render(&mut self, engine: &mut Engine) {
869 for (handle, enabled) in self.camera_state.drain(..) {
871 engine.scenes[self.scene].graph[handle]
872 .as_camera_mut()
873 .set_enabled(enabled);
874 }
875 }
876
877 fn update(
878 &mut self,
879 editor_selection: &Selection,
880 engine: &mut Engine,
881 dt: f32,
882 path: Option<&Path>,
883 settings: &mut Settings,
884 screen_bounds: Rect<f32>,
885 ) -> Option<TextureResource> {
886 self.draw_auxiliary_geometry(editor_selection, engine, settings);
887
888 let scene = &mut engine.scenes[self.scene];
889
890 for message in self.settings_receiver.try_iter() {
891 match message {
892 SettingsMessage::Changed => {
893 scene.graph[self.grid].set_visibility(settings.graphics.draw_grid);
894 }
895 }
896 }
897
898 let mut new_render_target = None;
900 if let TextureKind::Rectangle { width, height } = scene
901 .rendering_options
902 .render_target
903 .clone()
904 .unwrap()
905 .data_ref()
906 .kind()
907 {
908 let frame_size = screen_bounds.size;
909 if width != frame_size.x as u32 || height != frame_size.y as u32 {
910 scene.rendering_options.render_target = Some(TextureResource::new_render_target(
911 frame_size.x as u32,
912 frame_size.y as u32,
913 ));
914 new_render_target.clone_from(&scene.rendering_options.render_target);
915
916 if let GraphicsContext::Initialized(ref graphics_context) = engine.graphics_context
917 {
918 if let Some(highlighter) = self.highlighter.as_ref() {
919 highlighter.borrow_mut().resize(
920 &*graphics_context.renderer.server,
921 frame_size.x as usize,
922 frame_size.y as usize,
923 );
924 }
925 }
926 }
927 }
928
929 let node_overrides = self.graph_switches.node_overrides.as_mut().unwrap();
930 for (handle, _) in scene.graph.traverse_iter(self.editor_objects_root) {
931 node_overrides.insert(handle);
932 }
933
934 let camera = &mut scene.graph[self.camera_controller.camera];
935
936 let projection = camera.projection_mut();
937 projection.set_z_near(settings.graphics.z_near);
938 projection.set_z_far(settings.graphics.z_far);
939
940 let mut grid_material = self.grid_material.data_ref();
941 grid_material.set_property(
942 "orientation",
943 match projection {
944 Projection::Perspective(_) => 0i32,
945 Projection::Orthographic(_) => 1i32,
946 },
947 );
948 grid_material.set_property("isPerspective", projection.is_perspective());
949
950 let scale = if settings.move_mode_settings.grid_snapping {
951 fn div_safe(a: f32, b: f32) -> f32 {
952 if b == 0.0 {
953 a
954 } else {
955 a / b
956 }
957 }
958
959 match projection {
960 Projection::Perspective(_) => Vector2::new(
961 div_safe(1.0, settings.move_mode_settings.x_snap_step),
962 div_safe(1.0, settings.move_mode_settings.z_snap_step),
963 ),
964 Projection::Orthographic(_) => Vector2::new(
965 div_safe(1.0, settings.move_mode_settings.x_snap_step),
966 div_safe(1.0, settings.move_mode_settings.y_snap_step),
967 ),
968 }
969 } else {
970 Vector2::repeat(1.0)
971 };
972
973 grid_material.set_property("scale", scale);
974
975 let grid_offset = projection.z_far() - projection.z_near();
976 scene.graph[self.grid]
977 .local_transform_mut()
978 .set_position(Vector3::new(0.0, 0.0, grid_offset));
979
980 self.camera_controller.update(
981 &mut scene.graph,
982 settings,
983 path,
984 self.editor_objects_root,
985 self.scene_content_root,
986 screen_bounds.size,
987 dt,
988 );
989
990 if let Some(highlighter) = self.highlighter.as_ref() {
991 let mut highlighter = highlighter.borrow_mut();
992 highlighter.nodes_to_highlight.clear();
993
994 highlighter.scene_handle = self.scene;
995 if let Some(selection) = editor_selection.as_graph() {
996 for &handle in selection.nodes() {
997 highlighter.nodes_to_highlight.insert(handle);
998 }
999 }
1000 }
1001
1002 new_render_target
1003 }
1004
1005 fn is_interacting(&self) -> bool {
1006 self.camera_controller.is_interacting()
1007 }
1008
1009 fn on_destroy(
1010 &mut self,
1011 command_stack: &mut CommandStack,
1012 engine: &mut Engine,
1013 selection: &mut Selection,
1014 ) {
1015 GameSceneContext::exec(
1016 selection,
1017 &mut engine.scenes[self.scene],
1018 &mut self.scene_content_root,
1019 &mut self.clipboard,
1020 self.sender.clone(),
1021 engine.resource_manager.clone(),
1022 engine.serialization_context.clone(),
1023 |ctx| command_stack.clear(ctx),
1024 );
1025
1026 engine.scenes.remove(self.scene);
1027 }
1028
1029 fn on_message(
1030 &mut self,
1031 message: &Message,
1032 selection: &Selection,
1033 engine: &mut Engine,
1034 ) -> bool {
1035 match message {
1036 Message::SaveSelectionAsPrefab(path) => {
1037 self.try_save_selection_as_prefab(path, selection, engine);
1038 false
1039 }
1040 Message::SetEditorCameraProjection(projection) => {
1041 self.camera_controller
1042 .set_projection(&mut engine.scenes[self.scene].graph, projection.clone());
1043
1044 false
1045 }
1046 Message::SelectObject { handle } => {
1047 self.select_object(*handle, engine);
1048 false
1049 }
1050 Message::FocusObject(handle) => {
1051 let scene = &mut engine.scenes[self.scene];
1052 self.camera_controller.fit_object(scene, *handle, Some(2.0));
1053 false
1054 }
1055 Message::SyncNodeHandleName { view, handle } => {
1056 let scene = &engine.scenes[self.scene];
1057 engine.user_interfaces.first_mut().send_message(
1058 UiMessage::with_data(HandlePropertyEditorNameMessage(
1059 scene
1060 .graph
1061 .try_get_node((*handle).into())
1062 .ok()
1063 .map(|n| n.name_owned()),
1064 ))
1065 .with_destination(*view)
1066 .with_direction(MessageDirection::ToWidget),
1067 );
1068 false
1069 }
1070 Message::ProvideSceneHierarchy { view } => {
1071 let scene = &engine.scenes[self.scene];
1072
1073 engine.user_interfaces.first_mut().send_message(
1074 UiMessage::with_data(HandlePropertyEditorHierarchyMessage(
1075 HierarchyNode::from_scene_node(
1076 self.scene_content_root,
1077 Handle::NONE,
1078 &scene.graph,
1079 ),
1080 ))
1081 .with_destination(*view)
1082 .with_direction(MessageDirection::ToWidget),
1083 );
1084
1085 false
1086 }
1087 _ => false,
1088 }
1089 }
1090
1091 fn command_names(
1092 &mut self,
1093 command_stack: &mut CommandStack,
1094 selection: &mut Selection,
1095 engine: &mut Engine,
1096 ) -> Vec<String> {
1097 command_stack
1098 .commands
1099 .iter_mut()
1100 .map(|c| {
1101 let mut name = String::new();
1102 GameSceneContext::exec(
1103 selection,
1104 &mut engine.scenes[self.scene],
1105 &mut self.scene_content_root,
1106 &mut self.clipboard,
1107 self.sender.clone(),
1108 engine.resource_manager.clone(),
1109 engine.serialization_context.clone(),
1110 |ctx| {
1111 name = c.name(ctx);
1112 },
1113 );
1114 name
1115 })
1116 .collect::<Vec<_>>()
1117 }
1118}
1119
1120define_as_any_trait!(SelectionContainerAsAny => SelectionContainer);
1121
1122pub trait BaseSelectionContainer: SelectionContainerAsAny + Debug {
1123 fn clone_boxed(&self) -> Box<dyn SelectionContainer>;
1124 fn eq_ref(&self, other: &dyn SelectionContainer) -> bool;
1125}
1126
1127impl<T> BaseSelectionContainer for T
1128where
1129 T: Clone + SelectionContainer + PartialEq + 'static,
1130{
1131 fn clone_boxed(&self) -> Box<dyn SelectionContainer> {
1132 Box::new(self.clone())
1133 }
1134
1135 fn eq_ref(&self, other: &dyn SelectionContainer) -> bool {
1136 if let Some(other) = other.downcast_ref::<T>() {
1137 self == other
1138 } else {
1139 false
1140 }
1141 }
1142}
1143
1144pub struct EntityInfo<'a> {
1145 pub entity: &'a dyn Reflect,
1146 pub has_inheritance_parent: bool,
1147 pub read_only: bool,
1148}
1149
1150impl<'a> EntityInfo<'a> {
1151 pub fn with_no_parent(entity: &'a dyn Reflect) -> Self {
1152 Self {
1153 entity,
1154 has_inheritance_parent: false,
1155 read_only: false,
1156 }
1157 }
1158}
1159
1160pub trait SelectionContainer: BaseSelectionContainer {
1161 fn len(&self) -> usize;
1162
1163 fn is_empty(&self) -> bool {
1164 self.len() == 0
1165 }
1166
1167 fn is_single_selection(&self) -> bool {
1168 self.len() == 1
1169 }
1170
1171 fn is_multi_selection(&self) -> bool {
1172 self.len() > 1
1173 }
1174
1175 fn first_selected_entity(
1176 &self,
1177 controller: &dyn SceneController,
1178 scenes: &SceneContainer,
1179 callback: &mut dyn FnMut(EntityInfo),
1180 );
1181
1182 fn on_property_changed(
1183 &mut self,
1184 controller: &mut dyn SceneController,
1185 args: &PropertyChanged,
1186 engine: &mut Engine,
1187 sender: &MessageSender,
1188 );
1189
1190 fn paste_property(&mut self, path: &str, value: &dyn Reflect, sender: &MessageSender);
1191
1192 fn provide_docs(&self, controller: &dyn SceneController, engine: &Engine) -> Option<String>;
1193}
1194
1195impl dyn SelectionContainer {
1196 fn downcast_ref<T: SelectionContainer>(&self) -> Option<&T> {
1197 self.as_any().downcast_ref()
1198 }
1199
1200 fn downcast_mut<T: SelectionContainer>(&mut self) -> Option<&mut T> {
1201 self.as_any_mut().downcast_mut()
1202 }
1203}
1204
1205#[derive(Debug, Default)]
1206pub struct Selection(pub Option<Box<dyn SelectionContainer>>);
1207
1208impl PartialEq for Selection {
1209 fn eq(&self, other: &Self) -> bool {
1210 if let (Some(this), Some(other)) = (self.0.as_ref(), other.0.as_ref()) {
1211 this.eq_ref(&**other)
1212 } else {
1213 matches!((&self.0, &other.0), (None, None))
1214 }
1215 }
1216}
1217
1218impl Clone for Selection {
1219 fn clone(&self) -> Self {
1220 match self.0.as_ref() {
1221 Some(inner) => Self(Some(inner.clone_boxed())),
1222 None => Self::default(),
1223 }
1224 }
1225}
1226
1227macro_rules! define_downcast {
1228 ($ty:ty, $as_ref:ident, $as_mut:ident, $is:ident) => {
1229 pub fn $as_ref(&self) -> Option<&$ty> {
1230 self.0.as_ref().and_then(|s| s.downcast_ref())
1231 }
1232 pub fn $as_mut(&mut self) -> Option<&mut $ty> {
1233 self.0.as_mut().and_then(|s| s.downcast_mut())
1234 }
1235 pub fn $is(&mut self) -> bool {
1236 self.$as_ref().is_some()
1237 }
1238 };
1239}
1240
1241impl Selection {
1242 pub fn new<T: SelectionContainer>(container: T) -> Self {
1243 Self(Some(Box::new(container)))
1244 }
1245
1246 pub fn new_empty() -> Self {
1247 Self::default()
1248 }
1249
1250 pub fn is_none(&self) -> bool {
1251 self.0.is_none()
1252 }
1253
1254 pub fn is_empty(&self) -> bool {
1255 self.len() == 0
1256 }
1257
1258 pub fn len(&self) -> usize {
1259 self.0.as_ref().map_or(0, |s| s.len())
1260 }
1261
1262 pub fn is_single_selection(&self) -> bool {
1263 self.0.as_ref().is_some_and(|s| s.is_single_selection())
1264 }
1265
1266 pub fn is_multi_selection(&self) -> bool {
1267 self.0.as_ref().is_some_and(|s| s.is_multi_selection())
1268 }
1269
1270 pub fn as_ref<N: SelectionContainer>(&self) -> Option<&N> {
1271 self.0.as_ref().and_then(|v| v.downcast_ref::<N>())
1272 }
1273
1274 pub fn as_mut<N: SelectionContainer>(&mut self) -> Option<&mut N> {
1275 self.0.as_mut().and_then(|v| v.downcast_mut::<N>())
1276 }
1277
1278 define_downcast!(GraphSelection, as_graph, as_graph_mut, is_graph);
1279
1280 define_downcast!(NavmeshSelection, as_navmesh, as_navmesh_mut, is_navmesh);
1281
1282 define_downcast!(
1283 AudioBusSelection,
1284 as_audio_bus,
1285 as_audio_bus_mut,
1286 is_audio_bus
1287 );
1288
1289 pub fn as_absm<N: Reflect>(&self) -> Option<&AbsmSelection<N>> {
1290 self.0.as_ref().and_then(|s| s.downcast_ref())
1291 }
1292 pub fn as_absm_mut<N: Reflect>(&mut self) -> Option<&mut AbsmSelection<N>> {
1293 self.0.as_mut().and_then(|s| s.downcast_mut())
1294 }
1295 pub fn is_absm<N: Reflect>(&mut self) -> bool {
1296 self.as_absm::<N>().is_some()
1297 }
1298
1299 pub fn as_animation<N: Reflect>(&self) -> Option<&AnimationSelection<N>> {
1300 self.0.as_ref().and_then(|s| s.downcast_ref())
1301 }
1302 pub fn as_animation_mut<N: Reflect>(&mut self) -> Option<&mut AnimationSelection<N>> {
1303 self.0.as_mut().and_then(|s| s.downcast_mut())
1304 }
1305 pub fn is_animation<N: Reflect>(&mut self) -> bool {
1306 self.as_animation::<N>().is_some()
1307 }
1308
1309 define_downcast!(UiSelection, as_ui, as_ui_mut, is_ui);
1310
1311 pub fn first_selected_entity(
1312 &self,
1313 controller: &dyn SceneController,
1314 scenes: &SceneContainer,
1315 callback: &mut dyn FnMut(EntityInfo),
1316 ) {
1317 if let Some(container) = self.0.as_ref() {
1318 container.first_selected_entity(controller, scenes, callback);
1319 }
1320 }
1321
1322 pub fn on_property_changed(
1323 &mut self,
1324 controller: &mut dyn SceneController,
1325 args: &PropertyChanged,
1326 engine: &mut Engine,
1327 sender: &MessageSender,
1328 ) {
1329 if let Some(container) = self.0.as_mut() {
1330 container.on_property_changed(controller, args, engine, sender);
1331 }
1332 }
1333
1334 pub fn paste_property(&mut self, path: &str, value: &dyn Reflect, sender: &MessageSender) {
1335 if let Some(container) = self.0.as_mut() {
1336 container.paste_property(path, value, sender);
1337 }
1338 }
1339
1340 pub fn provide_docs(
1341 &self,
1342 controller: &dyn SceneController,
1343 engine: &Engine,
1344 ) -> Option<String> {
1345 self.0
1346 .as_ref()
1347 .and_then(|c| c.provide_docs(controller, engine))
1348 }
1349}