Remove animation 3D transform track, replace by loc/rot/scale tracks.

* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.

This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:

* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.

*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
This commit is contained in:
reduz 2021-10-11 19:20:58 -03:00
parent f9aec342dc
commit ec19ed3723
29 changed files with 1952 additions and 1419 deletions

View file

@ -253,6 +253,14 @@
Returns the arguments values to be called on a method track for a given key in a given track. Returns the arguments values to be called on a method track for a given key in a given track.
</description> </description>
</method> </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"> <method name="remove_track">
<return type="void" /> <return type="void" />
<argument index="0" name="track_idx" type="int" /> <argument index="0" name="track_idx" type="int" />
@ -260,6 +268,22 @@
Removes a track by specifying the track index. Removes a track by specifying the track index.
</description> </description>
</method> </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"> <method name="track_find_key" qualifiers="const">
<return type="int" /> <return type="int" />
<argument index="0" name="track_idx" 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]. Swaps the track [code]idx[/code]'s index position with the track [code]with_idx[/code].
</description> </description>
</method> </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"> <method name="value_track_get_key_indices" qualifiers="const">
<return type="PackedInt32Array" /> <return type="PackedInt32Array" />
<argument index="0" name="track_idx" type="int" /> <argument index="0" name="track_idx" type="int" />
@ -541,19 +546,22 @@
<constant name="TYPE_VALUE" value="0" enum="TrackType"> <constant name="TYPE_VALUE" value="0" enum="TrackType">
Value tracks set values in node properties, but only those which can be Interpolated. Value tracks set values in node properties, but only those which can be Interpolated.
</constant> </constant>
<constant name="TYPE_TRANSFORM3D" value="1" enum="TrackType"> <constant name="TYPE_POSITION_3D" value="1" enum="TrackType">
Transform3D tracks are used to change node local transforms or skeleton pose bones of 3D nodes. Transitions are interpolated.
</constant> </constant>
<constant name="TYPE_METHOD" value="2" enum="TrackType"> <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="4" enum="TrackType">
Method tracks call functions with given arguments per key. Method tracks call functions with given arguments per key.
</constant> </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]). 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>
<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. 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>
<constant name="TYPE_ANIMATION" value="5" enum="TrackType"> <constant name="TYPE_ANIMATION" value="7" enum="TrackType">
Animation tracks play animations in other [AnimationPlayer] nodes. Animation tracks play animations in other [AnimationPlayer] nodes.
</constant> </constant>
<constant name="INTERPOLATION_NEAREST" value="0" enum="InterpolationType"> <constant name="INTERPOLATION_NEAREST" value="0" enum="InterpolationType">

View file

@ -22,7 +22,7 @@
<method name="get_root_motion_transform" qualifiers="const"> <method name="get_root_motion_transform" qualifiers="const">
<return type="Transform3D" /> <return type="Transform3D" />
<description> <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> </description>
</method> </method>
<method name="rename_parameter"> <method name="rename_parameter">
@ -45,7 +45,7 @@
</member> </member>
<member name="root_motion_track" type="NodePath" setter="set_root_motion_track" getter="get_root_motion_track" default="NodePath(&quot;&quot;)"> <member name="root_motion_track" type="NodePath" setter="set_root_motion_track" getter="get_root_motion_track" default="NodePath(&quot;&quot;)">
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]. 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>
<member name="tree_root" type="AnimationNode" setter="set_tree_root" getter="get_tree_root"> <member name="tree_root" type="AnimationNode" setter="set_tree_root" getter="get_tree_root">
The root animation node of this [AnimationTree]. See [AnimationNode]. The root animation node of this [AnimationTree]. See [AnimationNode].

View file

@ -144,6 +144,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. 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> </description>
</method> </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"> <method name="get_bone_rest" qualifiers="const">
<return type="Transform3D" /> <return type="Transform3D" />
<argument index="0" name="bone_idx" type="int" /> <argument index="0" name="bone_idx" type="int" />
@ -337,13 +355,25 @@
[b]Note:[/b] [code]parent_idx[/code] must be less than [code]bone_idx[/code]. [b]Note:[/b] [code]parent_idx[/code] must be less than [code]bone_idx[/code].
</description> </description>
</method> </method>
<method name="set_bone_pose"> <method name="set_bone_pose_position">
<return type="void" /> <return type="void" />
<argument index="0" name="bone_idx" type="int" /> <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> <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> </description>
</method> </method>
<method name="set_bone_rest"> <method name="set_bone_rest">

View file

