summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/core_bind.cpp22
-rw-r--r--core/core_bind.h5
-rw-r--r--doc/classes/EditorPlugin.xml19
-rw-r--r--doc/classes/Node3D.xml9
-rw-r--r--doc/classes/Skeleton3D.xml26
-rw-r--r--doc/classes/Thread.xml16
-rw-r--r--editor/animation_track_editor.cpp363
-rw-r--r--editor/animation_track_editor.h8
-rw-r--r--editor/editor_node.cpp16
-rw-r--r--editor/editor_node.h2
-rw-r--r--editor/editor_plugin.cpp8
-rw-r--r--editor/editor_plugin.h10
-rw-r--r--editor/editor_themes.cpp1
-rw-r--r--editor/icons/EditorBoneHandle.svg1
-rw-r--r--editor/icons/ToolBoneSelect.svg1
-rw-r--r--editor/inspector_dock.cpp6
-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.cpp939
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.h135
-rw-r--r--editor/scene_tree_dock.cpp12
-rw-r--r--editor/scene_tree_editor.cpp4
-rw-r--r--modules/gdscript/gdscript_cache.cpp8
-rw-r--r--modules/gdscript/gdscript_parser.cpp2
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp48
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h4
-rw-r--r--scene/3d/node_3d.cpp13
-rw-r--r--scene/3d/node_3d.h4
-rw-r--r--scene/3d/skeleton_3d.cpp75
-rw-r--r--scene/3d/skeleton_3d.h6
-rw-r--r--scene/3d/soft_dynamic_body_3d.cpp5
-rw-r--r--scene/3d/soft_dynamic_body_3d.h2
-rw-r--r--scene/scene_string_names.cpp3
-rw-r--r--scene/scene_string_names.h3
-rw-r--r--servers/rendering/renderer_rd/effects_rd.cpp5
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp34
45 files changed, 1532 insertions, 636 deletions
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index f630adc39e..e11d9ab9c1 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -1771,6 +1771,7 @@ void Thread::_start_func(void *ud) {
Object *target_instance = t->target_callable.get_object();
if (!target_instance) {
+ t->running.clear();
ERR_FAIL_MSG(vformat("Could not call function '%s' on previously freed instance to start thread %s.", t->target_callable.get_method(), t->get_id()));
}
@@ -1813,19 +1814,22 @@ void Thread::_start_func(void *ud) {
t->target_callable.call(arg, argc, t->ret, ce);
if (ce.error != Callable::CallError::CALL_OK) {
+ t->running.clear();
ERR_FAIL_MSG("Could not call function '" + t->target_callable.get_method().operator String() + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, arg, argc, ce) + ".");
}
+
+ t->running.clear();
}
Error Thread::start(const Callable &p_callable, const Variant &p_userdata, Priority p_priority) {
- ERR_FAIL_COND_V_MSG(active.is_set(), ERR_ALREADY_IN_USE, "Thread already started.");
+ ERR_FAIL_COND_V_MSG(is_started(), ERR_ALREADY_IN_USE, "Thread already started.");
ERR_FAIL_COND_V(p_callable.is_null(), ERR_INVALID_PARAMETER);
ERR_FAIL_INDEX_V(p_priority, PRIORITY_MAX, ERR_INVALID_PARAMETER);
ret = Variant();
target_callable = p_callable;
userdata = p_userdata;
- active.set();
+ running.set();
Ref<Thread> *ud = memnew(Ref<Thread>(this));
@@ -1840,15 +1844,18 @@ String Thread::get_id() const {
return itos(thread.get_id());
}
-bool Thread::is_active() const {
- return active.is_set();
+bool Thread::is_started() const {
+ return thread.is_started();
+}
+
+bool Thread::is_alive() const {
+ return running.is_set();
}
Variant Thread::wait_to_finish() {
- ERR_FAIL_COND_V_MSG(!active.is_set(), Variant(), "Thread must be active to wait for its completion.");
+ ERR_FAIL_COND_V_MSG(!is_started(), Variant(), "Thread must have been started to wait for its completion.");
thread.wait_to_finish();
Variant r = ret;
- active.clear();
target_callable = Callable();
userdata = Variant();
@@ -1858,7 +1865,8 @@ Variant Thread::wait_to_finish() {
void Thread::_bind_methods() {
ClassDB::bind_method(D_METHOD("start", "callable", "userdata", "priority"), &Thread::start, DEFVAL(Variant()), DEFVAL(PRIORITY_NORMAL));
ClassDB::bind_method(D_METHOD("get_id"), &Thread::get_id);
- ClassDB::bind_method(D_METHOD("is_active"), &Thread::is_active);
+ ClassDB::bind_method(D_METHOD("is_started"), &Thread::is_started);
+ ClassDB::bind_method(D_METHOD("is_alive"), &Thread::is_alive);
ClassDB::bind_method(D_METHOD("wait_to_finish"), &Thread::wait_to_finish);
BIND_ENUM_CONSTANT(PRIORITY_LOW);
diff --git a/core/core_bind.h b/core/core_bind.h
index 84a284f948..4eab085dda 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -538,7 +538,7 @@ class Thread : public RefCounted {
protected:
Variant ret;
Variant userdata;
- SafeFlag active;
+ SafeFlag running;
Callable target_callable;
::Thread thread;
static void _bind_methods();
@@ -554,7 +554,8 @@ public:
Error start(const Callable &p_callable, const Variant &p_userdata = Variant(), Priority p_priority = PRIORITY_NORMAL);
String get_id() const;
- bool is_active() const;
+ bool is_started() const;
+ bool is_alive() const;
Variant wait_to_finish();
};
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index 43e65f1aa0..4aa6963f57 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -96,7 +96,7 @@
</description>
</method>
<method name="_forward_3d_gui_input" qualifiers="virtual">
- <return type="bool" />
+ <return type="int" />
<argument index="0" name="viewport_camera" type="Camera3D" />
<argument index="1" name="event" type="InputEvent" />
<description>
@@ -105,13 +105,13 @@
[gdscript]
# Prevents the InputEvent to reach other Editor classes.
func _forward_3d_gui_input(camera, event):
- return true
+ return EditorPlugin.AFTER_GUI_INPUT_STOP
[/gdscript]
[csharp]
// Prevents the InputEvent to reach other Editor classes.
public override bool _Forward3dGuiInput(Camera3D camera, InputEvent @event)
{
- return true;
+ return EditorPlugin.AFTER_GUI_INPUT_STOP;
}
[/csharp]
[/codeblocks]
@@ -185,12 +185,12 @@
Called when there is a root node in the current edited scene, [method _handles] is implemented and an [InputEvent] happens in the 2D 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:
[codeblocks]
[gdscript]
- # Prevents the InputEvent to reach other Editor classes
+ # Prevents the InputEvent to reach other Editor classes.
func _forward_canvas_gui_input(event):
return true
[/gdscript]
[csharp]
- // Prevents the InputEvent to reach other Editor classes
+ // Prevents the InputEvent to reach other Editor classes.
public override bool ForwardCanvasGuiInput(InputEvent @event)
{
return true;
@@ -202,13 +202,18 @@
[gdscript]
# Consumes InputEventMouseMotion and forwards other InputEvent types.
func _forward_canvas_gui_input(event):
- return event is InputEventMouseMotion
+ if (event is InputEventMouseMotion):
+ return true
+ return false
[/gdscript]
[csharp]
// Consumes InputEventMouseMotion and forwards other InputEvent types.
public override bool ForwardCanvasGuiInput(InputEvent @event)
{
- return @event is InputEventMouseMotion;
+ if (@event is InputEventMouseMotion) {
+ return true;
+ }
+ return false
}
[/csharp]
[/codeblocks]
diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml
index 49901dc4d9..28acd77a39 100644
--- a/doc/classes/Node3D.xml
+++ b/doc/classes/Node3D.xml
@@ -212,6 +212,15 @@
Sets whether the node notifies about its global and local transformation changes. [Node3D] will not propagate this by default, unless it is in the editor context and it has a valid gizmo.
</description>
</method>
+ <method name="set_subgizmo_selection">
+ <return type="void" />
+ <argument index="0" name="gizmo" type="Node3DGizmo" />
+ <argument index="1" name="id" type="int" />
+ <argument index="2" name="transform" type="Transform3D" />
+ <description>
+ Set subgizmo selection for this node in the editor.
+ </description>
+ </method>
<method name="show">
<return type="void" />
<description>
diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml
index c2b514f232..6ab5ed55a3 100644
--- a/doc/classes/Skeleton3D.xml
+++ b/doc/classes/Skeleton3D.xml
@@ -189,6 +189,13 @@
This is helper function to make using [method Transform3D.looking_at] easier with bone poses.
</description>
</method>
+ <method name="is_bone_enabled" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="bone_idx" type="int" />
+ <description>
+ Returns whether the bone pose for the bone at [code]bone_idx[/code] is enabled.
+ </description>
+ </method>
<method name="is_bone_rest_disabled" qualifiers="const">
<return type="bool" />
<argument index="0" name="bone_idx" type="int" />
@@ -282,6 +289,14 @@
Disables the rest pose for the bone at [code]bone_idx[/code] if [code]true[/code], enables the bone rest if [code]false[/code].
</description>
</method>
+ <method name="set_bone_enabled">
+ <return type="void" />
+ <argument index="0" name="bone_idx" type="int" />
+ <argument index="1" name="enabled" type="bool" default="true" />
+ <description>
+ Disables the pose for the bone at [code]bone_idx[/code] if [code]false[/code], enables the bone pose if [code]true[/code].
+ </description>
+ </method>
<method name="set_bone_global_pose_override">
<return type="void" />
<argument index="0" name="bone_idx" type="int" />
@@ -365,8 +380,15 @@
<members>
<member name="animate_physical_bones" type="bool" setter="set_animate_physical_bones" getter="get_animate_physical_bones" default="true">
</member>
+ <member name="show_rest_only" type="bool" setter="set_show_rest_only" getter="is_show_rest_only" default="false">
+ </member>
</members>
<signals>
+ <signal name="bone_enabled_changed">
+ <argument index="0" name="bone_idx" type="int" />
+ <description>
+ </description>
+ </signal>
<signal name="bone_pose_changed">
<argument index="0" name="bone_idx" type="int" />
<description>
@@ -377,6 +399,10 @@
<description>
</description>
</signal>
+ <signal name="show_rest_only_changed">
+ <description>
+ </description>
+ </signal>
</signals>
<constants>
<constant name="NOTIFICATION_UPDATE_SKELETON" value="50">
diff --git a/doc/classes/Thread.xml b/doc/classes/Thread.xml
index d5069dd8f3..eb2df8a4f9 100644
--- a/doc/classes/Thread.xml
+++ b/doc/classes/Thread.xml
@@ -19,10 +19,17 @@
Returns the current [Thread]'s ID, uniquely identifying it among all threads. If the [Thread] is not running this returns an empty string.
</description>
</method>
- <method name="is_active" qualifiers="const">
+ <method name="is_alive" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if this [Thread] is currently active. An active [Thread] cannot start work on a new method but can be joined with [method wait_to_finish].
+ Returns [code]true[/code] if this [Thread] is currently running. This is useful for determining if [method wait_to_finish] can be called without blocking the calling thread.
+ To check if a [Thread] is joinable, use [method is_started].
+ </description>
+ </method>
+ <method name="is_started" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if this [Thread] has been started. Once started, this will return [code]true[/code] until it is joined using [method wait_to_finish]. For checking if a [Thread] is still executing its task, use [method is_alive].
</description>
</method>
<method name="start">
@@ -31,15 +38,16 @@
<argument index="1" name="userdata" type="Variant" default="null" />
<argument index="2" name="priority" type="int" enum="Thread.Priority" default="1" />
<description>
- Starts a new [Thread] that calls [code]callable[/code] with [code]userdata[/code] passed as an argument. Even if no userdata is passed, [code]method[/code] must accept one argument and it will be null. The [code]priority[/code] of the [Thread] can be changed by passing a value from the [enum Priority] enum.
+ Starts a new [Thread] that calls [code]callable[/code] with [code]userdata[/code] passed as an argument. Even if no userdata is passed, [code]callable[/code] must accept one argument and it will be null. The [code]priority[/code] of the [Thread] can be changed by passing a value from the [enum Priority] enum.
Returns [constant OK] on success, or [constant ERR_CANT_CREATE] on failure.
</description>
</method>
<method name="wait_to_finish">
<return type="Variant" />
<description>
- Joins the [Thread] and waits for it to finish. Returns what the method called returned.
+ Joins the [Thread] and waits for it to finish. Returns the output of the [Callable] passed to [method start].
Should either be used when you want to retrieve the value returned from the method called by the [Thread] or before freeing the instance that contains the [Thread].
+ To determine if this can be called without blocking the calling thread, check if [method is_alive] is [code]false[/code].
[b]Note:[/b] After the [Thread] finishes joining it will be disposed. If you want to use it again you will have to create a new instance of it.
</description>
</method>
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index d5afd5020c..14ca35a664 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -541,7 +541,7 @@ public:
String hint_string;
if (v.get_type() == Variant::OBJECT) {
- //could actually check the object property if exists..? yes i will!
+ // Could actually check the object property if exists..? Yes I will!
Ref<Resource> res = v;
if (res.is_valid()) {
hint = PROPERTY_HINT_RESOURCE_TYPE;
@@ -1177,7 +1177,7 @@ public:
String hint_string;
if (v.get_type() == Variant::OBJECT) {
- //could actually check the object property if exists..? yes i will!
+ // Could actually check the object property if exists..? Yes I will!
Ref<Resource> res = v;
if (res.is_valid()) {
hint = PROPERTY_HINT_RESOURCE_TYPE;
@@ -1388,7 +1388,7 @@ void AnimationTimelineEdit::_notification(int p_what) {
float l = animation->get_length();
if (l <= 0) {
- l = 0.001; //avoid crashor
+ l = 0.001; // Avoid crashor.
}
Ref<Texture2D> hsize_icon = get_theme_icon(SNAME("Hsize"), SNAME("EditorIcons"));
@@ -1401,18 +1401,12 @@ void AnimationTimelineEdit::_notification(int p_what) {
for (int i = 0; i < animation->get_track_count(); i++) {
if (animation->track_get_key_count(i) > 0) {
float beg = animation->track_get_key_time(i, 0);
- /*if (animation->track_get_type(i) == Animation::TYPE_BEZIER) {
- beg += animation->bezier_track_get_key_in_handle(i, 0).x;
- }* not worth it since they have no use */
if (beg < time_min) {
time_min = beg;
}
float end = animation->track_get_key_time(i, animation->track_get_key_count(i) - 1);
- /*if (animation->track_get_type(i) == Animation::TYPE_BEZIER) {
- end += animation->bezier_track_get_key_out_handle(i, animation->track_get_key_count(i) - 1).x;
- } not worth it since they have no use */
if (end > time_max) {
time_max = end;
@@ -1422,8 +1416,6 @@ void AnimationTimelineEdit::_notification(int p_what) {
float extra = (zoomw / scale) * 0.5;
- //if (time_min < -0.001)
- // time_min -= extra;
time_max += extra;
set_min(time_min);
set_max(time_max);
@@ -1853,7 +1845,7 @@ void AnimationTrackEdit::_notification(int p_what) {
{
Ref<Texture2D> check = animation->track_is_enabled(track) ? get_theme_icon(SNAME("checked"), SNAME("CheckBox")) : get_theme_icon(SNAME("unchecked"), SNAME("CheckBox"));
- int ofs = in_group ? check->get_width() : 0; //not the best reference for margin but..
+ int ofs = in_group ? check->get_width() : 0; // Not the best reference for margin but..
check_rect = Rect2(Point2(ofs, int(get_size().height - check->get_height()) / 2), check->get_size());
draw_texture(check, check_rect.position);
@@ -1971,7 +1963,7 @@ void AnimationTrackEdit::_notification(int p_what) {
ofs += hsep;
{
- //callmode
+ // Callmode.
Animation::UpdateMode update_mode;
@@ -1990,7 +1982,7 @@ void AnimationTrackEdit::_notification(int p_what) {
if (animation->track_get_type(track) == Animation::TYPE_VALUE) {
draw_texture(update_icon, update_mode_rect.position);
}
- //make it easier to click
+ // Make it easier to click.
update_mode_rect.position.y = 0;
update_mode_rect.size.y = get_size().height;
@@ -2019,7 +2011,7 @@ void AnimationTrackEdit::_notification(int p_what) {
}
{
- //interp
+ // Interp.
Animation::InterpolationType interp_mode = animation->track_get_interpolation_type(track);
@@ -2032,7 +2024,7 @@ void AnimationTrackEdit::_notification(int p_what) {
if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
draw_texture(icon, interp_mode_rect.position);
}
- //make it easier to click
+ // Make it easier to click.
interp_mode_rect.position.y = 0;
interp_mode_rect.size.y = get_size().height;
@@ -2052,7 +2044,7 @@ void AnimationTrackEdit::_notification(int p_what) {
}
{
- //loop
+ // Loop.
bool loop_wrap = animation->track_get_interpolation_loop_wrap(track);
@@ -2085,7 +2077,7 @@ void AnimationTrackEdit::_notification(int p_what) {
}
{
- //erase
+ // Erase.
Ref<Texture2D> icon = get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"));
@@ -2132,7 +2124,7 @@ Rect2 AnimationTrackEdit::get_key_rect(int p_index, float p_pixels_sec) {
}
Rect2 rect = Rect2(-type_icon->get_width() / 2, 0, type_icon->get_width(), get_size().height);
- //make it a big easier to click
+ // Make it a big easier to click.
rect.position.x -= rect.size.x * 0.5;
rect.size.x *= 2;
return rect;
@@ -2221,7 +2213,7 @@ void AnimationTrackEdit::draw_key(int p_index, float p_pixels_sec, int p_x, bool
draw_texture(icon_to_draw, ofs);
}
-//helper
+// Helper.
void AnimationTrackEdit::draw_rect_clipped(const Rect2 &p_rect, const Color &p_color, bool p_filled) {
int clip_left = timeline->get_name_limit();
int clip_right = get_size().width - timeline->get_buttons_width();
@@ -2246,7 +2238,7 @@ void AnimationTrackEdit::draw_texture_region_clipped(const Ref<Texture2D> &p_tex
int clip_left = timeline->get_name_limit();
int clip_right = get_size().width - timeline->get_buttons_width();
- //clip left and right
+ // Clip left and right.
if (clip_left > p_rect.position.x + p_rect.size.x) {
return;
}
@@ -2455,7 +2447,7 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
key_distance = distance;
}
} else {
- //first one does it
+ // First one does it.
break;
}
}
@@ -3001,7 +2993,7 @@ AnimationTrackEdit::AnimationTrackEdit() {
play_position->set_anchors_and_offsets_preset(PRESET_WIDE);
play_position->connect("draw", callable_mp(this, &AnimationTrackEdit::_play_position_draw));
set_focus_mode(FOCUS_CLICK);
- set_mouse_filter(MOUSE_FILTER_PASS); //scroll has to work too for selection
+ set_mouse_filter(MOUSE_FILTER_PASS); // Scroll has to work too for selection.
}
//////////////////////////////////////
@@ -3338,30 +3330,16 @@ static bool track_type_is_resettable(Animation::TrackType p_type) {
}
}
-void AnimationTrackEditor::_query_insert(const InsertData &p_id) {
- if (insert_frame != Engine::get_singleton()->get_frames_drawn()) {
- //clear insert list for the frame if frame changed
- if (insert_confirm->is_visible()) {
- return; //do nothing
- }
- insert_data.clear();
- insert_query = false;
- }
- insert_frame = Engine::get_singleton()->get_frames_drawn();
-
- for (const InsertData &E : insert_data) {
- //prevent insertion of multiple tracks
- if (E.path == p_id.path) {
- return; //already inserted a track for this on this frame
- }
- }
-
- insert_data.push_back(p_id);
+void AnimationTrackEditor::make_insert_queue() {
+ insert_data.clear();
+ insert_queue = true;
+}
+void AnimationTrackEditor::commit_insert_queue() {
bool reset_allowed = true;
- AnimationPlayer *player = AnimationPlayerEditor::singleton->get_player();
+ AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player();
if (player->has_animation("RESET") && player->get_animation("RESET") == animation) {
- // Avoid messing with the reset animation itself
+ // Avoid messing with the reset animation itself.
reset_allowed = false;
} else {
bool some_resettable = false;
@@ -3376,74 +3354,82 @@ void AnimationTrackEditor::_query_insert(const InsertData &p_id) {
}
}
- if (p_id.track_idx == -1) {
- int num_tracks = 0;
- bool all_bezier = true;
- for (int i = 0; i < insert_data.size(); i++) {
- if (insert_data[i].type != Animation::TYPE_VALUE && insert_data[i].type != Animation::TYPE_BEZIER) {
- all_bezier = false;
- }
-
- if (insert_data[i].track_idx == -1) {
- ++num_tracks;
- }
+ // Organize insert data.
+ int num_tracks = 0;
+ String last_track_query;
+ bool all_bezier = true;
+ for (int i = 0; i < insert_data.size(); i++) {
+ if (insert_data[i].type != Animation::TYPE_VALUE && insert_data[i].type != Animation::TYPE_BEZIER) {
+ all_bezier = false;
+ }
- if (insert_data[i].type != Animation::TYPE_VALUE) {
- continue;
- }
+ if (insert_data[i].track_idx == -1) {
+ ++num_tracks;
+ last_track_query = insert_data[i].query;
+ }
- switch (insert_data[i].value.get_type()) {
- case Variant::INT:
- case Variant::FLOAT:
- case Variant::VECTOR2:
- case Variant::VECTOR3:
- case Variant::QUATERNION:
- case Variant::PLANE:
- case Variant::COLOR: {
- // Valid.
- } break;
- default: {
- all_bezier = false;
- }
- }
+ if (insert_data[i].type != Animation::TYPE_VALUE) {
+ continue;
}
- if (bool(EDITOR_DEF("editors/animation/confirm_insert_track", true))) {
- //potential new key, does not exist
- if (num_tracks == 1) {
- // TRANSLATORS: %s will be replaced by a phrase describing the target of track.
- insert_confirm_text->set_text(vformat(TTR("Create new track for %s and insert key?"), p_id.query));
- } else {
- insert_confirm_text->set_text(vformat(TTR("Create %d new tracks and insert keys?"), num_tracks));
+ switch (insert_data[i].value.get_type()) {
+ case Variant::INT:
+ case Variant::FLOAT:
+ case Variant::VECTOR2:
+ case Variant::VECTOR3:
+ case Variant::QUATERNION:
+ case Variant::PLANE:
+ case Variant::COLOR: {
+ // Valid.
+ } break;
+ default: {
+ all_bezier = false;
}
+ }
+ }
- insert_confirm_bezier->set_visible(all_bezier);
- insert_confirm_reset->set_visible(reset_allowed);
-
- insert_confirm->get_ok_button()->set_text(TTR("Create"));
- insert_confirm->popup_centered();
- insert_query = true;
+ if (bool(EDITOR_DEF("editors/animation/confirm_insert_track", true)) && num_tracks > 0) {
+ // Potentially a new key, does not exist.
+ if (num_tracks == 1) {
+ // TRANSLATORS: %s will be replaced by a phrase describing the target of track.
+ insert_confirm_text->set_text(vformat(TTR("Create new track for %s and insert key?"), last_track_query));
} else {
- call_deferred(SNAME("_insert_delay"), reset_allowed && EDITOR_GET("editors/animation/default_create_reset_tracks"), all_bezier && EDITOR_GET("editors/animation/default_create_bezier_tracks"));
- insert_queue = true;
+ insert_confirm_text->set_text(vformat(TTR("Create %d new tracks and insert keys?"), num_tracks));
}
+ insert_confirm_bezier->set_visible(all_bezier);
+ insert_confirm_reset->set_visible(reset_allowed);
+
+ insert_confirm->get_ok_button()->set_text(TTR("Create"));
+ insert_confirm->popup_centered();
} else {
- if (!insert_query && !insert_queue) {
- // Create Beziers wouldn't make sense in this case, where no tracks are being created
- call_deferred(SNAME("_insert_delay"), reset_allowed && EDITOR_GET("editors/animation/default_create_reset_tracks"), false);
- insert_queue = true;
- }
+ _insert_track(reset_allowed && EDITOR_GET("editors/animation/default_create_reset_tracks"), all_bezier && EDITOR_GET("editors/animation/default_create_bezier_tracks"));
}
+
+ insert_queue = false;
}
-void AnimationTrackEditor::_insert_delay(bool p_create_reset, bool p_create_beziers) {
- if (insert_query) {
- //discard since it's entered into query mode
- insert_queue = false;
- return;
+void AnimationTrackEditor::_query_insert(const InsertData &p_id) {
+ if (!insert_queue) {
+ insert_data.clear();
+ }
+
+ for (const InsertData &E : insert_data) {
+ // Prevent insertion of multiple tracks.
+ if (E.path == p_id.path) {
+ return; // Already inserted a track this frame.
+ }
+ }
+
+ insert_data.push_back(p_id);
+
+ // Without queue, commit immediately.
+ if (!insert_queue) {
+ commit_insert_queue();
}
+}
+void AnimationTrackEditor::_insert_track(bool p_create_reset, bool p_create_beziers) {
undo_redo->create_action(TTR("Anim Insert"));
Ref<Animation> reset_anim;
@@ -3478,7 +3464,6 @@ void AnimationTrackEditor::_insert_delay(bool p_create_reset, bool p_create_bezi
set_anim_pos(pos);
emit_signal(SNAME("timeline_changed"), pos, true);
}
- insert_queue = false;
}
void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_sub, const Transform3D &p_xform) {
@@ -3490,7 +3475,7 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
}
ERR_FAIL_COND(!root);
- //let's build a node path
+ // Let's build a node path.
String path = root->get_path_to(p_node);
if (p_sub != "") {
path += ":" + p_sub;
@@ -3523,20 +3508,44 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
id.query = vformat(TTR("node '%s'"), p_node->get_name());
id.advance = false;
- //dialog insert
-
+ // Dialog insert.
_query_insert(id);
}
+bool AnimationTrackEditor::has_transform_track(Node3D *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;
+ }
+ int track_id = animation->find_track(path);
+ if (track_id >= 0) {
+ if (animation->track_get_type(track_id) == Animation::TYPE_TRANSFORM3D) {
+ return true;
+ }
+ }
+ return false;
+}
+
void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant &p_value) {
String path = p_path;
- //animation property is a special case, always creates an animation track
+ // Animation property is a special case, always creates an animation track.
for (int i = 0; i < animation->get_track_count(); i++) {
String np = animation->track_get_path(i);
if (path == np && animation->track_get_type(i) == Animation::TYPE_ANIMATION) {
- //exists
+ // Exists.
InsertData id;
id.path = path;
id.track_idx = i;
@@ -3545,7 +3554,7 @@ void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant
// TRANSLATORS: This describes the target of new animation track, will be inserted into another string.
id.query = TTR("animation");
id.advance = false;
- //dialog insert
+ // Dialog insert.
_query_insert(id);
return;
}
@@ -3558,20 +3567,20 @@ void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant
id.type = Animation::TYPE_ANIMATION;
id.query = TTR("animation");
id.advance = false;
- //dialog insert
+ // Dialog insert.
_query_insert(id);
}
void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists) {
ERR_FAIL_COND(!root);
- //let's build a node path
+ // Let's build a node path.
Node *node = p_node;
String path = root->get_path_to(node);
if (Object::cast_to<AnimationPlayer>(node) && p_property == "current_animation") {
- if (node == AnimationPlayerEditor::singleton->get_player()) {
+ if (node == AnimationPlayerEditor::get_singleton()->get_player()) {
EditorNode::get_singleton()->show_warning(TTR("AnimationPlayer can't animate itself, only other players."));
return;
}
@@ -3590,7 +3599,7 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
NodePath np = path;
- //locate track
+ // Locate track.
bool inserted = false;
@@ -3608,14 +3617,14 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
// TRANSLATORS: This describes the target of new animation track, will be inserted into another string.
id.query = vformat(TTR("property '%s'"), p_property);
id.advance = false;
- //dialog insert
+ // Dialog insert.
_query_insert(id);
inserted = true;
} else if (animation->track_get_type(i) == Animation::TYPE_BEZIER) {
Variant value;
String track_path = animation->track_get_path(i);
if (track_path == np) {
- value = p_value; //all good
+ value = p_value; // All good.
} else {
int sep = track_path.rfind(":");
if (sep != -1) {
@@ -3638,7 +3647,7 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
id.type = Animation::TYPE_BEZIER;
id.query = vformat(TTR("property '%s'"), p_property);
id.advance = false;
- //dialog insert
+ // Dialog insert.
_query_insert(id);
inserted = true;
}
@@ -3654,7 +3663,7 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
id.type = Animation::TYPE_VALUE;
id.query = vformat(TTR("property '%s'"), p_property);
id.advance = false;
- //dialog insert
+ // Dialog insert.
_query_insert(id);
}
@@ -3662,7 +3671,7 @@ void AnimationTrackEditor::insert_value_key(const String &p_property, const Vari
EditorHistory *history = EditorNode::get_singleton()->get_editor_history();
ERR_FAIL_COND(!root);
- //let's build a node path
+ // Let's build a node path.
ERR_FAIL_COND(history->get_path_size() == 0);
Object *obj = ObjectDB::get_instance(history->get_path_object(0));
ERR_FAIL_COND(!Object::cast_to<Node>(obj));
@@ -3672,7 +3681,7 @@ void AnimationTrackEditor::insert_value_key(const String &p_property, const Vari
String path = root->get_path_to(node);
if (Object::cast_to<AnimationPlayer>(node) && p_property == "current_animation") {
- if (node == AnimationPlayerEditor::singleton->get_player()) {
+ if (node == AnimationPlayerEditor::get_singleton()->get_player()) {
EditorNode::get_singleton()->show_warning(TTR("AnimationPlayer can't animate itself, only other players."));
return;
}
@@ -3690,10 +3699,11 @@ void AnimationTrackEditor::insert_value_key(const String &p_property, const Vari
NodePath np = path;
- //locate track
+ // Locate track.
bool inserted = false;
+ make_insert_queue();
for (int i = 0; i < animation->get_track_count(); i++) {
if (animation->track_get_type(i) == Animation::TYPE_VALUE) {
if (animation->track_get_path(i) != np) {
@@ -3707,13 +3717,13 @@ void AnimationTrackEditor::insert_value_key(const String &p_property, const Vari
id.type = Animation::TYPE_VALUE;
id.query = vformat(TTR("property '%s'"), p_property);
id.advance = p_advance;
- //dialog insert
+ // Dialog insert.
_query_insert(id);
inserted = true;
} else if (animation->track_get_type(i) == Animation::TYPE_BEZIER) {
Variant value;
if (animation->track_get_path(i) == np) {
- value = p_value; //all good
+ value = p_value; // All good.
} else {
String tpath = animation->track_get_path(i);
int index = tpath.rfind(":");
@@ -3732,11 +3742,12 @@ void AnimationTrackEditor::insert_value_key(const String &p_property, const Vari
id.type = Animation::TYPE_BEZIER;
id.query = vformat(TTR("property '%s'"), p_property);
id.advance = p_advance;
- //dialog insert
+ // Dialog insert.
_query_insert(id);
inserted = true;
}
}
+ commit_insert_queue();
if (!inserted) {
InsertData id;
@@ -3746,13 +3757,13 @@ void AnimationTrackEditor::insert_value_key(const String &p_property, const Vari
id.type = Animation::TYPE_VALUE;
id.query = vformat(TTR("property '%s'"), p_property);
id.advance = p_advance;
- //dialog insert
+ // Dialog insert.
_query_insert(id);
}
}
Ref<Animation> AnimationTrackEditor::_create_and_get_reset_animation() {
- AnimationPlayer *player = AnimationPlayerEditor::singleton->get_player();
+ AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player();
if (player->has_animation("RESET")) {
return player->get_animation("RESET");
} else {
@@ -3760,9 +3771,9 @@ Ref<Animation> AnimationTrackEditor::_create_and_get_reset_animation() {
reset_anim.instantiate();
reset_anim->set_length(ANIM_MIN_LENGTH);
undo_redo->add_do_method(player, "add_animation", "RESET", reset_anim);
- undo_redo->add_do_method(AnimationPlayerEditor::singleton, "_animation_player_changed", player);
+ undo_redo->add_do_method(AnimationPlayerEditor::get_singleton(), "_animation_player_changed", player);
undo_redo->add_undo_method(player, "remove_animation", "RESET");
- undo_redo->add_undo_method(AnimationPlayerEditor::singleton, "_animation_player_changed", player);
+ undo_redo->add_undo_method(AnimationPlayerEditor::get_singleton(), "_animation_player_changed", player);
return reset_anim;
}
}
@@ -3929,7 +3940,7 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
animation->add_track(p_id.type);
animation->track_set_path(animation->get_track_count() - 1, p_id.path);
PropertyInfo h = _find_hint_for_track(animation->get_track_count() - 1, np);
- animation->remove_track(animation->get_track_count() - 1); //hack
+ animation->remove_track(animation->get_track_count() - 1); // Hack.
if (h.type == Variant::FLOAT ||
h.type == Variant::VECTOR2 ||
@@ -4078,7 +4089,7 @@ void AnimationTrackEditor::_update_tracks() {
for (int i = 0; i < animation->get_track_count(); i++) {
AnimationTrackEdit *track_edit = nullptr;
- //find hint and info for plugin
+ // Find hint and info for plugin.
if (use_filter) {
NodePath path = animation->track_get_path(i);
@@ -4086,10 +4097,10 @@ void AnimationTrackEditor::_update_tracks() {
if (root && root->has_node(path)) {
Node *node = root->get_node(path);
if (!node) {
- continue; // no node, no filter
+ continue; // No node, no filter.
}
if (!EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) {
- continue; //skip track due to not selected
+ continue; // Skip track due to not selected.
}
}
}
@@ -4151,7 +4162,7 @@ void AnimationTrackEditor::_update_tracks() {
}
if (track_edit == nullptr) {
- //no valid plugin_found
+ // No valid plugin_found.
track_edit = memnew(AnimationTrackEdit);
}
@@ -4226,11 +4237,11 @@ void AnimationTrackEditor::_update_tracks() {
void AnimationTrackEditor::_animation_changed() {
if (animation_changing_awaiting_update) {
- return; //all will be updated, don't bother with anything
+ return; // All will be updated, don't bother with anything.
}
if (key_edit && key_edit->setting) {
- //if editing a key, just update the edited track, makes refresh less costly
+ // If editing a key, just update the edited track, makes refresh less costly.
if (key_edit->track < track_edits.size()) {
if (animation->track_get_type(key_edit->track) == Animation::TYPE_BEZIER) {
bezier_edit->update();
@@ -4284,7 +4295,7 @@ void AnimationTrackEditor::_animation_update() {
}
if (track_edits.size() == animation->get_track_count()) {
- //check tracks are the same
+ // Check tracks are the same.
for (int i = 0; i < track_edits.size(); i++) {
if (track_edits[i]->get_path() != animation->track_get_path(i)) {
@@ -4446,7 +4457,7 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
return;
}
- if (node == AnimationPlayerEditor::singleton->get_player()) {
+ if (node == AnimationPlayerEditor::get_singleton()->get_player()) {
EditorNode::get_singleton()->show_warning(TTR("AnimationPlayer can't animate itself, only other players."));
return;
}
@@ -4478,12 +4489,12 @@ void AnimationTrackEditor::_new_track_property_selected(String p_name) {
if (adding_track_type == Animation::TYPE_VALUE) {
Animation::UpdateMode update_mode = Animation::UPDATE_DISCRETE;
{
- //hack
+ // Hack.
NodePath np;
animation->add_track(Animation::TYPE_VALUE);
animation->track_set_path(animation->get_track_count() - 1, full_path);
PropertyInfo h = _find_hint_for_track(animation->get_track_count() - 1, np);
- animation->remove_track(animation->get_track_count() - 1); //hack
+ animation->remove_track(animation->get_track_count() - 1); // Hack.
if (h.type == Variant::FLOAT ||
h.type == Variant::VECTOR2 ||
h.type == Variant::RECT2 ||
@@ -4511,12 +4522,12 @@ void AnimationTrackEditor::_new_track_property_selected(String p_name) {
} else {
Vector<String> subindices;
{
- //hack
+ // Hack.
NodePath np;
animation->add_track(Animation::TYPE_VALUE);
animation->track_set_path(animation->get_track_count() - 1, full_path);
PropertyInfo h = _find_hint_for_track(animation->get_track_count() - 1, np);
- animation->remove_track(animation->get_track_count() - 1); //hack
+ animation->remove_track(animation->get_track_count() - 1); // Hack.
bool valid;
subindices = _get_bezier_subindices_for_type(h.type, &valid);
if (!valid) {
@@ -4568,7 +4579,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
if (snap->is_pressed() && step->get_value() != 0) {
p_ofs = snap_time(p_ofs);
}
- while (animation->track_find_key(p_track, p_ofs, true) != -1) { //make sure insertion point is valid
+ while (animation->track_find_key(p_track, p_ofs, true) != -1) { // Make sure insertion point is valid.
p_ofs += 0.001;
}
@@ -4763,16 +4774,16 @@ struct _AnimMoveRestore {
Variant key;
float transition = 0;
};
-//used for undo/redo
+// Used for undo/redo.
void AnimationTrackEditor::_clear_key_edit() {
if (key_edit) {
- //if key edit is the object being inspected, remove it first
+ // If key edit is the object being inspected, remove it first.
if (EditorNode::get_singleton()->get_inspector()->get_edited_object() == key_edit) {
EditorNode::get_singleton()->push_item(nullptr);
}
- //then actually delete it
+ // Then actually delete it.
memdelete(key_edit);
key_edit = nullptr;
}
@@ -4886,11 +4897,11 @@ void AnimationTrackEditor::_move_selection_commit() {
List<_AnimMoveRestore> to_restore;
float motion = moving_selection_offset;
- // 1 - remove the keys
+ // 1 - remove the keys.
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->key().track, E->key().key);
}
- // 2 - remove overlapped keys
+ // 2 - Remove overlapped keys.
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float newtime = snap_time(E->get().pos + motion);
int idx = animation->track_find_key(E->key().track, newtime, true);
@@ -4901,7 +4912,7 @@ void AnimationTrackEditor::_move_selection_commit() {
sk.key = idx;
sk.track = E->key().track;
if (selection.has(sk)) {
- continue; //already in selection, don't save
+ continue; // Already in selection, don't save.
}
undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newtime);
@@ -4915,24 +4926,24 @@ void AnimationTrackEditor::_move_selection_commit() {
to_restore.push_back(amr);
}
- // 3 - move the keys (re insert them)
+ // 3 - Move the keys (Reinsert them).
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float newpos = snap_time(E->get().pos + motion);
undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->key().track, newpos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
}
- // 4 - (undo) remove inserted keys
+ // 4 - (Undo) Remove inserted keys.
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float newpos = snap_time(E->get().pos + motion);
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newpos);
}
- // 5 - (undo) reinsert keys
+ // 5 - (Undo) Reinsert keys.
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->key().track, E->get().pos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
}
- // 6 - (undo) reinsert overlapped keys
+ // 6 - (Undo) Reinsert overlapped keys.
for (_AnimMoveRestore &amr : to_restore) {
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, amr.transition);
}
@@ -4940,7 +4951,7 @@ void AnimationTrackEditor::_move_selection_commit() {
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
- // 7 - reselect
+ // 7 - Reselect.
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float oldpos = E->get().pos;
float newpos = snap_time(oldpos + motion);
@@ -5010,18 +5021,18 @@ void AnimationTrackEditor::_scroll_input(const Ref<InputEvent> &p_event) {
box_select_rect = Rect2();
} else if (box_selecting) {
if (box_selection->is_visible_in_tree()) {
- //only if moved
+ // Only if moved.
for (int i = 0; i < track_edits.size(); i++) {
Rect2 local_rect = box_select_rect;
local_rect.position -= track_edits[i]->get_global_position();
track_edits[i]->append_to_selection(local_rect, mb->is_command_pressed());
}
- if (_get_track_selected() == -1 && track_edits.size() > 0) { //minimal hack to make shortcuts work
+ if (_get_track_selected() == -1 && track_edits.size() > 0) { // Minimal hack to make shortcuts work.
track_edits[track_edits.size() - 1]->grab_focus();
}
} else {
- _clear_selection(); //clear it
+ _clear_selection(); // Clear it.
}
box_selection->hide();
@@ -5037,7 +5048,7 @@ void AnimationTrackEditor::_scroll_input(const Ref<InputEvent> &p_event) {
if (mm.is_valid() && box_selecting) {
if (!(mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT)) {
- //no longer
+ // No longer.
box_selection->hide();
box_selecting = false;
return;
@@ -5077,16 +5088,16 @@ void AnimationTrackEditor::_cancel_bezier_edit() {
}
void AnimationTrackEditor::_bezier_edit(int p_for_track) {
- _clear_selection(); //bezier probably wants to use a separate selection mode
+ _clear_selection(); // Bezier probably wants to use a separate selection mode.
bezier_edit->set_root(root);
bezier_edit->set_animation_and_track(animation, p_for_track);
scroll->hide();
bezier_edit->show();
- //search everything within the track and curve- edit it
+ // Search everything within the track and curve - edit it.
}
void AnimationTrackEditor::_anim_duplicate_keys(bool transpose) {
- //duplicait!
+ // Duplicait!
if (selection.size() && animation.is_valid() && (!transpose || (_get_track_selected() >= 0 && _get_track_selected() < animation->get_track_count()))) {
int top_track = 0x7FFFFFFF;
float top_time = 1e10;
@@ -5144,7 +5155,7 @@ void AnimationTrackEditor::_anim_duplicate_keys(bool transpose) {
undo_redo->commit_action();
- //reselect duplicated
+ // Reselect duplicated.
Map<SelectedKey, KeyInfo> new_selection;
for (const Pair<int, float> &E : new_selection_values) {
@@ -5173,7 +5184,7 @@ void AnimationTrackEditor::_anim_duplicate_keys(bool transpose) {
}
void AnimationTrackEditor::_edit_menu_about_to_popup() {
- AnimationPlayer *player = AnimationPlayerEditor::singleton->get_player();
+ AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player();
edit->get_popup()->set_item_disabled(edit->get_popup()->get_item_index(EDIT_APPLY_RESET), !player->can_apply_reset());
}
@@ -5256,7 +5267,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
text += sn[j];
}
- path = NodePath(node->get_path().get_names(), path.get_subnames(), true); //store full path instead for copying
+ path = NodePath(node->get_path().get_names(), path.get_subnames(), true); // Store full path instead for copying.
} else {
text = path;
int sep = text.find(":");
@@ -5410,11 +5421,11 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
List<_AnimMoveRestore> to_restore;
- // 1-remove the keys
+ // 1 - Remove the keys.
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->key().track, E->key().key);
}
- // 2- remove overlapped keys
+ // 2 - Remove overlapped keys.
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float newtime = (E->get().pos - from_t) * s + from_t;
int idx = animation->track_find_key(E->key().track, newtime, true);
@@ -5425,7 +5436,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
sk.key = idx;
sk.track = E->key().track;
if (selection.has(sk)) {
- continue; //already in selection, don't save
+ continue; // Already in selection, don't save.
}
undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newtime);
@@ -5440,24 +5451,24 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
}
#define _NEW_POS(m_ofs) (((s > 0) ? m_ofs : from_t + (len - (m_ofs - from_t))) - pivot) * ABS(s) + from_t
- // 3-move the keys (re insert them)
+ // 3 - Move the keys (re insert them).
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float newpos = _NEW_POS(E->get().pos);
undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->key().track, newpos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
}
- // 4-(undo) remove inserted keys
+ // 4 - (Undo) Remove inserted keys.
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float newpos = _NEW_POS(E->get().pos);
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newpos);
}
- // 5-(undo) reinsert keys
+ // 5 - (Undo) Reinsert keys.
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->key().track, E->get().pos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
}
- // 6-(undo) reinsert overlapped keys
+ // 6 - (Undo) Reinsert overlapped keys.
for (_AnimMoveRestore &amr : to_restore) {
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, amr.transition);
}
@@ -5465,7 +5476,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
- // 7-reselect
+ // 7-reselect.
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float oldpos = E->get().pos;
float newpos = _NEW_POS(oldpos);
@@ -5517,7 +5528,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
goto_prev_step(false);
} break;
case EDIT_APPLY_RESET: {
- AnimationPlayerEditor::singleton->get_player()->apply_reset(true);
+ AnimationPlayerEditor::get_singleton()->get_player()->apply_reset(true);
} break;
case EDIT_OPTIMIZE_ANIMATION: {
@@ -5537,9 +5548,9 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
case EDIT_CLEAN_UP_ANIMATION_CONFIRM: {
if (cleanup_all->is_pressed()) {
List<StringName> names;
- AnimationPlayerEditor::singleton->get_player()->get_animation_list(&names);
+ AnimationPlayerEditor::get_singleton()->get_player()->get_animation_list(&names);
for (const StringName &E : names) {
- _cleanup_animation(AnimationPlayerEditor::singleton->get_player()->get_animation(E));
+ _cleanup_animation(AnimationPlayerEditor::get_singleton()->get_player()->get_animation(E));
}
} else {
_cleanup_animation(animation);
@@ -5614,7 +5625,7 @@ bool AnimationTrackEditor::is_grouping_tracks() {
void AnimationTrackEditor::_selection_changed() {
if (selected_filter->is_pressed()) {
- _update_tracks(); //needs updatin
+ _update_tracks(); // Needs updatin.
} else {
for (int i = 0; i < track_edits.size(); i++) {
track_edits[i]->update();
@@ -5683,7 +5694,6 @@ void AnimationTrackEditor::_bind_methods() {
ClassDB::bind_method("_animation_update", &AnimationTrackEditor::_animation_update);
ClassDB::bind_method("_track_grab_focus", &AnimationTrackEditor::_track_grab_focus);
ClassDB::bind_method("_update_tracks", &AnimationTrackEditor::_update_tracks);
- ClassDB::bind_method("_insert_delay", &AnimationTrackEditor::_insert_delay);
ClassDB::bind_method("_clear_selection_for_anim", &AnimationTrackEditor::_clear_selection_for_anim);
ClassDB::bind_method("_select_at_anim", &AnimationTrackEditor::_select_at_anim);
@@ -5769,7 +5779,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
undo_redo = EditorNode::get_singleton()->get_undo_redo();
main_panel = memnew(PanelContainer);
- main_panel->set_focus_mode(FOCUS_ALL); // allow panel to have focus so that shortcuts work as expected.
+ main_panel->set_focus_mode(FOCUS_ALL); // Allow panel to have focus so that shortcuts work as expected.
add_child(main_panel);
main_panel->set_v_size_flags(SIZE_EXPAND_FILL);
HBoxContainer *timeline_scroll = memnew(HBoxContainer);
@@ -5805,7 +5815,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
scroll->set_v_size_flags(SIZE_EXPAND_FILL);
VScrollBar *sb = scroll->get_v_scrollbar();
scroll->remove_child(sb);
- timeline_scroll->add_child(sb); //move here so timeline and tracks are always aligned
+ timeline_scroll->add_child(sb); // Move here so timeline and tracks are always aligned.
scroll->connect("gui_input", callable_mp(this, &AnimationTrackEditor::_scroll_input));
bezier_edit = memnew(AnimationBezierTrackEdit);
@@ -5846,7 +5856,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
selected_filter = memnew(Button);
selected_filter->set_flat(true);
- selected_filter->connect("pressed", callable_mp(this, &AnimationTrackEditor::_view_group_toggle)); //same function works the same
+ selected_filter->connect("pressed", callable_mp(this, &AnimationTrackEditor::_view_group_toggle)); // Same function works the same.
selected_filter->set_toggle_mode(true);
selected_filter->set_tooltip(TTR("Only show tracks from nodes selected in tree."));
@@ -5947,9 +5957,6 @@ AnimationTrackEditor::AnimationTrackEditor() {
add_child(method_selector);
method_selector->connect("selected", callable_mp(this, &AnimationTrackEditor::_add_method_key));
- inserting = false;
- insert_query = false;
- insert_frame = 0;
insert_queue = false;
insert_confirm = memnew(ConfirmationDialog);
@@ -5982,13 +5989,13 @@ AnimationTrackEditor::AnimationTrackEditor() {
box_selection->connect("draw", callable_mp(this, &AnimationTrackEditor::_box_selection_draw));
box_selecting = false;
- //default plugins
+ // Default Plugins.
Ref<AnimationTrackEditDefaultPlugin> def_plugin;
def_plugin.instantiate();
add_track_edit_plugin(def_plugin);
- //dialogs
+ // Dialogs.
optimize_dialog = memnew(ConfirmationDialog);
add_child(optimize_dialog);
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index 4da708dd1c..576edef0c8 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -352,10 +352,7 @@ class AnimationTrackEditor : public VBoxContainer {
CheckBox *insert_confirm_reset;
ConfirmationDialog *insert_confirm;
bool insert_queue;
- bool inserting;
- bool insert_query;
List<InsertData> insert_data;
- uint64_t insert_frame;
void _query_insert(const InsertData &p_id);
Ref<Animation> _create_and_get_reset_animation();
@@ -370,7 +367,7 @@ class AnimationTrackEditor : public VBoxContainer {
}
};
TrackIndices _confirm_insert(InsertData p_id, TrackIndices p_next_tracks, bool p_create_reset, Ref<Animation> p_reset_anim, bool p_create_beziers);
- void _insert_delay(bool p_create_reset, bool p_create_beziers);
+ void _insert_track(bool p_create_reset, bool p_create_beziers);
void _root_removed(Node *p_root);
@@ -531,6 +528,9 @@ public:
void insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists = false);
void insert_value_key(const String &p_property, const Variant &p_value, bool p_advance);
void insert_transform_key(Node3D *p_node, const String &p_sub, const Transform3D &p_xform);
+ bool has_transform_track(Node3D *p_node, const String &p_sub);
+ void make_insert_queue();
+ void commit_insert_queue();
void show_select_node_warning(bool p_show);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index a8fdca1b20..5fd0a41788 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -6928,7 +6928,7 @@ EditorNode::EditorNode() {
add_child(editor_interface);
//more visually meaningful to have this later
- raise_bottom_panel_item(AnimationPlayerEditor::singleton);
+ raise_bottom_panel_item(AnimationPlayerEditor::get_singleton());
add_editor_plugin(VersionControlEditorPlugin::get_singleton());
add_editor_plugin(memnew(ShaderEditorPlugin(this)));
@@ -7202,20 +7202,24 @@ bool EditorPluginList::forward_gui_input(const Ref<InputEvent> &p_event) {
return discard;
}
-bool EditorPluginList::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event, bool serve_when_force_input_enabled) {
- bool discard = false;
+EditorPlugin::AfterGUIInput EditorPluginList::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event, bool serve_when_force_input_enabled) {
+ EditorPlugin::AfterGUIInput after = EditorPlugin::AFTER_GUI_INPUT_PASS;
for (int i = 0; i < plugins_list.size(); i++) {
if ((!serve_when_force_input_enabled) && plugins_list[i]->is_input_event_forwarding_always_enabled()) {
continue;
}
- if (plugins_list[i]->forward_spatial_gui_input(p_camera, p_event)) {
- discard = true;
+ EditorPlugin::AfterGUIInput current_after = plugins_list[i]->forward_spatial_gui_input(p_camera, p_event);
+ if (current_after == EditorPlugin::AFTER_GUI_INPUT_STOP) {
+ after = EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ if (after != EditorPlugin::AFTER_GUI_INPUT_STOP && current_after == EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ after = EditorPlugin::AFTER_GUI_INPUT_DESELECT;
}
}
- return discard;
+ return after;
}
void EditorPluginList::forward_canvas_draw_over_viewport(Control *p_overlay) {
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 07c834eeca..73feeecfee 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -942,7 +942,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(Camera3D *p_camera, const Ref<InputEvent> &p_event, bool serve_when_force_input_enabled);
+ EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *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 7afc8d4c83..aee8322a97 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -593,14 +593,14 @@ int EditorPlugin::update_overlays() const {
}
}
-bool EditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
- bool success;
+EditorPlugin::AfterGUIInput EditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+ int success;
if (GDVIRTUAL_CALL(_forward_3d_gui_input, p_camera, p_event, success)) {
- return success;
+ return static_cast<EditorPlugin::AfterGUIInput>(success);
}
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
void EditorPlugin::forward_spatial_draw_over_viewport(Control *p_overlay) {
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index 169106d901..57830df327 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -151,7 +151,7 @@ protected:
GDVIRTUAL1R(bool, _forward_canvas_gui_input, Ref<InputEvent>)
GDVIRTUAL1(_forward_canvas_draw_over_viewport, Control *)
GDVIRTUAL1(_forward_canvas_force_draw_over_viewport, Control *)
- GDVIRTUAL2R(bool, _forward_3d_gui_input, Camera3D *, Ref<InputEvent>)
+ GDVIRTUAL2R(int, _forward_3d_gui_input, Camera3D *, Ref<InputEvent>)
GDVIRTUAL1(_forward_3d_draw_over_viewport, Control *)
GDVIRTUAL1(_forward_3d_force_draw_over_viewport, Control *)
GDVIRTUAL0RC(String, _get_plugin_name)
@@ -200,6 +200,12 @@ public:
DOCK_SLOT_MAX
};
+ enum AfterGUIInput {
+ AFTER_GUI_INPUT_PASS,
+ AFTER_GUI_INPUT_STOP,
+ AFTER_GUI_INPUT_DESELECT
+ };
+
//TODO: send a resource for editing to the editor node?
void add_control_to_container(CustomControlContainer p_location, Control *p_control);
@@ -228,7 +234,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(Camera3D *p_camera, const Ref<InputEvent> &p_event);
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *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 6efbcbc61e..2d4db48f2a 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("Sky");
exceptions.insert("EditorControlAnchor");
diff --git a/editor/icons/EditorBoneHandle.svg b/editor/icons/EditorBoneHandle.svg
new file mode 100644
index 0000000000..a6d7c3f878
--- /dev/null
+++ b/editor/icons/EditorBoneHandle.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>
diff --git a/editor/icons/ToolBoneSelect.svg b/editor/icons/ToolBoneSelect.svg
new file mode 100644
index 0000000000..cc12b69a82
--- /dev/null
+++ b/editor/icons/ToolBoneSelect.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>
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index a559f05785..59d0b92ba0 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -383,7 +383,7 @@ void InspectorDock::_menu_expandall() {
}
void InspectorDock::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_value_key(p_keyed, p_value, p_advance);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_value_key(p_keyed, p_value, p_advance);
}
void InspectorDock::_transform_keyed(Object *sp, const String &p_sub, const Transform3D &p_key) {
@@ -391,7 +391,7 @@ void InspectorDock::_transform_keyed(Object *sp, const String &p_sub, const Tran
if (!s) {
return;
}
- AnimationPlayerEditor::singleton->get_track_editor()->insert_transform_key(s, p_sub, p_key);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, p_key);
}
void InspectorDock::_warning_pressed() {
@@ -545,7 +545,7 @@ void InspectorDock::go_back() {
void InspectorDock::update_keying() {
bool valid = false;
- if (AnimationPlayerEditor::singleton->get_track_editor()->has_keying()) {
+ if (AnimationPlayerEditor::get_singleton()->get_track_editor()->has_keying()) {
EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history();
if (editor_history->get_path_size() >= 1) {
Object *obj = ObjectDB::get_instance(editor_history->get_path_object(0));
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..531ffc6a73 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,152 @@ 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();
+ te->make_insert_queue();
+ 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);
+ }
+ te->commit_insert_queue();
+}
+
+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 +496,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 +510,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 +623,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 +645,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 +682,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 +716,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 +734,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 +799,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 +810,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 +819,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 +855,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 +866,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 +1108,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
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index a5b607c0e8..4a59eb4fb3 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1835,8 +1835,8 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
editor_data->get_undo_redo().add_do_method(this, "_set_owners", edited_scene, owners);
- if (AnimationPlayerEditor::singleton->get_track_editor()->get_root() == node) {
- editor_data->get_undo_redo().add_do_method(AnimationPlayerEditor::singleton->get_track_editor(), "set_root", node);
+ if (AnimationPlayerEditor::get_singleton()->get_track_editor()->get_root() == node) {
+ editor_data->get_undo_redo().add_do_method(AnimationPlayerEditor::get_singleton()->get_track_editor(), "set_root", node);
}
editor_data->get_undo_redo().add_undo_method(new_parent, "remove_child", node);
@@ -1861,8 +1861,8 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
editor_data->get_undo_redo().add_undo_method(node->get_parent(), "add_child", node);
editor_data->get_undo_redo().add_undo_method(node->get_parent(), "move_child", node, child_pos);
editor_data->get_undo_redo().add_undo_method(this, "_set_owners", edited_scene, owners);
- if (AnimationPlayerEditor::singleton->get_track_editor()->get_root() == node) {
- editor_data->get_undo_redo().add_undo_method(AnimationPlayerEditor::singleton->get_track_editor(), "set_root", node);
+ if (AnimationPlayerEditor::get_singleton()->get_track_editor()->get_root() == node) {
+ editor_data->get_undo_redo().add_undo_method(AnimationPlayerEditor::get_singleton()->get_track_editor(), "set_root", node);
}
if (p_keep_global_xform) {
@@ -2073,8 +2073,8 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
editor_data->get_undo_redo().add_do_method(n->get_parent(), "remove_child", n);
editor_data->get_undo_redo().add_undo_method(n->get_parent(), "add_child", n);
editor_data->get_undo_redo().add_undo_method(n->get_parent(), "move_child", n, n->get_index());
- if (AnimationPlayerEditor::singleton->get_track_editor()->get_root() == n) {
- editor_data->get_undo_redo().add_undo_method(AnimationPlayerEditor::singleton->get_track_editor(), "set_root", n);
+ if (AnimationPlayerEditor::get_singleton()->get_track_editor()->get_root() == n) {
+ editor_data->get_undo_redo().add_undo_method(AnimationPlayerEditor::get_singleton()->get_track_editor(), "set_root", n);
}
editor_data->get_undo_redo().add_undo_method(this, "_set_owners", edited_scene, owners);
editor_data->get_undo_redo().add_undo_reference(n);
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index 8ffaf0829e..c944068455 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -102,7 +102,7 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
undo_redo->commit_action();
} else if (p_id == BUTTON_PIN) {
if (n->is_class("AnimationPlayer")) {
- AnimationPlayerEditor::singleton->unpin();
+ AnimationPlayerEditor::get_singleton()->unpin();
_update_tree();
}
@@ -386,7 +386,7 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent, bool p_scroll
_update_visibility_color(p_node, item);
} else if (p_node->is_class("AnimationPlayer")) {
- bool is_pinned = AnimationPlayerEditor::singleton->get_player() == p_node && AnimationPlayerEditor::singleton->is_pinned();
+ bool is_pinned = AnimationPlayerEditor::get_singleton()->get_player() == p_node && AnimationPlayerEditor::get_singleton()->is_pinned();
if (is_pinned) {
item->add_button(0, get_theme_icon(SNAME("Pin"), SNAME("EditorIcons")), BUTTON_PIN, false, TTR("AnimationPlayer is pinned.\nClick to unpin."));
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 8121053245..bb0d9e9e9b 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -58,27 +58,27 @@ Error GDScriptParserRef::raise_status(Status p_new_status) {
while (p_new_status > status) {
switch (status) {
case EMPTY:
- result = parser->parse(GDScriptCache::get_source_code(path), path, false);
status = PARSED;
+ result = parser->parse(GDScriptCache::get_source_code(path), path, false);
break;
case PARSED: {
analyzer = memnew(GDScriptAnalyzer(parser));
- Error inheritance_result = analyzer->resolve_inheritance();
status = INHERITANCE_SOLVED;
+ Error inheritance_result = analyzer->resolve_inheritance();
if (result == OK) {
result = inheritance_result;
}
} break;
case INHERITANCE_SOLVED: {
- Error interface_result = analyzer->resolve_interface();
status = INTERFACE_SOLVED;
+ Error interface_result = analyzer->resolve_interface();
if (result == OK) {
result = interface_result;
}
} break;
case INTERFACE_SOLVED: {
- Error body_result = analyzer->resolve_body();
status = FULLY_SOLVED;
+ Error body_result = analyzer->resolve_body();
if (result == OK) {
result = body_result;
}
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index ad75e8174c..93a5f7d493 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -3500,7 +3500,7 @@ bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Nod
} else if (mode == "unreliable_ordered") {
rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED;
} else {
- push_error(R"(Invalid RPC argument. Must be one of: 'call_local'/'no_call_local' (local calls), 'any_peer'/'authority' (permission), 'reliable'/'unreliable'/'unreliable_ordered' (transfer mode).)", p_annotation);
+ push_error(R"(Invalid RPC argument. Must be one of: 'call_local'/'call_remote' (local calls), 'any_peer'/'authority' (permission), 'reliable'/'unreliable'/'unreliable_ordered' (transfer mode).)", p_annotation);
}
}
}
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index c170bb107e..08d55de976 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -603,9 +603,9 @@ void GridMapEditor::_do_paste() {
_clear_clipboard_data();
}
-bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
if (!node) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Ref<InputEventMouseButton> mb = p_event;
@@ -616,12 +616,12 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In
floor->set_value(floor->get_value() + mb->get_factor());
}
- return true; // Eaten.
+ return EditorPlugin::AFTER_GUI_INPUT_STOP; // Eaten.
} else if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && (mb->is_command_pressed() || mb->is_shift_pressed())) {
if (mb->is_pressed()) {
floor->set_value(floor->get_value() - mb->get_factor());
}
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
if (mb->is_pressed()) {
@@ -648,19 +648,22 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In
_clear_clipboard_data();
input_action = INPUT_NONE;
_update_paste_indicator();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else if (selection.active) {
_set_selection(false);
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
input_action = INPUT_ERASE;
set_items.clear();
}
} else {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
- return do_input_action(p_camera, Point2(mb->get_position().x, mb->get_position().y), true);
+ if (do_input_action(p_camera, Point2(mb->get_position().x, mb->get_position().y), true)) {
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
} else {
if ((mb->get_button_index() == MOUSE_BUTTON_RIGHT && input_action == INPUT_ERASE) || (mb->get_button_index() == MOUSE_BUTTON_LEFT && input_action == INPUT_PAINT)) {
if (set_items.size()) {
@@ -677,7 +680,11 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In
}
set_items.clear();
input_action = INPUT_NONE;
- return set_items.size() > 0;
+
+ if (set_items.size() > 0) {
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
if (mb->get_button_index() == MOUSE_BUTTON_LEFT && input_action == INPUT_SELECT) {
@@ -690,11 +697,11 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In
if (mb->get_button_index() == MOUSE_BUTTON_LEFT && input_action != INPUT_NONE) {
set_items.clear();
input_action = INPUT_NONE;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && (input_action == INPUT_ERASE || input_action == INPUT_PASTE)) {
input_action = INPUT_NONE;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
}
@@ -702,7 +709,10 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
- return do_input_action(p_camera, mm->get_position(), false);
+ if (do_input_action(p_camera, mm->get_position(), false)) {
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Ref<InputEventKey> k = p_event;
@@ -714,16 +724,16 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In
_clear_clipboard_data();
input_action = INPUT_NONE;
_update_paste_indicator();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else if (selection.active) {
_set_selection(false);
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
selected_palette = -1;
mesh_library_palette->deselect_all();
update_palette();
_update_cursor_instance();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
@@ -731,12 +741,12 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In
if (k->get_keycode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL))) {
selection.click[edit_axis]--;
_validate_selection();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
if (k->get_keycode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_NEXT_LEVEL))) {
selection.click[edit_axis]++;
_validate_selection();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
}
@@ -755,12 +765,12 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In
if (step) {
floor->set_value(floor->get_value() + step);
}
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
accumulated_floor_delta = 0.0;
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
struct _CGMEItemSort {
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index 4bc849e071..1a6b1310d8 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -232,7 +232,7 @@ protected:
static void _bind_methods();
public:
- bool forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event);
+ EditorPlugin::AfterGUIInput forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event);
void edit(GridMap *p_gridmap);
GridMapEditor() {}
@@ -250,7 +250,7 @@ protected:
void _notification(int p_what);
public:
- virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); }
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); }
virtual String get_name() const override { return "GridMap"; }
bool has_main_screen() const override { return false; }
virtual void edit(Object *p_object) override;
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index 12470939f5..272a06bce4 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -376,6 +376,18 @@ void Node3D::update_gizmos() {
#endif
}
+void Node3D::set_subgizmo_selection(Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform) {
+#ifdef TOOLS_ENABLED
+ if (!is_inside_world()) {
+ return;
+ }
+
+ if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) {
+ get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_set_subgizmo_selection, this, p_gizmo, p_id, p_transform);
+ }
+#endif
+}
+
void Node3D::clear_subgizmo_selection() {
#ifdef TOOLS_ENABLED
if (!is_inside_world()) {
@@ -792,6 +804,7 @@ void Node3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_gizmo", "gizmo"), &Node3D::add_gizmo);
ClassDB::bind_method(D_METHOD("get_gizmos"), &Node3D::get_gizmos_bind);
ClassDB::bind_method(D_METHOD("clear_gizmos"), &Node3D::clear_gizmos);
+ ClassDB::bind_method(D_METHOD("set_subgizmo_selection", "gizmo", "id", "transform"), &Node3D::set_subgizmo_selection);
ClassDB::bind_method(D_METHOD("clear_subgizmo_selection"), &Node3D::clear_subgizmo_selection);
ClassDB::bind_method(D_METHOD("set_visible", "visible"), &Node3D::set_visible);
diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h
index 0fd0c4e205..d6dcdd96fe 100644
--- a/scene/3d/node_3d.h
+++ b/scene/3d/node_3d.h
@@ -92,6 +92,7 @@ class Node3D : public Node {
Vector<Ref<Node3DGizmo>> gizmos;
bool gizmos_disabled = false;
bool gizmos_dirty = false;
+ bool transform_gizmo_visible = true;
#endif
} data;
@@ -145,6 +146,8 @@ public:
#ifdef TOOLS_ENABLED
virtual Transform3D get_global_gizmo_transform() const;
virtual Transform3D get_local_gizmo_transform() const;
+ virtual void set_transform_gizmo_visible(bool p_enabled) { data.transform_gizmo_visible = p_enabled; };
+ virtual bool is_transform_gizmo_visible() const { return data.transform_gizmo_visible; };
#endif
void set_as_top_level(bool p_enabled);
@@ -155,6 +158,7 @@ public:
void set_disable_gizmos(bool p_enabled);
void update_gizmos();
+ void set_subgizmo_selection(Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform = Transform3D());
void clear_subgizmo_selection();
Vector<Ref<Node3DGizmo>> get_gizmos() const;
Array get_gizmos_bind() const;
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index b7a79a2645..0a19e1d07b 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -32,6 +32,7 @@
#include "core/object/message_queue.h"
#include "core/variant/type_info.h"
+#include "editor/plugins/skeleton_3d_editor_plugin.h"
#include "scene/3d/physics_body_3d.h"
#include "scene/resources/skeleton_modification_3d.h"
#include "scene/resources/surface_tool.h"
@@ -178,7 +179,7 @@ void Skeleton3D::_update_process_order() {
for (int i = 0; i < len; i++) {
if (bonesptr[i].parent >= len) {
- //validate this just in case
+ // Validate this just in case.
ERR_PRINT("Bone " + itos(i) + " has invalid parent: " + itos(bonesptr[i].parent));
bonesptr[i].parent = -1;
}
@@ -186,9 +187,9 @@ void Skeleton3D::_update_process_order() {
if (bonesptr[i].parent != -1) {
int parent_bone_idx = bonesptr[i].parent;
- // Check to see if this node is already added to the parent:
+ // Check to see if this node is already added to the parent.
if (bonesptr[parent_bone_idx].child_bones.find(i) < 0) {
- // Add the child node
+ // Add the child node.
bonesptr[parent_bone_idx].child_bones.push_back(i);
} else {
ERR_PRINT("Skeleton3D parenthood graph is cyclic");
@@ -210,10 +211,10 @@ void Skeleton3D::_notification(int p_what) {
int len = bones.size();
dirty = false;
- // Update bone transforms
+ // Update bone transforms.
force_update_all_bone_transforms();
- //update skins
+ // Update skins.
for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
const Skin *skin = E->get()->skin.operator->();
RID skeleton = E->get()->skeleton;
@@ -231,7 +232,7 @@ void Skeleton3D::_notification(int p_what) {
StringName bind_name = skin->get_bind_name(i);
if (bind_name != StringName()) {
- //bind name used, use this
+ // Bind name used, use this.
bool found = false;
for (int j = 0; j < len; j++) {
if (bonesptr[j].name == bind_name) {
@@ -453,7 +454,8 @@ int Skeleton3D::get_bone_axis_forward_enum(int p_bone) {
return bones[p_bone].rest_bone_forward_axis;
}
-// skeleton creation api
+// Skeleton creation api
+
void Skeleton3D::add_bone(const String &p_name) {
ERR_FAIL_COND(p_name == "" || p_name.find(":") != -1 || p_name.find("/") != -1);
@@ -626,6 +628,7 @@ void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) {
ERR_FAIL_INDEX(p_bone, bone_size);
bones.write[p_bone].enabled = p_enabled;
+ emit_signal(SceneStringNames::get_singleton()->bone_enabled_changed, p_bone);
_make_dirty();
}
@@ -635,6 +638,16 @@ bool Skeleton3D::is_bone_enabled(int p_bone) const {
return bones[p_bone].enabled;
}
+void Skeleton3D::set_show_rest_only(bool p_enabled) {
+ show_rest_only = p_enabled;
+ emit_signal(SceneStringNames::get_singleton()->show_rest_only_changed);
+ _make_dirty();
+}
+
+bool Skeleton3D::is_show_rest_only() const {
+ return show_rest_only;
+}
+
void Skeleton3D::clear_bones() {
bones.clear();
process_order_dirty = true;
@@ -642,7 +655,7 @@ void Skeleton3D::clear_bones() {
_make_dirty();
}
-// posing api
+// Posing api
void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) {
const int bone_size = bones.size();
@@ -697,7 +710,7 @@ void Skeleton3D::localize_rests() {
set_bone_rest(current_bone_idx, bones[bones[current_bone_idx].parent].rest.affine_inverse() * bones[current_bone_idx].rest);
}
- // 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.
int child_bone_size = bones[current_bone_idx].child_bones.size();
for (int i = 0; i < child_bone_size; i++) {
bones_to_process.push_back(bones[current_bone_idx].child_bones[i]);
@@ -831,7 +844,7 @@ void Skeleton3D::physical_bones_start_simulation_on(const TypedArray<StringName>
Vector<int> sim_bones;
if (p_bones.size() <= 0) {
- sim_bones.push_back(0); // if no bones is specified, activate ragdoll on full body
+ sim_bones.push_back(0); // If no bones is specified, activate ragdoll on full body.
} else {
sim_bones.resize(p_bones.size());
int c = 0;
@@ -884,19 +897,19 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
Ref<Skin> skin = p_skin;
if (skin.is_null()) {
- //need to create one from existing code, this is for compatibility only
- //when skeletons did not support skins. It is also used by gizmo
- //to display the skeleton.
+ // Need to create one from existing code, this is for compatibility only
+ // when skeletons did not support skins. It is also used by gizmo
+ // to display the skeleton.
skin.instantiate();
skin->set_bind_count(bones.size());
- _update_process_order(); //just in case
+ _update_process_order(); // Just in case.
- // pose changed, rebuild cache of inverses
+ // Pose changed, rebuild cache of inverses.
const Bone *bonesptr = bones.ptr();
int len = bones.size();
- // calculate global rests and invert them
+ // Calculate global rests and invert them.
LocalVector<int> bones_to_process;
bones_to_process = get_parentless_bones();
while (bones_to_process.size() > 0) {
@@ -919,7 +932,7 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
}
for (int i = 0; i < len; i++) {
- //the inverse is what is actually required
+ // The inverse is what is actually required.
skin->set_bind_bone(i, i);
skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse());
}
@@ -940,11 +953,17 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
skin->connect("changed", Callable(skin_ref.operator->(), "_skin_changed"));
- _make_dirty(); //skin needs to be updated, so update skeleton
+ _make_dirty(); // Skin needs to be updated, so update skeleton.
return skin_ref;
}
+void Skeleton3D::force_update_all_dirty_bones() {
+ if (dirty) {
+ const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
+ }
+}
+
void Skeleton3D::force_update_all_bone_transforms() {
_update_process_order();
@@ -966,9 +985,10 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
bones_to_process.erase(current_bone_idx);
Bone &b = bonesptr[current_bone_idx];
+ bool bone_enabled = b.enabled && !show_rest_only;
if (b.disable_rest) {
- if (b.enabled) {
+ if (bone_enabled) {
Transform3D pose = b.pose;
if (b.custom_pose_enable) {
pose = b.custom_pose * pose;
@@ -991,7 +1011,7 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
}
} else {
- if (b.enabled) {
+ if (bone_enabled) {
Transform3D pose = b.pose;
if (b.custom_pose_enable) {
pose = b.custom_pose * pose;
@@ -1035,7 +1055,7 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
b.global_pose_override_amount = 0.0;
}
- // 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.
int child_bone_size = b.child_bones.size();
for (int i = 0; i < child_bone_size; i++) {
bones_to_process.push_back(b.child_bones[i]);
@@ -1045,7 +1065,7 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
}
}
-// helper functions
+// Helper functions
Transform3D Skeleton3D::global_pose_to_world_transform(Transform3D p_global_pose) {
return get_global_transform() * p_global_pose;
@@ -1175,6 +1195,9 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton3D::get_bone_pose);
ClassDB::bind_method(D_METHOD("set_bone_pose", "bone_idx", "pose"), &Skeleton3D::set_bone_pose);
+ ClassDB::bind_method(D_METHOD("is_bone_enabled", "bone_idx"), &Skeleton3D::is_bone_enabled);
+ ClassDB::bind_method(D_METHOD("set_bone_enabled", "bone_idx", "enabled"), &Skeleton3D::set_bone_enabled, DEFVAL(true));
+
ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override);
ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_bone_global_pose_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_override);
@@ -1198,6 +1221,9 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("local_pose_to_global_pose", "bone_idx", "local_pose"), &Skeleton3D::local_pose_to_global_pose);
ClassDB::bind_method(D_METHOD("global_pose_z_forward_to_bone_forward", "bone_idx", "basis"), &Skeleton3D::global_pose_z_forward_to_bone_forward);
+ ClassDB::bind_method(D_METHOD("set_show_rest_only"), &Skeleton3D::set_show_rest_only);
+ ClassDB::bind_method(D_METHOD("is_show_rest_only"), &Skeleton3D::is_show_rest_only);
+
ClassDB::bind_method(D_METHOD("set_animate_physical_bones"), &Skeleton3D::set_animate_physical_bones);
ClassDB::bind_method(D_METHOD("get_animate_physical_bones"), &Skeleton3D::get_animate_physical_bones);
@@ -1212,6 +1238,7 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("execute_modifications", "delta", "execution_mode"), &Skeleton3D::execute_modifications);
#ifndef _3D_DISABLED
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_rest_only"), "set_show_rest_only", "is_show_rest_only");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones"), "set_animate_physical_bones", "get_animate_physical_bones");
#endif // _3D_DISABLED
@@ -1220,6 +1247,8 @@ void Skeleton3D::_bind_methods() {
#endif // TOOLS_ENABLED
ADD_SIGNAL(MethodInfo("bone_pose_changed", PropertyInfo(Variant::INT, "bone_idx")));
+ ADD_SIGNAL(MethodInfo("bone_enabled_changed", PropertyInfo(Variant::INT, "bone_idx")));
+ ADD_SIGNAL(MethodInfo("show_rest_only_changed"));
BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
}
@@ -1228,7 +1257,7 @@ Skeleton3D::Skeleton3D() {
}
Skeleton3D::~Skeleton3D() {
- //some skins may remain bound
+ // Some skins may remain bound.
for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
E->get()->skeleton_node = nullptr;
}
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index c8a19db813..a50dc158f3 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -137,6 +137,8 @@ private:
void _make_dirty();
bool dirty = false;
+ bool show_rest_only = false;
+
uint64_t version = 1;
void _update_process_order();
@@ -197,6 +199,9 @@ public:
void set_bone_enabled(int p_bone, bool p_enabled);
bool is_bone_enabled(int p_bone) const;
+
+ void set_show_rest_only(bool p_enabled);
+ bool is_show_rest_only() const;
void clear_bones();
// posing api
@@ -219,6 +224,7 @@ public:
Ref<SkinReference> register_skin(const Ref<Skin> &p_skin);
+ void force_update_all_dirty_bones();
void force_update_all_bone_transforms();
void force_update_bone_children_transforms(int bone_idx);
diff --git a/scene/3d/soft_dynamic_body_3d.cpp b/scene/3d/soft_dynamic_body_3d.cpp
index 21f9b0a35d..9fceb21790 100644
--- a/scene/3d/soft_dynamic_body_3d.cpp
+++ b/scene/3d/soft_dynamic_body_3d.cpp
@@ -411,8 +411,9 @@ void SoftDynamicBody3D::_draw_soft_mesh() {
return;
}
- if (!rendering_server_handler.is_ready()) {
- rendering_server_handler.prepare(get_mesh()->get_rid(), 0);
+ const RID mesh_rid = get_mesh()->get_rid();
+ if (!rendering_server_handler.is_ready(mesh_rid)) {
+ rendering_server_handler.prepare(mesh_rid, 0);
/// Necessary in order to render the mesh correctly (Soft body nodes are in global space)
simulation_started = true;
diff --git a/scene/3d/soft_dynamic_body_3d.h b/scene/3d/soft_dynamic_body_3d.h
index 0b4b3021cd..5e7fbfe29e 100644
--- a/scene/3d/soft_dynamic_body_3d.h
+++ b/scene/3d/soft_dynamic_body_3d.h
@@ -50,7 +50,7 @@ class SoftDynamicBodyRenderingServerHandler : public RenderingServerHandler {
private:
SoftDynamicBodyRenderingServerHandler();
- bool is_ready() { return mesh.is_valid(); }
+ bool is_ready(RID p_mesh_rid) const { return mesh.is_valid() && mesh == p_mesh_rid; }
void prepare(RID p_mesh_rid, int p_surface);
void clear();
void open();
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index 29e5dd6056..186764e69e 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -64,6 +64,8 @@ SceneStringNames::SceneStringNames() {
pose_updated = StaticCString::create("pose_updated");
bone_pose_changed = StaticCString::create("bone_pose_changed");
+ bone_enabled_changed = StaticCString::create("bone_enabled_changed");
+ show_rest_only_changed = StaticCString::create("show_rest_only_changed");
mouse_entered = StaticCString::create("mouse_entered");
mouse_exited = StaticCString::create("mouse_exited");
@@ -135,6 +137,7 @@ SceneStringNames::SceneStringNames() {
_spatial_editor_group = StaticCString::create("_spatial_editor_group");
_request_gizmo = StaticCString::create("_request_gizmo");
+ _set_subgizmo_selection = StaticCString::create("_set_subgizmo_selection");
_clear_subgizmo_selection = StaticCString::create("_clear_subgizmo_selection");
offset = StaticCString::create("offset");
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index 5e3195952e..67007c85e0 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -100,6 +100,8 @@ public:
StringName pose_updated;
StringName bone_pose_changed;
+ StringName bone_enabled_changed;
+ StringName show_rest_only_changed;
StringName body_shape_entered;
StringName body_entered;
@@ -155,6 +157,7 @@ public:
StringName _spatial_editor_group;
StringName _request_gizmo;
+ StringName _set_subgizmo_selection;
StringName _clear_subgizmo_selection;
StringName offset;
diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp
index 236eb5e596..c69408a30b 100644
--- a/servers/rendering/renderer_rd/effects_rd.cpp
+++ b/servers/rendering/renderer_rd/effects_rd.cpp
@@ -1400,6 +1400,7 @@ void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_dep
ssao.gather_push_constant.half_screen_pixel_size_x025[0] = ssao.gather_push_constant.half_screen_pixel_size[0] * 0.25;
ssao.gather_push_constant.half_screen_pixel_size_x025[1] = ssao.gather_push_constant.half_screen_pixel_size[1] * 0.25;
+ ssao.gather_push_constant.radius = p_settings.radius;
float radius_near_limit = (p_settings.radius * 1.2f);
if (p_settings.quality <= RS::ENV_SSAO_QUALITY_LOW) {
radius_near_limit *= 1.50f;
@@ -1407,12 +1408,8 @@ void EffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RID p_dep
if (p_settings.quality == RS::ENV_SSAO_QUALITY_VERY_LOW) {
ssao.gather_push_constant.radius *= 0.8f;
}
- if (p_settings.half_size) {
- ssao.gather_push_constant.radius *= 0.5f;
- }
}
radius_near_limit /= tan_half_fov_y;
- ssao.gather_push_constant.radius = p_settings.radius;
ssao.gather_push_constant.intensity = p_settings.intensity;
ssao.gather_push_constant.shadow_power = p_settings.power;
ssao.gather_push_constant.shadow_clamp = 0.98;
diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
index 7925e735a0..5814c164cc 100644
--- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
@@ -1036,11 +1036,7 @@ void RendererSceneSkyRD::setup(RendererSceneEnvironmentRD *p_env, RID p_render_b
SkyShaderData *shader_data = nullptr;
- RS::EnvironmentBG background = p_env->background;
-
- if (!(background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) || sky) {
- // !BAS! Possibly silently fail here, we now get error spam when you select sky as the background but haven't setup the sky yet.
- ERR_FAIL_COND(!sky);
+ if (sky) {
sky_material = sky_get_material(p_env->sky);
if (sky_material.is_valid()) {
@@ -1060,9 +1056,7 @@ void RendererSceneSkyRD::setup(RendererSceneEnvironmentRD *p_env, RID p_render_b
shader_data = material->shader_data;
ERR_FAIL_COND(!shader_data);
- }
- if (sky) {
// Invalidate supbass buffers if screen size changes
if (sky->screen_size != p_screen_size) {
sky->screen_size = p_screen_size;
@@ -1371,7 +1365,6 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont
ERR_FAIL_COND(p_view_count > RendererSceneRender::MAX_RENDER_VIEWS);
Sky *sky = get_sky(p_env->sky);
- ERR_FAIL_COND(!sky);
SkyMaterialData *material = nullptr;
RID sky_material;
@@ -1483,27 +1476,17 @@ void RendererSceneSkyRD::update_res_buffers(RendererSceneEnvironmentRD *p_env, u
SkyMaterialData *material = nullptr;
RID sky_material;
- RS::EnvironmentBG background = p_env->background;
-
- if (!(background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) || sky) {
- ERR_FAIL_COND(!sky);
- sky_material = sky_get_material(p_env->sky);
-
- if (sky_material.is_valid()) {
- material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
- if (!material || !material->shader_data->valid) {
- material = nullptr;
- }
- }
+ sky_material = sky_get_material(p_env->sky);
- if (!material) {
- sky_material = sky_shader.default_material;
- material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+ if (sky_material.is_valid()) {
+ material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
+ if (!material || !material->shader_data->valid) {
+ material = nullptr;
}
}
- if (background == RS::ENV_BG_CLEAR_COLOR || background == RS::ENV_BG_COLOR) {
- sky_material = sky_scene_state.fog_material;
+ if (!material) {
+ sky_material = sky_shader.default_material;
material = (SkyMaterialData *)storage->material_get_data(sky_material, RendererStorageRD::SHADER_TYPE_SKY);
}
@@ -1572,7 +1555,6 @@ void RendererSceneSkyRD::draw(RD::DrawListID p_draw_list, RendererSceneEnvironme
ERR_FAIL_COND(p_view_count > RendererSceneRender::MAX_RENDER_VIEWS);
Sky *sky = get_sky(p_env->sky);
- ERR_FAIL_COND(!sky);
SkyMaterialData *material = nullptr;
RID sky_material;