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:
parent
f9aec342dc
commit
ec19ed3723
|
@ -253,6 +253,14 @@
|
|||
Returns the arguments values to be called on a method track for a given key in a given track.
|
||||
</description>
|
||||
</method>
|
||||
<method name="position_track_insert_key">
|
||||
<return type="int" />
|
||||
<argument index="0" name="track_idx" type="int" />
|
||||
<argument index="1" name="time" type="float" />
|
||||
<argument index="2" name="position" type="Vector3" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="remove_track">
|
||||
<return type="void" />
|
||||
<argument index="0" name="track_idx" type="int" />
|
||||
|
@ -260,6 +268,22 @@
|
|||
Removes a track by specifying the track index.
|
||||
</description>
|
||||
</method>
|
||||
<method name="rotation_track_insert_key">
|
||||
<return type="int" />
|
||||
<argument index="0" name="track_idx" type="int" />
|
||||
<argument index="1" name="time" type="float" />
|
||||
<argument index="2" name="rotation" type="Quaternion" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="scale_track_insert_key">
|
||||
<return type="int" />
|
||||
<argument index="0" name="track_idx" type="int" />
|
||||
<argument index="1" name="time" type="float" />
|
||||
<argument index="2" name="scale" type="Vector3" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="track_find_key" qualifiers="const">
|
||||
<return type="int" />
|
||||
<argument index="0" name="track_idx" type="int" />
|
||||
|
@ -466,25 +490,6 @@
|
|||
Swaps the track [code]idx[/code]'s index position with the track [code]with_idx[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="transform_track_insert_key">
|
||||
<return type="int" />
|
||||
<argument index="0" name="track_idx" type="int" />
|
||||
<argument index="1" name="time" type="float" />
|
||||
<argument index="2" name="location" type="Vector3" />
|
||||
<argument index="3" name="rotation" type="Quaternion" />
|
||||
<argument index="4" name="scale" type="Vector3" />
|
||||
<description>
|
||||
Insert a transform key for a transform track.
|
||||
</description>
|
||||
</method>
|
||||
<method name="transform_track_interpolate" qualifiers="const">
|
||||
<return type="Array" />
|
||||
<argument index="0" name="track_idx" type="int" />
|
||||
<argument index="1" name="time_sec" type="float" />
|
||||
<description>
|
||||
Returns the interpolated value of a transform track at a given time (in seconds). An array consisting of 3 elements: position ([Vector3]), rotation ([Quaternion]) and scale ([Vector3]).
|
||||
</description>
|
||||
</method>
|
||||
<method name="value_track_get_key_indices" qualifiers="const">
|
||||
<return type="PackedInt32Array" />
|
||||
<argument index="0" name="track_idx" type="int" />
|
||||
|
@ -541,19 +546,22 @@
|
|||
<constant name="TYPE_VALUE" value="0" enum="TrackType">
|
||||
Value tracks set values in node properties, but only those which can be Interpolated.
|
||||
</constant>
|
||||
<constant name="TYPE_TRANSFORM3D" value="1" enum="TrackType">
|
||||
Transform3D tracks are used to change node local transforms or skeleton pose bones of 3D nodes. Transitions are interpolated.
|
||||
<constant name="TYPE_POSITION_3D" value="1" enum="TrackType">
|
||||
</constant>
|
||||
<constant name="TYPE_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.
|
||||
</constant>
|
||||
<constant name="TYPE_BEZIER" value="3" enum="TrackType">
|
||||
<constant name="TYPE_BEZIER" value="5" enum="TrackType">
|
||||
Bezier tracks are used to interpolate a value using custom curves. They can also be used to animate sub-properties of vectors and colors (e.g. alpha value of a [Color]).
|
||||
</constant>
|
||||
<constant name="TYPE_AUDIO" value="4" enum="TrackType">
|
||||
<constant name="TYPE_AUDIO" value="6" enum="TrackType">
|
||||
Audio tracks are used to play an audio stream with either type of [AudioStreamPlayer]. The stream can be trimmed and previewed in the animation.
|
||||
</constant>
|
||||
<constant name="TYPE_ANIMATION" value="5" enum="TrackType">
|
||||
<constant name="TYPE_ANIMATION" value="7" enum="TrackType">
|
||||
Animation tracks play animations in other [AnimationPlayer] nodes.
|
||||
</constant>
|
||||
<constant name="INTERPOLATION_NEAREST" value="0" enum="InterpolationType">
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<method name="get_root_motion_transform" qualifiers="const">
|
||||
<return type="Transform3D" />
|
||||
<description>
|
||||
Retrieve the motion of the [member root_motion_track] as a [Transform3D] that can be used elsewhere. If [member root_motion_track] is not a path to a track of type [constant Animation.TYPE_TRANSFORM3D], returns an identity transformation. See also [member root_motion_track] and [RootMotionView].
|
||||
Retrieve the motion of the [member root_motion_track] as a [Transform3D] that can be used elsewhere. If [member root_motion_track] is not a path to a track of type [constant Animation.TYPE_POSITION_3D], [constant Animation.TYPE_SCALE_3D] or [constant Animation.TYPE_ROTATION_3D], returns an identity transformation. See also [member root_motion_track] and [RootMotionView].
|
||||
</description>
|
||||
</method>
|
||||
<method name="rename_parameter">
|
||||
|
@ -45,7 +45,7 @@
|
|||
</member>
|
||||
<member name="root_motion_track" type="NodePath" setter="set_root_motion_track" getter="get_root_motion_track" default="NodePath("")">
|
||||
The path to the Animation track used for root motion. Paths must be valid scene-tree paths to a node, and must be specified starting from the parent node of the node that will reproduce the animation. To specify a track that controls properties or bones, append its name after the path, separated by [code]":"[/code]. For example, [code]"character/skeleton:ankle"[/code] or [code]"character/mesh:transform/local"[/code].
|
||||
If the track has type [constant Animation.TYPE_TRANSFORM3D], the transformation will be cancelled visually, and the animation will appear to stay in place. See also [method get_root_motion_transform] and [RootMotionView].
|
||||
If the track has type [constant Animation.TYPE_POSITION_3D], [constant Animation.TYPE_ROTATION_3D] or [constant Animation.TYPE_SCALE_3D] the transformation will be cancelled visually, and the animation will appear to stay in place. See also [method get_root_motion_transform] and [RootMotionView].
|
||||
</member>
|
||||
<member name="tree_root" type="AnimationNode" setter="set_tree_root" getter="get_tree_root">
|
||||
The root animation node of this [AnimationTree]. See [AnimationNode].
|
||||
|
|
|
@ -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.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_bone_pose_position" qualifiers="const">
|
||||
<return type="Vector3" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_bone_pose_rotation" qualifiers="const">
|
||||
<return type="Quaternion" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_bone_pose_scale" qualifiers="const">
|
||||
<return type="Vector3" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_bone_rest" qualifiers="const">
|
||||
<return type="Transform3D" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
|
@ -337,13 +355,25 @@
|
|||
[b]Note:[/b] [code]parent_idx[/code] must be less than [code]bone_idx[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_bone_pose">
|
||||
<method name="set_bone_pose_position">
|
||||
<return type="void" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
<argument index="1" name="pose" type="Transform3D" />
|
||||
<argument index="1" name="position" type="Vector3" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_bone_pose_rotation">
|
||||
<return type="void" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
<argument index="1" name="rotation" type="Quaternion" />
|
||||
<description>
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_bone_pose_scale">
|
||||
<return type="void" />
|
||||
<argument index="0" name="bone_idx" type="int" />
|
||||
<argument index="1" name="scale" type="Vector3" />
|
||||
<description>
|
||||
Sets the pose transform for bone [code]bone_idx[/code].
|
||||
[b]Note:[/b] The pose transform needs to be in bone space. Use [method world_transform_to_global_pose] to convert a world transform, like one you can get from a [Node3D], to bone space.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_bone_rest">
|
||||
|
|
|
@ -165,20 +165,38 @@ public:
|
|||
}
|
||||
|
||||
switch (animation->track_get_type(track)) {
|
||||
case Animation::TYPE_TRANSFORM3D: {
|
||||
Dictionary d_old = animation->track_get_key_value(track, key);
|
||||
Dictionary d_new = d_old.duplicate();
|
||||
d_new[p_name] = p_value;
|
||||
setting = true;
|
||||
undo_redo->create_action(TTR("Anim Change Transform"));
|
||||
undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
|
||||
undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
|
||||
undo_redo->add_do_method(this, "_update_obj", animation);
|
||||
undo_redo->add_undo_method(this, "_update_obj", animation);
|
||||
undo_redo->commit_action();
|
||||
case Animation::TYPE_POSITION_3D:
|
||||
case Animation::TYPE_ROTATION_3D:
|
||||
case Animation::TYPE_SCALE_3D: {
|
||||
if (name == "position" || name == "rotation" || name == "scale") {
|
||||
Variant old = animation->track_get_key_value(track, key);
|
||||
setting = true;
|
||||
String chan;
|
||||
switch (animation->track_get_type(track)) {
|
||||
case Animation::TYPE_POSITION_3D:
|
||||
chan = "Position3D";
|
||||
break;
|
||||
case Animation::TYPE_ROTATION_3D:
|
||||
chan = "Rotation3D";
|
||||
break;
|
||||
case Animation::TYPE_SCALE_3D:
|
||||
chan = "Scale3D";
|
||||
break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
||||
undo_redo->create_action(vformat(TTR("Anim Change %s"), chan));
|
||||
undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value);
|
||||
undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old);
|
||||
undo_redo->add_do_method(this, "_update_obj", animation);
|
||||
undo_redo->add_undo_method(this, "_update_obj", animation);
|
||||
undo_redo->commit_action();
|
||||
|
||||
setting = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
setting = false;
|
||||
return true;
|
||||
} break;
|
||||
case Animation::TYPE_VALUE: {
|
||||
if (name == "value") {
|
||||
|
@ -412,12 +430,13 @@ public:
|
|||
}
|
||||
|
||||
switch (animation->track_get_type(track)) {
|
||||
case Animation::TYPE_TRANSFORM3D: {
|
||||
Dictionary d = animation->track_get_key_value(track, key);
|
||||
ERR_FAIL_COND_V(!d.has(name), false);
|
||||
r_ret = d[p_name];
|
||||
return true;
|
||||
|
||||
case Animation::TYPE_POSITION_3D:
|
||||
case Animation::TYPE_ROTATION_3D:
|
||||
case Animation::TYPE_SCALE_3D: {
|
||||
if (name == "position" || name == "rotation" || name == "scale") {
|
||||
r_ret = animation->track_get_key_value(track, key);
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
case Animation::TYPE_VALUE: {
|
||||
if (name == "value") {
|
||||
|
@ -523,11 +542,14 @@ public:
|
|||
}
|
||||
|
||||
switch (animation->track_get_type(track)) {
|
||||
case Animation::TYPE_TRANSFORM3D: {
|
||||
p_list->push_back(PropertyInfo(Variant::VECTOR3, "location"));
|
||||
p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation"));
|
||||
case Animation::TYPE_POSITION_3D: {
|
||||
p_list->push_back(PropertyInfo(Variant::VECTOR3, "position"));
|
||||
} break;
|
||||
case Animation::TYPE_ROTATION_3D: {
|
||||
p_list->push_back(PropertyInfo(Variant::VECTOR3, "rotation"));
|
||||
} break;
|
||||
case Animation::TYPE_SCALE_3D: {
|
||||
p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
|
||||
|
||||
} break;
|
||||
case Animation::TYPE_VALUE: {
|
||||
Variant v = animation->track_get_key_value(track, key);
|
||||
|
@ -779,17 +801,31 @@ public:
|
|||
}
|
||||
|
||||
switch (animation->track_get_type(track)) {
|
||||
case Animation::TYPE_TRANSFORM3D: {
|
||||
Dictionary d_old = animation->track_get_key_value(track, key);
|
||||
Dictionary d_new = d_old.duplicate();
|
||||
d_new[p_name] = p_value;
|
||||
|
||||
case Animation::TYPE_POSITION_3D:
|
||||
case Animation::TYPE_ROTATION_3D:
|
||||
case Animation::TYPE_SCALE_3D: {
|
||||
Variant old = animation->track_get_key_value(track, key);
|
||||
if (!setting) {
|
||||
String chan;
|
||||
switch (animation->track_get_type(track)) {
|
||||
case Animation::TYPE_POSITION_3D:
|
||||
chan = "Position3D";
|
||||
break;
|
||||
case Animation::TYPE_ROTATION_3D:
|
||||
chan = "Rotation3D";
|
||||
break;
|
||||
case Animation::TYPE_SCALE_3D:
|
||||
chan = "Scale3D";
|
||||
break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
||||
setting = true;
|
||||
undo_redo->create_action(TTR("Anim Multi Change Transform"));
|
||||
undo_redo->create_action(vformat(TTR("Anim Multi Change %s"), chan));
|
||||
}
|
||||
undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new);
|
||||
undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old);
|
||||
undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value);
|
||||
undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old);
|
||||
update_obj = true;
|
||||
} break;
|
||||
case Animation::TYPE_VALUE: {
|
||||
|
@ -1009,11 +1045,13 @@ public:
|
|||
}
|
||||
|
||||
switch (animation->track_get_type(track)) {
|
||||
case Animation::TYPE_TRANSFORM3D: {
|
||||
Dictionary d = animation->track_get_key_value(track, key);
|
||||
ERR_FAIL_COND_V(!d.has(name), false);
|
||||
r_ret = d[p_name];
|
||||
return true;
|
||||
case Animation::TYPE_POSITION_3D:
|
||||
case Animation::TYPE_ROTATION_3D:
|
||||
case Animation::TYPE_SCALE_3D: {
|
||||
if (name == "position" || name == "rotation" || name == "scale") {
|
||||
r_ret = animation->track_get_key_value(track, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
} break;
|
||||
case Animation::TYPE_VALUE: {
|
||||
|
@ -1159,9 +1197,13 @@ public:
|
|||
|
||||
if (same_track_type) {
|
||||
switch (animation->track_get_type(first_track)) {
|
||||
case Animation::TYPE_TRANSFORM3D: {
|
||||
p_list->push_back(PropertyInfo(Variant::VECTOR3, "location"));
|
||||
p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation"));
|
||||
case Animation::TYPE_POSITION_3D: {
|
||||
p_list->push_back(PropertyInfo(Variant::VECTOR3, "position"));
|
||||
} break;
|
||||
case Animation::TYPE_ROTATION_3D: {
|
||||
p_list->push_back(PropertyInfo(Variant::QUATERNION, "scale"));
|
||||
} break;
|
||||
case Animation::TYPE_SCALE_3D: {
|
||||
p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
|
||||
} break;
|
||||
case Animation::TYPE_VALUE: {
|
||||
|
@ -1359,7 +1401,9 @@ void AnimationTimelineEdit::_notification(int p_what) {
|
|||
|
||||
add_track->get_popup()->clear();
|
||||
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")), TTR("Property Track"));
|
||||
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")), TTR("3D Transform Track"));
|
||||
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")), TTR("3D Position Track"));
|
||||
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")), TTR("3D Rotation Track"));
|
||||
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")), TTR("3D Scale Track"));
|
||||
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")), TTR("Call Method Track"));
|
||||
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), TTR("Bezier Curve Track"));
|
||||
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")), TTR("Audio Playback Track"));
|
||||
|
@ -1828,9 +1872,11 @@ void AnimationTrackEdit::_notification(int p_what) {
|
|||
Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
|
||||
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
|
||||
Color color = get_theme_color(SNAME("font_color"), SNAME("Label"));
|
||||
Ref<Texture2D> type_icons[6] = {
|
||||
Ref<Texture2D> type_icons[8] = {
|
||||
get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")),
|
||||
get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")),
|
||||
get_theme_icon(SNAME("KeyTrackPosition"), SNAME("EditorIcons")),
|
||||
get_theme_icon(SNAME("KeyTrackRotation"), SNAME("EditorIcons")),
|
||||
get_theme_icon(SNAME("KeyTrackScale"), SNAME("EditorIcons")),
|
||||
get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")),
|
||||
get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
|
||||
get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")),
|
||||
|
@ -2021,7 +2067,7 @@ void AnimationTrackEdit::_notification(int p_what) {
|
|||
interp_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
|
||||
interp_mode_rect.size = icon->get_size();
|
||||
|
||||
if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
|
||||
if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
|
||||
draw_texture(icon, interp_mode_rect.position);
|
||||
}
|
||||
// Make it easier to click.
|
||||
|
@ -2031,7 +2077,7 @@ void AnimationTrackEdit::_notification(int p_what) {
|
|||
ofs += icon->get_width() + hsep;
|
||||
interp_mode_rect.size.x += hsep;
|
||||
|
||||
if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
|
||||
if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
|
||||
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
|
||||
interp_mode_rect.size.x += down_icon->get_width();
|
||||
} else {
|
||||
|
@ -2054,7 +2100,7 @@ void AnimationTrackEdit::_notification(int p_what) {
|
|||
loop_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
|
||||
loop_mode_rect.size = icon->get_size();
|
||||
|
||||
if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
|
||||
if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
|
||||
draw_texture(icon, loop_mode_rect.position);
|
||||
}
|
||||
|
||||
|
@ -2064,7 +2110,7 @@ void AnimationTrackEdit::_notification(int p_what) {
|
|||
ofs += icon->get_width() + hsep;
|
||||
loop_mode_rect.size.x += hsep;
|
||||
|
||||
if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_TRANSFORM3D) {
|
||||
if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
|
||||
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
|
||||
loop_mode_rect.size.x += down_icon->get_width();
|
||||
} else {
|
||||
|
@ -2284,9 +2330,11 @@ void AnimationTrackEdit::set_animation_and_track(const Ref<Animation> &p_animati
|
|||
track = p_track;
|
||||
update();
|
||||
|
||||
Ref<Texture2D> type_icons[6] = {
|
||||
Ref<Texture2D> type_icons[8] = {
|
||||
get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")),
|
||||
get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")),
|
||||
get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")),
|
||||
get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")),
|
||||
get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")),
|
||||
get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")),
|
||||
get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
|
||||
get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")),
|
||||
|
@ -2456,17 +2504,17 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
|
|||
if (key_idx != -1) {
|
||||
String text = TTR("Time (s): ") + rtos(animation->track_get_key_time(track, key_idx)) + "\n";
|
||||
switch (animation->track_get_type(track)) {
|
||||
case Animation::TYPE_TRANSFORM3D: {
|
||||
Dictionary d = animation->track_get_key_value(track, key_idx);
|
||||
if (d.has("location")) {
|
||||
text += "Pos: " + String(d["location"]) + "\n";
|
||||
}
|
||||
if (d.has("rotation")) {
|
||||
text += "Rot: " + String(d["rotation"]) + "\n";
|
||||
}
|
||||
if (d.has("scale")) {
|
||||
text += "Scale: " + String(d["scale"]) + "\n";
|
||||
}
|
||||
case Animation::TYPE_POSITION_3D: {
|
||||
Vector3 t = animation->track_get_key_value(track, key_idx);
|
||||
text += "Position: " + String(t) + "\n";
|
||||
} break;
|
||||
case Animation::TYPE_ROTATION_3D: {
|
||||
Quaternion t = animation->track_get_key_value(track, key_idx);
|
||||
text += "Rotation: " + String(t) + "\n";
|
||||
} break;
|
||||
case Animation::TYPE_SCALE_3D: {
|
||||
Vector3 t = animation->track_get_key_value(track, key_idx);
|
||||
text += "Scale: " + String(t) + "\n";
|
||||
} break;
|
||||
case Animation::TYPE_VALUE: {
|
||||
const Variant &v = animation->track_get_key_value(track, key_idx);
|
||||
|
@ -3323,7 +3371,11 @@ static bool track_type_is_resettable(Animation::TrackType p_type) {
|
|||
[[fallthrough]];
|
||||
case Animation::TYPE_BEZIER:
|
||||
[[fallthrough]];
|
||||
case Animation::TYPE_TRANSFORM3D:
|
||||
case Animation::TYPE_POSITION_3D:
|
||||
[[fallthrough]];
|
||||
case Animation::TYPE_ROTATION_3D:
|
||||
[[fallthrough]];
|
||||
case Animation::TYPE_SCALE_3D:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -3483,33 +3535,50 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
|
|||
|
||||
NodePath np = path;
|
||||
|
||||
int track_idx = -1;
|
||||
int position_idx = -1;
|
||||
int rotation_idx = -1;
|
||||
int scale_idx = -1;
|
||||
|
||||
for (int i = 0; i < animation->get_track_count(); i++) {
|
||||
if (animation->track_get_type(i) != Animation::TYPE_TRANSFORM3D) {
|
||||
continue;
|
||||
}
|
||||
if (animation->track_get_path(i) != np) {
|
||||
continue;
|
||||
}
|
||||
|
||||
track_idx = i;
|
||||
break;
|
||||
if (animation->track_get_type(i) == Animation::TYPE_POSITION_3D) {
|
||||
position_idx = i;
|
||||
}
|
||||
if (animation->track_get_type(i) == Animation::TYPE_ROTATION_3D) {
|
||||
rotation_idx = i;
|
||||
}
|
||||
if (animation->track_get_type(i) == Animation::TYPE_SCALE_3D) {
|
||||
scale_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
InsertData id;
|
||||
Dictionary val;
|
||||
|
||||
id.path = np;
|
||||
id.track_idx = track_idx;
|
||||
id.value = p_xform;
|
||||
id.type = Animation::TYPE_TRANSFORM3D;
|
||||
// TRANSLATORS: This describes the target of new animation track, will be inserted into another string.
|
||||
id.query = vformat(TTR("node '%s'"), p_node->get_name());
|
||||
id.advance = false;
|
||||
|
||||
// Dialog insert.
|
||||
_query_insert(id);
|
||||
{
|
||||
id.track_idx = position_idx;
|
||||
id.value = p_xform.origin;
|
||||
id.type = Animation::TYPE_POSITION_3D;
|
||||
_query_insert(id);
|
||||
}
|
||||
{
|
||||
id.track_idx = rotation_idx;
|
||||
id.value = p_xform.basis.get_rotation_quaternion();
|
||||
id.type = Animation::TYPE_ROTATION_3D;
|
||||
_query_insert(id);
|
||||
}
|
||||
{
|
||||
id.track_idx = scale_idx;
|
||||
id.value = p_xform.basis.get_scale();
|
||||
id.type = Animation::TYPE_SCALE_3D;
|
||||
_query_insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
bool AnimationTrackEditor::has_transform_track(Node3D *p_node, const String &p_sub) {
|
||||
|
@ -3530,7 +3599,7 @@ bool AnimationTrackEditor::has_transform_track(Node3D *p_node, const String &p_s
|
|||
}
|
||||
int track_id = animation->find_track(path);
|
||||
if (track_id >= 0) {
|
||||
if (animation->track_get_type(track_id) == Animation::TYPE_TRANSFORM3D) {
|
||||
if (animation->track_get_type(track_id) == Animation::TYPE_POSITION_3D || animation->track_get_type(track_id) == Animation::TYPE_ROTATION_3D || animation->track_get_type(track_id) == Animation::TYPE_SCALE_3D) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -3977,18 +4046,13 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
|
|||
Variant value;
|
||||
|
||||
switch (p_id.type) {
|
||||
case Animation::TYPE_POSITION_3D:
|
||||
case Animation::TYPE_ROTATION_3D:
|
||||
case Animation::TYPE_SCALE_3D:
|
||||
case Animation::TYPE_VALUE: {
|
||||
value = p_id.value;
|
||||
|
||||
} break;
|
||||
case Animation::TYPE_TRANSFORM3D: {
|
||||
Transform3D tr = p_id.value;
|
||||
Dictionary d;
|
||||
d["location"] = tr.origin;
|
||||
d["scale"] = tr.basis.get_scale();
|
||||
d["rotation"] = Quaternion(tr.basis);
|
||||
value = d;
|
||||
} break;
|
||||
case Animation::TYPE_BEZIER: {
|
||||
Array array;
|
||||
array.resize(5);
|
||||
|
@ -4404,8 +4468,8 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
|
|||
ERR_FAIL_COND(!node);
|
||||
NodePath path_to = root->get_path_to(node);
|
||||
|
||||
if (adding_track_type == Animation::TYPE_TRANSFORM3D && !node->is_class("Node3D")) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Transform3D tracks only apply to 3D-based nodes."));
|
||||
if ((adding_track_type == Animation::TYPE_POSITION_3D || adding_track_type == Animation::TYPE_ROTATION_3D || adding_track_type == Animation::TYPE_SCALE_3D) && !node->is_class("Node3D")) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Position/Rotation/Scale 3D tracks only apply to 3D-based nodes."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -4415,7 +4479,9 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
|
|||
prop_selector->set_type_filter(Vector<Variant::Type>());
|
||||
prop_selector->select_property_from_instance(node);
|
||||
} break;
|
||||
case Animation::TYPE_TRANSFORM3D:
|
||||
case Animation::TYPE_POSITION_3D:
|
||||
case Animation::TYPE_ROTATION_3D:
|
||||
case Animation::TYPE_SCALE_3D:
|
||||
case Animation::TYPE_METHOD: {
|
||||
undo_redo->create_action(TTR("Add Track"));
|
||||
undo_redo->add_do_method(animation.ptr(), "add_track", adding_track_type);
|
||||
|
@ -4584,7 +4650,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
|
|||
}
|
||||
|
||||
switch (animation->track_get_type(p_track)) {
|
||||
case Animation::TYPE_TRANSFORM3D: {
|
||||
case Animation::TYPE_POSITION_3D: {
|
||||
if (!root->has_node(animation->track_get_path(p_track))) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key."));
|
||||
return;
|
||||
|
@ -4596,14 +4662,50 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
|
|||
return;
|
||||
}
|
||||
|
||||
Transform3D xf = base->get_transform();
|
||||
Vector3 pos = base->get_position();
|
||||
|
||||
Vector3 loc = xf.get_origin();
|
||||
Vector3 scale = xf.basis.get_scale_local();
|
||||
Quaternion rot = xf.basis;
|
||||
undo_redo->create_action(TTR("Add Position Key"));
|
||||
undo_redo->add_do_method(animation.ptr(), "position_track_insert_key", p_track, p_ofs, pos);
|
||||
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
|
||||
undo_redo->commit_action();
|
||||
|
||||
undo_redo->create_action(TTR("Add Transform Track Key"));
|
||||
undo_redo->add_do_method(animation.ptr(), "transform_track_insert_key", p_track, p_ofs, loc, rot, scale);
|
||||
} break;
|
||||
case Animation::TYPE_ROTATION_3D: {
|
||||
if (!root->has_node(animation->track_get_path(p_track))) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a key."));
|
||||
return;
|
||||
}
|
||||
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->commit_action();
|
||||
|
||||
|
@ -5277,8 +5379,14 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
|
|||
}
|
||||
|
||||
switch (animation->track_get_type(i)) {
|
||||
case Animation::TYPE_TRANSFORM3D:
|
||||
text += " (Transform)";
|
||||
case Animation::TYPE_POSITION_3D:
|
||||
text += " (Position)";
|
||||
break;
|
||||
case Animation::TYPE_ROTATION_3D:
|
||||
text += " (Rotation)";
|
||||
break;
|
||||
case Animation::TYPE_SCALE_3D:
|
||||
text += " (Scale)";
|
||||
break;
|
||||
case Animation::TYPE_METHOD:
|
||||
text += " (Methods)";
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#include "editor/editor_spin_slider.h"
|
||||
#include "editor/property_editor.h"
|
||||
#include "editor/property_selector.h"
|
||||
#include "scene/animation/animation_cache.h"
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/gui/file_dialog.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
|
|
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -91,8 +91,8 @@ struct ColladaImport {
|
|||
Error _create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform3D &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<ImporterMesh>> p_morph_meshes = Vector<Ref<ImporterMesh>>(), bool p_use_compression = false, bool p_use_mesh_material = false);
|
||||
Error load(const String &p_path, int p_flags, bool p_force_make_tangents = false, bool p_use_compression = false);
|
||||
void _fix_param_animation_tracks();
|
||||
void create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks);
|
||||
void create_animations(bool p_make_tracks_in_all_bones, bool p_import_value_tracks);
|
||||
void create_animation(int p_clip, bool p_import_value_tracks);
|
||||
void create_animations(bool p_import_value_tracks);
|
||||
|
||||
Set<String> tracks_in_clips;
|
||||
Vector<String> missing_textures;
|
||||
|
@ -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();
|
||||
for (int i = 0; i < collada.state.animation_clips.size(); i++) {
|
||||
for (int j = 0; j < collada.state.animation_clips[i].tracks.size(); j++) {
|
||||
|
@ -1417,13 +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++) {
|
||||
create_animation(i, p_make_tracks_in_all_bones, p_import_value_tracks);
|
||||
create_animation(i, p_import_value_tracks);
|
||||
}
|
||||
}
|
||||
|
||||
void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks) {
|
||||
void ColladaImport::create_animation(int p_clip, bool p_import_value_tracks) {
|
||||
Ref<Animation> animation = Ref<Animation>(memnew(Animation));
|
||||
|
||||
if (p_clip == -1) {
|
||||
|
@ -1522,10 +1522,55 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
|
|||
continue;
|
||||
}
|
||||
|
||||
animation->add_track(Animation::TYPE_TRANSFORM3D);
|
||||
int track = animation->get_track_count() - 1;
|
||||
animation->track_set_path(track, path);
|
||||
animation->track_set_imported(track, true); //helps merging later
|
||||
bool has_position = false;
|
||||
bool has_rotation = false;
|
||||
bool has_scale = false;
|
||||
|
||||
for (int i = 0; cn->xform_list.size(); i++) {
|
||||
switch (cn->xform_list[i].op) {
|
||||
case Collada::Node::XForm::OP_ROTATE: {
|
||||
has_rotation = true;
|
||||
} break;
|
||||
case Collada::Node::XForm::OP_SCALE: {
|
||||
has_scale = true;
|
||||
} break;
|
||||
case Collada::Node::XForm::OP_TRANSLATE: {
|
||||
has_position = true;
|
||||
} break;
|
||||
case Collada::Node::XForm::OP_MATRIX: {
|
||||
has_position = true;
|
||||
has_rotation = true;
|
||||
has_scale = true;
|
||||
} break;
|
||||
case Collada::Node::XForm::OP_VISIBILITY: {
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
int base_track = animation->get_track_count();
|
||||
int position_idx = -1;
|
||||
if (has_position) {
|
||||
position_idx = animation->get_track_count();
|
||||
animation->add_track(Animation::TYPE_POSITION_3D);
|
||||
animation->track_set_path(position_idx, path);
|
||||
animation->track_set_imported(position_idx, true); //helps merging later
|
||||
}
|
||||
|
||||
int rotation_idx = -1;
|
||||
if (has_rotation) {
|
||||
rotation_idx = animation->get_track_count();
|
||||
animation->add_track(Animation::TYPE_ROTATION_3D);
|
||||
animation->track_set_path(rotation_idx, path);
|
||||
animation->track_set_imported(rotation_idx, true); //helps merging later
|
||||
}
|
||||
|
||||
int scale_idx = -1;
|
||||
if (has_scale) {
|
||||
scale_idx = animation->get_track_count();
|
||||
animation->add_track(Animation::TYPE_SCALE_3D);
|
||||
animation->track_set_path(scale_idx, path);
|
||||
animation->track_set_imported(scale_idx, true); //helps merging later
|
||||
}
|
||||
|
||||
Vector<real_t> snapshots = base_snapshots;
|
||||
|
||||
|
@ -1606,10 +1651,19 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
|
|||
|
||||
Vector3 s = xform.basis.get_scale();
|
||||
bool singular_matrix = Math::is_zero_approx(s.x) || Math::is_zero_approx(s.y) || Math::is_zero_approx(s.z);
|
||||
Quaternion q = singular_matrix ? Quaternion() : xform.basis.get_rotation_quaternion();
|
||||
Quaternion q = singular_matrix ? Quaternion() :
|
||||
xform.basis.get_rotation_quaternion();
|
||||
Vector3 l = xform.origin;
|
||||
|
||||
animation->transform_track_insert_key(track, snapshots[i], l, q, s);
|
||||
if (position_idx >= 0) {
|
||||
animation->position_track_insert_key(position_idx, snapshots[i], l);
|
||||
}
|
||||
if (rotation_idx >= 0) {
|
||||
animation->rotation_track_insert_key(rotation_idx, snapshots[i], q);
|
||||
}
|
||||
if (scale_idx >= 0) {
|
||||
animation->scale_track_insert_key(scale_idx, snapshots[i], s);
|
||||
}
|
||||
}
|
||||
|
||||
if (nm.bone >= 0) {
|
||||
|
@ -1621,48 +1675,15 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
|
|||
if (found_anim) {
|
||||
tracks_found = true;
|
||||
} else {
|
||||
animation->remove_track(track);
|
||||
}
|
||||
}
|
||||
|
||||
if (p_make_tracks_in_all_bones) {
|
||||
//some bones may lack animation, but since we don't store pose as a property, we must add keyframes!
|
||||
for (const KeyValue<String, bool> &E : bones_with_animation) {
|
||||
if (E.value) {
|
||||
continue;
|
||||
if (position_idx >= 0) {
|
||||
animation->remove_track(base_track);
|
||||
}
|
||||
|
||||
NodeMap &nm = node_map[E.key];
|
||||
String path = scene->get_path_to(nm.node);
|
||||
ERR_CONTINUE(nm.bone < 0);
|
||||
Skeleton3D *sk = static_cast<Skeleton3D *>(nm.node);
|
||||
String name = sk->get_bone_name(nm.bone);
|
||||
path = path + ":" + name;
|
||||
|
||||
Collada::Node *cn = collada.state.scene_map[E.key];
|
||||
if (cn->ignore_anim) {
|
||||
WARN_PRINT("Collada: Ignoring animation on node: " + path);
|
||||
continue;
|
||||
if (rotation_idx >= 0) {
|
||||
animation->remove_track(base_track);
|
||||
}
|
||||
if (scale_idx >= 0) {
|
||||
animation->remove_track(base_track);
|
||||
}
|
||||
|
||||
animation->add_track(Animation::TYPE_TRANSFORM3D);
|
||||
int track = animation->get_track_count() - 1;
|
||||
animation->track_set_path(track, path);
|
||||
animation->track_set_imported(track, true); //helps merging later
|
||||
|
||||
Transform3D xform = cn->compute_transform(collada);
|
||||
xform = collada.fix_transform(xform) * cn->post_transform;
|
||||
|
||||
xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform;
|
||||
|
||||
Vector3 s = xform.basis.get_scale();
|
||||
bool singular_matrix = Math::is_zero_approx(s.x) || Math::is_zero_approx(s.y) || Math::is_zero_approx(s.z);
|
||||
Quaternion q = singular_matrix ? Quaternion() : xform.basis.get_rotation_quaternion();
|
||||
Vector3 l = xform.origin;
|
||||
|
||||
animation->transform_track_insert_key(track, 0, l, q, s);
|
||||
|
||||
tracks_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1773,7 +1794,7 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_
|
|||
}
|
||||
|
||||
if (p_flags & IMPORT_ANIMATION) {
|
||||
state.create_animations(true, true);
|
||||
state.create_animations(true);
|
||||
AnimationPlayer *ap = memnew(AnimationPlayer);
|
||||
for (int i = 0; i < state.animations.size(); i++) {
|
||||
String name;
|
||||
|
@ -1800,7 +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);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load animation from file '" + p_path + "'.");
|
||||
|
||||
state.create_animations(true, true);
|
||||
state.create_animations(true);
|
||||
if (state.scene) {
|
||||
memdelete(state.scene);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/import/editor_importer_bake_reset.h"
|
||||
|
||||
#include "editor/import/scene_import_settings.h"
|
||||
#include "scene/3d/area_3d.h"
|
||||
#include "scene/3d/collision_shape_3d.h"
|
||||
|
@ -851,42 +851,57 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
|
|||
new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
|
||||
|
||||
if (kt > (from + 0.01) && k > 0) {
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
|
||||
Quaternion q;
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
|
||||
Vector3 p;
|
||||
default_anim->position_track_interpolate(j, from, &p);
|
||||
new_anim->position_track_insert_key(dtrack, 0, p);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
|
||||
Quaternion r;
|
||||
default_anim->rotation_track_interpolate(j, from, &r);
|
||||
new_anim->rotation_track_insert_key(dtrack, 0, r);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
|
||||
Vector3 s;
|
||||
default_anim->transform_track_interpolate(j, from, &p, &q, &s);
|
||||
new_anim->transform_track_insert_key(dtrack, 0, p, q, s);
|
||||
}
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
default_anim->scale_track_interpolate(j, from, &s);
|
||||
new_anim->scale_track_insert_key(dtrack, 0, s);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
Variant var = default_anim->value_track_interpolate(j, from);
|
||||
new_anim->track_insert_key(dtrack, 0, var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
|
||||
Quaternion q;
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
|
||||
Vector3 p;
|
||||
default_anim->position_track_get_key(j, k, &p);
|
||||
new_anim->position_track_insert_key(dtrack, kt - from, p);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
|
||||
Quaternion r;
|
||||
default_anim->rotation_track_get_key(j, k, &r);
|
||||
new_anim->rotation_track_insert_key(dtrack, kt - from, r);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
|
||||
Vector3 s;
|
||||
default_anim->transform_track_get_key(j, k, &p, &q, &s);
|
||||
new_anim->transform_track_insert_key(dtrack, kt - from, p, q, s);
|
||||
}
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
default_anim->scale_track_get_key(j, k, &s);
|
||||
new_anim->scale_track_insert_key(dtrack, kt - from, s);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
Variant var = default_anim->track_get_key_value(j, k);
|
||||
new_anim->track_insert_key(dtrack, kt - from, var);
|
||||
}
|
||||
}
|
||||
|
||||
if (dtrack != -1 && kt >= to) {
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
|
||||
Quaternion q;
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
|
||||
Vector3 p;
|
||||
default_anim->position_track_interpolate(j, to, &p);
|
||||
new_anim->position_track_insert_key(dtrack, to - from, p);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
|
||||
Quaternion r;
|
||||
default_anim->rotation_track_interpolate(j, to, &r);
|
||||
new_anim->rotation_track_insert_key(dtrack, to - from, r);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
|
||||
Vector3 s;
|
||||
default_anim->transform_track_interpolate(j, to, &p, &q, &s);
|
||||
new_anim->transform_track_insert_key(dtrack, to - from, p, q, s);
|
||||
}
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
default_anim->scale_track_interpolate(j, to, &s);
|
||||
new_anim->scale_track_insert_key(dtrack, to - from, s);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
Variant var = default_anim->value_track_interpolate(j, to);
|
||||
new_anim->track_insert_key(dtrack, to - from, var);
|
||||
}
|
||||
|
@ -897,16 +912,25 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
|
|||
new_anim->add_track(default_anim->track_get_type(j));
|
||||
dtrack = new_anim->get_track_count() - 1;
|
||||
new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
|
||||
Quaternion q;
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
|
||||
Vector3 p;
|
||||
default_anim->position_track_interpolate(j, from, &p);
|
||||
new_anim->position_track_insert_key(dtrack, 0, p);
|
||||
default_anim->position_track_interpolate(j, to, &p);
|
||||
new_anim->position_track_insert_key(dtrack, to - from, p);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
|
||||
Quaternion r;
|
||||
default_anim->rotation_track_interpolate(j, from, &r);
|
||||
new_anim->rotation_track_insert_key(dtrack, 0, r);
|
||||
default_anim->rotation_track_interpolate(j, to, &r);
|
||||
new_anim->rotation_track_insert_key(dtrack, to - from, r);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
|
||||
Vector3 s;
|
||||
default_anim->transform_track_interpolate(j, from, &p, &q, &s);
|
||||
new_anim->transform_track_insert_key(dtrack, 0, p, q, s);
|
||||
default_anim->transform_track_interpolate(j, to, &p, &q, &s);
|
||||
new_anim->transform_track_insert_key(dtrack, to - from, p, q, s);
|
||||
}
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
default_anim->scale_track_interpolate(j, from, &s);
|
||||
new_anim->scale_track_insert_key(dtrack, 0, s);
|
||||
default_anim->scale_track_interpolate(j, to, &s);
|
||||
new_anim->scale_track_insert_key(dtrack, to - from, s);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
Variant var = default_anim->value_track_interpolate(j, from);
|
||||
new_anim->track_insert_key(dtrack, 0, var);
|
||||
Variant to_var = default_anim->value_track_interpolate(j, to);
|
||||
|
@ -1000,6 +1024,9 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
|
|||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_linear_error"), 0.05));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error"), 0.01));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angle"), 22));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/position", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/rotation", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/scale", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/amount", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
|
@ -1171,7 +1198,6 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in
|
|||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/bake_reset_animation"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));
|
||||
|
||||
|
@ -1533,11 +1559,6 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
|
|||
|
||||
_pre_fix_node(scene, scene, collision_map);
|
||||
_post_fix_node(scene, scene, collision_map, scanned_meshes, node_data, material_data, animation_data, fps);
|
||||
bool use_bake_reset_animation = p_options["animation/bake_reset_animation"];
|
||||
if (use_bake_reset_animation) {
|
||||
BakeReset bake_reset;
|
||||
bake_reset._bake_animation_pose(scene, "RESET");
|
||||
}
|
||||
|
||||
String root_type = p_options["nodes/root_type"];
|
||||
root_type = root_type.split(" ")[0]; // full root_type is "ClassName (filename.gd)" for a script global class.
|
||||
|
|
|
@ -65,6 +65,13 @@ public:
|
|||
IMPORT_USE_NAMED_SKIN_BINDS = 16,
|
||||
};
|
||||
|
||||
enum AnimationImportBoneTracks {
|
||||
ANIMATION_IMPORT_BONE_TRACKS_IF_PRESENT,
|
||||
ANIMATION_IMPORT_BONE_TRACKS_IF_PRESENT_FOR_ALL,
|
||||
ANIMATION_IMPORT_BONE_TRACKS_ALWAYS,
|
||||
ANIMATION_IMPORT_BONE_TRACKS_NEVER,
|
||||
};
|
||||
|
||||
virtual uint32_t get_import_flags() const;
|
||||
virtual void get_extensions(List<String> *r_extensions) const;
|
||||
virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr);
|
||||
|
|
|
@ -1305,7 +1305,9 @@ void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gi
|
|||
t.origin = orig + to_local.xform(sub);
|
||||
|
||||
// Apply transform.
|
||||
skeleton->set_bone_pose(p_id, t);
|
||||
skeleton->set_bone_pose_position(p_id, t.origin);
|
||||
skeleton->set_bone_pose_rotation(p_id, t.basis.operator Quaternion());
|
||||
skeleton->set_bone_pose_scale(p_id, t.basis.get_scale());
|
||||
}
|
||||
|
||||
void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {
|
||||
|
|
|
@ -1011,9 +1011,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
|
|||
// track count is 5.
|
||||
// next track id is 5.
|
||||
const uint64_t target_id = track.key;
|
||||
int track_idx = animation->add_track(Animation::TYPE_TRANSFORM3D);
|
||||
|
||||
// animation->track_set_path(track_idx, node_path);
|
||||
Ref<FBXBone> bone;
|
||||
|
||||
// note we must not run the below code if the entry doesn't exist, it will create dummy entries which is very bad.
|
||||
|
@ -1037,22 +1035,21 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
|
|||
|
||||
// if this is a skeleton mapped track we can just set the path for the track.
|
||||
// todo: implement node paths here at some
|
||||
NodePath track_path;
|
||||
if (state.fbx_bone_map.size() > 0 && state.fbx_bone_map.has(target_id)) {
|
||||
if (bone->fbx_skeleton.is_valid() && bone.is_valid()) {
|
||||
Ref<FBXSkeleton> fbx_skeleton = bone->fbx_skeleton;
|
||||
String bone_path = state.root->get_path_to(fbx_skeleton->skeleton);
|
||||
bone_path += ":" + fbx_skeleton->skeleton->get_bone_name(bone->godot_bone_id);
|
||||
print_verbose("[doc] track bone path: " + bone_path);
|
||||
NodePath path = bone_path;
|
||||
animation->track_set_path(track_idx, path);
|
||||
track_path = bone_path;
|
||||
}
|
||||
} else if (state.fbx_target_map.has(target_id)) {
|
||||
//print_verbose("[doc] we have a valid target for a node animation");
|
||||
Ref<FBXNode> target_node = state.fbx_target_map[target_id];
|
||||
if (target_node.is_valid() && target_node->godot_node != nullptr) {
|
||||
String node_path = state.root->get_path_to(target_node->godot_node);
|
||||
NodePath path = node_path;
|
||||
animation->track_set_path(track_idx, path);
|
||||
track_path = node_path;
|
||||
//print_verbose("[doc] node animation path: " + node_path);
|
||||
}
|
||||
} else {
|
||||
|
@ -1186,6 +1183,30 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
|
|||
const Vector3 def_scale = scale_keys.has_default ? scale_keys.default_value : bone_rest.basis.get_scale();
|
||||
print_verbose("track defaults: p(" + def_pos + ") s(" + def_scale + ") r(" + def_rot + ")");
|
||||
|
||||
int position_idx = -1;
|
||||
if (pos_values.size()) {
|
||||
position_idx = animation->get_track_count();
|
||||
animation->add_track(Animation::TYPE_POSITION_3D);
|
||||
animation->track_set_path(position_idx, track_path);
|
||||
animation->track_set_imported(position_idx, true);
|
||||
}
|
||||
|
||||
int rotation_idx = -1;
|
||||
if (pos_values.size()) {
|
||||
rotation_idx = animation->get_track_count();
|
||||
animation->add_track(Animation::TYPE_ROTATION_3D);
|
||||
animation->track_set_path(rotation_idx, track_path);
|
||||
animation->track_set_imported(rotation_idx, true);
|
||||
}
|
||||
|
||||
int scale_idx = -1;
|
||||
if (pos_values.size()) {
|
||||
scale_idx = animation->get_track_count();
|
||||
animation->add_track(Animation::TYPE_SCALE_3D);
|
||||
animation->track_set_path(scale_idx, track_path);
|
||||
animation->track_set_imported(scale_idx, true);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
Vector3 pos = def_pos;
|
||||
Quaternion rot = def_rot;
|
||||
|
@ -1220,7 +1241,15 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
|
|||
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) {
|
||||
break;
|
||||
|
|
|
@ -5733,7 +5733,7 @@ struct EditorSceneImporterGLTFInterpolate<Quaternion> {
|
|||
template <class T>
|
||||
T GLTFDocument::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) {
|
||||
ERR_FAIL_COND_V(!p_values.size(), T());
|
||||
if (p_times.size() != p_values.size()) {
|
||||
if (p_times.size() != (p_values.size() / (p_interp == GLTFAnimation::INTERP_CUBIC_SPLINE ? 3 : 1))) {
|
||||
ERR_PRINT_ONCE("The interpolated values are not corresponding to its times.");
|
||||
return p_values[0];
|
||||
}
|
||||
|
@ -5868,9 +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;
|
||||
if ((track.rotation_track.values.size() || track.position_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) {
|
||||
//make transform track
|
||||
int track_idx = animation->get_track_count();
|
||||
animation->add_track(Animation::TYPE_TRANSFORM3D);
|
||||
animation->track_set_path(track_idx, transform_node_path);
|
||||
int base_idx = animation->get_track_count();
|
||||
int position_idx = -1;
|
||||
int rotation_idx = -1;
|
||||
int scale_idx = -1;
|
||||
|
||||
if (track.position_track.values.size()) {
|
||||
Vector3 base_pos = state->nodes[track_i.key]->position;
|
||||
bool not_default = false; //discard the track if all it contains is default values
|
||||
for (int i = 0; i < track.position_track.times.size(); i++) {
|
||||
Vector3 value = track.position_track.values[track.position_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i];
|
||||
if (!value.is_equal_approx(base_pos)) {
|
||||
not_default = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (not_default) {
|
||||
position_idx = base_idx;
|
||||
animation->add_track(Animation::TYPE_POSITION_3D);
|
||||
animation->track_set_path(position_idx, transform_node_path);
|
||||
animation->track_set_imported(position_idx, true); //helps merging later
|
||||
|
||||
base_idx++;
|
||||
}
|
||||
}
|
||||
if (track.rotation_track.values.size()) {
|
||||
Quaternion base_rot = state->nodes[track_i.key]->rotation.normalized();
|
||||
bool not_default = false; //discard the track if all it contains is default values
|
||||
for (int i = 0; i < track.rotation_track.times.size(); i++) {
|
||||
Quaternion value = track.rotation_track.values[track.rotation_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i].normalized();
|
||||
if (!value.is_equal_approx(base_rot)) {
|
||||
not_default = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (not_default) {
|
||||
rotation_idx = base_idx;
|
||||
animation->add_track(Animation::TYPE_ROTATION_3D);
|
||||
animation->track_set_path(rotation_idx, transform_node_path);
|
||||
animation->track_set_imported(rotation_idx, true); //helps merging later
|
||||
base_idx++;
|
||||
}
|
||||
}
|
||||
if (track.scale_track.values.size()) {
|
||||
Vector3 base_scale = state->nodes[track_i.key]->scale;
|
||||
bool not_default = false; //discard the track if all it contains is default values
|
||||
for (int i = 0; i < track.scale_track.times.size(); i++) {
|
||||
Vector3 value = track.scale_track.values[track.scale_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i];
|
||||
if (!value.is_equal_approx(base_scale)) {
|
||||
not_default = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (not_default) {
|
||||
scale_idx = base_idx;
|
||||
animation->add_track(Animation::TYPE_SCALE_3D);
|
||||
animation->track_set_path(scale_idx, transform_node_path);
|
||||
animation->track_set_imported(scale_idx, true); //helps merging later
|
||||
base_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
//first determine animation length
|
||||
|
||||
const double increment = 1.0 / bake_fps;
|
||||
|
@ -5880,15 +5938,15 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
|
|||
Quaternion base_rot;
|
||||
Vector3 base_scale = Vector3(1, 1, 1);
|
||||
|
||||
if (!track.rotation_track.values.size()) {
|
||||
if (rotation_idx == -1) {
|
||||
base_rot = state->nodes[track_i.key]->rotation.normalized();
|
||||
}
|
||||
|
||||
if (!track.position_track.values.size()) {
|
||||
if (position_idx == -1) {
|
||||
base_pos = state->nodes[track_i.key]->position;
|
||||
}
|
||||
|
||||
if (!track.scale_track.values.size()) {
|
||||
if (scale_idx == -1) {
|
||||
base_scale = state->nodes[track_i.key]->scale;
|
||||
}
|
||||
|
||||
|
@ -5898,15 +5956,15 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
|
|||
Quaternion rot = base_rot;
|
||||
Vector3 scale = base_scale;
|
||||
|
||||
if (track.position_track.times.size()) {
|
||||
if (position_idx >= 0) {
|
||||
pos = _interpolate_track<Vector3>(track.position_track.times, track.position_track.values, time, track.position_track.interpolation);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -5925,7 +5983,15 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
|
|||
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) {
|
||||
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) {
|
||||
#ifndef _MSC_VER
|
||||
#warning this needs to be redone
|
||||
#endif
|
||||
#if 0
|
||||
Animation::InterpolationType interpolation = p_animation->track_get_interpolation_type(p_track_i);
|
||||
|
||||
GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
|
||||
|
@ -6164,6 +6234,8 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
|
|||
for (int32_t key_i = 0; key_i < key_count; key_i++) {
|
||||
times.write[key_i] = p_animation->track_get_key_time(p_track_i, key_i);
|
||||
}
|
||||
|
||||
|
||||
if (track_type == Animation::TYPE_TRANSFORM3D) {
|
||||
p_track.position_track.times = times;
|
||||
p_track.position_track.interpolation = gltf_interpolation;
|
||||
|
@ -6323,7 +6395,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
return p_track;
|
||||
}
|
||||
|
||||
|
|
|
@ -99,8 +99,12 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
|
|||
set_bone_rest(which, p_value);
|
||||
} else if (what == "enabled") {
|
||||
set_bone_enabled(which, p_value);
|
||||
} else if (what == "pose") {
|
||||
set_bone_pose(which, p_value);
|
||||
} else if (what == "position") {
|
||||
set_bone_pose_position(which, p_value);
|
||||
} else if (what == "rotation") {
|
||||
set_bone_pose_rotation(which, p_value);
|
||||
} else if (what == "scale") {
|
||||
set_bone_pose_scale(which, p_value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -135,8 +139,12 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
|
|||
r_ret = get_bone_rest(which);
|
||||
} else if (what == "enabled") {
|
||||
r_ret = is_bone_enabled(which);
|
||||
} else if (what == "pose") {
|
||||
r_ret = get_bone_pose(which);
|
||||
} else if (what == "position") {
|
||||
r_ret = get_bone_pose_position(which);
|
||||
} else if (what == "rotation") {
|
||||
r_ret = get_bone_pose_rotation(which);
|
||||
} else if (what == "scale") {
|
||||
r_ret = get_bone_pose_scale(which);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -151,7 +159,9 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const {
|
|||
p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NOEDITOR));
|
||||
p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
|
||||
p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
|
||||
p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
|
||||
p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
|
||||
p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
|
||||
p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
|
||||
}
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
|
@ -657,19 +667,60 @@ void Skeleton3D::clear_bones() {
|
|||
|
||||
// Posing api
|
||||
|
||||
void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) {
|
||||
void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) {
|
||||
const int bone_size = bones.size();
|
||||
ERR_FAIL_INDEX(p_bone, bone_size);
|
||||
|
||||
bones.write[p_bone].pose = p_pose;
|
||||
bones.write[p_bone].pose_position = p_position;
|
||||
bones.write[p_bone].pose_cache_dirty = true;
|
||||
if (is_inside_tree()) {
|
||||
_make_dirty();
|
||||
}
|
||||
}
|
||||
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 {
|
||||
const int bone_size = bones.size();
|
||||
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) {
|
||||
|
@ -989,7 +1040,8 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
|
|||
|
||||
if (b.disable_rest) {
|
||||
if (bone_enabled) {
|
||||
Transform3D pose = b.pose;
|
||||
b.update_pose_cache();
|
||||
Transform3D pose = b.pose_cache;
|
||||
if (b.custom_pose_enable) {
|
||||
pose = b.custom_pose * pose;
|
||||
}
|
||||
|
@ -1012,7 +1064,8 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
|
|||
|
||||
} else {
|
||||
if (bone_enabled) {
|
||||
Transform3D pose = b.pose;
|
||||
b.update_pose_cache();
|
||||
Transform3D pose = b.pose_cache;
|
||||
if (b.custom_pose_enable) {
|
||||
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("get_bone_pose", "bone_idx"), &Skeleton3D::get_bone_pose);
|
||||
ClassDB::bind_method(D_METHOD("set_bone_pose", "bone_idx", "pose"), &Skeleton3D::set_bone_pose);
|
||||
ClassDB::bind_method(D_METHOD("set_bone_pose_position", "bone_idx", "position"), &Skeleton3D::set_bone_pose_position);
|
||||
ClassDB::bind_method(D_METHOD("set_bone_pose_rotation", "bone_idx", "rotation"), &Skeleton3D::set_bone_pose_rotation);
|
||||
ClassDB::bind_method(D_METHOD("set_bone_pose_scale", "bone_idx", "scale"), &Skeleton3D::set_bone_pose_scale);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_bone_pose_position", "bone_idx"), &Skeleton3D::get_bone_pose_position);
|
||||
ClassDB::bind_method(D_METHOD("get_bone_pose_rotation", "bone_idx"), &Skeleton3D::get_bone_pose_rotation);
|
||||
ClassDB::bind_method(D_METHOD("get_bone_pose_scale", "bone_idx"), &Skeleton3D::get_bone_pose_scale);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_bone_enabled", "bone_idx"), &Skeleton3D::is_bone_enabled);
|
||||
ClassDB::bind_method(D_METHOD("set_bone_enabled", "bone_idx", "enabled"), &Skeleton3D::set_bone_enabled, DEFVAL(true));
|
||||
|
|
|
@ -79,7 +79,19 @@ private:
|
|||
bool disable_rest = false;
|
||||
Transform3D rest;
|
||||
|
||||
Transform3D pose;
|
||||
_FORCE_INLINE_ void update_pose_cache() {
|
||||
if (pose_cache_dirty) {
|
||||
pose_cache.basis.set_quaternion_scale(pose_rotation, pose_scale);
|
||||
pose_cache.origin = pose_position;
|
||||
pose_cache_dirty = false;
|
||||
}
|
||||
}
|
||||
bool pose_cache_dirty = true;
|
||||
Transform3D pose_cache;
|
||||
Vector3 pose_position;
|
||||
Quaternion pose_rotation;
|
||||
Vector3 pose_scale = Vector3(1, 1, 1);
|
||||
|
||||
Transform3D pose_global;
|
||||
Transform3D pose_global_no_override;
|
||||
|
||||
|
@ -206,9 +218,16 @@ public:
|
|||
|
||||
// posing api
|
||||
|
||||
void set_bone_pose(int p_bone, const Transform3D &p_pose);
|
||||
void set_bone_pose_position(int p_bone, const Vector3 &p_position);
|
||||
void set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation);
|
||||
void set_bone_pose_scale(int p_bone, const Vector3 &p_scale);
|
||||
|
||||
Transform3D get_bone_pose(int p_bone) const;
|
||||
|
||||
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);
|
||||
Transform3D get_bone_custom_pose(int p_bone) const;
|
||||
|
||||
|
|
|
@ -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() {
|
||||
}
|
|
@ -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
|
|
@ -60,7 +60,12 @@ void AnimatedValuesBackup::restore() const {
|
|||
if (entry->bone_idx == -1) {
|
||||
entry->object->set_indexed(entry->subpath, entry->value);
|
||||
} else {
|
||||
Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose(entry->bone_idx, entry->value);
|
||||
Array arr = entry->value;
|
||||
if (arr.size() == 3) {
|
||||
Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_position(entry->bone_idx, arr[0]);
|
||||
Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_rotation(entry->bone_idx, arr[1]);
|
||||
Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_scale(entry->bone_idx, arr[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,6 +247,8 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
|
|||
|
||||
p_anim->node_cache.resize(a->get_track_count());
|
||||
|
||||
setup_pass++;
|
||||
|
||||
for (int i = 0; i < a->get_track_count(); i++) {
|
||||
p_anim->node_cache.write[i] = nullptr;
|
||||
RES resource;
|
||||
|
@ -275,46 +282,68 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
|
|||
node_cache_map[key] = TrackNodeCache();
|
||||
}
|
||||
|
||||
p_anim->node_cache.write[i] = &node_cache_map[key];
|
||||
p_anim->node_cache[i]->path = a->track_get_path(i);
|
||||
p_anim->node_cache[i]->node = child;
|
||||
p_anim->node_cache[i]->resource = resource;
|
||||
p_anim->node_cache[i]->node_2d = Object::cast_to<Node2D>(child);
|
||||
TrackNodeCache *node_cache = &node_cache_map[key];
|
||||
p_anim->node_cache.write[i] = node_cache;
|
||||
|
||||
node_cache->path = a->track_get_path(i);
|
||||
node_cache->node = child;
|
||||
node_cache->resource = resource;
|
||||
node_cache->node_2d = Object::cast_to<Node2D>(child);
|
||||
#ifndef _3D_DISABLED
|
||||
if (a->track_get_type(i) == Animation::TYPE_TRANSFORM3D) {
|
||||
if (a->track_get_type(i) == Animation::TYPE_POSITION_3D || a->track_get_type(i) == Animation::TYPE_ROTATION_3D || a->track_get_type(i) == Animation::TYPE_SCALE_3D) {
|
||||
// special cases and caches for transform tracks
|
||||
|
||||
if (node_cache->last_setup_pass != setup_pass) {
|
||||
node_cache->loc_used = false;
|
||||
node_cache->rot_used = false;
|
||||
node_cache->scale_used = false;
|
||||
}
|
||||
|
||||
// cache node_3d
|
||||
p_anim->node_cache[i]->node_3d = Object::cast_to<Node3D>(child);
|
||||
node_cache->node_3d = Object::cast_to<Node3D>(child);
|
||||
// cache skeleton
|
||||
p_anim->node_cache[i]->skeleton = Object::cast_to<Skeleton3D>(child);
|
||||
if (p_anim->node_cache[i]->skeleton) {
|
||||
node_cache->skeleton = Object::cast_to<Skeleton3D>(child);
|
||||
if (node_cache->skeleton) {
|
||||
if (a->track_get_path(i).get_subname_count() == 1) {
|
||||
StringName bone_name = a->track_get_path(i).get_subname(0);
|
||||
|
||||
p_anim->node_cache[i]->bone_idx = p_anim->node_cache[i]->skeleton->find_bone(bone_name);
|
||||
if (p_anim->node_cache[i]->bone_idx < 0) {
|
||||
node_cache->bone_idx = node_cache->skeleton->find_bone(bone_name);
|
||||
if (node_cache->bone_idx < 0) {
|
||||
// broken track (nonexistent bone)
|
||||
p_anim->node_cache[i]->skeleton = nullptr;
|
||||
p_anim->node_cache[i]->node_3d = nullptr;
|
||||
ERR_CONTINUE(p_anim->node_cache[i]->bone_idx < 0);
|
||||
node_cache->skeleton = nullptr;
|
||||
node_cache->node_3d = nullptr;
|
||||
ERR_CONTINUE(node_cache->bone_idx < 0);
|
||||
}
|
||||
} else {
|
||||
// no property, just use spatialnode
|
||||
p_anim->node_cache[i]->skeleton = nullptr;
|
||||
node_cache->skeleton = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
switch (a->track_get_type(i)) {
|
||||
case Animation::TYPE_POSITION_3D: {
|
||||
node_cache->loc_used = true;
|
||||
} break;
|
||||
case Animation::TYPE_ROTATION_3D: {
|
||||
node_cache->rot_used = true;
|
||||
} break;
|
||||
case Animation::TYPE_SCALE_3D: {
|
||||
node_cache->scale_used = true;
|
||||
} break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
|
||||
if (a->track_get_type(i) == Animation::TYPE_VALUE) {
|
||||
if (!p_anim->node_cache[i]->property_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
|
||||
if (!node_cache->property_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
|
||||
TrackNodeCache::PropertyAnim pa;
|
||||
pa.subpath = leftover_path;
|
||||
pa.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
|
||||
pa.special = SP_NONE;
|
||||
pa.owner = p_anim->node_cache[i];
|
||||
if (false && p_anim->node_cache[i]->node_2d) {
|
||||
if (false && node_cache->node_2d) {
|
||||
if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_pos) {
|
||||
pa.special = SP_NODE2D_POS;
|
||||
} else if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_rot) {
|
||||
|
@ -323,20 +352,22 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
|
|||
pa.special = SP_NODE2D_SCALE;
|
||||
}
|
||||
}
|
||||
p_anim->node_cache[i]->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa;
|
||||
node_cache->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa;
|
||||
}
|
||||
}
|
||||
|
||||
if (a->track_get_type(i) == Animation::TYPE_BEZIER && leftover_path.size()) {
|
||||
if (!p_anim->node_cache[i]->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
|
||||
if (!node_cache->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
|
||||
TrackNodeCache::BezierAnim ba;
|
||||
ba.bezier_property = leftover_path;
|
||||
ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
|
||||
ba.owner = p_anim->node_cache[i];
|
||||
|
||||
p_anim->node_cache[i]->bezier_anim[a->track_get_path(i).get_concatenated_subnames()] = ba;
|
||||
node_cache->bezier_anim[a->track_get_path(i).get_concatenated_subnames()] = ba;
|
||||
}
|
||||
}
|
||||
|
||||
node_cache->last_setup_pass = setup_pass;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,17 +400,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
|
|||
}
|
||||
|
||||
switch (a->track_get_type(i)) {
|
||||
case Animation::TYPE_TRANSFORM3D: {
|
||||
case Animation::TYPE_POSITION_3D: {
|
||||
#ifndef _3D_DISABLED
|
||||
if (!nc->node_3d) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector3 loc;
|
||||
Quaternion rot;
|
||||
Vector3 scale;
|
||||
|
||||
Error err = a->transform_track_interpolate(i, p_time, &loc, &rot, &scale);
|
||||
Error err = a->position_track_interpolate(i, p_time, &loc);
|
||||
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
|
||||
|
||||
if (err != OK) {
|
||||
|
@ -391,12 +420,63 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
|
|||
cache_update[cache_update_size++] = nc;
|
||||
nc->accum_pass = accum_pass;
|
||||
nc->loc_accum = loc;
|
||||
nc->rot_accum = rot;
|
||||
nc->scale_accum = scale;
|
||||
|
||||
nc->rot_accum = Quaternion();
|
||||
nc->scale_accum = Vector3();
|
||||
} else {
|
||||
nc->loc_accum = nc->loc_accum.lerp(loc, p_interp);
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
} break;
|
||||
case Animation::TYPE_ROTATION_3D: {
|
||||
#ifndef _3D_DISABLED
|
||||
if (!nc->node_3d) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Quaternion rot;
|
||||
|
||||
Error err = a->rotation_track_interpolate(i, p_time, &rot);
|
||||
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
|
||||
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nc->accum_pass != accum_pass) {
|
||||
ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
|
||||
cache_update[cache_update_size++] = nc;
|
||||
nc->accum_pass = accum_pass;
|
||||
nc->loc_accum = Vector3();
|
||||
nc->rot_accum = rot;
|
||||
nc->scale_accum = Vector3();
|
||||
} else {
|
||||
nc->rot_accum = nc->rot_accum.slerp(rot, p_interp);
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
} break;
|
||||
case Animation::TYPE_SCALE_3D: {
|
||||
#ifndef _3D_DISABLED
|
||||
if (!nc->node_3d) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector3 scale;
|
||||
|
||||
Error err = a->scale_track_interpolate(i, p_time, &scale);
|
||||
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
|
||||
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nc->accum_pass != accum_pass) {
|
||||
ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
|
||||
cache_update[cache_update_size++] = nc;
|
||||
nc->accum_pass = accum_pass;
|
||||
nc->loc_accum = Vector3();
|
||||
nc->rot_accum = Quaternion();
|
||||
nc->scale_accum = scale;
|
||||
} else {
|
||||
nc->scale_accum = nc->scale_accum.lerp(scale, p_interp);
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
|
@ -855,15 +935,30 @@ void AnimationPlayer::_animation_update_transforms() {
|
|||
TrackNodeCache *nc = cache_update[i];
|
||||
|
||||
ERR_CONTINUE(nc->accum_pass != accum_pass);
|
||||
|
||||
t.origin = nc->loc_accum;
|
||||
t.basis.set_quaternion_scale(nc->rot_accum, nc->scale_accum);
|
||||
#ifndef _3D_DISABLED
|
||||
if (nc->skeleton && nc->bone_idx >= 0) {
|
||||
nc->skeleton->set_bone_pose(nc->bone_idx, t);
|
||||
if (nc->loc_used) {
|
||||
nc->skeleton->set_bone_pose_position(nc->bone_idx, nc->loc_accum);
|
||||
}
|
||||
if (nc->rot_used) {
|
||||
nc->skeleton->set_bone_pose_rotation(nc->bone_idx, nc->rot_accum);
|
||||
}
|
||||
if (nc->scale_used) {
|
||||
nc->skeleton->set_bone_pose_scale(nc->bone_idx, nc->scale_accum);
|
||||
}
|
||||
|
||||
} else if (nc->node_3d) {
|
||||
nc->node_3d->set_transform(t);
|
||||
if (nc->loc_used) {
|
||||
nc->node_3d->set_position(nc->loc_accum);
|
||||
}
|
||||
if (nc->rot_used) {
|
||||
nc->node_3d->set_rotation(nc->rot_accum.get_euler());
|
||||
}
|
||||
if (nc->scale_used) {
|
||||
nc->node_3d->set_scale(nc->scale_accum);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _3D_DISABLED
|
||||
}
|
||||
}
|
||||
|
@ -1527,7 +1622,12 @@ Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values(Node *p_root_o
|
|||
AnimatedValuesBackup::Entry entry;
|
||||
entry.object = nc->skeleton;
|
||||
entry.bone_idx = nc->bone_idx;
|
||||
entry.value = nc->skeleton->get_bone_pose(nc->bone_idx);
|
||||
Array arr;
|
||||
arr.resize(3);
|
||||
arr[0] = nc->skeleton->get_bone_pose_position(nc->bone_idx);
|
||||
arr[1] = nc->skeleton->get_bone_pose_rotation(nc->bone_idx);
|
||||
arr[2] = nc->skeleton->get_bone_pose_scale(nc->bone_idx);
|
||||
entry.value = nc;
|
||||
backup->entries.push_back(entry);
|
||||
} else {
|
||||
if (nc->node_3d) {
|
||||
|
|
|
@ -88,6 +88,8 @@ private:
|
|||
SP_NODE2D_SCALE,
|
||||
};
|
||||
|
||||
uint32_t setup_pass = 1;
|
||||
|
||||
struct TrackNodeCache {
|
||||
NodePath path;
|
||||
uint32_t id = 0;
|
||||
|
@ -101,6 +103,10 @@ private:
|
|||
int bone_idx = -1;
|
||||
// accumulated transforms
|
||||
|
||||
bool loc_used = false;
|
||||
bool rot_used = false;
|
||||
bool scale_used = false;
|
||||
|
||||
Vector3 loc_accum;
|
||||
Quaternion rot_accum;
|
||||
Vector3 scale_accum;
|
||||
|
@ -134,6 +140,7 @@ private:
|
|||
|
||||
Map<StringName, BezierAnim> bezier_anim;
|
||||
|
||||
uint32_t last_setup_pass = 0;
|
||||
TrackNodeCache() {}
|
||||
};
|
||||
|
||||
|
|
|
@ -544,13 +544,18 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
|
|||
NodePath path = anim->track_get_path(i);
|
||||
Animation::TrackType track_type = anim->track_get_type(i);
|
||||
|
||||
Animation::TrackType track_cache_type = track_type;
|
||||
if (track_cache_type == Animation::TYPE_POSITION_3D || track_cache_type == Animation::TYPE_ROTATION_3D || track_cache_type == Animation::TYPE_SCALE_3D) {
|
||||
track_cache_type = Animation::TYPE_POSITION_3D; //reference them as position3D tracks, even if they modify rotation or scale
|
||||
}
|
||||
|
||||
TrackCache *track = nullptr;
|
||||
if (track_cache.has(path)) {
|
||||
track = track_cache.get(path);
|
||||
}
|
||||
|
||||
//if not valid, delete track
|
||||
if (track && (track->type != track_type || ObjectDB::get_instance(track->object_id) == nullptr)) {
|
||||
if (track && (track->type != track_cache_type || ObjectDB::get_instance(track->object_id) == nullptr)) {
|
||||
playing_caches.erase(track);
|
||||
memdelete(track);
|
||||
track_cache.erase(path);
|
||||
|
@ -587,7 +592,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
|
|||
track = track_value;
|
||||
|
||||
} break;
|
||||
case Animation::TYPE_TRANSFORM3D: {
|
||||
case Animation::TYPE_POSITION_3D:
|
||||
case Animation::TYPE_ROTATION_3D:
|
||||
case Animation::TYPE_SCALE_3D: {
|
||||
#ifndef _3D_DISABLED
|
||||
Node3D *node_3d = Object::cast_to<Node3D>(child);
|
||||
|
||||
|
@ -597,6 +604,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
|
|||
}
|
||||
|
||||
TrackCacheTransform *track_xform = memnew(TrackCacheTransform);
|
||||
track_xform->type = Animation::TYPE_POSITION_3D;
|
||||
|
||||
track_xform->node_3d = node_3d;
|
||||
track_xform->skeleton = nullptr;
|
||||
|
@ -615,6 +623,21 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
|
|||
track_xform->object_id = track_xform->object->get_instance_id();
|
||||
|
||||
track = track_xform;
|
||||
|
||||
switch (track_type) {
|
||||
case Animation::TYPE_POSITION_3D: {
|
||||
track_xform->loc_used = true;
|
||||
} break;
|
||||
case Animation::TYPE_ROTATION_3D: {
|
||||
track_xform->rot_used = true;
|
||||
} break;
|
||||
case Animation::TYPE_SCALE_3D: {
|
||||
track_xform->scale_used = true;
|
||||
} break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _3D_DISABLED
|
||||
} break;
|
||||
case Animation::TYPE_METHOD: {
|
||||
|
@ -670,6 +693,26 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
|
|||
}
|
||||
|
||||
track_cache[path] = track;
|
||||
} else if (track_cache_type == Animation::TYPE_POSITION_3D) {
|
||||
TrackCacheTransform *track_xform = static_cast<TrackCacheTransform *>(track);
|
||||
if (track->setup_pass != setup_pass) {
|
||||
track_xform->loc_used = false;
|
||||
track_xform->rot_used = false;
|
||||
track_xform->scale_used = false;
|
||||
}
|
||||
switch (track_type) {
|
||||
case Animation::TYPE_POSITION_3D: {
|
||||
track_xform->loc_used = true;
|
||||
} break;
|
||||
case Animation::TYPE_ROTATION_3D: {
|
||||
track_xform->rot_used = true;
|
||||
} break;
|
||||
case Animation::TYPE_SCALE_3D: {
|
||||
track_xform->scale_used = true;
|
||||
} break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
track->setup_pass = setup_pass;
|
||||
|
@ -831,8 +874,11 @@ void AnimationTree::_process_graph(real_t p_delta) {
|
|||
ERR_CONTINUE(!track_cache.has(path));
|
||||
|
||||
TrackCache *track = track_cache[path];
|
||||
if (track->type != a->track_get_type(i)) {
|
||||
continue; //may happen should not
|
||||
|
||||
Animation::TrackType ttype = a->track_get_type(i);
|
||||
if (ttype != Animation::TYPE_POSITION_3D && ttype != Animation::TYPE_ROTATION_3D && ttype != Animation::TYPE_SCALE_3D && track->type != ttype) {
|
||||
//broken animation, but avoid error spamming
|
||||
continue;
|
||||
}
|
||||
|
||||
track->root_motion = root_motion_track == path;
|
||||
|
@ -848,20 +894,20 @@ void AnimationTree::_process_graph(real_t p_delta) {
|
|||
continue; //nothing to blend
|
||||
}
|
||||
|
||||
switch (track->type) {
|
||||
case Animation::TYPE_TRANSFORM3D: {
|
||||
switch (ttype) {
|
||||
case Animation::TYPE_POSITION_3D: {
|
||||
#ifndef _3D_DISABLED
|
||||
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
|
||||
|
||||
if (track->root_motion) {
|
||||
if (t->process_pass != process_pass) {
|
||||
t->process_pass = process_pass;
|
||||
t->loc = Vector3();
|
||||
t->rot = Quaternion();
|
||||
t->rot_blend_accum = 0;
|
||||
t->scale = Vector3(1, 1, 1);
|
||||
}
|
||||
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()) {
|
||||
|
@ -872,60 +918,104 @@ void AnimationTree::_process_graph(real_t p_delta) {
|
|||
}
|
||||
|
||||
Vector3 loc[2];
|
||||
Quaternion rot[2];
|
||||
Vector3 scale[2];
|
||||
|
||||
if (prev_time > time) {
|
||||
Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]);
|
||||
Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
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->scale += (scale[1] - scale[0]) * blend;
|
||||
Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
|
||||
t->rot = (t->rot * q).normalized();
|
||||
|
||||
prev_time = 0;
|
||||
}
|
||||
|
||||
Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]);
|
||||
Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
|
||||
if (err != OK) {
|
||||
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->scale += (scale[1] - scale[0]) * blend;
|
||||
Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
|
||||
t->rot = (t->rot * q).normalized();
|
||||
|
||||
prev_time = 0;
|
||||
|
||||
} else {
|
||||
Vector3 loc;
|
||||
Quaternion rot;
|
||||
Vector3 scale;
|
||||
|
||||
Error err = a->transform_track_interpolate(i, time, &loc, &rot, &scale);
|
||||
Error err = a->position_track_interpolate(i, time, &loc);
|
||||
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
|
||||
|
||||
if (t->process_pass != process_pass) {
|
||||
t->process_pass = process_pass;
|
||||
t->loc = loc;
|
||||
t->rot = rot;
|
||||
t->rot_blend_accum = 0;
|
||||
t->scale = scale;
|
||||
}
|
||||
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
t->loc = t->loc.lerp(loc, blend);
|
||||
}
|
||||
#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) {
|
||||
t->rot = rot;
|
||||
t->rot_blend_accum = blend;
|
||||
|
@ -934,6 +1024,67 @@ void AnimationTree::_process_graph(real_t p_delta) {
|
|||
t->rot = rot.slerp(t->rot, t->rot_blend_accum / rot_total).normalized();
|
||||
t->rot_blend_accum = rot_total;
|
||||
}
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
} break;
|
||||
case Animation::TYPE_SCALE_3D: {
|
||||
#ifndef _3D_DISABLED
|
||||
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
|
||||
|
||||
if (t->process_pass != process_pass) {
|
||||
t->process_pass = process_pass;
|
||||
t->loc = Vector3();
|
||||
t->rot = Quaternion();
|
||||
t->rot_blend_accum = 0;
|
||||
t->scale = Vector3(1, 1, 1);
|
||||
}
|
||||
|
||||
if (track->root_motion) {
|
||||
real_t prev_time = time - delta;
|
||||
if (prev_time < 0) {
|
||||
if (!a->has_loop()) {
|
||||
prev_time = 0;
|
||||
} else {
|
||||
prev_time = a->get_length() + prev_time;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 scale[2];
|
||||
|
||||
if (prev_time > time) {
|
||||
Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
a->scale_track_interpolate(i, a->get_length(), &scale[1]);
|
||||
|
||||
t->scale += (scale[1] - scale[0]) * blend;
|
||||
|
||||
prev_time = 0;
|
||||
}
|
||||
|
||||
Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
a->scale_track_interpolate(i, time, &scale[1]);
|
||||
|
||||
t->scale += (scale[1] - scale[0]) * blend;
|
||||
|
||||
prev_time = 0;
|
||||
|
||||
} else {
|
||||
Vector3 scale;
|
||||
|
||||
Error err = a->scale_track_interpolate(i, time, &scale);
|
||||
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
|
||||
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
t->scale = t->scale.lerp(scale, blend);
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
|
@ -1198,26 +1349,41 @@ void AnimationTree::_process_graph(real_t p_delta) {
|
|||
}
|
||||
|
||||
switch (track->type) {
|
||||
case Animation::TYPE_TRANSFORM3D: {
|
||||
case Animation::TYPE_POSITION_3D: {
|
||||
#ifndef _3D_DISABLED
|
||||
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
|
||||
|
||||
Transform3D xform;
|
||||
xform.origin = t->loc;
|
||||
|
||||
xform.basis.set_quaternion_scale(t->rot, t->scale);
|
||||
|
||||
if (t->root_motion) {
|
||||
Transform3D xform;
|
||||
xform.origin = t->loc;
|
||||
xform.basis.set_quaternion_scale(t->rot, t->scale);
|
||||
|
||||
root_motion_transform = xform;
|
||||
|
||||
if (t->skeleton && t->bone_idx >= 0) {
|
||||
root_motion_transform = (t->skeleton->get_bone_rest(t->bone_idx) * root_motion_transform) * t->skeleton->get_bone_rest(t->bone_idx).affine_inverse();
|
||||
}
|
||||
} else if (t->skeleton && t->bone_idx >= 0) {
|
||||
t->skeleton->set_bone_pose(t->bone_idx, xform);
|
||||
if (t->loc_used) {
|
||||
t->skeleton->set_bone_pose_position(t->bone_idx, t->loc);
|
||||
}
|
||||
if (t->rot_used) {
|
||||
t->skeleton->set_bone_pose_rotation(t->bone_idx, t->rot);
|
||||
}
|
||||
if (t->scale_used) {
|
||||
t->skeleton->set_bone_pose_scale(t->bone_idx, t->scale);
|
||||
}
|
||||
|
||||
} else if (!t->skeleton) {
|
||||
t->node_3d->set_transform(xform);
|
||||
if (t->loc_used) {
|
||||
t->node_3d->set_position(t->loc);
|
||||
}
|
||||
if (t->rot_used) {
|
||||
t->node_3d->set_rotation(t->rot.get_euler());
|
||||
}
|
||||
if (t->scale_used) {
|
||||
t->node_3d->set_scale(t->scale);
|
||||
}
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
} break;
|
||||
|
|
|
@ -197,13 +197,16 @@ private:
|
|||
Skeleton3D *skeleton = nullptr;
|
||||
#endif // _3D_DISABLED
|
||||
int bone_idx = -1;
|
||||
bool loc_used = false;
|
||||
bool rot_used = false;
|
||||
bool scale_used = false;
|
||||
Vector3 loc;
|
||||
Quaternion rot;
|
||||
real_t rot_blend_accum = 0.0;
|
||||
Vector3 scale;
|
||||
|
||||
TrackCacheTransform() {
|
||||
type = Animation::TYPE_TRANSFORM3D;
|
||||
type = Animation::TYPE_POSITION_3D;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -42,7 +42,9 @@ class Animation : public Resource {
|
|||
public:
|
||||
enum TrackType {
|
||||
TYPE_VALUE, ///< Set a value in a property, can be interpolated.
|
||||
TYPE_TRANSFORM3D, ///< Transform a node or a bone.
|
||||
TYPE_POSITION_3D, ///< Position 3D track
|
||||
TYPE_ROTATION_3D, ///< Rotation 3D track
|
||||
TYPE_SCALE_3D, ///< Scale 3D track
|
||||
TYPE_METHOD, ///< Call any method on a specific node.
|
||||
TYPE_BEZIER, ///< Bezier curve
|
||||
TYPE_AUDIO,
|
||||
|
@ -86,21 +88,31 @@ private:
|
|||
T value;
|
||||
};
|
||||
|
||||
struct TransformKey {
|
||||
Vector3 loc;
|
||||
Quaternion rot;
|
||||
Vector3 scale;
|
||||
const int32_t POSITION_TRACK_SIZE = 5;
|
||||
const int32_t ROTATION_TRACK_SIZE = 6;
|
||||
const int32_t SCALE_TRACK_SIZE = 5;
|
||||
|
||||
/* POSITION TRACK */
|
||||
|
||||
struct PositionTrack : public Track {
|
||||
Vector<TKey<Vector3>> positions;
|
||||
|
||||
PositionTrack() { type = TYPE_POSITION_3D; }
|
||||
};
|
||||
|
||||
// Not necessarily the same size as Transform3D. The amount of numbers in Animation::Key and TransformKey.
|
||||
const int32_t TRANSFORM_TRACK_SIZE = 12;
|
||||
/* ROTATION TRACK */
|
||||
|
||||
/* TRANSFORM TRACK */
|
||||
struct RotationTrack : public Track {
|
||||
Vector<TKey<Quaternion>> rotations;
|
||||
|
||||
struct TransformTrack : public Track {
|
||||
Vector<TKey<TransformKey>> transforms;
|
||||
RotationTrack() { type = TYPE_ROTATION_3D; }
|
||||
};
|
||||
|
||||
TransformTrack() { type = TYPE_TRANSFORM3D; }
|
||||
/* SCALE TRACK */
|
||||
|
||||
struct ScaleTrack : public Track {
|
||||
Vector<TKey<Vector3>> scales;
|
||||
ScaleTrack() { type = TYPE_SCALE_3D; }
|
||||
};
|
||||
|
||||
/* PROPERTY VALUE TRACK */
|
||||
|
@ -186,14 +198,11 @@ private:
|
|||
template <class K>
|
||||
inline int _find(const Vector<K> &p_keys, double p_time) const;
|
||||
|
||||
_FORCE_INLINE_ Animation::TransformKey _interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const;
|
||||
|
||||
_FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const;
|
||||
_FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const;
|
||||
_FORCE_INLINE_ Variant _interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const;
|
||||
_FORCE_INLINE_ real_t _interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const;
|
||||
|
||||
_FORCE_INLINE_ Animation::TransformKey _cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const;
|
||||
_FORCE_INLINE_ Vector3 _cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const;
|
||||
_FORCE_INLINE_ Quaternion _cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const;
|
||||
_FORCE_INLINE_ Variant _cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const;
|
||||
|
@ -214,18 +223,6 @@ private:
|
|||
|
||||
// bind helpers
|
||||
private:
|
||||
Array _transform_track_interpolate(int p_track, double p_time) const {
|
||||
Vector3 loc;
|
||||
Quaternion rot;
|
||||
Vector3 scale;
|
||||
transform_track_interpolate(p_track, p_time, &loc, &rot, &scale);
|
||||
Array ret;
|
||||
ret.push_back(loc);
|
||||
ret.push_back(rot);
|
||||
ret.push_back(scale);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Vector<int> _value_track_get_key_indices(int p_track, double p_time, double p_delta) const {
|
||||
List<int> idxs;
|
||||
value_track_get_key_indices(p_track, p_time, p_delta, &idxs);
|
||||
|
@ -247,8 +244,13 @@ private:
|
|||
return idxr;
|
||||
}
|
||||
|
||||
bool _transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm);
|
||||
void _transform_track_optimize(int p_idx, real_t p_allowed_linear_err = 0.05, real_t p_allowed_angular_err = 0.01, real_t p_max_optimizable_angle = Math_PI * 0.125);
|
||||
bool _position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_alowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm);
|
||||
bool _rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle);
|
||||
bool _scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error);
|
||||
|
||||
void _position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err);
|
||||
void _rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle);
|
||||
void _scale_track_optimize(int p_idx, real_t p_allowed_linear_err);
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
|
@ -294,8 +296,18 @@ public:
|
|||
double track_get_key_time(int p_track, int p_key_idx) const;
|
||||
real_t track_get_key_transition(int p_track, int p_key_idx) const;
|
||||
|
||||
int transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot = Quaternion(), const Vector3 &p_scale = Vector3());
|
||||
Error transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
|
||||
int position_track_insert_key(int p_track, double p_time, const Vector3 &p_position);
|
||||
Error position_track_get_key(int p_track, int p_key, Vector3 *r_position) const;
|
||||
Error position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
|
||||
|
||||
int rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation);
|
||||
Error rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const;
|
||||
Error rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const;
|
||||
|
||||
int scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale);
|
||||
Error scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const;
|
||||
Error scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
|
||||
|
||||
void track_set_interpolation_type(int p_track, InterpolationType p_interp);
|
||||
InterpolationType track_get_interpolation_type(int p_track) const;
|
||||
|
||||
|
@ -324,8 +336,6 @@ public:
|
|||
void track_set_interpolation_loop_wrap(int p_track, bool p_enable);
|
||||
bool track_get_interpolation_loop_wrap(int p_track) const;
|
||||
|
||||
Error transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
|
||||
|
||||
Variant value_track_interpolate(int p_track, double p_time) const;
|
||||
void value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
|
||||
void value_track_set_update_mode(int p_track, UpdateMode p_mode);
|
||||
|
|
Loading…
Reference in New Issue