@ -165,20 +165,38 @@ public:
} }
switch (animation->track_get_type(track)) { switch (animation->track_get_type(track)) {
case Animation::TYPE_TRANSFORM3D: { case Animation::TYPE_POSITION_3D:
Dictionary d_old = animation->track_get_key_value(track, key); case Animation::TYPE_ROTATION_3D:
Dictionary d_new = d_old.duplicate(); case Animation::TYPE_SCALE_3D: {
d_new[p_name] = p_value; if (name == "position" || name == "rotation" || name == "scale") {
setting = true; Variant old = animation->track_get_key_value(track, key);
undo_redo->create_action(TTR("Anim Change Transform")); setting = true;
undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new); String chan;
undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old); switch (animation->track_get_type(track)) {
undo_redo->add_do_method(this, "_update_obj", animation); case Animation::TYPE_POSITION_3D:
undo_redo->add_undo_method(this, "_update_obj", animation); chan = "Position3D";
undo_redo->commit_action(); 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; } break;
case Animation::TYPE_VALUE: { case Animation::TYPE_VALUE: {
if (name == "value") { if (name == "value") {
@ -412,12 +430,13 @@ public:
} }
switch (animation->track_get_type(track)) { switch (animation->track_get_type(track)) {
case Animation::TYPE_TRANSFORM3D: { case Animation::TYPE_POSITION_3D:
Dictionary d = animation->track_get_key_value(track, key); case Animation::TYPE_ROTATION_3D:
ERR_FAIL_COND_V(!d.has(name), false); case Animation::TYPE_SCALE_3D: {
r_ret = d[p_name]; if (name == "position" || name == "rotation" || name == "scale") {
return true; r_ret = animation->track_get_key_value(track, key);
return true;
}
} break; } break;
case Animation::TYPE_VALUE: { case Animation::TYPE_VALUE: {
if (name == "value") { if (name == "value") {
@ -523,11 +542,14 @@ public:
} }
switch (animation->track_get_type(track)) { switch (animation->track_get_type(track)) {
case Animation::TYPE_TRANSFORM3D: { case Animation::TYPE_POSITION_3D: {
p_list->push_back(PropertyInfo(Variant::VECTOR3, "location")); p_list->push_back(PropertyInfo(Variant::VECTOR3, "position"));
p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation")); } 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")); p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
} break; } break;
case Animation::TYPE_VALUE: { case Animation::TYPE_VALUE: {
Variant v = animation->track_get_key_value(track, key); Variant v = animation->track_get_key_value(track, key);
@ -779,17 +801,31 @@ public:
} }
switch (animation->track_get_type(track)) { switch (animation->track_get_type(track)) {
case Animation::TYPE_TRANSFORM3D: { case Animation::TYPE_POSITION_3D:
Dictionary d_old = animation->track_get_key_value(track, key); case Animation::TYPE_ROTATION_3D:
Dictionary d_new = d_old.duplicate(); case Animation::TYPE_SCALE_3D: {
d_new[p_name] = p_value; Variant old = animation->track_get_key_value(track, key);
if (!setting) { 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; 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_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, d_old); undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old);
update_obj = true; update_obj = true;
} break; } break;
case Animation::TYPE_VALUE: { case Animation::TYPE_VALUE: {
@ -1009,11 +1045,13 @@ public:
} }
switch (animation->track_get_type(track)) { switch (animation->track_get_type(track)) {
case Animation::TYPE_TRANSFORM3D: { case Animation::TYPE_POSITION_3D:
Dictionary d = animation->track_get_key_value(track, key); case Animation::TYPE_ROTATION_3D:
ERR_FAIL_COND_V(!d.has(name), false); case Animation::TYPE_SCALE_3D: {
r_ret = d[p_name]; if (name == "position" || name == "rotation" || name == "scale") {
return true; r_ret = animation->track_get_key_value(track, key);
return true;
}
} break; } break;
case Animation::TYPE_VALUE: { case Animation::TYPE_VALUE: {
@ -1159,9 +1197,13 @@ public:
if (same_track_type) { if (same_track_type) {
switch (animation->track_get_type(first_track)) { switch (animation->track_get_type(first_track)) {
case Animation::TYPE_TRANSFORM3D: { case Animation::TYPE_POSITION_3D: {
p_list->push_back(PropertyInfo(Variant::VECTOR3, "location")); p_list->push_back(PropertyInfo(Variant::VECTOR3, "position"));
p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation")); } 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")); p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
} break; } break;
case Animation::TYPE_VALUE: { case Animation::TYPE_VALUE: {
@ -1359,7 +1401,9 @@ void AnimationTimelineEdit::_notification(int p_what) {
add_track->get_popup()->clear(); 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("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("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("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")); 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")); Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label")); int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
Color color = get_theme_color(SNAME("font_color"), 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("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("KeyCall"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyAudio"), 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.position.y = int(get_size().height - icon->get_height()) / 2;
interp_mode_rect.size = icon->get_size(); 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); draw_texture(icon, interp_mode_rect.position);
} }
// Make it easier to click. // Make it easier to click.
@ -2031,7 +2077,7 @@ void AnimationTrackEdit::_notification(int p_what) {
ofs += icon->get_width() + hsep; ofs += icon->get_width() + hsep;
interp_mode_rect.size.x += 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)); draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
interp_mode_rect.size.x += down_icon->get_width(); interp_mode_rect.size.x += down_icon->get_width();
} else { } 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.position.y = int(get_size().height - icon->get_height()) / 2;
loop_mode_rect.size = icon->get_size(); 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); draw_texture(icon, loop_mode_rect.position);
} }
@ -2064,7 +2110,7 @@ void AnimationTrackEdit::_notification(int p_what) {
ofs += icon->get_width() + hsep; ofs += icon->get_width() + hsep;
loop_mode_rect.size.x += 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)); draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
loop_mode_rect.size.x += down_icon->get_width(); loop_mode_rect.size.x += down_icon->get_width();
} else { } else {
@ -2284,9 +2330,11 @@ void AnimationTrackEdit::set_animation_and_track(const Ref<Animation> &p_animati
track = p_track; track = p_track;
update(); update();
Ref<Texture2D> type_icons[6] = { Ref<Texture2D> type_icons[8] = {
get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")), 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("KeyCall"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyAudio"), 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) { if (key_idx != -1) {
String text = TTR("Time (s): ") + rtos(animation->track_get_key_time(track, key_idx)) + "\n"; String text = TTR("Time (s): ") + rtos(animation->track_get_key_time(track, key_idx)) + "\n";
switch (animation->track_get_type(track)) { switch (animation->track_get_type(track)) {
case Animation::TYPE_TRANSFORM3D: { case Animation::TYPE_POSITION_3D: {
Dictionary d = animation->track_get_key_value(track, key_idx); Vector3 t = animation->track_get_key_value(track, key_idx);
if (d.has("location")) { text += "Position: " + String(t) + "\n";
text += "Pos: " + String(d["location"]) + "\n"; } break;
} case Animation::TYPE_ROTATION_3D: {
if (d.has("rotation")) { Quaternion t = animation->track_get_key_value(track, key_idx);
text += "Rot: " + String(d["rotation"]) + "\n"; text += "Rotation: " + String(t) + "\n";
} } break;
if (d.has("scale")) { case Animation::TYPE_SCALE_3D: {
text += "Scale: " + String(d["scale"]) + "\n"; Vector3 t = animation->track_get_key_value(track, key_idx);
} text += "Scale: " + String(t) + "\n";
} break; } break;
case Animation::TYPE_VALUE: { case Animation::TYPE_VALUE: {
const Variant &v = animation->track_get_key_value(track, key_idx); 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]]; [[fallthrough]];
case Animation::TYPE_BEZIER: case Animation::TYPE_BEZIER:
[[fallthrough]]; [[fallthrough]];
case Animation::TYPE_TRANSFORM3D: case Animation::TYPE_POSITION_3D:
[[fallthrough]];
case Animation::TYPE_ROTATION_3D:
[[fallthrough]];
case Animation::TYPE_SCALE_3D:
return true; return true;
default: default:
return false; return false;
@ -3483,33 +3535,50 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
NodePath np = path; 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++) { 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) { if (animation->track_get_path(i) != np) {
continue; continue;
} }
track_idx = i; if (animation->track_get_type(i) == Animation::TYPE_POSITION_3D) {
break; 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; InsertData id;
Dictionary val;
id.path = np; 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. // 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.query = vformat(TTR("node '%s'"), p_node->get_name());
id.advance = false; 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) { 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); int track_id = animation->find_track(path);
if (track_id >= 0) { 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; return true;
} }
} }
@ -3977,18 +4046,13 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
Variant value; Variant value;
switch (p_id.type) { switch (p_id.type) {
case Animation::TYPE_POSITION_3D:
case Animation::TYPE_ROTATION_3D:
case Animation::TYPE_SCALE_3D:
case Animation::TYPE_VALUE: { case Animation::TYPE_VALUE: {
value = p_id.value; value = p_id.value;
} break; } 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: { case Animation::TYPE_BEZIER: {
Array array; Array array;
array.resize(5); array.resize(5);
@ -4404,8 +4468,8 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
ERR_FAIL_COND(!node); ERR_FAIL_COND(!node);
NodePath path_to = root->get_path_to(node); NodePath path_to = root->get_path_to(node);
if (adding_track_type == Animation::TYPE_TRANSFORM3D && !node->is_class("Node3D")) { 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("Transform3D tracks only apply to 3D-based nodes.")); EditorNode::get_singleton()->show_warning(TTR("Position/Rotation/Scale 3D tracks only apply to 3D-based nodes."));
return; return;
} }
@ -4415,7 +4479,9 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
prop_selector->set_type_filter(Vector<Variant::Type>()); prop_selector->set_type_filter(Vector<Variant::Type>());
prop_selector->select_property_from_instance(node); prop_selector->select_property_from_instance(node);
} break; } break;
case Animation::TYPE_TRANSFORM3D: case Animation::TYPE_POSITION_3D:
case Animation::TYPE_ROTATION_3D:
case Animation::TYPE_SCALE_3D:
case Animation::TYPE_METHOD: { case Animation::TYPE_METHOD: {
undo_redo->create_action(TTR("Add Track")); undo_redo->create_action(TTR("Add Track"));
undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type); undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type);
@ -4584,7 +4650,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
} }
switch (animation->track_get_type(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))) { 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.")); EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key."));
return; return;
@ -4596,14 +4662,50 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
return; return;
} }
Transform3D xf = base->get_transform(); Vector3 pos = base->get_position();
Vector3 loc = xf.get_origin(); undo_redo->create_action(TTR("Add Position Key"));
Vector3 scale = xf.basis.get_scale_local(); undo_redo->add_do_method(animation.ptr(), "position_track_insert_key", p_track, p_ofs, pos);
Quaternion rot = xf.basis; undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
undo_redo->commit_action();
undo_redo->create_action(TTR("Add Transform Track Key")); } break;
undo_redo->add_do_method(animation.ptr(), "transform_track_insert_key", p_track, p_ofs, loc, rot, scale); 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;
}
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;
}
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 scale = base->get_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->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
undo_redo->commit_action(); undo_redo->commit_action();
@ -5277,8 +5379,14 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
} }
switch (animation->track_get_type(i)) { switch (animation->track_get_type(i)) {
case Animation::TYPE_TRANSFORM3D: case Animation::TYPE_POSITION_3D:
text += " (Transform)"; text += " (Position)";
break;
case Animation::TYPE_ROTATION_3D:
text += " (Rotation)";
break;
case Animation::TYPE_SCALE_3D:
text += " (Scale)";
break; break;
case Animation::TYPE_METHOD: case Animation::TYPE_METHOD:
text += " (Methods)"; text += " (Methods)";

View file

@ -35,7 +35,7 @@
#include "editor/editor_spin_slider.h" #include "editor/editor_spin_slider.h"
#include "editor/property_editor.h" #include "editor/property_editor.h"
#include "editor/property_selector.h" #include "editor/property_selector.h"
#include "scene/animation/animation_cache.h"
#include "scene/gui/control.h" #include "scene/gui/control.h"
#include "scene/gui/file_dialog.h" #include "scene/gui/file_dialog.h"
#include "scene/gui/menu_button.h" #include "scene/gui/menu_button.h"

View file

@ -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>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -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>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -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>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -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>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -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>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -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>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -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 _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); 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 _fix_param_animation_tracks();
void create_animation(int p_clip, 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_make_tracks_in_all_bones, bool p_import_value_tracks); void create_animations(bool p_import_value_tracks);
Set<String> tracks_in_clips; Set<String> tracks_in_clips;
Vector<String> missing_textures; Vector<String> missing_textures;
@ -1384,7 +1384,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(); _fix_param_animation_tracks();
for (int i = 0; i < collada.state.animation_clips.size(); i++) { for (int i = 0; i < collada.state.animation_clips.size(); i++) {
for (int j = 0; j < collada.state.animation_clips[i].tracks.size(); j++) { for (int j = 0; j < collada.state.animation_clips[i].tracks.size(); j++) {
@ -1417,13 +1417,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++) { 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)); Ref<Animation> animation = Ref<Animation>(memnew(Animation));
if (p_clip == -1) { if (p_clip == -1) {
@ -1522,10 +1522,55 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
continue; continue;
} }
animation->add_track(Animation::TYPE_TRANSFORM3D); bool has_position = false;
int track = animation->get_track_count() - 1; bool has_rotation = false;
animation->track_set_path(track, path); bool has_scale = false;
animation->track_set_imported(track, true); //helps merging later
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; Vector<real_t> snapshots = base_snapshots;
@ -1606,10 +1651,19 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
Vector3 s = xform.basis.get_scale(); 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); 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; 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) { if (nm.bone >= 0) {
@ -1621,48 +1675,15 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
if (found_anim) { if (found_anim) {
tracks_found = true; tracks_found = true;
} else { } else {
animation->remove_track(track); if (position_idx >= 0) {
} animation->remove_track(base_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 (rotation_idx >= 0) {
NodeMap &nm = node_map[E.key]; animation->remove_track(base_track);
String path = scene->get_path_to(nm.node); }
ERR_CONTINUE(nm.bone < 0); if (scale_idx >= 0) {
Skeleton3D *sk = static_cast<Skeleton3D *>(nm.node); animation->remove_track(base_track);
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;
} }
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 +1794,7 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_
} }
if (p_flags & IMPORT_ANIMATION) { if (p_flags & IMPORT_ANIMATION) {
state.create_animations(true, true); state.create_animations(true);
AnimationPlayer *ap = memnew(AnimationPlayer); AnimationPlayer *ap = memnew(AnimationPlayer);
for (int i = 0; i < state.animations.size(); i++) { for (int i = 0; i < state.animations.size(); i++) {
String name; String name;
@ -1800,7 +1821,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); 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 + "'."); 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) { if (state.scene) {
memdelete(state.scene); memdelete(state.scene);
} }

View file

@ -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);
}
}

View file

@ -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

View file

@ -32,7 +32,7 @@
#include "core/io/resource_saver.h" #include "core/io/resource_saver.h"
#include "editor/editor_node.h" #include "editor/editor_node.h"
#include "editor/import/editor_importer_bake_reset.h"
#include "editor/import/scene_import_settings.h" #include "editor/import/scene_import_settings.h"
#include "scene/3d/area_3d.h" #include "scene/3d/area_3d.h"
#include "scene/3d/collision_shape_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)); new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
if (kt > (from + 0.01) && k > 0) { if (kt > (from + 0.01) && k > 0) {
if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) { if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
Quaternion q;
Vector3 p; 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; Vector3 s;
default_anim->transform_track_interpolate(j, from, &p, &q, &s); default_anim->scale_track_interpolate(j, from, &s);
new_anim->transform_track_insert_key(dtrack, 0, p, q, s); new_anim->scale_track_insert_key(dtrack, 0, s);
} } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
Variant var = default_anim->value_track_interpolate(j, from); Variant var = default_anim->value_track_interpolate(j, from);
new_anim->track_insert_key(dtrack, 0, var); new_anim->track_insert_key(dtrack, 0, var);
} }
} }
} }
if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) { if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
Quaternion q;
Vector3 p; 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; Vector3 s;
default_anim->transform_track_get_key(j, k, &p, &q, &s); default_anim->scale_track_get_key(j, k, &s);
new_anim->transform_track_insert_key(dtrack, kt - from, p, q, s); new_anim->scale_track_insert_key(dtrack, kt - from, s);
} } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
Variant var = default_anim->track_get_key_value(j, k); Variant var = default_anim->track_get_key_value(j, k);
new_anim->track_insert_key(dtrack, kt - from, var); new_anim->track_insert_key(dtrack, kt - from, var);
} }
} }
if (dtrack != -1 && kt >= to) { if (dtrack != -1 && kt >= to) {
if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) { if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
Quaternion q;
Vector3 p; 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; Vector3 s;
default_anim->transform_track_interpolate(j, to, &p, &q, &s); default_anim->scale_track_interpolate(j, to, &s);
new_anim->transform_track_insert_key(dtrack, to - from, p, q, s); new_anim->scale_track_insert_key(dtrack, to - from, s);
} } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
Variant var = default_anim->value_track_interpolate(j, to); Variant var = default_anim->value_track_interpolate(j, to);
new_anim->track_insert_key(dtrack, to - from, var); 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)); new_anim->add_track(default_anim->track_get_type(j));
dtrack = new_anim->get_track_count() - 1; dtrack = new_anim->get_track_count() - 1;
new_anim->track_set_path(dtrack, default_anim->track_get_path(j)); new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) { if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
Quaternion q;
Vector3 p; 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; Vector3 s;
default_anim->transform_track_interpolate(j, from, &p, &q, &s); default_anim->scale_track_interpolate(j, from, &s);
new_anim->transform_track_insert_key(dtrack, 0, p, q, s); new_anim->scale_track_insert_key(dtrack, 0, s);
default_anim->transform_track_interpolate(j, to, &p, &q, &s); default_anim->scale_track_interpolate(j, to, &s);
new_anim->transform_track_insert_key(dtrack, to - from, p, q, s); new_anim->scale_track_insert_key(dtrack, to - from, s);
} } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
Variant var = default_anim->value_track_interpolate(j, from); Variant var = default_anim->value_track_interpolate(j, from);
new_anim->track_insert_key(dtrack, 0, var); new_anim->track_insert_key(dtrack, 0, var);
Variant to_var = default_anim->value_track_interpolate(j, to); 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_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_angular_error"), 0.01));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angle"), 22)); 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)); 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++) { 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::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, "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/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::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), "")); 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); _pre_fix_node(scene, scene, collision_map);
_post_fix_node(scene, scene, collision_map, scanned_meshes, node_data, material_data, animation_data, fps); _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"]; 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. root_type = root_type.split(" ")[0]; // full root_type is "ClassName (filename.gd)" for a script global class.

View file

@ -65,6 +65,13 @@ public:
IMPORT_USE_NAMED_SKIN_BINDS = 16, 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 uint32_t get_import_flags() const;
virtual void get_extensions(List<String> *r_extensions) 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); 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);

