Skip to main content

fyroxed_base/scene/
mod.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20use 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    // Handle to a root for all editor nodes.
136    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                // Freeze physics simulation while editing scene by setting time step to zero.
233                physics_dt: false,
234                physics2d: true,
235                physics: true,
236                // Prevent engine to update lifetime of the nodes and to delete "dead" nodes. Otherwise
237                // the editor will crash if some node is "dead".
238                delete_dead_nodes: false,
239                // Update only editor's camera.
240                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            // Ignore editor nodes.
337            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 pivots.
415        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    /// Checks whether the current graph selection has references to the nodes outside of the selection.
426    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                    // Make sure all resources loaded with relative paths only.
593                    // This will make scenes portable.
594                    if let Ok(relative_path) = make_relative_path(&item.path) {
595                        // No model was loaded yet, do it.
596                        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                            // Instantiate the model.
604                            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                        // We need info only about closest intersection.
637                        use_picking_loop: false,
638                        method: Default::default(),
639                        settings: &settings.selection,
640                    },
641                ) {
642                    Some(result.position)
643                } else {
644                    // In case of empty space, check intersection with oXZ plane (3D) or oXY (2D).
645                    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            // Make sure all resources loaded with relative paths only.
689            // This will make scenes portable.
690            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                    // Immediately after extract if from the scene to subgraph. This is required to not violate
695                    // the rule of one place of execution, only commands allowed to modify the scene.
696                    let sub_graph = scene.graph.take_reserve_sub_graph(preview.instance);
697
698                    let group = vec![
699                        Command::new(AddModelCommand::new(sub_graph)),
700                        // We also want to select newly instantiated model.
701                        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        // Apply render mask to all editor objects.
843        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        // Temporarily disable cameras in currently edited scene. This is needed to prevent any
854        // scene camera to interfere with the editor camera.
855
856        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        // Revert state of the cameras.
870        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        // Create new render target if preview frame has changed its size.
899        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}