broken_seals/patches/custom_skeleton_3d_editor_plugin.patch

3274 lines
125 KiB
Diff

From dba67e552f3ded667d287aee3c2d24abf454db82 Mon Sep 17 00:00:00 2001
From: Saracen <SaracenOne@gmail.com>
Date: Sat, 2 May 2020 10:11:48 +0100
Subject: [PATCH] Custom Skeleton3DEditorPlugin
Add fallthrough hint
Add missing semicolon
Fix regression from Skeleton3D Inspector redesign
It can be reproduced by reimporting many FBX files with a skeleton.
A change in commit f7fdc87 [Merged on May 27] mistakenly changed the Skeleton3D "pose" property from PROPERTY_USAGE_EDITOR to PROPERTY_USAGE_NOEDITOR.
skel edit
fix selection
---
doc/classes/EditorPlugin.xml | 10 +-
editor/animation_track_editor.cpp | 20 +
editor/animation_track_editor.h | 2 +
editor/editor_node.cpp | 4 +-
editor/editor_node.h | 2 +-
editor/editor_plugin.cpp | 6 +-
editor/editor_plugin.h | 2 +-
editor/editor_themes.cpp | 1 +
editor/icons/icon_editor_bone_handle.svg | 1 +
editor/icons/icon_tool_bone_move.svg | 1 +
editor/icons/icon_tool_bone_rest.svg | 1 +
editor/icons/icon_tool_bone_rotate.svg | 1 +
editor/icons/icon_tool_bone_scale.svg | 1 +
editor/icons/icon_tool_bone_select.svg | 1 +
.../collision_polygon_editor_plugin.cpp | 2 +-
.../plugins/collision_polygon_editor_plugin.h | 4 +-
editor/plugins/path_editor_plugin.cpp | 2 +-
editor/plugins/path_editor_plugin.h | 2 +-
editor/plugins/skeleton_editor_plugin.cpp | 1705 ++++++++++++++++-
editor/plugins/skeleton_editor_plugin.h | 234 ++-
editor/plugins/spatial_editor_plugin.cpp | 173 +-
editor/plugins/spatial_editor_plugin.h | 99 +-
editor/spatial_editor_gizmos.cpp | 94 +-
editor/spatial_editor_gizmos.h | 5 +
modules/gridmap/grid_map_editor_plugin.h | 2 +-
scene/3d/skeleton.cpp | 37 +-
scene/3d/skeleton.h | 6 +
27 files changed, 2223 insertions(+), 195 deletions(-)
create mode 100644 editor/icons/icon_editor_bone_handle.svg
create mode 100644 editor/icons/icon_tool_bone_move.svg
create mode 100644 editor/icons/icon_tool_bone_rest.svg
create mode 100644 editor/icons/icon_tool_bone_rotate.svg
create mode 100644 editor/icons/icon_tool_bone_scale.svg
create mode 100644 editor/icons/icon_tool_bone_select.svg
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index 3f741d3e76..585580261b 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -278,22 +278,24 @@
<method name="forward_spatial_gui_input" qualifiers="virtual">
<return type="bool">
</return>
- <argument index="0" name="camera" type="Camera">
+ <argument index="0" name="index" type="int">
</argument>
- <argument index="1" name="event" type="InputEvent">
+ <argument index="1" name="camera" type="Camera">
+ </argument>
+ <argument index="2" name="event" type="InputEvent">
</argument>
<description>
Called when there is a root node in the current edited scene, [method handles] is implemented and an [InputEvent] happens in the 3D viewport. Intercepts the [InputEvent], if [code]return true[/code] [EditorPlugin] consumes the [code]event[/code], otherwise forwards [code]event[/code] to other Editor classes. Example:
[codeblock]
# Prevents the InputEvent to reach other Editor classes
- func forward_spatial_gui_input(camera, event):
+ func forward_spatial_gui_input(index, camera, event):
var forward = true
return forward
[/codeblock]
Must [code]return false[/code] in order to forward the [InputEvent] to other Editor classes. Example:
[codeblock]
# Consumes InputEventMouseMotion and forwards other InputEvent types
- func forward_spatial_gui_input(camera, event):
+ func forward_spatial_gui_input(index, camera, event):
var forward = false
if event is InputEventMouseMotion:
forward = true
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index b5792034cf..cc9725d0bc 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -3562,6 +3562,26 @@ void AnimationTrackEditor::_insert_delay() {
insert_queue = false;
}
+bool AnimationTrackEditor::has_transform_key(Spatial *p_node, const String &p_sub) {
+
+ if (!keying)
+ return false;
+ if (!animation.is_valid())
+ return false;
+ if (!root)
+ return false;
+
+ //let's build a node path
+ String path = root->get_path_to(p_node);
+ if (p_sub != "")
+ path += ":" + p_sub;
+
+ if (animation->find_track(path) >= 0) {
+ return true;
+ }
+ return false;
+}
+
void AnimationTrackEditor::insert_transform_key(Spatial *p_node, const String &p_sub, const Transform &p_xform) {
if (!keying)
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index e1798affa9..fa8ac3ff69 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -514,6 +514,8 @@ public:
void insert_value_key(const String &p_property, const Variant &p_value, bool p_advance);
void insert_transform_key(Spatial *p_node, const String &p_sub, const Transform &p_xform);
+ bool has_transform_key(Spatial *p_node, const String &p_sub);
+
void show_select_node_warning(bool p_show);
bool is_key_selected(int p_track, int p_key) const;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 1db054c26f..d99598f817 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -7151,7 +7151,7 @@ bool EditorPluginList::forward_gui_input(const Ref<InputEvent> &p_event) {
return discard;
}
-bool EditorPluginList::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event, bool serve_when_force_input_enabled) {
+bool EditorPluginList::forward_spatial_gui_input(int p_index, Camera *p_camera, const Ref<InputEvent> &p_event, bool serve_when_force_input_enabled) {
bool discard = false;
for (int i = 0; i < plugins_list.size(); i++) {
@@ -7159,7 +7159,7 @@ bool EditorPluginList::forward_spatial_gui_input(Camera *p_camera, const Ref<Inp
continue;
}
- if (plugins_list[i]->forward_spatial_gui_input(p_camera, p_event)) {
+ if (plugins_list[i]->forward_spatial_gui_input(p_index, p_camera, p_event)) {
discard = true;
}
}
diff --git a/editor/editor_node.h b/editor/editor_node.h
index cddc0c84e4..b8d247abc9 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -907,7 +907,7 @@ public:
bool forward_gui_input(const Ref<InputEvent> &p_event);
void forward_canvas_draw_over_viewport(Control *p_overlay);
void forward_canvas_force_draw_over_viewport(Control *p_overlay);
- bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event, bool serve_when_force_input_enabled);
+ bool forward_spatial_gui_input(int p_index, Camera *p_camera, const Ref<InputEvent> &p_event, bool serve_when_force_input_enabled);
void forward_spatial_draw_over_viewport(Control *p_overlay);
void forward_spatial_force_draw_over_viewport(Control *p_overlay);
void add_plugin(EditorPlugin *p_plugin);
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index d4c16ee042..89e3316680 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -617,10 +617,10 @@ int EditorPlugin::update_overlays() const {
}
}
-bool EditorPlugin::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) {
+bool EditorPlugin::forward_spatial_gui_input(int p_index, Camera *p_camera, const Ref<InputEvent> &p_event) {
if (get_script_instance() && get_script_instance()->has_method("forward_spatial_gui_input")) {
- return get_script_instance()->call("forward_spatial_gui_input", p_camera, p_event);
+ return get_script_instance()->call("forward_spatial_gui_input", p_index, p_camera, p_event);
}
return false;
@@ -903,7 +903,7 @@ void EditorPlugin::_bind_methods() {
ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_canvas_gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_canvas_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_canvas_force_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control")));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_spatial_gui_input", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")));
+ ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "forward_spatial_gui_input", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_spatial_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo("forward_spatial_force_draw_over_viewport", PropertyInfo(Variant::OBJECT, "overlay", PROPERTY_HINT_RESOURCE_TYPE, "Control")));
ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_plugin_name"));
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index 21293cbbfc..af6732b867 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -193,7 +193,7 @@ public:
virtual void forward_canvas_draw_over_viewport(Control *p_overlay);
virtual void forward_canvas_force_draw_over_viewport(Control *p_overlay);
- virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event);
+ virtual bool forward_spatial_gui_input(int p_index, Camera *p_camera, const Ref<InputEvent> &p_event);
virtual void forward_spatial_draw_over_viewport(Control *p_overlay);
virtual void forward_spatial_force_draw_over_viewport(Control *p_overlay);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 2864ed652a..7b164c6223 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -213,6 +213,7 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
exceptions.insert("EditorPivot");
exceptions.insert("EditorHandle");
exceptions.insert("Editor3DHandle");
+ exceptions.insert("EditorBoneHandle");
exceptions.insert("Godot");
exceptions.insert("PanoramaSky");
exceptions.insert("ProceduralSky");
diff --git a/editor/icons/icon_editor_bone_handle.svg b/editor/icons/icon_editor_bone_handle.svg
new file mode 100644
index 0000000000..7658b90f7e
--- /dev/null
+++ b/editor/icons/icon_editor_bone_handle.svg
@@ -0,0 +1 @@
+<svg height="8" viewBox="0 0 8 8" width="8" xmlns="http://www.w3.org/2000/svg"><circle cx="4" cy="4" fill="#fff" r="4"/><circle cx="4" cy="4" fill="#000" r="2.5"/></svg>
\ No newline at end of file
diff --git a/editor/icons/icon_tool_bone_move.svg b/editor/icons/icon_tool_bone_move.svg
new file mode 100644
index 0000000000..b9b15eee3f
--- /dev/null
+++ b/editor/icons/icon_tool_bone_move.svg
@@ -0,0 +1 @@
+<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-opacity=".9961"><path d="m11.5 14.448-.831-.831-.909.909 1.286 1.285c.251.251.657.251.908 0l1.286-1.285-.909-.909z"/><circle cx="11.5" cy="11.5" r="1.286"/><path d="m8.753 10.038-.278-.278-1.286 1.286c-.251.251-.251.657 0 .908l.824.824.462.462.909-.909-.832-.831.832-.831z"/><path d="m15.812 11.046-1.286-1.286-.909.909.831.831-.831.831.909.909 1.285-1.286c.252-.251.252-.657.001-.908z"/><path d="m11.954 7.188c-.061-.061-.133-.107-.21-.139-.028-.012-.059-.01-.088-.018-.051-.013-.1-.03-.151-.03-.005 0-.01 0-.015 0-.167.002-.327.069-.444.187l-1.286 1.287.278.278.631.63.831-.831.831.831.909-.908-.465-.465z"/><path d="m6.128 9.985 1.286-1.286c.241-.242.545-.384.859-.426.044-.323.193-.626.426-.859l1.286-1.286c.396-.397.925-.619 1.47-.625h.05c.562 0 1.112.228 1.51.626l1.215 1.215c.016-.015.033-.025.048-.04.964-.963.964-2.524 0-3.488-.378-.378-.868-.623-1.397-.698-.074-.529-.318-1.019-.695-1.397-.455-.453-1.067-.711-1.707-.721-.667-.01-1.309.25-1.782.72-.828.829-.96 2.126-.314 3.105l-3.558 3.561c-.978-.646-2.274-.515-3.103.312-.963.962-.963 2.524 0 3.487.378.377.868.621 1.396.695.075.529.319 1.02.696 1.396.963.964 2.525.964 3.488 0 .015-.015.025-.032.04-.048l-1.215-1.215c-.835-.833-.835-2.193.001-3.028z"/></g></svg>
\ No newline at end of file
diff --git a/editor/icons/icon_tool_bone_rest.svg b/editor/icons/icon_tool_bone_rest.svg
new file mode 100644
index 0000000000..ab4f915e3d
--- /dev/null
+++ b/editor/icons/icon_tool_bone_rest.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m16 8c0 1.409-.72 2.641-1.824 3.345l1.824 1.823v2.832h-2.832l-4-4h-5.168v4h-4v-16h12c2.208 0 4 1.792 4 4zm-12 0h8v-4h-8z" fill="#e0e0e0"/></svg>
\ No newline at end of file
diff --git a/editor/icons/icon_tool_bone_rotate.svg b/editor/icons/icon_tool_bone_rotate.svg
new file mode 100644
index 0000000000..1c81687245
--- /dev/null
+++ b/editor/icons/icon_tool_bone_rotate.svg
@@ -0,0 +1 @@
+<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-opacity=".9961"><circle cx="11.501" cy="11.5" r="1.286"/><path d="m15.536 9.51c-.568-1.152-1.591-1.985-2.79-2.331-.027-.008-.056-.011-.084-.019-.163-.043-.327-.084-.496-.109-.199-.03-.402-.048-.608-.05-.027 0-.054 0-.081 0-.039 0-.077 0-.115.001-.149.004-.299.017-.448.037-1.911.251-3.45 1.693-3.826 3.584-.049.248-.069.497-.076.745-.002.062-.002.123-.001.185.004.496.092.983.256 1.447.019.054.04.106.061.16.091.228.194.45.321.661.001.001.001.003.002.004.194.321.429.62.704.889h-.71v1.286h2.571c.355 0 .643-.287.643-.643 0-.053-.006-.105-.019-.156l-.643-2.571-1.248.313.181.721c-.54-.591-.841-1.362-.843-2.163 0-.442.09-.863.251-1.246.08-.189.178-.369.291-.537.001-.005.003-.009.005-.013.348-.516.841-.924 1.42-1.168.384-.162.805-.251 1.247-.251 1.775 0 3.214 1.439 3.214 3.214-.001.853-.34 1.669-.942 2.271l.91.91c1.363-1.363 1.706-3.442.853-5.171z"/><path d="m5.616 10.33c.502-2.522 2.553-4.443 5.103-4.778.199-.026.399-.043.598-.049l.16-.002h.1c1.182.015 2.294.375 3.236 1 .369-.894.191-1.959-.535-2.686-.378-.377-.868-.622-1.397-.697-.074-.529-.318-1.019-.695-1.397-.455-.453-1.067-.711-1.707-.721-.667-.01-1.309.25-1.782.72-.828.829-.96 2.126-.314 3.105l-3.558 3.561c-.978-.646-2.274-.515-3.103.312-.963.962-.963 2.524 0 3.487.378.377.868.621 1.396.695.075.529.319 1.02.696 1.396.632.633 1.52.842 2.329.645v-.208c0-.14.019-.275.055-.403-.639-1.202-.856-2.601-.582-3.98z"/></g></svg>
\ No newline at end of file
diff --git a/editor/icons/icon_tool_bone_scale.svg b/editor/icons/icon_tool_bone_scale.svg
new file mode 100644
index 0000000000..b1facc4049
--- /dev/null
+++ b/editor/icons/icon_tool_bone_scale.svg
@@ -0,0 +1 @@
+<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-opacity=".9961"><path d="m10.857 14.714h-1.662l.83-.831-.908-.909-.832.832v-1.663c0-.355-.288-.643-.643-.643s-.642.287-.642.643v2.383.832c0 .355.288.642.643.642h3.214c.355 0 .643-.287.643-.643s-.287-.643-.643-.643z"/><path d="m15.357 7h-.832-2.383c-.355 0-.642.288-.642.643s.287.643.643.643h1.663l-.832.832.909.908.831-.83v1.662c0 .355.288.643.644.643s.642-.288.642-.644v-3.214c0-.355-.287-.643-.643-.643z"/><circle cx="11.5" cy="11.5" r="1.286"/><path d="m8.573 10.218 1.645-1.645c-.137-.282-.218-.596-.218-.93 0-1.182.961-2.143 2.143-2.143h2.852c-.014-.611-.25-1.218-.717-1.685-.378-.377-.868-.622-1.397-.697-.074-.529-.318-1.019-.695-1.397-.455-.453-1.067-.711-1.707-.721-.667-.01-1.309.25-1.782.72-.828.829-.96 2.126-.314 3.105l-3.558 3.561c-.978-.646-2.274-.515-3.103.312-.963.962-.963 2.524 0 3.487.378.377.868.621 1.396.695.075.529.319 1.02.696 1.396.467.467 1.074.703 1.685.717v-2.852c.001-1.18.962-2.141 2.144-2.141.334 0 .648.081.93.218z"/></g></svg>
\ No newline at end of file
diff --git a/editor/icons/icon_tool_bone_select.svg b/editor/icons/icon_tool_bone_select.svg
new file mode 100644
index 0000000000..73e79a191d
--- /dev/null
+++ b/editor/icons/icon_tool_bone_select.svg
@@ -0,0 +1 @@
+<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-opacity=".9961"><path d="m16 11.46-6.142-2.527-1.572-.647.647 1.572 2.527 6.142.913-2.72 1.815 1.817.91-.909-1.818-1.815z"/><path d="m7.784 11.008-.886-2.152c-.23-.56-.102-1.203.327-1.631.287-.287.67-.439 1.061-.439.192 0 .386.037.57.113l2.151.885.17-.17c.977.645 2.271.516 3.1-.311.964-.963.964-2.524 0-3.488-.377-.377-.867-.622-1.396-.697-.074-.529-.318-1.019-.695-1.397-.455-.453-1.067-.711-1.707-.721-.667-.01-1.309.25-1.782.72-.828.829-.96 2.126-.314 3.105l-3.558 3.561c-.978-.646-2.274-.515-3.103.312-.963.962-.963 2.524 0 3.487.378.377.868.621 1.396.695.075.529.319 1.02.696 1.396.963.964 2.525.964 3.488 0 .828-.828.96-2.125.314-3.104z"/></g></svg>
\ No newline at end of file
diff --git a/editor/plugins/collision_polygon_editor_plugin.cpp b/editor/plugins/collision_polygon_editor_plugin.cpp
index 9cd7a60946..b207590996 100644
--- a/editor/plugins/collision_polygon_editor_plugin.cpp
+++ b/editor/plugins/collision_polygon_editor_plugin.cpp
@@ -109,7 +109,7 @@ void Polygon3DEditor::_wip_close() {
undo_redo->commit_action();
}
-bool Polygon3DEditor::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) {
+bool Polygon3DEditor::forward_spatial_gui_input(int p_index, Camera *p_camera, const Ref<InputEvent> &p_event) {
if (!node)
return false;
diff --git a/editor/plugins/collision_polygon_editor_plugin.h b/editor/plugins/collision_polygon_editor_plugin.h
index 26e0a22713..ed2bf8cee6 100644
--- a/editor/plugins/collision_polygon_editor_plugin.h
+++ b/editor/plugins/collision_polygon_editor_plugin.h
@@ -90,7 +90,7 @@ protected:
static void _bind_methods();
public:
- virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event);
+ virtual bool forward_spatial_gui_input(int p_index, Camera *p_camera, const Ref<InputEvent> &p_event);
void edit(Node *p_collision_polygon);
Polygon3DEditor(EditorNode *p_editor);
~Polygon3DEditor();
@@ -104,7 +104,7 @@ class Polygon3DEditorPlugin : public EditorPlugin {
EditorNode *editor;
public:
- virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); }
+ virtual bool forward_spatial_gui_input(int p_index, Camera *p_camera, const Ref<InputEvent> &p_event) { return collision_polygon_editor->forward_spatial_gui_input(p_index, p_camera, p_event); }
virtual String get_name() const { return "Polygon3DEditor"; }
bool has_main_screen() const { return false; }
diff --git a/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp
index 35eef6102e..e5221fc17e 100644
--- a/editor/plugins/path_editor_plugin.cpp
+++ b/editor/plugins/path_editor_plugin.cpp
@@ -292,7 +292,7 @@ PathSpatialGizmo::PathSpatialGizmo(Path *p_path) {
set_spatial_node(p_path);
}
-bool PathEditorPlugin::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) {
+bool PathEditorPlugin::forward_spatial_gui_input(int p_index, Camera *p_camera, const Ref<InputEvent> &p_event) {
if (!path)
return false;
diff --git a/editor/plugins/path_editor_plugin.h b/editor/plugins/path_editor_plugin.h
index ea908b654a..f9e736ea56 100644
--- a/editor/plugins/path_editor_plugin.h
+++ b/editor/plugins/path_editor_plugin.h
@@ -101,7 +101,7 @@ public:
Path *get_edited_path() { return path; }
static PathEditorPlugin *singleton;
- virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event);
+ virtual bool forward_spatial_gui_input(int p_index, Camera *p_camera, const Ref<InputEvent> &p_event);
//virtual bool forward_gui_input(const InputEvent& p_event) { return collision_polygon_editor->forward_gui_input(p_event); }
//virtual Ref<SpatialEditorGizmo> create_spatial_gizmo(Spatial *p_spatial);
diff --git a/editor/plugins/skeleton_editor_plugin.cpp b/editor/plugins/skeleton_editor_plugin.cpp
index 92d396b903..5cd989efce 100644
--- a/editor/plugins/skeleton_editor_plugin.cpp
+++ b/editor/plugins/skeleton_editor_plugin.cpp
@@ -30,27 +30,523 @@
#include "skeleton_editor_plugin.h"
+#include "core/io/resource_saver.h"
+#include "editor/editor_file_dialog.h"
+#include "editor/editor_properties.h"
+#include "editor/editor_scale.h"
+#include "editor/plugins/animation_player_editor_plugin.h"
#include "scene/3d/collision_shape.h"
+#include "scene/3d/mesh_instance.h"
#include "scene/3d/physics_body.h"
#include "scene/3d/physics_joint.h"
#include "scene/resources/capsule_shape.h"
#include "scene/resources/sphere_shape.h"
#include "spatial_editor_plugin.h"
+#define DISTANCE_DEFAULT 4
+
+#define GIZMO_ARROW_SIZE 0.35
+#define GIZMO_RING_HALF_WIDTH 0.1
+#define GIZMO_SCALE_DEFAULT 0.15
+#define GIZMO_PLANE_SIZE 0.2
+#define GIZMO_PLANE_DST 0.3
+#define GIZMO_CIRCLE_SIZE 1.1
+#define GIZMO_SCALE_OFFSET (GIZMO_CIRCLE_SIZE + 0.3)
+#define GIZMO_ARROW_OFFSET (GIZMO_CIRCLE_SIZE + 0.3)
+
+#define ZOOM_MIN_DISTANCE 0.001
+#define ZOOM_MULTIPLIER 1.08
+#define ZOOM_INDICATOR_DELAY_S 1.5
+
+#define FREELOOK_MIN_SPEED 0.01
+#define FREELOOK_SPEED_MULTIPLIER 1.08
+
+#define MIN_Z 0.01
+#define MAX_Z 1000000.0
+
+#define MIN_FOV 0.01
+#define MAX_FOV 179
+
+void BoneTransformEditor::create_editors() {
+ const Color section_color = get_color("prop_subsection", "Editor");
+
+ section = memnew(EditorInspectorSection);
+ section->setup("trf_properties", label, this, section_color, true);
+ add_child(section);
+
+ key_button = memnew(Button);
+ key_button->set_text(TTR("Key Transform"));
+ key_button->set_visible(keyable);
+ key_button->set_icon(get_icon("Key", "EditorIcons"));
+ key_button->set_flat(true);
+ section->get_vbox()->add_child(key_button);
+
+ enabled_checkbox = memnew(CheckBox(TTR("Pose Enabled")));
+ enabled_checkbox->set_flat(true);
+ enabled_checkbox->set_visible(toggle_enabled);
+ section->get_vbox()->add_child(enabled_checkbox);
+
+ Label *l1 = memnew(Label(TTR("Translation")));
+ section->get_vbox()->add_child(l1);
+
+ translation_grid = memnew(GridContainer());
+ translation_grid->set_columns(TRANSLATION_COMPONENTS);
+ section->get_vbox()->add_child(translation_grid);
+
+ Label *l2 = memnew(Label(TTR("Rotation Degrees")));
+ section->get_vbox()->add_child(l2);
+
+ rotation_grid = memnew(GridContainer());
+ rotation_grid->set_columns(ROTATION_DEGREES_COMPONENTS);
+ section->get_vbox()->add_child(rotation_grid);
+
+ Label *l3 = memnew(Label(TTR("Scale")));
+ section->get_vbox()->add_child(l3);
+
+ scale_grid = memnew(GridContainer());
+ scale_grid->set_columns(SCALE_COMPONENTS);
+ section->get_vbox()->add_child(scale_grid);
+
+ Label *l4 = memnew(Label(TTR("Transform")));
+ section->get_vbox()->add_child(l4);
+
+ transform_grid = memnew(GridContainer());
+ transform_grid->set_columns(TRANSFORM_CONTROL_COMPONENTS);
+ section->get_vbox()->add_child(transform_grid);
+
+ static const char *desc[TRANSFORM_COMPONENTS] = { "x", "y", "z", "x", "y", "z", "x", "y", "z", "x", "y", "z" };
+ float snap = EDITOR_GET("interface/inspector/default_float_step");
+
+ for (int i = 0; i < TRANSFORM_CONTROL_COMPONENTS; ++i) {
+ translation_slider[i] = memnew(EditorSpinSlider());
+ translation_slider[i]->set_label(desc[i]);
+ translation_slider[i]->set_step(snap);
+ setup_spinner(translation_slider[i], false);
+ translation_grid->add_child(translation_slider[i]);
+
+ rotation_slider[i] = memnew(EditorSpinSlider());
+ rotation_slider[i]->set_label(desc[i]);
+ rotation_slider[i]->set_step(snap);
+ setup_spinner(rotation_slider[i], false);
+ rotation_grid->add_child(rotation_slider[i]);
+
+ scale_slider[i] = memnew(EditorSpinSlider());
+ scale_slider[i]->set_label(desc[i]);
+ scale_slider[i]->set_step(snap);
+ setup_spinner(scale_slider[i], false);
+ scale_grid->add_child(scale_slider[i]);
+ }
+
+ for (int i = 0; i < TRANSFORM_COMPONENTS; ++i) {
+ transform_slider[i] = memnew(EditorSpinSlider());
+ transform_slider[i]->set_label(desc[i]);
+ transform_slider[i]->set_step(snap);
+ setup_spinner(transform_slider[i], true);
+ transform_grid->add_child(transform_slider[i]);
+ }
+}
+
+void BoneTransformEditor::setup_spinner(EditorSpinSlider *spinner, const bool is_transform_spinner) {
+ spinner->set_flat(true);
+ spinner->set_min(-10000);
+ spinner->set_max(10000);
+ spinner->set_hide_slider(true);
+ spinner->set_allow_greater(true);
+ spinner->set_allow_lesser(true);
+ spinner->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ spinner->connect("value_changed", this, "_value_changed", varray(is_transform_spinner));
+}
+
+void BoneTransformEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ create_editors();
+ key_button->connect("pressed", this, "_key_button_pressed");
+ enabled_checkbox->connect("toggled", this, "_checkbox_toggled");
+ FALLTHROUGH;
+ }
+ case NOTIFICATION_THEME_CHANGED: {
+ const Color base = get_color("accent_color", "Editor");
+ const Color bg_color = get_color("property_color", "Editor");
+ const Color bg_lbl_color(bg_color.r, bg_color.g, bg_color.b, 0.5);
+
+ for (int i = 0; i < TRANSLATION_COMPONENTS; i++) {
+ Color c = base;
+ c.set_hsv(float(i % TRANSLATION_COMPONENTS) / TRANSLATION_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v());
+ if (!translation_slider[i]) {
+ continue;
+ }
+ translation_slider[i]->set_custom_label_color(true, c);
+ }
+
+ for (int i = 0; i < ROTATION_DEGREES_COMPONENTS; i++) {
+ Color c = base;
+ c.set_hsv(float(i % ROTATION_DEGREES_COMPONENTS) / ROTATION_DEGREES_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v());
+ if (!rotation_slider[i]) {
+ continue;
+ }
+ rotation_slider[i]->set_custom_label_color(true, c);
+ }
+
+ for (int i = 0; i < SCALE_COMPONENTS; i++) {
+ Color c = base;
+ c.set_hsv(float(i % SCALE_COMPONENTS) / SCALE_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v());
+ if (!scale_slider[i]) {
+ continue;
+ }
+ scale_slider[i]->set_custom_label_color(true, c);
+ }
+
+ for (int i = 0; i < TRANSFORM_COMPONENTS; i++) {
+ Color c = base;
+ c.set_hsv(float(i % TRANSFORM_COMPONENTS) / TRANSFORM_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v());
+ if (!transform_slider[i]) {
+ continue;
+ }
+ transform_slider[i]->set_custom_label_color(true, c);
+ }
+
+ break;
+ }
+ case NOTIFICATION_SORT_CHILDREN: {
+ const Ref<Font> font = get_font("font", "Tree");
+
+ Point2 buffer;
+ buffer.x += get_constant("inspector_margin", "Editor");
+ buffer.y += font->get_height();
+ buffer.y += get_constant("vseparation", "Tree");
+
+ const float vector_height = translation_grid->get_size().y;
+ const float transform_height = transform_grid->get_size().y;
+ const float button_height = key_button->get_size().y;
+
+ const float width = get_size().x - get_constant("inspector_margin", "Editor");
+ Vector<Rect2> input_rects;
+ if (keyable && section->get_vbox()->is_visible()) {
+ input_rects.push_back(Rect2(key_button->get_position() + buffer, Size2(width, button_height)));
+ } else {
+ input_rects.push_back(Rect2(0, 0, 0, 0));
+ }
+
+ if (section->get_vbox()->is_visible()) {
+ input_rects.push_back(Rect2(translation_grid->get_position() + buffer, Size2(width, vector_height)));
+ input_rects.push_back(Rect2(rotation_grid->get_position() + buffer, Size2(width, vector_height)));
+ input_rects.push_back(Rect2(scale_grid->get_position() + buffer, Size2(width, vector_height)));
+ input_rects.push_back(Rect2(transform_grid->get_position() + buffer, Size2(width, transform_height)));
+ } else {
+ const int32_t start = input_rects.size();
+ const int32_t empty_input_rect_elements = 4;
+ const int32_t end = start + empty_input_rect_elements;
+ for (int i = start; i < end; ++i) {
+ input_rects.push_back(Rect2(0, 0, 0, 0));
+ }
+ }
+
+ for (int32_t i = 0; i < input_rects.size(); i++) {
+ background_rects[i] = input_rects[i];
+ }
+
+ update();
+ break;
+ }
+ case NOTIFICATION_DRAW: {
+ const Color dark_color = get_color("dark_color_2", "Editor");
+
+ for (int i = 0; i < 5; ++i) {
+ draw_rect(background_rects[i], dark_color);
+ }
+
+ break;
+ }
+ }
+}
+
+void BoneTransformEditor::_value_changed(const double p_value, const bool p_from_transform) {
+ if (updating)
+ return;
+
+ if (property.get_slicec('/', 0) == "bones" && property.get_slicec('/', 2) == "custom_pose") {
+ const Transform tform = compute_transform(p_from_transform);
+
+ undo_redo->create_action(TTR("Set Custom Bone Pose Transform"), UndoRedo::MERGE_ENDS);
+ undo_redo->add_undo_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), skeleton->get_bone_custom_pose(property.get_slicec('/', 1).to_int()));
+ undo_redo->add_do_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), tform);
+ undo_redo->commit_action();
+ } else if (property.get_slicec('/', 0) == "bones") {
+ const Transform tform = compute_transform(p_from_transform);
+
+ undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
+ undo_redo->add_undo_property(skeleton, property, skeleton->get(property));
+ undo_redo->add_do_property(skeleton, property, tform);
+ undo_redo->commit_action();
+ }
+}
+
+Transform BoneTransformEditor::compute_transform(const bool p_from_transform) const {
+
+ // Last modified was a raw transform column...
+ if (p_from_transform) {
+ Transform tform;
+
+ for (int i = 0; i < BASIS_COMPONENTS; ++i) {
+ tform.basis[i / BASIS_SPLIT_COMPONENTS][i % BASIS_SPLIT_COMPONENTS] = transform_slider[i]->get_value();
+ }
+
+ for (int i = 0; i < TRANSLATION_COMPONENTS; ++i) {
+ tform.origin[i] = transform_slider[i + BASIS_COMPONENTS]->get_value();
+ }
+
+ return tform;
+ }
+
+ return Transform(
+ Basis(Vector3(Math::deg2rad(rotation_slider[0]->get_value()), Math::deg2rad(rotation_slider[1]->get_value()), Math::deg2rad(rotation_slider[2]->get_value())),
+ Vector3(scale_slider[0]->get_value(), scale_slider[1]->get_value(), scale_slider[2]->get_value())),
+ Vector3(translation_slider[0]->get_value(), translation_slider[1]->get_value(), translation_slider[2]->get_value()));
+}
+
+void BoneTransformEditor::update_enabled_checkbox() {
+ if (enabled_checkbox) {
+ const String path = "bones/" + property.get_slicec('/', 1) + "/enabled";
+ const bool is_enabled = skeleton->get(path);
+ enabled_checkbox->set_pressed(is_enabled);
+ }
+}
+
+void BoneTransformEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_value_changed", "value"), &BoneTransformEditor::_value_changed);
+ ClassDB::bind_method(D_METHOD("_key_button_pressed"), &BoneTransformEditor::_key_button_pressed);
+ ClassDB::bind_method(D_METHOD("_checkbox_toggled", "toggled"), &BoneTransformEditor::_checkbox_toggled);
+}
+
+void BoneTransformEditor::_update_properties() {
+ if (updating)
+ return;
+
+ if (skeleton == nullptr)
+ return;
+
+ updating = true;
+
+ Transform tform = skeleton->get(property);
+ _update_transform_properties(tform);
+}
+
+void BoneTransformEditor::_update_custom_pose_properties() {
+ if (updating)
+ return;
+
+ if (skeleton == nullptr)
+ return;
+
+ updating = true;
+
+ Transform tform = skeleton->get_bone_custom_pose(property.to_int());
+ _update_transform_properties(tform);
+}
+
+void BoneTransformEditor::_update_transform_properties(Transform tform) {
+
+ Quat rot = tform.get_basis().orthonormalized();
+ Vector3 rot_rad = rot.get_euler();
+ Vector3 rot_degrees = Vector3(Math::rad2deg(rot_rad.x), Math::rad2deg(rot_rad.y), Math::rad2deg(rot_rad.z));
+ Vector3 tr = tform.get_origin();
+ Vector3 scale = tform.basis.get_scale();
+
+ for (int i = 0; i < TRANSLATION_COMPONENTS; i++) {
+ translation_slider[i]->set_value(tr[i]);
+ }
+
+ for (int i = 0; i < ROTATION_DEGREES_COMPONENTS; i++) {
+ rotation_slider[i]->set_value(rot_degrees[i]);
+ }
+
+ for (int i = 0; i < SCALE_COMPONENTS; i++) {
+ scale_slider[i]->set_value(scale[i]);
+ }
+
+ transform_slider[0]->set_value(tform.get_basis()[Vector3::AXIS_X].x);
+ transform_slider[1]->set_value(tform.get_basis()[Vector3::AXIS_X].y);
+ transform_slider[2]->set_value(tform.get_basis()[Vector3::AXIS_X].z);
+ transform_slider[3]->set_value(tform.get_basis()[Vector3::AXIS_Y].x);
+ transform_slider[4]->set_value(tform.get_basis()[Vector3::AXIS_Y].y);
+ transform_slider[5]->set_value(tform.get_basis()[Vector3::AXIS_Y].z);
+ transform_slider[6]->set_value(tform.get_basis()[Vector3::AXIS_Z].x);
+ transform_slider[7]->set_value(tform.get_basis()[Vector3::AXIS_Z].y);
+ transform_slider[8]->set_value(tform.get_basis()[Vector3::AXIS_Z].z);
+
+ for (int i = 0; i < TRANSLATION_COMPONENTS; i++) {
+ transform_slider[BASIS_COMPONENTS + i]->set_value(tform.get_origin()[i]);
+ }
+
+ update_enabled_checkbox();
+ updating = false;
+}
+
+BoneTransformEditor::BoneTransformEditor(Skeleton *p_skeleton) :
+ translation_slider(),
+ rotation_slider(),
+ scale_slider(),
+ transform_slider(),
+ skeleton(p_skeleton),
+ key_button(nullptr),
+ enabled_checkbox(nullptr),
+ keyable(false),
+ toggle_enabled(false),
+ updating(false) {
+
+ undo_redo = EditorNode::get_undo_redo();
+}
+
+void BoneTransformEditor::set_target(const String &p_prop) {
+ property = p_prop;
+}
+
+void BoneTransformEditor::set_keyable(const bool p_keyable) {
+ keyable = p_keyable;
+ if (key_button) {
+ key_button->set_visible(p_keyable);
+ }
+}
+
+void BoneTransformEditor::set_toggle_enabled(const bool p_enabled) {
+ toggle_enabled = p_enabled;
+ if (enabled_checkbox) {
+ enabled_checkbox->set_visible(p_enabled);
+ }
+}
+
+void BoneTransformEditor::_key_button_pressed() {
+ if (skeleton == nullptr)
+ return;
+
+ const BoneId bone_id = property.get_slicec('/', 1).to_int();
+ const String name = skeleton->get_bone_name(bone_id);
+
+ if (name.empty())
+ return;
+
+ // Need to normalize the basis before you key it
+ Transform tform = compute_transform(true);
+ tform.orthonormalize();
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_transform_key(skeleton, name, tform);
+}
+
+void BoneTransformEditor::_checkbox_toggled(const bool p_toggled) {
+ if (enabled_checkbox) {
+ const String path = "bones/" + property.get_slicec('/', 1) + "/enabled";
+ skeleton->set(path, p_toggled);
+ }
+}
+
+void BoneTransformEditor::set_read_only(const bool p_read_only) {
+ for (int i = 0; i < TRANSLATION_COMPONENTS; i++) {
+ translation_slider[i]->set_read_only(p_read_only);
+ }
+ for (int i = 0; i < ROTATION_DEGREES_COMPONENTS; i++) {
+ rotation_slider[i]->set_read_only(p_read_only);
+ }
+ for (int i = 0; i < SCALE_COMPONENTS; i++) {
+ scale_slider[i]->set_read_only(p_read_only);
+ }
+ for (int i = 0; i < TRANSFORM_COMPONENTS; i++) {
+ transform_slider[i]->set_read_only(p_read_only);
+ }
+}
+
+
+void SkeletonEditor::set_keyable(const bool p_keyable) {
+ keyable = p_keyable;
+ options->get_popup()->set_item_disabled(MENU_OPTION_INSERT_KEYS, !p_keyable);
+ options->get_popup()->set_item_disabled(MENU_OPTION_INSERT_KEYS_EXISTED, !p_keyable);
+};
+
void SkeletonEditor::_on_click_option(int p_option) {
if (!skeleton) {
return;
}
switch (p_option) {
+ case MENU_OPTION_INIT_POSE: {
+ init_pose();
+ } break;
+ case MENU_OPTION_INSERT_KEYS: {
+ insert_keys(true);
+ } break;
+ case MENU_OPTION_INSERT_KEYS_EXISTED: {
+ insert_keys(false);
+ } break;
+ case MENU_OPTION_POSE_TO_REST: {
+ pose_to_rest();
+ } break;
case MENU_OPTION_CREATE_PHYSICAL_SKELETON: {
create_physical_skeleton();
} break;
}
}
+void SkeletonEditor::init_pose() {
+ const int bone_len = skeleton->get_bone_count();
+ if (!bone_len) {
+ return;
+ }
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
+ for (int i = 0; i < bone_len; i++) {
+ ur->add_do_method(skeleton, "set_bone_pose", i, Transform());
+ ur->add_undo_method(skeleton, "set_bone_pose", i, skeleton->get_bone_pose(i));
+ }
+ ur->commit_action();
+}
+
+void SkeletonEditor::insert_keys(bool p_all_bones) {
+ if (skeleton == nullptr)
+ return;
+
+ int bone_len = skeleton->get_bone_count();
+ Node *root = EditorNode::get_singleton()->get_tree()->get_root();
+ String path = root->get_path_to(skeleton);
+
+ for (int i = 0; i < bone_len; i++) {
+ const String name = skeleton->get_bone_name(i);
+
+ if (name.empty())
+ continue;
+
+ if (!p_all_bones && !AnimationPlayerEditor::singleton->get_track_editor()->has_transform_key(skeleton, name)) {
+ continue;
+ }
+
+ // Need to normalize the basis before you key it
+ Transform tform = skeleton->get_bone_pose(i);
+ tform.orthonormalize();
+ AnimationPlayerEditor::singleton->get_track_editor()->insert_transform_key(skeleton, name, tform);
+ }
+
+}
+
+void SkeletonEditor::pose_to_rest() {
+ const int bone_len = skeleton->get_bone_count();
+ if (!bone_len) {
+ return;
+ }
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
+ for (int i = 0; i < bone_len; i++) {
+ ur->add_do_method(skeleton, "set_bone_pose", i, Transform());
+ ur->add_undo_method(skeleton, "set_bone_pose", i, skeleton->get_bone_pose(i));
+ ur->add_do_method(skeleton, "set_bone_custom_pose", i, Transform());
+ ur->add_undo_method(skeleton, "set_bone_custom_pose", i, skeleton->get_bone_custom_pose(i));
+ ur->add_do_method(skeleton, "set_bone_rest", i, skeleton->get_bone_rest(i) * skeleton->get_bone_custom_pose(i) * skeleton->get_bone_pose(i));
+ ur->add_undo_method(skeleton, "set_bone_rest", i, skeleton->get_bone_rest(i));
+ }
+ ur->commit_action();
+}
+
void SkeletonEditor::create_physical_skeleton() {
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ERR_FAIL_COND(!get_tree());
Node *owner = skeleton == get_tree()->get_edited_scene_root() ? skeleton : skeleton->get_owner();
const int bc = skeleton->get_bone_count();
@@ -131,68 +627,1213 @@ PhysicalBone *SkeletonEditor::create_physical_bone(int bone_id, int bone_child_i
return physical_bone;
}
-void SkeletonEditor::edit(Skeleton *p_node) {
+Variant SkeletonEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
+ TreeItem *selected = joint_tree->get_selected();
+
+ if (!selected)
+ return Variant();
+
+ Ref<Texture> icon = selected->get_icon(0);
+
+ VBoxContainer *vb = memnew(VBoxContainer);
+ HBoxContainer *hb = memnew(HBoxContainer);
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(icon);
+ tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
+ hb->add_child(tf);
+ Label *label = memnew(Label(selected->get_text(0)));
+ hb->add_child(label);
+ vb->add_child(hb);
+ hb->set_modulate(Color(1, 1, 1, 1));
+
+ set_drag_preview(vb);
+ Dictionary drag_data;
+ drag_data["type"] = "nodes";
+ drag_data["node"] = selected;
+
+ return drag_data;
+}
+
+bool SkeletonEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
+
+ TreeItem *target = joint_tree->get_item_at_position(p_point);
+ if (!target)
+ return false;
+
+ const String path = target->get_metadata(0);
+ if (!path.begins_with("bones/"))
+ return false;
- skeleton = p_node;
+ TreeItem *selected = Object::cast_to<TreeItem>(Dictionary(p_data)["node"]);
+ if (target == selected)
+ return false;
+
+ const String path2 = target->get_metadata(0);
+ if (!path2.begins_with("bones/"))
+ return false;
+
+ return true;
}
-void SkeletonEditor::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- get_tree()->connect("node_removed", this, "_node_removed");
+void SkeletonEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
+ if (!can_drop_data_fw(p_point, p_data, p_from))
+ return;
+
+ TreeItem *target = joint_tree->get_item_at_position(p_point);
+ TreeItem *selected = Object::cast_to<TreeItem>(Dictionary(p_data)["node"]);
+
+ const BoneId target_boneidx = String(target->get_metadata(0)).get_slicec('/', 1).to_int();
+ const BoneId selected_boneidx = String(selected->get_metadata(0)).get_slicec('/', 1).to_int();
+
+ move_skeleton_bone(skeleton->get_path(), selected_boneidx, target_boneidx);
+}
+
+void SkeletonEditor::move_skeleton_bone(NodePath p_skeleton_path, int32_t p_selected_boneidx, int32_t p_target_boneidx) {
+ Node *node = get_node_or_null(p_skeleton_path);
+ Skeleton *skeleton = Object::cast_to<Skeleton>(node);
+ ERR_FAIL_NULL(skeleton);
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Bone Parentage"));
+ // If the target is a child of ourselves, we move only *us* and not our children
+ if (skeleton->is_bone_parent_of(p_target_boneidx, p_selected_boneidx)) {
+ const BoneId parent_idx = skeleton->get_bone_parent(p_selected_boneidx);
+ const int bone_count = skeleton->get_bone_count();
+ for (BoneId i = 0; i < bone_count; ++i) {
+ if (skeleton->get_bone_parent(i) == p_selected_boneidx) {
+ ur->add_undo_method(skeleton, "set_bone_parent", i, skeleton->get_bone_parent(i));
+ ur->add_do_method(skeleton, "set_bone_parent", i, parent_idx);
+ skeleton->set_bone_parent(i, parent_idx);
+ }
+ }
}
+ ur->add_undo_method(skeleton, "set_bone_parent", p_selected_boneidx, skeleton->get_bone_parent(p_selected_boneidx));
+ ur->add_do_method(skeleton, "set_bone_parent", p_selected_boneidx, p_target_boneidx);
+ skeleton->set_bone_parent(p_selected_boneidx, p_target_boneidx);
+
+ update_joint_tree();
+ ur->commit_action();
}
-void SkeletonEditor::_node_removed(Node *p_node) {
+void SkeletonEditor::_update_spatial_transform_gizmo() {
+ SpatialEditor::get_singleton()->clear_externals();
+ if (skeleton->get_selected_bone() >= 0) {
+ SpatialEditor::get_singleton()->append_to_externals(skeleton->get_global_transform() * skeleton->get_bone_global_pose(skeleton->get_selected_bone()));
+ }
+ SpatialEditor::get_singleton()->update_transform_gizmo();
+};
- if (p_node == skeleton) {
- skeleton = NULL;
- options->hide();
+void SkeletonEditor::_joint_tree_selection_changed() {
+ TreeItem *selected = joint_tree->get_selected();
+ const String path = selected->get_metadata(0);
+
+ if (path.begins_with("bones/")) {
+ const int b_idx = path.get_slicec('/', 1).to_int();
+ const String bone_path = "bones/" + itos(b_idx) + "/";
+
+ pose_editor->set_target(bone_path + "pose");
+ rest_editor->set_target(bone_path + "rest");
+ custom_pose_editor->set_target(bone_path + "custom_pose");
+
+ pose_editor->set_visible(true);
+ rest_editor->set_visible(true);
+ custom_pose_editor->set_visible(true);
+
+ skeleton->set_selected_bone(b_idx);
}
+
+ _update_properties();
}
-void SkeletonEditor::_bind_methods() {
- ClassDB::bind_method("_on_click_option", &SkeletonEditor::_on_click_option);
- ClassDB::bind_method("_node_removed", &SkeletonEditor::_node_removed);
+void SkeletonEditor::_joint_tree_rmb_select(const Vector2 &p_pos) {
+ skeleton->set_selected_bone(-1);
+ _update_spatial_transform_gizmo();
+}
+
+void SkeletonEditor::_update_properties() {
+ if (rest_editor)
+ rest_editor->_update_properties();
+ if (pose_editor)
+ pose_editor->_update_properties();
+ if (custom_pose_editor)
+ custom_pose_editor->_update_custom_pose_properties();
+ _update_spatial_transform_gizmo();
+}
+
+void SkeletonEditor::update_joint_tree() {
+ joint_tree->clear();
+
+ if (skeleton == nullptr)
+ return;
+
+ TreeItem *root = joint_tree->create_item();
+
+ Map<int, TreeItem *> items;
+
+ items.insert(-1, root);
+
+ const Vector<int> &joint_porder = skeleton->get_bone_process_order();
+
+ Ref<Texture> bone_icon = get_icon("Bone", "EditorIcons");
+
+ for (int i = 0; i < joint_porder.size(); ++i) {
+ const int b_idx = joint_porder[i];
+
+ const int p_idx = skeleton->get_bone_parent(b_idx);
+ TreeItem *p_item = items.find(p_idx)->get();
+
+ TreeItem *joint_item = joint_tree->create_item(p_item);
+ items.insert(b_idx, joint_item);
+
+ joint_item->set_text(0, skeleton->get_bone_name(b_idx));
+ joint_item->set_icon(0, bone_icon);
+ joint_item->set_selectable(0, true);
+ joint_item->set_metadata(0, "bones/" + itos(b_idx));
+ }
}
-SkeletonEditor::SkeletonEditor() {
- skeleton = NULL;
+void SkeletonEditor::update_editors() {
+}
+
+void SkeletonEditor::create_editors() {
+
+ set_h_size_flags(SIZE_EXPAND_FILL);
+ add_constant_override("separation", 0);
+
+ set_focus_mode(FOCUS_ALL);
+
+ // Create Top Menu Bar
+ separators[0] = memnew(VSeparator);
+ separators[1] = memnew(VSeparator);
+
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(separators[0]);
+
options = memnew(MenuButton);
SpatialEditor::get_singleton()->add_control_to_menu_panel(options);
-
options->set_text(TTR("Skeleton"));
options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Skeleton", "EditorIcons"));
-
+ options->get_popup()->add_item(TTR("Init pose"), MENU_OPTION_INIT_POSE);
+ options->get_popup()->add_item(TTR("Insert key of all bone poses"), MENU_OPTION_INSERT_KEYS);
+ options->get_popup()->add_item(TTR("Insert key of bone poses already exist track"), MENU_OPTION_INSERT_KEYS_EXISTED);
+ options->get_popup()->add_item(TTR("Apply current pose to rest"), MENU_OPTION_POSE_TO_REST);
options->get_popup()->add_item(TTR("Create physical skeleton"), MENU_OPTION_CREATE_PHYSICAL_SKELETON);
-
options->get_popup()->connect("id_pressed", this, "_on_click_option");
- options->hide();
+
+ Vector<Variant> button_binds;
+ button_binds.resize(1);
+
+ tool_button[TOOL_MODE_BONE_SELECT] = memnew(ToolButton);
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(tool_button[TOOL_MODE_BONE_SELECT]);
+ tool_button[TOOL_MODE_BONE_SELECT]->set_tooltip(TTR("Transform Bone Mode"));
+ tool_button[TOOL_MODE_BONE_SELECT]->set_toggle_mode(true);
+ tool_button[TOOL_MODE_BONE_SELECT]->set_flat(true);
+ button_binds.write[0] = MENU_TOOL_BONE_SELECT;
+ tool_button[TOOL_MODE_BONE_SELECT]->connect("pressed", this, "_menu_tool_item_pressed", button_binds);
+
+ tool_button[TOOL_MODE_BONE_MOVE] = memnew(ToolButton);
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(tool_button[TOOL_MODE_BONE_MOVE]);
+ tool_button[TOOL_MODE_BONE_MOVE]->set_tooltip(TTR("Move Bone Mode"));
+ tool_button[TOOL_MODE_BONE_MOVE]->set_toggle_mode(true);
+ tool_button[TOOL_MODE_BONE_MOVE]->set_flat(true);
+ button_binds.write[0] = MENU_TOOL_BONE_MOVE;
+ tool_button[TOOL_MODE_BONE_MOVE]->connect("pressed", this, "_menu_tool_item_pressed", button_binds);
+
+ tool_button[TOOL_MODE_BONE_ROTATE] = memnew(ToolButton);
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(tool_button[TOOL_MODE_BONE_ROTATE]);
+ tool_button[TOOL_MODE_BONE_ROTATE]->set_tooltip(TTR("Rotate Bone Mode"));
+ tool_button[TOOL_MODE_BONE_ROTATE]->set_toggle_mode(true);
+ tool_button[TOOL_MODE_BONE_ROTATE]->set_flat(true);
+ button_binds.write[0] = MENU_TOOL_BONE_ROTATE;
+ tool_button[TOOL_MODE_BONE_ROTATE]->connect("pressed", this, "_menu_tool_item_pressed", button_binds);
+
+ tool_button[TOOL_MODE_BONE_SCALE] = memnew(ToolButton);
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(tool_button[TOOL_MODE_BONE_SCALE]);
+ tool_button[TOOL_MODE_BONE_SCALE]->set_tooltip(TTR("Scale Bone Mode"));
+ tool_button[TOOL_MODE_BONE_SCALE]->set_toggle_mode(true);
+ tool_button[TOOL_MODE_BONE_SCALE]->set_flat(true);
+ button_binds.write[0] = MENU_TOOL_BONE_SCALE;
+ tool_button[TOOL_MODE_BONE_SCALE]->connect("pressed", this, "_menu_tool_item_pressed", button_binds);
+
+ tool_button[TOOL_MODE_BONE_NONE] = memnew(ToolButton);
+ button_binds.write[0] = MENU_TOOL_BONE_NONE;
+ tool_button[TOOL_MODE_BONE_NONE]->connect("pressed", this, "_menu_tool_item_pressed", button_binds);
+ SpatialEditor::get_singleton()->connect("change_tool_mode", this, "_menu_tool_item_pressed", button_binds);
+
+ tool_mode = TOOL_MODE_BONE_NONE;
+
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(separators[1]);
+
+ rest_mode_button = memnew(ToolButton);
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(rest_mode_button);
+ rest_mode_button->set_tooltip(TTR("Rest Mode\nNote: Bone poses are disabled during Rest Mode."));
+ rest_mode_button->set_toggle_mode(true);
+ rest_mode_button->set_flat(true);
+ rest_mode_button->connect("toggled", this, "rest_mode_toggled");
+
+ rest_mode = false;
+
+ set_keyable(AnimationPlayerEditor::singleton->get_track_editor()->has_keying());
+
+ if (skeleton) {
+ skeleton->add_child(pointsm);
+ pointsm->set_skeleton_path(NodePath(""));
+ skeleton->connect("pose_updated", this, "_draw_handles");
+ }
+
+ const Color section_color = get_color("prop_subsection", "Editor");
+
+ EditorInspectorSection *bones_section = memnew(EditorInspectorSection);
+ bones_section->setup("bones", "Bones", skeleton, section_color, true);
+ add_child(bones_section);
+ bones_section->unfold();
+
+ ScrollContainer *s_con = memnew(ScrollContainer);
+ s_con->set_h_size_flags(SIZE_EXPAND_FILL);
+ s_con->set_custom_minimum_size(Size2(1, 350) * EDSCALE);
+ bones_section->get_vbox()->add_child(s_con);
+
+ joint_tree = memnew(Tree);
+ joint_tree->set_columns(1);
+ joint_tree->set_focus_mode(Control::FocusMode::FOCUS_NONE);
+ joint_tree->set_select_mode(Tree::SELECT_SINGLE);
+ joint_tree->set_hide_root(true);
+ joint_tree->set_v_size_flags(SIZE_EXPAND_FILL);
+ joint_tree->set_h_size_flags(SIZE_EXPAND_FILL);
+ joint_tree->set_allow_rmb_select(true);
+ joint_tree->set_drag_forwarding(this);
+ s_con->add_child(joint_tree);
+
+ pose_editor = memnew(BoneTransformEditor(skeleton));
+ pose_editor->set_label(TTR("Bone Pose"));
+ pose_editor->set_keyable(AnimationPlayerEditor::singleton->get_track_editor()->has_keying());
+ // pose_editor->set_toggle_enabled(true);
+ pose_editor->set_visible(false);
+ add_child(pose_editor);
+
+ rest_editor = memnew(BoneTransformEditor(skeleton));
+ rest_editor->set_label(TTR("Bone Rest"));
+ rest_editor->set_visible(false);
+ add_child(rest_editor);
+
+ custom_pose_editor = memnew(BoneTransformEditor(skeleton));
+ custom_pose_editor->set_label(TTR("Bone Custom Pose"));
+ custom_pose_editor->set_visible(false);
+ add_child(custom_pose_editor);
+
+ skeleton->set_selected_bone(-1);
}
-SkeletonEditor::~SkeletonEditor() {}
+void SkeletonEditor::_notification(int p_what) {
+
+ switch (p_what) {
+ case NOTIFICATION_READY: {
+ tool_button[TOOL_MODE_BONE_SELECT]->set_icon(get_icon("ToolBoneSelect", "EditorIcons"));
+ tool_button[TOOL_MODE_BONE_MOVE]->set_icon(get_icon("ToolBoneMove", "EditorIcons"));
+ tool_button[TOOL_MODE_BONE_ROTATE]->set_icon(get_icon("ToolBoneRotate", "EditorIcons"));
+ tool_button[TOOL_MODE_BONE_SCALE]->set_icon(get_icon("ToolBoneScale", "EditorIcons"));
+ rest_mode_button->set_icon(get_icon("ToolBoneRest", "EditorIcons"));
+ } break;
+ case NOTIFICATION_ENTER_TREE: {
+ create_editors();
+ update_joint_tree();
+ update_editors();
+
+ get_tree()->connect("node_removed", this, "_node_removed", Vector<Variant>(), Object::CONNECT_ONESHOT);
+ joint_tree->connect("item_selected", this, "_joint_tree_selection_changed");
+ joint_tree->connect("item_rmb_selected", this, "_joint_tree_rmb_select");
+
+#ifdef TOOLS_ENABLED
+ skeleton->connect("pose_updated", this, "_update_properties");
+#endif // TOOLS_ENABLED
-void SkeletonEditorPlugin::edit(Object *p_object) {
- skeleton_editor->edit(Object::cast_to<Skeleton>(p_object));
+ break;
+ }
+ }
}
-bool SkeletonEditorPlugin::handles(Object *p_object) const {
- return p_object->is_class("Skeleton");
+void SkeletonEditor::_node_removed(Node *p_node) {
+ if (skeleton && p_node == skeleton) {
+ skeleton = nullptr;
+ }
+}
+
+void SkeletonEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_node_removed"), &SkeletonEditor::_node_removed);
+ ClassDB::bind_method(D_METHOD("_joint_tree_selection_changed"), &SkeletonEditor::_joint_tree_selection_changed);
+ ClassDB::bind_method(D_METHOD("_joint_tree_rmb_select"), &SkeletonEditor::_joint_tree_rmb_select);
+ ClassDB::bind_method(D_METHOD("_update_properties"), &SkeletonEditor::_update_properties);
+ ClassDB::bind_method(D_METHOD("_on_click_option"), &SkeletonEditor::_on_click_option);
+ ClassDB::bind_method(D_METHOD("_menu_tool_item_pressed"), &SkeletonEditor::_menu_tool_item_pressed);
+ ClassDB::bind_method(D_METHOD("rest_mode_toggled"), &SkeletonEditor::rest_mode_toggled);
+ ClassDB::bind_method(D_METHOD("set_rest_mode_toggled"), &SkeletonEditor::set_rest_mode_toggled);
+
+ ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &SkeletonEditor::get_drag_data_fw);
+ ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &SkeletonEditor::can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("drop_data_fw"), &SkeletonEditor::drop_data_fw);
+ ClassDB::bind_method(D_METHOD("move_skeleton_bone"), &SkeletonEditor::move_skeleton_bone);
+
+ ClassDB::bind_method(D_METHOD("_draw_handles"), &SkeletonEditor::_draw_handles);
+}
+
+void SkeletonEditor::_menu_tool_item_pressed(int p_option) {
+
+ if (p_option != TOOL_MODE_BONE_NONE && !SpatialEditor::get_singleton()->is_tool_external()) {
+ SpatialEditor::get_singleton()->set_tool_mode(SpatialEditor::TOOL_MODE_EXTERNAL);
+ }
+ for (int i = 0; i < TOOL_MODE_BONE_MAX; i++)
+ tool_button[i]->set_pressed(i == p_option);
+ tool_mode = (ToolMode)p_option;
+ if (skeleton) {
+ if (p_option == TOOL_MODE_BONE_NONE) {
+ _hide_handles();
+ } else {
+ _draw_handles();
+ if (skeleton->get_selected_bone() >= 0) {
+ SpatialEditor::get_singleton()->clear_externals();
+ SpatialEditor::get_singleton()->append_to_externals(skeleton->get_global_transform() * skeleton->get_bone_global_pose(skeleton->get_selected_bone()));
+ }
+ }
+ }
+
+ switch (p_option) {
+ case TOOL_MODE_BONE_SELECT: {
+ SpatialEditor::get_singleton()->set_external_tool_mode(SpatialEditor::EX_TOOL_MODE_SELECT);
+ } break;
+ case TOOL_MODE_BONE_MOVE: {
+ SpatialEditor::get_singleton()->set_external_tool_mode(SpatialEditor::EX_TOOL_MODE_MOVE);
+ } break;
+ case TOOL_MODE_BONE_ROTATE: {
+ SpatialEditor::get_singleton()->set_external_tool_mode(SpatialEditor::EX_TOOL_MODE_ROTATE);
+ } break;
+ case TOOL_MODE_BONE_SCALE: {
+ SpatialEditor::get_singleton()->set_external_tool_mode(SpatialEditor::EX_TOOL_MODE_SCALE);
+ } break;
+ case TOOL_MODE_BONE_NONE:
+ break;
+ }
+
+ _update_spatial_transform_gizmo();
+}
+
+void SkeletonEditor::rest_mode_toggled(const bool pressed) {
+ bool before_val = rest_mode;
+
+ // Prevent that bone pose will be undo during rest mode.
+ // However SkeletonEditor will be memdeleted,
+ // so it need to record in SpatialEditor with calling method in
+ // EditorInspectorPluginSkeleton and it will not be memdeleted.
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Toggled Rest Mode"));
+ set_rest_mode_toggled(pressed);
+ ur->add_undo_method(editor_plugin, "set_rest_mode_toggled", before_val);
+ ur->add_do_method(editor_plugin, "set_rest_mode_toggled", pressed);
+ ur->commit_action();
+}
+
+void SkeletonEditor::set_rest_mode_toggled(const bool pressed) {
+ rest_mode_button->disconnect("toggled", this, "rest_mode_toggled");
+ rest_mode_button->set_pressed(pressed);
+ rest_mode_button->connect("toggled", this, "rest_mode_toggled");
+
+ rest_mode = pressed;
+ const int bone_len = skeleton->get_bone_count();
+ for (int i = 0; i < bone_len; i++) {
+ skeleton->set_bone_enabled(i, !rest_mode);
+ }
+ if (pose_editor) {
+ pose_editor->set_read_only(rest_mode);
+ }
+ if (custom_pose_editor) {
+ custom_pose_editor->set_read_only(rest_mode);
+ }
+ set_keyable(AnimationPlayerEditor::singleton->get_track_editor()->has_keying() && !rest_mode);
+}
+
+SkeletonEditor::SkeletonEditor(EditorInspectorPluginSkeleton *e_plugin, EditorNode *p_editor, Skeleton *p_skeleton) :
+ editor(p_editor),
+ editor_plugin(e_plugin),
+ skeleton(p_skeleton) {
+ handle_material = Ref<ShaderMaterial>(memnew(ShaderMaterial));
+ handle_shader = Ref<Shader>(memnew(Shader));
+ handle_shader->set_code(" \
+ shader_type spatial; \
+ render_mode unshaded; \
+ uniform vec4 albedo : hint_color = vec4(1,1,1,1); \
+ uniform sampler2D texture_albedo : hint_albedo; \
+ uniform float point_size : hint_range(0,128) = 32; \
+ void vertex() { \
+ if (!OUTPUT_IS_SRGB) { \
+ COLOR.rgb = mix( pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb* (1.0 / 12.92), lessThan(COLOR.rgb,vec3(0.04045)) ); \
+ } \
+ POINT_SIZE=point_size; \
+ VERTEX = VERTEX; \
+ POSITION=PROJECTION_MATRIX*INV_CAMERA_MATRIX*WORLD_MATRIX*vec4(VERTEX.xyz,1.0); \
+ POSITION.z = mix(POSITION.z, -POSITION.w, 0.999); \
+ } \
+ void fragment() { \
+ vec4 albedo_tex = texture(texture_albedo,POINT_COORD); \
+ if (albedo.a * albedo_tex.a < 0.5) { discard; } \
+ vec3 col = albedo_tex.rgb + COLOR.rgb; \
+ col = vec3(min(col.r,1.0),min(col.g,1.0),min(col.b,1.0)); \
+ ALBEDO = albedo.rgb * col; \
+ } \
+ ");
+ handle_material->set_shader(handle_shader);
+ // handle_material->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, true);
+ handle_material->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN);
+ // handle_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ // handle_material->set_flag(SpatialMaterial::FLAG_USE_POINT_SIZE, true);
+ // handle_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ Ref<Texture> handle = editor->get_gui_base()->get_icon("EditorBoneHandle", "EditorIcons");
+ handle_material->set_shader_param("point_size", handle->get_width());
+ handle_material->set_shader_param("texture_albedo", handle);
+ //handle_material->set_texture(SpatialMaterial::TEXTURE_ALBEDO, handle);
+
+ pointsm = memnew(MeshInstance);
+ am.instance();
+ pointsm->set_mesh(am);
+ pointsm->set_transform(Transform(Basis(), Vector3(0, 0, 0.00001)));
+}
+
+SkeletonEditor::~SkeletonEditor() {
+ set_rest_mode_toggled(false);
+ SpatialEditor::get_singleton()->disconnect("change_tool_mode", this, "_menu_tool_item_pressed");
+ if (skeleton) {
+ pointsm->get_parent()->remove_child(pointsm);
+ skeleton->set_selected_bone(-1);
+ skeleton->disconnect("pose_updated", this, "_draw_handles");
+ memdelete(pointsm);
+ }
+ for (int i = 0; i < 2; i++) {
+ if (separators[i]) {
+ SpatialEditor::get_singleton()->remove_control_from_menu_panel(separators[i]);
+ memdelete(separators[i]);
+ }
+ }
+ if (options) {
+ SpatialEditor::get_singleton()->remove_control_from_menu_panel(options);
+ memdelete(options);
+ }
+ SpatialEditor::get_singleton()->remove_control_from_menu_panel(tool_button[TOOL_MODE_BONE_SELECT]);
+ SpatialEditor::get_singleton()->remove_control_from_menu_panel(tool_button[TOOL_MODE_BONE_MOVE]);
+ SpatialEditor::get_singleton()->remove_control_from_menu_panel(tool_button[TOOL_MODE_BONE_ROTATE]);
+ SpatialEditor::get_singleton()->remove_control_from_menu_panel(tool_button[TOOL_MODE_BONE_SCALE]);
+ for (int i = 0; i < TOOL_MODE_BONE_MAX; i++) {
+ if (tool_button[i]) {
+ memdelete(tool_button[i]);
+ }
+ }
+ SpatialEditor::get_singleton()->remove_control_from_menu_panel(rest_mode_button);
+ if (rest_mode_button) {
+ memdelete(rest_mode_button);
+ }
+ if (SpatialEditor::get_singleton()->is_tool_external()) {
+ SpatialEditor::get_singleton()->set_tool_mode(SpatialEditor::TOOL_MODE_SELECT);
+ SpatialEditor::get_singleton()->set_external_tool_mode(SpatialEditor::EX_TOOL_MODE_SELECT);
+ }
+
+}
+
+void SkeletonEditor::_hide_handles() {
+ if (!skeleton)
+ return;
+
+ pointsm->hide();
+}
+
+void SkeletonEditor::_draw_handles() {
+
+ if (!skeleton || tool_mode == TOOL_MODE_BONE_NONE)
+ return;
+
+ while (am->get_surface_count()) {
+ am->surface_remove(0);
+ }
+
+ pointsm->show();
+
+ Array a;
+ a.resize(Mesh::ARRAY_MAX);
+ PoolVector<Vector3> va;
+ PoolVector<Color> ca;
+ {
+ const int bone_len = skeleton->get_bone_count();
+ va.resize(bone_len);
+ ca.resize(bone_len);
+ PoolVector<Vector3>::Write vaw = va.write();
+ PoolVector<Color>::Write caw = ca.write();
+
+ for (int i = 0; i < bone_len; i++) {
+ Vector3 point = skeleton->get_bone_global_pose(i).origin;
+ vaw[i] = point;
+ Color c;
+ if (i == skeleton->get_selected_bone()) {
+ c = Color(1,1,0);
+ } else {
+ c = Color(0,0,1);
+ }
+ caw[i] = c;
+ }
+
+ }
+ a[Mesh::ARRAY_VERTEX] = va;
+ a[Mesh::ARRAY_COLOR] = ca;
+ am->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, a);
+ am->surface_set_material(0, handle_material);
+}
+
+bool SkeletonEditor::forward_spatial_gui_input(int p_index, Camera *p_camera, const Ref<InputEvent> &p_event) {
+
+ if (!skeleton || tool_mode == TOOL_MODE_BONE_NONE)
+ return false;
+
+ SpatialEditor *se = SpatialEditor::get_singleton();
+ SpatialEditorViewport *sev = se->get_editor_viewport(p_index);
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid()) {
+
+ Transform gt = skeleton->get_global_transform();
+ Vector3 ray_from = p_camera->get_global_transform().origin;
+ Vector2 gpoint = mb->get_position();
+ real_t grab_threshold = 4 * EDSCALE;
+
+ switch (mb->get_button_index()) {
+ case BUTTON_LEFT: {
+ if (mb->is_pressed()) {
+
+ _edit.mouse_pos = mb->get_position();
+ _edit.snap = se->is_snap_enabled();
+ _edit.mode = SpatialEditorViewport::TRANSFORM_NONE;
+
+ // check gizmo
+ if (_gizmo_select(p_index, _edit.mouse_pos)) {
+ return true;
+ }
+
+ // select bone
+ int closest_idx = -1;
+ real_t closest_dist = 1e10;
+ const int bone_len = skeleton->get_bone_count();
+ for (int i = 0; i < bone_len; i++) {
+
+ Vector3 joint_pos_3d = gt.xform(skeleton->get_bone_global_pose(i).origin);
+ Vector2 joint_pos_2d = p_camera->unproject_position(joint_pos_3d);
+ real_t dist_3d = ray_from.distance_to(joint_pos_3d);
+ real_t dist_2d = gpoint.distance_to(joint_pos_2d);
+ if (dist_2d < grab_threshold && dist_3d < closest_dist) {
+ closest_dist = dist_3d;
+ closest_idx = i;
+ }
+ }
+ if (closest_idx >= 0) {
+ TreeItem *ti = _find(joint_tree->get_root(), "bones/" + itos(closest_idx));
+ if (ti) {
+ // make visible when it's collapsed
+ TreeItem *node = ti->get_parent();
+ while (node && node != joint_tree->get_root()) {
+ node->set_collapsed(false);
+ node = node->get_parent();
+ }
+ ti->select(0);
+ joint_tree->scroll_to_item(ti);
+ }
+ } else {
+ skeleton->set_selected_bone(-1);
+ joint_tree->deselect_all();
+ }
+
+ } else {
+ if (_edit.mode != SpatialEditorViewport::TRANSFORM_NONE) {
+ if (skeleton && (skeleton->get_selected_bone() >= 0)) {
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
+ if (rest_mode) {
+ ur->add_do_method(skeleton, "set_bone_rest", skeleton->get_selected_bone(), skeleton->get_bone_rest(skeleton->get_selected_bone()));
+ ur->add_undo_method(skeleton, "set_bone_rest", skeleton->get_selected_bone(), original_local);
+ } else {
+ ur->add_do_method(skeleton, "set_bone_pose", skeleton->get_selected_bone(), skeleton->get_bone_pose(skeleton->get_selected_bone()));
+ ur->add_undo_method(skeleton, "set_bone_pose", skeleton->get_selected_bone(), original_local);
+ }
+ ur->commit_action();
+ _edit.mode = SpatialEditorViewport::TRANSFORM_NONE;
+ }
+ }
+ }
+ return true;
+ } break;
+ default:
+ break;
+ }
+
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+ _edit.mouse_pos = mm->get_position();
+ if (!(mm->get_button_mask() & 1)) {
+ _gizmo_select(p_index, _edit.mouse_pos, true);
+ }
+ if (mm->get_button_mask() & BUTTON_MASK_LEFT) {
+ if (_edit.mode == SpatialEditorViewport::TRANSFORM_NONE)
+ return true;
+
+ Vector3 ray_pos = sev->get_ray_pos(mm->get_position());
+ Vector3 ray = sev->get_ray(mm->get_position());
+ float snap = EDITOR_GET("interface/inspector/default_float_step");
+
+ switch (_edit.mode) {
+
+ case SpatialEditorViewport::TRANSFORM_SCALE: {
+
+ Vector3 motion_mask;
+ Plane plane;
+ bool plane_mv = false;
+
+ switch (_edit.plane) {
+ case SpatialEditorViewport::TRANSFORM_VIEW:
+ motion_mask = Vector3(0, 0, 0);
+ plane = Plane(_edit.center, sev->get_camera_normal());
+ break;
+ case SpatialEditorViewport::TRANSFORM_X_AXIS:
+ motion_mask = se->get_gizmo_transform().basis.get_axis(0);
+ plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(sev->get_camera_normal())).normalized());
+ break;
+ case SpatialEditorViewport::TRANSFORM_Y_AXIS:
+ motion_mask = se->get_gizmo_transform().basis.get_axis(1);
+ plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(sev->get_camera_normal())).normalized());
+ break;
+ case SpatialEditorViewport::TRANSFORM_Z_AXIS:
+ motion_mask = se->get_gizmo_transform().basis.get_axis(2);
+ plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(sev->get_camera_normal())).normalized());
+ break;
+ case SpatialEditorViewport::TRANSFORM_YZ:
+ motion_mask = se->get_gizmo_transform().basis.get_axis(2) + se->get_gizmo_transform().basis.get_axis(1);
+ plane = Plane(_edit.center, se->get_gizmo_transform().basis.get_axis(0));
+ plane_mv = true;
+ break;
+ case SpatialEditorViewport::TRANSFORM_XZ:
+ motion_mask = se->get_gizmo_transform().basis.get_axis(2) + se->get_gizmo_transform().basis.get_axis(0);
+ plane = Plane(_edit.center, se->get_gizmo_transform().basis.get_axis(1));
+ plane_mv = true;
+ break;
+ case SpatialEditorViewport::TRANSFORM_XY:
+ motion_mask = se->get_gizmo_transform().basis.get_axis(0) + se->get_gizmo_transform().basis.get_axis(1);
+ plane = Plane(_edit.center, se->get_gizmo_transform().basis.get_axis(2));
+ plane_mv = true;
+ break;
+ }
+
+ Vector3 intersection;
+ if (!plane.intersects_ray(ray_pos, ray, &intersection))
+ break;
+
+ Vector3 click;
+ if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click))
+ break;
+
+ Vector3 motion = intersection - click;
+ if (_edit.plane != SpatialEditorViewport::TRANSFORM_VIEW) {
+ motion = motion_mask.dot(motion) * motion_mask;
+ } else {
+ float center_click_dist = click.distance_to(_edit.center);
+ float center_inters_dist = intersection.distance_to(_edit.center);
+ if (center_click_dist == 0)
+ break;
+
+ float scale = center_inters_dist - center_click_dist;
+ motion = Vector3(scale, scale, scale);
+ }
+
+ bool local_coords = (se->are_local_coords_enabled() && _edit.plane != SpatialEditorViewport::TRANSFORM_VIEW);
+
+ if (_edit.snap || se->is_snap_enabled()) {
+ snap = se->get_scale_snap() / 100;
+ }
+
+ Transform t;
+
+ if (local_coords) {
+ Basis g = original_global.basis;
+ motion = g.inverse().xform(motion);
+ if (_edit.snap || se->is_snap_enabled()) {
+ motion.snap(Vector3(snap, snap, snap));
+ }
+ Vector3 local_scale = original_local.basis.get_scale() * (motion + Vector3(1, 1, 1));
+ // Prevent scaling to 0 it would break the gizmo
+ Basis check = original_local.basis;
+ check.scale(local_scale);
+ if (check.determinant() != 0) {
+ t = original_local;
+ t.basis = t.basis.scaled_local(motion + Vector3(1, 1, 1));
+ }
+ } else {
+ if (_edit.snap || se->is_snap_enabled()) {
+ motion.snap(Vector3(snap, snap, snap));
+ }
+ t = original_local;
+ Transform r;
+ r.basis.scale(motion + Vector3(1, 1, 1));
+ Basis base = original_to_local.get_basis().orthonormalized().inverse();
+ t.basis = base * (r.get_basis() * (base.inverse() * original_local.get_basis()));
+ }
+
+ // Apply scale
+ if (rest_mode) {
+ skeleton->set_bone_rest(skeleton->get_selected_bone(), t);
+ } else {
+ skeleton->set_bone_pose(skeleton->get_selected_bone(), t);
+ }
+
+ sev->update_surface();
+
+ } break;
+
+ case SpatialEditorViewport::TRANSFORM_TRANSLATE: {
+
+ Vector3 motion_mask;
+ Plane plane;
+ bool plane_mv = false;
+
+ switch (_edit.plane) {
+ case SpatialEditorViewport::TRANSFORM_VIEW:
+ plane = Plane(_edit.center, sev->get_camera_normal());
+ break;
+ case SpatialEditorViewport::TRANSFORM_X_AXIS:
+ motion_mask = se->get_gizmo_transform().basis.get_axis(0);
+ plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(sev->get_camera_normal())).normalized());
+ break;
+ case SpatialEditorViewport::TRANSFORM_Y_AXIS:
+ motion_mask = se->get_gizmo_transform().basis.get_axis(1);
+ plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(sev->get_camera_normal())).normalized());
+ break;
+ case SpatialEditorViewport::TRANSFORM_Z_AXIS:
+ motion_mask = se->get_gizmo_transform().basis.get_axis(2);
+ plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(sev->get_camera_normal())).normalized());
+ break;
+ case SpatialEditorViewport::TRANSFORM_YZ:
+ plane = Plane(_edit.center, se->get_gizmo_transform().basis.get_axis(0));
+ plane_mv = true;
+ break;
+ case SpatialEditorViewport::TRANSFORM_XZ:
+ plane = Plane(_edit.center, se->get_gizmo_transform().basis.get_axis(1));
+ plane_mv = true;
+ break;
+ case SpatialEditorViewport::TRANSFORM_XY:
+ plane = Plane(_edit.center, se->get_gizmo_transform().basis.get_axis(2));
+ plane_mv = true;
+ break;
+ }
+
+ Vector3 intersection;
+ if (!plane.intersects_ray(ray_pos, ray, &intersection))
+ break;
+
+ Vector3 click;
+ if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click))
+ break;
+
+ Vector3 motion = intersection - click;
+ if (_edit.plane != SpatialEditorViewport::TRANSFORM_VIEW) {
+ if (!plane_mv) {
+ motion = motion_mask.dot(motion) * motion_mask;
+ }
+ }
+
+ if (_edit.snap || se->is_snap_enabled()) {
+ snap = se->get_translate_snap();
+ }
+
+ motion = original_to_local.basis.inverse().xform(motion);
+ if (_edit.snap || se->is_snap_enabled()) {
+ motion.snap(Vector3(snap, snap, snap));
+ }
+
+ Transform t;
+ // Apply translation
+ t = original_local;
+ t.origin += motion;
+
+ if (rest_mode) {
+ skeleton->set_bone_rest(skeleton->get_selected_bone(), t);
+ } else {
+ skeleton->set_bone_pose(skeleton->get_selected_bone(), t);
+ }
+
+ sev->update_surface();
+
+ } break;
+
+ case SpatialEditorViewport::TRANSFORM_ROTATE: {
+
+ Plane plane;
+ Vector3 axis;
+
+ switch (_edit.plane) {
+ case SpatialEditorViewport::TRANSFORM_VIEW:
+ plane = Plane(_edit.center, sev->get_camera_normal());
+ break;
+ case SpatialEditorViewport::TRANSFORM_X_AXIS:
+ plane = Plane(_edit.center, se->get_gizmo_transform().basis.get_axis(0));
+ axis = Vector3(1, 0, 0);
+ break;
+ case SpatialEditorViewport::TRANSFORM_Y_AXIS:
+ plane = Plane(_edit.center, se->get_gizmo_transform().basis.get_axis(1));
+ axis = Vector3(0, 1, 0);
+ break;
+ case SpatialEditorViewport::TRANSFORM_Z_AXIS:
+ plane = Plane(_edit.center, se->get_gizmo_transform().basis.get_axis(2));
+ axis = Vector3(0, 0, 1);
+ break;
+ case SpatialEditorViewport::TRANSFORM_YZ:
+ case SpatialEditorViewport::TRANSFORM_XZ:
+ case SpatialEditorViewport::TRANSFORM_XY:
+ break;
+ }
+
+ Vector3 intersection;
+ if (!plane.intersects_ray(ray_pos, ray, &intersection))
+ break;
+
+ Vector3 click;
+ if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click))
+ break;
+
+ Vector3 y_axis = (click - _edit.center).normalized();
+ Vector3 x_axis = plane.normal.cross(y_axis).normalized();
+
+ float angle = Math::atan2(x_axis.dot(intersection - _edit.center), y_axis.dot(intersection - _edit.center));
+
+ if (_edit.snap || se->is_snap_enabled()) {
+ snap = se->get_rotate_snap();
+ }
+ angle = Math::rad2deg(angle) + snap * 0.5; //else it won't reach +180
+ angle -= Math::fmod(angle, snap);
+ // set_message(vformat(TTR("Rotating %s degrees."), String::num(angle, snap_step_decimals)));
+ angle = Math::deg2rad(angle);
+
+ bool local_coords = (se->are_local_coords_enabled() && _edit.plane != SpatialEditorViewport::TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW
+
+ Transform t;
+
+ if (local_coords) {
+ Basis rot = Basis(axis, angle);
+ t.basis = original_local.get_basis().orthonormalized() * rot;
+ t.basis = t.basis.scaled_local(original_local.basis.get_scale());
+ t.origin = original_local.origin;
+ } else {
+ Transform r;
+ Basis base = original_to_local.get_basis().orthonormalized().inverse();
+ r.basis.rotate(plane.normal, angle);
+ t.basis = base * r.get_basis() * base.inverse() * original_local.get_basis();
+ // t.basis = t.basis.scaled(original_local.basis.get_scale());
+ t.origin = original_local.origin;
+ }
+
+ // Apply rotation
+ if (rest_mode) {
+ skeleton->set_bone_rest(skeleton->get_selected_bone(), t);
+ } else {
+ skeleton->set_bone_pose(skeleton->get_selected_bone(), t);
+ }
+
+ sev->update_surface();
+
+ } break;
+ default: {
+ }
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void EditorInspectorPluginSkeleton::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_rest_mode_toggled"), &EditorInspectorPluginSkeleton::set_rest_mode_toggled);
+}
+
+bool EditorInspectorPluginSkeleton::can_handle(Object *p_object) {
+ return Object::cast_to<Skeleton>(p_object) != nullptr;
}
-void SkeletonEditorPlugin::make_visible(bool p_visible) {
- if (p_visible) {
- skeleton_editor->options->show();
- } else {
+void EditorInspectorPluginSkeleton::parse_begin(Object *p_object) {
+ Skeleton *skeleton = Object::cast_to<Skeleton>(p_object);
+ ERR_FAIL_COND(!skeleton);
- skeleton_editor->options->hide();
- skeleton_editor->edit(NULL);
+ skel_editor = memnew(SkeletonEditor(this, editor, skeleton));
+ add_custom_control(skel_editor);
+}
+
+void EditorInspectorPluginSkeleton::set_rest_mode_toggled(const bool p_pressed) {
+ if (SpatialEditor::get_singleton()->get_selected()->get_class() == "Skeleton" && skel_editor) {
+ skel_editor->set_rest_mode_toggled(p_pressed);
}
}
SkeletonEditorPlugin::SkeletonEditorPlugin(EditorNode *p_node) {
editor = p_node;
- skeleton_editor = memnew(SkeletonEditor);
- editor->get_viewport()->add_child(skeleton_editor);
+
+ skeleton_plugin = memnew(EditorInspectorPluginSkeleton);
+ skeleton_plugin->editor = editor;
+
+ EditorInspector::add_inspector_plugin(skeleton_plugin);
+}
+
+bool SkeletonEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("Skeleton");
}
-SkeletonEditorPlugin::~SkeletonEditorPlugin() {}
+void SkeletonEditor::_compute_edit(int p_index, const Point2 &p_point) {
+
+ SpatialEditor *se = SpatialEditor::get_singleton();
+ SpatialEditorViewport *sev = se->get_editor_viewport(p_index);
+
+ _edit.click_ray = sev->get_ray(Vector2(p_point.x, p_point.y));
+ _edit.click_ray_pos = sev->get_ray_pos(Vector2(p_point.x, p_point.y));
+ _edit.plane = SpatialEditorViewport::TRANSFORM_VIEW;
+ _update_spatial_transform_gizmo();
+ _edit.center = se->get_gizmo_transform().origin;
+
+ if (skeleton->get_selected_bone() != -1) {
+ original_global = skeleton->get_global_transform() * skeleton->get_bone_global_pose(skeleton->get_selected_bone());
+ if (rest_mode) {
+ original_local = skeleton->get_bone_rest(skeleton->get_selected_bone());
+ } else {
+ original_local = skeleton->get_bone_pose(skeleton->get_selected_bone());
+ }
+ original_to_local = skeleton->get_global_transform();
+ int parent_idx = skeleton->get_bone_parent(skeleton->get_selected_bone());
+ if (parent_idx >= 0) {
+ original_to_local = original_to_local * skeleton->get_bone_global_pose(parent_idx);
+ }
+ if (!rest_mode) {
+ original_to_local = original_to_local * skeleton->get_bone_rest(skeleton->get_selected_bone()) * skeleton->get_bone_custom_pose(skeleton->get_selected_bone());
+ }
+ }
+}
+
+bool SkeletonEditor::_gizmo_select(int p_index, const Vector2 &p_screenpos, bool p_highlight_only) {
+
+ SpatialEditor *se = SpatialEditor::get_singleton();
+ SpatialEditorViewport *sev = se->get_editor_viewport(p_index);
+
+ if (!se->is_gizmo_visible())
+ return false;
+ if (skeleton->get_selected_bone() == -1) {
+ if (p_highlight_only)
+ se->select_gizmo_highlight_axis(-1);
+ return false;
+ }
+
+ Vector3 ray_pos = sev->get_ray_pos(Vector2(p_screenpos.x, p_screenpos.y));
+ Vector3 ray = sev->get_ray(Vector2(p_screenpos.x, p_screenpos.y));
+
+ Transform gt = se->get_gizmo_transform();
+ float gs = sev->get_gizmo_scale();
+
+ if (se->get_external_tool_mode() == SpatialEditor::EX_TOOL_MODE_SELECT || se->get_external_tool_mode() == SpatialEditor::EX_TOOL_MODE_MOVE) {
+
+ int col_axis = -1;
+ float col_d = 1e20;
+
+ for (int i = 0; i < 3; i++) {
+
+ Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i) * gs * (GIZMO_ARROW_OFFSET + (GIZMO_ARROW_SIZE * 0.5));
+ float grabber_radius = gs * GIZMO_ARROW_SIZE;
+
+ Vector3 r;
+
+ if (Geometry::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) {
+ float d = r.distance_to(ray_pos);
+ if (d < col_d) {
+ col_d = d;
+ col_axis = i;
+ }
+ }
+ }
+
+ bool is_plane_translate = false;
+ // plane select
+ if (col_axis == -1) {
+ col_d = 1e20;
+
+ for (int i = 0; i < 3; i++) {
+
+ Vector3 ivec2 = gt.basis.get_axis((i + 1) % 3).normalized();
+ Vector3 ivec3 = gt.basis.get_axis((i + 2) % 3).normalized();
+
+ Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gs * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST);
+
+ Vector3 r;
+ Plane plane(gt.origin, gt.basis.get_axis(i).normalized());
+
+ if (plane.intersects_ray(ray_pos, ray, &r)) {
+
+ float dist = r.distance_to(grabber_pos);
+ if (dist < (gs * GIZMO_PLANE_SIZE)) {
+
+ float d = ray_pos.distance_to(r);
+ if (d < col_d) {
+ col_d = d;
+ col_axis = i;
+
+ is_plane_translate = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (col_axis != -1) {
+
+ if (p_highlight_only) {
+
+ se->select_gizmo_highlight_axis(col_axis + (is_plane_translate ? 6 : 0));
+
+ } else {
+ //handle plane translate
+ _edit.mode = SpatialEditorViewport::TRANSFORM_TRANSLATE;
+ _compute_edit(p_index, Point2(p_screenpos.x, p_screenpos.y));
+ _edit.plane = SpatialEditorViewport::TransformPlane(SpatialEditorViewport::TRANSFORM_X_AXIS + col_axis + (is_plane_translate ? 3 : 0));
+ }
+ return true;
+ }
+ }
+
+ if (se->get_external_tool_mode() == SpatialEditor::EX_TOOL_MODE_SELECT || se->get_external_tool_mode() == SpatialEditor::EX_TOOL_MODE_ROTATE) {
+
+ int col_axis = -1;
+ float col_d = 1e20;
+
+ for (int i = 0; i < 3; i++) {
+
+ Plane plane(gt.origin, gt.basis.get_axis(i).normalized());
+ Vector3 r;
+ if (!plane.intersects_ray(ray_pos, ray, &r))
+ continue;
+
+ float dist = r.distance_to(gt.origin);
+
+ if (dist > gs * (GIZMO_CIRCLE_SIZE - GIZMO_RING_HALF_WIDTH) && dist < gs * (GIZMO_CIRCLE_SIZE + GIZMO_RING_HALF_WIDTH)) {
+
+ float d = ray_pos.distance_to(r);
+ if (d < col_d) {
+ col_d = d;
+ col_axis = i;
+ }
+ }
+ }
+
+ if (col_axis != -1) {
+
+ if (p_highlight_only) {
+
+ se->select_gizmo_highlight_axis(col_axis + 3);
+ } else {
+ //handle rotate
+ _edit.mode = SpatialEditorViewport::TRANSFORM_ROTATE;
+ _compute_edit(p_index, Point2(p_screenpos.x, p_screenpos.y));
+ _edit.plane = SpatialEditorViewport::TransformPlane(SpatialEditorViewport::TRANSFORM_X_AXIS + col_axis);
+ }
+ return true;
+ }
+ }
+
+ if (se->get_external_tool_mode() == SpatialEditor::EX_TOOL_MODE_SCALE) {
+
+ int col_axis = -1;
+ float col_d = 1e20;
+
+ for (int i = 0; i < 3; i++) {
+
+ Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i) * gs * GIZMO_SCALE_OFFSET;
+ float grabber_radius = gs * GIZMO_ARROW_SIZE;
+
+ Vector3 r;
+
+ if (Geometry::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) {
+ float d = r.distance_to(ray_pos);
+ if (d < col_d) {
+ col_d = d;
+ col_axis = i;
+ }
+ }
+ }
+
+ bool is_plane_scale = false;
+ // plane select
+ if (col_axis == -1) {
+ col_d = 1e20;
+
+ for (int i = 0; i < 3; i++) {
+
+ Vector3 ivec2 = gt.basis.get_axis((i + 1) % 3).normalized();
+ Vector3 ivec3 = gt.basis.get_axis((i + 2) % 3).normalized();
+
+ Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gs * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST);
+
+ Vector3 r;
+ Plane plane(gt.origin, gt.basis.get_axis(i).normalized());
+
+ if (plane.intersects_ray(ray_pos, ray, &r)) {
+
+ float dist = r.distance_to(grabber_pos);
+ if (dist < (gs * GIZMO_PLANE_SIZE)) {
+
+ float d = ray_pos.distance_to(r);
+ if (d < col_d) {
+ col_d = d;
+ col_axis = i;
+
+ is_plane_scale = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (col_axis != -1) {
+
+ if (p_highlight_only) {
+
+ se->select_gizmo_highlight_axis(col_axis + (is_plane_scale ? 12 : 9));
+
+ } else {
+ //handle scale
+ _edit.mode = SpatialEditorViewport::TRANSFORM_SCALE;
+ _compute_edit(p_index, Point2(p_screenpos.x, p_screenpos.y));
+ _edit.plane = SpatialEditorViewport::TransformPlane(SpatialEditorViewport::TRANSFORM_X_AXIS + col_axis + (is_plane_scale ? 3 : 0));
+ }
+ return true;
+ }
+ }
+
+ if (p_highlight_only)
+ se->select_gizmo_highlight_axis(-1);
+
+ return false;
+}
+
+TreeItem *SkeletonEditor::_find(TreeItem *p_node, const NodePath &p_path) {
+ if (!p_node) {
+ return NULL;
+ }
+
+ NodePath np = p_node->get_metadata(0);
+ if (np == p_path) {
+ return p_node;
+ }
+
+ TreeItem *children = p_node->get_children();
+ while (children) {
+ TreeItem *n = _find(children, p_path);
+ if (n) {
+ return n;
+ }
+ children = children->get_next();
+ }
+
+ return NULL;
+}
\ No newline at end of file
diff --git a/editor/plugins/skeleton_editor_plugin.h b/editor/plugins/skeleton_editor_plugin.h
index de3f752af6..b701d1af1e 100644
--- a/editor/plugins/skeleton_editor_plugin.h
+++ b/editor/plugins/skeleton_editor_plugin.h
@@ -31,20 +31,133 @@
#ifndef SKELETON_EDITOR_PLUGIN_H
#define SKELETON_EDITOR_PLUGIN_H
+#include "core/os/input_event.h"
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
+#include "spatial_editor_plugin.h"
+#include "scene/3d/camera.h"
+#include "scene/3d/mesh_instance.h"
#include "scene/3d/skeleton.h"
-class PhysicalBone;
+class EditorInspectorPluginSkeleton;
class Joint;
+class PhysicalBone;
+class SkeletonEditorPlugin;
+class Button;
+class CheckBox;
+
+class BoneTransformEditor : public VBoxContainer {
+ GDCLASS(BoneTransformEditor, VBoxContainer);
+
+ static const int32_t TRANSLATION_COMPONENTS = 3;
+ static const int32_t ROTATION_DEGREES_COMPONENTS = 3;
+ static const int32_t SCALE_COMPONENTS = 3;
+ static const int32_t BASIS_COMPONENTS = 9;
+ static const int32_t BASIS_SPLIT_COMPONENTS = 3;
+ static const int32_t TRANSFORM_COMPONENTS = 12;
+ static const int32_t TRANSFORM_SPLIT_COMPONENTS = 3;
+ static const int32_t TRANSFORM_CONTROL_COMPONENTS = 3;
+
+ EditorInspectorSection *section;
+
+ GridContainer *translation_grid;
+ GridContainer *rotation_grid;
+ GridContainer *scale_grid;
+ GridContainer *transform_grid;
+
+ EditorSpinSlider *translation_slider[TRANSLATION_COMPONENTS];
+ EditorSpinSlider *rotation_slider[ROTATION_DEGREES_COMPONENTS];
+ EditorSpinSlider *scale_slider[SCALE_COMPONENTS];
+ EditorSpinSlider *transform_slider[TRANSFORM_COMPONENTS];
+
+ Rect2 background_rects[5];
+
+ Skeleton *skeleton;
+ String property;
+
+ UndoRedo *undo_redo;
+
+ Button *key_button;
+ CheckBox *enabled_checkbox;
+
+ bool keyable;
+ bool toggle_enabled;
+ bool updating;
+
+ String label;
+
+ void create_editors();
+ void setup_spinner(EditorSpinSlider *spinner, const bool is_transform_spinner);
+
+ void _value_changed(const double p_value, const bool p_from_transform);
+
+ Transform compute_transform(const bool p_from_transform) const;
+
+ void update_enabled_checkbox();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ BoneTransformEditor(Skeleton *p_skeleton);
+
+ // Which transform target to modify
+ void set_target(const String &p_prop);
+ void set_label(const String &p_label) { label = p_label; }
+
+ void _update_properties();
+ void _update_custom_pose_properties();
+ void _update_transform_properties(Transform p_transform);
+
+ // Can/cannot modify the spinner values for the Transform
+ void set_read_only(const bool p_read_only);
+
+ // Transform can be keyed, whether or not to show the button
+ void set_keyable(const bool p_keyable);
+
+ // Bone can be toggled enabled or disabled, whether or not to show the checkbox
+ void set_toggle_enabled(const bool p_enabled);
+
+ // Key Transform Button pressed
+ void _key_button_pressed();
-class SkeletonEditor : public Node {
- GDCLASS(SkeletonEditor, Node);
+ // Bone Enabled Checkbox toggled
+ void _checkbox_toggled(const bool p_toggled);
+};
+
+class SkeletonEditor : public VBoxContainer {
+
+ GDCLASS(SkeletonEditor, VBoxContainer);
+
+ friend class SkeletonEditorPlugin;
enum Menu {
+ MENU_OPTION_INIT_POSE,
+ MENU_OPTION_INSERT_KEYS,
+ MENU_OPTION_INSERT_KEYS_EXISTED,
+ MENU_OPTION_POSE_TO_REST,
MENU_OPTION_CREATE_PHYSICAL_SKELETON
};
+ enum ToolMode {
+ TOOL_MODE_BONE_SELECT,
+ TOOL_MODE_BONE_MOVE,
+ TOOL_MODE_BONE_ROTATE,
+ TOOL_MODE_BONE_SCALE,
+ TOOL_MODE_BONE_NONE,
+ TOOL_MODE_BONE_MAX
+ };
+
+ enum MenuToolOption {
+ MENU_TOOL_BONE_SELECT,
+ MENU_TOOL_BONE_MOVE,
+ MENU_TOOL_BONE_ROTATE,
+ MENU_TOOL_BONE_SCALE,
+ MENU_TOOL_BONE_NONE,
+ MENU_TOOL_BONE_MAX
+ };
+
struct BoneInfo {
PhysicalBone *physical_bone;
Transform relative_rest; // Relative to skeleton node
@@ -52,45 +165,134 @@ class SkeletonEditor : public Node {
physical_bone(NULL) {}
};
+ EditorNode *editor;
+ EditorInspectorPluginSkeleton *editor_plugin;
+
Skeleton *skeleton;
+ Tree *joint_tree;
+ BoneTransformEditor *rest_editor;
+ BoneTransformEditor *pose_editor;
+ BoneTransformEditor *custom_pose_editor;
+
+ VSeparator *separators[2];
MenuButton *options;
+ ToolButton *tool_button[TOOL_MODE_BONE_MAX];
+ ToolButton *rest_mode_button;
+
+ ToolMode tool_mode = TOOL_MODE_BONE_NONE;
+ bool rest_mode = false;
+
+ EditorFileDialog *file_dialog;
+
+ UndoRedo *undo_redo;
+
+ bool keyable;
void _on_click_option(int p_option);
+ void _file_selected(const String &p_file);
+ void _menu_tool_item_pressed(int p_option);
+ TreeItem *_find(TreeItem *p_node, const NodePath &p_path);
+ void rest_mode_toggled(const bool pressed);
- friend class SkeletonEditorPlugin;
+ EditorFileDialog *file_export_lib;
+
+ void update_joint_tree();
+ void update_editors();
+
+ void create_editors();
+
+ void init_pose();
+ void insert_keys(bool p_all_bones);
+ void pose_to_rest();
+ void create_physical_skeleton();
+ PhysicalBone *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos);
+
+ Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
+ bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
+ void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+
+ Ref<ShaderMaterial> handle_material;
+ Ref<Shader> handle_shader;
+ MeshInstance *pointsm;
+ Ref<ArrayMesh> am;
+ void _hide_handles();
+ void _draw_handles();
+
+ SpatialEditorViewport::EditData _edit;
+ void _compute_edit(int p_index, const Point2 &p_point);
+ bool _gizmo_select(int p_index, const Vector2 &p_screenpos, bool p_highlight_only = false);
+
+ Transform original_local;
+ Transform original_global;
+ Transform original_to_local;
+
+ void _update_spatial_transform_gizmo();
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
static void _bind_methods();
- void create_physical_skeleton();
- PhysicalBone *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos);
-
public:
- void edit(Skeleton *p_node);
+ virtual bool forward_spatial_gui_input(int p_index, Camera *p_camera, const Ref<InputEvent> &p_event);
+ void move_skeleton_bone(NodePath p_skeleton_path, int32_t p_selected_boneidx, int32_t p_target_boneidx);
- SkeletonEditor();
+ // Transform can be keyed, whether or not to show the button
+ void set_keyable(const bool p_keyable);
+
+ Skeleton *get_skeleton() const { return skeleton; };
+
+ void set_rest_mode_toggled(const bool pressed);
+
+ void _joint_tree_selection_changed();
+ void _joint_tree_rmb_select(const Vector2 &p_pos);
+
+ void _update_properties();
+
+ SkeletonEditor(EditorInspectorPluginSkeleton *e_plugin, EditorNode *p_editor, Skeleton *skeleton);
~SkeletonEditor();
};
-class SkeletonEditorPlugin : public EditorPlugin {
+class EditorInspectorPluginSkeleton : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorPluginSkeleton, EditorInspectorPlugin);
+
+ friend class SkeletonEditorPlugin;
+
+ SkeletonEditor *skel_editor;
+ EditorNode *editor;
+ UndoRedo *undo_redo;
+ void set_rest_mode_toggled (const bool p_pressed);
+
+protected:
+ static void _bind_methods();
+public:
+ virtual bool forward_spatial_gui_input(int p_index, Camera *p_camera, const Ref<InputEvent> &p_event) { return skel_editor->forward_spatial_gui_input(p_index, p_camera, p_event); }
+ virtual bool can_handle(Object *p_object);
+ virtual void parse_begin(Object *p_object);
+ UndoRedo *get_undo_redo() { return undo_redo; }
+};
+
+class SkeletonEditorPlugin : public EditorPlugin {
GDCLASS(SkeletonEditorPlugin, EditorPlugin);
+ EditorInspectorPluginSkeleton *skeleton_plugin;
EditorNode *editor;
- SkeletonEditor *skeleton_editor;
public:
- virtual String get_name() const { return "Skeleton"; }
- virtual bool has_main_screen() const { return false; }
- virtual void edit(Object *p_object);
+ virtual bool forward_spatial_gui_input(int p_index, Camera *p_camera, const Ref<InputEvent> &p_event) {
+ if (SpatialEditor::get_singleton()->get_tool_mode() != SpatialEditor::TOOL_MODE_EXTERNAL) {
+ return false;
+ }
+ return skeleton_plugin->forward_spatial_gui_input(p_index, p_camera, p_event);
+ }
+ bool has_main_screen() const { return false; }
virtual bool handles(Object *p_object) const;
- virtual void make_visible(bool p_visible);
+
+ virtual String get_name() const { return "Skeleton"; }
SkeletonEditorPlugin(EditorNode *p_node);
- ~SkeletonEditorPlugin();
};
#endif // SKELETON_EDITOR_PLUGIN_H
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index 483dff7616..4ff84c658c 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -422,18 +422,15 @@ Point2 SpatialEditorViewport::_point_to_screen(const Vector3 &p_point) {
return camera->unproject_position(p_point) * viewport_container->get_stretch_shrink();
}
-Vector3 SpatialEditorViewport::_get_ray_pos(const Vector2 &p_pos) const {
-
+Vector3 SpatialEditorViewport::get_ray_pos(const Vector2 &p_pos) const {
return camera->project_ray_origin(p_pos / viewport_container->get_stretch_shrink());
}
-Vector3 SpatialEditorViewport::_get_camera_normal() const {
-
+Vector3 SpatialEditorViewport::get_camera_normal() const {
return -_get_camera_transform().basis.get_axis(2);
}
-Vector3 SpatialEditorViewport::_get_ray(const Vector2 &p_pos) const {
-
+Vector3 SpatialEditorViewport::get_ray(const Vector2 &p_pos) const {
return camera->project_ray_normal(p_pos / viewport_container->get_stretch_shrink());
}
@@ -490,8 +487,8 @@ ObjectID SpatialEditorViewport::_select_ray(const Point2 &p_pos, bool p_append,
if (r_gizmo_handle)
*r_gizmo_handle = -1;
- Vector3 ray = _get_ray(p_pos);
- Vector3 pos = _get_ray_pos(p_pos);
+ Vector3 ray = get_ray(p_pos);
+ Vector3 pos = get_ray_pos(p_pos);
Vector2 shrinked_pos = p_pos / viewport_container->get_stretch_shrink();
Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world()->get_scenario());
@@ -554,8 +551,8 @@ ObjectID SpatialEditorViewport::_select_ray(const Point2 &p_pos, bool p_append,
void SpatialEditorViewport::_find_items_at_pos(const Point2 &p_pos, bool &r_includes_current, Vector<_RayResult> &results, bool p_alt_select) {
- Vector3 ray = _get_ray(p_pos);
- Vector3 pos = _get_ray_pos(p_pos);
+ Vector3 ray = get_ray(p_pos);
+ Vector3 pos = get_ray_pos(p_pos);
Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world()->get_scenario());
Set<Ref<EditorSpatialGizmo> > found_gizmos;
@@ -668,7 +665,7 @@ void SpatialEditorViewport::_select_region() {
}
}
- Plane near(cam_pos, -_get_camera_normal());
+ Plane near(cam_pos, -get_camera_normal());
near.d -= get_znear();
frustum.push_back(near);
@@ -740,8 +737,8 @@ void SpatialEditorViewport::_update_name() {
void SpatialEditorViewport::_compute_edit(const Point2 &p_point) {
- _edit.click_ray = _get_ray(Vector2(p_point.x, p_point.y));
- _edit.click_ray_pos = _get_ray_pos(Vector2(p_point.x, p_point.y));
+ _edit.click_ray = get_ray(Vector2(p_point.x, p_point.y));
+ _edit.click_ray_pos = get_ray_pos(Vector2(p_point.x, p_point.y));
_edit.plane = TRANSFORM_VIEW;
spatial_editor->update_transform_gizmo();
_edit.center = spatial_editor->get_gizmo_transform().origin;
@@ -798,8 +795,8 @@ bool SpatialEditorViewport::_gizmo_select(const Vector2 &p_screenpos, bool p_hig
return false;
}
- Vector3 ray_pos = _get_ray_pos(Vector2(p_screenpos.x, p_screenpos.y));
- Vector3 ray = _get_ray(Vector2(p_screenpos.x, p_screenpos.y));
+ Vector3 ray_pos = get_ray_pos(Vector2(p_screenpos.x, p_screenpos.y));
+ Vector3 ray = get_ray(Vector2(p_screenpos.x, p_screenpos.y));
Transform gt = spatial_editor->get_gizmo_transform();
float gs = gizmo_scale;
@@ -887,7 +884,7 @@ bool SpatialEditorViewport::_gizmo_select(const Vector2 &p_screenpos, bool p_hig
float dist = r.distance_to(gt.origin);
Vector3 r_dir = (r - gt.origin).normalized();
- if (_get_camera_normal().dot(r_dir) <= 0.005) {
+ if (get_camera_normal().dot(r_dir) <= 0.005) {
if (dist > gs * (GIZMO_CIRCLE_SIZE - GIZMO_RING_HALF_WIDTH) && dist < gs * (GIZMO_CIRCLE_SIZE + GIZMO_RING_HALF_WIDTH)) {
float d = ray_pos.distance_to(r);
if (d < col_d) {
@@ -982,7 +979,7 @@ bool SpatialEditorViewport::_gizmo_select(const Vector2 &p_screenpos, bool p_hig
}
}
- if (p_highlight_only)
+ if (p_highlight_only && spatial_editor->get_tool_mode() != SpatialEditor::TOOL_MODE_EXTERNAL)
spatial_editor->select_gizmo_highlight_axis(-1);
return false;
@@ -1071,7 +1068,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
EditorNode *en = editor;
EditorPluginList *force_input_forwarding_list = en->get_editor_plugins_force_input_forwarding();
if (!force_input_forwarding_list->empty()) {
- bool discard = force_input_forwarding_list->forward_spatial_gui_input(camera, p_event, true);
+ bool discard = force_input_forwarding_list->forward_spatial_gui_input(index, camera, p_event, true);
if (discard)
return;
}
@@ -1080,7 +1077,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
EditorNode *en = editor;
EditorPluginList *over_plugin_list = en->get_editor_plugins_over();
if (!over_plugin_list->empty()) {
- bool discard = over_plugin_list->forward_spatial_gui_input(camera, p_event, false);
+ bool discard = over_plugin_list->forward_spatial_gui_input(index, camera, p_event, false);
if (discard)
return;
}
@@ -1462,8 +1459,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (_edit.mode == TRANSFORM_NONE)
return;
- Vector3 ray_pos = _get_ray_pos(m->get_position());
- Vector3 ray = _get_ray(m->get_position());
+ Vector3 ray_pos = get_ray_pos(m->get_position());
+ Vector3 ray = get_ray(m->get_position());
float snap = EDITOR_GET("interface/inspector/default_float_step");
int snap_step_decimals = Math::range_step_decimals(snap);
@@ -1478,19 +1475,19 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
switch (_edit.plane) {
case TRANSFORM_VIEW:
motion_mask = Vector3(0, 0, 0);
- plane = Plane(_edit.center, _get_camera_normal());
+ plane = Plane(_edit.center, get_camera_normal());
break;
case TRANSFORM_X_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(get_camera_normal())).normalized());
break;
case TRANSFORM_Y_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(get_camera_normal())).normalized());
break;
case TRANSFORM_Z_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(get_camera_normal())).normalized());
break;
case TRANSFORM_YZ:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(1);
@@ -1625,19 +1622,19 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
switch (_edit.plane) {
case TRANSFORM_VIEW:
- plane = Plane(_edit.center, _get_camera_normal());
+ plane = Plane(_edit.center, get_camera_normal());
break;
case TRANSFORM_X_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(get_camera_normal())).normalized());
break;
case TRANSFORM_Y_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(get_camera_normal())).normalized());
break;
case TRANSFORM_Z_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(get_camera_normal())).normalized());
break;
case TRANSFORM_YZ:
plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0));
@@ -1734,7 +1731,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
switch (_edit.plane) {
case TRANSFORM_VIEW:
- plane = Plane(_edit.center, _get_camera_normal());
+ plane = Plane(_edit.center, get_camera_normal());
break;
case TRANSFORM_X_AXIS:
plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0));
@@ -3215,7 +3212,6 @@ void SpatialEditorViewport::update_transform_gizmo_view() {
return;
Transform xform = spatial_editor->get_gizmo_transform();
-
Transform camera_xform = camera->get_transform();
if (xform.origin.distance_squared_to(camera_xform.origin) < 0.01) {
@@ -3254,17 +3250,32 @@ void SpatialEditorViewport::update_transform_gizmo_view() {
xform.basis.scale(scale);
- for (int i = 0; i < 3; i++) {
- VisualServer::get_singleton()->instance_set_transform(move_gizmo_instance[i], xform);
- VisualServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_MOVE));
- VisualServer::get_singleton()->instance_set_transform(move_plane_gizmo_instance[i], xform);
- VisualServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_MOVE));
- VisualServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[i], xform);
- VisualServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_ROTATE));
- VisualServer::get_singleton()->instance_set_transform(scale_gizmo_instance[i], xform);
- VisualServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SCALE));
- VisualServer::get_singleton()->instance_set_transform(scale_plane_gizmo_instance[i], xform);
- VisualServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SCALE));
+ if (spatial_editor->is_tool_external()) {
+ for (int i = 0; i < 3; i++) {
+ VisualServer::get_singleton()->instance_set_transform(move_gizmo_instance[i], xform);
+ VisualServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_external_tool_mode() == SpatialEditor::EX_TOOL_MODE_SELECT || spatial_editor->get_external_tool_mode() == SpatialEditor::EX_TOOL_MODE_MOVE));
+ VisualServer::get_singleton()->instance_set_transform(move_plane_gizmo_instance[i], xform);
+ VisualServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_external_tool_mode() == SpatialEditor::EX_TOOL_MODE_SELECT || spatial_editor->get_external_tool_mode() == SpatialEditor::EX_TOOL_MODE_MOVE));
+ VisualServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[i], xform);
+ VisualServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_external_tool_mode() == SpatialEditor::EX_TOOL_MODE_SELECT || spatial_editor->get_external_tool_mode() == SpatialEditor::EX_TOOL_MODE_ROTATE));
+ VisualServer::get_singleton()->instance_set_transform(scale_gizmo_instance[i], xform);
+ VisualServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_external_tool_mode() == SpatialEditor::EX_TOOL_MODE_SCALE));
+ VisualServer::get_singleton()->instance_set_transform(scale_plane_gizmo_instance[i], xform);
+ VisualServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_external_tool_mode() == SpatialEditor::EX_TOOL_MODE_SCALE));
+ }
+ } else {
+ for (int i = 0; i < 3; i++) {
+ VisualServer::get_singleton()->instance_set_transform(move_gizmo_instance[i], xform);
+ VisualServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_MOVE));
+ VisualServer::get_singleton()->instance_set_transform(move_plane_gizmo_instance[i], xform);
+ VisualServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_MOVE));
+ VisualServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[i], xform);
+ VisualServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_ROTATE));
+ VisualServer::get_singleton()->instance_set_transform(scale_gizmo_instance[i], xform);
+ VisualServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SCALE));
+ VisualServer::get_singleton()->instance_set_transform(scale_plane_gizmo_instance[i], xform);
+ VisualServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SCALE));
+ }
}
// Rotation white outline
VisualServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[3], xform);
@@ -3498,8 +3509,8 @@ void SpatialEditorViewport::assign_pending_data_pointers(Spatial *p_preview_node
Vector3 SpatialEditorViewport::_get_instance_position(const Point2 &p_pos) const {
const float MAX_DISTANCE = 10;
- Vector3 world_ray = _get_ray(p_pos);
- Vector3 world_pos = _get_ray_pos(p_pos);
+ Vector3 world_ray = get_ray(p_pos);
+ Vector3 world_pos = get_ray_pos(p_pos);
Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_ray(world_pos, world_ray, get_tree()->get_root()->get_world()->get_scenario());
Set<Ref<EditorSpatialGizmo> > found_gizmos;
@@ -4410,42 +4421,60 @@ void SpatialEditor::select_gizmo_highlight_axis(int p_axis) {
void SpatialEditor::update_transform_gizmo() {
- List<Node *> &selection = editor_selection->get_selected_node_list();
AABB center;
bool first = true;
Basis gizmo_basis;
bool local_gizmo_coords = are_local_coords_enabled();
- for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
+ if (is_tool_external()) {
+ Vector<Transform> transforms = get_externals();
+ for (int i=0; i < transforms.size(); i++) {
+ Transform xf = transforms[i];
+ if (first) {
+ center.position = xf.origin;
+ first = false;
+ if (local_gizmo_coords) {
+ gizmo_basis = xf.basis;
+ gizmo_basis.orthonormalize();
+ }
+ } else {
+ center.expand_to(xf.origin);
+ gizmo_basis = Basis();
+ }
+ }
+ } else {
+ List<Node *> &selection = editor_selection->get_selected_node_list();
+ for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
- Spatial *sp = Object::cast_to<Spatial>(E->get());
- if (!sp)
- continue;
+ Spatial *sp = Object::cast_to<Spatial>(E->get());
+ if (!sp)
+ continue;
- SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
- if (!se)
- continue;
+ SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
+ if (!se)
+ continue;
- Transform xf = se->sp->get_global_gizmo_transform();
+ Transform xf = se->sp->get_global_gizmo_transform();
- if (first) {
- center.position = xf.origin;
- first = false;
- if (local_gizmo_coords) {
- gizmo_basis = xf.basis;
- gizmo_basis.orthonormalize();
+ if (first) {
+ center.position = xf.origin;
+ first = false;
+ if (local_gizmo_coords) {
+ gizmo_basis = xf.basis;
+ gizmo_basis.orthonormalize();
+ }
+ } else {
+ center.expand_to(xf.origin);
+ gizmo_basis = Basis();
}
- } else {
- center.expand_to(xf.origin);
- gizmo_basis = Basis();
}
}
Vector3 pcenter = center.position + center.size * 0.5;
- gizmo.visible = !first;
gizmo.transform.origin = pcenter;
gizmo.transform.basis = gizmo_basis;
+ gizmo.visible = !first;
for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) {
viewports[i]->update_transform_gizmo_view();
@@ -4880,12 +4909,15 @@ void SpatialEditor::_menu_item_pressed(int p_option) {
case MENU_TOOL_MOVE:
case MENU_TOOL_ROTATE:
case MENU_TOOL_SCALE:
- case MENU_TOOL_LIST_SELECT: {
-
- for (int i = 0; i < TOOL_MAX; i++)
+ case MENU_TOOL_LIST_SELECT:
+ case MENU_TOOL_EXTERNAL: {
+ for (int i = 0; i < TOOL_MAX; i++) {
tool_button[i]->set_pressed(i == p_option);
+ }
tool_mode = (ToolMode)p_option;
+ clear_externals();
update_transform_gizmo();
+ emit_signal("change_tool_mode");
} break;
case MENU_TRANSFORM_CONFIGURE_SNAP: {
@@ -6233,6 +6265,7 @@ void SpatialEditor::_bind_methods() {
ADD_SIGNAL(MethodInfo("transform_key_request"));
ADD_SIGNAL(MethodInfo("item_lock_status_changed"));
ADD_SIGNAL(MethodInfo("item_group_status_changed"));
+ ADD_SIGNAL(MethodInfo("change_tool_mode"));
}
void SpatialEditor::clear() {
@@ -6326,6 +6359,10 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
tool_button[TOOL_MODE_SCALE]->connect("pressed", this, "_menu_item_pressed", button_binds);
tool_button[TOOL_MODE_SCALE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_scale", TTR("Scale Mode"), KEY_R));
+ tool_button[TOOL_MODE_EXTERNAL] = memnew(ToolButton);
+ button_binds.write[0] = MENU_TOOL_EXTERNAL;
+ tool_button[TOOL_MODE_EXTERNAL]->connect("pressed", this, "_menu_item_pressed", button_binds);
+
hbc_menu->add_child(memnew(VSeparator));
tool_button[TOOL_MODE_LIST_SELECT] = memnew(ToolButton);
@@ -6625,6 +6662,10 @@ SpatialEditor::~SpatialEditor() {
memdelete(preview_node);
}
+void SpatialEditor::set_tool_mode(ToolMode p_tool_mode) {
+ _menu_item_pressed(p_tool_mode);
+}
+
void SpatialEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h
index 4644144a58..351063ed47 100644
--- a/editor/plugins/spatial_editor_plugin.h
+++ b/editor/plugins/spatial_editor_plugin.h
@@ -233,6 +233,40 @@ public:
FREELOOK_FULLY_AXIS_LOCKED,
};
+ enum TransformMode {
+ TRANSFORM_NONE,
+ TRANSFORM_ROTATE,
+ TRANSFORM_TRANSLATE,
+ TRANSFORM_SCALE
+ };
+
+ enum TransformPlane {
+ TRANSFORM_VIEW,
+ TRANSFORM_X_AXIS,
+ TRANSFORM_Y_AXIS,
+ TRANSFORM_Z_AXIS,
+ TRANSFORM_YZ,
+ TRANSFORM_XZ,
+ TRANSFORM_XY,
+ };
+
+ struct EditData {
+ TransformMode mode;
+ TransformPlane plane;
+ Transform original;
+ Vector3 click_ray;
+ Vector3 click_ray_pos;
+ Vector3 center;
+ Vector3 orig_gizmo_pos;
+ int edited_gizmo;
+ Point2 mouse_pos;
+ bool snap;
+ Ref<EditorSpatialGizmo> gizmo;
+ int gizmo_handle;
+ Variant gizmo_initial_value;
+ Vector3 gizmo_initial_pos;
+ };
+
private:
int index;
String name;
@@ -292,14 +326,11 @@ private:
void _select(Node *p_node, bool p_append, bool p_single);
ObjectID _select_ray(const Point2 &p_pos, bool p_append, bool &r_includes_current, int *r_gizmo_handle = NULL, bool p_alt_select = false);
void _find_items_at_pos(const Point2 &p_pos, bool &r_includes_current, Vector<_RayResult> &results, bool p_alt_select = false);
- Vector3 _get_ray_pos(const Vector2 &p_pos) const;
- Vector3 _get_ray(const Vector2 &p_pos) const;
Point2 _point_to_screen(const Vector3 &p_point);
Transform _get_camera_transform() const;
int get_selected_count() const;
Vector3 _get_camera_position() const;
- Vector3 _get_camera_normal() const;
Vector3 _get_screen_to_space(const Vector3 &p_vector3);
void _select_region();
@@ -333,39 +364,8 @@ private:
NAVIGATION_ORBIT,
NAVIGATION_LOOK
};
- enum TransformMode {
- TRANSFORM_NONE,
- TRANSFORM_ROTATE,
- TRANSFORM_TRANSLATE,
- TRANSFORM_SCALE
- };
- enum TransformPlane {
- TRANSFORM_VIEW,
- TRANSFORM_X_AXIS,
- TRANSFORM_Y_AXIS,
- TRANSFORM_Z_AXIS,
- TRANSFORM_YZ,
- TRANSFORM_XZ,
- TRANSFORM_XY,
- };
-
- struct EditData {
- TransformMode mode;
- TransformPlane plane;
- Transform original;
- Vector3 click_ray;
- Vector3 click_ray_pos;
- Vector3 center;
- Vector3 orig_gizmo_pos;
- int edited_gizmo;
- Point2 mouse_pos;
- bool snap;
- Ref<EditorSpatialGizmo> gizmo;
- int gizmo_handle;
- Variant gizmo_initial_value;
- Vector3 gizmo_initial_pos;
- } _edit;
+ EditData _edit;
struct Cursor {
@@ -451,6 +451,8 @@ public:
void update_surface() { surface->update(); }
void update_transform_gizmo_view();
+ ViewportContainer *get_viewport_container() const { return viewport_container; }
+
void set_can_preview(Camera *p_preview);
void set_state(const Dictionary &p_state);
Dictionary get_state() const;
@@ -466,6 +468,11 @@ public:
Viewport *get_viewport_node() { return viewport; }
Camera *get_camera() { return camera; } // return the default camera object.
+ float get_gizmo_scale() { return gizmo_scale; };
+
+ Vector3 get_camera_normal() const;
+ Vector3 get_ray_pos(const Vector2 &p_pos) const;
+ Vector3 get_ray(const Vector2 &p_pos) const;
SpatialEditorViewport(SpatialEditor *p_spatial_editor, EditorNode *p_editor, int p_index);
};
@@ -545,6 +552,7 @@ public:
TOOL_MODE_MOVE,
TOOL_MODE_ROTATE,
TOOL_MODE_SCALE,
+ TOOL_MODE_EXTERNAL,
TOOL_MODE_LIST_SELECT,
TOOL_LOCK_SELECTED,
TOOL_UNLOCK_SELECTED,
@@ -559,7 +567,15 @@ public:
TOOL_OPT_USE_SNAP,
TOOL_OPT_OVERRIDE_CAMERA,
TOOL_OPT_MAX
+ };
+
+ enum ExternalToolMode {
+ EX_TOOL_MODE_SELECT,
+ EX_TOOL_MODE_MOVE,
+ EX_TOOL_MODE_ROTATE,
+ EX_TOOL_MODE_SCALE,
+ EX_TOOL_MAX
};
private:
@@ -574,6 +590,7 @@ private:
/////
ToolMode tool_mode;
+ ExternalToolMode external_tool_mode = EX_TOOL_MODE_SELECT;
VisualServer::ScenarioDebugMode scenario_debug;
@@ -626,6 +643,7 @@ private:
MENU_TOOL_MOVE,
MENU_TOOL_ROTATE,
MENU_TOOL_SCALE,
+ MENU_TOOL_EXTERNAL,
MENU_TOOL_LIST_SELECT,
MENU_TOOL_LOCAL_COORDS,
MENU_TOOL_USE_SNAP,
@@ -728,6 +746,8 @@ private:
void _refresh_menu_icons();
+ Vector<Transform> externals;
+
protected:
void _notification(int p_what);
//void _gui_input(InputEvent p_event);
@@ -748,7 +768,11 @@ public:
Transform get_gizmo_transform() const { return gizmo.transform; }
bool is_gizmo_visible() const { return gizmo.visible; }
+ void set_tool_mode(ToolMode p_tool_mode);
ToolMode get_tool_mode() const { return tool_mode; }
+ bool is_tool_external() const { return tool_mode == TOOL_MODE_EXTERNAL; }
+ ExternalToolMode get_external_tool_mode() const { return external_tool_mode; }
+ void set_external_tool_mode(ExternalToolMode p_external_tool_mode) { external_tool_mode = p_external_tool_mode; }
bool are_local_coords_enabled() const { return tool_option_button[SpatialEditor::TOOL_OPT_LOCAL_COORDS]->is_pressed(); }
bool is_snap_enabled() const { return snap_enabled ^ snap_key_enabled; }
float get_translate_snap() const;
@@ -800,6 +824,11 @@ public:
void edit(Spatial *p_spatial);
void clear();
+ void append_to_externals(Transform p_transform) { externals.push_back(p_transform); }
+ void append_array_to_externals(Vector<Transform> p_transforms) { externals.append_array(p_transforms); }
+ void clear_externals() { externals.clear(); }
+ Vector<Transform> get_externals() const { return externals; }
+
SpatialEditor(EditorNode *p_editor);
~SpatialEditor();
};
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index c60952e5a6..ddecd103f4 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -32,6 +32,7 @@
#include "core/math/geometry.h"
#include "core/math/quick_hull.h"
+#include "editor/plugins/skeleton_editor_plugin.h"
#include "scene/3d/audio_stream_player_3d.h"
#include "scene/3d/baked_lightmap.h"
#include "scene/3d/collision_polygon.h"
@@ -1611,8 +1612,35 @@ void Position3DSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
SkeletonSpatialGizmoPlugin::SkeletonSpatialGizmoPlugin() {
- Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4));
- create_material("skeleton_material", gizmo_color);
+ skeleton_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4));
+ selected_bone_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/selected_bone", Color(1, 0, 0));
+ bone_axis_length = EDITOR_DEF("editors/3d_gizmos/gizmo_settings/bone_axis_length", (float)0.015);
+ create_material("skeleton_material", skeleton_color);
+ selected_mat = Ref<ShaderMaterial>(memnew(ShaderMaterial));
+ selected_sh = Ref<Shader>(memnew(Shader));
+ selected_sh->set_code(" \
+ shader_type spatial; \
+ render_mode unshaded; \
+ uniform vec4 albedo : hint_color = vec4(1,1,1,1); \
+ uniform sampler2D texture_albedo : hint_albedo; \
+ uniform float point_size : hint_range(0,128) = 32; \
+ void vertex() { \
+ if (!OUTPUT_IS_SRGB) { \
+ COLOR.rgb = mix( pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb* (1.0 / 12.92), lessThan(COLOR.rgb,vec3(0.04045)) ); \
+ } \
+ VERTEX = VERTEX; \
+ POSITION=PROJECTION_MATRIX*INV_CAMERA_MATRIX*WORLD_MATRIX*vec4(VERTEX.xyz,1.0); \
+ POSITION.z = mix(POSITION.z, -POSITION.w, 0.998); \
+ } \
+ void fragment() { \
+ vec2 base_uv = UV; \
+ vec4 albedo_tex = texture(texture_albedo,base_uv); \
+ albedo_tex *= COLOR; \
+ if (albedo.a * albedo_tex.a < 0.5) { discard; } \
+ ALBEDO = albedo.rgb * albedo_tex.rgb; \
+ } \
+ ");
+ selected_mat->set_shader(selected_sh);
}
bool SkeletonSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) {
@@ -1633,7 +1661,12 @@ void SkeletonSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
p_gizmo->clear();
- Ref<Material> material = get_material("skeleton_material", p_gizmo);
+ Ref<Material> material;
+ if (p_gizmo->is_selected()) {
+ material = selected_mat;
+ } else {
+ material = get_material("skeleton_material", p_gizmo);
+ }
Ref<SurfaceTool> surface_tool(memnew(SurfaceTool));
@@ -1654,12 +1687,15 @@ void SkeletonSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
weights.write[0] = 1;
+ Color bone_color;
AABB aabb;
- Color bonecolor = Color(1.0, 0.4, 0.4, 0.3);
- Color rootcolor = Color(0.4, 1.0, 0.4, 0.1);
-
for (int i_bone = 0; i_bone < skel->get_bone_count(); i_bone++) {
+ if (skel->get_bone_parent(i_bone) == skel->get_selected_bone()) {
+ bone_color = selected_bone_color;
+ } else {
+ bone_color = skeleton_color;
+ }
int i = skel->get_process_order(i_bone);
@@ -1687,17 +1723,33 @@ void SkeletonSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
Vector3 first;
Vector3 points[4];
int pointidx = 0;
+ Color axis_color[3];
+ axis_color[0] = Color(1,0,0);
+ axis_color[1] = Color(0,1,0);
+ axis_color[2] = Color(0,0,1);
for (int j = 0; j < 3; j++) {
- bones.write[0] = parent;
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(rootcolor);
- surface_tool->add_vertex(v0 - grests[parent].basis[j].normalized() * dist * 0.05);
- surface_tool->add_bones(bones);
- surface_tool->add_weights(weights);
- surface_tool->add_color(rootcolor);
- surface_tool->add_vertex(v0 + grests[parent].basis[j].normalized() * dist * 0.05);
+ if (p_gizmo->is_selected()) {
+ bones.write[0] = i;
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(axis_color[j]);
+ surface_tool->add_vertex(v1);
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(axis_color[j]);
+ surface_tool->add_vertex(v1 + grests[i].basis[j].normalized() * bone_axis_length);
+ } else {
+ bones.write[0] = i;
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(axis_color[j]);
+ surface_tool->add_vertex(v1 - grests[i].basis[j].normalized() * dist * 0.05);
+ surface_tool->add_bones(bones);
+ surface_tool->add_weights(weights);
+ surface_tool->add_color(axis_color[j]);
+ surface_tool->add_vertex(v1 + grests[i].basis[j].normalized() * dist * 0.05);
+ }
if (j == closest)
continue;
@@ -1720,22 +1772,22 @@ void SkeletonSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
bones.write[0] = parent;
surface_tool->add_bones(bones);
surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
+ surface_tool->add_color(bone_color);
surface_tool->add_vertex(v0);
surface_tool->add_bones(bones);
surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
+ surface_tool->add_color(bone_color);
surface_tool->add_vertex(point);
bones.write[0] = parent;
surface_tool->add_bones(bones);
surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
+ surface_tool->add_color(bone_color);
surface_tool->add_vertex(point);
bones.write[0] = i;
surface_tool->add_bones(bones);
surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
+ surface_tool->add_color(bone_color);
surface_tool->add_vertex(v1);
points[pointidx++] = point;
}
@@ -1747,11 +1799,11 @@ void SkeletonSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
bones.write[0] = parent;
surface_tool->add_bones(bones);
surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
+ surface_tool->add_color(bone_color);
surface_tool->add_vertex(points[j]);
surface_tool->add_bones(bones);
surface_tool->add_weights(weights);
- surface_tool->add_color(bonecolor);
+ surface_tool->add_color(bone_color);
surface_tool->add_vertex(points[(j + 1) % 4]);
}
diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h
index 58598d1ff3..36070116d9 100644
--- a/editor/spatial_editor_gizmos.h
+++ b/editor/spatial_editor_gizmos.h
@@ -137,6 +137,11 @@ public:
class SkeletonSpatialGizmoPlugin : public EditorSpatialGizmoPlugin {
GDCLASS(SkeletonSpatialGizmoPlugin, EditorSpatialGizmoPlugin);
+ Color skeleton_color = Color(1, 0.8, 0.4);
+ Color selected_bone_color = Color(1, 0, 0);
+ float bone_axis_length = 0.015;
+ Ref<ShaderMaterial> selected_mat;
+ Ref<Shader> selected_sh;
public:
bool has_gizmo(Spatial *p_spatial);
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index 941f3c52f4..6de4061ca9 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -261,7 +261,7 @@ protected:
void _notification(int p_what);
public:
- virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); }
+ virtual bool forward_spatial_gui_input(int p_index, Camera *p_camera, const Ref<InputEvent> &p_event) { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); }
virtual String get_name() const { return "GridMap"; }
bool has_main_screen() const { return false; }
virtual void edit(Object *p_object);
diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp
index ec6d4dceef..2c77bec64a 100644
--- a/scene/3d/skeleton.cpp
+++ b/scene/3d/skeleton.cpp
@@ -158,17 +158,17 @@ bool Skeleton::_get(const StringName &p_path, Variant &r_ret) const {
return true;
}
-void Skeleton::_get_property_list(List<PropertyInfo> *p_list) const {
+void Skeleton::_get_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < bones.size(); i++) {
String prep = "bones/" + itos(i) + "/";
- p_list->push_back(PropertyInfo(Variant::STRING, prep + "name"));
- p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1"));
- p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "rest"));
- p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled"));
+ p_list->push_back(PropertyInfo(Variant::STRING, prep + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
- p_list->push_back(PropertyInfo(Variant::ARRAY, prep + "bound_children"));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, prep + "bound_children", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
}
@@ -375,7 +375,11 @@ void Skeleton::_notification(int p_what) {
}
dirty = false;
- emit_signal("skeleton_updated");
+
+#ifdef TOOLS_ENABLED
+ emit_signal("pose_updated");
+#endif // TOOLS_ENABLED
+
} break;
}
}
@@ -624,7 +628,6 @@ int Skeleton::get_process_order(int p_idx) {
}
void Skeleton::localize_rests() {
-
_update_process_order();
for (int i = bones.size() - 1; i >= 0; i--) {
@@ -849,6 +852,16 @@ Ref<SkinReference> Skeleton::register_skin(const Ref<Skin> &p_skin) {
return skin_ref;
}
+void Skeleton::set_selected_bone(int p_bone) {
+ selected_bone = p_bone;
+ update_gizmo();
+ return;
+}
+
+int Skeleton::get_selected_bone() const {
+ return selected_bone;
+}
+
void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton::add_bone);
@@ -897,7 +910,10 @@ void Skeleton::_bind_methods() {
#endif // _3D_DISABLED
+#ifdef TOOLS_ENABLED
+ ADD_SIGNAL(MethodInfo("pose_updated"));
ADD_SIGNAL(MethodInfo("skeleton_updated"));
+#endif // TOOLS_ENABLED
BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
}
@@ -916,3 +932,8 @@ Skeleton::~Skeleton() {
E->get()->skeleton_node = nullptr;
}
}
+
+Vector<int> Skeleton::get_bone_process_order() {
+ _update_process_order();
+ return process_order;
+}
\ No newline at end of file
diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h
index fb5d1367d4..73df15ec98 100644
--- a/scene/3d/skeleton.h
+++ b/scene/3d/skeleton.h
@@ -144,6 +144,8 @@ private:
void _update_process_order();
+ int selected_bone = -1;
+
protected:
bool _get(const StringName &p_path, Variant &r_ret) const;
bool _set(const StringName &p_path, const Variant &p_value);
@@ -200,9 +202,13 @@ public:
void localize_rests(); // used for loaders and tools
int get_process_order(int p_idx);
+ Vector<int> get_bone_process_order();
Ref<SkinReference> register_skin(const Ref<Skin> &p_skin);
+ void set_selected_bone(int p_bone);
+ int get_selected_bone() const;
+
#ifndef _3D_DISABLED
// Physical bone API
--
2.30.1