View file

@ -1305,7 +1305,9 @@ void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gi
t.origin = orig + to_local.xform(sub); t.origin = orig + to_local.xform(sub);
// Apply transform. // 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) { void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {

View file

@ -1011,9 +1011,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
// track count is 5. // track count is 5.
// next track id is 5. // next track id is 5.
const uint64_t target_id = track.key; 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; 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. // 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. // if this is a skeleton mapped track we can just set the path for the track.
// todo: implement node paths here at some // 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 (state.fbx_bone_map.size() > 0 && state.fbx_bone_map.has(target_id)) {
if (bone->fbx_skeleton.is_valid() && bone.is_valid()) { if (bone->fbx_skeleton.is_valid() && bone.is_valid()) {
Ref<FBXSkeleton> fbx_skeleton = bone->fbx_skeleton; Ref<FBXSkeleton> fbx_skeleton = bone->fbx_skeleton;
String bone_path = state.root->get_path_to(fbx_skeleton->skeleton); String bone_path = state.root->get_path_to(fbx_skeleton->skeleton);
bone_path += ":" + fbx_skeleton->skeleton->get_bone_name(bone->godot_bone_id); bone_path += ":" + fbx_skeleton->skeleton->get_bone_name(bone->godot_bone_id);
print_verbose("[doc] track bone path: " + bone_path); print_verbose("[doc] track bone path: " + bone_path);
NodePath path = bone_path; track_path = bone_path;
animation->track_set_path(track_idx, path);
} }
} else if (state.fbx_target_map.has(target_id)) { } else if (state.fbx_target_map.has(target_id)) {
//print_verbose("[doc] we have a valid target for a node animation"); //print_verbose("[doc] we have a valid target for a node animation");
Ref<FBXNode> target_node = state.fbx_target_map[target_id]; Ref<FBXNode> target_node = state.fbx_target_map[target_id];
if (target_node.is_valid() && target_node->godot_node != nullptr) { if (target_node.is_valid() && target_node->godot_node != nullptr) {
String node_path = state.root->get_path_to(target_node->godot_node); String node_path = state.root->get_path_to(target_node->godot_node);
NodePath path = node_path; track_path = node_path;
animation->track_set_path(track_idx, path);
//print_verbose("[doc] node animation path: " + node_path); //print_verbose("[doc] node animation path: " + node_path);
} }
} else { } 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(); 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 + ")"); 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) { while (true) {
Vector3 pos = def_pos; Vector3 pos = def_pos;
Quaternion rot = def_rot; Quaternion rot = def_rot;
@ -1220,7 +1241,15 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
pos = t.origin; pos = t.origin;
} }
animation->transform_track_insert_key(track_idx, time, pos, rot, scale); 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);
}
if (last) { if (last) {
break; break;

View file

@ -5733,7 +5733,7 @@ struct EditorSceneImporterGLTFInterpolate<Quaternion> {
template <class T> 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) { 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()); 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."); ERR_PRINT_ONCE("The interpolated values are not corresponding to its times.");
return p_values[0]; return p_values[0];
} }
@ -5868,9 +5868,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; 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) { if ((track.rotation_track.values.size() || track.position_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) {
//make transform track //make transform track
int track_idx = animation->get_track_count(); int base_idx = animation->get_track_count();
animation->add_track(Animation::TYPE_TRANSFORM3D); int position_idx = -1;
animation->track_set_path(track_idx, transform_node_path); 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 //first determine animation length
const double increment = 1.0 / bake_fps; const double increment = 1.0 / bake_fps;
@ -5880,15 +5938,15 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
Quaternion base_rot; Quaternion base_rot;
Vector3 base_scale = Vector3(1, 1, 1); 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(); 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; 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; base_scale = state->nodes[track_i.key]->scale;
} }
@ -5898,15 +5956,15 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
Quaternion rot = base_rot; Quaternion rot = base_rot;
Vector3 scale = base_scale; 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); pos = _interpolate_track<Vector3>(track.position_track.times, track.position_track.values, time, track.position_track.interpolation);
} }
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); rot = _interpolate_track<Quaternion>(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation);
} }
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); scale = _interpolate_track<Vector3>(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation);
} }
@ -5925,7 +5983,15 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
pos = xform.origin; pos = xform.origin;
} }
animation->transform_track_insert_key(track_idx, time, pos, rot, scale); 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);
}
if (last) { if (last) {
break; break;
@ -6146,6 +6212,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) { 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); Animation::InterpolationType interpolation = p_animation->track_get_interpolation_type(p_track_i);
GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR; GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
@ -6164,6 +6234,8 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
for (int32_t key_i = 0; key_i < key_count; key_i++) { 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); times.write[key_i] = p_animation->track_get_key_time(p_track_i, key_i);
} }
if (track_type == Animation::TYPE_TRANSFORM3D) { if (track_type == Animation::TYPE_TRANSFORM3D) {
p_track.position_track.times = times; p_track.position_track.times = times;
p_track.position_track.interpolation = gltf_interpolation; p_track.position_track.interpolation = gltf_interpolation;
@ -6323,7 +6395,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
} }
} }
} }
#endif
return p_track; return p_track;
} }

