diff options
63 files changed, 2525 insertions, 1907 deletions
diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index 289caf5247..9c84c2add7 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -110,15 +110,16 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { actions.write[actions.size() - 1].last_tick = ticks; + merge_mode = p_mode; merging = true; } else { Action new_action; new_action.name = p_name; new_action.last_tick = ticks; actions.push_back(new_action); - } - merge_mode = p_mode; + merge_mode = MERGE_DISABLE; + } } action_level++; diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index d2ecbdde26..a35b784106 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -253,6 +253,14 @@ Returns the arguments values to be called on a method track for a given key in a given track. </description> </method> + <method name="position_track_insert_key"> + <return type="int" /> + <argument index="0" name="track_idx" type="int" /> + <argument index="1" name="time" type="float" /> + <argument index="2" name="position" type="Vector3" /> + <description> + </description> + </method> <method name="remove_track"> <return type="void" /> <argument index="0" name="track_idx" type="int" /> @@ -260,6 +268,22 @@ Removes a track by specifying the track index. </description> </method> + <method name="rotation_track_insert_key"> + <return type="int" /> + <argument index="0" name="track_idx" type="int" /> + <argument index="1" name="time" type="float" /> + <argument index="2" name="rotation" type="Quaternion" /> + <description> + </description> + </method> + <method name="scale_track_insert_key"> + <return type="int" /> + <argument index="0" name="track_idx" type="int" /> + <argument index="1" name="time" type="float" /> + <argument index="2" name="scale" type="Vector3" /> + <description> + </description> + </method> <method name="track_find_key" qualifiers="const"> <return type="int" /> <argument index="0" name="track_idx" type="int" /> @@ -466,25 +490,6 @@ Swaps the track [code]idx[/code]'s index position with the track [code]with_idx[/code]. </description> </method> - <method name="transform_track_insert_key"> - <return type="int" /> - <argument index="0" name="track_idx" type="int" /> - <argument index="1" name="time" type="float" /> - <argument index="2" name="location" type="Vector3" /> - <argument index="3" name="rotation" type="Quaternion" /> - <argument index="4" name="scale" type="Vector3" /> - <description> - Insert a transform key for a transform track. - </description> - </method> - <method name="transform_track_interpolate" qualifiers="const"> - <return type="Array" /> - <argument index="0" name="track_idx" type="int" /> - <argument index="1" name="time_sec" type="float" /> - <description> - Returns the interpolated value of a transform track at a given time (in seconds). An array consisting of 3 elements: position ([Vector3]), rotation ([Quaternion]) and scale ([Vector3]). - </description> - </method> <method name="value_track_get_key_indices" qualifiers="const"> <return type="PackedInt32Array" /> <argument index="0" name="track_idx" type="int" /> @@ -541,19 +546,22 @@ <constant name="TYPE_VALUE" value="0" enum="TrackType"> Value tracks set values in node properties, but only those which can be Interpolated. </constant> - <constant name="TYPE_TRANSFORM3D" value="1" enum="TrackType"> - Transform3D tracks are used to change node local transforms or skeleton pose bones of 3D nodes. Transitions are interpolated. + <constant name="TYPE_POSITION_3D" value="1" enum="TrackType"> + </constant> + <constant name="TYPE_ROTATION_3D" value="2" enum="TrackType"> + </constant> + <constant name="TYPE_SCALE_3D" value="3" enum="TrackType"> </constant> - <constant name="TYPE_METHOD" value="2" enum="TrackType"> + <constant name="TYPE_METHOD" value="4" enum="TrackType"> Method tracks call functions with given arguments per key. </constant> - <constant name="TYPE_BEZIER" value="3" enum="TrackType"> + <constant name="TYPE_BEZIER" value="5" enum="TrackType"> Bezier tracks are used to interpolate a value using custom curves. They can also be used to animate sub-properties of vectors and colors (e.g. alpha value of a [Color]). </constant> - <constant name="TYPE_AUDIO" value="4" enum="TrackType"> + <constant name="TYPE_AUDIO" value="6" enum="TrackType"> Audio tracks are used to play an audio stream with either type of [AudioStreamPlayer]. The stream can be trimmed and previewed in the animation. </constant> - <constant name="TYPE_ANIMATION" value="5" enum="TrackType"> + <constant name="TYPE_ANIMATION" value="7" enum="TrackType"> Animation tracks play animations in other [AnimationPlayer] nodes. </constant> <constant name="INTERPOLATION_NEAREST" value="0" enum="InterpolationType"> diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml index 3d112e258e..40dcd950d7 100644 --- a/doc/classes/AnimationTree.xml +++ b/doc/classes/AnimationTree.xml @@ -22,7 +22,7 @@ <method name="get_root_motion_transform" qualifiers="const"> <return type="Transform3D" /> <description> - Retrieve the motion of the [member root_motion_track] as a [Transform3D] that can be used elsewhere. If [member root_motion_track] is not a path to a track of type [constant Animation.TYPE_TRANSFORM3D], returns an identity transformation. See also [member root_motion_track] and [RootMotionView]. + Retrieve the motion of the [member root_motion_track] as a [Transform3D] that can be used elsewhere. If [member root_motion_track] is not a path to a track of type [constant Animation.TYPE_POSITION_3D], [constant Animation.TYPE_SCALE_3D] or [constant Animation.TYPE_ROTATION_3D], returns an identity transformation. See also [member root_motion_track] and [RootMotionView]. </description> </method> <method name="rename_parameter"> @@ -45,7 +45,7 @@ </member> <member name="root_motion_track" type="NodePath" setter="set_root_motion_track" getter="get_root_motion_track" default="NodePath("")"> The path to the Animation track used for root motion. Paths must be valid scene-tree paths to a node, and must be specified starting from the parent node of the node that will reproduce the animation. To specify a track that controls properties or bones, append its name after the path, separated by [code]":"[/code]. For example, [code]"character/skeleton:ankle"[/code] or [code]"character/mesh:transform/local"[/code]. - If the track has type [constant Animation.TYPE_TRANSFORM3D], the transformation will be cancelled visually, and the animation will appear to stay in place. See also [method get_root_motion_transform] and [RootMotionView]. + If the track has type [constant Animation.TYPE_POSITION_3D], [constant Animation.TYPE_ROTATION_3D] or [constant Animation.TYPE_SCALE_3D] the transformation will be cancelled visually, and the animation will appear to stay in place. See also [method get_root_motion_transform] and [RootMotionView]. </member> <member name="tree_root" type="AnimationNode" setter="set_tree_root" getter="get_tree_root"> The root animation node of this [AnimationTree]. See [AnimationNode]. diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml index 6ab5ed55a3..e804e7bf24 100644 --- a/doc/classes/Skeleton3D.xml +++ b/doc/classes/Skeleton3D.xml @@ -47,6 +47,11 @@ Removes the local pose override on all bones in the skeleton. </description> </method> + <method name="create_skin_from_rest_transforms"> + <return type="Skin" /> + <description> + </description> + </method> <method name="execute_modifications"> <return type="void" /> <argument index="0" name="delta" type="float" /> @@ -88,13 +93,6 @@ Returns the amount of bones in the skeleton. </description> </method> - <method name="get_bone_custom_pose" qualifiers="const"> - <return type="Transform3D" /> - <argument index="0" name="bone_idx" type="int" /> - <description> - Returns the custom pose of the specified bone. Custom pose is applied on top of the rest pose. - </description> - </method> <method name="get_bone_global_pose" qualifiers="const"> <return type="Transform3D" /> <argument index="0" name="bone_idx" type="int" /> @@ -144,6 +142,24 @@ Returns the pose transform of the specified bone. Pose is applied on top of the custom pose, which is applied on top the rest pose. </description> </method> + <method name="get_bone_pose_position" qualifiers="const"> + <return type="Vector3" /> + <argument index="0" name="bone_idx" type="int" /> + <description> + </description> + </method> + <method name="get_bone_pose_rotation" qualifiers="const"> + <return type="Quaternion" /> + <argument index="0" name="bone_idx" type="int" /> + <description> + </description> + </method> + <method name="get_bone_pose_scale" qualifiers="const"> + <return type="Vector3" /> + <argument index="0" name="bone_idx" type="int" /> + <description> + </description> + </method> <method name="get_bone_rest" qualifiers="const"> <return type="Transform3D" /> <argument index="0" name="bone_idx" type="int" /> @@ -196,13 +212,6 @@ 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" /> - <description> - Returns whether the bone rest for the bone at [code]bone_idx[/code] is disabled. - </description> - </method> <method name="local_pose_to_global_pose"> <return type="Transform3D" /> <argument index="0" name="bone_idx" type="int" /> @@ -272,23 +281,6 @@ Sets the children for the passed in bone, [code]bone_idx[/code], to the passed-in array of bone indexes, [code]bone_children[/code]. </description> </method> - <method name="set_bone_custom_pose"> - <return type="void" /> - <argument index="0" name="bone_idx" type="int" /> - <argument index="1" name="custom_pose" type="Transform3D" /> - <description> - Sets the custom pose transform, [code]custom_pose[/code], for the bone at [code]bone_idx[/code]. This pose is an addition to the bone rest pose. - [b]Note:[/b] The pose transform needs to be in bone space. Use [method world_transform_to_global_pose] to convert a world transform, like one you can get from a [Node3D], to bone space. - </description> - </method> - <method name="set_bone_disable_rest"> - <return type="void" /> - <argument index="0" name="bone_idx" type="int" /> - <argument index="1" name="disable" type="bool" /> - <description> - 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" /> @@ -337,13 +329,25 @@ [b]Note:[/b] [code]parent_idx[/code] must be less than [code]bone_idx[/code]. </description> </method> - <method name="set_bone_pose"> + <method name="set_bone_pose_position"> <return type="void" /> <argument index="0" name="bone_idx" type="int" /> - <argument index="1" name="pose" type="Transform3D" /> + <argument index="1" name="position" type="Vector3" /> + <description> + </description> + </method> + <method name="set_bone_pose_rotation"> + <return type="void" /> + <argument index="0" name="bone_idx" type="int" /> + <argument index="1" name="rotation" type="Quaternion" /> + <description> + </description> + </method> + <method name="set_bone_pose_scale"> + <return type="void" /> + <argument index="0" name="bone_idx" type="int" /> + <argument index="1" name="scale" type="Vector3" /> <description> - Sets the pose transform for bone [code]bone_idx[/code]. - [b]Note:[/b] The pose transform needs to be in bone space. Use [method world_transform_to_global_pose] to convert a world transform, like one you can get from a [Node3D], to bone space. </description> </method> <method name="set_bone_rest"> diff --git a/doc/classes/TileSetAtlasSource.xml b/doc/classes/TileSetAtlasSource.xml index d12ac840f4..881a1c3d07 100644 --- a/doc/classes/TileSetAtlasSource.xml +++ b/doc/classes/TileSetAtlasSource.xml @@ -15,12 +15,6 @@ <tutorials> </tutorials> <methods> - <method name="clear_tiles_outside_texture"> - <return type="void" /> - <description> - Clears all tiles that are defined outside the texture boundaries. - </description> - </method> <method name="create_alternative_tile"> <return type="int" /> <argument index="0" name="atlas_coords" type="Vector2i" /> @@ -124,6 +118,16 @@ Returns a tile's texture region in the atlas texture. For animated tiles, a [code]frame[/code] argument might be provided for the different frames of the animation. </description> </method> + <method name="get_tiles_to_be_removed_on_change"> + <return type="PackedVector2Array" /> + <argument index="0" name="texture" type="Texture2D" /> + <argument index="1" name="margins" type="Vector2i" /> + <argument index="2" name="separation" type="Vector2i" /> + <argument index="3" name="texture_region_size" type="Vector2i" /> + <description> + Returns an array of tiles coordinates ID that will be automatically removed when modifying one or several of those properties: [code]texture[/code], [code]margins[/code], [code]separation[/code] or [code]texture_region_size[/code]. This can be used to undo changes that would have caused tiles data loss. + </description> + </method> <method name="has_room_for_tile" qualifiers="const"> <return type="bool" /> <argument index="0" name="atlas_coords" type="Vector2i" /> @@ -136,12 +140,6 @@ Returns whether there is enough room in an atlas to create/modify a tile with the given properties. If [code]ignored_tile[/code] is provided, act as is the given tile was not present in the atlas. This may be used when you want to modify a tile's properties. </description> </method> - <method name="has_tiles_outside_texture"> - <return type="bool" /> - <description> - Returns if this atlas has tiles outside of its texture. - </description> - </method> <method name="move_tile_in_atlas"> <return type="void" /> <argument index="0" name="atlas_coords" type="Vector2i" /> diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 14ca35a664..2ee7f0ffa5 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -165,20 +165,38 @@ public: } switch (animation->track_get_type(track)) { - case Animation::TYPE_TRANSFORM3D: { - Dictionary d_old = animation->track_get_key_value(track, key); - Dictionary d_new = d_old.duplicate(); - d_new[p_name] = p_value; - setting = true; - undo_redo->create_action(TTR("Anim Change Transform")); - undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new); - undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old); - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - undo_redo->commit_action(); + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + if (name == "position" || name == "rotation" || name == "scale") { + Variant old = animation->track_get_key_value(track, key); + setting = true; + String chan; + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + chan = "Position3D"; + break; + case Animation::TYPE_ROTATION_3D: + chan = "Rotation3D"; + break; + case Animation::TYPE_SCALE_3D: + chan = "Scale3D"; + break; + default: { + } + } + + undo_redo->create_action(vformat(TTR("Anim Change %s"), chan)); + undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value); + undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); + + setting = false; + return true; + } - setting = false; - return true; } break; case Animation::TYPE_VALUE: { if (name == "value") { @@ -412,12 +430,13 @@ public: } switch (animation->track_get_type(track)) { - case Animation::TYPE_TRANSFORM3D: { - Dictionary d = animation->track_get_key_value(track, key); - ERR_FAIL_COND_V(!d.has(name), false); - r_ret = d[p_name]; - return true; - + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + if (name == "position" || name == "rotation" || name == "scale") { + r_ret = animation->track_get_key_value(track, key); + return true; + } } break; case Animation::TYPE_VALUE: { if (name == "value") { @@ -523,11 +542,14 @@ public: } switch (animation->track_get_type(track)) { - case Animation::TYPE_TRANSFORM3D: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, "location")); - p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation")); + case Animation::TYPE_POSITION_3D: { + p_list->push_back(PropertyInfo(Variant::VECTOR3, "position")); + } break; + case Animation::TYPE_ROTATION_3D: { + p_list->push_back(PropertyInfo(Variant::VECTOR3, "rotation")); + } break; + case Animation::TYPE_SCALE_3D: { p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale")); - } break; case Animation::TYPE_VALUE: { Variant v = animation->track_get_key_value(track, key); @@ -779,17 +801,31 @@ public: } switch (animation->track_get_type(track)) { - case Animation::TYPE_TRANSFORM3D: { - Dictionary d_old = animation->track_get_key_value(track, key); - Dictionary d_new = d_old.duplicate(); - d_new[p_name] = p_value; - + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + Variant old = animation->track_get_key_value(track, key); if (!setting) { + String chan; + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + chan = "Position3D"; + break; + case Animation::TYPE_ROTATION_3D: + chan = "Rotation3D"; + break; + case Animation::TYPE_SCALE_3D: + chan = "Scale3D"; + break; + default: { + } + } + setting = true; - undo_redo->create_action(TTR("Anim Multi Change Transform")); + undo_redo->create_action(vformat(TTR("Anim Multi Change %s"), chan)); } - undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new); - undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old); + undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value); + undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old); update_obj = true; } break; case Animation::TYPE_VALUE: { @@ -1009,11 +1045,13 @@ public: } switch (animation->track_get_type(track)) { - case Animation::TYPE_TRANSFORM3D: { - Dictionary d = animation->track_get_key_value(track, key); - ERR_FAIL_COND_V(!d.has(name), false); - r_ret = d[p_name]; - return true; + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + if (name == "position" || name == "rotation" || name == "scale") { + r_ret = animation->track_get_key_value(track, key); + return true; + } } break; case Animation::TYPE_VALUE: { @@ -1159,9 +1197,13 @@ public: if (same_track_type) { switch (animation->track_get_type(first_track)) { - case Animation::TYPE_TRANSFORM3D: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, "location")); - p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation")); + case Animation::TYPE_POSITION_3D: { + p_list->push_back(PropertyInfo(Variant::VECTOR3, "position")); + } break; + case Animation::TYPE_ROTATION_3D: { + p_list->push_back(PropertyInfo(Variant::QUATERNION, "scale")); + } break; + case Animation::TYPE_SCALE_3D: { p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale")); } break; case Animation::TYPE_VALUE: { @@ -1359,7 +1401,9 @@ void AnimationTimelineEdit::_notification(int p_what) { add_track->get_popup()->clear(); add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")), TTR("Property Track")); - add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")), TTR("3D Transform Track")); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")), TTR("3D Position Track")); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")), TTR("3D Rotation Track")); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")), TTR("3D Scale Track")); add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")), TTR("Call Method Track")); add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), TTR("Bezier Curve Track")); add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")), TTR("Audio Playback Track")); @@ -1828,9 +1872,11 @@ void AnimationTrackEdit::_notification(int p_what) { Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label")); int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label")); Color color = get_theme_color(SNAME("font_color"), SNAME("Label")); - Ref<Texture2D> type_icons[6] = { + Ref<Texture2D> type_icons[8] = { get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")), + get_theme_icon(SNAME("KeyTrackPosition"), SNAME("EditorIcons")), + get_theme_icon(SNAME("KeyTrackRotation"), SNAME("EditorIcons")), + get_theme_icon(SNAME("KeyTrackScale"), SNAME("EditorIcons")), get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")), get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")), @@ -2021,7 +2067,7 @@ void AnimationTrackEdit::_notification(int p_what) { interp_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2; interp_mode_rect.size = icon->get_size(); - if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) { + if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) { draw_texture(icon, interp_mode_rect.position); } // Make it easier to click. @@ -2031,7 +2077,7 @@ void AnimationTrackEdit::_notification(int p_what) { ofs += icon->get_width() + hsep; interp_mode_rect.size.x += hsep; - if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) { + if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) { draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2)); interp_mode_rect.size.x += down_icon->get_width(); } else { @@ -2054,7 +2100,7 @@ void AnimationTrackEdit::_notification(int p_what) { loop_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2; loop_mode_rect.size = icon->get_size(); - if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) { + if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) { draw_texture(icon, loop_mode_rect.position); } @@ -2064,7 +2110,7 @@ void AnimationTrackEdit::_notification(int p_what) { ofs += icon->get_width() + hsep; loop_mode_rect.size.x += hsep; - if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) { + if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) { draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2)); loop_mode_rect.size.x += down_icon->get_width(); } else { @@ -2284,9 +2330,11 @@ void AnimationTrackEdit::set_animation_and_track(const Ref<Animation> &p_animati track = p_track; update(); - Ref<Texture2D> type_icons[6] = { + Ref<Texture2D> type_icons[8] = { get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")), + get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")), + get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")), + get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")), get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")), get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")), @@ -2456,17 +2504,17 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { if (key_idx != -1) { String text = TTR("Time (s): ") + rtos(animation->track_get_key_time(track, key_idx)) + "\n"; switch (animation->track_get_type(track)) { - case Animation::TYPE_TRANSFORM3D: { - Dictionary d = animation->track_get_key_value(track, key_idx); - if (d.has("location")) { - text += "Pos: " + String(d["location"]) + "\n"; - } - if (d.has("rotation")) { - text += "Rot: " + String(d["rotation"]) + "\n"; - } - if (d.has("scale")) { - text += "Scale: " + String(d["scale"]) + "\n"; - } + case Animation::TYPE_POSITION_3D: { + Vector3 t = animation->track_get_key_value(track, key_idx); + text += "Position: " + String(t) + "\n"; + } break; + case Animation::TYPE_ROTATION_3D: { + Quaternion t = animation->track_get_key_value(track, key_idx); + text += "Rotation: " + String(t) + "\n"; + } break; + case Animation::TYPE_SCALE_3D: { + Vector3 t = animation->track_get_key_value(track, key_idx); + text += "Scale: " + String(t) + "\n"; } break; case Animation::TYPE_VALUE: { const Variant &v = animation->track_get_key_value(track, key_idx); @@ -3323,7 +3371,11 @@ static bool track_type_is_resettable(Animation::TrackType p_type) { [[fallthrough]]; case Animation::TYPE_BEZIER: [[fallthrough]]; - case Animation::TYPE_TRANSFORM3D: + case Animation::TYPE_POSITION_3D: + [[fallthrough]]; + case Animation::TYPE_ROTATION_3D: + [[fallthrough]]; + case Animation::TYPE_SCALE_3D: return true; default: return false; @@ -3483,33 +3535,50 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_ NodePath np = path; - int track_idx = -1; + int position_idx = -1; + int rotation_idx = -1; + int scale_idx = -1; for (int i = 0; i < animation->get_track_count(); i++) { - if (animation->track_get_type(i) != Animation::TYPE_TRANSFORM3D) { - continue; - } if (animation->track_get_path(i) != np) { continue; } - track_idx = i; - break; + if (animation->track_get_type(i) == Animation::TYPE_POSITION_3D) { + position_idx = i; + } + if (animation->track_get_type(i) == Animation::TYPE_ROTATION_3D) { + rotation_idx = i; + } + if (animation->track_get_type(i) == Animation::TYPE_SCALE_3D) { + scale_idx = i; + } } InsertData id; - Dictionary val; - id.path = np; - id.track_idx = track_idx; - id.value = p_xform; - id.type = Animation::TYPE_TRANSFORM3D; // TRANSLATORS: This describes the target of new animation track, will be inserted into another string. id.query = vformat(TTR("node '%s'"), p_node->get_name()); id.advance = false; - // Dialog insert. - _query_insert(id); + { + id.track_idx = position_idx; + id.value = p_xform.origin; + id.type = Animation::TYPE_POSITION_3D; + _query_insert(id); + } + { + id.track_idx = rotation_idx; + id.value = p_xform.basis.get_rotation_quaternion(); + id.type = Animation::TYPE_ROTATION_3D; + _query_insert(id); + } + { + id.track_idx = scale_idx; + id.value = p_xform.basis.get_scale(); + id.type = Animation::TYPE_SCALE_3D; + _query_insert(id); + } } bool AnimationTrackEditor::has_transform_track(Node3D *p_node, const String &p_sub) { @@ -3530,7 +3599,7 @@ bool AnimationTrackEditor::has_transform_track(Node3D *p_node, const String &p_s } int track_id = animation->find_track(path); if (track_id >= 0) { - if (animation->track_get_type(track_id) == Animation::TYPE_TRANSFORM3D) { + if (animation->track_get_type(track_id) == Animation::TYPE_POSITION_3D || animation->track_get_type(track_id) == Animation::TYPE_ROTATION_3D || animation->track_get_type(track_id) == Animation::TYPE_SCALE_3D) { return true; } } @@ -3977,18 +4046,13 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD Variant value; switch (p_id.type) { + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: case Animation::TYPE_VALUE: { value = p_id.value; } break; - case Animation::TYPE_TRANSFORM3D: { - Transform3D tr = p_id.value; - Dictionary d; - d["location"] = tr.origin; - d["scale"] = tr.basis.get_scale(); - d["rotation"] = Quaternion(tr.basis); - value = d; - } break; case Animation::TYPE_BEZIER: { Array array; array.resize(5); @@ -4404,8 +4468,8 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) { ERR_FAIL_COND(!node); NodePath path_to = root->get_path_to(node); - if (adding_track_type == Animation::TYPE_TRANSFORM3D && !node->is_class("Node3D")) { - EditorNode::get_singleton()->show_warning(TTR("Transform3D tracks only apply to 3D-based nodes.")); + if ((adding_track_type == Animation::TYPE_POSITION_3D || adding_track_type == Animation::TYPE_ROTATION_3D || adding_track_type == Animation::TYPE_SCALE_3D) && !node->is_class("Node3D")) { + EditorNode::get_singleton()->show_warning(TTR("Position/Rotation/Scale 3D tracks only apply to 3D-based nodes.")); return; } @@ -4415,7 +4479,9 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) { prop_selector->set_type_filter(Vector<Variant::Type>()); prop_selector->select_property_from_instance(node); } break; - case Animation::TYPE_TRANSFORM3D: + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: case Animation::TYPE_METHOD: { undo_redo->create_action(TTR("Add Track")); undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type); @@ -4584,7 +4650,27 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { } switch (animation->track_get_type(p_track)) { - case Animation::TYPE_TRANSFORM3D: { + case Animation::TYPE_POSITION_3D: { + if (!root->has_node(animation->track_get_path(p_track))) { + EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key.")); + return; + } + Node3D *base = Object::cast_to<Node3D>(root->get_node(animation->track_get_path(p_track))); + + if (!base) { + EditorNode::get_singleton()->show_warning(TTR("Track is not of type Node3D, can't insert key")); + return; + } + + Vector3 pos = base->get_position(); + + undo_redo->create_action(TTR("Add Position Key")); + undo_redo->add_do_method(animation.ptr(), "position_track_insert_key", p_track, p_ofs, pos); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs); + undo_redo->commit_action(); + + } break; + case Animation::TYPE_ROTATION_3D: { if (!root->has_node(animation->track_get_path(p_track))) { EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key.")); return; @@ -4596,14 +4682,30 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { return; } - Transform3D xf = base->get_transform(); + Quaternion rot = base->get_transform().basis.operator Quaternion(); + + undo_redo->create_action(TTR("Add Rotation Key")); + undo_redo->add_do_method(animation.ptr(), "rotation_track_insert_key", p_track, p_ofs, rot); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs); + undo_redo->commit_action(); + + } break; + case Animation::TYPE_SCALE_3D: { + if (!root->has_node(animation->track_get_path(p_track))) { + EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key.")); + return; + } + Node3D *base = Object::cast_to<Node3D>(root->get_node(animation->track_get_path(p_track))); + + if (!base) { + EditorNode::get_singleton()->show_warning(TTR("Track is not of type Node3D, can't insert key")); + return; + } - Vector3 loc = xf.get_origin(); - Vector3 scale = xf.basis.get_scale_local(); - Quaternion rot = xf.basis; + Vector3 scale = base->get_scale(); - undo_redo->create_action(TTR("Add Transform Track Key")); - undo_redo->add_do_method(animation.ptr(), "transform_track_insert_key", p_track, p_ofs, loc, rot, scale); + undo_redo->create_action(TTR("Add Scale Key")); + undo_redo->add_do_method(animation.ptr(), "scale_track_insert_key", p_track, p_ofs, scale); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs); undo_redo->commit_action(); @@ -5277,8 +5379,14 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { } switch (animation->track_get_type(i)) { - case Animation::TYPE_TRANSFORM3D: - text += " (Transform)"; + case Animation::TYPE_POSITION_3D: + text += " (Position)"; + break; + case Animation::TYPE_ROTATION_3D: + text += " (Rotation)"; + break; + case Animation::TYPE_SCALE_3D: + text += " (Scale)"; break; case Animation::TYPE_METHOD: text += " (Methods)"; diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 576edef0c8..2555901557 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -35,7 +35,7 @@ #include "editor/editor_spin_slider.h" #include "editor/property_editor.h" #include "editor/property_selector.h" -#include "scene/animation/animation_cache.h" + #include "scene/gui/control.h" #include "scene/gui/file_dialog.h" #include "scene/gui/menu_button.h" diff --git a/editor/icons/KeyTrackPosition.svg b/editor/icons/KeyTrackPosition.svg new file mode 100644 index 0000000000..05c6e88841 --- /dev/null +++ b/editor/icons/KeyTrackPosition.svg @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + height="10" + viewBox="0 0 10 10" + width="10" + version="1.1" + id="svg12" + sodipodi:docname="KeyTrackPosition.svg" + inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs16" /> + <sodipodi:namedview + id="namedview14" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="42.2" + inkscape:cx="12.78436" + inkscape:cy="6.1729858" + inkscape:window-width="1848" + inkscape:window-height="1016" + inkscape:window-x="72" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="svg12" /> + <ellipse + style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round" + id="path921" + cx="-0.88986981" + cy="6.0959954" + rx="1.2495773" + ry="1.0867468" /> + <path + d="M 4.949661,0.60426977 A 0.6444116,0.6444116 0 0 0 4.504153,0.79178767 L 3.215459,2.0804819 4.12663,2.9916532 4.95977,2.1585124 5.792911,2.9916532 6.704083,2.0804819 5.415388,0.79178767 A 0.6444116,0.6444116 0 0 0 4.949744,0.60426977 Z M 1.926771,3.3691634 0.638077,4.6578577 a 0.6444116,0.6444116 0 0 0 0,0.9111713 L 1.926771,6.8577233 2.837942,5.946552 2.004801,5.1134111 2.837942,4.2802702 1.926771,3.3690989 Z m 6.065948,0 -0.911171,0.9111713 0.83314,0.8331409 -0.83314,0.8331408 0.911171,0.9111714 1.288694,-1.2886944 a 0.6444116,0.6444116 0 0 0 0,-0.9111713 L 7.992719,3.3692278 Z M 4.959777,3.8247361 A 1.2886943,1.2886943 0 0 0 3.671083,5.1134305 1.2886943,1.2886943 0 0 0 4.959777,6.4021248 1.2886943,1.2886943 0 0 0 6.248471,5.1134305 1.2886943,1.2886943 0 0 0 4.959777,3.8247361 Z m -0.83314,3.4105296 -0.911172,0.9111715 1.288694,1.288694 a 0.6444116,0.6444116 0 0 0 0.911171,0 L 6.704025,8.1464372 5.792853,7.2352657 4.959712,8.0684062 4.126572,7.2352657 Z" + fill="#e0e0e0" + fill-opacity="0.99608" + id="path1400" + style="fill:#ea7940;fill-opacity:1;stroke-width:0.644347" /> +</svg> diff --git a/editor/icons/KeyTrackRotation.svg b/editor/icons/KeyTrackRotation.svg new file mode 100644 index 0000000000..d05e381eb2 --- /dev/null +++ b/editor/icons/KeyTrackRotation.svg @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + height="10" + viewBox="0 0 10 10" + width="10" + version="1.1" + id="svg12" + sodipodi:docname="KeyTrackRotation.svg" + inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs16" /> + <sodipodi:namedview + id="namedview14" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="42.2" + inkscape:cx="1.9075829" + inkscape:cy="5.8175355" + inkscape:window-width="1848" + inkscape:window-height="1016" + inkscape:window-x="72" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="svg12" /> + <ellipse + style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round" + id="path921" + cx="-0.88986981" + cy="6.0959954" + rx="1.2495773" + ry="1.0867468" /> + <path + d="m 5.0711986,0.88214062 a 4.1086779,4.1086779 0 0 0 -0.178839,0.00115 4.1086779,4.1086779 0 0 0 -0.409265,0.033245 A 4.1086779,4.1086779 0 0 0 0.99001362,4.1883346 4.1086779,4.1086779 0 0 0 2.1467236,7.9244144 h -0.648877 v 1.1739077 h 2.347816 a 0.58701268,0.58701268 0 0 0 0.569756,-0.729119 l -0.586953,-2.3478164 -1.139514,0.2854534 0.165082,0.6580347 a 2.9347699,2.9347699 0 0 1 -0.769204,-1.9752178 2.9347699,2.9347699 0 0 1 2.93477,-2.93477 2.9347699,2.9347699 0 0 1 2.93477,2.93477 2.9347699,2.9347699 0 0 1 -0.860944,2.0738257 l 0.831127,0.8311266 A 4.1086779,4.1086779 0 0 0 8.7040866,3.1726236 4.1086779,4.1086779 0 0 0 5.0711336,0.88215292 Z m -0.05159,2.93359608 a 1.173908,1.173908 0 0 0 -1.173907,1.173908 1.173908,1.173908 0 0 0 1.173907,1.173908 1.173908,1.173908 0 0 0 1.173909,-1.173908 1.173908,1.173908 0 0 0 -1.173909,-1.173908 z" + fill="#e0e0e0" + fill-opacity="0.99608" + id="path1165" + style="fill:#ff2b88;fill-opacity:1;stroke-width:0.586954" /> +</svg> diff --git a/editor/icons/KeyTrackScale.svg b/editor/icons/KeyTrackScale.svg new file mode 100644 index 0000000000..9269ccbca2 --- /dev/null +++ b/editor/icons/KeyTrackScale.svg @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + height="10" + viewBox="0 0 10 10" + width="10" + version="1.1" + id="svg12" + sodipodi:docname="KeyTrackScale.svg" + inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs16" /> + <sodipodi:namedview + id="namedview14" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="42.2" + inkscape:cx="1.9075829" + inkscape:cy="5.8175355" + inkscape:window-width="1848" + inkscape:window-height="1016" + inkscape:window-x="72" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="svg12" /> + <ellipse + style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round" + id="path921" + cx="-0.88986981" + cy="6.0959954" + rx="1.2495773" + ry="1.0867468" /> + <path + d="M 5.5742494,0.94786723 A 0.58774585,0.58774585 0 0 0 4.9865036,1.535613 0.58774585,0.58774585 0 0 0 5.5742494,2.1233589 h 1.519852 L 6.334146,2.8833142 7.1652774,3.7144456 7.9252328,2.9544903 V 4.4743422 A 0.58774585,0.58774585 0 0 0 8.5129787,5.0620881 0.58774585,0.58774585 0 0 0 9.1007245,4.4743422 V 1.535613 A 0.58780462,0.58780462 0 0 0 8.5129787,0.94786723 Z M 4.9865036,3.8865964 A 1.1754917,1.1754917 0 0 0 3.8110119,5.0620881 1.1754917,1.1754917 0 0 0 4.9865036,6.2375798 1.1754917,1.1754917 0 0 0 6.1619953,5.0620881 1.1754917,1.1754917 0 0 0 4.9865036,3.8865964 Z M 1.4600285,5.0620881 A 0.58774585,0.58774585 0 0 0 0.8722826,5.6498339 V 8.5885638 A 0.58780462,0.58780462 0 0 0 1.4600285,9.1763092 H 4.3987577 A 0.58774585,0.58774585 0 0 0 4.9865036,8.5885638 0.58774585,0.58774585 0 0 0 4.3987577,8.0008173 H 2.8789057 L 3.6388611,7.2408619 2.8077297,6.4097305 2.0477743,7.1696859 V 5.6498339 A 0.58774585,0.58774585 0 0 0 1.4600285,5.0620881 Z" + fill="#e0e0e0" + fill-opacity="0.99608" + id="path7" + style="fill:#eac840;fill-opacity:1;stroke-width:0.587745" /> +</svg> diff --git a/editor/icons/KeyXPosition.svg b/editor/icons/KeyXPosition.svg new file mode 100644 index 0000000000..5816a241c9 --- /dev/null +++ b/editor/icons/KeyXPosition.svg @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + height="10" + viewBox="0 0 10 10" + width="10" + version="1.1" + id="svg1678" + sodipodi:docname="KeyXPosition.svg" + inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs1682" /> + <sodipodi:namedview + id="namedview1680" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="84.4" + inkscape:cx="4.9940758" + inkscape:cy="5.0059242" + inkscape:window-width="1848" + inkscape:window-height="1016" + inkscape:window-x="72" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="svg1678" /> + <rect + fill="#ea7940" + height="6.1027" + ry=".76286" + transform="matrix(.70710678 -.70710678 .70710678 .70710678 0 -1042.4)" + width="6.1027" + x="-740.13947" + y="741.10779" + id="rect1676" /> +</svg> diff --git a/editor/icons/KeyXRotation.svg b/editor/icons/KeyXRotation.svg new file mode 100644 index 0000000000..6cd5a67ac0 --- /dev/null +++ b/editor/icons/KeyXRotation.svg @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + height="10" + viewBox="0 0 10 10" + width="10" + version="1.1" + id="svg1678" + sodipodi:docname="KeyXRotation.svg" + inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs1682" /> + <sodipodi:namedview + id="namedview1680" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="84.4" + inkscape:cx="0.32582938" + inkscape:cy="5.0059242" + inkscape:window-width="1848" + inkscape:window-height="1016" + inkscape:window-x="72" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="svg1678" /> + <rect + fill="#ea7940" + height="6.1027002" + ry="0.76286" + transform="rotate(-45,-1258.2881,-521.2)" + width="6.1030002" + x="-740.13947" + y="741.10779" + id="rect1676" + style="fill:#ee3c94;fill-opacity:1" /> +</svg> diff --git a/editor/icons/KeyXScale.svg b/editor/icons/KeyXScale.svg new file mode 100644 index 0000000000..588fa5d2f3 --- /dev/null +++ b/editor/icons/KeyXScale.svg @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + height="10" + viewBox="0 0 10 10" + width="10" + version="1.1" + id="svg1678" + sodipodi:docname="KeyXScale.svg" + inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs1682" /> + <sodipodi:namedview + id="namedview1680" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="84.4" + inkscape:cx="2.6658768" + inkscape:cy="5.0059242" + inkscape:window-width="1473" + inkscape:window-height="752" + inkscape:window-x="122" + inkscape:window-y="114" + inkscape:window-maximized="0" + inkscape:current-layer="svg1678" /> + <rect + fill="#ea7940" + height="6.1027002" + ry="0.76286" + transform="rotate(-45,-1258.2881,-521.2)" + width="6.1030002" + x="-740.13947" + y="741.10779" + id="rect1676" + style="fill:#dbbf4f;fill-opacity:1" /> +</svg> diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp index 4b01595028..d1bacf54de 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/editor_import_collada.cpp @@ -91,8 +91,8 @@ struct ColladaImport { Error _create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform3D &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<ImporterMesh>> p_morph_meshes = Vector<Ref<ImporterMesh>>(), bool p_use_compression = false, bool p_use_mesh_material = false); Error load(const String &p_path, int p_flags, bool p_force_make_tangents = false, bool p_use_compression = false); void _fix_param_animation_tracks(); - void create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks); - void create_animations(bool p_make_tracks_in_all_bones, bool p_import_value_tracks); + void create_animation(int p_clip, bool p_import_value_tracks); + void create_animations(bool p_import_value_tracks); Set<String> tracks_in_clips; Vector<String> missing_textures; @@ -120,6 +120,15 @@ Error ColladaImport::_populate_skeleton(Skeleton3D *p_skeleton, Collada::Node *p skeleton_bone_map[p_skeleton][joint->sid] = r_bone; + { + Transform3D xform = joint->compute_transform(collada); + collada.fix_transform(xform) * joint->post_transform; + + p_skeleton->set_bone_pose_position(r_bone, xform.origin); + p_skeleton->set_bone_pose_rotation(r_bone, xform.basis.get_rotation_quaternion()); + p_skeleton->set_bone_pose_scale(r_bone, xform.basis.get_scale()); + } + if (collada.state.bone_rest_map.has(joint->sid)) { p_skeleton->set_bone_rest(r_bone, collada.fix_transform(collada.state.bone_rest_map[joint->sid])); //should map this bone to something for animation? @@ -1384,7 +1393,7 @@ void ColladaImport::_fix_param_animation_tracks() { } } -void ColladaImport::create_animations(bool p_make_tracks_in_all_bones, bool p_import_value_tracks) { +void ColladaImport::create_animations(bool p_import_value_tracks) { _fix_param_animation_tracks(); for (int i = 0; i < collada.state.animation_clips.size(); i++) { for (int j = 0; j < collada.state.animation_clips[i].tracks.size(); j++) { @@ -1417,13 +1426,13 @@ void ColladaImport::create_animations(bool p_make_tracks_in_all_bones, bool p_im } } - create_animation(-1, p_make_tracks_in_all_bones, p_import_value_tracks); + create_animation(-1, p_import_value_tracks); for (int i = 0; i < collada.state.animation_clips.size(); i++) { - create_animation(i, p_make_tracks_in_all_bones, p_import_value_tracks); + create_animation(i, p_import_value_tracks); } } -void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks) { +void ColladaImport::create_animation(int p_clip, bool p_import_value_tracks) { Ref<Animation> animation = Ref<Animation>(memnew(Animation)); if (p_clip == -1) { @@ -1522,10 +1531,55 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones continue; } - animation->add_track(Animation::TYPE_TRANSFORM3D); - int track = animation->get_track_count() - 1; - animation->track_set_path(track, path); - animation->track_set_imported(track, true); //helps merging later + bool has_position = false; + bool has_rotation = false; + bool has_scale = false; + + for (int i = 0; cn->xform_list.size(); i++) { + switch (cn->xform_list[i].op) { + case Collada::Node::XForm::OP_ROTATE: { + has_rotation = true; + } break; + case Collada::Node::XForm::OP_SCALE: { + has_scale = true; + } break; + case Collada::Node::XForm::OP_TRANSLATE: { + has_position = true; + } break; + case Collada::Node::XForm::OP_MATRIX: { + has_position = true; + has_rotation = true; + has_scale = true; + } break; + case Collada::Node::XForm::OP_VISIBILITY: { + } break; + } + } + + int base_track = animation->get_track_count(); + int position_idx = -1; + if (has_position) { + position_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_POSITION_3D); + animation->track_set_path(position_idx, path); + animation->track_set_imported(position_idx, true); //helps merging later + } + + int rotation_idx = -1; + if (has_rotation) { + rotation_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_ROTATION_3D); + animation->track_set_path(rotation_idx, path); + animation->track_set_imported(rotation_idx, true); //helps merging later + } + + int scale_idx = -1; + if (has_scale) { + scale_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_SCALE_3D); + animation->track_set_path(scale_idx, path); + animation->track_set_imported(scale_idx, true); //helps merging later + } Vector<real_t> snapshots = base_snapshots; @@ -1594,22 +1648,21 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones Transform3D xform = cn->compute_transform(collada); xform = collada.fix_transform(xform) * cn->post_transform; - if (nm.bone >= 0) { - //make bone transform relative to rest (in case of skeleton) - Skeleton3D *sk = Object::cast_to<Skeleton3D>(nm.node); - if (sk) { - xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform; - } else { - ERR_PRINT("Collada: Invalid skeleton"); - } - } - Vector3 s = xform.basis.get_scale(); bool singular_matrix = Math::is_zero_approx(s.x) || Math::is_zero_approx(s.y) || Math::is_zero_approx(s.z); - Quaternion q = singular_matrix ? Quaternion() : xform.basis.get_rotation_quaternion(); + Quaternion q = singular_matrix ? Quaternion() : + xform.basis.get_rotation_quaternion(); Vector3 l = xform.origin; - animation->transform_track_insert_key(track, snapshots[i], l, q, s); + if (position_idx >= 0) { + animation->position_track_insert_key(position_idx, snapshots[i], l); + } + if (rotation_idx >= 0) { + animation->rotation_track_insert_key(rotation_idx, snapshots[i], q); + } + if (scale_idx >= 0) { + animation->scale_track_insert_key(scale_idx, snapshots[i], s); + } } if (nm.bone >= 0) { @@ -1621,48 +1674,15 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones if (found_anim) { tracks_found = true; } else { - animation->remove_track(track); - } - } - - if (p_make_tracks_in_all_bones) { - //some bones may lack animation, but since we don't store pose as a property, we must add keyframes! - for (const KeyValue<String, bool> &E : bones_with_animation) { - if (E.value) { - continue; + if (position_idx >= 0) { + animation->remove_track(base_track); } - - NodeMap &nm = node_map[E.key]; - String path = scene->get_path_to(nm.node); - ERR_CONTINUE(nm.bone < 0); - Skeleton3D *sk = static_cast<Skeleton3D *>(nm.node); - String name = sk->get_bone_name(nm.bone); - path = path + ":" + name; - - Collada::Node *cn = collada.state.scene_map[E.key]; - if (cn->ignore_anim) { - WARN_PRINT("Collada: Ignoring animation on node: " + path); - continue; + if (rotation_idx >= 0) { + animation->remove_track(base_track); + } + if (scale_idx >= 0) { + animation->remove_track(base_track); } - - animation->add_track(Animation::TYPE_TRANSFORM3D); - int track = animation->get_track_count() - 1; - animation->track_set_path(track, path); - animation->track_set_imported(track, true); //helps merging later - - Transform3D xform = cn->compute_transform(collada); - xform = collada.fix_transform(xform) * cn->post_transform; - - xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform; - - Vector3 s = xform.basis.get_scale(); - bool singular_matrix = Math::is_zero_approx(s.x) || Math::is_zero_approx(s.y) || Math::is_zero_approx(s.z); - Quaternion q = singular_matrix ? Quaternion() : xform.basis.get_rotation_quaternion(); - Vector3 l = xform.origin; - - animation->transform_track_insert_key(track, 0, l, q, s); - - tracks_found = true; } } @@ -1773,7 +1793,7 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_ } if (p_flags & IMPORT_ANIMATION) { - state.create_animations(true, true); + state.create_animations(true); AnimationPlayer *ap = memnew(AnimationPlayer); for (int i = 0; i < state.animations.size(); i++) { String name; @@ -1800,7 +1820,7 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String &p_path Error err = state.load(p_path, Collada::IMPORT_FLAG_ANIMATION, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS); ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load animation from file '" + p_path + "'."); - state.create_animations(true, true); + state.create_animations(true); if (state.scene) { memdelete(state.scene); } diff --git a/editor/import/editor_importer_bake_reset.cpp b/editor/import/editor_importer_bake_reset.cpp deleted file mode 100644 index 451a07351c..0000000000 --- a/editor/import/editor_importer_bake_reset.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/*************************************************************************/ -/* editor_importer_bake_reset.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "editor/import/editor_importer_bake_reset.h" - -#include "core/error/error_list.h" -#include "core/error/error_macros.h" -#include "core/math/transform_3d.h" -#include "resource_importer_scene.h" -#include "scene/3d/importer_mesh_instance_3d.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/3d/node_3d.h" -#include "scene/3d/skeleton_3d.h" -#include "scene/animation/animation_player.h" - -// Given that an engineering team has made a reference character, one wants ten animators to create animations. -// Currently, a tech artist needs to combine the ten files into one exported gltf2 to import into Godot Engine. -// We bake the RESET animation and then set it to identity, -// so that rigs with corresponding RESET animation can have their animations transferred with ease. -// -// The original algorithm for the code was used to change skeleton bone rolls to be parent to child. -// -// Reference https://github.com/godotengine/godot-proposals/issues/2961 -void BakeReset::_bake_animation_pose(Node *scene, const String &p_bake_anim) { - Map<StringName, BakeResetRestBone> r_rest_bones; - Vector<Node3D *> r_meshes; - List<Node *> queue; - queue.push_back(scene); - while (!queue.is_empty()) { - List<Node *>::Element *E = queue.front(); - Node *node = E->get(); - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(node); - // Step 1: import scene with animations into the rest bones data structure. - _fetch_reset_animation(ap, r_rest_bones, p_bake_anim); - - int child_count = node->get_child_count(); - for (int i = 0; i < child_count; i++) { - queue.push_back(node->get_child(i)); - } - queue.pop_front(); - } - - queue.push_back(scene); - while (!queue.is_empty()) { - List<Node *>::Element *E = queue.front(); - Node *node = E->get(); - ImporterMeshInstance3D *editor_mesh_3d = scene->cast_to<ImporterMeshInstance3D>(node); - MeshInstance3D *mesh_3d = scene->cast_to<MeshInstance3D>(node); - if (scene->cast_to<Skeleton3D>(node)) { - Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node); - - // Step 2: Bake the RESET animation from the RestBone to the skeleton. - _fix_skeleton(skeleton, r_rest_bones); - } - if (editor_mesh_3d) { - NodePath path = editor_mesh_3d->get_skeleton_path(); - if (!path.is_empty() && editor_mesh_3d->get_node_or_null(path) && Object::cast_to<Skeleton3D>(editor_mesh_3d->get_node_or_null(path))) { - r_meshes.push_back(editor_mesh_3d); - } - } else if (mesh_3d) { - NodePath path = mesh_3d->get_skeleton_path(); - if (!path.is_empty() && mesh_3d->get_node_or_null(path) && Object::cast_to<Skeleton3D>(mesh_3d->get_node_or_null(path))) { - r_meshes.push_back(mesh_3d); - } - } - int child_count = node->get_child_count(); - for (int i = 0; i < child_count; i++) { - queue.push_back(node->get_child(i)); - } - queue.pop_front(); - } - - queue.push_back(scene); - while (!queue.is_empty()) { - List<Node *>::Element *E = queue.front(); - Node *node = E->get(); - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(node); - if (ap) { - // Step 3: Key all RESET animation frames to identity. - _align_animations(ap, r_rest_bones); - } - - int child_count = node->get_child_count(); - for (int i = 0; i < child_count; i++) { - queue.push_back(node->get_child(i)); - } - queue.pop_front(); - } -} - -void BakeReset::_align_animations(AnimationPlayer *p_ap, const Map<StringName, BakeResetRestBone> &r_rest_bones) { - ERR_FAIL_NULL(p_ap); - List<StringName> anim_names; - p_ap->get_animation_list(&anim_names); - for (List<StringName>::Element *anim_i = anim_names.front(); anim_i; anim_i = anim_i->next()) { - Ref<Animation> a = p_ap->get_animation(anim_i->get()); - ERR_CONTINUE(a.is_null()); - for (const KeyValue<StringName, BakeResetRestBone> &rest_bone_i : r_rest_bones) { - int track = a->find_track(NodePath(rest_bone_i.key)); - if (track == -1) { - continue; - } - int new_track = a->add_track(Animation::TYPE_TRANSFORM3D); - NodePath new_path = NodePath(rest_bone_i.key); - const BakeResetRestBone rest_bone = rest_bone_i.value; - a->track_set_path(new_track, new_path); - for (int key_i = 0; key_i < a->track_get_key_count(track); key_i++) { - Vector3 loc; - Quaternion rot; - Vector3 scale; - Error err = a->transform_track_get_key(track, key_i, &loc, &rot, &scale); - ERR_CONTINUE(err); - real_t time = a->track_get_key_time(track, key_i); - rot.normalize(); - loc = loc - rest_bone.loc; - rot = rest_bone.rest_delta.get_rotation_quaternion().inverse() * rot; - rot.normalize(); - scale = Vector3(1, 1, 1) - (rest_bone.rest_delta.get_scale() - scale); - // Apply the reverse of the rest changes to make the key be close to identity transform. - a->transform_track_insert_key(new_track, time, loc, rot, scale); - } - a->remove_track(track); - } - } -} - -void BakeReset::_fetch_reset_animation(AnimationPlayer *p_ap, Map<StringName, BakeResetRestBone> &r_rest_bones, const String &p_bake_anim) { - if (!p_ap) { - return; - } - List<StringName> anim_names; - p_ap->get_animation_list(&anim_names); - Node *root = p_ap->get_owner(); - ERR_FAIL_NULL(root); - if (!p_ap->has_animation(p_bake_anim)) { - return; - } - Ref<Animation> a = p_ap->get_animation(p_bake_anim); - if (a.is_null()) { - return; - } - for (int32_t track = 0; track < a->get_track_count(); track++) { - NodePath path = a->track_get_path(track); - String string_path = path; - Skeleton3D *skeleton = root->cast_to<Skeleton3D>(root->get_node(string_path.get_slice(":", 0))); - if (!skeleton) { - continue; - } - String bone_name = string_path.get_slice(":", 1); - for (int key_i = 0; key_i < a->track_get_key_count(track); key_i++) { - Vector3 loc; - Quaternion rot; - Vector3 scale; - Error err = a->transform_track_get_key(track, key_i, &loc, &rot, &scale); - if (err != OK) { - ERR_PRINT_ONCE("Reset animation baker can't get key."); - continue; - } - rot.normalize(); - Basis rot_basis = Basis(rot, scale); - BakeResetRestBone rest_bone; - rest_bone.rest_delta = rot_basis; - rest_bone.loc = loc; - // Store the animation into the RestBone. - r_rest_bones[StringName(String(skeleton->get_owner()->get_path_to(skeleton)) + ":" + bone_name)] = rest_bone; - break; - } - } -} - -void BakeReset::_fix_skeleton(Skeleton3D *p_skeleton, Map<StringName, BakeReset::BakeResetRestBone> &r_rest_bones) { - int bone_count = p_skeleton->get_bone_count(); - - // First iterate through all the bones and update the RestBone. - for (int j = 0; j < bone_count; j++) { - StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(j); - BakeResetRestBone &rest_bone = r_rest_bones[final_path]; - rest_bone.rest_local = p_skeleton->get_bone_rest(j); - } - for (int i = 0; i < bone_count; i++) { - int parent_bone = p_skeleton->get_bone_parent(i); - String path = p_skeleton->get_owner()->get_path_to(p_skeleton); - StringName final_path = String(path) + String(":") + p_skeleton->get_bone_name(parent_bone); - if (parent_bone >= 0) { - r_rest_bones[path].children.push_back(i); - } - } - - // When we apply transform to a bone, we also have to move all of its children in the opposite direction. - for (int i = 0; i < bone_count; i++) { - StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(i); - r_rest_bones[final_path].rest_local = r_rest_bones[final_path].rest_local * Transform3D(r_rest_bones[final_path].rest_delta, r_rest_bones[final_path].loc); - // Iterate through the children and move in the opposite direction. - for (int j = 0; j < r_rest_bones[final_path].children.size(); j++) { - int child_index = r_rest_bones[final_path].children[j]; - StringName children_path = String(p_skeleton->get_name()) + String(":") + p_skeleton->get_bone_name(child_index); - r_rest_bones[children_path].rest_local = Transform3D(r_rest_bones[final_path].rest_delta, r_rest_bones[final_path].loc).affine_inverse() * r_rest_bones[children_path].rest_local; - } - } - - for (int i = 0; i < bone_count; i++) { - StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(i); - ERR_CONTINUE(!r_rest_bones.has(final_path)); - Transform3D rest_transform = r_rest_bones[final_path].rest_local; - p_skeleton->set_bone_rest(i, rest_transform); - } -} diff --git a/editor/import/editor_importer_bake_reset.h b/editor/import/editor_importer_bake_reset.h deleted file mode 100644 index e36ae86181..0000000000 --- a/editor/import/editor_importer_bake_reset.h +++ /dev/null @@ -1,54 +0,0 @@ -/*************************************************************************/ -/* editor_importer_bake_reset.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef RESOURCE_IMPORTER_BAKE_RESET_H -#define RESOURCE_IMPORTER_BAKE_RESET_H - -#include "scene/main/node.h" - -class Skeleton3D; -class AnimationPlayer; -class BakeReset { - struct BakeResetRestBone { - Transform3D rest_local; - Basis rest_delta; - Vector3 loc; - Vector<int> children; - }; - -public: - void _bake_animation_pose(Node *scene, const String &p_bake_anim); - -private: - void _fix_skeleton(Skeleton3D *p_skeleton, Map<StringName, BakeReset::BakeResetRestBone> &r_rest_bones); - void _align_animations(AnimationPlayer *p_ap, const Map<StringName, BakeResetRestBone> &r_rest_bones); - void _fetch_reset_animation(AnimationPlayer *p_ap, Map<StringName, BakeResetRestBone> &r_rest_bones, const String &p_bake_anim); -}; -#endif diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 1e93113488..35f1533dd0 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -32,7 +32,7 @@ #include "core/io/resource_saver.h" #include "editor/editor_node.h" -#include "editor/import/editor_importer_bake_reset.h" + #include "editor/import/scene_import_settings.h" #include "scene/3d/area_3d.h" #include "scene/3d/collision_shape_3d.h" @@ -851,42 +851,57 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_ new_anim->track_set_path(dtrack, default_anim->track_get_path(j)); if (kt > (from + 0.01) && k > 0) { - if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) { - Quaternion q; + if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) { Vector3 p; + default_anim->position_track_interpolate(j, from, &p); + new_anim->position_track_insert_key(dtrack, 0, p); + } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) { + Quaternion r; + default_anim->rotation_track_interpolate(j, from, &r); + new_anim->rotation_track_insert_key(dtrack, 0, r); + } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) { Vector3 s; - default_anim->transform_track_interpolate(j, from, &p, &q, &s); - new_anim->transform_track_insert_key(dtrack, 0, p, q, s); - } - if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { + default_anim->scale_track_interpolate(j, from, &s); + new_anim->scale_track_insert_key(dtrack, 0, s); + } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { Variant var = default_anim->value_track_interpolate(j, from); new_anim->track_insert_key(dtrack, 0, var); } } } - if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) { - Quaternion q; + if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) { Vector3 p; + default_anim->position_track_get_key(j, k, &p); + new_anim->position_track_insert_key(dtrack, kt - from, p); + } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) { + Quaternion r; + default_anim->rotation_track_get_key(j, k, &r); + new_anim->rotation_track_insert_key(dtrack, kt - from, r); + } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) { Vector3 s; - default_anim->transform_track_get_key(j, k, &p, &q, &s); - new_anim->transform_track_insert_key(dtrack, kt - from, p, q, s); - } - if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { + default_anim->scale_track_get_key(j, k, &s); + new_anim->scale_track_insert_key(dtrack, kt - from, s); + } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { Variant var = default_anim->track_get_key_value(j, k); new_anim->track_insert_key(dtrack, kt - from, var); } } if (dtrack != -1 && kt >= to) { - if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) { - Quaternion q; + if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) { Vector3 p; + default_anim->position_track_interpolate(j, to, &p); + new_anim->position_track_insert_key(dtrack, to - from, p); + } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) { + Quaternion r; + default_anim->rotation_track_interpolate(j, to, &r); + new_anim->rotation_track_insert_key(dtrack, to - from, r); + } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) { Vector3 s; - default_anim->transform_track_interpolate(j, to, &p, &q, &s); - new_anim->transform_track_insert_key(dtrack, to - from, p, q, s); - } - if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { + default_anim->scale_track_interpolate(j, to, &s); + new_anim->scale_track_insert_key(dtrack, to - from, s); + } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { Variant var = default_anim->value_track_interpolate(j, to); new_anim->track_insert_key(dtrack, to - from, var); } @@ -897,16 +912,25 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_ new_anim->add_track(default_anim->track_get_type(j)); dtrack = new_anim->get_track_count() - 1; new_anim->track_set_path(dtrack, default_anim->track_get_path(j)); - if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) { - Quaternion q; + if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) { Vector3 p; + default_anim->position_track_interpolate(j, from, &p); + new_anim->position_track_insert_key(dtrack, 0, p); + default_anim->position_track_interpolate(j, to, &p); + new_anim->position_track_insert_key(dtrack, to - from, p); + } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) { + Quaternion r; + default_anim->rotation_track_interpolate(j, from, &r); + new_anim->rotation_track_insert_key(dtrack, 0, r); + default_anim->rotation_track_interpolate(j, to, &r); + new_anim->rotation_track_insert_key(dtrack, to - from, r); + } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) { Vector3 s; - default_anim->transform_track_interpolate(j, from, &p, &q, &s); - new_anim->transform_track_insert_key(dtrack, 0, p, q, s); - default_anim->transform_track_interpolate(j, to, &p, &q, &s); - new_anim->transform_track_insert_key(dtrack, to - from, p, q, s); - } - if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { + default_anim->scale_track_interpolate(j, from, &s); + new_anim->scale_track_insert_key(dtrack, 0, s); + default_anim->scale_track_interpolate(j, to, &s); + new_anim->scale_track_insert_key(dtrack, to - from, s); + } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { Variant var = default_anim->value_track_interpolate(j, from); new_anim->track_insert_key(dtrack, 0, var); Variant to_var = default_anim->value_track_interpolate(j, to); @@ -1000,6 +1024,9 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_linear_error"), 0.05)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error"), 0.01)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angle"), 22)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/position", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/rotation", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/scale", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/amount", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0)); for (int i = 0; i < 256; i++) { @@ -1171,7 +1198,6 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true)); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/bake_reset_animation"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15)); r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), "")); @@ -1533,11 +1559,6 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p _pre_fix_node(scene, scene, collision_map); _post_fix_node(scene, scene, collision_map, scanned_meshes, node_data, material_data, animation_data, fps); - bool use_bake_reset_animation = p_options["animation/bake_reset_animation"]; - if (use_bake_reset_animation) { - BakeReset bake_reset; - bake_reset._bake_animation_pose(scene, "RESET"); - } String root_type = p_options["nodes/root_type"]; root_type = root_type.split(" ")[0]; // full root_type is "ClassName (filename.gd)" for a script global class. diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 2a67fa9aae..eb17193c00 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -65,6 +65,13 @@ public: IMPORT_USE_NAMED_SKIN_BINDS = 16, }; + enum AnimationImportBoneTracks { + ANIMATION_IMPORT_BONE_TRACKS_IF_PRESENT, + ANIMATION_IMPORT_BONE_TRACKS_IF_PRESENT_FOR_ALL, + ANIMATION_IMPORT_BONE_TRACKS_ALWAYS, + ANIMATION_IMPORT_BONE_TRACKS_NEVER, + }; + virtual uint32_t get_import_flags() const; virtual void get_extensions(List<String> *r_extensions) const; virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index ea6ef8ab84..8600ef3ae8 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -3863,7 +3863,7 @@ void Node3DEditorViewport::assign_pending_data_pointers(Node3D *p_preview_node, } Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const { - const float MAX_DISTANCE = 10; + const float MAX_DISTANCE = 50.0; Vector3 world_ray = _get_ray(p_pos); Vector3 world_pos = _get_ray_pos(p_pos); @@ -6469,7 +6469,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() { // We add a bit of margin to the from position to avoid it from snapping // when the spatial is already on a floor and there's another floor under // it - from = from + Vector3(0.0, 0.2, 0.0); + from = from + Vector3(0.0, 1, 0.0); Dictionary d; @@ -6485,7 +6485,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() { Array keys = snap_data.keys(); // The maximum height an object can travel to be snapped - const float max_snap_height = 20.0; + const float max_snap_height = 500.0; // Will be set to `true` if at least one node from the selection was successfully snapped bool snapped_to_floor = false; diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 531ffc6a73..f9c46d35c4 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -200,12 +200,7 @@ void BoneTransformEditor::_value_changed_transform(const String p_property_name, } void BoneTransformEditor::_change_transform(Transform3D p_new_transform) { - if (property.get_slicec('/', 0) == "bones" && property.get_slicec('/', 2) == "custom_pose") { - undo_redo->create_action(TTR("Set Custom Bone Pose Transform"), UndoRedo::MERGE_ENDS); - undo_redo->add_undo_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), skeleton->get_bone_custom_pose(property.get_slicec('/', 1).to_int())); - undo_redo->add_do_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), p_new_transform); - undo_redo->commit_action(); - } else if (property.get_slicec('/', 0) == "bones") { + if (property.get_slicec('/', 0) == "bones") { undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS); undo_redo->add_undo_property(skeleton, property, skeleton->get(property)); undo_redo->add_do_property(skeleton, property, p_new_transform); @@ -236,21 +231,6 @@ void BoneTransformEditor::_update_properties() { _update_transform_properties(tform); } -void BoneTransformEditor::_update_custom_pose_properties() { - if (updating) { - return; - } - - if (!skeleton) { - return; - } - - updating = true; - - Transform3D tform = skeleton->get_bone_custom_pose(property.to_int()); - _update_transform_properties(tform); -} - void BoneTransformEditor::_update_transform_properties(Transform3D tform) { Basis rotation_basis = tform.get_basis(); Vector3 rotation_radians = rotation_basis.get_rotation_euler(); @@ -463,9 +443,7 @@ void Skeleton3DEditor::pose_to_rest() { 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_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(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(); @@ -654,11 +632,9 @@ void Skeleton3DEditor::_joint_tree_selection_changed() { pose_editor->set_target(bone_path + "pose"); rest_editor->set_target(bone_path + "rest"); - custom_pose_editor->set_target(bone_path + "custom_pose"); pose_editor->set_visible(true); rest_editor->set_visible(true); - custom_pose_editor->set_visible(true); selected_bone = b_idx; } @@ -679,9 +655,6 @@ void Skeleton3DEditor::_update_properties() { if (pose_editor) { pose_editor->_update_properties(); } - if (custom_pose_editor) { - custom_pose_editor->_update_custom_pose_properties(); - } _update_gizmo_transform(); } @@ -820,12 +793,6 @@ void Skeleton3DEditor::create_editors() { 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) { @@ -1289,7 +1256,6 @@ void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gi 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. @@ -1305,7 +1271,9 @@ void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gi t.origin = orig + to_local.xform(sub); // Apply transform. - skeleton->set_bone_pose(p_id, t); + skeleton->set_bone_pose_position(p_id, t.origin); + skeleton->set_bone_pose_rotation(p_id, t.basis.operator Quaternion()); + skeleton->set_bone_pose_scale(p_id, t.basis.get_scale()); } void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) { @@ -1516,5 +1484,5 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { } Ref<ArrayMesh> m = surface_tool->commit(); - p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skeleton->register_skin(Ref<Skin>())); + p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skeleton->register_skin(skeleton->create_skin_from_rest_transforms())); } diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h index e2a1d9a628..2c21aab739 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.h +++ b/editor/plugins/skeleton_3d_editor_plugin.h @@ -102,13 +102,12 @@ public: void set_label(const String &p_label) { label = p_label; } void _update_properties(); - void _update_custom_pose_properties(); void _update_transform_properties(Transform3D p_transform); // Transform can be keyed, whether or not to show the button. void set_keyable(const bool p_keyable); - // When rest mode, pose and custom_pose editor are diasbled. + // When rest mode, pose editor are diasbled. void set_properties_read_only(const bool p_readonly); void set_transform_read_only(const bool p_readonly); @@ -151,7 +150,6 @@ class Skeleton3DEditor : public VBoxContainer { Tree *joint_tree = nullptr; BoneTransformEditor *rest_editor = nullptr; BoneTransformEditor *pose_editor = nullptr; - BoneTransformEditor *custom_pose_editor = nullptr; VSeparator *separator; MenuButton *skeleton_options = nullptr; diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index f21d5098d3..c064073b77 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -97,15 +97,6 @@ Size2i TileAtlasView::_compute_base_tiles_control_size() { if (texture.is_valid()) { size = texture->get_size(); } - - // Extend the size to all existing tiles. - Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size(); - for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { - Vector2i tile_id = tile_set_atlas_source->get_tile_id(i); - grid_size = grid_size.max(tile_id + Vector2i(1, 1)); - } - size = size.max(grid_size * (tile_set_atlas_source->get_texture_region_size() + tile_set_atlas_source->get_separation()) + tile_set_atlas_source->get_margins()); - return size; } @@ -213,43 +204,56 @@ void TileAtlasView::_draw_base_tiles() { Ref<Texture2D> texture = tile_set_atlas_source->get_texture(); if (texture.is_valid()) { Vector2i margins = tile_set_atlas_source->get_margins(); + Vector2i separation = tile_set_atlas_source->get_separation(); Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size(); - - // Draw the texture, square by square. Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size(); + + // Draw the texture where there is no tile. for (int x = 0; x < grid_size.x; x++) { for (int y = 0; y < grid_size.y; y++) { Vector2i coords = Vector2i(x, y); if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { - Rect2i rect = Rect2i(texture_region_size * coords + margins, texture_region_size); - base_tiles_draw->draw_texture_rect_region(texture, rect, rect); + Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation); + rect = rect.intersection(Rect2i(Vector2(), texture->get_size())); + if (rect.size.x > 0 && rect.size.y > 0) { + base_tiles_draw->draw_texture_rect_region(texture, rect, rect); + base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5)); + } } } } // Draw the texture around the grid. Rect2i rect; + // Top. rect.position = Vector2i(); rect.set_end(Vector2i(texture->get_size().x, margins.y)); base_tiles_draw->draw_texture_rect_region(texture, rect, rect); + base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5)); + // Bottom - int bottom_border = margins.y + (grid_size.y * texture_region_size.y); + int bottom_border = margins.y + (grid_size.y * (texture_region_size.y + separation.y)); if (bottom_border < texture->get_size().y) { rect.position = Vector2i(0, bottom_border); rect.set_end(texture->get_size()); base_tiles_draw->draw_texture_rect_region(texture, rect, rect); + base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5)); } + // Left rect.position = Vector2i(0, margins.y); - rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * texture_region_size.y))); + rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * (texture_region_size.y + separation.y)))); base_tiles_draw->draw_texture_rect_region(texture, rect, rect); + base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5)); + // Right. - int right_border = margins.x + (grid_size.x * texture_region_size.x); + int right_border = margins.x + (grid_size.x * (texture_region_size.x + separation.x)); if (right_border < texture->get_size().x) { rect.position = Vector2i(right_border, margins.y); - rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * texture_region_size.y))); + rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * (texture_region_size.y + separation.y)))); base_tiles_draw->draw_texture_rect_region(texture, rect, rect); + base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5)); } // Draw actual tiles, using their properties (modulation, etc...) @@ -258,12 +262,30 @@ void TileAtlasView::_draw_base_tiles() { for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) { // Update the y to max value. - int animation_columns = tile_set_atlas_source->get_tile_animation_columns(atlas_coords); - Vector2i frame_coords = atlas_coords + (tile_set_atlas_source->get_tile_size_in_atlas(atlas_coords) + tile_set_atlas_source->get_tile_animation_separation(atlas_coords)) * ((animation_columns > 0) ? Vector2i(frame % animation_columns, frame / animation_columns) : Vector2i(frame, 0)); - Vector2i offset_pos = (margins + (frame_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0)); + Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame); + Vector2i offset_pos = base_frame_rect.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0); // Draw the tile. TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0, frame); + + // Draw, the texture in the separation areas + if (separation.x > 0) { + Rect2i right_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(base_frame_rect.size.x, 0), Vector2i(separation.x, base_frame_rect.size.y)); + right_sep_rect = right_sep_rect.intersection(Rect2i(Vector2(), texture->get_size())); + if (right_sep_rect.size.x > 0 && right_sep_rect.size.y > 0) { + base_tiles_draw->draw_texture_rect_region(texture, right_sep_rect, right_sep_rect); + base_tiles_draw->draw_rect(right_sep_rect, Color(0.0, 0.0, 0.0, 0.5)); + } + } + + if (separation.y > 0) { + Rect2i bottom_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(0, base_frame_rect.size.y), Vector2i(base_frame_rect.size.x + separation.x, separation.y)); + bottom_sep_rect = bottom_sep_rect.intersection(Rect2i(Vector2(), texture->get_size())); + if (bottom_sep_rect.size.x > 0 && bottom_sep_rect.size.y > 0) { + base_tiles_draw->draw_texture_rect_region(texture, bottom_sep_rect, bottom_sep_rect); + base_tiles_draw->draw_rect(bottom_sep_rect, Color(0.0, 0.0, 0.0, 0.5)); + } + } } } } @@ -299,30 +321,6 @@ void TileAtlasView::_draw_base_tiles_texture_grid() { } } -void TileAtlasView::_draw_base_tiles_dark() { - Ref<Texture2D> texture = tile_set_atlas_source->get_texture(); - if (texture.is_valid()) { - Vector2i margins = tile_set_atlas_source->get_margins(); - Vector2i separation = tile_set_atlas_source->get_separation(); - Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size(); - - Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size(); - - // Draw each tile texture region. - for (int x = 0; x < grid_size.x; x++) { - for (int y = 0; y < grid_size.y; y++) { - Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation)); - Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y)); - - if (base_tile_coords == TileSetSource::INVALID_ATLAS_COORDS) { - // Draw the grid. - base_tiles_dark->draw_rect(Rect2i(origin, texture_region_size), Color(0.0, 0.0, 0.0, 0.5), true); - } - } - } - } -} - void TileAtlasView::_draw_base_tiles_shape_grid() { // Draw the shapes. Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); @@ -453,7 +451,6 @@ void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_ base_tiles_draw->update(); base_tiles_texture_grid->update(); base_tiles_shape_grid->update(); - base_tiles_dark->update(); alternatives_draw->update(); background_left->update(); background_right->update(); @@ -544,7 +541,6 @@ void TileAtlasView::update() { base_tiles_draw->update(); base_tiles_texture_grid->update(); base_tiles_shape_grid->update(); - base_tiles_dark->update(); alternatives_draw->update(); background_left->update(); background_right->update(); @@ -660,12 +656,6 @@ TileAtlasView::TileAtlasView() { base_tiles_shape_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid)); base_tiles_drawing_root->add_child(base_tiles_shape_grid); - base_tiles_dark = memnew(Control); - base_tiles_dark->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); - base_tiles_dark->set_anchors_and_offsets_preset(Control::PRESET_WIDE); - base_tiles_dark->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_dark)); - base_tiles_drawing_root->add_child(base_tiles_dark); - // Alternative tiles. Label *alternative_tiles_label = memnew(Label); alternative_tiles_label->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h index 5b0df366ae..e1ca3eebee 100644 --- a/editor/plugins/tiles/tile_atlas_view.h +++ b/editor/plugins/tiles/tile_atlas_view.h @@ -93,9 +93,6 @@ private: Control *base_tiles_shape_grid; void _draw_base_tiles_shape_grid(); - Control *base_tiles_dark; - void _draw_base_tiles_dark(); - Size2i _compute_base_tiles_control_size(); // Right side. @@ -124,7 +121,6 @@ public: // Left side. void set_texture_grid_visible(bool p_visible) { base_tiles_texture_grid->set_visible(p_visible); }; - void set_dark_visible(bool p_visible) { base_tiles_dark->set_visible(p_visible); }; void set_tile_shape_grid_visible(bool p_visible) { base_tiles_shape_grid->set_visible(p_visible); }; Vector2i get_atlas_tile_coords_at_pos(const Vector2 p_pos) const; diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 1a69d19d3c..104f46f773 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -38,8 +38,16 @@ #include "editor/editor_properties.h" #include "editor/editor_scale.h" -void TileDataEditor::_call_tile_set_changed() { - _tile_set_changed(); +void TileDataEditor::_tile_set_changed_plan_update() { + _tile_set_changed_update_needed = true; + call_deferred("_tile_set_changed_deferred_update"); +} + +void TileDataEditor::_tile_set_changed_deferred_update() { + if (_tile_set_changed_update_needed) { + _tile_set_changed(); + _tile_set_changed_update_needed = false; + } } TileData *TileDataEditor::_get_tile_data(TileMapCell p_cell) { @@ -59,18 +67,20 @@ TileData *TileDataEditor::_get_tile_data(TileMapCell p_cell) { } void TileDataEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update"), &TileDataEditor::_tile_set_changed_deferred_update); + ADD_SIGNAL(MethodInfo("needs_redraw")); } void TileDataEditor::set_tile_set(Ref<TileSet> p_tile_set) { if (tile_set.is_valid()) { - tile_set->disconnect("changed", callable_mp(this, &TileDataEditor::_call_tile_set_changed)); + tile_set->disconnect("changed", callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update)); } tile_set = p_tile_set; if (tile_set.is_valid()) { - tile_set->connect("changed", callable_mp(this, &TileDataEditor::_call_tile_set_changed)); + tile_set->connect("changed", callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update)); } - _call_tile_set_changed(); + _tile_set_changed_plan_update(); } bool DummyObject::_set(const StringName &p_name, const Variant &p_value) { diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h index 99998dc779..acb4c5882d 100644 --- a/editor/plugins/tiles/tile_data_editors.h +++ b/editor/plugins/tiles/tile_data_editors.h @@ -45,7 +45,9 @@ class TileDataEditor : public VBoxContainer { GDCLASS(TileDataEditor, VBoxContainer); private: - void _call_tile_set_changed(); + bool _tile_set_changed_update_needed = false; + void _tile_set_changed_plan_update(); + void _tile_set_changed_deferred_update(); protected: Ref<TileSet> tile_set; diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 3fbd315aec..c43a854d9a 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -520,9 +520,6 @@ void TileSetAtlasSourceEditor::_update_tile_id_label() { void TileSetAtlasSourceEditor::_update_source_inspector() { // Update the proxy object. atlas_source_proxy_object->edit(tile_set, tile_set_atlas_source, tile_set_atlas_source_id); - - // Update the "clear outside texture" button. - tool_advanced_menu_buttom->get_popup()->set_item_disabled(0, !tile_set_atlas_source->has_tiles_outside_texture()); } void TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles() { @@ -763,7 +760,11 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() { // Update visibility. bool is_visible = tools_button_group->get_pressed_button() == tool_paint_button; tile_data_editor_dropdown_button->set_visible(is_visible); - tile_data_editor_dropdown_button->set_text(TTR("Select a property editor")); + if (tile_data_editors_tree->get_selected()) { + tile_data_editor_dropdown_button->set_text(tile_data_editors_tree->get_selected()->get_text(0)); + } else { + tile_data_editor_dropdown_button->set_text(TTR("Select a property editor")); + } tile_data_editors_label->set_visible(is_visible); } @@ -959,7 +960,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven current_tile_data_editor->forward_painting_atlas_gui_input(tile_atlas_view, tile_set_atlas_source, p_event); } // Update only what's needed. - tile_set_atlas_source_changed_needs_update = false; + tile_set_changed_needs_update = false; tile_atlas_control->update(); tile_atlas_control_unscaled->update(); @@ -1061,7 +1062,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven drag_current_tile = coords; // Update only what's needed. - tile_set_atlas_source_changed_needs_update = false; + tile_set_changed_needs_update = false; _update_tile_inspector(); _update_atlas_view(); _update_tile_id_label(); @@ -1101,7 +1102,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven drag_current_tile = new_rect.position; // Update only what's needed. - tile_set_atlas_source_changed_needs_update = false; + tile_set_changed_needs_update = false; _update_tile_inspector(); _update_atlas_view(); _update_tile_id_label(); @@ -1600,9 +1601,6 @@ void TileSetAtlasSourceEditor::_menu_option(int p_option) { undo_redo->commit_action(); _update_tile_id_label(); } break; - case ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE: { - tile_set_atlas_source->clear_tiles_outside_texture(); - } break; case ADVANCED_AUTO_CREATE_TILES: { _auto_create_tiles(); } break; @@ -2012,12 +2010,12 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() { } } -void TileSetAtlasSourceEditor::_tile_set_atlas_source_changed() { - tile_set_atlas_source_changed_needs_update = true; +void TileSetAtlasSourceEditor::_tile_set_changed() { + tile_set_changed_needs_update = true; } void TileSetAtlasSourceEditor::_tile_proxy_object_changed(String p_what) { - tile_set_atlas_source_changed_needs_update = false; // Avoid updating too many things. + tile_set_changed_needs_update = false; // Avoid updating too many things. _update_atlas_view(); } @@ -2035,30 +2033,66 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo #define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property)); - AtlasTileProxyObject *tile_data = Object::cast_to<AtlasTileProxyObject>(p_edited); - if (tile_data) { + undo_redo->start_force_keep_in_merge_ends(); + AtlasTileProxyObject *tile_data_proxy = Object::cast_to<AtlasTileProxyObject>(p_edited); + if (tile_data_proxy) { Vector<String> components = String(p_property).split("/", true, 2); if (components.size() == 2 && components[1] == "polygons_count") { int layer_index = components[0].trim_prefix("physics_layer_").to_int(); int new_polygons_count = p_new_value; - int old_polygons_count = tile_data->get(vformat("physics_layer_%d/polygons_count", layer_index)); + int old_polygons_count = tile_data_proxy->get(vformat("physics_layer_%d/polygons_count", layer_index)); if (new_polygons_count < old_polygons_count) { for (int i = new_polygons_count - 1; i < old_polygons_count; i++) { - ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/points", layer_index, i)); - ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, i)); - ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, i)); + ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/points", layer_index, i)); + ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, i)); + ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, i)); } } } else if (p_property == "terrain_set") { - int current_terrain_set = tile_data->get("terrain_set"); + int current_terrain_set = tile_data_proxy->get("terrain_set"); for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); if (tile_set->is_valid_peering_bit_terrain(current_terrain_set, bit)) { - ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i])); + ADD_UNDO(tile_data_proxy, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i])); } } } } + + TileSetAtlasSourceProxyObject *atlas_source_proxy = Object::cast_to<TileSetAtlasSourceProxyObject>(p_edited); + if (atlas_source_proxy) { + TileSetAtlasSource *atlas_source = atlas_source_proxy->get_edited(); + ERR_FAIL_COND(!atlas_source); + + PackedVector2Array arr; + if (p_property == "texture") { + arr = atlas_source->get_tiles_to_be_removed_on_change(p_new_value, atlas_source->get_margins(), atlas_source->get_separation(), atlas_source->get_texture_region_size()); + } else if (p_property == "margins") { + arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), p_new_value, atlas_source->get_separation(), atlas_source->get_texture_region_size()); + } else if (p_property == "separation") { + arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), atlas_source->get_margins(), p_new_value, atlas_source->get_texture_region_size()); + } else if (p_property == "texture_region_size") { + arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), atlas_source->get_margins(), atlas_source->get_separation(), p_new_value); + } + + if (!arr.is_empty()) { + // Get all properties assigned to a tile. + List<PropertyInfo> properties; + atlas_source->get_property_list(&properties); + + for (int i = 0; i < arr.size(); i++) { + Vector2i coords = arr[i]; + String prefix = vformat("%d:%d/", coords.x, coords.y); + for (PropertyInfo pi : properties) { + if (pi.name.begins_with(prefix)) { + ADD_UNDO(atlas_source, pi.name); + } + } + } + } + } + undo_redo->end_force_keep_in_merge_ends(); + #undef ADD_UNDO } @@ -2073,8 +2107,8 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource } // Remove listener for old objects. - if (tile_set_atlas_source) { - tile_set_atlas_source->disconnect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_atlas_source_changed)); + if (tile_set.is_valid()) { + tile_set->disconnect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed)); } // Clear the selection. @@ -2086,8 +2120,8 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource tile_set_atlas_source_id = p_source_id; // Add the listener again. - if (tile_set_atlas_source) { - tile_set_atlas_source->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_atlas_source_changed)); + if (tile_set.is_valid()) { + tile_set->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed)); } // Update everything. @@ -2228,7 +2262,7 @@ void TileSetAtlasSourceEditor::_notification(int p_what) { resize_handle_disabled = get_theme_icon(SNAME("EditorHandleDisabled"), SNAME("EditorIcons")); break; case NOTIFICATION_INTERNAL_PROCESS: - if (tile_set_atlas_source_changed_needs_update) { + if (tile_set_changed_needs_update) { // Update everything. _update_source_inspector(); @@ -2241,7 +2275,7 @@ void TileSetAtlasSourceEditor::_notification(int p_what) { _update_tile_data_editors(); _update_current_tile_data_editor(); - tile_set_atlas_source_changed_needs_update = false; + tile_set_changed_needs_update = false; } break; default: @@ -2406,8 +2440,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tool_advanced_menu_buttom = memnew(MenuButton); tool_advanced_menu_buttom->set_flat(true); - tool_advanced_menu_buttom->get_popup()->add_item(TTR("Cleanup Tiles Outside Texture"), ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE); - tool_advanced_menu_buttom->get_popup()->set_item_disabled(0, true); tool_advanced_menu_buttom->get_popup()->add_item(TTR("Create Tiles in Non-Transparent Texture Regions"), ADVANCED_AUTO_CREATE_TILES); tool_advanced_menu_buttom->get_popup()->add_item(TTR("Remove Tiles in Fully Transparent Texture Regions"), ADVANCED_AUTO_REMOVE_TILES); tool_advanced_menu_buttom->get_popup()->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option)); @@ -2481,6 +2513,8 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tile_atlas_view_missing_source_label->set_v_size_flags(SIZE_EXPAND_FILL); tile_atlas_view_missing_source_label->hide(); right_panel->add_child(tile_atlas_view_missing_source_label); + + EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetAtlasSourceEditor::_undo_redo_inspector_callback)); } TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() { diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index 6448b55feb..ea6f2847ae 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -78,6 +78,7 @@ private: int get_id(); void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id); + TileSetAtlasSource *get_edited() { return tile_set_atlas_source; }; }; // -- Proxy object for a tile, needed by the inspector -- @@ -112,7 +113,7 @@ private: UndoRedo *undo_redo = EditorNode::get_undo_redo(); - bool tile_set_atlas_source_changed_needs_update = false; + bool tile_set_changed_needs_update = false; // -- Properties painting -- VBoxContainer *tile_data_painting_editor_container; @@ -189,7 +190,6 @@ private: TILE_CREATE_ALTERNATIVE, TILE_DELETE, - ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE, ADVANCED_AUTO_CREATE_TILES, ADVANCED_AUTO_REMOVE_TILES, }; @@ -263,7 +263,7 @@ private: void _auto_remove_tiles(); AcceptDialog *confirm_auto_create_tiles; - void _tile_set_atlas_source_changed(); + void _tile_set_changed(); void _tile_proxy_object_changed(String p_what); void _atlas_source_proxy_object_changed(String p_what); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index b1b64564bb..48f64e041c 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -777,7 +777,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { expand->connect("pressed", callable_mp(VisualShaderEditor::get_singleton(), &VisualShaderEditor::_expand_output_port), varray(p_id, i, !vsnode->_is_output_port_expanded(i)), CONNECT_DEFERRED); hb->add_child(expand); } - if (visual_shader->get_shader_type() == VisualShader::TYPE_FRAGMENT && port_right != VisualShaderNode::PORT_TYPE_TRANSFORM && port_right != VisualShaderNode::PORT_TYPE_SAMPLER) { + if (vsnode->has_output_port_preview(i) && port_right != VisualShaderNode::PORT_TYPE_TRANSFORM && port_right != VisualShaderNode::PORT_TYPE_SAMPLER) { TextureButton *preview = memnew(TextureButton); preview->set_toggle_mode(true); preview->set_normal_texture(VisualShaderEditor::get_singleton()->get_theme_icon(SNAME("GuiVisibilityHidden"), SNAME("EditorIcons"))); diff --git a/modules/fbx/data/fbx_skeleton.cpp b/modules/fbx/data/fbx_skeleton.cpp index 3dc163964c..11eed2576f 100644 --- a/modules/fbx/data/fbx_skeleton.cpp +++ b/modules/fbx/data/fbx_skeleton.cpp @@ -104,6 +104,13 @@ void FBXSkeleton::init_skeleton(const ImportState &state) { print_verbose("working on bone: " + itos(bone_index) + " bone name:" + bone->bone_name); skeleton->set_bone_rest(bone->godot_bone_id, get_unscaled_transform(bone->node->pivot_transform->LocalTransform, state.scale)); + { + Transform3D base_xform = bone->node->pivot_transform->LocalTransform; + + skeleton->set_bone_pose_position(bone_index, base_xform.origin); + skeleton->set_bone_pose_rotation(bone_index, base_xform.basis.get_rotation_quaternion()); + skeleton->set_bone_pose_scale(bone_index, base_xform.basis.get_scale()); + } // lookup parent ID if (bone->valid_parent && state.fbx_bone_map.has(bone->parent_bone_id)) { diff --git a/modules/fbx/editor_scene_importer_fbx.cpp b/modules/fbx/editor_scene_importer_fbx.cpp index e4de204cf1..879f281292 100644 --- a/modules/fbx/editor_scene_importer_fbx.cpp +++ b/modules/fbx/editor_scene_importer_fbx.cpp @@ -1011,9 +1011,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene( // track count is 5. // next track id is 5. const uint64_t target_id = track.key; - int track_idx = animation->add_track(Animation::TYPE_TRANSFORM3D); - // animation->track_set_path(track_idx, node_path); Ref<FBXBone> bone; // note we must not run the below code if the entry doesn't exist, it will create dummy entries which is very bad. @@ -1037,22 +1035,21 @@ Node3D *EditorSceneImporterFBX::_generate_scene( // if this is a skeleton mapped track we can just set the path for the track. // todo: implement node paths here at some + NodePath track_path; if (state.fbx_bone_map.size() > 0 && state.fbx_bone_map.has(target_id)) { if (bone->fbx_skeleton.is_valid() && bone.is_valid()) { Ref<FBXSkeleton> fbx_skeleton = bone->fbx_skeleton; String bone_path = state.root->get_path_to(fbx_skeleton->skeleton); bone_path += ":" + fbx_skeleton->skeleton->get_bone_name(bone->godot_bone_id); print_verbose("[doc] track bone path: " + bone_path); - NodePath path = bone_path; - animation->track_set_path(track_idx, path); + track_path = bone_path; } } else if (state.fbx_target_map.has(target_id)) { //print_verbose("[doc] we have a valid target for a node animation"); Ref<FBXNode> target_node = state.fbx_target_map[target_id]; if (target_node.is_valid() && target_node->godot_node != nullptr) { String node_path = state.root->get_path_to(target_node->godot_node); - NodePath path = node_path; - animation->track_set_path(track_idx, path); + track_path = node_path; //print_verbose("[doc] node animation path: " + node_path); } } else { @@ -1186,6 +1183,30 @@ Node3D *EditorSceneImporterFBX::_generate_scene( const Vector3 def_scale = scale_keys.has_default ? scale_keys.default_value : bone_rest.basis.get_scale(); print_verbose("track defaults: p(" + def_pos + ") s(" + def_scale + ") r(" + def_rot + ")"); + int position_idx = -1; + if (pos_values.size()) { + position_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_POSITION_3D); + animation->track_set_path(position_idx, track_path); + animation->track_set_imported(position_idx, true); + } + + int rotation_idx = -1; + if (pos_values.size()) { + rotation_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_ROTATION_3D); + animation->track_set_path(rotation_idx, track_path); + animation->track_set_imported(rotation_idx, true); + } + + int scale_idx = -1; + if (pos_values.size()) { + scale_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_SCALE_3D); + animation->track_set_path(scale_idx, track_path); + animation->track_set_imported(scale_idx, true); + } + while (true) { Vector3 pos = def_pos; Quaternion rot = def_rot; @@ -1206,21 +1227,15 @@ Node3D *EditorSceneImporterFBX::_generate_scene( AssetImportAnimation::INTERP_LINEAR); } - // node animations must also include pivots - if (skeleton_bone >= 0) { - Transform3D xform = Transform3D(); - xform.basis.set_quaternion_scale(rot, scale); - xform.origin = pos; - const Transform3D t = bone_rest.affine_inverse() * xform; - - // populate this again - rot = t.basis.get_rotation_quaternion(); - rot.normalize(); - scale = t.basis.get_scale(); - pos = t.origin; + if (position_idx >= 0) { + animation->position_track_insert_key(position_idx, time, pos); + } + if (rotation_idx >= 0) { + animation->rotation_track_insert_key(rotation_idx, time, rot); + } + if (scale_idx >= 0) { + animation->scale_track_insert_key(scale_idx, time, scale); } - - animation->transform_track_insert_key(track_idx, time, pos, rot, scale); if (last) { break; diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index ba98592600..6fd542ca68 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -4319,6 +4319,9 @@ Error GLTFDocument::_create_skeletons(Ref<GLTFState> state) { skeleton->add_bone(node->get_name()); skeleton->set_bone_rest(bone_index, node->xform); + skeleton->set_bone_pose_position(bone_index, node->position); + skeleton->set_bone_pose_rotation(bone_index, node->rotation.normalized()); + skeleton->set_bone_pose_scale(bone_index, node->scale); if (node->parent >= 0 && state->nodes[node->parent]->skeleton == skel_i) { const int bone_parent = skeleton->find_bone(state->nodes[node->parent]->get_name()); @@ -5470,7 +5473,7 @@ void GLTFDocument::_convert_skeleton_to_gltf(Skeleton3D *p_skeleton3d, Ref<GLTFS // Note that we cannot use _gen_unique_bone_name here, because glTF spec requires all node // names to be unique regardless of whether or not they are used as joints. joint_node->set_name(_gen_unique_name(state, skeleton->get_bone_name(bone_i))); - Transform3D xform = skeleton->get_bone_rest(bone_i) * skeleton->get_bone_pose(bone_i); + Transform3D xform = skeleton->get_bone_pose(bone_i); joint_node->scale = xform.basis.get_scale(); joint_node->rotation = xform.basis.get_rotation_quaternion(); joint_node->position = xform.origin; @@ -5733,7 +5736,7 @@ struct EditorSceneImporterGLTFInterpolate<Quaternion> { template <class T> T GLTFDocument::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) { ERR_FAIL_COND_V(!p_values.size(), T()); - if (p_times.size() != p_values.size()) { + if (p_times.size() != (p_values.size() / (p_interp == GLTFAnimation::INTERP_CUBIC_SPLINE ? 3 : 1))) { ERR_PRINT_ONCE("The interpolated values are not corresponding to its times."); return p_values[0]; } @@ -5868,9 +5871,67 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, const bool transform_affects_skinned_mesh_instance = gltf_node->skeleton < 0 && gltf_node->skin >= 0; if ((track.rotation_track.values.size() || track.position_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) { //make transform track - int track_idx = animation->get_track_count(); - animation->add_track(Animation::TYPE_TRANSFORM3D); - animation->track_set_path(track_idx, transform_node_path); + int base_idx = animation->get_track_count(); + int position_idx = -1; + int rotation_idx = -1; + int scale_idx = -1; + + if (track.position_track.values.size()) { + Vector3 base_pos = state->nodes[track_i.key]->position; + bool not_default = false; //discard the track if all it contains is default values + for (int i = 0; i < track.position_track.times.size(); i++) { + Vector3 value = track.position_track.values[track.position_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i]; + if (!value.is_equal_approx(base_pos)) { + not_default = true; + break; + } + } + if (not_default) { + position_idx = base_idx; + animation->add_track(Animation::TYPE_POSITION_3D); + animation->track_set_path(position_idx, transform_node_path); + animation->track_set_imported(position_idx, true); //helps merging later + + base_idx++; + } + } + if (track.rotation_track.values.size()) { + Quaternion base_rot = state->nodes[track_i.key]->rotation.normalized(); + bool not_default = false; //discard the track if all it contains is default values + for (int i = 0; i < track.rotation_track.times.size(); i++) { + Quaternion value = track.rotation_track.values[track.rotation_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i].normalized(); + if (!value.is_equal_approx(base_rot)) { + not_default = true; + break; + } + } + if (not_default) { + rotation_idx = base_idx; + animation->add_track(Animation::TYPE_ROTATION_3D); + animation->track_set_path(rotation_idx, transform_node_path); + animation->track_set_imported(rotation_idx, true); //helps merging later + base_idx++; + } + } + if (track.scale_track.values.size()) { + Vector3 base_scale = state->nodes[track_i.key]->scale; + bool not_default = false; //discard the track if all it contains is default values + for (int i = 0; i < track.scale_track.times.size(); i++) { + Vector3 value = track.scale_track.values[track.scale_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i]; + if (!value.is_equal_approx(base_scale)) { + not_default = true; + break; + } + } + if (not_default) { + scale_idx = base_idx; + animation->add_track(Animation::TYPE_SCALE_3D); + animation->track_set_path(scale_idx, transform_node_path); + animation->track_set_imported(scale_idx, true); //helps merging later + base_idx++; + } + } + //first determine animation length const double increment = 1.0 / bake_fps; @@ -5880,15 +5941,15 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, Quaternion base_rot; Vector3 base_scale = Vector3(1, 1, 1); - if (!track.rotation_track.values.size()) { + if (rotation_idx == -1) { base_rot = state->nodes[track_i.key]->rotation.normalized(); } - if (!track.position_track.values.size()) { + if (position_idx == -1) { base_pos = state->nodes[track_i.key]->position; } - if (!track.scale_track.values.size()) { + if (scale_idx == -1) { base_scale = state->nodes[track_i.key]->scale; } @@ -5898,35 +5959,21 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, Quaternion rot = base_rot; Vector3 scale = base_scale; - if (track.position_track.times.size()) { + if (position_idx >= 0) { pos = _interpolate_track<Vector3>(track.position_track.times, track.position_track.values, time, track.position_track.interpolation); + animation->position_track_insert_key(position_idx, time, pos); } - if (track.rotation_track.times.size()) { + if (rotation_idx >= 0) { rot = _interpolate_track<Quaternion>(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation); + animation->rotation_track_insert_key(rotation_idx, time, rot); } - if (track.scale_track.times.size()) { + if (scale_idx >= 0) { scale = _interpolate_track<Vector3>(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation); + animation->scale_track_insert_key(scale_idx, time, scale); } - if (gltf_node->skeleton >= 0) { - Transform3D xform; - xform.basis.set_quaternion_scale(rot, scale); - xform.origin = pos; - - const Skeleton3D *skeleton = state->skeletons[gltf_node->skeleton]->godot_skeleton; - const int bone_idx = skeleton->find_bone(gltf_node->get_name()); - xform = skeleton->get_bone_rest(bone_idx).affine_inverse() * xform; - - rot = xform.basis.get_rotation_quaternion(); - rot.normalize(); - scale = xform.basis.get_scale(); - pos = xform.origin; - } - - animation->transform_track_insert_key(track_idx, time, pos, rot, scale); - if (last) { break; } @@ -6042,7 +6089,7 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) { } else { if (skin.is_null()) { // Note that gltf_skin_key should remain null, so these can share a reference. - skin = skeleton->register_skin(nullptr)->get_skin(); + skin = skeleton->create_skin_from_rest_transforms(); } gltf_skin.instantiate(); gltf_skin->godot_skin = skin; @@ -6146,6 +6193,10 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_roo } GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state, GLTFAnimation::Track p_track, Ref<Animation> p_animation, Transform3D p_bone_rest, int32_t p_track_i, GLTFNodeIndex p_node_i) { +#ifndef _MSC_VER +#warning this needs to be redone +#endif +#if 0 Animation::InterpolationType interpolation = p_animation->track_get_interpolation_type(p_track_i); GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR; @@ -6164,6 +6215,8 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state for (int32_t key_i = 0; key_i < key_count; key_i++) { times.write[key_i] = p_animation->track_get_key_time(p_track_i, key_i); } + + if (track_type == Animation::TYPE_TRANSFORM3D) { p_track.position_track.times = times; p_track.position_track.interpolation = gltf_interpolation; @@ -6323,7 +6376,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state } } } - +#endif return p_track; } diff --git a/modules/lightmapper_rd/lm_common_inc.glsl b/modules/lightmapper_rd/lm_common_inc.glsl index 22172d50e4..58523dc1f8 100644 --- a/modules/lightmapper_rd/lm_common_inc.glsl +++ b/modules/lightmapper_rd/lm_common_inc.glsl @@ -81,3 +81,7 @@ layout(set = 0, binding = 8) uniform texture2DArray albedo_tex; layout(set = 0, binding = 9) uniform texture2DArray emission_tex; layout(set = 0, binding = 10) uniform sampler linear_sampler; + +// Fragment action constants +const uint FA_NONE = 0; +const uint FA_SMOOTHEN_POSITION = 1; diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl index 25b334c5eb..6e5c9f25ba 100644 --- a/modules/lightmapper_rd/lm_compute.glsl +++ b/modules/lightmapper_rd/lm_compute.glsl @@ -148,7 +148,7 @@ uint trace_ray(vec3 p_from, vec3 p_to ivec3 icell = ivec3(from_cell); ivec3 iendcell = ivec3(to_cell); vec3 dir_cell = normalize(rel_cell); - vec3 delta = abs(1.0 / dir_cell); //vec3(length(rel_cell)) / rel_cell); + vec3 delta = min(abs(1.0 / dir_cell), params.grid_size); // use params.grid_size as max to prevent infinity values ivec3 step = ivec3(sign(rel_cell)); vec3 side = (sign(rel_cell) * (vec3(icell) - from_cell) + (sign(rel_cell) * 0.5) + 0.5) * delta; @@ -420,20 +420,22 @@ void main() { light = textureLod(sampler2DArray(source_light, linear_sampler), uvw, 0.0).rgb; active_rays += 1.0; - } else if (trace_result == RAY_MISS && params.env_transform[0][3] == 0.0) { // Use env_transform[0][3] to indicate when we are computing the first bounce - // Did not hit a triangle, reach out for the sky - vec3 sky_dir = normalize(mat3(params.env_transform) * ray_dir); + } else if (trace_result == RAY_MISS) { + if (params.env_transform[0][3] == 0.0) { // Use env_transform[0][3] to indicate when we are computing the first bounce + // Did not hit a triangle, reach out for the sky + vec3 sky_dir = normalize(mat3(params.env_transform) * ray_dir); - vec2 st = vec2( - atan(sky_dir.x, sky_dir.z), - acos(sky_dir.y)); + vec2 st = vec2( + atan(sky_dir.x, sky_dir.z), + acos(sky_dir.y)); - if (st.x < 0.0) - st.x += PI * 2.0; + if (st.x < 0.0) + st.x += PI * 2.0; - st /= vec2(PI * 2.0, PI); + st /= vec2(PI * 2.0, PI); - light = textureLod(sampler2D(environment, linear_sampler), st, 0.0).rgb; + light = textureLod(sampler2D(environment, linear_sampler), st, 0.0).rgb; + } active_rays += 1.0; } diff --git a/modules/lightmapper_rd/lm_raster.glsl b/modules/lightmapper_rd/lm_raster.glsl index 55ca193cc1..a86968a4f3 100644 --- a/modules/lightmapper_rd/lm_raster.glsl +++ b/modules/lightmapper_rd/lm_raster.glsl @@ -12,6 +12,7 @@ layout(location = 2) out vec2 uv_interp; layout(location = 3) out vec3 barycentric; layout(location = 4) flat out uvec3 vertex_indices; layout(location = 5) flat out vec3 face_normal; +layout(location = 6) flat out uint fragment_action; layout(push_constant, binding = 0, std430) uniform Params { vec2 atlas_size; @@ -49,6 +50,14 @@ void main() { face_normal = -normalize(cross((vertices.data[vertex_indices.x].position - vertices.data[vertex_indices.y].position), (vertices.data[vertex_indices.x].position - vertices.data[vertex_indices.z].position))); + { + const float FLAT_THRESHOLD = 0.99; + const vec3 norm_a = vec3(vertices.data[vertex_indices.x].normal_xy, vertices.data[vertex_indices.x].normal_z); + const vec3 norm_b = vec3(vertices.data[vertex_indices.y].normal_xy, vertices.data[vertex_indices.y].normal_z); + const vec3 norm_c = vec3(vertices.data[vertex_indices.z].normal_xy, vertices.data[vertex_indices.z].normal_z); + fragment_action = (dot(norm_a, norm_b) < FLAT_THRESHOLD || dot(norm_a, norm_c) < FLAT_THRESHOLD || dot(norm_b, norm_c) < FLAT_THRESHOLD) ? FA_SMOOTHEN_POSITION : FA_NONE; + } + gl_Position = vec4((uv_interp + params.uv_offset) * 2.0 - 1.0, 0.0001, 1.0); } @@ -78,6 +87,7 @@ layout(location = 2) in vec2 uv_interp; layout(location = 3) in vec3 barycentric; layout(location = 4) in flat uvec3 vertex_indices; layout(location = 5) in flat vec3 face_normal; +layout(location = 6) in flat uint fragment_action; layout(location = 0) out vec4 position; layout(location = 1) out vec4 normal; @@ -86,7 +96,7 @@ layout(location = 2) out vec4 unocclude; void main() { vec3 vertex_pos = vertex_interp; - { + if (fragment_action == FA_SMOOTHEN_POSITION) { // smooth out vertex position by interpolating its projection in the 3 normal planes (normal plane is created by vertex pos and normal) // because we don't want to interpolate inwards, normals found pointing inwards are pushed out. vec3 pos_a = vertices.data[vertex_indices.x].position; diff --git a/platform/linuxbsd/freedesktop_screensaver.cpp b/platform/linuxbsd/freedesktop_screensaver.cpp index a6a3b27d76..3973d43d49 100644 --- a/platform/linuxbsd/freedesktop_screensaver.cpp +++ b/platform/linuxbsd/freedesktop_screensaver.cpp @@ -50,6 +50,7 @@ void FreeDesktopScreenSaver::inhibit() { DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error); if (dbus_error_is_set(&error)) { + dbus_error_free(&error); unsupported = true; return; } @@ -72,6 +73,7 @@ void FreeDesktopScreenSaver::inhibit() { DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error); dbus_message_unref(message); if (dbus_error_is_set(&error)) { + dbus_error_free(&error); dbus_connection_unref(bus); unsupported = false; return; @@ -96,6 +98,7 @@ void FreeDesktopScreenSaver::uninhibit() { DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error); if (dbus_error_is_set(&error)) { + dbus_error_free(&error); unsupported = true; return; } @@ -110,6 +113,7 @@ void FreeDesktopScreenSaver::uninhibit() { DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error); if (dbus_error_is_set(&error)) { + dbus_error_free(&error); dbus_connection_unref(bus); unsupported = true; return; diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 5bce705dd5..3637594e1b 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -163,8 +163,8 @@ void GPUParticles2D::set_trail_sections(int p_sections) { } void GPUParticles2D::set_trail_section_subdivisions(int p_subdivisions) { - ERR_FAIL_COND(trail_section_subdivisions < 1); - ERR_FAIL_COND(trail_section_subdivisions > 1024); + ERR_FAIL_COND(p_subdivisions < 1); + ERR_FAIL_COND(p_subdivisions > 1024); trail_section_subdivisions = p_subdivisions; update(); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index a791d99c39..c2f150ce00 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -811,8 +811,9 @@ void TileMap::_rendering_cleanup_layer(int p_layer) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); RenderingServer *rs = RenderingServer::get_singleton(); - if (!layers[p_layer].canvas_item.is_valid()) { + if (layers[p_layer].canvas_item.is_valid()) { rs->free(layers[p_layer].canvas_item); + layers[p_layer].canvas_item = RID(); } } @@ -848,9 +849,11 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List Color modulate = get_self_modulate(); modulate *= get_layer_modulate(q.layer); if (selected_layer >= 0) { - if (q.layer < selected_layer) { + int z1 = get_layer_z_index(q.layer); + int z2 = get_layer_z_index(selected_layer); + if (z1 < z2 || (z1 == z2 && q.layer < selected_layer)) { modulate = modulate.darkened(0.5); - } else if (q.layer > selected_layer) { + } else if (z1 > z2 || (z1 == z2 && q.layer > selected_layer)) { modulate = modulate.darkened(0.5); modulate.a *= 0.3; } @@ -3092,6 +3095,8 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_tile_data", "layer"), &TileMap::_set_tile_data); ClassDB::bind_method(D_METHOD("_get_tile_data", "layer"), &TileMap::_get_tile_data); + ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update"), &TileMap::_tile_set_changed_deferred_update); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_animatable"), "set_collision_animatable", "is_collision_animatable"); @@ -3111,8 +3116,16 @@ void TileMap::_bind_methods() { void TileMap::_tile_set_changed() { emit_signal(SNAME("changed")); - _clear_internals(); - _recreate_internals(); + _tile_set_changed_deferred_update_needed = true; + call_deferred("_tile_set_changed_deferred_update"); +} + +void TileMap::_tile_set_changed_deferred_update() { + if (_tile_set_changed_deferred_update_needed) { + _clear_internals(); + _recreate_internals(); + _tile_set_changed_deferred_update_needed = false; + } } TileMap::TileMap() { diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index b1fbdfdf8a..e5809deabb 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -285,6 +285,8 @@ private: Vector<int> _get_tile_data(int p_layer) const; void _tile_set_changed(); + bool _tile_set_changed_deferred_update_needed = false; + void _tile_set_changed_deferred_update(); protected: bool _set(const StringName &p_name, const Variant &p_value); diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index afd11482e3..8e89f4fc54 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -215,8 +215,6 @@ void BoneAttachment3D::_transform_changed() { sk->set_bone_global_pose_override(bone_idx, our_trans, 1.0, true); } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { sk->set_bone_local_pose_override(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans), 1.0, true); - } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { - sk->set_bone_custom_pose(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans)); } } } @@ -273,8 +271,6 @@ void BoneAttachment3D::set_override_pose(bool p_override) { sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false); } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false); - } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { - sk->set_bone_custom_pose(bone_idx, Transform3D()); } } _transform_changed(); @@ -294,8 +290,6 @@ void BoneAttachment3D::set_override_mode(int p_mode) { sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false); } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false); - } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { - sk->set_bone_custom_pose(bone_idx, Transform3D()); } } override_mode = p_mode; diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h index cf681cace8..57b9854e0e 100644 --- a/scene/3d/bone_attachment_3d.h +++ b/scene/3d/bone_attachment_3d.h @@ -47,7 +47,6 @@ class BoneAttachment3D : public Node3D { enum OVERRIDE_MODES { MODE_GLOBAL_POSE, MODE_LOCAL_POSE, - MODE_CUSTOM_POSE }; bool use_external_skeleton = false; diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index 67f4a88228..cfd90e5da0 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -146,11 +146,13 @@ void MeshInstance3D::_resolve_skeleton_path() { if (!skeleton_path.is_empty()) { Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(get_node(skeleton_path)); if (skeleton) { - new_skin_reference = skeleton->register_skin(skin_internal); if (skin_internal.is_null()) { + new_skin_reference = skeleton->register_skin(skeleton->create_skin_from_rest_transforms()); //a skin was created for us skin_internal = new_skin_reference->get_skin(); notify_property_list_changed(); + } else { + new_skin_reference = skeleton->register_skin(skin_internal); } } } diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 2b52f034b2..9b28d7aff8 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -99,8 +99,12 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { set_bone_rest(which, p_value); } else if (what == "enabled") { set_bone_enabled(which, p_value); - } else if (what == "pose") { - set_bone_pose(which, p_value); + } else if (what == "position") { + set_bone_pose_position(which, p_value); + } else if (what == "rotation") { + set_bone_pose_rotation(which, p_value); + } else if (what == "scale") { + set_bone_pose_scale(which, p_value); } else { return false; } @@ -135,8 +139,12 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { r_ret = get_bone_rest(which); } else if (what == "enabled") { r_ret = is_bone_enabled(which); - } else if (what == "pose") { - r_ret = get_bone_pose(which); + } else if (what == "position") { + r_ret = get_bone_pose_position(which); + } else if (what == "rotation") { + r_ret = get_bone_pose_rotation(which); + } else if (what == "scale") { + r_ret = get_bone_pose_scale(which); } else { return false; } @@ -151,7 +159,9 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } #ifndef _3D_DISABLED @@ -547,18 +557,6 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) { _make_dirty(); } -void Skeleton3D::set_bone_disable_rest(int p_bone, bool p_disable) { - const int bone_size = bones.size(); - ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].disable_rest = p_disable; -} - -bool Skeleton3D::is_bone_rest_disabled(int p_bone) const { - const int bone_size = bones.size(); - ERR_FAIL_INDEX_V(p_bone, bone_size, false); - return bones[p_bone].disable_rest; -} - int Skeleton3D::get_bone_parent(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, -1); @@ -657,36 +655,60 @@ void Skeleton3D::clear_bones() { // Posing api -void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) { +void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].pose = p_pose; + bones.write[p_bone].pose_position = p_position; + bones.write[p_bone].pose_cache_dirty = true; if (is_inside_tree()) { _make_dirty(); } } -Transform3D Skeleton3D::get_bone_pose(int p_bone) const { +void Skeleton3D::set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation) { const int bone_size = bones.size(); - ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); - return bones[p_bone].pose; -} + ERR_FAIL_INDEX(p_bone, bone_size); -void Skeleton3D::set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose) { + bones.write[p_bone].pose_rotation = p_rotation; + bones.write[p_bone].pose_cache_dirty = true; + if (is_inside_tree()) { + _make_dirty(); + } +} +void Skeleton3D::set_bone_pose_scale(int p_bone, const Vector3 &p_scale) { const int bone_size = bones.size(); ERR_FAIL_INDEX(p_bone, bone_size); - //ERR_FAIL_COND( !is_inside_scene() ); - bones.write[p_bone].custom_pose_enable = (p_custom_pose != Transform3D()); - bones.write[p_bone].custom_pose = p_custom_pose; + bones.write[p_bone].pose_scale = p_scale; + bones.write[p_bone].pose_cache_dirty = true; + if (is_inside_tree()) { + _make_dirty(); + } +} + +Vector3 Skeleton3D::get_bone_pose_position(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3()); + return bones[p_bone].pose_position; +} - _make_dirty(); +Quaternion Skeleton3D::get_bone_pose_rotation(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Quaternion()); + return bones[p_bone].pose_rotation; } -Transform3D Skeleton3D::get_bone_custom_pose(int p_bone) const { +Vector3 Skeleton3D::get_bone_pose_scale(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3()); + return bones[p_bone].pose_scale; +} + +Transform3D Skeleton3D::get_bone_pose(int p_bone) const { const int bone_size = bones.size(); ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); - return bones[p_bone].custom_pose; + ((Skeleton3D *)this)->bones.write[p_bone].update_pose_cache(); + return bones[p_bone].pose_cache; } void Skeleton3D::_make_dirty() { @@ -887,59 +909,57 @@ void Skeleton3D::_skin_changed() { _make_dirty(); } -Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { - for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { - if (E->get()->skin == p_skin) { - return Ref<SkinReference>(E->get()); +Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() { + Ref<Skin> skin; + + skin.instantiate(); + skin->set_bind_count(bones.size()); + _update_process_order(); // Just in case. + + // Pose changed, rebuild cache of inverses. + const Bone *bonesptr = bones.ptr(); + int len = bones.size(); + + // Calculate global rests and invert them. + LocalVector<int> bones_to_process; + bones_to_process = get_parentless_bones(); + while (bones_to_process.size() > 0) { + int current_bone_idx = bones_to_process[0]; + const Bone &b = bonesptr[current_bone_idx]; + bones_to_process.erase(current_bone_idx); + LocalVector<int> child_bones_vector; + child_bones_vector = get_bone_children(current_bone_idx); + int child_bones_size = child_bones_vector.size(); + if (b.parent < 0) { + skin->set_bind_pose(current_bone_idx, b.rest); + } + for (int i = 0; i < child_bones_size; i++) { + int child_bone_idx = child_bones_vector[i]; + const Bone &cb = bonesptr[child_bone_idx]; + skin->set_bind_pose(child_bone_idx, skin->get_bind_pose(current_bone_idx) * cb.rest); + // Add the bone's children to the list of bones to be processed. + bones_to_process.push_back(child_bones_vector[i]); } } - 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. - - skin.instantiate(); - skin->set_bind_count(bones.size()); - _update_process_order(); // Just in case. - - // Pose changed, rebuild cache of inverses. - const Bone *bonesptr = bones.ptr(); - int len = bones.size(); - - // Calculate global rests and invert them. - LocalVector<int> bones_to_process; - bones_to_process = get_parentless_bones(); - while (bones_to_process.size() > 0) { - int current_bone_idx = bones_to_process[0]; - const Bone &b = bonesptr[current_bone_idx]; - bones_to_process.erase(current_bone_idx); - LocalVector<int> child_bones_vector; - child_bones_vector = get_bone_children(current_bone_idx); - int child_bones_size = child_bones_vector.size(); - if (b.parent < 0) { - skin->set_bind_pose(current_bone_idx, b.rest); - } - for (int i = 0; i < child_bones_size; i++) { - int child_bone_idx = child_bones_vector[i]; - const Bone &cb = bonesptr[child_bone_idx]; - skin->set_bind_pose(child_bone_idx, skin->get_bind_pose(current_bone_idx) * cb.rest); - // Add the bone's children to the list of bones to be processed. - bones_to_process.push_back(child_bones_vector[i]); - } - } + for (int i = 0; i < len; i++) { + // 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()); + } + + return skin; +} + +Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { + ERR_FAIL_COND_V(p_skin.is_null(), Ref<SkinReference>()); - for (int i = 0; i < len; i++) { - // 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()); + for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { + if (E->get()->skin == p_skin) { + return Ref<SkinReference>(E->get()); } } - ERR_FAIL_COND_V(skin.is_null(), Ref<SkinReference>()); - Ref<SkinReference> skin_ref; skin_ref.instantiate(); @@ -947,11 +967,11 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { skin_ref->bind_count = 0; skin_ref->skeleton = RenderingServer::get_singleton()->skeleton_create(); skin_ref->skeleton_node = this; - skin_ref->skin = skin; + skin_ref->skin = p_skin; skin_bindings.insert(skin_ref.operator->()); - skin->connect("changed", Callable(skin_ref.operator->(), "_skin_changed")); + skin_ref->skin->connect("changed", Callable(skin_ref.operator->(), "_skin_changed")); _make_dirty(); // Skin needs to be updated, so update skeleton. @@ -987,59 +1007,33 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { Bone &b = bonesptr[current_bone_idx]; bool bone_enabled = b.enabled && !show_rest_only; - if (b.disable_rest) { - if (bone_enabled) { - Transform3D pose = b.pose; - if (b.custom_pose_enable) { - pose = b.custom_pose * pose; - } - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * pose; - b.pose_global_no_override = b.pose_global; - } else { - b.pose_global = pose; - b.pose_global_no_override = b.pose_global; - } + if (bone_enabled) { + b.update_pose_cache(); + Transform3D pose = b.pose_cache; + + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * pose; + b.pose_global_no_override = b.pose_global; } else { - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global; - b.pose_global_no_override = b.pose_global; - } else { - b.pose_global = Transform3D(); - b.pose_global_no_override = b.pose_global; - } + b.pose_global = pose; + b.pose_global_no_override = b.pose_global; } - } else { - if (bone_enabled) { - Transform3D pose = b.pose; - if (b.custom_pose_enable) { - pose = b.custom_pose * pose; - } - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose); - b.pose_global_no_override = b.pose_global; - } else { - b.pose_global = b.rest * pose; - b.pose_global_no_override = b.pose_global; - } + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * b.rest; + b.pose_global_no_override = b.pose_global; } else { - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * b.rest; - b.pose_global_no_override = b.pose_global; - } else { - b.pose_global = b.rest; - b.pose_global_no_override = b.pose_global; - } + b.pose_global = b.rest; + b.pose_global_no_override = b.pose_global; } } if (b.local_pose_override_amount >= CMP_EPSILON) { Transform3D override_local_pose; if (b.parent >= 0) { - override_local_pose = bonesptr[b.parent].pose_global * (b.rest * b.local_pose_override); + override_local_pose = bonesptr[b.parent].pose_global * b.local_pose_override; } else { - override_local_pose = (b.rest * b.local_pose_override); + override_local_pose = b.local_pose_override; } b.pose_global = b.pose_global.interpolate_with(override_local_pose, b.local_pose_override_amount); } @@ -1080,8 +1074,8 @@ Transform3D Skeleton3D::global_pose_to_local_pose(int p_bone_idx, Transform3D p_ ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D()); if (bones[p_bone_idx].parent >= 0) { int parent_bone_idx = bones[p_bone_idx].parent; - Transform3D conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest); - return conversion_transform.affine_inverse() * p_global_pose; + Transform3D conversion_transform = bones[parent_bone_idx].pose_global.affine_inverse(); + return conversion_transform * p_global_pose; } else { return p_global_pose; } @@ -1092,8 +1086,7 @@ Transform3D Skeleton3D::local_pose_to_global_pose(int p_bone_idx, Transform3D p_ ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D()); if (bones[p_bone_idx].parent >= 0) { int parent_bone_idx = bones[p_bone_idx].parent; - Transform3D conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest); - return conversion_transform * p_local_pose; + return bones[parent_bone_idx].pose_global * p_local_pose; } else { return p_local_pose; } @@ -1183,17 +1176,21 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton3D::get_bone_rest); ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton3D::set_bone_rest); + ClassDB::bind_method(D_METHOD("create_skin_from_rest_transforms"), &Skeleton3D::create_skin_from_rest_transforms); ClassDB::bind_method(D_METHOD("register_skin", "skin"), &Skeleton3D::register_skin); ClassDB::bind_method(D_METHOD("localize_rests"), &Skeleton3D::localize_rests); - ClassDB::bind_method(D_METHOD("set_bone_disable_rest", "bone_idx", "disable"), &Skeleton3D::set_bone_disable_rest); - ClassDB::bind_method(D_METHOD("is_bone_rest_disabled", "bone_idx"), &Skeleton3D::is_bone_rest_disabled); - ClassDB::bind_method(D_METHOD("clear_bones"), &Skeleton3D::clear_bones); 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("set_bone_pose_position", "bone_idx", "position"), &Skeleton3D::set_bone_pose_position); + ClassDB::bind_method(D_METHOD("set_bone_pose_rotation", "bone_idx", "rotation"), &Skeleton3D::set_bone_pose_rotation); + ClassDB::bind_method(D_METHOD("set_bone_pose_scale", "bone_idx", "scale"), &Skeleton3D::set_bone_pose_scale); + + ClassDB::bind_method(D_METHOD("get_bone_pose_position", "bone_idx"), &Skeleton3D::get_bone_pose_position); + ClassDB::bind_method(D_METHOD("get_bone_pose_rotation", "bone_idx"), &Skeleton3D::get_bone_pose_rotation); + ClassDB::bind_method(D_METHOD("get_bone_pose_scale", "bone_idx"), &Skeleton3D::get_bone_pose_scale); 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)); @@ -1208,9 +1205,6 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bone_local_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_local_pose_override, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_bone_local_pose_override", "bone_idx"), &Skeleton3D::get_bone_local_pose_override); - ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton3D::get_bone_custom_pose); - ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton3D::set_bone_custom_pose); - ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms); ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms); diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 9aa4fc823f..f3cf551af7 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -76,16 +76,24 @@ private: bool enabled; int parent; - bool disable_rest = false; Transform3D rest; - Transform3D pose; + _FORCE_INLINE_ void update_pose_cache() { + if (pose_cache_dirty) { + pose_cache.basis.set_quaternion_scale(pose_rotation, pose_scale); + pose_cache.origin = pose_position; + pose_cache_dirty = false; + } + } + bool pose_cache_dirty = true; + Transform3D pose_cache; + Vector3 pose_position; + Quaternion pose_rotation; + Vector3 pose_scale = Vector3(1, 1, 1); + Transform3D pose_global; Transform3D pose_global_no_override; - bool custom_pose_enable = false; - Transform3D custom_pose; - real_t global_pose_override_amount = 0.0; bool global_pose_override_reset = false; Transform3D global_pose_override; @@ -107,8 +115,6 @@ private: Bone() { parent = -1; enabled = true; - disable_rest = false; - custom_pose_enable = false; global_pose_override_amount = 0; global_pose_override_reset = false; #ifndef _3D_DISABLED @@ -187,9 +193,6 @@ public: void remove_bone_child(int p_bone, int p_child); Vector<int> get_parentless_bones(); - void set_bone_disable_rest(int p_bone, bool p_disable); - bool is_bone_rest_disabled(int p_bone) const; - int get_bone_count() const; void set_bone_rest(int p_bone, const Transform3D &p_rest); @@ -206,11 +209,15 @@ public: // posing api - void set_bone_pose(int p_bone, const Transform3D &p_pose); + void set_bone_pose_position(int p_bone, const Vector3 &p_position); + void set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation); + void set_bone_pose_scale(int p_bone, const Vector3 &p_scale); + Transform3D get_bone_pose(int p_bone) const; - void set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose); - Transform3D get_bone_custom_pose(int p_bone) const; + Vector3 get_bone_pose_position(int p_bone) const; + Quaternion get_bone_pose_rotation(int p_bone) const; + Vector3 get_bone_pose_scale(int p_bone) const; void clear_bones_global_pose_override(); Transform3D get_bone_global_pose_override(int p_bone) const; @@ -222,6 +229,8 @@ public: void localize_rests(); // used for loaders and tools + Ref<Skin> create_skin_from_rest_transforms(); + Ref<SkinReference> register_skin(const Ref<Skin> &p_skin); void force_update_all_dirty_bones(); diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index 9a2aaa8be2..6761fdd944 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -275,7 +275,7 @@ void VehicleWheel3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_steering"), &VehicleWheel3D::get_steering); ADD_GROUP("Per-Wheel Motion", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "-1024,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering"); ADD_GROUP("VehicleBody3D Motion", ""); @@ -914,7 +914,7 @@ void VehicleBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_steering"), &VehicleBody3D::get_steering); ADD_GROUP("Motion", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "-1024,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering"); } diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp deleted file mode 100644 index 56743007e4..0000000000 --- a/scene/animation/animation_cache.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/*************************************************************************/ -/* animation_cache.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "animation_cache.h" - -void AnimationCache::_node_exit_tree(Node *p_node) { - //it is one shot, so it disconnects upon arrival - - ERR_FAIL_COND(!connected_nodes.has(p_node)); - - connected_nodes.erase(p_node); - - for (int i = 0; i < path_cache.size(); i++) { - if (path_cache[i].node != p_node) { - continue; - } - - path_cache.write[i].valid = false; //invalidate path cache - } -} - -void AnimationCache::_animation_changed() { - _clear_cache(); -} - -void AnimationCache::_clear_cache() { - while (connected_nodes.size()) { - connected_nodes.front()->get()->disconnect("tree_exiting", callable_mp(this, &AnimationCache::_node_exit_tree)); - connected_nodes.erase(connected_nodes.front()); - } - path_cache.clear(); - cache_valid = false; - cache_dirty = true; -} - -void AnimationCache::_update_cache() { - cache_valid = false; - - ERR_FAIL_COND(!root); - ERR_FAIL_COND(!root->is_inside_tree()); - ERR_FAIL_COND(animation.is_null()); - - for (int i = 0; i < animation->get_track_count(); i++) { - NodePath np = animation->track_get_path(i); - - Node *node = root->get_node(np); - if (!node) { - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(!node, "Invalid track path in animation '" + np + "'."); - } - - Path path; - - Ref<Resource> res; - - if (animation->track_get_type(i) == Animation::TYPE_TRANSFORM3D) { -#ifndef _3D_DISABLED - if (np.get_subname_count() > 1) { - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(animation->track_get_type(i) == Animation::TYPE_TRANSFORM3D, "Transform tracks can't have a subpath '" + np + "'."); - } - - Node3D *sp = Object::cast_to<Node3D>(node); - - if (!sp) { - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(!sp, "Transform track not of type Node3D '" + np + "'."); - } - - if (np.get_subname_count() == 1) { - StringName property = np.get_subname(0); - String ps = property; - - Skeleton3D *sk = Object::cast_to<Skeleton3D>(node); - if (!sk) { - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(!sk, "Property defined in Transform track, but not a Skeleton! '" + np + "'."); - } - - int idx = sk->find_bone(ps); - if (idx == -1) { - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(idx == -1, "Property defined in Transform track, but not a Skeleton Bone! '" + np + "'."); - } - - path.bone_idx = idx; - path.skeleton = sk; - } - - path.node_3d = sp; -#endif // _3D_DISABLED - } else { - if (np.get_subname_count() > 0) { - RES res2; - Vector<StringName> leftover_subpath; - - // We don't want to cache the last resource unless it is a method call - bool is_method = animation->track_get_type(i) == Animation::TYPE_METHOD; - root->get_node_and_resource(np, res2, leftover_subpath, is_method); - - if (res2.is_valid()) { - path.resource = res2; - } else { - path.node = node; - } - path.object = res2.is_valid() ? res2.ptr() : (Object *)node; - path.subpath = leftover_subpath; - - } else { - path.node = node; - path.object = node; - path.subpath = np.get_subnames(); - } - } - - if (animation->track_get_type(i) == Animation::TYPE_VALUE) { - if (np.get_subname_count() == 0) { - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(np.get_subname_count() == 0, "Value Track lacks property: " + np + "."); - } - - } else if (animation->track_get_type(i) == Animation::TYPE_METHOD) { - if (path.subpath.size() != 0) { // Trying to call a method of a non-resource - - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(path.subpath.size() != 0, "Method Track has property: " + np + "."); - } - } - - path.valid = true; - - path_cache.push_back(path); - - if (!connected_nodes.has(path.node)) { - connected_nodes.insert(path.node); - path.node->connect("tree_exiting", callable_mp(this, &AnimationCache::_node_exit_tree), Node::make_binds(path.node), CONNECT_ONESHOT); - } - } - - cache_dirty = false; - cache_valid = true; -} - -void AnimationCache::set_track_transform(int p_idx, const Transform3D &p_transform) { - if (cache_dirty) { - _update_cache(); - } - - ERR_FAIL_COND(!cache_valid); - ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache.write[p_idx]; - if (!p.valid) { - return; - } - -#ifndef _3D_DISABLED - ERR_FAIL_COND(!p.node); - ERR_FAIL_COND(!p.node_3d); - - if (p.skeleton) { - p.skeleton->set_bone_pose(p.bone_idx, p_transform); - } else { - p.node_3d->set_transform(p_transform); - } -#endif // _3D_DISABLED -} - -void AnimationCache::set_track_value(int p_idx, const Variant &p_value) { - if (cache_dirty) { - _update_cache(); - } - - ERR_FAIL_COND(!cache_valid); - ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache.write[p_idx]; - if (!p.valid) { - return; - } - - ERR_FAIL_COND(!p.object); - p.object->set_indexed(p.subpath, p_value); -} - -void AnimationCache::call_track(int p_idx, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (cache_dirty) { - _update_cache(); - } - - ERR_FAIL_COND(!cache_valid); - ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache.write[p_idx]; - if (!p.valid) { - return; - } - - ERR_FAIL_COND(!p.object); - p.object->call(p_method, p_args, p_argcount, r_error); -} - -void AnimationCache::set_all(float p_time, float p_delta) { - if (cache_dirty) { - _update_cache(); - } - - ERR_FAIL_COND(!cache_valid); - - int tc = animation->get_track_count(); - for (int i = 0; i < tc; i++) { - switch (animation->track_get_type(i)) { - case Animation::TYPE_TRANSFORM3D: { - Vector3 loc, scale; - Quaternion rot; - animation->transform_track_interpolate(i, p_time, &loc, &rot, &scale); - Transform3D tr(Basis(rot), loc); - tr.basis.scale(scale); - - set_track_transform(i, tr); - - } break; - case Animation::TYPE_VALUE: { - if (animation->value_track_get_update_mode(i) == Animation::UPDATE_CONTINUOUS || (animation->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE && p_delta == 0)) { - Variant v = animation->value_track_interpolate(i, p_time); - set_track_value(i, v); - } else { - List<int> indices; - animation->value_track_get_key_indices(i, p_time, p_delta, &indices); - - for (int &E : indices) { - Variant v = animation->track_get_key_value(i, E); - set_track_value(i, v); - } - } - - } break; - case Animation::TYPE_METHOD: { - List<int> indices; - animation->method_track_get_key_indices(i, p_time, p_delta, &indices); - - for (int &E : indices) { - Vector<Variant> args = animation->method_track_get_params(i, E); - StringName name = animation->method_track_get_name(i, E); - Callable::CallError err; - - if (!args.size()) { - call_track(i, name, nullptr, 0, err); - } else { - Vector<const Variant *> argptrs; - argptrs.resize(args.size()); - for (int j = 0; j < args.size(); j++) { - argptrs.write[j] = &args.write[j]; - } - - call_track(i, name, (const Variant **)&argptrs[0], args.size(), err); - } - } - - } break; - default: { - } - } - } -} - -void AnimationCache::set_animation(const Ref<Animation> &p_animation) { - _clear_cache(); - - if (animation.is_valid()) { - animation->disconnect("changed", callable_mp(this, &AnimationCache::_animation_changed)); - } - - animation = p_animation; - - if (animation.is_valid()) { - animation->connect("changed", callable_mp(this, &AnimationCache::_animation_changed)); - } -} - -void AnimationCache::_bind_methods() { -} - -void AnimationCache::set_root(Node *p_root) { - _clear_cache(); - root = p_root; -} - -AnimationCache::AnimationCache() { -} diff --git a/scene/animation/animation_cache.h b/scene/animation/animation_cache.h deleted file mode 100644 index c856e644f7..0000000000 --- a/scene/animation/animation_cache.h +++ /dev/null @@ -1,84 +0,0 @@ -/*************************************************************************/ -/* animation_cache.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef ANIMATION_CACHE_H -#define ANIMATION_CACHE_H - -#include "scene/3d/skeleton_3d.h" -#include "scene/resources/animation.h" - -class AnimationCache : public Object { - GDCLASS(AnimationCache, Object); - - struct Path { - RES resource; - Object *object = nullptr; -#ifndef _3D_DISABLED - Skeleton3D *skeleton = nullptr; - Node3D *node_3d = nullptr; -#endif // _3D_DISABLED - Node *node = nullptr; - - int bone_idx = -1; - Vector<StringName> subpath; - bool valid = false; - }; - - Set<Node *> connected_nodes; - Vector<Path> path_cache; - - Node *root = nullptr; - Ref<Animation> animation; - bool cache_dirty = true; - bool cache_valid = false; - - void _node_exit_tree(Node *p_node); - - void _clear_cache(); - void _update_cache(); - void _animation_changed(); - -protected: - static void _bind_methods(); - -public: - void set_track_transform(int p_idx, const Transform3D &p_transform); - void set_track_value(int p_idx, const Variant &p_value); - void call_track(int p_idx, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); - - void set_all(float p_time, float p_delta = 0); - - void set_animation(const Ref<Animation> &p_animation); - void set_root(Node *p_root); - - AnimationCache(); -}; - -#endif // ANIMATION_CACHE_H diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 2c8c4ee788..8407e0dd99 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -60,7 +60,12 @@ void AnimatedValuesBackup::restore() const { if (entry->bone_idx == -1) { entry->object->set_indexed(entry->subpath, entry->value); } else { - Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose(entry->bone_idx, entry->value); + Array arr = entry->value; + if (arr.size() == 3) { + Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_position(entry->bone_idx, arr[0]); + Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_rotation(entry->bone_idx, arr[1]); + Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_scale(entry->bone_idx, arr[0]); + } } } } @@ -242,6 +247,8 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov p_anim->node_cache.resize(a->get_track_count()); + setup_pass++; + for (int i = 0; i < a->get_track_count(); i++) { p_anim->node_cache.write[i] = nullptr; RES resource; @@ -275,46 +282,68 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov node_cache_map[key] = TrackNodeCache(); } - p_anim->node_cache.write[i] = &node_cache_map[key]; - p_anim->node_cache[i]->path = a->track_get_path(i); - p_anim->node_cache[i]->node = child; - p_anim->node_cache[i]->resource = resource; - p_anim->node_cache[i]->node_2d = Object::cast_to<Node2D>(child); + TrackNodeCache *node_cache = &node_cache_map[key]; + p_anim->node_cache.write[i] = node_cache; + + node_cache->path = a->track_get_path(i); + node_cache->node = child; + node_cache->resource = resource; + node_cache->node_2d = Object::cast_to<Node2D>(child); #ifndef _3D_DISABLED - if (a->track_get_type(i) == Animation::TYPE_TRANSFORM3D) { + if (a->track_get_type(i) == Animation::TYPE_POSITION_3D || a->track_get_type(i) == Animation::TYPE_ROTATION_3D || a->track_get_type(i) == Animation::TYPE_SCALE_3D) { // special cases and caches for transform tracks + if (node_cache->last_setup_pass != setup_pass) { + node_cache->loc_used = false; + node_cache->rot_used = false; + node_cache->scale_used = false; + } + // cache node_3d - p_anim->node_cache[i]->node_3d = Object::cast_to<Node3D>(child); + node_cache->node_3d = Object::cast_to<Node3D>(child); // cache skeleton - p_anim->node_cache[i]->skeleton = Object::cast_to<Skeleton3D>(child); - if (p_anim->node_cache[i]->skeleton) { + node_cache->skeleton = Object::cast_to<Skeleton3D>(child); + if (node_cache->skeleton) { if (a->track_get_path(i).get_subname_count() == 1) { StringName bone_name = a->track_get_path(i).get_subname(0); - p_anim->node_cache[i]->bone_idx = p_anim->node_cache[i]->skeleton->find_bone(bone_name); - if (p_anim->node_cache[i]->bone_idx < 0) { + node_cache->bone_idx = node_cache->skeleton->find_bone(bone_name); + if (node_cache->bone_idx < 0) { // broken track (nonexistent bone) - p_anim->node_cache[i]->skeleton = nullptr; - p_anim->node_cache[i]->node_3d = nullptr; - ERR_CONTINUE(p_anim->node_cache[i]->bone_idx < 0); + node_cache->skeleton = nullptr; + node_cache->node_3d = nullptr; + ERR_CONTINUE(node_cache->bone_idx < 0); } } else { // no property, just use spatialnode - p_anim->node_cache[i]->skeleton = nullptr; + node_cache->skeleton = nullptr; + } + } + + switch (a->track_get_type(i)) { + case Animation::TYPE_POSITION_3D: { + node_cache->loc_used = true; + } break; + case Animation::TYPE_ROTATION_3D: { + node_cache->rot_used = true; + } break; + case Animation::TYPE_SCALE_3D: { + node_cache->scale_used = true; + } break; + default: { } } } #endif // _3D_DISABLED if (a->track_get_type(i) == Animation::TYPE_VALUE) { - if (!p_anim->node_cache[i]->property_anim.has(a->track_get_path(i).get_concatenated_subnames())) { + if (!node_cache->property_anim.has(a->track_get_path(i).get_concatenated_subnames())) { TrackNodeCache::PropertyAnim pa; pa.subpath = leftover_path; pa.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child; pa.special = SP_NONE; pa.owner = p_anim->node_cache[i]; - if (false && p_anim->node_cache[i]->node_2d) { + if (false && node_cache->node_2d) { if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_pos) { pa.special = SP_NODE2D_POS; } else if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_rot) { @@ -323,20 +352,22 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov pa.special = SP_NODE2D_SCALE; } } - p_anim->node_cache[i]->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa; + node_cache->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa; } } if (a->track_get_type(i) == Animation::TYPE_BEZIER && leftover_path.size()) { - if (!p_anim->node_cache[i]->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) { + if (!node_cache->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) { TrackNodeCache::BezierAnim ba; ba.bezier_property = leftover_path; ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child; ba.owner = p_anim->node_cache[i]; - p_anim->node_cache[i]->bezier_anim[a->track_get_path(i).get_concatenated_subnames()] = ba; + node_cache->bezier_anim[a->track_get_path(i).get_concatenated_subnames()] = ba; } } + + node_cache->last_setup_pass = setup_pass; } } @@ -369,17 +400,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double } switch (a->track_get_type(i)) { - case Animation::TYPE_TRANSFORM3D: { + case Animation::TYPE_POSITION_3D: { #ifndef _3D_DISABLED if (!nc->node_3d) { continue; } Vector3 loc; - Quaternion rot; - Vector3 scale; - Error err = a->transform_track_interpolate(i, p_time, &loc, &rot, &scale); + Error err = a->position_track_interpolate(i, p_time, &loc); //ERR_CONTINUE(err!=OK); //used for testing, should be removed if (err != OK) { @@ -391,12 +420,63 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double cache_update[cache_update_size++] = nc; nc->accum_pass = accum_pass; nc->loc_accum = loc; - nc->rot_accum = rot; - nc->scale_accum = scale; - + nc->rot_accum = Quaternion(); + nc->scale_accum = Vector3(); } else { nc->loc_accum = nc->loc_accum.lerp(loc, p_interp); + } +#endif // _3D_DISABLED + } break; + case Animation::TYPE_ROTATION_3D: { +#ifndef _3D_DISABLED + if (!nc->node_3d) { + continue; + } + + Quaternion rot; + + Error err = a->rotation_track_interpolate(i, p_time, &rot); + //ERR_CONTINUE(err!=OK); //used for testing, should be removed + + if (err != OK) { + continue; + } + + if (nc->accum_pass != accum_pass) { + ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); + cache_update[cache_update_size++] = nc; + nc->accum_pass = accum_pass; + nc->loc_accum = Vector3(); + nc->rot_accum = rot; + nc->scale_accum = Vector3(); + } else { nc->rot_accum = nc->rot_accum.slerp(rot, p_interp); + } +#endif // _3D_DISABLED + } break; + case Animation::TYPE_SCALE_3D: { +#ifndef _3D_DISABLED + if (!nc->node_3d) { + continue; + } + + Vector3 scale; + + Error err = a->scale_track_interpolate(i, p_time, &scale); + //ERR_CONTINUE(err!=OK); //used for testing, should be removed + + if (err != OK) { + continue; + } + + if (nc->accum_pass != accum_pass) { + ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX); + cache_update[cache_update_size++] = nc; + nc->accum_pass = accum_pass; + nc->loc_accum = Vector3(); + nc->rot_accum = Quaternion(); + nc->scale_accum = scale; + } else { nc->scale_accum = nc->scale_accum.lerp(scale, p_interp); } #endif // _3D_DISABLED @@ -855,15 +935,30 @@ void AnimationPlayer::_animation_update_transforms() { TrackNodeCache *nc = cache_update[i]; ERR_CONTINUE(nc->accum_pass != accum_pass); - - t.origin = nc->loc_accum; - t.basis.set_quaternion_scale(nc->rot_accum, nc->scale_accum); #ifndef _3D_DISABLED if (nc->skeleton && nc->bone_idx >= 0) { - nc->skeleton->set_bone_pose(nc->bone_idx, t); + if (nc->loc_used) { + nc->skeleton->set_bone_pose_position(nc->bone_idx, nc->loc_accum); + } + if (nc->rot_used) { + nc->skeleton->set_bone_pose_rotation(nc->bone_idx, nc->rot_accum); + } + if (nc->scale_used) { + nc->skeleton->set_bone_pose_scale(nc->bone_idx, nc->scale_accum); + } + } else if (nc->node_3d) { - nc->node_3d->set_transform(t); + if (nc->loc_used) { + nc->node_3d->set_position(nc->loc_accum); + } + if (nc->rot_used) { + nc->node_3d->set_rotation(nc->rot_accum.get_euler()); + } + if (nc->scale_used) { + nc->node_3d->set_scale(nc->scale_accum); + } } + #endif // _3D_DISABLED } } @@ -1527,7 +1622,12 @@ Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values(Node *p_root_o AnimatedValuesBackup::Entry entry; entry.object = nc->skeleton; entry.bone_idx = nc->bone_idx; - entry.value = nc->skeleton->get_bone_pose(nc->bone_idx); + Array arr; + arr.resize(3); + arr[0] = nc->skeleton->get_bone_pose_position(nc->bone_idx); + arr[1] = nc->skeleton->get_bone_pose_rotation(nc->bone_idx); + arr[2] = nc->skeleton->get_bone_pose_scale(nc->bone_idx); + entry.value = nc; backup->entries.push_back(entry); } else { if (nc->node_3d) { diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index b693e29bdf..f8e72a67b6 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -88,6 +88,8 @@ private: SP_NODE2D_SCALE, }; + uint32_t setup_pass = 1; + struct TrackNodeCache { NodePath path; uint32_t id = 0; @@ -101,6 +103,10 @@ private: int bone_idx = -1; // accumulated transforms + bool loc_used = false; + bool rot_used = false; + bool scale_used = false; + Vector3 loc_accum; Quaternion rot_accum; Vector3 scale_accum; @@ -134,6 +140,7 @@ private: Map<StringName, BezierAnim> bezier_anim; + uint32_t last_setup_pass = 0; TrackNodeCache() {} }; diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 9ca8d478b1..dd5fe46223 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -544,13 +544,18 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { NodePath path = anim->track_get_path(i); Animation::TrackType track_type = anim->track_get_type(i); + Animation::TrackType track_cache_type = track_type; + if (track_cache_type == Animation::TYPE_POSITION_3D || track_cache_type == Animation::TYPE_ROTATION_3D || track_cache_type == Animation::TYPE_SCALE_3D) { + track_cache_type = Animation::TYPE_POSITION_3D; //reference them as position3D tracks, even if they modify rotation or scale + } + TrackCache *track = nullptr; if (track_cache.has(path)) { track = track_cache.get(path); } //if not valid, delete track - if (track && (track->type != track_type || ObjectDB::get_instance(track->object_id) == nullptr)) { + if (track && (track->type != track_cache_type || ObjectDB::get_instance(track->object_id) == nullptr)) { playing_caches.erase(track); memdelete(track); track_cache.erase(path); @@ -587,7 +592,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track = track_value; } break; - case Animation::TYPE_TRANSFORM3D: { + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { #ifndef _3D_DISABLED Node3D *node_3d = Object::cast_to<Node3D>(child); @@ -597,6 +604,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { } TrackCacheTransform *track_xform = memnew(TrackCacheTransform); + track_xform->type = Animation::TYPE_POSITION_3D; track_xform->node_3d = node_3d; track_xform->skeleton = nullptr; @@ -615,6 +623,21 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track_xform->object_id = track_xform->object->get_instance_id(); track = track_xform; + + switch (track_type) { + case Animation::TYPE_POSITION_3D: { + track_xform->loc_used = true; + } break; + case Animation::TYPE_ROTATION_3D: { + track_xform->rot_used = true; + } break; + case Animation::TYPE_SCALE_3D: { + track_xform->scale_used = true; + } break; + default: { + } + } + #endif // _3D_DISABLED } break; case Animation::TYPE_METHOD: { @@ -670,6 +693,26 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { } track_cache[path] = track; + } else if (track_cache_type == Animation::TYPE_POSITION_3D) { + TrackCacheTransform *track_xform = static_cast<TrackCacheTransform *>(track); + if (track->setup_pass != setup_pass) { + track_xform->loc_used = false; + track_xform->rot_used = false; + track_xform->scale_used = false; + } + switch (track_type) { + case Animation::TYPE_POSITION_3D: { + track_xform->loc_used = true; + } break; + case Animation::TYPE_ROTATION_3D: { + track_xform->rot_used = true; + } break; + case Animation::TYPE_SCALE_3D: { + track_xform->scale_used = true; + } break; + default: { + } + } } track->setup_pass = setup_pass; @@ -831,8 +874,11 @@ void AnimationTree::_process_graph(real_t p_delta) { ERR_CONTINUE(!track_cache.has(path)); TrackCache *track = track_cache[path]; - if (track->type != a->track_get_type(i)) { - continue; //may happen should not + + Animation::TrackType ttype = a->track_get_type(i); + if (ttype != Animation::TYPE_POSITION_3D && ttype != Animation::TYPE_ROTATION_3D && ttype != Animation::TYPE_SCALE_3D && track->type != ttype) { + //broken animation, but avoid error spamming + continue; } track->root_motion = root_motion_track == path; @@ -848,20 +894,81 @@ void AnimationTree::_process_graph(real_t p_delta) { continue; //nothing to blend } - switch (track->type) { - case Animation::TYPE_TRANSFORM3D: { + switch (ttype) { + case Animation::TYPE_POSITION_3D: { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); + if (t->process_pass != process_pass) { + t->process_pass = process_pass; + t->loc = Vector3(); + t->rot = Quaternion(); + t->rot_blend_accum = 0; + t->scale = Vector3(1, 1, 1); + } + if (track->root_motion) { - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->loc = Vector3(); - t->rot = Quaternion(); - t->rot_blend_accum = 0; - t->scale = Vector3(1, 1, 1); + real_t prev_time = time - delta; + if (prev_time < 0) { + if (!a->has_loop()) { + prev_time = 0; + } else { + prev_time = a->get_length() + prev_time; + } } + Vector3 loc[2]; + + if (prev_time > time) { + Error err = a->position_track_interpolate(i, prev_time, &loc[0]); + if (err != OK) { + continue; + } + + a->position_track_interpolate(i, a->get_length(), &loc[1]); + + t->loc += (loc[1] - loc[0]) * blend; + prev_time = 0; + } + + Error err = a->position_track_interpolate(i, prev_time, &loc[0]); + if (err != OK) { + continue; + } + + a->position_track_interpolate(i, time, &loc[1]); + + t->loc += (loc[1] - loc[0]) * blend; + + prev_time = 0; + + } else { + Vector3 loc; + + Error err = a->position_track_interpolate(i, time, &loc); + //ERR_CONTINUE(err!=OK); //used for testing, should be removed + + if (err != OK) { + continue; + } + + t->loc = t->loc.lerp(loc, blend); + } +#endif // _3D_DISABLED + } break; + case Animation::TYPE_ROTATION_3D: { +#ifndef _3D_DISABLED + TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); + + if (t->process_pass != process_pass) { + t->process_pass = process_pass; + t->loc = Vector3(); + t->rot = Quaternion(); + t->rot_blend_accum = 0; + t->scale = Vector3(1, 1, 1); + } + + if (track->root_motion) { real_t prev_time = time - delta; if (prev_time < 0) { if (!a->has_loop()) { @@ -871,61 +978,44 @@ void AnimationTree::_process_graph(real_t p_delta) { } } - Vector3 loc[2]; Quaternion rot[2]; - Vector3 scale[2]; if (prev_time > time) { - Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]); + Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]); if (err != OK) { continue; } - a->transform_track_interpolate(i, a->get_length(), &loc[1], &rot[1], &scale[1]); + a->rotation_track_interpolate(i, a->get_length(), &rot[1]); - t->loc += (loc[1] - loc[0]) * blend; - t->scale += (scale[1] - scale[0]) * blend; Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized(); t->rot = (t->rot * q).normalized(); prev_time = 0; } - Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]); + Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]); if (err != OK) { continue; } - a->transform_track_interpolate(i, time, &loc[1], &rot[1], &scale[1]); + a->rotation_track_interpolate(i, time, &rot[1]); - t->loc += (loc[1] - loc[0]) * blend; - t->scale += (scale[1] - scale[0]) * blend; Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized(); t->rot = (t->rot * q).normalized(); prev_time = 0; } else { - Vector3 loc; Quaternion rot; - Vector3 scale; - Error err = a->transform_track_interpolate(i, time, &loc, &rot, &scale); + Error err = a->rotation_track_interpolate(i, time, &rot); //ERR_CONTINUE(err!=OK); //used for testing, should be removed - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->loc = loc; - t->rot = rot; - t->rot_blend_accum = 0; - t->scale = scale; - } - if (err != OK) { continue; } - t->loc = t->loc.lerp(loc, blend); if (t->rot_blend_accum == 0) { t->rot = rot; t->rot_blend_accum = blend; @@ -934,6 +1024,67 @@ void AnimationTree::_process_graph(real_t p_delta) { t->rot = rot.slerp(t->rot, t->rot_blend_accum / rot_total).normalized(); t->rot_blend_accum = rot_total; } + } +#endif // _3D_DISABLED + } break; + case Animation::TYPE_SCALE_3D: { +#ifndef _3D_DISABLED + TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); + + if (t->process_pass != process_pass) { + t->process_pass = process_pass; + t->loc = Vector3(); + t->rot = Quaternion(); + t->rot_blend_accum = 0; + t->scale = Vector3(1, 1, 1); + } + + if (track->root_motion) { + real_t prev_time = time - delta; + if (prev_time < 0) { + if (!a->has_loop()) { + prev_time = 0; + } else { + prev_time = a->get_length() + prev_time; + } + } + + Vector3 scale[2]; + + if (prev_time > time) { + Error err = a->scale_track_interpolate(i, prev_time, &scale[0]); + if (err != OK) { + continue; + } + + a->scale_track_interpolate(i, a->get_length(), &scale[1]); + + t->scale += (scale[1] - scale[0]) * blend; + + prev_time = 0; + } + + Error err = a->scale_track_interpolate(i, prev_time, &scale[0]); + if (err != OK) { + continue; + } + + a->scale_track_interpolate(i, time, &scale[1]); + + t->scale += (scale[1] - scale[0]) * blend; + + prev_time = 0; + + } else { + Vector3 scale; + + Error err = a->scale_track_interpolate(i, time, &scale); + //ERR_CONTINUE(err!=OK); //used for testing, should be removed + + if (err != OK) { + continue; + } + t->scale = t->scale.lerp(scale, blend); } #endif // _3D_DISABLED @@ -1198,26 +1349,38 @@ void AnimationTree::_process_graph(real_t p_delta) { } switch (track->type) { - case Animation::TYPE_TRANSFORM3D: { + case Animation::TYPE_POSITION_3D: { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); - Transform3D xform; - xform.origin = t->loc; - - xform.basis.set_quaternion_scale(t->rot, t->scale); - if (t->root_motion) { + Transform3D xform; + xform.origin = t->loc; + xform.basis.set_quaternion_scale(t->rot, t->scale); + root_motion_transform = xform; - if (t->skeleton && t->bone_idx >= 0) { - root_motion_transform = (t->skeleton->get_bone_rest(t->bone_idx) * root_motion_transform) * t->skeleton->get_bone_rest(t->bone_idx).affine_inverse(); - } } else if (t->skeleton && t->bone_idx >= 0) { - t->skeleton->set_bone_pose(t->bone_idx, xform); + if (t->loc_used) { + t->skeleton->set_bone_pose_position(t->bone_idx, t->loc); + } + if (t->rot_used) { + t->skeleton->set_bone_pose_rotation(t->bone_idx, t->rot); + } + if (t->scale_used) { + t->skeleton->set_bone_pose_scale(t->bone_idx, t->scale); + } } else if (!t->skeleton) { - t->node_3d->set_transform(xform); + if (t->loc_used) { + t->node_3d->set_position(t->loc); + } + if (t->rot_used) { + t->node_3d->set_rotation(t->rot.get_euler()); + } + if (t->scale_used) { + t->node_3d->set_scale(t->scale); + } } #endif // _3D_DISABLED } break; diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 1e0267682e..ed207dfe0c 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -197,13 +197,16 @@ private: Skeleton3D *skeleton = nullptr; #endif // _3D_DISABLED int bone_idx = -1; + bool loc_used = false; + bool rot_used = false; + bool scale_used = false; Vector3 loc; Quaternion rot; real_t rot_blend_accum = 0.0; Vector3 scale; TrackCacheTransform() { - type = Animation::TYPE_TRANSFORM3D; + type = Animation::TYPE_POSITION_3D; } }; diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index b4eec2530b..6612c5ff3c 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -43,8 +43,12 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { if (tracks.size() == track && what == "type") { String type = p_value; - if (type == "transform" || type == "transform3d") { - add_track(TYPE_TRANSFORM3D); + if (type == "position_3d") { + add_track(TYPE_POSITION_3D); + } else if (type == "rotation_3d") { + add_track(TYPE_ROTATION_3D); + } else if (type == "scale_3d") { + add_track(TYPE_SCALE_3D); } else if (type == "value") { add_track(TYPE_VALUE); } else if (type == "method") { @@ -75,35 +79,72 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { } else if (what == "enabled") { track_set_enabled(track, p_value); } else if (what == "keys" || what == "key_values") { - if (track_get_type(track) == TYPE_TRANSFORM3D) { - TransformTrack *tt = static_cast<TransformTrack *>(tracks[track]); + if (track_get_type(track) == TYPE_POSITION_3D) { + PositionTrack *tt = static_cast<PositionTrack *>(tracks[track]); Vector<real_t> values = p_value; int vcount = values.size(); - ERR_FAIL_COND_V(vcount % TRANSFORM_TRACK_SIZE, false); + ERR_FAIL_COND_V(vcount % POSITION_TRACK_SIZE, false); const real_t *r = values.ptr(); - int64_t count = vcount / TRANSFORM_TRACK_SIZE; - tt->transforms.resize(count); + int64_t count = vcount / POSITION_TRACK_SIZE; + tt->positions.resize(count); + TKey<Vector3> *tw = tt->positions.ptrw(); for (int i = 0; i < count; i++) { - TKey<TransformKey> &tk = tt->transforms.write[i]; - const real_t *ofs = &r[i * TRANSFORM_TRACK_SIZE]; + TKey<Vector3> &tk = tw[i]; + const real_t *ofs = &r[i * POSITION_TRACK_SIZE]; tk.time = ofs[0]; tk.transition = ofs[1]; - tk.value.loc.x = ofs[2]; - tk.value.loc.y = ofs[3]; - tk.value.loc.z = ofs[4]; + tk.value.x = ofs[2]; + tk.value.y = ofs[3]; + tk.value.z = ofs[4]; + } + } else if (track_get_type(track) == TYPE_ROTATION_3D) { + RotationTrack *rt = static_cast<RotationTrack *>(tracks[track]); + Vector<real_t> values = p_value; + int vcount = values.size(); + ERR_FAIL_COND_V(vcount % ROTATION_TRACK_SIZE, false); + + const real_t *r = values.ptr(); + + int64_t count = vcount / ROTATION_TRACK_SIZE; + rt->rotations.resize(count); + + TKey<Quaternion> *rw = rt->rotations.ptrw(); + for (int i = 0; i < count; i++) { + TKey<Quaternion> &rk = rw[i]; + const real_t *ofs = &r[i * ROTATION_TRACK_SIZE]; + rk.time = ofs[0]; + rk.transition = ofs[1]; + + rk.value.x = ofs[2]; + rk.value.y = ofs[3]; + rk.value.z = ofs[4]; + rk.value.w = ofs[5]; + } + } else if (track_get_type(track) == TYPE_SCALE_3D) { + ScaleTrack *st = static_cast<ScaleTrack *>(tracks[track]); + Vector<real_t> values = p_value; + int vcount = values.size(); + ERR_FAIL_COND_V(vcount % SCALE_TRACK_SIZE, false); - tk.value.rot.x = ofs[5]; - tk.value.rot.y = ofs[6]; - tk.value.rot.z = ofs[7]; - tk.value.rot.w = ofs[8]; + const real_t *r = values.ptr(); + + int64_t count = vcount / SCALE_TRACK_SIZE; + st->scales.resize(count); - tk.value.scale.x = ofs[9]; - tk.value.scale.y = ofs[10]; - tk.value.scale.z = ofs[11]; + TKey<Vector3> *sw = st->scales.ptrw(); + for (int i = 0; i < count; i++) { + TKey<Vector3> &sk = sw[i]; + const real_t *ofs = &r[i * SCALE_TRACK_SIZE]; + sk.time = ofs[0]; + sk.transition = ofs[1]; + + sk.value.x = ofs[2]; + sk.value.y = ofs[3]; + sk.value.z = ofs[4]; } } else if (track_get_type(track) == TYPE_VALUE) { @@ -319,8 +360,14 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { ERR_FAIL_INDEX_V(track, tracks.size(), false); if (what == "type") { switch (track_get_type(track)) { - case TYPE_TRANSFORM3D: - r_ret = "transform"; + case TYPE_POSITION_3D: + r_ret = "position_3d"; + break; + case TYPE_ROTATION_3D: + r_ret = "rotation_3d"; + break; + case TYPE_SCALE_3D: + r_ret = "scale_3d"; break; case TYPE_VALUE: r_ret = "value"; @@ -352,31 +399,64 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { } else if (what == "enabled") { r_ret = track_is_enabled(track); } else if (what == "keys") { - if (track_get_type(track) == TYPE_TRANSFORM3D) { + if (track_get_type(track) == TYPE_POSITION_3D) { Vector<real_t> keys; int kk = track_get_key_count(track); - keys.resize(kk * TRANSFORM_TRACK_SIZE); + keys.resize(kk * POSITION_TRACK_SIZE); real_t *w = keys.ptrw(); int idx = 0; for (int i = 0; i < track_get_key_count(track); i++) { Vector3 loc; - Quaternion rot; - Vector3 scale; - transform_track_get_key(track, i, &loc, &rot, &scale); + position_track_get_key(track, i, &loc); w[idx++] = track_get_key_time(track, i); w[idx++] = track_get_key_transition(track, i); w[idx++] = loc.x; w[idx++] = loc.y; w[idx++] = loc.z; + } + r_ret = keys; + return true; + } else if (track_get_type(track) == TYPE_ROTATION_3D) { + Vector<real_t> keys; + int kk = track_get_key_count(track); + keys.resize(kk * ROTATION_TRACK_SIZE); + + real_t *w = keys.ptrw(); + + int idx = 0; + for (int i = 0; i < track_get_key_count(track); i++) { + Quaternion rot; + rotation_track_get_key(track, i, &rot); + + w[idx++] = track_get_key_time(track, i); + w[idx++] = track_get_key_transition(track, i); w[idx++] = rot.x; w[idx++] = rot.y; w[idx++] = rot.z; w[idx++] = rot.w; + } + r_ret = keys; + return true; + + } else if (track_get_type(track) == TYPE_SCALE_3D) { + Vector<real_t> keys; + int kk = track_get_key_count(track); + keys.resize(kk * SCALE_TRACK_SIZE); + + real_t *w = keys.ptrw(); + + int idx = 0; + for (int i = 0; i < track_get_key_count(track); i++) { + Vector3 scale; + scale_track_get_key(track, i, &scale); + + w[idx++] = track_get_key_time(track, i); + w[idx++] = track_get_key_transition(track, i); w[idx++] = scale.x; w[idx++] = scale.y; w[idx++] = scale.z; @@ -384,7 +464,6 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { r_ret = keys; return true; - } else if (track_get_type(track) == TYPE_VALUE) { const ValueTrack *vt = static_cast<const ValueTrack *>(tracks[track]); @@ -591,10 +670,18 @@ int Animation::add_track(TrackType p_type, int p_at_pos) { } switch (p_type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = memnew(TransformTrack); + case TYPE_POSITION_3D: { + PositionTrack *tt = memnew(PositionTrack); tracks.insert(p_at_pos, tt); } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = memnew(RotationTrack); + tracks.insert(p_at_pos, rt); + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = memnew(ScaleTrack); + tracks.insert(p_at_pos, st); + } break; case TYPE_VALUE: { tracks.insert(p_at_pos, memnew(ValueTrack)); @@ -629,9 +716,19 @@ void Animation::remove_track(int p_track) { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - _clear(tt->transforms); + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + _clear(tt->positions); + + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + _clear(rt->rotations); + + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + _clear(st->scales); } break; case TYPE_VALUE: { @@ -672,7 +769,7 @@ int Animation::get_track_count() const { } Animation::TrackType Animation::track_get_type(int p_track) const { - ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_TRANSFORM3D); + ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_VALUE); return tracks[p_track]->type; } @@ -720,31 +817,6 @@ bool Animation::track_get_interpolation_loop_wrap(int p_track) const { return tracks[p_track]->loop_wrap; } -// transform -/* -template<class T> -int Animation::_insert_pos(double p_time, T& p_keys) { - // simple, linear time inset that should be fast enough in reality. - - int idx=p_keys.size(); - - while(true) { - - - if (idx==0 || p_keys[idx-1].time < p_time) { - //condition for insertion. - p_keys.insert(idx,T()); - return idx; - } else if (p_keys[idx-1].time == p_time) { - // condition for replacing. - return idx-1; - } - - idx--; - } -} - -*/ template <class T, class V> int Animation::_insert(double p_time, T &p_keys, const V &p_value) { int idx = p_keys.size(); @@ -774,45 +846,153 @@ void Animation::_clear(T &p_keys) { p_keys.clear(); } -Error Animation::transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const { +//// + +int Animation::position_track_insert_key(int p_track, double p_time, const Vector3 &p_position) { + ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, -1); + + PositionTrack *tt = static_cast<PositionTrack *>(t); + + TKey<Vector3> tkey; + tkey.time = p_time; + tkey.value = p_position; + + int ret = _insert(p_time, tt->positions, tkey); + emit_changed(); + return ret; +} + +Error Animation::position_track_get_key(int p_track, int p_key, Vector3 *r_position) const { ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); Track *t = tracks[p_track]; - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER); - ERR_FAIL_INDEX_V(p_key, tt->transforms.size(), ERR_INVALID_PARAMETER); + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_key, tt->positions.size(), ERR_INVALID_PARAMETER); - if (r_loc) { - *r_loc = tt->transforms[p_key].value.loc; - } - if (r_rot) { - *r_rot = tt->transforms[p_key].value.rot; - } - if (r_scale) { - *r_scale = tt->transforms[p_key].value.scale; + *r_position = tt->positions[p_key].value; + + return OK; +} + +Error Animation::position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER); + + PositionTrack *tt = static_cast<PositionTrack *>(t); + + bool ok = false; + + Vector3 tk = _interpolate(tt->positions, p_time, tt->interpolation, tt->loop_wrap, &ok); + + if (!ok) { + return ERR_UNAVAILABLE; } + *r_interpolation = tk; + return OK; +} + +//// + +int Animation::rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation) { + ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, -1); + + RotationTrack *rt = static_cast<RotationTrack *>(t); + + TKey<Quaternion> tkey; + tkey.time = p_time; + tkey.value = p_rotation; + + int ret = _insert(p_time, rt->rotations, tkey); + emit_changed(); + return ret; +} + +Error Animation::rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + + RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_key, rt->rotations.size(), ERR_INVALID_PARAMETER); + + *r_rotation = rt->rotations[p_key].value; return OK; } -int Animation::transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot, const Vector3 &p_scale) { +Error Animation::rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER); + + RotationTrack *rt = static_cast<RotationTrack *>(t); + + bool ok = false; + + Quaternion tk = _interpolate(rt->rotations, p_time, rt->interpolation, rt->loop_wrap, &ok); + + if (!ok) { + return ERR_UNAVAILABLE; + } + *r_interpolation = tk; + return OK; +} + +//// + +int Animation::scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale) { ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); Track *t = tracks[p_track]; - ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, -1); + ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, -1); - TransformTrack *tt = static_cast<TransformTrack *>(t); + ScaleTrack *st = static_cast<ScaleTrack *>(t); - TKey<TransformKey> tkey; + TKey<Vector3> tkey; tkey.time = p_time; - tkey.value.loc = p_loc; - tkey.value.rot = p_rot; - tkey.value.scale = p_scale; + tkey.value = p_scale; - int ret = _insert(p_time, tt->transforms, tkey); + int ret = _insert(p_time, st->scales, tkey); emit_changed(); return ret; } +Error Animation::scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + + ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_key, st->scales.size(), ERR_INVALID_PARAMETER); + + *r_scale = st->scales[p_key].value; + + return OK; +} + +Error Animation::scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER); + + ScaleTrack *st = static_cast<ScaleTrack *>(t); + + bool ok = false; + + Vector3 tk = _interpolate(st->scales, p_time, st->interpolation, st->loop_wrap, &ok); + + if (!ok) { + return ERR_UNAVAILABLE; + } + *r_interpolation = tk; + return OK; +} + void Animation::track_remove_key_at_time(int p_track, double p_time) { int idx = track_find_key(p_track, p_time, true); ERR_FAIL_COND(idx < 0); @@ -824,10 +1004,22 @@ void Animation::track_remove_key(int p_track, int p_idx) { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_INDEX(p_idx, tt->transforms.size()); - tt->transforms.remove(p_idx); + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_INDEX(p_idx, tt->positions.size()); + tt->positions.remove(p_idx); + + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_INDEX(p_idx, rt->rotations.size()); + rt->rotations.remove(p_idx); + + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_INDEX(p_idx, st->scales.size()); + st->scales.remove(p_idx); } break; case TYPE_VALUE: { @@ -870,13 +1062,37 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - int k = _find(tt->transforms, p_time); - if (k < 0 || k >= tt->transforms.size()) { + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + int k = _find(tt->positions, p_time); + if (k < 0 || k >= tt->positions.size()) { return -1; } - if (tt->transforms[k].time != p_time && p_exact) { + if (tt->positions[k].time != p_time && p_exact) { + return -1; + } + return k; + + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + int k = _find(rt->rotations, p_time); + if (k < 0 || k >= rt->rotations.size()) { + return -1; + } + if (rt->rotations[k].time != p_time && p_exact) { + return -1; + } + return k; + + } break; + case TYPE_SCALE_3D: { + ScaleTrack *rt = static_cast<ScaleTrack *>(t); + int k = _find(rt->scales, p_time); + if (k < 0 || k >= rt->scales.size()) { + return -1; + } + if (rt->scales[k].time != p_time && p_exact) { return -1; } return k; @@ -952,24 +1168,21 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - Dictionary d = p_key; - Vector3 loc; - if (d.has("location")) { - loc = d["location"]; - } - - Quaternion rot; - if (d.has("rotation")) { - rot = d["rotation"]; - } + case TYPE_POSITION_3D: { + ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I)); + int idx = position_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, idx, p_transition); - Vector3 scale; - if (d.has("scale")) { - scale = d["scale"]; - } + } break; + case TYPE_ROTATION_3D: { + ERR_FAIL_COND((p_key.get_type() != Variant::QUATERNION) && (p_key.get_type() != Variant::BASIS)); + int idx = rotation_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, idx, p_transition); - int idx = transform_track_insert_key(p_track, p_time, loc, rot, scale); + } break; + case TYPE_SCALE_3D: { + ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I)); + int idx = scale_track_insert_key(p_track, p_time, p_key); track_set_key_transition(p_track, idx, p_transition); } break; @@ -1054,9 +1267,17 @@ int Animation::track_get_key_count(int p_track) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - return tt->transforms.size(); + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + return tt->positions.size(); + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + return rt->rotations.size(); + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + return st->scales.size(); } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); @@ -1089,16 +1310,23 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), Variant()); + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), Variant()); - Dictionary d; - d["location"] = tt->transforms[p_key_idx].value.loc; - d["rotation"] = tt->transforms[p_key_idx].value.rot; - d["scale"] = tt->transforms[p_key_idx].value.scale; + return tt->positions[p_key_idx].value; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), Variant()); - return d; + return rt->rotations[p_key_idx].value; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), Variant()); + + return st->scales[p_key_idx].value; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); @@ -1157,10 +1385,20 @@ double Animation::track_get_key_time(int p_track, int p_key_idx) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1); - return tt->transforms[p_key_idx].time; + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1); + return tt->positions[p_key_idx].time; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1); + return rt->rotations[p_key_idx].time; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1); + return st->scales[p_key_idx].time; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); @@ -1202,13 +1440,31 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); - TKey<TransformKey> key = tt->transforms[p_key_idx]; + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, tt->positions.size()); + TKey<Vector3> key = tt->positions[p_key_idx]; key.time = p_time; - tt->transforms.remove(p_key_idx); - _insert(p_time, tt->transforms, key); + tt->positions.remove(p_key_idx); + _insert(p_time, tt->positions, key); + return; + } + case TYPE_ROTATION_3D: { + RotationTrack *tt = static_cast<RotationTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, tt->rotations.size()); + TKey<Quaternion> key = tt->rotations[p_key_idx]; + key.time = p_time; + tt->rotations.remove(p_key_idx); + _insert(p_time, tt->rotations, key); + return; + } + case TYPE_SCALE_3D: { + ScaleTrack *tt = static_cast<ScaleTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, tt->scales.size()); + TKey<Vector3> key = tt->scales[p_key_idx]; + key.time = p_time; + tt->scales.remove(p_key_idx); + _insert(p_time, tt->scales, key); return; } case TYPE_VALUE: { @@ -1266,10 +1522,20 @@ real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1); - return tt->transforms[p_key_idx].transition; + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1); + return tt->positions[p_key_idx].transition; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1); + return rt->rotations[p_key_idx].transition; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1); + return st->scales[p_key_idx].transition; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); @@ -1302,21 +1568,28 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); + case TYPE_POSITION_3D: { + ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I)); + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, tt->positions.size()); - Dictionary d = p_value; + tt->positions.write[p_key_idx].value = p_value; - if (d.has("location")) { - tt->transforms.write[p_key_idx].value.loc = d["location"]; - } - if (d.has("rotation")) { - tt->transforms.write[p_key_idx].value.rot = d["rotation"]; - } - if (d.has("scale")) { - tt->transforms.write[p_key_idx].value.scale = d["scale"]; - } + } break; + case TYPE_ROTATION_3D: { + ERR_FAIL_COND((p_value.get_type() != Variant::QUATERNION) && (p_value.get_type() != Variant::BASIS)); + RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, rt->rotations.size()); + + rt->rotations.write[p_key_idx].value = p_value; + + } break; + case TYPE_SCALE_3D: { + ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I)); + ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, st->scales.size()); + + st->scales.write[p_key_idx].value = p_value; } break; case TYPE_VALUE: { @@ -1385,10 +1658,20 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast<TransformTrack *>(t); - ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); - tt->transforms.write[p_key_idx].transition = p_transition; + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast<PositionTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, tt->positions.size()); + tt->positions.write[p_key_idx].transition = p_transition; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast<RotationTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, rt->rotations.size()); + rt->rotations.write[p_key_idx].transition = p_transition; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast<ScaleTrack *>(t); + ERR_FAIL_INDEX(p_key_idx, st->scales.size()); + st->scales.write[p_key_idx].transition = p_transition; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); @@ -1450,15 +1733,6 @@ int Animation::_find(const Vector<K> &p_keys, double p_time) const { return middle; } -Animation::TransformKey Animation::_interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const { - TransformKey ret; - ret.loc = _interpolate(p_a.loc, p_b.loc, p_c); - ret.rot = _interpolate(p_a.rot, p_b.rot, p_c); - ret.scale = _interpolate(p_a.scale, p_b.scale, p_c); - - return ret; -} - Vector3 Animation::_interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const { return p_a.lerp(p_b, p_c); } @@ -1477,16 +1751,6 @@ real_t Animation::_interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) return p_a * (1.0 - p_c) + p_b * p_c; } -Animation::TransformKey Animation::_cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const { - Animation::TransformKey tk; - - tk.loc = p_a.loc.cubic_interpolate(p_b.loc, p_pre_a.loc, p_post_b.loc, p_c); - tk.scale = p_a.scale.cubic_interpolate(p_b.scale, p_pre_a.scale, p_post_b.scale, p_c); - tk.rot = p_a.rot.cubic_slerp(p_b.rot, p_pre_a.rot, p_post_b.rot, p_c); - - return tk; -} - Vector3 Animation::_cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const { return p_a.cubic_interpolate(p_b, p_pre_a, p_post_b, p_c); } @@ -1729,36 +1993,6 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol // do a barrel roll } -Error Animation::transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const { - ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); - Track *t = tracks[p_track]; - ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER); - - TransformTrack *tt = static_cast<TransformTrack *>(t); - - bool ok = false; - - TransformKey tk = _interpolate(tt->transforms, p_time, tt->interpolation, tt->loop_wrap, &ok); - - if (!ok) { - return ERR_UNAVAILABLE; - } - - if (r_loc) { - *r_loc = tk.loc; - } - - if (r_rot) { - *r_rot = tk.rot; - } - - if (r_scale) { - *r_scale = tk.scale; - } - - return OK; -} - Variant Animation::value_track_interpolate(int p_track, double p_time) const { ERR_FAIL_INDEX_V(p_track, tracks.size(), 0); Track *t = tracks[p_track]; @@ -1933,10 +2167,22 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl // handle loop by splitting switch (t->type) { - case TYPE_TRANSFORM3D: { - const TransformTrack *tt = static_cast<const TransformTrack *>(t); - _track_get_key_indices_in_range(tt->transforms, from_time, length, p_indices); - _track_get_key_indices_in_range(tt->transforms, 0, to_time, p_indices); + case TYPE_POSITION_3D: { + const PositionTrack *tt = static_cast<const PositionTrack *>(t); + _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices); + _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices); + + } break; + case TYPE_ROTATION_3D: { + const RotationTrack *rt = static_cast<const RotationTrack *>(t); + _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices); + _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices); + + } break; + case TYPE_SCALE_3D: { + const ScaleTrack *st = static_cast<const ScaleTrack *>(t); + _track_get_key_indices_in_range(st->scales, from_time, length, p_indices); + _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices); } break; case TYPE_VALUE: { @@ -1989,9 +2235,19 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl } switch (t->type) { - case TYPE_TRANSFORM3D: { - const TransformTrack *tt = static_cast<const TransformTrack *>(t); - _track_get_key_indices_in_range(tt->transforms, from_time, to_time, p_indices); + case TYPE_POSITION_3D: { + const PositionTrack *tt = static_cast<const PositionTrack *>(t); + _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices); + + } break; + case TYPE_ROTATION_3D: { + const RotationTrack *rt = static_cast<const RotationTrack *>(t); + _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices); + + } break; + case TYPE_SCALE_3D: { + const ScaleTrack *st = static_cast<const ScaleTrack *>(t); + _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices); } break; case TYPE_VALUE: { @@ -2608,7 +2864,10 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("track_set_enabled", "track_idx", "enabled"), &Animation::track_set_enabled); ClassDB::bind_method(D_METHOD("track_is_enabled", "track_idx"), &Animation::track_is_enabled); - ClassDB::bind_method(D_METHOD("transform_track_insert_key", "track_idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key); + ClassDB::bind_method(D_METHOD("position_track_insert_key", "track_idx", "time", "position"), &Animation::position_track_insert_key); + ClassDB::bind_method(D_METHOD("rotation_track_insert_key", "track_idx", "time", "rotation"), &Animation::rotation_track_insert_key); + ClassDB::bind_method(D_METHOD("scale_track_insert_key", "track_idx", "time", "scale"), &Animation::scale_track_insert_key); + ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1)); ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key); ClassDB::bind_method(D_METHOD("track_remove_key_at_time", "track_idx", "time"), &Animation::track_remove_key_at_time); @@ -2628,7 +2887,6 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("track_set_interpolation_loop_wrap", "track_idx", "interpolation"), &Animation::track_set_interpolation_loop_wrap); ClassDB::bind_method(D_METHOD("track_get_interpolation_loop_wrap", "track_idx"), &Animation::track_get_interpolation_loop_wrap); - ClassDB::bind_method(D_METHOD("transform_track_interpolate", "track_idx", "time_sec"), &Animation::_transform_track_interpolate); ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "track_idx", "mode"), &Animation::value_track_set_update_mode); ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "track_idx"), &Animation::value_track_get_update_mode); @@ -2682,7 +2940,9 @@ void Animation::_bind_methods() { ADD_SIGNAL(MethodInfo("tracks_changed")); BIND_ENUM_CONSTANT(TYPE_VALUE); - BIND_ENUM_CONSTANT(TYPE_TRANSFORM3D); + BIND_ENUM_CONSTANT(TYPE_POSITION_3D); + BIND_ENUM_CONSTANT(TYPE_ROTATION_3D); + BIND_ENUM_CONSTANT(TYPE_SCALE_3D); BIND_ENUM_CONSTANT(TYPE_METHOD); BIND_ENUM_CONSTANT(TYPE_BEZIER); BIND_ENUM_CONSTANT(TYPE_AUDIO); @@ -2709,193 +2969,215 @@ void Animation::clear() { emit_signal(SceneStringNames::get_singleton()->tracks_changed); } -bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm) { - real_t c = (t1.time - t0.time) / (t2.time - t0.time); - real_t t[3] = { -1, -1, -1 }; +bool Animation::_position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm) { + const Vector3 &v0 = t0.value; + const Vector3 &v1 = t1.value; + const Vector3 &v2 = t2.value; - { //translation + if (v0.is_equal_approx(v2)) { + //0 and 2 are close, let's see if 1 is close + if (!v0.is_equal_approx(v1)) { + //not close, not optimizable + return false; + } - const Vector3 &v0 = t0.value.loc; - const Vector3 &v1 = t1.value.loc; - const Vector3 &v2 = t2.value.loc; + } else { + Vector3 pd = (v2 - v0); + real_t d0 = pd.dot(v0); + real_t d1 = pd.dot(v1); + real_t d2 = pd.dot(v2); + if (d1 < d0 || d1 > d2) { + return false; + } - if (v0.is_equal_approx(v2)) { - //0 and 2 are close, let's see if 1 is close - if (!v0.is_equal_approx(v1)) { - //not close, not optimizable - return false; - } + Vector3 s[2] = { v0, v2 }; + real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); - } else { - Vector3 pd = (v2 - v0); - real_t d0 = pd.dot(v0); - real_t d1 = pd.dot(v1); - real_t d2 = pd.dot(v2); - if (d1 < d0 || d1 > d2) { - return false; - } + if (d > pd.length() * p_allowed_linear_err) { + return false; //beyond allowed error for collinearity + } - Vector3 s[2] = { v0, v2 }; - real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); + if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_allowed_angular_error) { + return false; + } + } - if (d > pd.length() * p_alowed_linear_err) { - return false; //beyond allowed error for collinearity - } + return true; +} - if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_alowed_angular_err) { - return false; - } +bool Animation::_rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle) { + const Quaternion &q0 = t0.value; + const Quaternion &q1 = t1.value; + const Quaternion &q2 = t2.value; + + //localize both to rotation from q0 - t[0] = (d1 - d0) / (d2 - d0); + if (q0.is_equal_approx(q2)) { + if (!q0.is_equal_approx(q1)) { + return false; } - } - { //rotation + } else { + Quaternion r02 = (q0.inverse() * q2).normalized(); + Quaternion r01 = (q0.inverse() * q1).normalized(); - const Quaternion &q0 = t0.value.rot; - const Quaternion &q1 = t1.value.rot; - const Quaternion &q2 = t2.value.rot; + Vector3 v02, v01; + real_t a02, a01; - //localize both to rotation from q0 + r02.get_axis_angle(v02, a02); + r01.get_axis_angle(v01, a01); - if (q0.is_equal_approx(q2)) { - if (!q0.is_equal_approx(q1)) { - return false; - } + if (Math::abs(a02) > p_max_optimizable_angle) { + return false; + } - } else { - Quaternion r02 = (q0.inverse() * q2).normalized(); - Quaternion r01 = (q0.inverse() * q1).normalized(); + if (v01.dot(v02) < 0) { + //make sure both rotations go the same way to compare + v02 = -v02; + a02 = -a02; + } - Vector3 v02, v01; - real_t a02, a01; + real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized())) / Math_PI; + if (err_01 > p_allowed_angular_error) { + //not rotating in the same axis + return false; + } - r02.get_axis_angle(v02, a02); - r01.get_axis_angle(v01, a01); + if (a01 * a02 < 0) { + //not rotating in the same direction + return false; + } - if (Math::abs(a02) > p_max_optimizable_angle) { - return false; - } + real_t tr = a01 / a02; + if (tr < 0 || tr > 1) { + return false; //rotating too much or too less + } + } - if (v01.dot(v02) < 0) { - //make sure both rotations go the same way to compare - v02 = -v02; - a02 = -a02; - } + return true; +} - real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized())) / Math_PI; - if (err_01 > p_alowed_angular_err) { - //not rotating in the same axis - return false; - } +bool Animation::_scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error) { + const Vector3 &v0 = t0.value; + const Vector3 &v1 = t1.value; + const Vector3 &v2 = t2.value; - if (a01 * a02 < 0) { - //not rotating in the same direction - return false; - } + if (v0.is_equal_approx(v2)) { + //0 and 2 are close, let's see if 1 is close + if (!v0.is_equal_approx(v1)) { + //not close, not optimizable + return false; + } - real_t tr = a01 / a02; - if (tr < 0 || tr > 1) { - return false; //rotating too much or too less - } + } else { + Vector3 pd = (v2 - v0); + real_t d0 = pd.dot(v0); + real_t d1 = pd.dot(v1); + real_t d2 = pd.dot(v2); + if (d1 < d0 || d1 > d2) { + return false; //beyond segment range + } - t[1] = tr; + Vector3 s[2] = { v0, v2 }; + real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); + + if (d > pd.length() * p_allowed_linear_error) { + return false; //beyond allowed error for colinearity } } - { //scale + return true; +} - const Vector3 &v0 = t0.value.scale; - const Vector3 &v1 = t1.value.scale; - const Vector3 &v2 = t2.value.scale; +void Animation::_position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err) { + ERR_FAIL_INDEX(p_idx, tracks.size()); + ERR_FAIL_COND(tracks[p_idx]->type != TYPE_POSITION_3D); + PositionTrack *tt = static_cast<PositionTrack *>(tracks[p_idx]); + bool prev_erased = false; + TKey<Vector3> first_erased; - if (v0.is_equal_approx(v2)) { - //0 and 2 are close, let's see if 1 is close - if (!v0.is_equal_approx(v1)) { - //not close, not optimizable - return false; - } + Vector3 norm; - } else { - Vector3 pd = (v2 - v0); - real_t d0 = pd.dot(v0); - real_t d1 = pd.dot(v1); - real_t d2 = pd.dot(v2); - if (d1 < d0 || d1 > d2) { - return false; //beyond segment range - } + for (int i = 1; i < tt->positions.size() - 1; i++) { + TKey<Vector3> &t0 = tt->positions.write[i - 1]; + TKey<Vector3> &t1 = tt->positions.write[i]; + TKey<Vector3> &t2 = tt->positions.write[i + 1]; - Vector3 s[2] = { v0, v2 }; - real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); + bool erase = _position_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, norm); + if (erase && !prev_erased) { + norm = (t2.value - t1.value).normalized(); + } + + if (prev_erased && !_position_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err, p_allowed_angular_err, norm)) { + //avoid error to go beyond first erased key + erase = false; + } - if (d > pd.length() * p_alowed_linear_err) { - return false; //beyond allowed error for collinearity + if (erase) { + if (!prev_erased) { + first_erased = t1; + prev_erased = true; } - t[2] = (d1 - d0) / (d2 - d0); + tt->positions.remove(i); + i--; + + } else { + prev_erased = false; + norm = Vector3(); } } +} - bool erase = false; - if (t[0] == -1 && t[1] == -1 && t[2] == -1) { - erase = true; - } else { - erase = true; - real_t lt = -1.0; - for (int j = 0; j < 3; j++) { - //search for t on first, one must be it - if (t[j] != -1) { - lt = t[j]; //official t - //validate rest - for (int k = j + 1; k < 3; k++) { - if (t[k] == -1) { - continue; - } +void Animation::_rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) { + ERR_FAIL_INDEX(p_idx, tracks.size()); + ERR_FAIL_COND(tracks[p_idx]->type != TYPE_ROTATION_3D); + RotationTrack *tt = static_cast<RotationTrack *>(tracks[p_idx]); + bool prev_erased = false; + TKey<Quaternion> first_erased; - if (Math::abs(lt - t[k]) > p_alowed_linear_err) { - erase = false; - break; - } - } - break; - } - } + for (int i = 1; i < tt->rotations.size() - 1; i++) { + TKey<Quaternion> &t0 = tt->rotations.write[i - 1]; + TKey<Quaternion> &t1 = tt->rotations.write[i]; + TKey<Quaternion> &t2 = tt->rotations.write[i + 1]; - ERR_FAIL_COND_V(lt == -1, false); + bool erase = _rotation_track_optimize_key(t0, t1, t2, p_allowed_angular_err, p_max_optimizable_angle); + + if (prev_erased && !_rotation_track_optimize_key(t0, first_erased, t2, p_allowed_angular_err, p_max_optimizable_angle)) { + //avoid error to go beyond first erased key + erase = false; + } if (erase) { - if (Math::abs(lt - c) > p_alowed_linear_err) { - //todo, evaluate changing the transition if this fails? - //this could be done as a second pass and would be - //able to optimize more - erase = false; + if (!prev_erased) { + first_erased = t1; + prev_erased = true; } + + tt->rotations.remove(i); + i--; + + } else { + prev_erased = false; } } - - return erase; } -void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) { +void Animation::_scale_track_optimize(int p_idx, real_t p_allowed_linear_err) { ERR_FAIL_INDEX(p_idx, tracks.size()); - ERR_FAIL_COND(tracks[p_idx]->type != TYPE_TRANSFORM3D); - TransformTrack *tt = static_cast<TransformTrack *>(tracks[p_idx]); + ERR_FAIL_COND(tracks[p_idx]->type != TYPE_SCALE_3D); + ScaleTrack *tt = static_cast<ScaleTrack *>(tracks[p_idx]); bool prev_erased = false; - TKey<TransformKey> first_erased; + TKey<Vector3> first_erased; - Vector3 norm; + for (int i = 1; i < tt->scales.size() - 1; i++) { + TKey<Vector3> &t0 = tt->scales.write[i - 1]; + TKey<Vector3> &t1 = tt->scales.write[i]; + TKey<Vector3> &t2 = tt->scales.write[i + 1]; - for (int i = 1; i < tt->transforms.size() - 1; i++) { - TKey<TransformKey> &t0 = tt->transforms.write[i - 1]; - TKey<TransformKey> &t1 = tt->transforms.write[i]; - TKey<TransformKey> &t2 = tt->transforms.write[i + 1]; + bool erase = _scale_track_optimize_key(t0, t1, t2, p_allowed_linear_err); - bool erase = _transform_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm); - if (erase && !prev_erased) { - norm = (t2.value.loc - t1.value.loc).normalized(); - } - - if (prev_erased && !_transform_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm)) { + if (prev_erased && !_scale_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err)) { //avoid error to go beyond first erased key erase = false; } @@ -2906,20 +3188,23 @@ void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err prev_erased = true; } - tt->transforms.remove(i); + tt->scales.remove(i); i--; } else { prev_erased = false; - norm = Vector3(); } } } void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) { for (int i = 0; i < tracks.size(); i++) { - if (tracks[i]->type == TYPE_TRANSFORM3D) { - _transform_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle); + if (tracks[i]->type == TYPE_POSITION_3D) { + _position_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err); + } else if (tracks[i]->type == TYPE_ROTATION_3D) { + _rotation_track_optimize(i, p_allowed_angular_err, p_max_optimizable_angle); + } else if (tracks[i]->type == TYPE_SCALE_3D) { + _scale_track_optimize(i, p_allowed_linear_err); } } } diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 9a410bd566..614e12a560 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -42,7 +42,9 @@ class Animation : public Resource { public: enum TrackType { TYPE_VALUE, ///< Set a value in a property, can be interpolated. - TYPE_TRANSFORM3D, ///< Transform a node or a bone. + TYPE_POSITION_3D, ///< Position 3D track + TYPE_ROTATION_3D, ///< Rotation 3D track + TYPE_SCALE_3D, ///< Scale 3D track TYPE_METHOD, ///< Call any method on a specific node. TYPE_BEZIER, ///< Bezier curve TYPE_AUDIO, @@ -86,21 +88,31 @@ private: T value; }; - struct TransformKey { - Vector3 loc; - Quaternion rot; - Vector3 scale; + const int32_t POSITION_TRACK_SIZE = 5; + const int32_t ROTATION_TRACK_SIZE = 6; + const int32_t SCALE_TRACK_SIZE = 5; + + /* POSITION TRACK */ + + struct PositionTrack : public Track { + Vector<TKey<Vector3>> positions; + + PositionTrack() { type = TYPE_POSITION_3D; } }; - // Not necessarily the same size as Transform3D. The amount of numbers in Animation::Key and TransformKey. - const int32_t TRANSFORM_TRACK_SIZE = 12; + /* ROTATION TRACK */ - /* TRANSFORM TRACK */ + struct RotationTrack : public Track { + Vector<TKey<Quaternion>> rotations; - struct TransformTrack : public Track { - Vector<TKey<TransformKey>> transforms; + RotationTrack() { type = TYPE_ROTATION_3D; } + }; - TransformTrack() { type = TYPE_TRANSFORM3D; } + /* SCALE TRACK */ + + struct ScaleTrack : public Track { + Vector<TKey<Vector3>> scales; + ScaleTrack() { type = TYPE_SCALE_3D; } }; /* PROPERTY VALUE TRACK */ @@ -186,14 +198,11 @@ private: template <class K> inline int _find(const Vector<K> &p_keys, double p_time) const; - _FORCE_INLINE_ Animation::TransformKey _interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const; - _FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const; _FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const; _FORCE_INLINE_ Variant _interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const; _FORCE_INLINE_ real_t _interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const; - _FORCE_INLINE_ Animation::TransformKey _cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const; _FORCE_INLINE_ Vector3 _cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const; _FORCE_INLINE_ Quaternion _cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const; _FORCE_INLINE_ Variant _cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const; @@ -214,18 +223,6 @@ private: // bind helpers private: - Array _transform_track_interpolate(int p_track, double p_time) const { - Vector3 loc; - Quaternion rot; - Vector3 scale; - transform_track_interpolate(p_track, p_time, &loc, &rot, &scale); - Array ret; - ret.push_back(loc); - ret.push_back(rot); - ret.push_back(scale); - return ret; - } - Vector<int> _value_track_get_key_indices(int p_track, double p_time, double p_delta) const { List<int> idxs; value_track_get_key_indices(p_track, p_time, p_delta, &idxs); @@ -247,8 +244,13 @@ private: return idxr; } - bool _transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm); - void _transform_track_optimize(int p_idx, real_t p_allowed_linear_err = 0.05, real_t p_allowed_angular_err = 0.01, real_t p_max_optimizable_angle = Math_PI * 0.125); + bool _position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_alowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm); + bool _rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle); + bool _scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error); + + void _position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err); + void _rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle); + void _scale_track_optimize(int p_idx, real_t p_allowed_linear_err); protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -294,8 +296,18 @@ public: double track_get_key_time(int p_track, int p_key_idx) const; real_t track_get_key_transition(int p_track, int p_key_idx) const; - int transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot = Quaternion(), const Vector3 &p_scale = Vector3()); - Error transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const; + int position_track_insert_key(int p_track, double p_time, const Vector3 &p_position); + Error position_track_get_key(int p_track, int p_key, Vector3 *r_position) const; + Error position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const; + + int rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation); + Error rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const; + Error rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const; + + int scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale); + Error scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const; + Error scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const; + void track_set_interpolation_type(int p_track, InterpolationType p_interp); InterpolationType track_get_interpolation_type(int p_track) const; @@ -324,8 +336,6 @@ public: void track_set_interpolation_loop_wrap(int p_track, bool p_enable); bool track_get_interpolation_loop_wrap(int p_track) const; - Error transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const; - Variant value_track_interpolate(int p_track, double p_time) const; void value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const; void value_track_set_update_mode(int p_track, UpdateMode p_mode); diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp index e615615924..20ebbda256 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.cpp +++ b/scene/resources/skeleton_modification_3d_fabrik.cpp @@ -168,7 +168,7 @@ void SkeletonModification3DFABRIK::_execute(real_t p_delta) { // Apply magnet positions: if (stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx) >= 0) { int parent_bone_idx = stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx); - Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx) * stack->skeleton->get_bone_rest(parent_bone_idx)); + Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx)); local_pose_override.origin += conversion_transform.basis.xform_inv(fabrik_data_chain[i].magnet_position); } else { local_pose_override.origin += fabrik_data_chain[i].magnet_position; diff --git a/scene/resources/skeleton_modification_3d_twoboneik.cpp b/scene/resources/skeleton_modification_3d_twoboneik.cpp index ae7a3bab7e..c1a71148a7 100644 --- a/scene/resources/skeleton_modification_3d_twoboneik.cpp +++ b/scene/resources/skeleton_modification_3d_twoboneik.cpp @@ -455,7 +455,7 @@ void SkeletonModification3DTwoBoneIK::calculate_joint_lengths() { joint_two_length = 0; for (int i = 0; i < bone_two_children.size(); i++) { joint_two_length += bone_two_rest_trans.origin.distance_to( - stack->skeleton->local_pose_to_global_pose(bone_two_children[i], stack->skeleton->get_bone_rest(bone_two_children[i])).origin); + stack->skeleton->get_bone_global_pose(bone_two_children[i]).origin); } joint_two_length = joint_two_length / bone_two_children.size(); } else { diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index f5e52b70e6..275b430f2a 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -3066,6 +3066,7 @@ void TileSetAtlasSource::reset_state() { void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) { texture = p_texture; + _clear_tiles_outside_texture(); emit_changed(); } @@ -3081,6 +3082,7 @@ void TileSetAtlasSource::set_margins(Vector2i p_margins) { margins = p_margins; } + _clear_tiles_outside_texture(); emit_changed(); } Vector2i TileSetAtlasSource::get_margins() const { @@ -3095,6 +3097,7 @@ void TileSetAtlasSource::set_separation(Vector2i p_separation) { separation = p_separation; } + _clear_tiles_outside_texture(); emit_changed(); } Vector2i TileSetAtlasSource::get_separation() const { @@ -3109,6 +3112,7 @@ void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) { texture_region_size = p_tile_size; } + _clear_tiles_outside_texture(); emit_changed(); } Vector2i TileSetAtlasSource::get_texture_region_size() const { @@ -3354,7 +3358,7 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0); bool room_for_tile = has_room_for_tile(p_atlas_coords, p_size, 1, Vector2i(), 1); - ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile, tiles are already present in the space the tile would cover."); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile. The tile is outside the texture or tiles are already present in the space the tile would cover."); // Initialize the tile data. TileAlternativesData tad; @@ -3552,9 +3556,7 @@ bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_s return false; } if (coords.x >= atlas_grid_size.x || coords.y >= atlas_grid_size.y) { - if (!(_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] == p_ignored_tile)) { - return false; // Only accept tiles outside the atlas if they are part of the ignored tile. - } + return false; } } } @@ -3562,6 +3564,33 @@ bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_s return true; } +PackedVector2Array TileSetAtlasSource::get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size) { + // Compute the new atlas grid size. + Size2 new_grid_size; + if (p_texture.is_valid()) { + Size2i valid_area = p_texture->get_size() - p_margins; + + // Compute the number of valid tiles in the tiles atlas + if (valid_area.x >= p_texture_region_size.x && valid_area.y >= p_texture_region_size.y) { + valid_area -= p_texture_region_size; + new_grid_size = Size2i(1, 1) + valid_area / (p_texture_region_size + p_separation); + } + } + + Vector<Vector2> output; + for (KeyValue<Vector2i, TileAlternativesData> &E : tiles) { + for (unsigned int frame = 0; frame < E.value.animation_frames_durations.size(); frame++) { + Vector2i frame_coords = E.key + (E.value.size_in_atlas + E.value.animation_separation) * ((E.value.animation_columns > 0) ? Vector2i(frame % E.value.animation_columns, frame / E.value.animation_columns) : Vector2i(frame, 0)); + frame_coords += E.value.size_in_atlas; + if (frame_coords.x > new_grid_size.x || frame_coords.y > new_grid_size.y) { + output.push_back(E.key); + break; + } + } + } + return output; +} + Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int p_frame) const { ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); ERR_FAIL_INDEX_V(p_frame, (int)tiles[p_atlas_coords].animation_frames_durations.size(), Rect2i()); @@ -3626,34 +3655,6 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_ emit_signal(SNAME("changed")); } -bool TileSetAtlasSource::has_tiles_outside_texture() { - Vector2i grid_size = get_atlas_grid_size(); - Vector<Vector2i> to_remove; - - for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) { - if (E.key.x >= grid_size.x || E.key.y >= grid_size.y) { - return true; - } - } - - return false; -} - -void TileSetAtlasSource::clear_tiles_outside_texture() { - Vector2i grid_size = get_atlas_grid_size(); - Vector<Vector2i> to_remove; - - for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) { - if (E.key.x >= grid_size.x || E.key.y >= grid_size.y) { - to_remove.append(E.key); - } - } - - for (int i = 0; i < to_remove.size(); i++) { - remove_tile(to_remove[i]); - } -} - int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override) { ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && tiles[p_atlas_coords].alternatives.has(p_alternative_id_override), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override)); @@ -3754,7 +3755,7 @@ void TileSetAtlasSource::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas); ClassDB::bind_method(D_METHOD("has_room_for_tile", "atlas_coords", "size", "animation_columns", "animation_separation", "frames_count", "ignored_tile"), &TileSetAtlasSource::has_room_for_tile, DEFVAL(INVALID_ATLAS_COORDS)); - + ClassDB::bind_method(D_METHOD("get_tiles_to_be_removed_on_change", "texture", "margins", "separation", "texture_region_size"), &TileSetAtlasSource::get_tiles_to_be_removed_on_change); ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords); ClassDB::bind_method(D_METHOD("set_tile_animation_columns", "atlas_coords", "frame_columns"), &TileSetAtlasSource::set_tile_animation_columns); @@ -3779,8 +3780,6 @@ void TileSetAtlasSource::_bind_methods() { // Helpers. ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size); - ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture); - ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture); ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_tile_texture_region, DEFVAL(0)); } @@ -3854,6 +3853,20 @@ void TileSetAtlasSource::_create_coords_mapping_cache(Vector2i p_atlas_coords) { } } +void TileSetAtlasSource::_clear_tiles_outside_texture() { + LocalVector<Vector2i> to_remove; + + for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) { + if (!has_room_for_tile(E.key, E.value.size_in_atlas, E.value.animation_columns, E.value.animation_separation, E.value.animation_frames_durations.size(), E.key)) { + to_remove.push_back(E.key); + } + } + + for (unsigned int i = 0; i < to_remove.size(); i++) { + remove_tile(to_remove[i]); + } +} + /////////////////////////////// TileSetScenesCollectionSource ////////////////////////////////////// void TileSetScenesCollectionSource::_compute_next_alternative_id() { diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 716b66405f..351bdff89d 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -479,8 +479,10 @@ private: void _compute_next_alternative_id(const Vector2i p_atlas_coords); - void _create_coords_mapping_cache(Vector2i p_atlas_coords); void _clear_coords_mapping_cache(Vector2i p_atlas_coords); + void _create_coords_mapping_cache(Vector2i p_atlas_coords); + + void _clear_tiles_outside_texture(); protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -534,7 +536,7 @@ public: virtual Vector2i get_tile_id(int p_index) const override; bool has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile = INVALID_ATLAS_COORDS) const; - + PackedVector2Array get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size); Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const; // Animation. @@ -565,8 +567,6 @@ public: // Helpers. Vector2i get_atlas_grid_size() const; - bool has_tiles_outside_texture(); - void clear_tiles_outside_texture(); Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const; Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const; diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 934d16bd7e..0dc83b4bb8 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -113,6 +113,10 @@ bool VisualShaderNode::is_output_port_expandable(int p_port) const { return false; } +bool VisualShaderNode::has_output_port_preview(int p_port) const { + return true; +} + void VisualShaderNode::_set_output_ports_expanded(const Array &p_values) { for (int i = 0; i < p_values.size(); i++) { expanded_output_ports[p_values[i]] = true; @@ -2307,7 +2311,21 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { }; const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { + // Spatial, Vertex + + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Spatial, Fragment + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" }, @@ -2315,44 +2333,59 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "1.0" }, - - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, // Spatial, Light - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Canvas Item, Vertex + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Canvas Item, Fragment + + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Canvas Item, Light + + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Sky + + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Particles + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr }, }; @@ -2413,13 +2446,10 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T case PORT_TYPE_VECTOR: { code = " " + p_output_vars[0] + " = vec3(0.0);\n"; } break; - case PORT_TYPE_TRANSFORM: { - code = " " + p_output_vars[0] + " = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; - } break; case PORT_TYPE_BOOLEAN: { code = " " + p_output_vars[0] + " = false;\n"; } break; - default: //default (none found) is scalar + default: break; } } diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index b3efac02aa..f910f28536 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -254,6 +254,8 @@ public: void set_input_port_connected(int p_port, bool p_connected); virtual bool is_generate_input_var(int p_port) const; + virtual bool has_output_port_preview(int p_port) const; + virtual bool is_output_port_expandable(int p_port) const; void _set_output_ports_expanded(const Array &p_data); Array _get_output_ports_expanded() const; diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp index 5fe801e037..18b933e5cf 100644 --- a/scene/resources/visual_shader_particle_nodes.cpp +++ b/scene/resources/visual_shader_particle_nodes.cpp @@ -47,6 +47,10 @@ String VisualShaderNodeParticleEmitter::get_output_port_name(int p_port) const { return String(); } +bool VisualShaderNodeParticleEmitter::has_output_port_preview(int p_port) const { + return false; +} + VisualShaderNodeParticleEmitter::VisualShaderNodeParticleEmitter() { } @@ -265,6 +269,10 @@ Vector<StringName> VisualShaderNodeParticleMultiplyByAxisAngle::get_editable_pro return props; } +bool VisualShaderNodeParticleMultiplyByAxisAngle::has_output_port_preview(int p_port) const { + return false; +} + VisualShaderNodeParticleMultiplyByAxisAngle::VisualShaderNodeParticleMultiplyByAxisAngle() { set_input_port_default_value(1, Vector3(1, 0, 0)); set_input_port_default_value(2, 0.0); @@ -313,6 +321,10 @@ String VisualShaderNodeParticleConeVelocity::get_output_port_name(int p_port) co return String(); } +bool VisualShaderNodeParticleConeVelocity::has_output_port_preview(int p_port) const { + return false; +} + String VisualShaderNodeParticleConeVelocity::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; code += " __radians = radians(" + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n"; @@ -421,6 +433,10 @@ VisualShaderNodeParticleRandomness::OpType VisualShaderNodeParticleRandomness::g return op_type; } +bool VisualShaderNodeParticleRandomness::has_output_port_preview(int p_port) const { + return false; +} + VisualShaderNodeParticleRandomness::VisualShaderNodeParticleRandomness() { set_input_port_default_value(0, 0.0); set_input_port_default_value(1, 1.0); @@ -521,6 +537,10 @@ VisualShaderNodeParticleAccelerator::Mode VisualShaderNodeParticleAccelerator::g return mode; } +bool VisualShaderNodeParticleAccelerator::has_output_port_preview(int p_port) const { + return false; +} + VisualShaderNodeParticleAccelerator::VisualShaderNodeParticleAccelerator() { set_input_port_default_value(0, Vector3(1, 1, 1)); set_input_port_default_value(1, 0.0); diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h index f5435c3511..b8bc7992cc 100644 --- a/scene/resources/visual_shader_particle_nodes.h +++ b/scene/resources/visual_shader_particle_nodes.h @@ -42,6 +42,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool has_output_port_preview(int p_port) const override; VisualShaderNodeParticleEmitter(); }; @@ -112,6 +113,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool has_output_port_preview(int p_port) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; @@ -135,6 +137,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool has_output_port_preview(int p_port) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; @@ -168,6 +171,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool has_output_port_preview(int p_port) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; @@ -209,6 +213,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool has_output_port_preview(int p_port) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; diff --git a/servers/rendering/renderer_rd/shader_compiler_rd.cpp b/servers/rendering/renderer_rd/shader_compiler_rd.cpp index 4c26941dae..42d11d8bc1 100644 --- a/servers/rendering/renderer_rd/shader_compiler_rd.cpp +++ b/servers/rendering/renderer_rd/shader_compiler_rd.cpp @@ -1463,6 +1463,7 @@ void ShaderCompilerRD::initialize(DefaultIdentifierActions p_actions) { texture_functions.insert("textureLod"); texture_functions.insert("textureProjLod"); texture_functions.insert("textureGrad"); + texture_functions.insert("textureGather"); texture_functions.insert("textureSize"); texture_functions.insert("texelFetch"); } diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 81a9e00d7c..9c38bf7606 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -2456,6 +2456,23 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, { "textureGrad", TYPE_VEC4, { TYPE_SAMPLERCUBEARRAY, TYPE_VEC4, TYPE_VEC3, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords", "dPdx", "dPdy" }, TAG_GLOBAL, true }, + // textureGather + + { "textureGather", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_VEC4, { TYPE_SAMPLER2D, TYPE_VEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2D, TYPE_VEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2D, TYPE_VEC2, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_VEC4, { TYPE_SAMPLER2DARRAY, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_IVEC4, { TYPE_ISAMPLER2DARRAY, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_UVEC4, { TYPE_USAMPLER2DARRAY, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_VOID }, { "sampler", "coords" }, TAG_GLOBAL, true }, + { "textureGather", TYPE_VEC4, { TYPE_SAMPLERCUBE, TYPE_VEC3, TYPE_INT, TYPE_VOID }, { "sampler", "coords", "comp" }, TAG_GLOBAL, true }, + // dFdx { "dFdx", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID }, { "p" }, TAG_GLOBAL, true }, @@ -2576,6 +2593,20 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "findMSB", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, { "findMSB", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID }, { "value" }, TAG_GLOBAL, true }, + // umulExtended + + { "umulExtended", TYPE_VOID, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + { "umulExtended", TYPE_VOID, { TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_UVEC2, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + { "umulExtended", TYPE_VOID, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + { "umulExtended", TYPE_VOID, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + + // imulExtended + + { "imulExtended", TYPE_VOID, { TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + { "imulExtended", TYPE_VOID, { TYPE_IVEC2, TYPE_IVEC2, TYPE_IVEC2, TYPE_IVEC2, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + { "imulExtended", TYPE_VOID, { TYPE_IVEC3, TYPE_IVEC3, TYPE_IVEC3, TYPE_IVEC3, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + { "imulExtended", TYPE_VOID, { TYPE_IVEC4, TYPE_IVEC4, TYPE_IVEC4, TYPE_IVEC4, TYPE_VOID }, { "x", "y", "msb", "lsb" }, TAG_GLOBAL, true }, + // uaddCarry { "uaddCarry", TYPE_UINT, { TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_VOID }, { "x", "y", "carry" }, TAG_GLOBAL, true }, @@ -2590,15 +2621,37 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "usubBorrow", TYPE_UVEC3, { TYPE_UVEC3, TYPE_UVEC3, TYPE_UVEC3, TYPE_VOID }, { "x", "y", "borrow" }, TAG_GLOBAL, true }, { "usubBorrow", TYPE_UVEC4, { TYPE_UVEC4, TYPE_UVEC4, TYPE_UVEC4, TYPE_VOID }, { "x", "y", "borrow" }, TAG_GLOBAL, true }, + // ldexp + + { "ldexp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_INT, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + { "ldexp", TYPE_VEC2, { TYPE_VEC2, TYPE_IVEC2, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + { "ldexp", TYPE_VEC3, { TYPE_VEC3, TYPE_IVEC3, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + { "ldexp", TYPE_VEC4, { TYPE_VEC4, TYPE_IVEC4, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + + // frexp + + { "frexp", TYPE_FLOAT, { TYPE_FLOAT, TYPE_INT, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + { "frexp", TYPE_VEC2, { TYPE_VEC2, TYPE_IVEC2, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + { "frexp", TYPE_VEC3, { TYPE_VEC3, TYPE_IVEC3, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + { "frexp", TYPE_VEC4, { TYPE_VEC4, TYPE_IVEC4, TYPE_VOID }, { "x", "exp" }, TAG_GLOBAL, true }, + { nullptr, TYPE_VOID, { TYPE_VOID }, { "" }, TAG_GLOBAL, false } }; const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[] = { - //constructors - { "modf", 1 }, - { "uaddCarry", 2 }, - { "usubBorrow", 2 }, - { nullptr, 0 } + { "modf", { 1, -1 } }, + { "umulExtended", { 2, 3 } }, + { "imulExtended", { 2, 3 } }, + { "uaddCarry", { 2, -1 } }, + { "usubBorrow", { 2, -1 } }, + { "ldexp", { 1, -1 } }, + { "frexp", { 1, -1 } }, + { nullptr, { 0, -1 } } +}; + +const ShaderLanguage::BuiltinFuncConstArgs ShaderLanguage::builtin_func_const_args[] = { + { "textureGather", 2, 0, 3 }, + { nullptr, 0, 0, 0 } }; bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) { @@ -2692,100 +2745,152 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI } if (!fail) { - //make sure its not an out argument used in the wrong way - int outarg_idx = 0; - while (builtin_func_out_args[outarg_idx].name) { - if (String(name) == builtin_func_out_args[outarg_idx].name) { - int arg_idx = builtin_func_out_args[outarg_idx].argument; - - if (arg_idx < argcount) { - if (p_func->arguments[arg_idx + 1]->type != Node::TYPE_VARIABLE && p_func->arguments[arg_idx + 1]->type != Node::TYPE_MEMBER && p_func->arguments[arg_idx + 1]->type != Node::TYPE_ARRAY) { - _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member."); - return false; + { + int constarg_idx = 0; + while (builtin_func_const_args[constarg_idx].name) { + if (String(name) == builtin_func_const_args[constarg_idx].name) { + int arg = builtin_func_const_args[constarg_idx].arg + 1; + if (p_func->arguments.size() <= arg) { + break; } - if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) { - ArrayNode *mn = static_cast<ArrayNode *>(p_func->arguments[arg_idx + 1]); - if (mn->is_const) { - fail = true; - } - } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) { - MemberNode *mn = static_cast<MemberNode *>(p_func->arguments[arg_idx + 1]); - if (mn->basetype_const) { - fail = true; + int min = builtin_func_const_args[constarg_idx].min; + int max = builtin_func_const_args[constarg_idx].max; + + bool error = false; + if (p_func->arguments[arg]->type == Node::TYPE_VARIABLE) { + const VariableNode *vn = (VariableNode *)p_func->arguments[arg]; + + bool is_const = false; + ConstantNode::Value value; + + _find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, &is_const, nullptr, nullptr, &value); + if (!is_const || value.sint < min || value.sint > max) { + error = true; } - } else { // TYPE_VARIABLE - VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg_idx + 1]); - if (vn->is_const) { - fail = true; - } else { - StringName varname = vn->name; - if (shader->uniforms.has(varname)) { - fail = true; - } else { - if (shader->varyings.has(varname)) { - _set_error(vformat("Varyings cannot be passed for '%s' parameter!", "out")); - return false; - } - if (p_function_info.built_ins.has(varname)) { - BuiltInInfo info = p_function_info.built_ins[varname]; - if (info.constant) { - fail = true; - } + } else { + if (p_func->arguments[arg]->type == Node::TYPE_CONSTANT) { + ConstantNode *cn = (ConstantNode *)p_func->arguments[arg]; + + if (cn->get_datatype() == TYPE_INT && cn->values.size() == 1) { + int value = cn->values[0].sint; + + if (value < min || value > max) { + error = true; } + } else { + error = true; } + } else { + error = true; } } - if (fail) { - _set_error(vformat("Constant value cannot be passed for '%s' parameter!", "out")); + if (error) { + _set_error(vformat("Expected integer constant within %s..%s range.", min, max)); return false; } + } + constarg_idx++; + } + } - StringName var_name; - if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) { - var_name = static_cast<const ArrayNode *>(p_func->arguments[arg_idx + 1])->name; - } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) { - Node *n = static_cast<const MemberNode *>(p_func->arguments[arg_idx + 1])->owner; - while (n->type == Node::TYPE_MEMBER) { - n = static_cast<const MemberNode *>(n)->owner; - } - if (n->type != Node::TYPE_VARIABLE && n->type != Node::TYPE_ARRAY) { + //make sure its not an out argument used in the wrong way + int outarg_idx = 0; + while (builtin_func_out_args[outarg_idx].name) { + if (String(name) == builtin_func_out_args[outarg_idx].name) { + for (int arg = 0; arg < BuiltinFuncOutArgs::MAX_ARGS; arg++) { + int arg_idx = builtin_func_out_args[outarg_idx].arguments[arg]; + if (arg_idx == -1) { + break; + } + if (arg_idx < argcount) { + if (p_func->arguments[arg_idx + 1]->type != Node::TYPE_VARIABLE && p_func->arguments[arg_idx + 1]->type != Node::TYPE_MEMBER && p_func->arguments[arg_idx + 1]->type != Node::TYPE_ARRAY) { _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member."); return false; } - if (n->type == Node::TYPE_VARIABLE) { - var_name = static_cast<const VariableNode *>(n)->name; - } else { // TYPE_ARRAY - var_name = static_cast<const ArrayNode *>(n)->name; + + if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) { + ArrayNode *mn = static_cast<ArrayNode *>(p_func->arguments[arg_idx + 1]); + if (mn->is_const) { + fail = true; + } + } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) { + MemberNode *mn = static_cast<MemberNode *>(p_func->arguments[arg_idx + 1]); + if (mn->basetype_const) { + fail = true; + } + } else { // TYPE_VARIABLE + VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg_idx + 1]); + if (vn->is_const) { + fail = true; + } else { + StringName varname = vn->name; + if (shader->uniforms.has(varname)) { + fail = true; + } else { + if (shader->varyings.has(varname)) { + _set_error(vformat("Varyings cannot be passed for '%s' parameter!", "out")); + return false; + } + if (p_function_info.built_ins.has(varname)) { + BuiltInInfo info = p_function_info.built_ins[varname]; + if (info.constant) { + fail = true; + } + } + } + } } - } else { // TYPE_VARIABLE - var_name = static_cast<const VariableNode *>(p_func->arguments[arg_idx + 1])->name; - } - const BlockNode *b = p_block; - bool valid = false; - while (b) { - if (b->variables.has(var_name) || p_function_info.built_ins.has(var_name)) { - valid = true; - break; + if (fail) { + _set_error(vformat("Constant value cannot be passed for '%s' parameter!", "out")); + return false; } - if (b->parent_function) { - for (int i = 0; i < b->parent_function->arguments.size(); i++) { - if (b->parent_function->arguments[i].name == var_name) { - valid = true; - break; + + StringName var_name; + if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_ARRAY) { + var_name = static_cast<const ArrayNode *>(p_func->arguments[arg_idx + 1])->name; + } else if (p_func->arguments[arg_idx + 1]->type == Node::TYPE_MEMBER) { + Node *n = static_cast<const MemberNode *>(p_func->arguments[arg_idx + 1])->owner; + while (n->type == Node::TYPE_MEMBER) { + n = static_cast<const MemberNode *>(n)->owner; + } + if (n->type != Node::TYPE_VARIABLE && n->type != Node::TYPE_ARRAY) { + _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' is not a variable, array or member."); + return false; + } + if (n->type == Node::TYPE_VARIABLE) { + var_name = static_cast<const VariableNode *>(n)->name; + } else { // TYPE_ARRAY + var_name = static_cast<const ArrayNode *>(n)->name; + } + } else { // TYPE_VARIABLE + var_name = static_cast<const VariableNode *>(p_func->arguments[arg_idx + 1])->name; + } + const BlockNode *b = p_block; + bool valid = false; + while (b) { + if (b->variables.has(var_name) || p_function_info.built_ins.has(var_name)) { + valid = true; + break; + } + if (b->parent_function) { + for (int i = 0; i < b->parent_function->arguments.size(); i++) { + if (b->parent_function->arguments[i].name == var_name) { + valid = true; + break; + } } } + b = b->parent_block; } - b = b->parent_block; - } - if (!valid) { - _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' can only take a local variable, array or member."); - return false; + if (!valid) { + _set_error("Argument " + itos(arg_idx + 1) + " of function '" + String(name) + "' can only take a local variable, array or member."); + return false; + } } } } - outarg_idx++; } //implicitly convert values if possible @@ -9157,10 +9262,16 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct } int idx2 = 0; - int out_arg = -1; + Set<int> out_args; while (builtin_func_out_args[idx2].name != nullptr) { if (builtin_func_out_args[idx2].name == builtin_func_defs[idx].name) { - out_arg = builtin_func_out_args[idx2].argument; + for (int i = 0; i < BuiltinFuncOutArgs::MAX_ARGS; i++) { + int arg = builtin_func_out_args[idx2].arguments[i]; + if (arg == -1) { + break; + } + out_args.insert(arg); + } break; } idx2++; @@ -9197,7 +9308,7 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct calltip += char32_t(0xFFFF); } - if (out_arg >= 0 && i == out_arg) { + if (out_args.has(i)) { calltip += "out "; } diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 4740da0c37..c82f71d10d 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -949,8 +949,16 @@ private: }; struct BuiltinFuncOutArgs { //arguments used as out in built in functions + enum { MAX_ARGS = 2 }; const char *name; - int argument; + const int arguments[MAX_ARGS]; + }; + + struct BuiltinFuncConstArgs { + const char *name; + int arg; + int min; + int max; }; CompletionType completion_type; @@ -966,6 +974,7 @@ private: bool _get_completable_identifier(BlockNode *p_block, CompletionType p_type, StringName &identifier); static const BuiltinFuncDef builtin_func_defs[]; static const BuiltinFuncOutArgs builtin_func_out_args[]; + static const BuiltinFuncConstArgs builtin_func_const_args[]; Error _validate_datatype(DataType p_type); bool _compare_datatypes(DataType p_datatype_a, String p_datatype_name_a, int p_array_size_a, DataType p_datatype_b, String p_datatype_name_b, int p_array_size_b); |