summaryrefslogtreecommitdiff
path: root/editor/plugins
diff options
context:
space:
mode:
authorRĂ©mi Verschelde <remi@verschelde.fr>2021-10-06 21:11:20 +0200
committerGitHub <noreply@github.com>2021-10-06 21:11:20 +0200
commit164dc11e04b9cb5d0758dc310c624340355f32a3 (patch)
treee3e99f0512cf79299469b98636eb73ef07da7e27 /editor/plugins
parent98b81ad35c30b1336ae100fbd21a0d6a5572c0bd (diff)
parentf2e9867e9f6dc58ab3a016c7b2a90bc9c3b5f1b6 (diff)
Merge pull request #45699 from TokageItLab/implement-skeleton-editor-gizmo
Implement Skeleton Editor Gizmo
Diffstat (limited to 'editor/plugins')
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp4
-rw-r--r--editor/plugins/animation_player_editor_plugin.h4
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp24
-rw-r--r--editor/plugins/collision_polygon_3d_editor_plugin.cpp28
-rw-r--r--editor/plugins/collision_polygon_3d_editor_plugin.h4
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp163
-rw-r--r--editor/plugins/node_3d_editor_gizmos.h12
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp87
-rw-r--r--editor/plugins/node_3d_editor_plugin.h3
-rw-r--r--editor/plugins/path_3d_editor_plugin.cpp22
-rw-r--r--editor/plugins/path_3d_editor_plugin.h2
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp937
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.h135
13 files changed, 1097 insertions, 328 deletions
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 902b0aa9ec..ea025dad3e 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -298,7 +298,7 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
autoplay->set_pressed(current == player->get_autoplay());
- AnimationPlayerEditor::singleton->get_track_editor()->update_keying();
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->update_keying();
EditorNode::get_singleton()->update_keying();
_animation_key_editor_seek(timeline_position, false);
}
@@ -826,7 +826,7 @@ void AnimationPlayerEditor::_update_player() {
pin->set_disabled(player == nullptr);
if (!player) {
- AnimationPlayerEditor::singleton->get_track_editor()->update_keying();
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->update_keying();
EditorNode::get_singleton()->update_keying();
return;
}
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index 0a514d3ff1..eb8db2eaba 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -128,6 +128,7 @@ class AnimationPlayerEditor : public VBoxContainer {
bool updating_blends;
AnimationTrackEditor *track_editor;
+ static AnimationPlayerEditor *singleton;
// Onion skinning.
struct {
@@ -226,7 +227,8 @@ protected:
public:
AnimationPlayer *get_player() const;
- static AnimationPlayerEditor *singleton;
+
+ static AnimationPlayerEditor *get_singleton() { return singleton; }
bool is_pinned() const { return pin->is_pressed(); }
void unpin() { pin->set_pressed(false); }
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index a49dd916f3..ef872bcead 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -513,7 +513,7 @@ Object *CanvasItemEditor::_get_editor_data(Object *p_what) {
}
void CanvasItemEditor::_keying_changed() {
- if (AnimationPlayerEditor::singleton->get_track_editor()->is_visible_in_tree()) {
+ if (AnimationPlayerEditor::get_singleton()->get_track_editor()->is_visible_in_tree()) {
animation_hb->show();
} else {
animation_hb->hide();
@@ -3816,7 +3816,7 @@ void CanvasItemEditor::_notification(int p_what) {
select_sb->set_default_margin(Side(i), 4);
}
- AnimationPlayerEditor::singleton->get_track_editor()->connect("visibility_changed", callable_mp(this, &CanvasItemEditor::_keying_changed));
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->connect("visibility_changed", callable_mp(this, &CanvasItemEditor::_keying_changed));
_keying_changed();
} else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
@@ -4277,13 +4277,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
Node2D *n2d = Object::cast_to<Node2D>(canvas_item);
if (key_pos && p_location) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing);
}
if (key_rot && p_rotation) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "rotation", n2d->get_rotation(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "rotation", n2d->get_rotation(), p_on_existing);
}
if (key_scale && p_scale) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing);
}
if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) {
@@ -4309,13 +4309,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
if (has_chain && ik_chain.size()) {
for (Node2D *&F : ik_chain) {
if (key_pos) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F, "position", F->get_position(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "position", F->get_position(), p_on_existing);
}
if (key_rot) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F, "rotation", F->get_rotation(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "rotation", F->get_rotation(), p_on_existing);
}
if (key_scale) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F, "scale", F->get_scale(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "scale", F->get_scale(), p_on_existing);
}
}
}
@@ -4325,13 +4325,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
Control *ctrl = Object::cast_to<Control>(canvas_item);
if (key_pos) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing);
}
if (key_rot) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation(), p_on_existing);
}
if (key_scale) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing);
}
}
}
@@ -4771,7 +4771,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
}
/*
if (key_scale)
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size());
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size());
*/
}
}
diff --git a/editor/plugins/collision_polygon_3d_editor_plugin.cpp b/editor/plugins/collision_polygon_3d_editor_plugin.cpp
index 8b354c33a1..1ee834a974 100644
--- a/editor/plugins/collision_polygon_3d_editor_plugin.cpp
+++ b/editor/plugins/collision_polygon_3d_editor_plugin.cpp
@@ -103,9 +103,9 @@ void CollisionPolygon3DEditor::_wip_close() {
undo_redo->commit_action();
}
-bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+EditorPlugin::AfterGUIInput CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
if (!node) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Transform3D gt = node->get_global_transform();
@@ -124,7 +124,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
Vector3 spoint;
if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
spoint = gi.xform(spoint);
@@ -151,19 +151,19 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
snap_ignore = false;
_polygon_draw();
edited_point = 1;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, wip[0].y, depth))).distance_to(gpoint) < grab_threshold) {
//wip closed
_wip_close();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
wip.push_back(cpoint);
edited_point = wip.size();
snap_ignore = false;
_polygon_draw();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
} else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && mb->is_pressed() && wip_active) {
@@ -184,7 +184,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
//search edges
@@ -219,7 +219,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
_polygon_draw();
snap_ignore = true;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
} else {
//look for points to move
@@ -244,7 +244,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
edited_point_pos = poly[closest_idx];
_polygon_draw();
snap_ignore = false;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
} else {
@@ -253,7 +253,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
if (edited_point != -1) {
//apply
- ERR_FAIL_INDEX_V(edited_point, poly.size(), false);
+ ERR_FAIL_INDEX_V(edited_point, poly.size(), EditorPlugin::AFTER_GUI_INPUT_PASS);
poly.write[edited_point] = edited_point_pos;
undo_redo->create_action(TTR("Edit Poly"));
undo_redo->add_do_method(node, "set_polygon", poly);
@@ -263,7 +263,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
undo_redo->commit_action();
edited_point = -1;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
}
@@ -290,7 +290,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
@@ -310,7 +310,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
Vector3 spoint;
if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
spoint = gi.xform(spoint);
@@ -332,7 +332,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
}
}
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
float CollisionPolygon3DEditor::_get_depth() {
diff --git a/editor/plugins/collision_polygon_3d_editor_plugin.h b/editor/plugins/collision_polygon_3d_editor_plugin.h
index 5db0f7308a..10b0adf76c 100644
--- a/editor/plugins/collision_polygon_3d_editor_plugin.h
+++ b/editor/plugins/collision_polygon_3d_editor_plugin.h
@@ -88,7 +88,7 @@ protected:
static void _bind_methods();
public:
- virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event);
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event);
void edit(Node *p_collision_polygon);
CollisionPolygon3DEditor(EditorNode *p_editor);
~CollisionPolygon3DEditor();
@@ -101,7 +101,7 @@ class Polygon3DEditorPlugin : public EditorPlugin {
EditorNode *editor;
public:
- virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); }
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); }
virtual String get_name() const override { return "Polygon3DEditor"; }
bool has_main_screen() const override { return false; }
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index 7b0fc07fe7..f1b25c0dde 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -2029,169 +2029,6 @@ void Position3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_collision_segments(cursor_points);
}
-/////
-
-Skeleton3DGizmoPlugin::Skeleton3DGizmoPlugin() {
- Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4));
- create_material("skeleton_material", gizmo_color);
-}
-
-bool Skeleton3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
- return Object::cast_to<Skeleton3D>(p_spatial) != nullptr;
-}
-
-String Skeleton3DGizmoPlugin::get_gizmo_name() const {
- return "Skeleton3D";
-}
-
-int Skeleton3DGizmoPlugin::get_priority() const {
- return -1;
-}
-
-void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
- Skeleton3D *skel = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
-
- p_gizmo->clear();
-
- Ref<Material> material = get_material("skeleton_material", p_gizmo);
-
- Ref<SurfaceTool> surface_tool(memnew(SurfaceTool));
-
- surface_tool->begin(Mesh::PRIMITIVE_LINES);
- surface_tool->set_material(material);
- LocalVector<Transform3D> grests;
- grests.resize(skel->get_bone_count());
-
- LocalVector<int> bones;
- LocalVector<float> weights;
- bones.resize(4);
- weights.resize(4);
-
- for (int i = 0; i < 4; i++) {
- bones[i] = 0;
- weights[i] = 0;
- }
-
- weights[0] = 1;
-
- AABB aabb;
-
- Color bonecolor = Color(1.0, 0.4, 0.4, 0.3);
- Color rootcolor = Color(0.4, 1.0, 0.4, 0.1);
-
- LocalVector<int> bones_to_process;
- bones_to_process = skel->get_parentless_bones();
-
- while (bones_to_process.size() > 0) {
- int current_bone_idx = bones_to_process[0];
- bones_to_process.erase(current_bone_idx);
-
- LocalVector<int> child_bones_vector;
- child_bones_vector = skel->get_bone_children(current_bone_idx);
- int child_bones_size = child_bones_vector.size();
-
- if (skel->get_bone_parent(current_bone_idx) < 0) {
- grests[current_bone_idx] = skel->get_bone_rest(current_bone_idx);
- }
-
- for (int i = 0; i < child_bones_size; i++) {
- int child_bone_idx = child_bones_vector[i];
-
- grests[child_bone_idx] = grests[current_bone_idx] * skel->get_bone_rest(child_bone_idx);
- Vector3 v0 = grests[current_bone_idx].origin;
- Vector3 v1 = grests[child_bone_idx].origin;
- Vector3 d = (v1 - v0).normalized();
- real_t dist = v0.distance_to(v1);
-
- // Find closest axis.
- int closest = -1;
- real_t closest_d = 0.0;
- for (int j = 0; j < 3; j++) {
- real_t dp = Math::abs(grests[current_bone_idx].basis[j].normalized().dot(d));
- if (j == 0 || dp > closest_d) {
- closest = j;
- }
- }
-
- // Find closest other.
- Vector3 first;
- Vector3 points[4];
- int point_idx = 0;
- for (int j = 0; j < 3; j++) {
- bones[0] = current_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(rootcolor);
- surface_tool->add_vertex(v0 - grests[current_bone_idx].basis[j].normalized() * dist * 0.05);
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(rootcolor);
- surface_tool->add_vertex(v0 + grests[current_bone_idx].basis[j].normalized() * dist * 0.05);
-
- if (j == closest) {
- continue;
- }
-
- Vector3 axis;
- if (first == Vector3()) {
- axis = d.cross(d.cross(grests[current_bone_idx].basis[j])).normalized();
- first = axis;
- } else {
- axis = d.cross(first).normalized();
- }
-
- for (int k = 0; k < 2; k++) {
- if (k == 1) {
- axis = -axis;
- }
- Vector3 point = v0 + d * dist * 0.2;
- point += axis * dist * 0.1;
-
- bones[0] = current_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(v0);
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(point);
-
- bones[0] = current_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(point);
- bones[0] = child_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(v1);
- points[point_idx++] = point;
- }
- }
- SWAP(points[1], points[2]);
- for (int j = 0; j < 4; j++) {
- bones[0] = current_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(points[j]);
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(points[(j + 1) % 4]);
- }
-
- // Add the bone's children to the list of bones to be processed.
- bones_to_process.push_back(child_bones_vector[i]);
- }
- }
-
- Ref<ArrayMesh> m = surface_tool->commit();
- p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skel->register_skin(Ref<Skin>()));
-}
-
////
PhysicalBone3DGizmoPlugin::PhysicalBone3DGizmoPlugin() {
diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h
index 24b4a23d4b..d1aca4d92e 100644
--- a/editor/plugins/node_3d_editor_gizmos.h
+++ b/editor/plugins/node_3d_editor_gizmos.h
@@ -332,18 +332,6 @@ public:
Position3DGizmoPlugin();
};
-class Skeleton3DGizmoPlugin : public EditorNode3DGizmoPlugin {
- GDCLASS(Skeleton3DGizmoPlugin, EditorNode3DGizmoPlugin);
-
-public:
- bool has_gizmo(Node3D *p_spatial) override;
- String get_gizmo_name() const override;
- int get_priority() const override;
- void redraw(EditorNode3DGizmo *p_gizmo) override;
-
- Skeleton3DGizmoPlugin();
-};
-
class PhysicalBone3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(PhysicalBone3DGizmoPlugin, EditorNode3DGizmoPlugin);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 3173d2c7f0..ea6ef8ab84 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1291,24 +1291,31 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
return; //do NONE
}
+ EditorPlugin::AfterGUIInput after = EditorPlugin::AFTER_GUI_INPUT_PASS;
{
EditorNode *en = editor;
EditorPluginList *force_input_forwarding_list = en->get_editor_plugins_force_input_forwarding();
if (!force_input_forwarding_list->is_empty()) {
- bool discard = force_input_forwarding_list->forward_spatial_gui_input(camera, p_event, true);
- if (discard) {
+ EditorPlugin::AfterGUIInput discard = force_input_forwarding_list->forward_spatial_gui_input(camera, p_event, true);
+ if (discard == EditorPlugin::AFTER_GUI_INPUT_STOP) {
return;
}
+ if (discard == EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ after = EditorPlugin::AFTER_GUI_INPUT_DESELECT;
+ }
}
}
{
EditorNode *en = editor;
EditorPluginList *over_plugin_list = en->get_editor_plugins_over();
if (!over_plugin_list->is_empty()) {
- bool discard = over_plugin_list->forward_spatial_gui_input(camera, p_event, false);
- if (discard) {
+ EditorPlugin::AfterGUIInput discard = over_plugin_list->forward_spatial_gui_input(camera, p_event, false);
+ if (discard == EditorPlugin::AFTER_GUI_INPUT_STOP) {
return;
}
+ if (discard == EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ after = EditorPlugin::AFTER_GUI_INPUT_DESELECT;
+ }
}
}
@@ -1573,17 +1580,19 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
break;
}
- clicked = _select_ray(b->get_position());
+ if (after != EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ clicked = _select_ray(b->get_position());
- //clicking is always deferred to either move or release
+ //clicking is always deferred to either move or release
- clicked_wants_append = b->is_shift_pressed();
+ clicked_wants_append = b->is_shift_pressed();
- if (clicked.is_null()) {
- //default to regionselect
- cursor.region_select = true;
- cursor.region_begin = b->get_position();
- cursor.region_end = b->get_position();
+ if (clicked.is_null()) {
+ //default to regionselect
+ cursor.region_select = true;
+ cursor.region_begin = b->get_position();
+ cursor.region_end = b->get_position();
+ }
}
surface->update();
@@ -1594,14 +1603,16 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
break;
}
- if (clicked.is_valid()) {
- _select_clicked(false);
- }
+ if (after != EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ if (clicked.is_valid()) {
+ _select_clicked(false);
+ }
- if (cursor.region_select) {
- _select_region();
- cursor.region_select = false;
- surface->update();
+ if (cursor.region_select) {
+ _select_region();
+ cursor.region_select = false;
+ surface->update();
+ }
}
if (_edit.mode != TRANSFORM_NONE) {
@@ -2237,7 +2248,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
return;
}
- if (!AnimationPlayerEditor::singleton->get_track_editor()->has_keying()) {
+ if (!AnimationPlayerEditor::get_singleton()->get_track_editor()->has_keying()) {
set_message(TTR("Keying is disabled (no key inserted)."));
return;
}
@@ -6757,6 +6768,33 @@ void Node3DEditor::_request_gizmo(Object *p_obj) {
}
}
+void Node3DEditor::_set_subgizmo_selection(Object *p_obj, Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform) {
+ if (p_id == -1) {
+ _clear_subgizmo_selection(p_obj);
+ return;
+ }
+
+ Node3D *sp = nullptr;
+ if (p_obj) {
+ sp = Object::cast_to<Node3D>(p_obj);
+ } else {
+ sp = selected;
+ }
+
+ if (!sp) {
+ return;
+ }
+
+ Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
+ if (se) {
+ se->subgizmos.clear();
+ se->subgizmos.insert(p_id, p_transform);
+ se->gizmo = p_gizmo;
+ sp->update_gizmos();
+ update_transform_gizmo();
+ }
+}
+
void Node3DEditor::_clear_subgizmo_selection(Object *p_obj) {
Node3D *sp = nullptr;
if (p_obj) {
@@ -6882,7 +6920,6 @@ void Node3DEditor::_register_all_gizmos() {
add_gizmo_plugin(Ref<OccluderInstance3DGizmoPlugin>(memnew(OccluderInstance3DGizmoPlugin)));
add_gizmo_plugin(Ref<SoftDynamicBody3DGizmoPlugin>(memnew(SoftDynamicBody3DGizmoPlugin)));
add_gizmo_plugin(Ref<Sprite3DGizmoPlugin>(memnew(Sprite3DGizmoPlugin)));
- add_gizmo_plugin(Ref<Skeleton3DGizmoPlugin>(memnew(Skeleton3DGizmoPlugin)));
add_gizmo_plugin(Ref<Position3DGizmoPlugin>(memnew(Position3DGizmoPlugin)));
add_gizmo_plugin(Ref<RayCast3DGizmoPlugin>(memnew(RayCast3DGizmoPlugin)));
add_gizmo_plugin(Ref<SpringArm3DGizmoPlugin>(memnew(SpringArm3DGizmoPlugin)));
@@ -6907,6 +6944,7 @@ void Node3DEditor::_register_all_gizmos() {
void Node3DEditor::_bind_methods() {
ClassDB::bind_method("_get_editor_data", &Node3DEditor::_get_editor_data);
ClassDB::bind_method("_request_gizmo", &Node3DEditor::_request_gizmo);
+ ClassDB::bind_method("_set_subgizmo_selection", &Node3DEditor::_set_subgizmo_selection);
ClassDB::bind_method("_clear_subgizmo_selection", &Node3DEditor::_clear_subgizmo_selection);
ClassDB::bind_method("_refresh_menu_icons", &Node3DEditor::_refresh_menu_icons);
@@ -7733,6 +7771,13 @@ Vector3 Node3DEditor::snap_point(Vector3 p_target, Vector3 p_start) const {
return p_target;
}
+bool Node3DEditor::is_gizmo_visible() const {
+ if (selected) {
+ return gizmo.visible && selected->is_transform_gizmo_visible();
+ }
+ return gizmo.visible;
+}
+
double Node3DEditor::get_translate_snap() const {
double snap_value;
if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index e8948071e6..2d5aeaa981 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -665,6 +665,7 @@ private:
Node3D *selected;
void _request_gizmo(Object *p_obj);
+ void _set_subgizmo_selection(Object *p_obj, Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform = Transform3D());
void _clear_subgizmo_selection(Object *p_obj = nullptr);
static Node3DEditor *singleton;
@@ -758,7 +759,7 @@ public:
float get_fov() const { return settings_fov->get_value(); }
Transform3D get_gizmo_transform() const { return gizmo.transform; }
- bool is_gizmo_visible() const { return gizmo.visible; }
+ bool is_gizmo_visible() const;
ToolMode get_tool_mode() const { return tool_mode; }
bool are_local_coords_enabled() const { return tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->is_pressed(); }
diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp
index bb0c270baa..0268b6e5ea 100644
--- a/editor/plugins/path_3d_editor_plugin.cpp
+++ b/editor/plugins/path_3d_editor_plugin.cpp
@@ -294,13 +294,13 @@ Path3DGizmo::Path3DGizmo(Path3D *p_path) {
orig_out_length = 0;
}
-bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
if (!path) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Ref<Curve3D> c = path->get_curve();
if (c.is_null()) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Transform3D gt = path->get_global_transform();
Transform3D it = gt.affine_inverse();
@@ -329,14 +329,14 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref
const Vector3 *r = v3a.ptr();
if (p_camera->unproject_position(gt.xform(c->get_point_position(0))).distance_to(mbpos) < click_dist) {
- return false; //nope, existing
+ return EditorPlugin::AFTER_GUI_INPUT_PASS; //nope, existing
}
for (int i = 0; i < c->get_point_count() - 1; i++) {
//find the offset and point index of the place to break up
int j = idx;
if (p_camera->unproject_position(gt.xform(c->get_point_position(i + 1))).distance_to(mbpos) < click_dist) {
- return false; //nope, existing
+ return EditorPlugin::AFTER_GUI_INPUT_PASS; //nope, existing
}
while (j < rc && c->get_point_position(i + 1) != r[j]) {
@@ -386,7 +386,7 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref
ur->add_do_method(c.ptr(), "add_point", closest_seg_point, Vector3(), Vector3(), closest_seg + 1);
ur->add_undo_method(c.ptr(), "remove_point", closest_seg + 1);
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
Vector3 org;
@@ -405,7 +405,7 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref
ur->add_do_method(c.ptr(), "add_point", it.xform(inters), Vector3(), Vector3(), -1);
ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count());
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
//add new at pos
@@ -425,27 +425,27 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref
ur->add_do_method(c.ptr(), "remove_point", i);
ur->add_undo_method(c.ptr(), "add_point", c->get_point_position(i), c->get_point_in(i), c->get_point_out(i), i);
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else if (dist_to_p_out < click_dist) {
UndoRedo *ur = editor->get_undo_redo();
ur->create_action(TTR("Remove Out-Control Point"));
ur->add_do_method(c.ptr(), "set_point_out", i, Vector3());
ur->add_undo_method(c.ptr(), "set_point_out", i, c->get_point_out(i));
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else if (dist_to_p_in < click_dist) {
UndoRedo *ur = editor->get_undo_redo();
ur->create_action(TTR("Remove In-Control Point"));
ur->add_do_method(c.ptr(), "set_point_in", i, Vector3());
ur->add_undo_method(c.ptr(), "set_point_in", i, c->get_point_in(i));
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
}
}
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
void Path3DEditorPlugin::edit(Object *p_object) {
diff --git a/editor/plugins/path_3d_editor_plugin.h b/editor/plugins/path_3d_editor_plugin.h
index b74d7cc59e..974234ba8f 100644
--- a/editor/plugins/path_3d_editor_plugin.h
+++ b/editor/plugins/path_3d_editor_plugin.h
@@ -100,7 +100,7 @@ public:
Path3D *get_edited_path() { return path; }
static Path3DEditorPlugin *singleton;
- virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override;
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override;
virtual String get_name() const override { return "Path3D"; }
bool has_main_screen() const override { return false; }
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 4e3ab5380b..52ba8f1bcd 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -42,6 +42,7 @@
#include "scene/3d/physics_body_3d.h"
#include "scene/resources/capsule_shape_3d.h"
#include "scene/resources/sphere_shape_3d.h"
+#include "scene/resources/surface_tool.h"
void BoneTransformEditor::create_editors() {
const Color section_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor"));
@@ -50,6 +51,11 @@ void BoneTransformEditor::create_editors() {
section->setup("trf_properties", label, this, section_color, true);
add_child(section);
+ 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);
+
key_button = memnew(Button);
key_button->set_text(TTR("Key Transform"));
key_button->set_visible(keyable);
@@ -57,49 +63,40 @@ void BoneTransformEditor::create_editors() {
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);
-
- // Translation property
+ // Translation property.
translation_property = memnew(EditorPropertyVector3());
translation_property->setup(-10000, 10000, 0.001f, true);
translation_property->set_label("Translation");
translation_property->set_use_folding(true);
- translation_property->set_read_only(false);
translation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
section->get_vbox()->add_child(translation_property);
- // Rotation property
+ // Rotation property.
rotation_property = memnew(EditorPropertyVector3());
rotation_property->setup(-10000, 10000, 0.001f, true);
rotation_property->set_label("Rotation Degrees");
rotation_property->set_use_folding(true);
- rotation_property->set_read_only(false);
rotation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
section->get_vbox()->add_child(rotation_property);
- // Scale property
+ // Scale property.
scale_property = memnew(EditorPropertyVector3());
scale_property->setup(-10000, 10000, 0.001f, true);
scale_property->set_label("Scale");
scale_property->set_use_folding(true);
- scale_property->set_read_only(false);
scale_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
section->get_vbox()->add_child(scale_property);
- // Transform/Matrix section
+ // Transform/Matrix section.
transform_section = memnew(EditorInspectorSection);
transform_section->setup("trf_properties_transform", "Matrix", this, section_color, true);
section->get_vbox()->add_child(transform_section);
- // Transform/Matrix property
+ // Transform/Matrix property.
transform_property = memnew(EditorPropertyTransform3D());
transform_property->setup(-10000, 10000, 0.001f, true);
transform_property->set_label("Transform");
transform_property->set_use_folding(true);
- transform_property->set_read_only(false);
transform_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_transform));
transform_section->get_vbox()->add_child(transform_property);
}
@@ -109,7 +106,7 @@ void BoneTransformEditor::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
create_editors();
key_button->connect("pressed", callable_mp(this, &BoneTransformEditor::_key_button_pressed));
- enabled_checkbox->connect("toggled", callable_mp(this, &BoneTransformEditor::_checkbox_toggled));
+ enabled_checkbox->connect("pressed", callable_mp(this, &BoneTransformEditor::_checkbox_pressed));
[[fallthrough]];
}
case NOTIFICATION_SORT_CHILDREN: {
@@ -229,7 +226,7 @@ void BoneTransformEditor::_update_properties() {
return;
}
- if (skeleton == nullptr) {
+ if (!skeleton) {
return;
}
@@ -244,7 +241,7 @@ void BoneTransformEditor::_update_custom_pose_properties() {
return;
}
- if (skeleton == nullptr) {
+ if (!skeleton) {
return;
}
@@ -281,11 +278,32 @@ void BoneTransformEditor::set_target(const String &p_prop) {
void BoneTransformEditor::set_keyable(const bool p_keyable) {
keyable = p_keyable;
+}
+
+void BoneTransformEditor::_update_key_button(const bool p_keyable) {
+ bool is_keyable = keyable && p_keyable;
if (key_button) {
- key_button->set_visible(p_keyable);
+ key_button->set_visible(is_keyable);
}
}
+void BoneTransformEditor::set_properties_read_only(const bool p_readonly) {
+ enabled_checkbox->set_disabled(p_readonly);
+ enabled_checkbox->update();
+}
+
+void BoneTransformEditor::set_transform_read_only(const bool p_readonly) {
+ translation_property->set_read_only(p_readonly);
+ rotation_property->set_read_only(p_readonly);
+ scale_property->set_read_only(p_readonly);
+ transform_property->set_read_only(p_readonly);
+ translation_property->update();
+ rotation_property->update();
+ scale_property->update();
+ transform_property->update();
+ _update_key_button(!p_readonly);
+}
+
void BoneTransformEditor::set_toggle_enabled(const bool p_enabled) {
toggle_enabled = p_enabled;
if (enabled_checkbox) {
@@ -294,7 +312,7 @@ void BoneTransformEditor::set_toggle_enabled(const bool p_enabled) {
}
void BoneTransformEditor::_key_button_pressed() {
- if (skeleton == nullptr) {
+ if (!skeleton) {
return;
}
@@ -305,30 +323,150 @@ void BoneTransformEditor::_key_button_pressed() {
return;
}
- // Need to normalize the basis before you key it
Transform3D tform = compute_transform_from_vector3s();
- tform.orthonormalize();
- AnimationPlayerEditor::singleton->get_track_editor()->insert_transform_key(skeleton, name, tform);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(skeleton, name, tform);
}
-void BoneTransformEditor::_checkbox_toggled(const bool p_toggled) {
+void BoneTransformEditor::_checkbox_pressed() {
+ if (!skeleton) {
+ return;
+ }
+
+ const BoneId bone_id = property.get_slicec('/', 1).to_int();
if (enabled_checkbox) {
- const String path = "bones/" + property.get_slicec('/', 1) + "/enabled";
- skeleton->set(path, p_toggled);
+ undo_redo->create_action(TTR("Set Pose Enabled"));
+ bool enabled = skeleton->is_bone_enabled(bone_id);
+ undo_redo->add_do_method(skeleton, "set_bone_enabled", bone_id, !enabled);
+ undo_redo->add_undo_method(skeleton, "set_bone_enabled", bone_id, enabled);
+ undo_redo->commit_action();
}
}
-void Skeleton3DEditor::_on_click_option(int p_option) {
+Skeleton3DEditor *Skeleton3DEditor::singleton = nullptr;
+
+void Skeleton3DEditor::set_keyable(const bool p_keyable) {
+ keyable = p_keyable;
+ skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INSERT_KEYS, !p_keyable);
+ skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INSERT_KEYS_EXISTED, !p_keyable);
+};
+
+void Skeleton3DEditor::set_rest_options_enabled(const bool p_rest_options_enabled) {
+ rest_options->get_popup()->set_item_disabled(REST_OPTION_POSE_TO_REST, !p_rest_options_enabled);
+};
+
+void Skeleton3DEditor::_update_show_rest_only() {
+ _update_pose_enabled(-1);
+}
+
+void Skeleton3DEditor::_update_pose_enabled(int p_bone) {
if (!skeleton) {
return;
}
+ if (pose_editor) {
+ pose_editor->set_properties_read_only(skeleton->is_show_rest_only());
- switch (p_option) {
- case MENU_OPTION_CREATE_PHYSICAL_SKELETON: {
+ if (selected_bone > 0) {
+ pose_editor->set_transform_read_only(skeleton->is_show_rest_only() || !(skeleton->is_bone_enabled(selected_bone)));
+ }
+ }
+ _update_gizmo_visible();
+}
+
+void Skeleton3DEditor::_on_click_skeleton_option(int p_skeleton_option) {
+ if (!skeleton) {
+ return;
+ }
+
+ switch (p_skeleton_option) {
+ case SKELETON_OPTION_CREATE_PHYSICAL_SKELETON: {
create_physical_skeleton();
break;
}
+ case SKELETON_OPTION_INIT_POSE: {
+ init_pose();
+ break;
+ }
+ case SKELETON_OPTION_INSERT_KEYS: {
+ insert_keys(true);
+ break;
+ }
+ case SKELETON_OPTION_INSERT_KEYS_EXISTED: {
+ insert_keys(false);
+ break;
+ }
+ }
+}
+
+void Skeleton3DEditor::_on_click_rest_option(int p_rest_option) {
+ if (!skeleton) {
+ return;
+ }
+
+ switch (p_rest_option) {
+ case REST_OPTION_POSE_TO_REST: {
+ pose_to_rest();
+ break;
+ }
+ }
+}
+
+void Skeleton3DEditor::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, Transform3D());
+ ur->add_undo_method(skeleton, "set_bone_pose", i, skeleton->get_bone_pose(i));
+ }
+ ur->commit_action();
+}
+
+void Skeleton3DEditor::insert_keys(bool p_all_bones) {
+ if (!skeleton) {
+ return;
+ }
+
+ int bone_len = skeleton->get_bone_count();
+ Node *root = EditorNode::get_singleton()->get_tree()->get_root();
+ String path = root->get_path_to(skeleton);
+
+ AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
+ for (int i = 0; i < bone_len; i++) {
+ const String name = skeleton->get_bone_name(i);
+
+ if (name.is_empty()) {
+ continue;
+ }
+
+ if (!p_all_bones && !te->has_transform_track(skeleton, name)) {
+ continue;
+ }
+
+ Transform3D tform = skeleton->get_bone_pose(i);
+ te->insert_transform_key(skeleton, name, tform);
+ }
+}
+
+void Skeleton3DEditor::pose_to_rest() {
+ if (!skeleton) {
+ return;
}
+
+ // Todo: Do method with multiple bone selection.
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
+
+ ur->add_do_method(skeleton, "set_bone_pose", selected_bone, Transform3D());
+ ur->add_undo_method(skeleton, "set_bone_pose", selected_bone, skeleton->get_bone_pose(selected_bone));
+ ur->add_do_method(skeleton, "set_bone_custom_pose", selected_bone, Transform3D());
+ ur->add_undo_method(skeleton, "set_bone_custom_pose", selected_bone, skeleton->get_bone_custom_pose(selected_bone));
+ ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone) * skeleton->get_bone_custom_pose(selected_bone) * skeleton->get_bone_pose(selected_bone));
+ ur->add_undo_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone));
+
+ ur->commit_action();
}
void Skeleton3DEditor::create_physical_skeleton() {
@@ -356,7 +494,7 @@ void Skeleton3DEditor::create_physical_skeleton() {
bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id);
- /// create physical bone on parent
+ // Create physical bone on parent.
if (!bones_infos[parent].physical_bone) {
bones_infos.write[parent].physical_bone = create_physical_bone(parent, bone_id, bones_infos);
@@ -370,7 +508,7 @@ void Skeleton3DEditor::create_physical_skeleton() {
bones_infos[parent].physical_bone->set_owner(owner);
bones_infos[parent].physical_bone->get_child(0)->set_owner(owner); // set shape owner
- /// Create joint between parent of parent
+ // Create joint between parent of parent.
if (-1 != parent_parent) {
bones_infos[parent].physical_bone->set_joint_type(PhysicalBone3D::JOINT_TYPE_PIN);
}
@@ -483,7 +621,7 @@ void Skeleton3DEditor::move_skeleton_bone(NodePath p_skeleton_path, int32_t p_se
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 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();
@@ -505,24 +643,30 @@ void Skeleton3DEditor::move_skeleton_bone(NodePath p_skeleton_path, int32_t p_se
void Skeleton3DEditor::_joint_tree_selection_changed() {
TreeItem *selected = joint_tree->get_selected();
- const String path = selected->get_metadata(0);
+ if (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) + "/";
+ 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_target(bone_path + "pose");
+ rest_editor->set_target(bone_path + "rest");
+ custom_pose_editor->set_target(bone_path + "custom_pose");
- _update_properties();
+ pose_editor->set_visible(true);
+ rest_editor->set_visible(true);
+ custom_pose_editor->set_visible(true);
- pose_editor->set_visible(true);
- rest_editor->set_visible(true);
- custom_pose_editor->set_visible(true);
+ selected_bone = b_idx;
+ }
}
+ set_rest_options_enabled(selected);
+ _update_properties();
+ _update_pose_enabled();
}
+// May be not used with single select mode.
void Skeleton3DEditor::_joint_tree_rmb_select(const Vector2 &p_pos) {
}
@@ -536,12 +680,13 @@ void Skeleton3DEditor::_update_properties() {
if (custom_pose_editor) {
custom_pose_editor->_update_custom_pose_properties();
}
+ _update_gizmo_transform();
}
void Skeleton3DEditor::update_joint_tree() {
joint_tree->clear();
- if (skeleton == nullptr) {
+ if (!skeleton) {
return;
}
@@ -569,7 +714,7 @@ void Skeleton3DEditor::update_joint_tree() {
joint_item->set_selectable(0, true);
joint_item->set_metadata(0, "bones/" + itos(current_bone_idx));
- // Add the bone's children to the list of bones to be processed
+ // Add the bone's children to the list of bones to be processed.
Vector<int> current_bone_child_bones = skeleton->get_bone_children(current_bone_idx);
int child_bone_size = current_bone_child_bones.size();
for (int i = 0; i < child_bone_size; i++) {
@@ -587,16 +732,56 @@ void Skeleton3DEditor::create_editors() {
set_focus_mode(FOCUS_ALL);
- // Create Top Menu Bar
- options = memnew(MenuButton);
- Node3DEditor::get_singleton()->add_control_to_menu_panel(options);
+ Node3DEditor *ne = Node3DEditor::get_singleton();
+ AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
- options->set_text(TTR("Skeleton3D"));
- options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Skeleton3D"), SNAME("EditorIcons")));
+ // Create Top Menu Bar.
+ separator = memnew(VSeparator);
+ ne->add_control_to_menu_panel(separator);
- options->get_popup()->add_item(TTR("Create physical skeleton"), MENU_OPTION_CREATE_PHYSICAL_SKELETON);
+ // Create Skeleton Option in Top Menu Bar.
+ skeleton_options = memnew(MenuButton);
+ ne->add_control_to_menu_panel(skeleton_options);
- options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_option));
+ skeleton_options->set_text(TTR("Skeleton3D"));
+ skeleton_options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Skeleton3D"), SNAME("EditorIcons")));
+
+ skeleton_options->get_popup()->add_item(TTR("Init pose"), SKELETON_OPTION_INIT_POSE);
+ skeleton_options->get_popup()->add_item(TTR("Insert key of all bone poses"), SKELETON_OPTION_INSERT_KEYS);
+ skeleton_options->get_popup()->add_item(TTR("Insert key of bone poses already exist track"), SKELETON_OPTION_INSERT_KEYS_EXISTED);
+ skeleton_options->get_popup()->add_item(TTR("Create physical skeleton"), SKELETON_OPTION_CREATE_PHYSICAL_SKELETON);
+
+ skeleton_options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_skeleton_option));
+
+ // Create Rest Option in Top Menu Bar.
+ rest_options = memnew(MenuButton);
+ ne->add_control_to_menu_panel(rest_options);
+
+ rest_options->set_text(TTR("Edit Rest"));
+ rest_options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("BoneAttachment3D"), SNAME("EditorIcons")));
+
+ rest_options->get_popup()->add_item(TTR("Apply current pose to rest"), REST_OPTION_POSE_TO_REST);
+ rest_options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_rest_option));
+ set_rest_options_enabled(false);
+
+ Vector<Variant> button_binds;
+ button_binds.resize(1);
+
+ edit_mode_button = memnew(Button);
+ ne->add_control_to_menu_panel(edit_mode_button);
+ edit_mode_button->set_tooltip(TTR("Edit Mode\nShow buttons on joints."));
+ edit_mode_button->set_toggle_mode(true);
+ edit_mode_button->set_flat(true);
+ edit_mode_button->connect("toggled", callable_mp(this, &Skeleton3DEditor::edit_mode_toggled));
+
+ edit_mode = false;
+
+ set_keyable(te->has_keying());
+
+ if (skeleton) {
+ skeleton->add_child(handles_mesh_instance);
+ handles_mesh_instance->set_skeleton_path(NodePath(""));
+ }
const Color section_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor"));
@@ -612,7 +797,7 @@ void Skeleton3DEditor::create_editors() {
joint_tree = memnew(Tree);
joint_tree->set_columns(1);
- joint_tree->set_focus_mode(Control::FocusMode::FOCUS_NONE);
+ joint_tree->set_focus_mode(Control::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);
@@ -623,8 +808,8 @@ void Skeleton3DEditor::create_editors() {
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_keyable(te->has_keying());
pose_editor->set_visible(false);
add_child(pose_editor);
@@ -632,27 +817,34 @@ void Skeleton3DEditor::create_editors() {
rest_editor->set_label(TTR("Bone Rest"));
rest_editor->set_visible(false);
add_child(rest_editor);
+ rest_editor->set_transform_read_only(true);
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);
+ custom_pose_editor->set_transform_read_only(true);
}
void Skeleton3DEditor::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_READY: {
+ edit_mode_button->set_icon(get_theme_icon("ToolBoneSelect", "EditorIcons"));
+ get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed), Vector<Variant>(), Object::CONNECT_ONESHOT);
+ break;
+ }
case NOTIFICATION_ENTER_TREE: {
create_editors();
update_joint_tree();
update_editors();
-
- get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed), Vector<Variant>(), Object::CONNECT_ONESHOT);
joint_tree->connect("item_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_selection_changed));
joint_tree->connect("item_rmb_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_rmb_select));
#ifdef TOOLS_ENABLED
+ skeleton->connect("pose_updated", callable_mp(this, &Skeleton3DEditor::_draw_gizmo));
skeleton->connect("pose_updated", callable_mp(this, &Skeleton3DEditor::_update_properties));
-#endif // TOOLS_ENABLED
-
+ skeleton->connect("bone_enabled_changed", callable_mp(this, &Skeleton3DEditor::_update_pose_enabled));
+ skeleton->connect("show_rest_only_changed", callable_mp(this, &Skeleton3DEditor::_update_show_rest_only));
+#endif
break;
}
}
@@ -661,7 +853,8 @@ void Skeleton3DEditor::_notification(int p_what) {
void Skeleton3DEditor::_node_removed(Node *p_node) {
if (skeleton && p_node == skeleton) {
skeleton = nullptr;
- options->hide();
+ skeleton_options->hide();
+ rest_options->hide();
}
_update_properties();
@@ -671,24 +864,237 @@ void Skeleton3DEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_node_removed"), &Skeleton3DEditor::_node_removed);
ClassDB::bind_method(D_METHOD("_joint_tree_selection_changed"), &Skeleton3DEditor::_joint_tree_selection_changed);
ClassDB::bind_method(D_METHOD("_joint_tree_rmb_select"), &Skeleton3DEditor::_joint_tree_rmb_select);
+ ClassDB::bind_method(D_METHOD("_update_show_rest_only"), &Skeleton3DEditor::_update_show_rest_only);
+ ClassDB::bind_method(D_METHOD("_update_pose_enabled"), &Skeleton3DEditor::_update_pose_enabled);
ClassDB::bind_method(D_METHOD("_update_properties"), &Skeleton3DEditor::_update_properties);
- ClassDB::bind_method(D_METHOD("_on_click_option"), &Skeleton3DEditor::_on_click_option);
+ ClassDB::bind_method(D_METHOD("_on_click_skeleton_option"), &Skeleton3DEditor::_on_click_skeleton_option);
+ ClassDB::bind_method(D_METHOD("_on_click_rest_option"), &Skeleton3DEditor::_on_click_rest_option);
+
+ ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &Skeleton3DEditor::get_drag_data_fw);
+ ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &Skeleton3DEditor::can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("drop_data_fw"), &Skeleton3DEditor::drop_data_fw);
- ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &Skeleton3DEditor::get_drag_data_fw);
- ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &Skeleton3DEditor::can_drop_data_fw);
- ClassDB::bind_method(D_METHOD("_drop_data_fw"), &Skeleton3DEditor::drop_data_fw);
ClassDB::bind_method(D_METHOD("move_skeleton_bone"), &Skeleton3DEditor::move_skeleton_bone);
+
+ ClassDB::bind_method(D_METHOD("_draw_gizmo"), &Skeleton3DEditor::_draw_gizmo);
+}
+
+void Skeleton3DEditor::edit_mode_toggled(const bool pressed) {
+ edit_mode = pressed;
+ _update_gizmo_visible();
}
Skeleton3DEditor::Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, EditorNode *p_editor, Skeleton3D *p_skeleton) :
editor(p_editor),
editor_plugin(e_plugin),
skeleton(p_skeleton) {
+ singleton = this;
+
+ // Handle.
+ handle_material = Ref<ShaderMaterial>(memnew(ShaderMaterial));
+ handle_shader = Ref<Shader>(memnew(Shader));
+ handle_shader->set_code(R"(
+// Skeleton 3D gizmo handle shader.
+
+shader_type spatial;
+render_mode unshaded, shadows_disabled, depth_draw_always;
+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, 0, 0.999);
+ POINT_SIZE = point_size;
+}
+void fragment() {
+ vec4 albedo_tex = texture(texture_albedo,POINT_COORD);
+ 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 = col;
+ if (albedo_tex.a < 0.5) { discard; }
+ ALPHA = albedo_tex.a;
+}
+)");
+ handle_material->set_shader(handle_shader);
+ Ref<Texture2D> handle = editor->get_gui_base()->get_theme_icon("EditorBoneHandle", "EditorIcons");
+ handle_material->set_shader_param("point_size", handle->get_width());
+ handle_material->set_shader_param("texture_albedo", handle);
+
+ handles_mesh_instance = memnew(MeshInstance3D);
+ handles_mesh_instance->set_cast_shadows_setting(GeometryInstance3D::SHADOW_CASTING_SETTING_OFF);
+ handles_mesh.instantiate();
+ handles_mesh_instance->set_mesh(handles_mesh);
+}
+
+void Skeleton3DEditor::update_bone_original() {
+ if (!skeleton) {
+ return;
+ }
+ if (skeleton->get_bone_count() == 0 || selected_bone == -1) {
+ return;
+ }
+ bone_original = skeleton->get_bone_pose(selected_bone);
+}
+
+void Skeleton3DEditor::_hide_handles() {
+ handles_mesh_instance->hide();
+}
+
+void Skeleton3DEditor::_draw_gizmo() {
+ if (!skeleton) {
+ return;
+ }
+
+ // If you call get_bone_global_pose() while drawing the surface, such as toggle rest mode,
+ // the skeleton update will be done first and
+ // the drawing surface will be interrupted once and an error will occur.
+ skeleton->force_update_all_dirty_bones();
+
+ // Handles.
+ if (edit_mode) {
+ _draw_handles();
+ } else {
+ _hide_handles();
+ }
+}
+
+void Skeleton3DEditor::_draw_handles() {
+ handles_mesh_instance->show();
+
+ const int bone_len = skeleton->get_bone_count();
+ handles_mesh->clear_surfaces();
+ handles_mesh->surface_begin(Mesh::PRIMITIVE_POINTS);
+
+ for (int i = 0; i < bone_len; i++) {
+ Color c;
+ if (i == selected_bone) {
+ c = Color(1, 1, 0);
+ } else {
+ c = Color(0.1, 0.25, 0.8);
+ }
+ Vector3 point = skeleton->get_bone_global_pose(i).origin;
+ handles_mesh->surface_set_color(c);
+ handles_mesh->surface_add_vertex(point);
+ }
+ handles_mesh->surface_end();
+ handles_mesh->surface_set_material(0, handle_material);
+}
+
+TreeItem *Skeleton3DEditor::_find(TreeItem *p_node, const NodePath &p_path) {
+ if (!p_node) {
+ return nullptr;
+ }
+
+ NodePath np = p_node->get_metadata(0);
+ if (np == p_path) {
+ return p_node;
+ }
+
+ TreeItem *children = p_node->get_first_child();
+ while (children) {
+ TreeItem *n = _find(children, p_path);
+ if (n) {
+ return n;
+ }
+ children = children->get_next();
+ }
+
+ return nullptr;
+}
+
+void Skeleton3DEditor::_subgizmo_selection_change() {
+ if (!skeleton) {
+ return;
+ }
+
+ // Once validated by subgizmos_intersect_ray, but required if through inspector's bones tree.
+ if (!edit_mode) {
+ skeleton->clear_subgizmo_selection();
+ return;
+ }
+
+ int selected = -1;
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+ if (se) {
+ selected = se->get_selected_bone();
+ }
+
+ if (selected >= 0) {
+ Vector<Ref<Node3DGizmo>> gizmos = skeleton->get_gizmos();
+ for (int i = 0; i < gizmos.size(); i++) {
+ Ref<EditorNode3DGizmo> gizmo = gizmos[i];
+ if (!gizmo.is_valid()) {
+ continue;
+ }
+ Ref<Skeleton3DGizmoPlugin> plugin = gizmo->get_plugin();
+ if (!plugin.is_valid()) {
+ continue;
+ }
+ skeleton->set_subgizmo_selection(gizmo, selected, skeleton->get_bone_global_pose(selected));
+ break;
+ }
+ } else {
+ skeleton->clear_subgizmo_selection();
+ }
+}
+
+void Skeleton3DEditor::select_bone(int p_idx) {
+ if (p_idx >= 0) {
+ TreeItem *ti = _find(joint_tree->get_root(), "bones/" + itos(p_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 {
+ selected_bone = -1;
+ joint_tree->deselect_all();
+ _joint_tree_selection_changed();
+ }
}
Skeleton3DEditor::~Skeleton3DEditor() {
- if (options) {
- Node3DEditor::get_singleton()->remove_control_from_menu_panel(options);
+ if (skeleton) {
+#ifdef TOOLS_ENABLED
+ skeleton->disconnect("show_rest_only_changed", callable_mp(this, &Skeleton3DEditor::_update_show_rest_only));
+ skeleton->disconnect("bone_enabled_changed", callable_mp(this, &Skeleton3DEditor::_update_pose_enabled));
+ skeleton->disconnect("pose_updated", callable_mp(this, &Skeleton3DEditor::_draw_gizmo));
+ skeleton->disconnect("pose_updated", callable_mp(this, &Skeleton3DEditor::_update_properties));
+ skeleton->set_transform_gizmo_visible(true);
+#endif
+ handles_mesh_instance->get_parent()->remove_child(handles_mesh_instance);
+ }
+
+ handles_mesh_instance->queue_delete();
+
+ Node3DEditor *ne = Node3DEditor::get_singleton();
+
+ if (separator) {
+ ne->remove_control_from_menu_panel(separator);
+ memdelete(separator);
+ }
+
+ if (skeleton_options) {
+ ne->remove_control_from_menu_panel(skeleton_options);
+ memdelete(skeleton_options);
+ }
+
+ if (rest_options) {
+ ne->remove_control_from_menu_panel(rest_options);
+ memdelete(rest_options);
+ }
+
+ if (edit_mode_button) {
+ ne->remove_control_from_menu_panel(edit_mode_button);
+ memdelete(edit_mode_button);
}
}
@@ -700,16 +1106,413 @@ void EditorInspectorPluginSkeleton::parse_begin(Object *p_object) {
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_object);
ERR_FAIL_COND(!skeleton);
- Skeleton3DEditor *skel_editor = memnew(Skeleton3DEditor(this, editor, skeleton));
+ skel_editor = memnew(Skeleton3DEditor(this, editor, skeleton));
add_custom_control(skel_editor);
}
Skeleton3DEditorPlugin::Skeleton3DEditorPlugin(EditorNode *p_node) {
editor = p_node;
- Ref<EditorInspectorPluginSkeleton> skeleton_plugin;
- skeleton_plugin.instantiate();
+ skeleton_plugin = memnew(EditorInspectorPluginSkeleton);
skeleton_plugin->editor = editor;
EditorInspector::add_inspector_plugin(skeleton_plugin);
+
+ Ref<Skeleton3DGizmoPlugin> gizmo_plugin = Ref<Skeleton3DGizmoPlugin>(memnew(Skeleton3DGizmoPlugin));
+ Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin);
+}
+
+EditorPlugin::AfterGUIInput Skeleton3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+ Node3DEditor *ne = Node3DEditor::get_singleton();
+ if (se->is_edit_mode()) {
+ const Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (ne->get_tool_mode() != Node3DEditor::TOOL_MODE_SELECT) {
+ if (!ne->is_gizmo_visible()) {
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+ if (mb->is_pressed()) {
+ se->update_bone_original();
+ }
+ }
+ return EditorPlugin::AFTER_GUI_INPUT_DESELECT;
+ }
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
+}
+
+bool Skeleton3DEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("Skeleton3D");
+}
+
+void Skeleton3DEditor::_update_gizmo_transform() {
+ Node3DEditor::get_singleton()->update_transform_gizmo();
+};
+
+void Skeleton3DEditor::_update_gizmo_visible() {
+ _subgizmo_selection_change();
+ if (edit_mode) {
+ if (selected_bone == -1) {
+#ifdef TOOLS_ENABLED
+ skeleton->set_transform_gizmo_visible(false);
+#endif
+ } else {
+#ifdef TOOLS_ENABLED
+ if (skeleton->is_bone_enabled(selected_bone) && !skeleton->is_show_rest_only()) {
+ skeleton->set_transform_gizmo_visible(true);
+ } else {
+ skeleton->set_transform_gizmo_visible(false);
+ }
+#endif
+ }
+ } else {
+#ifdef TOOLS_ENABLED
+ skeleton->set_transform_gizmo_visible(true);
+#endif
+ }
+ _draw_gizmo();
+}
+
+int Skeleton3DEditor::get_selected_bone() const {
+ return selected_bone;
+}
+
+Skeleton3DGizmoPlugin::Skeleton3DGizmoPlugin() {
+ unselected_mat = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ unselected_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ unselected_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ unselected_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ unselected_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+
+ selected_mat = Ref<ShaderMaterial>(memnew(ShaderMaterial));
+ selected_sh = Ref<Shader>(memnew(Shader));
+ selected_sh->set_code(R"(
+// Skeleton 3D gizmo bones shader.
+
+shader_type spatial;
+render_mode unshaded, shadows_disabled;
+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, 0, 0.998);
+}
+void fragment() {
+ ALBEDO = COLOR.rgb;
+ ALPHA = COLOR.a;
+}
+)");
+ selected_mat->set_shader(selected_sh);
+
+ // Regist properties in editor settings.
+ EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4));
+ EDITOR_DEF("editors/3d_gizmos/gizmo_colors/selected_bone", Color(0.8, 0.3, 0.0));
+ EDITOR_DEF("editors/3d_gizmos/gizmo_settings/bone_axis_length", (float)0.1);
+ EDITOR_DEF("editors/3d_gizmos/gizmo_settings/bone_shape", 1);
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d_gizmos/gizmo_settings/bone_shape", PROPERTY_HINT_ENUM, "Wire,Octahedron"));
+}
+
+bool Skeleton3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return Object::cast_to<Skeleton3D>(p_spatial) != nullptr;
+}
+
+String Skeleton3DGizmoPlugin::get_gizmo_name() const {
+ return "Skeleton3D";
+}
+
+int Skeleton3DGizmoPlugin::get_priority() const {
+ return -1;
+}
+
+int Skeleton3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ ERR_FAIL_COND_V(!skeleton, -1);
+
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+
+ if (!se->is_edit_mode()) {
+ return -1;
+ }
+
+ if (Node3DEditor::get_singleton()->get_tool_mode() != Node3DEditor::TOOL_MODE_SELECT) {
+ return -1;
+ }
+
+ // Select bone.
+ real_t grab_threshold = 4 * EDSCALE;
+ Vector3 ray_from = p_camera->get_global_transform().origin;
+ Transform3D gt = skeleton->get_global_transform();
+ 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 = p_point.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) {
+ WARN_PRINT("ray:");
+ WARN_PRINT(itos(closest_idx));
+ se->select_bone(closest_idx);
+ return closest_idx;
+ }
+
+ se->select_bone(-1);
+ return -1;
+}
+
+Transform3D Skeleton3DGizmoPlugin::get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ ERR_FAIL_COND_V(!skeleton, Transform3D());
+
+ return skeleton->get_bone_global_pose(p_id);
+}
+
+void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ ERR_FAIL_COND(!skeleton);
+
+ // Prepare for global to local.
+ Transform3D original_to_local = Transform3D();
+ int parent_idx = skeleton->get_bone_parent(p_id);
+ if (parent_idx >= 0) {
+ original_to_local = original_to_local * skeleton->get_bone_global_pose(parent_idx);
+ }
+ original_to_local = original_to_local * skeleton->get_bone_rest(p_id) * skeleton->get_bone_custom_pose(p_id);
+ Basis to_local = original_to_local.get_basis().inverse();
+
+ // Prepare transform.
+ Transform3D t = Transform3D();
+
+ // Basis.
+ t.basis = to_local * p_transform.get_basis();
+
+ // Origin.
+ Vector3 orig = Vector3();
+ orig = skeleton->get_bone_pose(p_id).origin;
+ Vector3 sub = p_transform.origin - skeleton->get_bone_global_pose(p_id).origin;
+ t.origin = orig + to_local.xform(sub);
+
+ // Apply transform.
+ skeleton->set_bone_pose(p_id, t);
+}
+
+void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ ERR_FAIL_COND(!skeleton);
+
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ for (int i = 0; i < p_ids.size(); i++) {
+ ur->create_action(TTR("Set Bone Transform"));
+ ur->add_do_method(skeleton, "set_bone_pose", p_ids[i], skeleton->get_bone_pose(p_ids[i]));
+ ur->add_undo_method(skeleton, "set_bone_pose", p_ids[i], se->get_bone_original());
+ }
+ ur->commit_action();
+}
+
+void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ p_gizmo->clear();
+
+ int selected = -1;
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+ if (se) {
+ selected = se->get_selected_bone();
+ }
+
+ Color bone_color = EditorSettings::get_singleton()->get("editors/3d_gizmos/gizmo_colors/skeleton");
+ Color selected_bone_color = EditorSettings::get_singleton()->get("editors/3d_gizmos/gizmo_colors/selected_bone");
+ real_t bone_axis_length = EditorSettings::get_singleton()->get("editors/3d_gizmos/gizmo_settings/bone_axis_length");
+ int bone_shape = EditorSettings::get_singleton()->get("editors/3d_gizmos/gizmo_settings/bone_shape");
+
+ LocalVector<Color> axis_colors;
+ axis_colors.push_back(Node3DEditor::get_singleton()->get_theme_color(SNAME("axis_x_color"), SNAME("Editor")));
+ axis_colors.push_back(Node3DEditor::get_singleton()->get_theme_color(SNAME("axis_y_color"), SNAME("Editor")));
+ axis_colors.push_back(Node3DEditor::get_singleton()->get_theme_color(SNAME("axis_z_color"), SNAME("Editor")));
+
+ Ref<SurfaceTool> surface_tool(memnew(SurfaceTool));
+ surface_tool->begin(Mesh::PRIMITIVE_LINES);
+
+ if (p_gizmo->is_selected()) {
+ surface_tool->set_material(selected_mat);
+ } else {
+ unselected_mat->set_albedo(bone_color);
+ surface_tool->set_material(unselected_mat);
+ }
+
+ Vector<Transform3D> grests;
+ grests.resize(skeleton->get_bone_count());
+
+ LocalVector<int> bones;
+ LocalVector<float> weights;
+ bones.resize(4);
+ weights.resize(4);
+ for (int i = 0; i < 4; i++) {
+ bones[i] = 0;
+ weights[i] = 0;
+ }
+ weights[0] = 1;
+
+ int current_bone_index = 0;
+ Vector<int> bones_to_process = skeleton->get_parentless_bones();
+
+ while (bones_to_process.size() > current_bone_index) {
+ int current_bone_idx = bones_to_process[current_bone_index];
+ current_bone_index++;
+
+ Color current_bone_color = (current_bone_idx == selected) ? selected_bone_color : bone_color;
+
+ Vector<int> child_bones_vector;
+ child_bones_vector = skeleton->get_bone_children(current_bone_idx);
+ int child_bones_size = child_bones_vector.size();
+
+ // You have children but no parent, then you must be a root/parentless bone.
+ if (skeleton->get_bone_parent(current_bone_idx) < 0) {
+ grests.write[current_bone_idx] = skeleton->get_bone_rest(current_bone_idx);
+ }
+
+ for (int i = 0; i < child_bones_size; i++) {
+ // Something wrong.
+ if (child_bones_vector[i] < 0) {
+ continue;
+ }
+
+ int child_bone_idx = child_bones_vector[i];
+
+ grests.write[child_bone_idx] = grests[current_bone_idx] * skeleton->get_bone_rest(child_bone_idx);
+
+ Vector3 v0 = grests[current_bone_idx].origin;
+ Vector3 v1 = grests[child_bone_idx].origin;
+ Vector3 d = (v1 - v0).normalized();
+ real_t dist = v0.distance_to(v1);
+
+ // Find closest axis.
+ int closest = -1;
+ real_t closest_d = 0.0;
+ for (int j = 0; j < 3; j++) {
+ real_t dp = Math::abs(grests[current_bone_idx].basis[j].normalized().dot(d));
+ if (j == 0 || dp > closest_d) {
+ closest = j;
+ }
+ }
+
+ // Draw bone.
+ switch (bone_shape) {
+ case 0: { // Wire shape.
+ surface_tool->set_color(current_bone_color);
+ bones[0] = current_bone_idx;
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v0);
+ bones[0] = child_bone_idx;
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v1);
+ } break;
+
+ case 1: { // Octahedron shape.
+ Vector3 first;
+ Vector3 points[6];
+ int point_idx = 0;
+ for (int j = 0; j < 3; j++) {
+ Vector3 axis;
+ if (first == Vector3()) {
+ axis = d.cross(d.cross(grests[current_bone_idx].basis[j])).normalized();
+ first = axis;
+ } else {
+ axis = d.cross(first).normalized();
+ }
+
+ surface_tool->set_color(current_bone_color);
+ for (int k = 0; k < 2; k++) {
+ if (k == 1) {
+ axis = -axis;
+ }
+ Vector3 point = v0 + d * dist * 0.2;
+ point += axis * dist * 0.1;
+
+ bones[0] = current_bone_idx;
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v0);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(point);
+
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(point);
+ bones[0] = child_bone_idx;
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v1);
+ points[point_idx++] = point;
+ }
+ }
+ surface_tool->set_color(current_bone_color);
+ SWAP(points[1], points[2]);
+ bones[0] = current_bone_idx;
+ for (int j = 0; j < 6; j++) {
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(points[j]);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(points[(j + 1) % 6]);
+ }
+ } break;
+ }
+
+ // Axis as root of the bone.
+ for (int j = 0; j < 3; j++) {
+ bones[0] = current_bone_idx;
+ surface_tool->set_color(axis_colors[j]);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v0);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v0 + (grests[current_bone_idx].basis.inverse())[j].normalized() * dist * bone_axis_length);
+
+ if (j == closest) {
+ continue;
+ }
+ }
+
+ // Axis at the end of the bone children.
+ if (i == child_bones_size - 1) {
+ for (int j = 0; j < 3; j++) {
+ bones[0] = child_bone_idx;
+ surface_tool->set_color(axis_colors[j]);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v1);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v1 + (grests[child_bone_idx].basis.inverse())[j].normalized() * dist * bone_axis_length);
+
+ if (j == closest) {
+ continue;
+ }
+ }
+ }
+
+ // Add the bone's children to the list of bones to be processed.
+ bones_to_process.push_back(child_bones_vector[i]);
+ }
+ }
+
+ Ref<ArrayMesh> m = surface_tool->commit();
+ p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skeleton->register_skin(Ref<Skin>()));
}
diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h
index 9de52c6fa8..e2a1d9a628 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_3d_editor_plugin.h
@@ -33,7 +33,12 @@
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
+#include "editor/editor_properties.h"
+#include "node_3d_editor_plugin.h"
+#include "scene/3d/camera_3d.h"
+#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/immediate_mesh.h"
class EditorInspectorPluginSkeleton;
class Joint;
@@ -41,8 +46,6 @@ class PhysicalBone3D;
class Skeleton3DEditorPlugin;
class Button;
class CheckBox;
-class EditorPropertyTransform3D;
-class EditorPropertyVector3;
class BoneTransformEditor : public VBoxContainer {
GDCLASS(BoneTransformEditor, VBoxContainer);
@@ -81,6 +84,8 @@ class BoneTransformEditor : public VBoxContainer {
void _value_changed_transform(const String p_property_name, const Transform3D p_transform, const StringName p_edited_property_name, const bool p_boolean);
// Changes the transform to the given transform and updates the UI accordingly.
void _change_transform(Transform3D p_new_transform);
+ // Update it is truely keyable then.
+ void _update_key_button(const bool p_keyable);
// Creates a Transform using the EditorPropertyVector3 properties.
Transform3D compute_transform_from_vector3s() const;
@@ -92,7 +97,7 @@ protected:
public:
BoneTransformEditor(Skeleton3D *p_skeleton);
- // Which transform target to modify
+ // Which transform target to modify.
void set_target(const String &p_prop);
void set_label(const String &p_label) { label = p_label; }
@@ -100,20 +105,21 @@ public:
void _update_custom_pose_properties();
void _update_transform_properties(Transform3D 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
+ // 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
+ // When rest mode, pose and custom_pose editor are diasbled.
+ void set_properties_read_only(const bool p_readonly);
+ void set_transform_read_only(const bool p_readonly);
+
+ // 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
+ // Key Transform Button pressed.
void _key_button_pressed();
- // Bone Enabled Checkbox toggled
- void _checkbox_toggled(const bool p_toggled);
+ // Bone Enabled Checkbox toggled.
+ void _checkbox_pressed();
};
class Skeleton3DEditor : public VBoxContainer {
@@ -121,13 +127,20 @@ class Skeleton3DEditor : public VBoxContainer {
friend class Skeleton3DEditorPlugin;
- enum Menu {
- MENU_OPTION_CREATE_PHYSICAL_SKELETON
+ enum SkeletonOption {
+ SKELETON_OPTION_INIT_POSE,
+ SKELETON_OPTION_INSERT_KEYS,
+ SKELETON_OPTION_INSERT_KEYS_EXISTED,
+ SKELETON_OPTION_CREATE_PHYSICAL_SKELETON
+ };
+
+ enum RestOption {
+ REST_OPTION_POSE_TO_REST
};
struct BoneInfo {
PhysicalBone3D *physical_bone = nullptr;
- Transform3D relative_rest; // Relative to skeleton node
+ Transform3D relative_rest; // Relative to skeleton node.
};
EditorNode *editor;
@@ -140,13 +153,24 @@ class Skeleton3DEditor : public VBoxContainer {
BoneTransformEditor *pose_editor = nullptr;
BoneTransformEditor *custom_pose_editor = nullptr;
- MenuButton *options = nullptr;
+ VSeparator *separator;
+ MenuButton *skeleton_options = nullptr;
+ MenuButton *rest_options = nullptr;
+ Button *edit_mode_button;
+
+ bool edit_mode = false;
+
EditorFileDialog *file_dialog = nullptr;
- UndoRedo *undo_redo = nullptr;
+ bool keyable;
+
+ static Skeleton3DEditor *singleton;
- void _on_click_option(int p_option);
+ void _on_click_skeleton_option(int p_skeleton_option);
+ void _on_click_rest_option(int p_rest_option);
void _file_selected(const String &p_file);
+ TreeItem *_find(TreeItem *p_node, const NodePath &p_path);
+ void edit_mode_toggled(const bool pressed);
EditorFileDialog *file_export_lib = nullptr;
@@ -155,6 +179,10 @@ class Skeleton3DEditor : public VBoxContainer {
void create_editors();
+ void init_pose();
+ void insert_keys(bool p_all_bones);
+ void pose_to_rest();
+
void create_physical_skeleton();
PhysicalBone3D *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos);
@@ -162,20 +190,56 @@ class Skeleton3DEditor : public VBoxContainer {
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);
+ void set_keyable(const bool p_keyable);
+ void set_rest_options_enabled(const bool p_rest_options_enabled);
+
+ // Handle.
+ MeshInstance3D *handles_mesh_instance;
+ Ref<ImmediateMesh> handles_mesh;
+ Ref<ShaderMaterial> handle_material;
+ Ref<Shader> handle_shader;
+
+ Transform3D bone_original;
+
+ void _update_pose_enabled(int p_bone = -1);
+ void _update_show_rest_only();
+
+ void _update_gizmo_transform();
+ void _update_gizmo_visible();
+
+ void _hide_handles();
+
+ void _draw_gizmo();
+ void _draw_handles();
+
+ void _joint_tree_selection_changed();
+ void _joint_tree_rmb_select(const Vector2 &p_pos);
+ void _update_properties();
+
+ void _subgizmo_selection_change();
+
+ int selected_bone = -1;
+
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
static void _bind_methods();
public:
+ static Skeleton3DEditor *get_singleton() { return singleton; }
+
+ void select_bone(int p_idx);
+
+ int get_selected_bone() const;
+
void move_skeleton_bone(NodePath p_skeleton_path, int32_t p_selected_boneidx, int32_t p_target_boneidx);
Skeleton3D *get_skeleton() const { return skeleton; };
- void _joint_tree_selection_changed();
- void _joint_tree_rmb_select(const Vector2 &p_pos);
+ bool is_edit_mode() const { return edit_mode; }
- void _update_properties();
+ void update_bone_original();
+ Transform3D get_bone_original() { return bone_original; };
Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, EditorNode *p_editor, Skeleton3D *skeleton);
~Skeleton3DEditor();
@@ -186,6 +250,7 @@ class EditorInspectorPluginSkeleton : public EditorInspectorPlugin {
friend class Skeleton3DEditorPlugin;
+ Skeleton3DEditor *skel_editor;
EditorNode *editor;
public:
@@ -196,12 +261,40 @@ public:
class Skeleton3DEditorPlugin : public EditorPlugin {
GDCLASS(Skeleton3DEditorPlugin, EditorPlugin);
+ EditorInspectorPluginSkeleton *skeleton_plugin;
EditorNode *editor;
public:
- Skeleton3DEditorPlugin(EditorNode *p_node);
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override;
+
+ bool has_main_screen() const override { return false; }
+ virtual bool handles(Object *p_object) const override;
virtual String get_name() const override { return "Skeleton3D"; }
+
+ Skeleton3DEditorPlugin(EditorNode *p_node);
+};
+
+class Skeleton3DGizmoPlugin : public EditorNode3DGizmoPlugin {
+ GDCLASS(Skeleton3DGizmoPlugin, EditorNode3DGizmoPlugin);
+
+ Ref<StandardMaterial3D> unselected_mat;
+ Ref<ShaderMaterial> selected_mat;
+ Ref<Shader> selected_sh;
+
+public:
+ bool has_gizmo(Node3D *p_spatial) override;
+ String get_gizmo_name() const override;
+ int get_priority() const override;
+
+ int subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const override;
+ Transform3D get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const override;
+ void set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) override;
+ void commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) override;
+
+ void redraw(EditorNode3DGizmo *p_gizmo) override;
+
+ Skeleton3DGizmoPlugin();
};
#endif // SKELETON_3D_EDITOR_PLUGIN_H