mirror of
https://github.com/FyroxEngine/Fyrox.git
synced 2026-10-12 02:02:20 +00:00
preview for prefab instantiation
This commit is contained in:
+22
-13
@@ -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() {
|
||||
|
||||
@@ -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) = (
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) = (
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user