use crate::{
camera::PickingOptions, gui::make_dropdown_list_option,
gui::make_dropdown_list_option_with_height, load_image, settings::keys::KeyBindings,
utils::enable_widget, AddModelCommand, AssetItem, AssetKind, BuildProfile,
ChangeSelectionCommand, CommandGroup, DropdownListBuilder, EditorScene, GameEngine,
GraphSelection, InteractionMode, InteractionModeKind, Message, Mode, SceneCommand, Selection,
SetMeshTextureCommand, Settings,
};
use fyrox::{
core::{
algebra::{Vector2, Vector3},
color::Color,
make_relative_path,
math::{plane::Plane, Rect},
pool::Handle,
},
engine::Engine,
fxhash::FxHashSet,
gui::{
border::BorderBuilder,
brush::{Brush, GradientPoint},
button::{Button, ButtonBuilder, ButtonContent, ButtonMessage},
canvas::CanvasBuilder,
decorator::{DecoratorBuilder, DecoratorMessage},
dropdown_list::DropdownListMessage,
grid::{Column, GridBuilder, Row},
image::{ImageBuilder, ImageMessage},
message::{KeyCode, MessageDirection, MouseButton, UiMessage},
stack_panel::StackPanelBuilder,
utils::make_simple_tooltip,
vec::vec3::{Vec3EditorBuilder, Vec3EditorMessage},
widget::{WidgetBuilder, WidgetMessage},
window::{WindowBuilder, WindowMessage, WindowTitle},
BuildContext, HorizontalAlignment, Orientation, Thickness, UiNode, UserInterface,
BRUSH_BRIGHT_BLUE, BRUSH_LIGHT, BRUSH_LIGHTER, BRUSH_LIGHTEST, COLOR_DARKEST,
COLOR_LIGHTEST,
},
resource::texture::{Texture, TextureState},
scene::{
camera::{Camera, Projection},
node::Node,
},
utils::into_gui_texture,
};
use std::sync::mpsc::Sender;
struct PreviewInstance {
instance: Handle<Node>,
nodes: FxHashSet<Handle<Node>>,
}
pub struct SceneViewer {
frame: Handle<UiNode>,
window: Handle<UiNode>,
pub last_mouse_pos: Option<Vector2<f32>>,
pub click_mouse_pos: Option<Vector2<f32>>,
selection_frame: Handle<UiNode>,
select_mode: Handle<UiNode>,
move_mode: Handle<UiNode>,
rotate_mode: Handle<UiNode>,
scale_mode: Handle<UiNode>,
navmesh_mode: Handle<UiNode>,
terrain_mode: Handle<UiNode>,
camera_projection: Handle<UiNode>,
switch_mode: Handle<UiNode>,
build_profile: Handle<UiNode>,
sender: Sender<Message>,
interaction_mode_panel: Handle<UiNode>,
contextual_actions: Handle<UiNode>,
global_position_display: Handle<UiNode>,
preview_instance: Option<PreviewInstance>,
}
fn make_interaction_mode_button(
ctx: &mut BuildContext,
image: &[u8],
tooltip: &str,
selected: bool,
) -> Handle<UiNode> {
ButtonBuilder::new(
WidgetBuilder::new()
.with_tooltip(make_simple_tooltip(ctx, tooltip))
.with_margin(Thickness::uniform(1.0)),
)
.with_back(
DecoratorBuilder::new(
BorderBuilder::new(WidgetBuilder::new().with_foreground(Brush::LinearGradient {
from: Vector2::new(0.5, 0.0),
to: Vector2::new(0.5, 1.0),
stops: vec![
GradientPoint {
stop: 0.0,
color: COLOR_LIGHTEST,
},
GradientPoint {
stop: 0.25,
color: COLOR_LIGHTEST,
},
GradientPoint {
stop: 1.0,
color: COLOR_DARKEST,
},
],
}))
.with_stroke_thickness(Thickness::uniform(1.0)),
)
.with_normal_brush(BRUSH_LIGHT)
.with_hover_brush(BRUSH_LIGHTER)
.with_pressed_brush(BRUSH_LIGHTEST)
.with_selected_brush(BRUSH_BRIGHT_BLUE)
.with_selected(selected)
.build(ctx),
)
.with_content(
ImageBuilder::new(
WidgetBuilder::new()
.with_margin(Thickness::uniform(1.0))
.with_width(32.0)
.with_height(32.0),
)
.with_opt_texture(load_image(image))
.build(ctx),
)
.build(ctx)
}
impl SceneViewer {
pub fn new(engine: &mut GameEngine, sender: Sender<Message>) -> Self {
let ctx = &mut engine.user_interface.build_ctx();
let select_mode_tooltip = "Select Object(s) - Shortcut: [1]\n\nSelection interaction mode \
allows you to select an object by a single left mouse button click or multiple objects using either \
frame selection (click and drag) or by holding Ctrl+Click";
let move_mode_tooltip =
"Move Object(s) - Shortcut: [2]\n\nMovement interaction mode allows you to move selected \
objects. Keep in mind that movement always works in local coordinates!\n\n\
This also allows you to select an object or add an object to current selection using Ctrl+Click";
let rotate_mode_tooltip =
"Rotate Object(s) - Shortcut: [3]\n\nRotation interaction mode allows you to rotate selected \
objects. Keep in mind that rotation always works in local coordinates!\n\n\
This also allows you to select an object or add an object to current selection using Ctrl+Click";
let scale_mode_tooltip =
"Scale Object(s) - Shortcut: [4]\n\nScaling interaction mode allows you to scale selected \
objects. Keep in mind that scaling always works in local coordinates!\n\n\
This also allows you to select an object or add an object to current selection using Ctrl+Click";
let navmesh_mode_tooltip =
"Edit Navmesh\n\nNavmesh edit mode allows you to modify selected \
navigational mesh.";
let terrain_mode_tooltip =
"Edit Terrain\n\nTerrain edit mode allows you to modify selected \
terrain.";
let frame;
let select_mode;
let move_mode;
let rotate_mode;
let scale_mode;
let navmesh_mode;
let terrain_mode;
let selection_frame;
let camera_projection;
let switch_mode;
let build_profile;
let interaction_mode_panel = StackPanelBuilder::new(
WidgetBuilder::new()
.with_margin(Thickness::uniform(1.0))
.on_row(0)
.on_column(0)
.with_child({
select_mode = make_interaction_mode_button(
ctx,
include_bytes!("../resources/embed/select.png"),
select_mode_tooltip,
true,
);
select_mode
})
.with_child({
move_mode = make_interaction_mode_button(
ctx,
include_bytes!("../resources/embed/move_arrow.png"),
move_mode_tooltip,
false,
);
move_mode
})
.with_child({
rotate_mode = make_interaction_mode_button(
ctx,
include_bytes!("../resources/embed/rotate_arrow.png"),
rotate_mode_tooltip,
false,
);
rotate_mode
})
.with_child({
scale_mode = make_interaction_mode_button(
ctx,
include_bytes!("../resources/embed/scale_arrow.png"),
scale_mode_tooltip,
false,
);
scale_mode
})
.with_child({
navmesh_mode = make_interaction_mode_button(
ctx,
include_bytes!("../resources/embed/navmesh.png"),
navmesh_mode_tooltip,
false,
);
navmesh_mode
})
.with_child({
terrain_mode = make_interaction_mode_button(
ctx,
include_bytes!("../resources/embed/terrain.png"),
terrain_mode_tooltip,
false,
);
terrain_mode
}),
)
.build(ctx);
let contextual_actions = StackPanelBuilder::new(
WidgetBuilder::new()
.on_column(1)
.with_horizontal_alignment(HorizontalAlignment::Right)
.with_child({
camera_projection = DropdownListBuilder::new(
WidgetBuilder::new()
.with_margin(Thickness::uniform(1.0))
.with_width(150.0),
)
.with_items(vec![
make_dropdown_list_option_with_height(ctx, "Perspective (3D)", 22.0),
make_dropdown_list_option_with_height(ctx, "Orthographic (2D)", 22.0),
])
.with_selected(0)
.build(ctx);
camera_projection
}),
)
.with_orientation(Orientation::Horizontal)
.build(ctx);
let top_ribbon = GridBuilder::new(
WidgetBuilder::new()
.with_child({
StackPanelBuilder::new(
WidgetBuilder::new()
.with_horizontal_alignment(HorizontalAlignment::Right)
.with_child({
switch_mode = ButtonBuilder::new(
WidgetBuilder::new()
.with_margin(Thickness::uniform(1.0))
.with_width(100.0),
)
.with_text("Play")
.build(ctx);
switch_mode
})
.with_child({
build_profile = DropdownListBuilder::new(
WidgetBuilder::new()
.with_margin(Thickness::uniform(1.0))
.with_width(100.0),
)
.with_items(vec![
make_dropdown_list_option(ctx, "Debug"),
make_dropdown_list_option(ctx, "Release"),
])
.with_selected(0)
.build(ctx);
build_profile
}),
)
.with_orientation(Orientation::Horizontal)
.build(ctx)
})
.with_child(contextual_actions),
)
.add_column(Column::stretch())
.add_column(Column::auto())
.add_row(Row::stretch())
.build(ctx);
let global_position_display;
let bottom_toolbar = StackPanelBuilder::new(
WidgetBuilder::new()
.with_horizontal_alignment(HorizontalAlignment::Right)
.with_margin(Thickness::uniform(1.0))
.with_child({
global_position_display = Vec3EditorBuilder::<f32>::new(
WidgetBuilder::new()
.with_tooltip(make_simple_tooltip(
ctx,
"Global Coordinates of the Current Selection",
))
.with_width(200.0),
)
.with_editable(false)
.build(ctx);
global_position_display
})
.on_row(2),
)
.with_orientation(Orientation::Horizontal)
.build(ctx);
let window = WindowBuilder::new(WidgetBuilder::new())
.can_close(false)
.can_minimize(false)
.with_content(
GridBuilder::new(
WidgetBuilder::new()
.on_row(0)
.with_child(top_ribbon)
.with_child(
GridBuilder::new(
WidgetBuilder::new()
.on_row(1)
.with_child({
frame = ImageBuilder::new(
WidgetBuilder::new()
.on_row(0)
.on_column(1)
.with_allow_drop(true),
)
.with_flip(true)
.build(ctx);
frame
})
.with_child(
CanvasBuilder::new(
WidgetBuilder::new().on_column(1).with_child({
selection_frame = BorderBuilder::new(
WidgetBuilder::new()
.with_visibility(false)
.with_background(Brush::Solid(
Color::from_rgba(255, 255, 255, 40),
))
.with_foreground(Brush::Solid(
Color::opaque(0, 255, 0),
)),
)
.with_stroke_thickness(Thickness::uniform(1.0))
.build(ctx);
selection_frame
}),
)
.build(ctx),
)
.with_child(interaction_mode_panel),
)
.add_row(Row::stretch())
.add_column(Column::auto())
.add_column(Column::stretch())
.build(ctx),
)
.with_child(bottom_toolbar),
)
.add_row(Row::strict(25.0))
.add_row(Row::stretch())
.add_row(Row::strict(25.0))
.add_column(Column::stretch())
.build(ctx),
)
.with_title(WindowTitle::text("Scene Preview"))
.build(ctx);
Self {
sender,
window,
frame,
last_mouse_pos: None,
move_mode,
rotate_mode,
scale_mode,
selection_frame,
select_mode,
navmesh_mode,
terrain_mode,
camera_projection,
click_mouse_pos: None,
switch_mode,
interaction_mode_panel,
contextual_actions,
global_position_display,
build_profile,
preview_instance: None,
}
}
}
impl SceneViewer {
pub fn window(&self) -> Handle<UiNode> {
self.window
}
pub fn frame(&self) -> Handle<UiNode> {
self.frame
}
pub fn selection_frame(&self) -> Handle<UiNode> {
self.selection_frame
}
pub fn handle_message(&mut self, message: &Message, engine: &mut Engine) {
if let Message::SetInteractionMode(mode) = message {
let active_button = match mode {
InteractionModeKind::Select => self.select_mode,
InteractionModeKind::Move => self.move_mode,
InteractionModeKind::Scale => self.scale_mode,
InteractionModeKind::Rotate => self.rotate_mode,
InteractionModeKind::Navmesh => self.navmesh_mode,
InteractionModeKind::Terrain => self.terrain_mode,
};
for mode_button in [
self.select_mode,
self.move_mode,
self.scale_mode,
self.rotate_mode,
self.navmesh_mode,
self.terrain_mode,
] {
let decorator = engine
.user_interface
.node(mode_button)
.query_component::<Button>()
.unwrap()
.decorator;
engine.user_interface.send_message(DecoratorMessage::select(
decorator,
MessageDirection::ToWidget,
mode_button == active_button,
));
}
}
}
pub fn handle_ui_message(
&mut self,
message: &mut UiMessage,
engine: &mut Engine,
editor_scene: Option<&mut EditorScene>,
interaction_mode: Option<&mut Box<dyn InteractionMode>>,
settings: &Settings,
mode: &Mode,
) {
let ui = &engine.user_interface;
if let Some(ButtonMessage::Click) = message.data::<ButtonMessage>() {
if message.destination() == self.scale_mode {
self.sender
.send(Message::SetInteractionMode(InteractionModeKind::Scale))
.unwrap();
} else if message.destination() == self.rotate_mode {
self.sender
.send(Message::SetInteractionMode(InteractionModeKind::Rotate))
.unwrap();
} else if message.destination() == self.move_mode {
self.sender
.send(Message::SetInteractionMode(InteractionModeKind::Move))
.unwrap();
} else if message.destination() == self.select_mode {
self.sender
.send(Message::SetInteractionMode(InteractionModeKind::Select))
.unwrap();
} else if message.destination() == self.navmesh_mode {
self.sender
.send(Message::SetInteractionMode(InteractionModeKind::Navmesh))
.unwrap();
} else if message.destination() == self.terrain_mode {
self.sender
.send(Message::SetInteractionMode(InteractionModeKind::Terrain))
.unwrap();
} else if message.destination() == self.switch_mode {
self.sender.send(Message::SwitchMode).unwrap();
}
} else if let Some(WidgetMessage::MouseDown { button, .. }) =
message.data::<WidgetMessage>()
{
if ui.is_node_child_of(message.destination(), self.move_mode)
&& *button == MouseButton::Right
{
self.sender.send(Message::OpenSettings).unwrap();
}
} else if let Some(DropdownListMessage::SelectionChanged(Some(index))) = message.data() {
if message.direction == MessageDirection::FromWidget {
if message.destination() == self.camera_projection {
if *index == 0 {
self.sender
.send(Message::SetEditorCameraProjection(Projection::Perspective(
Default::default(),
)))
.unwrap()
} else {
self.sender
.send(Message::SetEditorCameraProjection(
Projection::Orthographic(Default::default()),
))
.unwrap()
}
} else if message.destination() == self.build_profile {
if *index == 0 {
self.sender
.send(Message::SetBuildProfile(BuildProfile::Debug))
.unwrap();
} else {
self.sender
.send(Message::SetBuildProfile(BuildProfile::Release))
.unwrap();
}
}
}
}
if let (Some(editor_scene), Some(msg), Mode::Edit) =
(editor_scene, message.data::<WidgetMessage>(), mode)
{
if message.destination() == self.frame() {
match *msg {
WidgetMessage::MouseDown { button, pos, .. } => self.on_mouse_down(
button,
pos,
editor_scene,
interaction_mode,
engine,
settings,
),
WidgetMessage::MouseUp { button, pos, .. } => self.on_mouse_up(
button,
pos,
editor_scene,
interaction_mode,
engine,
settings,
),
WidgetMessage::MouseWheel { amount, .. } => {
editor_scene
.camera_controller
.on_mouse_wheel(amount, &mut engine.scenes[editor_scene.scene].graph);
}
WidgetMessage::MouseMove { pos, .. } => {
self.on_mouse_move(pos, editor_scene, interaction_mode, engine, settings)
}
WidgetMessage::KeyUp(key) => {
if self.on_key_up(
key,
editor_scene,
interaction_mode,
engine,
&settings.key_bindings,
) {
message.set_handled(true);
}
}
WidgetMessage::KeyDown(key) => {
if self.on_key_down(
key,
editor_scene,
interaction_mode,
engine,
&settings.key_bindings,
) {
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);
}
}
WidgetMessage::DragOver(handle) => {
match self.preview_instance.as_ref() {
None => {
if let Some(item) =
engine.user_interface.node(handle).cast::<AssetItem>()
{
if let Ok(relative_path) = make_relative_path(&item.path) {
if let AssetKind::Model = item.kind {
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];
let instance = model.instantiate(scene);
scene.graph[instance]
.local_transform_mut()
.set_scale(settings.model.instantiation_scale);
let nodes = scene
.graph
.traverse_handle_iter(instance)
.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;
let position = 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,
use_picking_loop: false,
only_meshes: false,
}) {
Some(result.position)
} else {
let camera = graph[editor_scene.camera_controller.camera]
.query_component_ref::<Camera>()
.unwrap();
let normal = match camera.projection() {
Projection::Perspective(_) => Vector3::new(0.0, 1.0, 0.0),
Projection::Orthographic(_) => Vector3::new(0.0, 0.0, 1.0),
};
let plane =
Plane::from_normal_and_point(&normal, &Default::default())
.unwrap_or_default();
let ray = camera.make_ray(rel_pos, frame_size);
ray.plane_intersection_point(&plane)
};
if let Some(position) = position {
graph[preview.instance].local_transform_mut().set_position(
settings
.move_mode_settings
.try_snap_vector_to_grid(position),
);
}
}
}
}
WidgetMessage::Drop(handle) => {
self.on_drop(handle, engine, editor_scene, settings)
}
_ => {}
}
}
}
}
pub fn sync_to_model(&self, editor_scene: &EditorScene, engine: &Engine) {
if let Selection::Graph(ref selection) = editor_scene.selection {
let scene = &engine.scenes[editor_scene.scene];
if let Some((_, position)) = selection.global_rotation_position(&scene.graph) {
engine.user_interface.send_message(Vec3EditorMessage::value(
self.global_position_display,
MessageDirection::ToWidget,
position,
));
}
}
}
pub fn on_mode_changed(&self, ui: &UserInterface, mode: &Mode) {
let enabled = mode.is_edit();
ui.send_message(ButtonMessage::content(
self.switch_mode,
MessageDirection::ToWidget,
ButtonContent::text(if enabled { "Play" } else { "Stop" }),
));
for widget in [self.interaction_mode_panel, self.contextual_actions] {
enable_widget(widget, enabled, ui);
}
}
pub fn set_render_target(&self, ui: &UserInterface, render_target: Option<Texture>) {
ui.send_message(ImageMessage::texture(
self.frame,
MessageDirection::ToWidget,
render_target.map(into_gui_texture),
));
}
pub fn set_title(&self, ui: &UserInterface, title: String) {
ui.send_message(WindowMessage::title(
self.window,
MessageDirection::ToWidget,
WindowTitle::Text(title),
));
}
pub fn reset_camera_projection(&self, ui: &UserInterface) {
ui.send_message(DropdownListMessage::selection(
self.camera_projection,
MessageDirection::ToWidget,
Some(0),
));
}
pub fn frame_bounds(&self, ui: &UserInterface) -> Rect<f32> {
ui.node(self.frame).screen_bounds()
}
#[must_use]
fn on_key_up(
&mut self,
key: KeyCode,
editor_scene: &mut EditorScene,
active_interaction_mode: Option<&mut Box<dyn InteractionMode>>,
engine: &mut Engine,
key_bindings: &KeyBindings,
) -> bool {
if editor_scene.camera_controller.on_key_up(key_bindings, key) {
return true;
}
if let Some(interaction_mode) = active_interaction_mode {
if interaction_mode.on_key_up(key, editor_scene, engine) {
return true;
}
}
false
}
#[must_use]
fn on_key_down(
&mut self,
key: KeyCode,
editor_scene: &mut EditorScene,
active_interaction_mode: Option<&mut Box<dyn InteractionMode>>,
engine: &mut Engine,
key_bindings: &KeyBindings,
) -> bool {
if editor_scene
.camera_controller
.on_key_down(key_bindings, key)
{
return true;
}
if let Some(interaction_mode) = active_interaction_mode {
if interaction_mode.on_key_down(key, editor_scene, engine) {
return true;
}
}
false
}
fn on_mouse_move(
&mut self,
pos: Vector2<f32>,
editor_scene: &mut EditorScene,
active_interaction_mode: Option<&mut Box<dyn InteractionMode>>,
engine: &mut Engine,
settings: &Settings,
) {
let screen_bounds = self.frame_bounds(&engine.user_interface);
let last_pos = *self.last_mouse_pos.get_or_insert(pos);
let mouse_offset = pos - last_pos;
editor_scene
.camera_controller
.on_mouse_move(mouse_offset, &settings.camera);
let rel_pos = pos - screen_bounds.position;
if let Some(interaction_mode) = active_interaction_mode {
interaction_mode.on_mouse_move(
mouse_offset,
rel_pos,
editor_scene.camera_controller.camera,
editor_scene,
engine,
screen_bounds.size,
settings,
);
}
self.last_mouse_pos = Some(pos);
}
fn on_mouse_up(
&mut self,
button: MouseButton,
pos: Vector2<f32>,
editor_scene: &mut EditorScene,
active_interaction_mode: Option<&mut Box<dyn InteractionMode>>,
engine: &mut Engine,
settings: &Settings,
) {
engine.user_interface.release_mouse_capture();
let screen_bounds = self.frame_bounds(&engine.user_interface);
if button == MouseButton::Left {
self.click_mouse_pos = None;
if let Some(current_im) = active_interaction_mode {
let rel_pos = pos - screen_bounds.position;
current_im.on_left_mouse_button_up(
editor_scene,
engine,
rel_pos,
screen_bounds.size,
settings,
);
}
}
editor_scene.camera_controller.on_mouse_button_up(button);
}
fn on_mouse_down(
&mut self,
button: MouseButton,
pos: Vector2<f32>,
editor_scene: &mut EditorScene,
active_interaction_mode: Option<&mut Box<dyn InteractionMode>>,
engine: &mut Engine,
settings: &Settings,
) {
engine.user_interface.capture_mouse(self.frame());
let screen_bounds = self.frame_bounds(&engine.user_interface);
if button == MouseButton::Left {
if let Some(current_im) = active_interaction_mode {
let rel_pos = pos - screen_bounds.position;
self.click_mouse_pos = Some(rel_pos);
current_im.on_left_mouse_button_down(
editor_scene,
engine,
rel_pos,
screen_bounds.size,
settings,
);
}
}
editor_scene.camera_controller.on_mouse_button_down(button);
}
fn on_drop(
&mut self,
handle: Handle<UiNode>,
engine: &mut Engine,
editor_scene: &mut EditorScene,
settings: &Settings,
) {
if handle.is_none() {
return;
}
let screen_bounds = self.frame_bounds(&engine.user_interface);
let frame_size = screen_bounds.size;
if let Some(item) = engine.user_interface.node(handle).cast::<AssetItem>() {
if let Ok(relative_path) = make_relative_path(&item.path) {
match item.kind {
AssetKind::Model => {
if let Some(preview) = self.preview_instance.take() {
let scene = &mut engine.scenes[editor_scene.scene];
let sub_graph = scene.graph.take_reserve_sub_graph(preview.instance);
let group = vec![
SceneCommand::new(AddModelCommand::new(sub_graph)),
SceneCommand::new(ChangeSelectionCommand::new(
Selection::Graph(GraphSelection::single_or_empty(
preview.instance,
)),
editor_scene.selection.clone(),
)),
];
self.sender
.send(Message::do_scene_command(CommandGroup::from(group)))
.unwrap();
}
}
AssetKind::Texture => {
let cursor_pos = engine.user_interface.cursor_position();
let rel_pos = cursor_pos - screen_bounds.position;
let graph = &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: |_, _| 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();
let texture = texture.state();
if let TextureState::Ok(_) = *texture {
let node =
&mut engine.scenes[editor_scene.scene].graph[result.node];
if node.is_mesh() {
self.sender
.send(Message::do_scene_command(
SetMeshTextureCommand::new(result.node, tex),
))
.unwrap();
}
}
}
}
_ => {}
}
}
}
}
}