preview for prefab instantiation

This commit is contained in:
Dmitry
2022-10-06 21:14:46 +03:00
parent 5271432e0c
commit bec4b7607f
6 changed files with 160 additions and 32 deletions
+22 -13
View File
@@ -75,6 +75,8 @@ where
pub editor_only: bool,
pub filter: F,
pub ignore_back_faces: bool,
pub use_picking_loop: bool,
pub only_meshes: bool,
}
impl CameraController {
@@ -393,6 +395,8 @@ impl CameraController {
editor_only,
mut filter,
ignore_back_faces,
use_picking_loop,
only_meshes,
} = options;
if let Some(camera) = graph[self.camera].cast::<Camera>() {
@@ -442,7 +446,7 @@ impl CameraController {
toi: closest_distance,
});
}
} else {
} else if !only_meshes {
// Hull-less objects (light sources, cameras, etc.) can still be selected
// by coarse intersection test results.
let da = points[0].metric_distance(&object_space_ray.origin);
@@ -466,24 +470,29 @@ impl CameraController {
.pick_list
.sort_by(|a, b| a.toi.partial_cmp(&b.toi).unwrap());
let mut hasher = DefaultHasher::new();
for result in context.pick_list.iter() {
result.node.hash(&mut hasher);
}
let selection_hash = hasher.finish();
if selection_hash == context.old_selection_hash && cursor_pos == context.old_cursor_pos
{
context.pick_index += 1;
if use_picking_loop {
let mut hasher = DefaultHasher::new();
for result in context.pick_list.iter() {
result.node.hash(&mut hasher);
}
let selection_hash = hasher.finish();
if selection_hash == context.old_selection_hash
&& cursor_pos == context.old_cursor_pos
{
context.pick_index += 1;
// Wrap picking loop.
if context.pick_index >= context.pick_list.len() {
// Wrap picking loop.
if context.pick_index >= context.pick_list.len() {
context.pick_index = 0;
}
} else {
// Select is different, start from beginning.
context.pick_index = 0;
}
context.old_selection_hash = selection_hash;
} else {
// Select is different, start from beginning.
context.pick_index = 0;
}
context.old_selection_hash = selection_hash;
context.old_cursor_pos = cursor_pos;
if !context.pick_list.is_empty() {
+4
View File
@@ -228,6 +228,8 @@ impl InteractionMode for MoveInteractionMode {
handle != camera && handle != camera_pivot && handle != self.move_gizmo.origin
},
ignore_back_faces: settings.selection.ignore_back_faces,
use_picking_loop: true,
only_meshes: false,
}) {
if let Some(plane_kind) = self.move_gizmo.handle_pick(result.node, graph) {
if let Selection::Graph(selection) = &editor_scene.selection {
@@ -300,6 +302,8 @@ impl InteractionMode for MoveInteractionMode {
editor_only: false,
filter: |_, _| true,
ignore_back_faces: settings.selection.ignore_back_faces,
use_picking_loop: true,
only_meshes: false,
})
.map(|result| {
if let (Selection::Graph(selection), true) = (
+2
View File
@@ -389,6 +389,8 @@ impl InteractionMode for EditNavmeshMode {
handle != camera && handle != camera_pivot && handle != gizmo_origin
},
ignore_back_faces: settings.selection.ignore_back_faces,
use_picking_loop: true,
only_meshes: false,
})
.map(|r| r.node)
.unwrap_or_default();
+4
View File
@@ -67,6 +67,8 @@ impl InteractionMode for RotateInteractionMode {
handle != camera && handle != camera_pivot && handle != self.rotation_gizmo.origin
},
ignore_back_faces: settings.selection.ignore_back_faces,
use_picking_loop: true,
only_meshes: false,
}) {
if self
.rotation_gizmo
@@ -131,6 +133,8 @@ impl InteractionMode for RotateInteractionMode {
editor_only: false,
filter: |_, _| true,
ignore_back_faces: settings.selection.ignore_back_faces,
use_picking_loop: true,
only_meshes: false,
})
.map(|result| {
if let (Selection::Graph(selection), true) = (
+4
View File
@@ -66,6 +66,8 @@ impl InteractionMode for ScaleInteractionMode {
editor_only: true,
filter: |handle, _| handle != camera && handle != camera_pivot,
ignore_back_faces: settings.selection.ignore_back_faces,
use_picking_loop: true,
only_meshes: false,
}) {
if self
.scale_gizmo
@@ -127,6 +129,8 @@ impl InteractionMode for ScaleInteractionMode {
editor_only: false,
filter: |_, _| true,
ignore_back_faces: settings.selection.ignore_back_faces,
use_picking_loop: true,
only_meshes: false,
})
.map(|result| {
if let (Selection::Graph(selection), true) = (
+124 -19
View File
@@ -11,10 +11,11 @@ use fyrox::{
algebra::{Vector2, Vector3},
color::Color,
make_relative_path,
math::Rect,
math::{plane::Plane, Rect},
pool::Handle,
},
engine::Engine,
fxhash::FxHashSet,
gui::{
border::BorderBuilder,
brush::{Brush, GradientPoint},
@@ -36,12 +37,23 @@ use fyrox::{
BRUSH_BRIGHT_BLUE, BRUSH_LIGHT, BRUSH_LIGHTER, BRUSH_LIGHTEST, COLOR_DARKEST,
COLOR_LIGHTEST,
},
resource::texture::{Texture, TextureState},
scene::camera::Projection,
resource::{
model::ModelInstance,
texture::{Texture, TextureState},
},
scene::{
camera::{Camera, Projection},
node::Node,
},
utils::into_gui_texture,
};
use std::sync::mpsc::Sender;
struct PreviewInstance {
instance: ModelInstance,
nodes: FxHashSet<Handle<Node>>,
}
pub struct SceneViewer {
frame: Handle<UiNode>,
window: Handle<UiNode>,
@@ -62,6 +74,7 @@ pub struct SceneViewer {
interaction_mode_panel: Handle<UiNode>,
contextual_actions: Handle<UiNode>,
global_position_display: Handle<UiNode>,
preview_instance: Option<PreviewInstance>,
}
fn make_interaction_mode_button(
@@ -403,6 +416,7 @@ impl SceneViewer {
contextual_actions,
global_position_display,
build_profile,
preview_instance: None,
}
}
}
@@ -571,6 +585,103 @@ impl SceneViewer {
message.set_handled(true);
}
}
WidgetMessage::MouseLeave => {
if let Some(preview) = self.preview_instance.take() {
let scene = &mut engine.scenes[editor_scene.scene];
scene.graph.remove_node(preview.instance.root);
for animation in preview.instance.animations {
scene.animations.remove(animation);
}
}
}
WidgetMessage::DragOver(handle) => {
match self.preview_instance.as_ref() {
None => {
if let Some(item) =
engine.user_interface.node(handle).cast::<AssetItem>()
{
// Make sure all resources loaded with relative paths only.
// This will make scenes portable.
if let Ok(relative_path) = make_relative_path(&item.path) {
if let AssetKind::Model = item.kind {
// No model was loaded yet, do it.
if let Ok(model) =
fyrox::core::futures::executor::block_on(
engine
.resource_manager
.request_model(&relative_path),
)
{
let scene = &mut engine.scenes[editor_scene.scene];
// Instantiate the model.
let instance = model.instantiate(scene);
for &animation in instance.animations.iter() {
scene.animations[animation].set_enabled(true);
}
let nodes = scene
.graph
.traverse_handle_iter(instance.root)
.collect::<FxHashSet<Handle<Node>>>();
self.preview_instance =
Some(PreviewInstance { instance, nodes });
}
}
}
}
}
Some(preview) => {
let screen_bounds = self.frame_bounds(&engine.user_interface);
let frame_size = screen_bounds.size;
let cursor_pos = engine.user_interface.cursor_position();
let rel_pos = cursor_pos - screen_bounds.position;
let graph = &mut engine.scenes[editor_scene.scene].graph;
if let Some(result) =
editor_scene.camera_controller.pick(PickingOptions {
cursor_pos: rel_pos,
graph,
editor_objects_root: editor_scene.editor_objects_root,
screen_size: frame_size,
editor_only: false,
filter: |handle, _| !preview.nodes.contains(&handle),
ignore_back_faces: settings.selection.ignore_back_faces,
// We need info only about closest intersection.
use_picking_loop: false,
only_meshes: false,
})
{
graph[preview.instance.root]
.local_transform_mut()
.set_position(result.position);
} else {
// In case of empty space, check intersection with oXZ plane.
let plane = Plane::from_normal_and_point(
&Vector3::new(0.0, 1.0, 0.0),
&Default::default(),
)
.unwrap_or_default();
if let Some(camera) = graph
[editor_scene.camera_controller.camera]
.cast::<Camera>()
{
let ray = camera.make_ray(rel_pos, frame_size);
if let Some(point) = ray.plane_intersection_point(&plane) {
graph[preview.instance.root]
.local_transform_mut()
.set_position(point);
}
}
}
}
}
}
WidgetMessage::Drop(handle) => {
self.on_drop(handle, engine, editor_scene, settings)
}
@@ -771,7 +882,7 @@ impl SceneViewer {
}
fn on_drop(
&self,
&mut self,
handle: Handle<UiNode>,
engine: &mut Engine,
editor_scene: &mut EditorScene,
@@ -790,23 +901,15 @@ impl SceneViewer {
if let Ok(relative_path) = make_relative_path(&item.path) {
match item.kind {
AssetKind::Model => {
// No model was loaded yet, do it.
if let Ok(model) = fyrox::core::futures::executor::block_on(
engine.resource_manager.request_model(&item.path),
) {
if let Some(preview) = self.preview_instance.take() {
let scene = &mut engine.scenes[editor_scene.scene];
// Instantiate the model.
let instance = model.instantiate(scene);
// Enable instantiated animations.
for &animation in instance.animations.iter() {
scene.animations[animation].set_enabled(true);
}
// Immediately after extract if from the scene to subgraph. This is required to not violate
// the rule of one place of execution, only commands allowed to modify the scene.
let sub_graph = scene.graph.take_reserve_sub_graph(instance.root);
let animations_container = instance
let sub_graph =
scene.graph.take_reserve_sub_graph(preview.instance.root);
let animations_container = preview
.instance
.animations
.iter()
.map(|&anim| scene.animations.take_reserve(anim))
@@ -820,12 +923,12 @@ impl SceneViewer {
// We also want to select newly instantiated model.
SceneCommand::new(ChangeSelectionCommand::new(
Selection::Graph(GraphSelection::single_or_empty(
instance.root,
preview.instance.root,
)),
editor_scene.selection.clone(),
)),
SceneCommand::new(ScaleNodeCommand::new(
instance.root,
preview.instance.root,
Vector3::new(1.0, 1.0, 1.0),
settings.model.instantiation_scale,
)),
@@ -848,6 +951,8 @@ impl SceneViewer {
editor_only: false,
filter: |_, _| true,
ignore_back_faces: settings.selection.ignore_back_faces,
use_picking_loop: true,
only_meshes: false,
}) {
let tex = engine.resource_manager.request_texture(&relative_path);
let texture = tex.clone();