diff options
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; |