View file

@ -99,8 +99,12 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
set_bone_rest(which, p_value); set_bone_rest(which, p_value);
} else if (what == "enabled") { } else if (what == "enabled") {
set_bone_enabled(which, p_value); set_bone_enabled(which, p_value);
} else if (what == "pose") { } else if (what == "position") {
set_bone_pose(which, p_value); 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 { } else {
return false; return false;
} }
@ -135,8 +139,12 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
r_ret = get_bone_rest(which); r_ret = get_bone_rest(which);
} else if (what == "enabled") { } else if (what == "enabled") {
r_ret = is_bone_enabled(which); r_ret = is_bone_enabled(which);
} else if (what == "pose") { } else if (what == "position") {
r_ret = get_bone_pose(which); 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 { } else {
return false; 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::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::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::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 #ifndef _3D_DISABLED
@ -657,19 +667,60 @@ void Skeleton3D::clear_bones() {
// Posing api // 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(); const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_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()) { if (is_inside_tree()) {
_make_dirty(); _make_dirty();
} }
} }
void Skeleton3D::set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
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);
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;
}
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;
}
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 { Transform3D Skeleton3D::get_bone_pose(int p_bone) const {
const int bone_size = bones.size(); const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
return bones[p_bone].pose; ((Skeleton3D *)this)->bones.write[p_bone].update_pose_cache();
return bones[p_bone].pose_cache;
} }
void Skeleton3D::set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose) { void Skeleton3D::set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose) {
@ -989,7 +1040,8 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
if (b.disable_rest) { if (b.disable_rest) {
if (bone_enabled) { if (bone_enabled) {
Transform3D pose = b.pose; b.update_pose_cache();
Transform3D pose = b.pose_cache;
if (b.custom_pose_enable) { if (b.custom_pose_enable) {
pose = b.custom_pose * pose; pose = b.custom_pose * pose;
} }
@ -1012,7 +1064,8 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
} else { } else {
if (bone_enabled) { if (bone_enabled) {
Transform3D pose = b.pose; b.update_pose_cache();
Transform3D pose = b.pose_cache;
if (b.custom_pose_enable) { if (b.custom_pose_enable) {
pose = b.custom_pose * pose; pose = b.custom_pose * pose;
} }
@ -1193,7 +1246,13 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear_bones"), &Skeleton3D::clear_bones); 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("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("is_bone_enabled", "bone_idx"), &Skeleton3D::is_bone_enabled);
ClassDB::bind_method(D_METHOD("set_bone_enabled", "bone_idx", "enabled"), &Skeleton3D::set_bone_enabled, DEFVAL(true)); ClassDB::bind_method(D_METHOD("set_bone_enabled", "bone_idx", "enabled"), &Skeleton3D::set_bone_enabled, DEFVAL(true));

View file

@ -79,7 +79,19 @@ private:
bool disable_rest = false; bool disable_rest = false;
Transform3D rest; 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;
Transform3D pose_global_no_override; Transform3D pose_global_no_override;
@ -206,9 +218,16 @@ public:
// posing api // 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; Transform3D get_bone_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 set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose); void set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose);
Transform3D get_bone_custom_pose(int p_bone) const; Transform3D get_bone_custom_pose(int p_bone) const;

View file

@ -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() {
}

View file

@ -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

View file

@ -60,7 +60,12 @@ void AnimatedValuesBackup::restore() const {
if (entry->bone_idx == -1) { if (entry->bone_idx == -1) {
entry->object->set_indexed(entry->subpath, entry->value); entry->object->set_indexed(entry->subpath, entry->value);
} else { } 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()); p_anim->node_cache.resize(a->get_track_count());
setup_pass++;
for (int i = 0; i < a->get_track_count(); i++) { for (int i = 0; i < a->get_track_count(); i++) {
p_anim->node_cache.write[i] = nullptr; p_anim->node_cache.write[i] = nullptr;
RES resource; RES resource;
@ -275,46 +282,68 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
node_cache_map[key] = TrackNodeCache(); node_cache_map[key] = TrackNodeCache();
} }
p_anim->node_cache.write[i] = &node_cache_map[key]; TrackNodeCache *node_cache = &node_cache_map[key];
p_anim->node_cache[i]->path = a->track_get_path(i); p_anim->node_cache.write[i] = node_cache;
p_anim->node_cache[i]->node = child;
p_anim->node_cache[i]->resource = resource; node_cache->path = a->track_get_path(i);
p_anim->node_cache[i]->node_2d = Object::cast_to<Node2D>(child); node_cache->node = child;
node_cache->resource = resource;
node_cache->node_2d = Object::cast_to<Node2D>(child);
#ifndef _3D_DISABLED #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 // 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 // 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 // cache skeleton
p_anim->node_cache[i]->skeleton = Object::cast_to<Skeleton3D>(child); node_cache->skeleton = Object::cast_to<Skeleton3D>(child);
if (p_anim->node_cache[i]->skeleton) { if (node_cache->skeleton) {
if (a->track_get_path(i).get_subname_count() == 1) { if (a->track_get_path(i).get_subname_count() == 1) {
StringName bone_name = a->track_get_path(i).get_subname(0); 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); node_cache->bone_idx = node_cache->skeleton->find_bone(bone_name);
if (p_anim->node_cache[i]->bone_idx < 0) { if (node_cache->bone_idx < 0) {
// broken track (nonexistent bone) // broken track (nonexistent bone)
p_anim->node_cache[i]->skeleton = nullptr; node_cache->skeleton = nullptr;
p_anim->node_cache[i]->node_3d = nullptr; node_cache->node_3d = nullptr;
ERR_CONTINUE(p_anim->node_cache[i]->bone_idx < 0); ERR_CONTINUE(node_cache->bone_idx < 0);
} }
} else { } else {
// no property, just use spatialnode // 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 #endif // _3D_DISABLED
if (a->track_get_type(i) == Animation::TYPE_VALUE) { 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; TrackNodeCache::PropertyAnim pa;
pa.subpath = leftover_path; pa.subpath = leftover_path;
pa.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child; pa.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
pa.special = SP_NONE; pa.special = SP_NONE;
pa.owner = p_anim->node_cache[i]; 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) { if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_pos) {
pa.special = SP_NODE2D_POS; pa.special = SP_NODE2D_POS;
} else if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_rot) { } 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; 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 (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; TrackNodeCache::BezierAnim ba;
ba.bezier_property = leftover_path; ba.bezier_property = leftover_path;
ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child; ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
ba.owner = p_anim->node_cache[i]; 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)) { switch (a->track_get_type(i)) {
case Animation::TYPE_TRANSFORM3D: { case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED #ifndef _3D_DISABLED
if (!nc->node_3d) { if (!nc->node_3d) {
continue; continue;
} }
Vector3 loc; 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 //ERR_CONTINUE(err!=OK); //used for testing, should be removed
if (err != OK) { if (err != OK) {
@ -391,12 +420,63 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
cache_update[cache_update_size++] = nc; cache_update[cache_update_size++] = nc;
nc->accum_pass = accum_pass; nc->accum_pass = accum_pass;
nc->loc_accum = loc; nc->loc_accum = loc;
nc->rot_accum = rot; nc->rot_accum = Quaternion();
nc->scale_accum = scale; nc->scale_accum = Vector3();
} else { } else {
nc->loc_accum = nc->loc_accum.lerp(loc, p_interp); 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); 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); nc->scale_accum = nc->scale_accum.lerp(scale, p_interp);
} }
#endif // _3D_DISABLED #endif // _3D_DISABLED
@ -855,15 +935,30 @@ void AnimationPlayer::_animation_update_transforms() {
TrackNodeCache *nc = cache_update[i]; TrackNodeCache *nc = cache_update[i];
ERR_CONTINUE(nc->accum_pass != accum_pass); 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 #ifndef _3D_DISABLED
if (nc->skeleton && nc->bone_idx >= 0) { 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) { } 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 #endif // _3D_DISABLED
} }
} }
@ -1527,7 +1622,12 @@ Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values(Node *p_root_o
AnimatedValuesBackup::Entry entry; AnimatedValuesBackup::Entry entry;
entry.object = nc->skeleton; entry.object = nc->skeleton;
entry.bone_idx = nc->bone_idx; 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); backup->entries.push_back(entry);
} else { } else {
if (nc->node_3d) { if (nc->node_3d) {

View file

@ -88,6 +88,8 @@ private:
SP_NODE2D_SCALE, SP_NODE2D_SCALE,
}; };
uint32_t setup_pass = 1;
struct TrackNodeCache { struct TrackNodeCache {
NodePath path; NodePath path;
uint32_t id = 0; uint32_t id = 0;
@ -101,6 +103,10 @@ private:
int bone_idx = -1; int bone_idx = -1;
// accumulated transforms // accumulated transforms
bool loc_used = false;
bool rot_used = false;
bool scale_used = false;
Vector3 loc_accum; Vector3 loc_accum;
Quaternion rot_accum; Quaternion rot_accum;
Vector3 scale_accum; Vector3 scale_accum;
@ -134,6 +140,7 @@ private:
Map<StringName, BezierAnim> bezier_anim; Map<StringName, BezierAnim> bezier_anim;
uint32_t last_setup_pass = 0;
TrackNodeCache() {} TrackNodeCache() {}
}; };

View file

@ -544,13 +544,18 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
NodePath path = anim->track_get_path(i); NodePath path = anim->track_get_path(i);
Animation::TrackType track_type = anim->track_get_type(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; TrackCache *track = nullptr;
if (track_cache.has(path)) { if (track_cache.has(path)) {
track = track_cache.get(path); track = track_cache.get(path);
} }
//if not valid, delete track //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); playing_caches.erase(track);
memdelete(track); memdelete(track);
track_cache.erase(path); track_cache.erase(path);
@ -587,7 +592,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track = track_value; track = track_value;
} break; } break;
case Animation::TYPE_TRANSFORM3D: { case Animation::TYPE_POSITION_3D:
case Animation::TYPE_ROTATION_3D:
case Animation::TYPE_SCALE_3D: {
#ifndef _3D_DISABLED #ifndef _3D_DISABLED
Node3D *node_3d = Object::cast_to<Node3D>(child); Node3D *node_3d = Object::cast_to<Node3D>(child);
@ -597,6 +604,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
} }
TrackCacheTransform *track_xform = memnew(TrackCacheTransform); TrackCacheTransform *track_xform = memnew(TrackCacheTransform);
track_xform->type = Animation::TYPE_POSITION_3D;
track_xform->node_3d = node_3d; track_xform->node_3d = node_3d;
track_xform->skeleton = nullptr; 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_xform->object_id = track_xform->object->get_instance_id();
track = track_xform; 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 #endif // _3D_DISABLED
} break; } break;
case Animation::TYPE_METHOD: { case Animation::TYPE_METHOD: {
@ -670,6 +693,26 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
} }
track_cache[path] = track; 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; track->setup_pass = setup_pass;
@ -831,8 +874,11 @@ void AnimationTree::_process_graph(real_t p_delta) {
ERR_CONTINUE(!track_cache.has(path)); ERR_CONTINUE(!track_cache.has(path));
TrackCache *track = track_cache[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; track->root_motion = root_motion_track == path;
@ -848,20 +894,20 @@ void AnimationTree::_process_graph(real_t p_delta) {
continue; //nothing to blend continue; //nothing to blend
} }
switch (track->type) { switch (ttype) {
case Animation::TYPE_TRANSFORM3D: { case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED #ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
if (track->root_motion) { if (t->process_pass != process_pass) {
if (t->process_pass != process_pass) { t->process_pass = process_pass;
t->process_pass = process_pass; t->loc = Vector3();
t->loc = Vector3(); t->rot = Quaternion();
t->rot = Quaternion(); t->rot_blend_accum = 0;
t->rot_blend_accum = 0; t->scale = Vector3(1, 1, 1);
t->scale = Vector3(1, 1, 1); }
}
if (track->root_motion) {
real_t prev_time = time - delta; real_t prev_time = time - delta;
if (prev_time < 0) { if (prev_time < 0) {
if (!a->has_loop()) { if (!a->has_loop()) {
@ -872,60 +918,104 @@ void AnimationTree::_process_graph(real_t p_delta) {
} }
Vector3 loc[2]; Vector3 loc[2];
Quaternion rot[2];
Vector3 scale[2];
if (prev_time > time) { if (prev_time > time) {
Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]); Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
if (err != OK) { if (err != OK) {
continue; continue;
} }
a->transform_track_interpolate(i, a->get_length(), &loc[1], &rot[1], &scale[1]); a->position_track_interpolate(i, a->get_length(), &loc[1]);
t->loc += (loc[1] - loc[0]) * blend; 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; prev_time = 0;
} }
Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]); Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
if (err != OK) { if (err != OK) {
continue; continue;
} }
a->transform_track_interpolate(i, time, &loc[1], &rot[1], &scale[1]); a->position_track_interpolate(i, time, &loc[1]);
t->loc += (loc[1] - loc[0]) * blend; 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; prev_time = 0;
} else { } else {
Vector3 loc; Vector3 loc;
Quaternion rot;
Vector3 scale;
Error err = a->transform_track_interpolate(i, time, &loc, &rot, &scale); Error err = a->position_track_interpolate(i, time, &loc);
//ERR_CONTINUE(err!=OK); //used for testing, should be removed //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) { if (err != OK) {
continue; continue;
} }
t->loc = t->loc.lerp(loc, blend); 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()) {
prev_time = 0;
} else {
prev_time = a->get_length() + prev_time;
}
}
Quaternion rot[2];
if (prev_time > time) {
Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
if (err != OK) {
continue;
}
a->rotation_track_interpolate(i, a->get_length(), &rot[1]);
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->rotation_track_interpolate(i, prev_time, &rot[0]);
if (err != OK) {
continue;
}
a->rotation_track_interpolate(i, time, &rot[1]);
Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
t->rot = (t->rot * q).normalized();
prev_time = 0;
} else {
Quaternion rot;
Error err = a->rotation_track_interpolate(i, time, &rot);
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
if (err != OK) {
continue;
}
if (t->rot_blend_accum == 0) { if (t->rot_blend_accum == 0) {
t->rot = rot; t->rot = rot;
t->rot_blend_accum = blend; 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 = rot.slerp(t->rot, t->rot_blend_accum / rot_total).normalized();
t->rot_blend_accum = rot_total; 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); t->scale = t->scale.lerp(scale, blend);
} }
#endif // _3D_DISABLED #endif // _3D_DISABLED
@ -1198,26 +1349,41 @@ void AnimationTree::_process_graph(real_t p_delta) {
} }
switch (track->type) { switch (track->type) {
case Animation::TYPE_TRANSFORM3D: { case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED #ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); 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) { if (t->root_motion) {
Transform3D xform;
xform.origin = t->loc;
xform.basis.set_quaternion_scale(t->rot, t->scale);
root_motion_transform = xform; root_motion_transform = xform;
if (t->skeleton && t->bone_idx >= 0) { 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(); 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) { } 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) { } 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 #endif // _3D_DISABLED
} break; } break;

View file

@ -197,13 +197,16 @@ private:
Skeleton3D *skeleton = nullptr; Skeleton3D *skeleton = nullptr;
#endif // _3D_DISABLED #endif // _3D_DISABLED
int bone_idx = -1; int bone_idx = -1;
bool loc_used = false;
bool rot_used = false;
bool scale_used = false;
Vector3 loc; Vector3 loc;
Quaternion rot; Quaternion rot;
real_t rot_blend_accum = 0.0; real_t rot_blend_accum = 0.0;
Vector3 scale; Vector3 scale;
TrackCacheTransform() { TrackCacheTransform() {
type = Animation::TYPE_TRANSFORM3D; type = Animation::TYPE_POSITION_3D;
} }
}; };

File diff suppressed because it is too large Load diff

View file

@ -42,7 +42,9 @@ class Animation : public Resource {
public: public:
enum TrackType { enum TrackType {
TYPE_VALUE, ///< Set a value in a property, can be interpolated. 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_METHOD, ///< Call any method on a specific node.
TYPE_BEZIER, ///< Bezier curve TYPE_BEZIER, ///< Bezier curve
TYPE_AUDIO, TYPE_AUDIO,
@ -86,21 +88,31 @@ private:
T value; T value;
}; };
struct TransformKey { const int32_t POSITION_TRACK_SIZE = 5;
Vector3 loc; const int32_t ROTATION_TRACK_SIZE = 6;
Quaternion rot; const int32_t SCALE_TRACK_SIZE = 5;
Vector3 scale;
/* 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. /* ROTATION TRACK */
const int32_t TRANSFORM_TRACK_SIZE = 12;
/* TRANSFORM TRACK */ struct RotationTrack : public Track {
Vector<TKey<Quaternion>> rotations;
struct TransformTrack : public Track { RotationTrack() { type = TYPE_ROTATION_3D; }
Vector<TKey<TransformKey>> transforms; };
TransformTrack() { type = TYPE_TRANSFORM3D; } /* SCALE TRACK */
struct ScaleTrack : public Track {
Vector<TKey<Vector3>> scales;
ScaleTrack() { type = TYPE_SCALE_3D; }
}; };
/* PROPERTY VALUE TRACK */ /* PROPERTY VALUE TRACK */
@ -186,14 +198,11 @@ private:
template <class K> template <class K>
inline int _find(const Vector<K> &p_keys, double p_time) const; 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_ 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_ 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_ 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_ 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_ 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_ 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; _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 // bind helpers
private: 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 { Vector<int> _value_track_get_key_indices(int p_track, double p_time, double p_delta) const {
List<int> idxs; List<int> idxs;
value_track_get_key_indices(p_track, p_time, p_delta, &idxs); value_track_get_key_indices(p_track, p_time, p_delta, &idxs);
@ -247,8 +244,13 @@ private:
return idxr; 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); 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);
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 _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: protected:
bool _set(const StringName &p_name, const Variant &p_value); 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; 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; 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()); int position_track_insert_key(int p_track, double p_time, const Vector3 &p_position);
Error transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const; 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); void track_set_interpolation_type(int p_track, InterpolationType p_interp);
InterpolationType track_get_interpolation_type(int p_track) const; 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); void track_set_interpolation_loop_wrap(int p_track, bool p_enable);
bool track_get_interpolation_loop_wrap(int p_track) const; 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; 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_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); void value_track_set_update_mode(int p_track, UpdateMode p_mode);