Skip to content

Commit

Permalink
editor plugins improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
mrDIMAS committed Sep 10, 2024
1 parent 9ea2eb5 commit ba0bab1
Show file tree
Hide file tree
Showing 2 changed files with 320 additions and 16 deletions.
244 changes: 232 additions & 12 deletions src/code/snippets/src/editor/plugins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@ use fyrox::{
visitor::prelude::*,
},
engine::Engine,
graph::BaseSceneGraph,
gui::{BuildContext, UiNode},
scene::node::Node,
scene::{base::BaseBuilder, graph::Graph, node::Node, sprite::SpriteBuilder},
script::ScriptTrait,
};
use fyroxed_base::{
interaction::{gizmo::move_gizmo::MoveGizmo, make_interaction_mode_button, InteractionMode},
camera::PickingOptions,
command::{CommandContext, CommandTrait},
interaction::{
gizmo::move_gizmo::MoveGizmo, make_interaction_mode_button, plane::PlaneKind,
InteractionMode,
},
message::MessageSender,
plugin::EditorPlugin,
scene::{controller::SceneController, GameScene, Selection},
scene::{commands::GameSceneContext, controller::SceneController, GameScene, Selection},
settings::Settings,
Editor, Message,
};
Expand Down Expand Up @@ -86,12 +93,12 @@ impl EditorPlugin for MyPlugin {
// ANCHOR_END: selection_2

// ANCHOR: interaction_mode_create
let move_gizmo = MoveGizmo::new(game_scene, &mut editor.engine);

entry.interaction_modes.add(MyInteractionMode {
move_gizmo,
drag_context: None,
});
entry.interaction_modes.add(MyInteractionMode::new(
game_scene,
&mut editor.engine,
editor.message_sender.clone(),
*node_handle,
));
// ANCHOR_END: interaction_mode_create

// ANCHOR: selection_3
Expand All @@ -106,29 +113,45 @@ impl EditorPlugin for MyPlugin {
}
// ANCHOR_END: plugin_impl_2

// ANCHOR: interaction_mode_definition
struct DragContext {
point_index: usize,
initial_position: Vector3<f32>,
plane_kind: PlaneKind,
}

// ANCHOR: interaction_mode
#[derive(TypeUuidProvider)]
#[type_uuid(id = "d7f56947-a106-408a-9c18-d0191ef89925")]
pub struct MyInteractionMode {
move_gizmo: MoveGizmo,
node_handle: Handle<Node>,
drag_context: Option<DragContext>,
message_sender: MessageSender,
line_points_gizmo: LinePointsGizmo,
selected_point_index: Option<usize>,
}

impl MyInteractionMode {
pub fn new(game_scene: &GameScene, engine: &mut Engine) -> Self {
pub fn new(
game_scene: &GameScene,
engine: &mut Engine,
message_sender: MessageSender,
node_handle: Handle<Node>,
) -> Self {
Self {
move_gizmo: MoveGizmo::new(game_scene, engine),
node_handle,
drag_context: None,
message_sender,
line_points_gizmo: LinePointsGizmo::default(),
selected_point_index: None,
}
}
}
// ANCHOR_END: interaction_mode_definition

impl InteractionMode for MyInteractionMode {
// ANCHOR: on_left_mouse_button_down
fn on_left_mouse_button_down(
&mut self,
editor_selection: &Selection,
Expand All @@ -138,8 +161,47 @@ impl InteractionMode for MyInteractionMode {
frame_size: Vector2<f32>,
settings: &Settings,
) {
let Some(game_scene) = controller.downcast_mut::<GameScene>() else {
return;
};

let scene = &mut engine.scenes[game_scene.scene];

// Pick scene entity at the cursor position.
if let Some(result) = game_scene.camera_controller.pick(
&scene.graph,
PickingOptions {
cursor_pos: mouse_pos,
editor_only: true,
filter: Some(&mut |handle, _| handle != self.move_gizmo.origin),
..Default::default()
},
) {
// The gizmo needs to be fed with input events as well, so it can react to the cursor.
if let Some(plane_kind) = self.move_gizmo.handle_pick(result.node, &mut scene.graph) {
// Start point dragging if there's any point selected.
if let Some(selected_point_index) = self.selected_point_index {
self.drag_context = Some(DragContext {
point_index: selected_point_index,
initial_position: scene.graph
[self.line_points_gizmo.point_nodes[selected_point_index]]
.global_position(),
plane_kind,
})
}
} else {
// Handle point picking and remember a selected point.
for (index, point_handle) in self.line_points_gizmo.point_nodes.iter().enumerate() {
if result.node == *point_handle {
self.selected_point_index = Some(index);
}
}
}
}
}
// ANCHOR_END: on_left_mouse_button_down

// ANCHOR: on_left_mouse_button_up
fn on_left_mouse_button_up(
&mut self,
editor_selection: &Selection,
Expand All @@ -149,8 +211,36 @@ impl InteractionMode for MyInteractionMode {
frame_size: Vector2<f32>,
settings: &Settings,
) {
let Some(game_scene) = controller.downcast_mut::<GameScene>() else {
return;
};

let scene = &mut engine.scenes[game_scene.scene];

if let Some(drag_context) = self.drag_context.take() {
if let Some(script) = scene
.graph
.try_get_script_of_mut::<MyScript>(self.node_handle)
{
// Restore the position of the point and use its new position as the value for
// the command below.
let new_position = std::mem::replace(
&mut script.points[drag_context.point_index],
drag_context.initial_position,
);

// Confirm the action by creating respective command.
self.message_sender.do_command(SetPointPositionCommand {
node_handle: self.node_handle,
point_index: drag_context.point_index,
point_position: new_position,
});
}
}
}
// ANCHOR_END: on_left_mouse_button_up

// ANCHOR: on_mouse_move
fn on_mouse_move(
&mut self,
mouse_offset: Vector2<f32>,
Expand All @@ -161,22 +251,152 @@ impl InteractionMode for MyInteractionMode {
frame_size: Vector2<f32>,
settings: &Settings,
) {
let Some(game_scene) = controller.downcast_mut::<GameScene>() else {
return;
};

let scene = &mut engine.scenes[game_scene.scene];

if let Some(drag_context) = self.drag_context.as_ref() {
let global_offset = self.move_gizmo.calculate_offset(
&scene.graph,
game_scene.camera_controller.camera,
mouse_offset,
mouse_position,
frame_size,
drag_context.plane_kind,
);

if let Some(script) = scene
.graph
.try_get_script_of_mut::<MyScript>(self.node_handle)
{
script.points[drag_context.point_index] =
drag_context.initial_position + global_offset;
}
}
}
// ANCHOR_END: on_mouse_move

// ANCHOR: update
fn update(
&mut self,
editor_selection: &Selection,
controller: &mut dyn SceneController,
engine: &mut Engine,
settings: &Settings,
) {
let Some(game_scene) = controller.downcast_mut::<GameScene>() else {
return;
};

let scene = &mut engine.scenes[game_scene.scene];

self.line_points_gizmo
.sync_to_model(self.node_handle, game_scene, &mut scene.graph);
}
// ANCHOR_END: update

fn deactivate(&mut self, controller: &dyn SceneController, engine: &mut Engine) {}

// ANCHOR: make_button
fn make_button(&mut self, ctx: &mut BuildContext, selected: bool) -> Handle<UiNode> {
make_interaction_mode_button(ctx, include_bytes!("icon.png"), "Line Edit Mode", selected)
}
// ANCHOR_END: make_button

fn uuid(&self) -> Uuid {
Self::type_uuid()
}
}
// ANCHOR_END: interaction_mode

fn add_my_plugin(editor: &mut Editor) {
// ANCHOR: plugin_registration
editor.add_editor_plugin(MyPlugin::default());
// ANCHOR_END: plugin_registration
}

// ANCHOR: line_points_gizmo
#[derive(Default)]
struct LinePointsGizmo {
point_nodes: Vec<Handle<Node>>,
}

impl LinePointsGizmo {
fn sync_to_model(
&mut self,
node_handle: Handle<Node>,
game_scene: &GameScene,
graph: &mut Graph,
) {
let Some(script) = graph.try_get_script_of::<MyScript>(node_handle) else {
return;
};
let points = script.points.clone();

if self.point_nodes.len() != points.len() {
self.remove_points(graph);
for point in points {
// Point could be represented via sprite - it will always be facing towards editor's
// camera.
let point_node = SpriteBuilder::new(BaseBuilder::new())
.with_size(0.1)
.build(graph);

self.point_nodes.push(point_node);

// Link the sprite with the special scene node - the name of it should clearly state
// its purpose.
graph.link_nodes(point_node, game_scene.editor_objects_root);
}
}
}

fn remove_points(&mut self, graph: &mut Graph) {
for handle in self.point_nodes.drain(..) {
graph.remove_node(handle);
}
}
}

// ANCHOR_END: line_points_gizmo

// ANCHOR: command
#[derive(Debug)]
struct SetPointPositionCommand {
node_handle: Handle<Node>,
point_index: usize,
point_position: Vector3<f32>,
}

impl SetPointPositionCommand {
fn swap(&mut self, context: &mut dyn CommandContext) {
// Get typed version of the context, it could also be UiSceneContext for
// UI scenes.
let context = context.get_mut::<GameSceneContext>();
// Get a reference to the script instance.
let script = context.scene.graph[self.node_handle]
.try_get_script_mut::<MyScript>()
.unwrap();
// Swap the position of the point with the one stored in the command.
std::mem::swap(
&mut script.points[self.point_index],
&mut self.point_position,
);
}
}

impl CommandTrait for SetPointPositionCommand {
fn name(&mut self, context: &dyn CommandContext) -> String {
"Set Point Position".to_owned()
}

fn execute(&mut self, context: &mut dyn CommandContext) {
self.swap(context)
}

fn revert(&mut self, context: &mut dyn CommandContext) {
self.swap(context)
}
}
// ANCHOR_END: command
Loading

0 comments on commit ba0bab1

Please sign in to comment.