From ec19ed37230c368b3d24050c9f1e99869dd94bec Mon Sep 17 00:00:00 2001 From: reduz Date: Mon, 11 Oct 2021 19:20:58 -0300 Subject: [PATCH] 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. --- doc/classes/Animation.xml | 58 +- doc/classes/AnimationTree.xml | 4 +- doc/classes/Skeleton3D.xml | 38 +- editor/animation_track_editor.cpp | 296 +++-- editor/animation_track_editor.h | 2 +- editor/icons/KeyTrackPosition.svg | 47 + editor/icons/KeyTrackRotation.svg | 47 + editor/icons/KeyTrackScale.svg | 47 + editor/icons/KeyXPosition.svg | 43 + editor/icons/KeyXRotation.svg | 44 + editor/icons/KeyXScale.svg | 44 + editor/import/editor_import_collada.cpp | 129 ++- editor/import/editor_importer_bake_reset.cpp | 234 ---- editor/import/editor_importer_bake_reset.h | 54 - editor/import/resource_importer_scene.cpp | 87 +- editor/import/resource_importer_scene.h | 7 + editor/plugins/skeleton_3d_editor_plugin.cpp | 4 +- modules/fbx/editor_scene_importer_fbx.cpp | 43 +- modules/gltf/gltf_document.cpp | 96 +- scene/3d/skeleton_3d.cpp | 81 +- scene/3d/skeleton_3d.h | 23 +- scene/animation/animation_cache.cpp | 314 ------ scene/animation/animation_cache.h | 84 -- scene/animation/animation_player.cpp | 168 ++- scene/animation/animation_player.h | 7 + scene/animation/animation_tree.cpp | 258 ++++- scene/animation/animation_tree.h | 5 +- scene/resources/animation.cpp | 1033 +++++++++++------- scene/resources/animation.h | 74 +- 29 files changed, 1952 insertions(+), 1419 deletions(-) create mode 100644 editor/icons/KeyTrackPosition.svg create mode 100644 editor/icons/KeyTrackRotation.svg create mode 100644 editor/icons/KeyTrackScale.svg create mode 100644 editor/icons/KeyXPosition.svg create mode 100644 editor/icons/KeyXRotation.svg create mode 100644 editor/icons/KeyXScale.svg delete mode 100644 editor/import/editor_importer_bake_reset.cpp delete mode 100644 editor/import/editor_importer_bake_reset.h delete mode 100644 scene/animation/animation_cache.cpp delete mode 100644 scene/animation/animation_cache.h diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index d2ecbdde26..a35b784106 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -253,6 +253,14 @@ Returns the arguments values to be called on a method track for a given key in a given track. + + + + + + + + @@ -260,6 +268,22 @@ Removes a track by specifying the track index. + + + + + + + + + + + + + + + + @@ -466,25 +490,6 @@ Swaps the track [code]idx[/code]'s index position with the track [code]with_idx[/code]. - - - - - - - - - Insert a transform key for a transform track. - - - - - - - - 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]). - - @@ -541,19 +546,22 @@ Value tracks set values in node properties, but only those which can be Interpolated. - - Transform3D tracks are used to change node local transforms or skeleton pose bones of 3D nodes. Transitions are interpolated. + - + + + + + Method tracks call functions with given arguments per key. - + 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]). - + Audio tracks are used to play an audio stream with either type of [AudioStreamPlayer]. The stream can be trimmed and previewed in the animation. - + Animation tracks play animations in other [AnimationPlayer] nodes. diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml index 3d112e258e..40dcd950d7 100644 --- a/doc/classes/AnimationTree.xml +++ b/doc/classes/AnimationTree.xml @@ -22,7 +22,7 @@ - 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]. @@ -45,7 +45,7 @@ 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]. The root animation node of this [AnimationTree]. See [AnimationNode]. diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml index 6ab5ed55a3..e046527b17 100644 --- a/doc/classes/Skeleton3D.xml +++ b/doc/classes/Skeleton3D.xml @@ -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. + + + + + + + + + + + + + + + + + + @@ -337,13 +355,25 @@ [b]Note:[/b] [code]parent_idx[/code] must be less than [code]bone_idx[/code]. - + - + + + + + + + + + + + + + + + - 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. diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 14ca35a664..2ee7f0ffa5 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -165,20 +165,38 @@ public: } switch (animation->track_get_type(track)) { - case Animation::TYPE_TRANSFORM3D: { - Dictionary d_old = animation->track_get_key_value(track, key); - Dictionary d_new = d_old.duplicate(); - d_new[p_name] = p_value; - setting = true; - undo_redo->create_action(TTR("Anim Change Transform")); - undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new); - undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old); - undo_redo->add_do_method(this, "_update_obj", animation); - undo_redo->add_undo_method(this, "_update_obj", animation); - undo_redo->commit_action(); + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + if (name == "position" || name == "rotation" || name == "scale") { + Variant old = animation->track_get_key_value(track, key); + setting = true; + String chan; + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + chan = "Position3D"; + break; + case Animation::TYPE_ROTATION_3D: + chan = "Rotation3D"; + break; + case Animation::TYPE_SCALE_3D: + chan = "Scale3D"; + break; + default: { + } + } + + undo_redo->create_action(vformat(TTR("Anim Change %s"), chan)); + undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value); + undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old); + undo_redo->add_do_method(this, "_update_obj", animation); + undo_redo->add_undo_method(this, "_update_obj", animation); + undo_redo->commit_action(); + + setting = false; + return true; + } - setting = false; - return true; } break; case Animation::TYPE_VALUE: { if (name == "value") { @@ -412,12 +430,13 @@ public: } switch (animation->track_get_type(track)) { - case Animation::TYPE_TRANSFORM3D: { - Dictionary d = animation->track_get_key_value(track, key); - ERR_FAIL_COND_V(!d.has(name), false); - r_ret = d[p_name]; - return true; - + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + if (name == "position" || name == "rotation" || name == "scale") { + r_ret = animation->track_get_key_value(track, key); + return true; + } } break; case Animation::TYPE_VALUE: { if (name == "value") { @@ -523,11 +542,14 @@ public: } switch (animation->track_get_type(track)) { - case Animation::TYPE_TRANSFORM3D: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, "location")); - p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation")); + case Animation::TYPE_POSITION_3D: { + p_list->push_back(PropertyInfo(Variant::VECTOR3, "position")); + } break; + case Animation::TYPE_ROTATION_3D: { + p_list->push_back(PropertyInfo(Variant::VECTOR3, "rotation")); + } break; + case Animation::TYPE_SCALE_3D: { p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale")); - } break; case Animation::TYPE_VALUE: { Variant v = animation->track_get_key_value(track, key); @@ -779,17 +801,31 @@ public: } switch (animation->track_get_type(track)) { - case Animation::TYPE_TRANSFORM3D: { - Dictionary d_old = animation->track_get_key_value(track, key); - Dictionary d_new = d_old.duplicate(); - d_new[p_name] = p_value; - + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + Variant old = animation->track_get_key_value(track, key); if (!setting) { + String chan; + switch (animation->track_get_type(track)) { + case Animation::TYPE_POSITION_3D: + chan = "Position3D"; + break; + case Animation::TYPE_ROTATION_3D: + chan = "Rotation3D"; + break; + case Animation::TYPE_SCALE_3D: + chan = "Scale3D"; + break; + default: { + } + } + setting = true; - undo_redo->create_action(TTR("Anim Multi Change Transform")); + undo_redo->create_action(vformat(TTR("Anim Multi Change %s"), chan)); } - undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, d_new); - undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, d_old); + undo_redo->add_do_method(animation.ptr(), "track_set_key_value", track, key, p_value); + undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old); update_obj = true; } break; case Animation::TYPE_VALUE: { @@ -1009,11 +1045,13 @@ public: } switch (animation->track_get_type(track)) { - case Animation::TYPE_TRANSFORM3D: { - Dictionary d = animation->track_get_key_value(track, key); - ERR_FAIL_COND_V(!d.has(name), false); - r_ret = d[p_name]; - return true; + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + if (name == "position" || name == "rotation" || name == "scale") { + r_ret = animation->track_get_key_value(track, key); + return true; + } } break; case Animation::TYPE_VALUE: { @@ -1159,9 +1197,13 @@ public: if (same_track_type) { switch (animation->track_get_type(first_track)) { - case Animation::TYPE_TRANSFORM3D: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, "location")); - p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation")); + case Animation::TYPE_POSITION_3D: { + p_list->push_back(PropertyInfo(Variant::VECTOR3, "position")); + } break; + case Animation::TYPE_ROTATION_3D: { + p_list->push_back(PropertyInfo(Variant::QUATERNION, "scale")); + } break; + case Animation::TYPE_SCALE_3D: { p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale")); } break; case Animation::TYPE_VALUE: { @@ -1359,7 +1401,9 @@ void AnimationTimelineEdit::_notification(int p_what) { add_track->get_popup()->clear(); add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")), TTR("Property Track")); - add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXform"), SNAME("EditorIcons")), TTR("3D Transform Track")); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")), TTR("3D Position Track")); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")), TTR("3D Rotation Track")); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")), TTR("3D Scale Track")); add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")), TTR("Call Method Track")); add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), TTR("Bezier Curve Track")); add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")), TTR("Audio Playback Track")); @@ -1828,9 +1872,11 @@ void AnimationTrackEdit::_notification(int p_what) { Ref font = 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 type_icons[6] = { + Ref 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 &p_animati track = p_track; update(); - Ref type_icons[6] = { + Ref 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()); 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(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(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)"; diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 576edef0c8..2555901557 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -35,7 +35,7 @@ #include "editor/editor_spin_slider.h" #include "editor/property_editor.h" #include "editor/property_selector.h" -#include "scene/animation/animation_cache.h" + #include "scene/gui/control.h" #include "scene/gui/file_dialog.h" #include "scene/gui/menu_button.h" diff --git a/editor/icons/KeyTrackPosition.svg b/editor/icons/KeyTrackPosition.svg new file mode 100644 index 0000000000..05c6e88841 --- /dev/null +++ b/editor/icons/KeyTrackPosition.svg @@ -0,0 +1,47 @@ + + + + + + + diff --git a/editor/icons/KeyTrackRotation.svg b/editor/icons/KeyTrackRotation.svg new file mode 100644 index 0000000000..d05e381eb2 --- /dev/null +++ b/editor/icons/KeyTrackRotation.svg @@ -0,0 +1,47 @@ + + + + + + + diff --git a/editor/icons/KeyTrackScale.svg b/editor/icons/KeyTrackScale.svg new file mode 100644 index 0000000000..9269ccbca2 --- /dev/null +++ b/editor/icons/KeyTrackScale.svg @@ -0,0 +1,47 @@ + + + + + + + diff --git a/editor/icons/KeyXPosition.svg b/editor/icons/KeyXPosition.svg new file mode 100644 index 0000000000..5816a241c9 --- /dev/null +++ b/editor/icons/KeyXPosition.svg @@ -0,0 +1,43 @@ + + + + + + diff --git a/editor/icons/KeyXRotation.svg b/editor/icons/KeyXRotation.svg new file mode 100644 index 0000000000..6cd5a67ac0 --- /dev/null +++ b/editor/icons/KeyXRotation.svg @@ -0,0 +1,44 @@ + + + + + + diff --git a/editor/icons/KeyXScale.svg b/editor/icons/KeyXScale.svg new file mode 100644 index 0000000000..588fa5d2f3 --- /dev/null +++ b/editor/icons/KeyXScale.svg @@ -0,0 +1,44 @@ + + + + + + diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp index 4b01595028..022dc899da 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/editor_import_collada.cpp @@ -91,8 +91,8 @@ struct ColladaImport { Error _create_mesh_surfaces(bool p_optimize, Ref &p_mesh, const Map &p_material_map, const Collada::MeshData &meshdata, const Transform3D &p_local_xform, const Vector &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector> p_morph_meshes = Vector>(), 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 tracks_in_clips; Vector 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 = Ref(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 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 &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(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 EditorSceneImporterCollada::import_animation(const String &p_path Error err = state.load(p_path, Collada::IMPORT_FLAG_ANIMATION, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS); ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load animation from file '" + p_path + "'."); - state.create_animations(true, true); + state.create_animations(true); if (state.scene) { memdelete(state.scene); } diff --git a/editor/import/editor_importer_bake_reset.cpp b/editor/import/editor_importer_bake_reset.cpp deleted file mode 100644 index 451a07351c..0000000000 --- a/editor/import/editor_importer_bake_reset.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/*************************************************************************/ -/* editor_importer_bake_reset.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "editor/import/editor_importer_bake_reset.h" - -#include "core/error/error_list.h" -#include "core/error/error_macros.h" -#include "core/math/transform_3d.h" -#include "resource_importer_scene.h" -#include "scene/3d/importer_mesh_instance_3d.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/3d/node_3d.h" -#include "scene/3d/skeleton_3d.h" -#include "scene/animation/animation_player.h" - -// Given that an engineering team has made a reference character, one wants ten animators to create animations. -// Currently, a tech artist needs to combine the ten files into one exported gltf2 to import into Godot Engine. -// We bake the RESET animation and then set it to identity, -// so that rigs with corresponding RESET animation can have their animations transferred with ease. -// -// The original algorithm for the code was used to change skeleton bone rolls to be parent to child. -// -// Reference https://github.com/godotengine/godot-proposals/issues/2961 -void BakeReset::_bake_animation_pose(Node *scene, const String &p_bake_anim) { - Map r_rest_bones; - Vector r_meshes; - List queue; - queue.push_back(scene); - while (!queue.is_empty()) { - List::Element *E = queue.front(); - Node *node = E->get(); - AnimationPlayer *ap = Object::cast_to(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::Element *E = queue.front(); - Node *node = E->get(); - ImporterMeshInstance3D *editor_mesh_3d = scene->cast_to(node); - MeshInstance3D *mesh_3d = scene->cast_to(node); - if (scene->cast_to(node)) { - Skeleton3D *skeleton = Object::cast_to(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(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(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::Element *E = queue.front(); - Node *node = E->get(); - AnimationPlayer *ap = Object::cast_to(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 &r_rest_bones) { - ERR_FAIL_NULL(p_ap); - List anim_names; - p_ap->get_animation_list(&anim_names); - for (List::Element *anim_i = anim_names.front(); anim_i; anim_i = anim_i->next()) { - Ref a = p_ap->get_animation(anim_i->get()); - ERR_CONTINUE(a.is_null()); - for (const KeyValue &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 &r_rest_bones, const String &p_bake_anim) { - if (!p_ap) { - return; - } - List 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 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(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 &r_rest_bones) { - int bone_count = p_skeleton->get_bone_count(); - - // First iterate through all the bones and update the RestBone. - for (int j = 0; j < bone_count; j++) { - StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(j); - BakeResetRestBone &rest_bone = r_rest_bones[final_path]; - rest_bone.rest_local = p_skeleton->get_bone_rest(j); - } - for (int i = 0; i < bone_count; i++) { - int parent_bone = p_skeleton->get_bone_parent(i); - String path = p_skeleton->get_owner()->get_path_to(p_skeleton); - StringName final_path = String(path) + String(":") + p_skeleton->get_bone_name(parent_bone); - if (parent_bone >= 0) { - r_rest_bones[path].children.push_back(i); - } - } - - // When we apply transform to a bone, we also have to move all of its children in the opposite direction. - for (int i = 0; i < bone_count; i++) { - StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(i); - r_rest_bones[final_path].rest_local = r_rest_bones[final_path].rest_local * Transform3D(r_rest_bones[final_path].rest_delta, r_rest_bones[final_path].loc); - // Iterate through the children and move in the opposite direction. - for (int j = 0; j < r_rest_bones[final_path].children.size(); j++) { - int child_index = r_rest_bones[final_path].children[j]; - StringName children_path = String(p_skeleton->get_name()) + String(":") + p_skeleton->get_bone_name(child_index); - r_rest_bones[children_path].rest_local = Transform3D(r_rest_bones[final_path].rest_delta, r_rest_bones[final_path].loc).affine_inverse() * r_rest_bones[children_path].rest_local; - } - } - - for (int i = 0; i < bone_count; i++) { - StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(i); - ERR_CONTINUE(!r_rest_bones.has(final_path)); - Transform3D rest_transform = r_rest_bones[final_path].rest_local; - p_skeleton->set_bone_rest(i, rest_transform); - } -} diff --git a/editor/import/editor_importer_bake_reset.h b/editor/import/editor_importer_bake_reset.h deleted file mode 100644 index e36ae86181..0000000000 --- a/editor/import/editor_importer_bake_reset.h +++ /dev/null @@ -1,54 +0,0 @@ -/*************************************************************************/ -/* editor_importer_bake_reset.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef RESOURCE_IMPORTER_BAKE_RESET_H -#define RESOURCE_IMPORTER_BAKE_RESET_H - -#include "scene/main/node.h" - -class Skeleton3D; -class AnimationPlayer; -class BakeReset { - struct BakeResetRestBone { - Transform3D rest_local; - Basis rest_delta; - Vector3 loc; - Vector children; - }; - -public: - void _bake_animation_pose(Node *scene, const String &p_bake_anim); - -private: - void _fix_skeleton(Skeleton3D *p_skeleton, Map &r_rest_bones); - void _align_animations(AnimationPlayer *p_ap, const Map &r_rest_bones); - void _fetch_reset_animation(AnimationPlayer *p_ap, Map &r_rest_bones, const String &p_bake_anim); -}; -#endif diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 1e93113488..35f1533dd0 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -32,7 +32,7 @@ #include "core/io/resource_saver.h" #include "editor/editor_node.h" -#include "editor/import/editor_importer_bake_reset.h" + #include "editor/import/scene_import_settings.h" #include "scene/3d/area_3d.h" #include "scene/3d/collision_shape_3d.h" @@ -851,42 +851,57 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_ new_anim->track_set_path(dtrack, default_anim->track_get_path(j)); if (kt > (from + 0.01) && k > 0) { - if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) { - Quaternion q; + if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) { Vector3 p; + default_anim->position_track_interpolate(j, from, &p); + new_anim->position_track_insert_key(dtrack, 0, p); + } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) { + Quaternion r; + default_anim->rotation_track_interpolate(j, from, &r); + new_anim->rotation_track_insert_key(dtrack, 0, r); + } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) { Vector3 s; - default_anim->transform_track_interpolate(j, from, &p, &q, &s); - new_anim->transform_track_insert_key(dtrack, 0, p, q, s); - } - if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { + default_anim->scale_track_interpolate(j, from, &s); + new_anim->scale_track_insert_key(dtrack, 0, s); + } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { Variant var = default_anim->value_track_interpolate(j, from); new_anim->track_insert_key(dtrack, 0, var); } } } - if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) { - Quaternion q; + if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) { Vector3 p; + default_anim->position_track_get_key(j, k, &p); + new_anim->position_track_insert_key(dtrack, kt - from, p); + } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) { + Quaternion r; + default_anim->rotation_track_get_key(j, k, &r); + new_anim->rotation_track_insert_key(dtrack, kt - from, r); + } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) { Vector3 s; - default_anim->transform_track_get_key(j, k, &p, &q, &s); - new_anim->transform_track_insert_key(dtrack, kt - from, p, q, s); - } - if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { + default_anim->scale_track_get_key(j, k, &s); + new_anim->scale_track_insert_key(dtrack, kt - from, s); + } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { Variant var = default_anim->track_get_key_value(j, k); new_anim->track_insert_key(dtrack, kt - from, var); } } if (dtrack != -1 && kt >= to) { - if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) { - Quaternion q; + if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) { Vector3 p; + default_anim->position_track_interpolate(j, to, &p); + new_anim->position_track_insert_key(dtrack, to - from, p); + } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) { + Quaternion r; + default_anim->rotation_track_interpolate(j, to, &r); + new_anim->rotation_track_insert_key(dtrack, to - from, r); + } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) { Vector3 s; - default_anim->transform_track_interpolate(j, to, &p, &q, &s); - new_anim->transform_track_insert_key(dtrack, to - from, p, q, s); - } - if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { + default_anim->scale_track_interpolate(j, to, &s); + new_anim->scale_track_insert_key(dtrack, to - from, s); + } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { Variant var = default_anim->value_track_interpolate(j, to); new_anim->track_insert_key(dtrack, to - from, var); } @@ -897,16 +912,25 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_ new_anim->add_track(default_anim->track_get_type(j)); dtrack = new_anim->get_track_count() - 1; new_anim->track_set_path(dtrack, default_anim->track_get_path(j)); - if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) { - Quaternion q; + if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) { Vector3 p; + default_anim->position_track_interpolate(j, from, &p); + new_anim->position_track_insert_key(dtrack, 0, p); + default_anim->position_track_interpolate(j, to, &p); + new_anim->position_track_insert_key(dtrack, to - from, p); + } else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) { + Quaternion r; + default_anim->rotation_track_interpolate(j, from, &r); + new_anim->rotation_track_insert_key(dtrack, 0, r); + default_anim->rotation_track_interpolate(j, to, &r); + new_anim->rotation_track_insert_key(dtrack, to - from, r); + } else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) { Vector3 s; - default_anim->transform_track_interpolate(j, from, &p, &q, &s); - new_anim->transform_track_insert_key(dtrack, 0, p, q, s); - default_anim->transform_track_interpolate(j, to, &p, &q, &s); - new_anim->transform_track_insert_key(dtrack, to - from, p, q, s); - } - if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { + default_anim->scale_track_interpolate(j, from, &s); + new_anim->scale_track_insert_key(dtrack, 0, s); + default_anim->scale_track_interpolate(j, to, &s); + new_anim->scale_track_insert_key(dtrack, to - from, s); + } else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) { Variant var = default_anim->value_track_interpolate(j, from); new_anim->track_insert_key(dtrack, 0, var); Variant to_var = default_anim->value_track_interpolate(j, to); @@ -1000,6 +1024,9 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_linear_error"), 0.05)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error"), 0.01)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angle"), 22)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/position", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/rotation", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/scale", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/amount", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0)); for (int i = 0; i < 256; i++) { @@ -1171,7 +1198,6 @@ void ResourceImporterScene::get_import_options(List *r_options, in r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true)); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/bake_reset_animation"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15)); r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), "")); @@ -1533,11 +1559,6 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p _pre_fix_node(scene, scene, collision_map); _post_fix_node(scene, scene, collision_map, scanned_meshes, node_data, material_data, animation_data, fps); - bool use_bake_reset_animation = p_options["animation/bake_reset_animation"]; - if (use_bake_reset_animation) { - BakeReset bake_reset; - bake_reset._bake_animation_pose(scene, "RESET"); - } String root_type = p_options["nodes/root_type"]; root_type = root_type.split(" ")[0]; // full root_type is "ClassName (filename.gd)" for a script global class. diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 2a67fa9aae..eb17193c00 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -65,6 +65,13 @@ public: IMPORT_USE_NAMED_SKIN_BINDS = 16, }; + enum AnimationImportBoneTracks { + ANIMATION_IMPORT_BONE_TRACKS_IF_PRESENT, + ANIMATION_IMPORT_BONE_TRACKS_IF_PRESENT_FOR_ALL, + ANIMATION_IMPORT_BONE_TRACKS_ALWAYS, + ANIMATION_IMPORT_BONE_TRACKS_NEVER, + }; + virtual uint32_t get_import_flags() const; virtual void get_extensions(List *r_extensions) const; virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List *r_missing_deps, Error *r_err = nullptr); diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 531ffc6a73..3a67c5415c 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -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 &p_ids, const Vector &p_restore, bool p_cancel) { diff --git a/modules/fbx/editor_scene_importer_fbx.cpp b/modules/fbx/editor_scene_importer_fbx.cpp index e4de204cf1..e90eab522f 100644 --- a/modules/fbx/editor_scene_importer_fbx.cpp +++ b/modules/fbx/editor_scene_importer_fbx.cpp @@ -1011,9 +1011,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene( // track count is 5. // next track id is 5. const uint64_t target_id = track.key; - int track_idx = animation->add_track(Animation::TYPE_TRANSFORM3D); - // animation->track_set_path(track_idx, node_path); Ref 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 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 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; diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index ba98592600..32540e7a22 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -5733,7 +5733,7 @@ struct EditorSceneImporterGLTFInterpolate { template T GLTFDocument::_interpolate_track(const Vector &p_times, const Vector &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 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 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 state, AnimationPlayer *ap, Quaternion rot = base_rot; Vector3 scale = base_scale; - if (track.position_track.times.size()) { + if (position_idx >= 0) { pos = _interpolate_track(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(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(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation); } @@ -5925,7 +5983,15 @@ void GLTFDocument::_import_animation(Ref 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 state, Node *scene_roo } GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref state, GLTFAnimation::Track p_track, Ref 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 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 state } } } - +#endif return p_track; } diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 2b52f034b2..79504b10bb 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -99,8 +99,12 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { set_bone_rest(which, p_value); } else if (what == "enabled") { set_bone_enabled(which, p_value); - } else if (what == "pose") { - set_bone_pose(which, p_value); + } else if (what == "position") { + set_bone_pose_position(which, p_value); + } else if (what == "rotation") { + set_bone_pose_rotation(which, p_value); + } else if (what == "scale") { + set_bone_pose_scale(which, p_value); } else { return false; } @@ -135,8 +139,12 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { r_ret = get_bone_rest(which); } else if (what == "enabled") { r_ret = is_bone_enabled(which); - } else if (what == "pose") { - r_ret = get_bone_pose(which); + } else if (what == "position") { + r_ret = get_bone_pose_position(which); + } else if (what == "rotation") { + r_ret = get_bone_pose_rotation(which); + } else if (what == "scale") { + r_ret = get_bone_pose_scale(which); } else { return false; } @@ -151,7 +159,9 @@ void Skeleton3D::_get_property_list(List *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)); diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 9aa4fc823f..aacee3da4e 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -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; diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp deleted file mode 100644 index 56743007e4..0000000000 --- a/scene/animation/animation_cache.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/*************************************************************************/ -/* animation_cache.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "animation_cache.h" - -void AnimationCache::_node_exit_tree(Node *p_node) { - //it is one shot, so it disconnects upon arrival - - ERR_FAIL_COND(!connected_nodes.has(p_node)); - - connected_nodes.erase(p_node); - - for (int i = 0; i < path_cache.size(); i++) { - if (path_cache[i].node != p_node) { - continue; - } - - path_cache.write[i].valid = false; //invalidate path cache - } -} - -void AnimationCache::_animation_changed() { - _clear_cache(); -} - -void AnimationCache::_clear_cache() { - while (connected_nodes.size()) { - connected_nodes.front()->get()->disconnect("tree_exiting", callable_mp(this, &AnimationCache::_node_exit_tree)); - connected_nodes.erase(connected_nodes.front()); - } - path_cache.clear(); - cache_valid = false; - cache_dirty = true; -} - -void AnimationCache::_update_cache() { - cache_valid = false; - - ERR_FAIL_COND(!root); - ERR_FAIL_COND(!root->is_inside_tree()); - ERR_FAIL_COND(animation.is_null()); - - for (int i = 0; i < animation->get_track_count(); i++) { - NodePath np = animation->track_get_path(i); - - Node *node = root->get_node(np); - if (!node) { - path_cache.push_back(Path()); - ERR_CONTINUE_MSG(!node, "Invalid track path in animation '" + np + "'."); - } - - Path path; - - Ref 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(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(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 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 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 indices; - animation->method_track_get_key_indices(i, p_time, p_delta, &indices); - - for (int &E : indices) { - Vector 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 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 &p_animation) { - _clear_cache(); - - if (animation.is_valid()) { - animation->disconnect("changed", callable_mp(this, &AnimationCache::_animation_changed)); - } - - animation = p_animation; - - if (animation.is_valid()) { - animation->connect("changed", callable_mp(this, &AnimationCache::_animation_changed)); - } -} - -void AnimationCache::_bind_methods() { -} - -void AnimationCache::set_root(Node *p_root) { - _clear_cache(); - root = p_root; -} - -AnimationCache::AnimationCache() { -} diff --git a/scene/animation/animation_cache.h b/scene/animation/animation_cache.h deleted file mode 100644 index c856e644f7..0000000000 --- a/scene/animation/animation_cache.h +++ /dev/null @@ -1,84 +0,0 @@ -/*************************************************************************/ -/* animation_cache.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef ANIMATION_CACHE_H -#define ANIMATION_CACHE_H - -#include "scene/3d/skeleton_3d.h" -#include "scene/resources/animation.h" - -class AnimationCache : public Object { - GDCLASS(AnimationCache, Object); - - struct Path { - RES resource; - Object *object = nullptr; -#ifndef _3D_DISABLED - Skeleton3D *skeleton = nullptr; - Node3D *node_3d = nullptr; -#endif // _3D_DISABLED - Node *node = nullptr; - - int bone_idx = -1; - Vector subpath; - bool valid = false; - }; - - Set connected_nodes; - Vector path_cache; - - Node *root = nullptr; - Ref 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 &p_animation); - void set_root(Node *p_root); - - AnimationCache(); -}; - -#endif // ANIMATION_CACHE_H diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 2c8c4ee788..8407e0dd99 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -60,7 +60,12 @@ void AnimatedValuesBackup::restore() const { if (entry->bone_idx == -1) { entry->object->set_indexed(entry->subpath, entry->value); } else { - Object::cast_to(entry->object)->set_bone_pose(entry->bone_idx, entry->value); + Array arr = entry->value; + if (arr.size() == 3) { + Object::cast_to(entry->object)->set_bone_pose_position(entry->bone_idx, arr[0]); + Object::cast_to(entry->object)->set_bone_pose_rotation(entry->bone_idx, arr[1]); + Object::cast_to(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(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(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(child); + node_cache->node_3d = Object::cast_to(child); // cache skeleton - p_anim->node_cache[i]->skeleton = Object::cast_to(child); - if (p_anim->node_cache[i]->skeleton) { + node_cache->skeleton = Object::cast_to(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 AnimationPlayer::backup_animated_values(Node *p_root_o AnimatedValuesBackup::Entry entry; entry.object = nc->skeleton; entry.bone_idx = nc->bone_idx; - entry.value = nc->skeleton->get_bone_pose(nc->bone_idx); + Array arr; + arr.resize(3); + arr[0] = nc->skeleton->get_bone_pose_position(nc->bone_idx); + arr[1] = nc->skeleton->get_bone_pose_rotation(nc->bone_idx); + arr[2] = nc->skeleton->get_bone_pose_scale(nc->bone_idx); + entry.value = nc; backup->entries.push_back(entry); } else { if (nc->node_3d) { diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index b693e29bdf..f8e72a67b6 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -88,6 +88,8 @@ private: SP_NODE2D_SCALE, }; + uint32_t setup_pass = 1; + struct TrackNodeCache { NodePath path; uint32_t id = 0; @@ -101,6 +103,10 @@ private: int bone_idx = -1; // accumulated transforms + bool loc_used = false; + bool rot_used = false; + bool scale_used = false; + Vector3 loc_accum; Quaternion rot_accum; Vector3 scale_accum; @@ -134,6 +140,7 @@ private: Map bezier_anim; + uint32_t last_setup_pass = 0; TrackNodeCache() {} }; diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 9ca8d478b1..2ed8268289 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -544,13 +544,18 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { NodePath path = anim->track_get_path(i); Animation::TrackType track_type = anim->track_get_type(i); + Animation::TrackType track_cache_type = track_type; + if (track_cache_type == Animation::TYPE_POSITION_3D || track_cache_type == Animation::TYPE_ROTATION_3D || track_cache_type == Animation::TYPE_SCALE_3D) { + track_cache_type = Animation::TYPE_POSITION_3D; //reference them as position3D tracks, even if they modify rotation or scale + } + TrackCache *track = nullptr; if (track_cache.has(path)) { track = track_cache.get(path); } //if not valid, delete track - if (track && (track->type != track_type || ObjectDB::get_instance(track->object_id) == nullptr)) { + if (track && (track->type != track_cache_type || ObjectDB::get_instance(track->object_id) == nullptr)) { playing_caches.erase(track); memdelete(track); track_cache.erase(path); @@ -587,7 +592,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track = track_value; } break; - case Animation::TYPE_TRANSFORM3D: { + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { #ifndef _3D_DISABLED Node3D *node_3d = Object::cast_to(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(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(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(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(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(track); - Transform3D xform; - xform.origin = t->loc; - - xform.basis.set_quaternion_scale(t->rot, t->scale); - if (t->root_motion) { + Transform3D xform; + xform.origin = t->loc; + xform.basis.set_quaternion_scale(t->rot, t->scale); + root_motion_transform = xform; if (t->skeleton && t->bone_idx >= 0) { root_motion_transform = (t->skeleton->get_bone_rest(t->bone_idx) * root_motion_transform) * t->skeleton->get_bone_rest(t->bone_idx).affine_inverse(); } } else if (t->skeleton && t->bone_idx >= 0) { - t->skeleton->set_bone_pose(t->bone_idx, xform); + if (t->loc_used) { + t->skeleton->set_bone_pose_position(t->bone_idx, t->loc); + } + if (t->rot_used) { + t->skeleton->set_bone_pose_rotation(t->bone_idx, t->rot); + } + if (t->scale_used) { + t->skeleton->set_bone_pose_scale(t->bone_idx, t->scale); + } } else if (!t->skeleton) { - t->node_3d->set_transform(xform); + if (t->loc_used) { + t->node_3d->set_position(t->loc); + } + if (t->rot_used) { + t->node_3d->set_rotation(t->rot.get_euler()); + } + if (t->scale_used) { + t->node_3d->set_scale(t->scale); + } } #endif // _3D_DISABLED } break; diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 1e0267682e..ed207dfe0c 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -197,13 +197,16 @@ private: Skeleton3D *skeleton = nullptr; #endif // _3D_DISABLED int bone_idx = -1; + bool loc_used = false; + bool rot_used = false; + bool scale_used = false; Vector3 loc; Quaternion rot; real_t rot_blend_accum = 0.0; Vector3 scale; TrackCacheTransform() { - type = Animation::TYPE_TRANSFORM3D; + type = Animation::TYPE_POSITION_3D; } }; diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index b4eec2530b..6612c5ff3c 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -43,8 +43,12 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { if (tracks.size() == track && what == "type") { String type = p_value; - if (type == "transform" || type == "transform3d") { - add_track(TYPE_TRANSFORM3D); + if (type == "position_3d") { + add_track(TYPE_POSITION_3D); + } else if (type == "rotation_3d") { + add_track(TYPE_ROTATION_3D); + } else if (type == "scale_3d") { + add_track(TYPE_SCALE_3D); } else if (type == "value") { add_track(TYPE_VALUE); } else if (type == "method") { @@ -75,35 +79,72 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { } else if (what == "enabled") { track_set_enabled(track, p_value); } else if (what == "keys" || what == "key_values") { - if (track_get_type(track) == TYPE_TRANSFORM3D) { - TransformTrack *tt = static_cast(tracks[track]); + if (track_get_type(track) == TYPE_POSITION_3D) { + PositionTrack *tt = static_cast(tracks[track]); Vector values = p_value; int vcount = values.size(); - ERR_FAIL_COND_V(vcount % TRANSFORM_TRACK_SIZE, false); + ERR_FAIL_COND_V(vcount % POSITION_TRACK_SIZE, false); const real_t *r = values.ptr(); - int64_t count = vcount / TRANSFORM_TRACK_SIZE; - tt->transforms.resize(count); + int64_t count = vcount / POSITION_TRACK_SIZE; + tt->positions.resize(count); + TKey *tw = tt->positions.ptrw(); for (int i = 0; i < count; i++) { - TKey &tk = tt->transforms.write[i]; - const real_t *ofs = &r[i * TRANSFORM_TRACK_SIZE]; + TKey &tk = tw[i]; + const real_t *ofs = &r[i * POSITION_TRACK_SIZE]; tk.time = ofs[0]; tk.transition = ofs[1]; - tk.value.loc.x = ofs[2]; - tk.value.loc.y = ofs[3]; - tk.value.loc.z = ofs[4]; + tk.value.x = ofs[2]; + tk.value.y = ofs[3]; + tk.value.z = ofs[4]; + } + } else if (track_get_type(track) == TYPE_ROTATION_3D) { + RotationTrack *rt = static_cast(tracks[track]); + Vector values = p_value; + int vcount = values.size(); + ERR_FAIL_COND_V(vcount % ROTATION_TRACK_SIZE, false); - tk.value.rot.x = ofs[5]; - tk.value.rot.y = ofs[6]; - tk.value.rot.z = ofs[7]; - tk.value.rot.w = ofs[8]; + const real_t *r = values.ptr(); - tk.value.scale.x = ofs[9]; - tk.value.scale.y = ofs[10]; - tk.value.scale.z = ofs[11]; + int64_t count = vcount / ROTATION_TRACK_SIZE; + rt->rotations.resize(count); + + TKey *rw = rt->rotations.ptrw(); + for (int i = 0; i < count; i++) { + TKey &rk = rw[i]; + const real_t *ofs = &r[i * ROTATION_TRACK_SIZE]; + rk.time = ofs[0]; + rk.transition = ofs[1]; + + rk.value.x = ofs[2]; + rk.value.y = ofs[3]; + rk.value.z = ofs[4]; + rk.value.w = ofs[5]; + } + } else if (track_get_type(track) == TYPE_SCALE_3D) { + ScaleTrack *st = static_cast(tracks[track]); + Vector values = p_value; + int vcount = values.size(); + ERR_FAIL_COND_V(vcount % SCALE_TRACK_SIZE, false); + + const real_t *r = values.ptr(); + + int64_t count = vcount / SCALE_TRACK_SIZE; + st->scales.resize(count); + + TKey *sw = st->scales.ptrw(); + for (int i = 0; i < count; i++) { + TKey &sk = sw[i]; + const real_t *ofs = &r[i * SCALE_TRACK_SIZE]; + sk.time = ofs[0]; + sk.transition = ofs[1]; + + sk.value.x = ofs[2]; + sk.value.y = ofs[3]; + sk.value.z = ofs[4]; } } else if (track_get_type(track) == TYPE_VALUE) { @@ -319,8 +360,14 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { ERR_FAIL_INDEX_V(track, tracks.size(), false); if (what == "type") { switch (track_get_type(track)) { - case TYPE_TRANSFORM3D: - r_ret = "transform"; + case TYPE_POSITION_3D: + r_ret = "position_3d"; + break; + case TYPE_ROTATION_3D: + r_ret = "rotation_3d"; + break; + case TYPE_SCALE_3D: + r_ret = "scale_3d"; break; case TYPE_VALUE: r_ret = "value"; @@ -352,31 +399,64 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { } else if (what == "enabled") { r_ret = track_is_enabled(track); } else if (what == "keys") { - if (track_get_type(track) == TYPE_TRANSFORM3D) { + if (track_get_type(track) == TYPE_POSITION_3D) { Vector keys; int kk = track_get_key_count(track); - keys.resize(kk * TRANSFORM_TRACK_SIZE); + keys.resize(kk * POSITION_TRACK_SIZE); real_t *w = keys.ptrw(); int idx = 0; for (int i = 0; i < track_get_key_count(track); i++) { Vector3 loc; - Quaternion rot; - Vector3 scale; - transform_track_get_key(track, i, &loc, &rot, &scale); + position_track_get_key(track, i, &loc); w[idx++] = track_get_key_time(track, i); w[idx++] = track_get_key_transition(track, i); w[idx++] = loc.x; w[idx++] = loc.y; w[idx++] = loc.z; + } + r_ret = keys; + return true; + } else if (track_get_type(track) == TYPE_ROTATION_3D) { + Vector keys; + int kk = track_get_key_count(track); + keys.resize(kk * ROTATION_TRACK_SIZE); + + real_t *w = keys.ptrw(); + + int idx = 0; + for (int i = 0; i < track_get_key_count(track); i++) { + Quaternion rot; + rotation_track_get_key(track, i, &rot); + + w[idx++] = track_get_key_time(track, i); + w[idx++] = track_get_key_transition(track, i); w[idx++] = rot.x; w[idx++] = rot.y; w[idx++] = rot.z; w[idx++] = rot.w; + } + r_ret = keys; + return true; + + } else if (track_get_type(track) == TYPE_SCALE_3D) { + Vector keys; + int kk = track_get_key_count(track); + keys.resize(kk * SCALE_TRACK_SIZE); + + real_t *w = keys.ptrw(); + + int idx = 0; + for (int i = 0; i < track_get_key_count(track); i++) { + Vector3 scale; + scale_track_get_key(track, i, &scale); + + w[idx++] = track_get_key_time(track, i); + w[idx++] = track_get_key_transition(track, i); w[idx++] = scale.x; w[idx++] = scale.y; w[idx++] = scale.z; @@ -384,7 +464,6 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const { r_ret = keys; return true; - } else if (track_get_type(track) == TYPE_VALUE) { const ValueTrack *vt = static_cast(tracks[track]); @@ -591,10 +670,18 @@ int Animation::add_track(TrackType p_type, int p_at_pos) { } switch (p_type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = memnew(TransformTrack); + case TYPE_POSITION_3D: { + PositionTrack *tt = memnew(PositionTrack); tracks.insert(p_at_pos, tt); } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = memnew(RotationTrack); + tracks.insert(p_at_pos, rt); + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = memnew(ScaleTrack); + tracks.insert(p_at_pos, st); + } break; case TYPE_VALUE: { tracks.insert(p_at_pos, memnew(ValueTrack)); @@ -629,9 +716,19 @@ void Animation::remove_track(int p_track) { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast(t); - _clear(tt->transforms); + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast(t); + _clear(tt->positions); + + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast(t); + _clear(rt->rotations); + + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast(t); + _clear(st->scales); } break; case TYPE_VALUE: { @@ -672,7 +769,7 @@ int Animation::get_track_count() const { } Animation::TrackType Animation::track_get_type(int p_track) const { - ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_TRANSFORM3D); + ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_VALUE); return tracks[p_track]->type; } @@ -720,31 +817,6 @@ bool Animation::track_get_interpolation_loop_wrap(int p_track) const { return tracks[p_track]->loop_wrap; } -// transform -/* -template -int Animation::_insert_pos(double p_time, T& p_keys) { - // simple, linear time inset that should be fast enough in reality. - - int idx=p_keys.size(); - - while(true) { - - - if (idx==0 || p_keys[idx-1].time < p_time) { - //condition for insertion. - p_keys.insert(idx,T()); - return idx; - } else if (p_keys[idx-1].time == p_time) { - // condition for replacing. - return idx-1; - } - - idx--; - } -} - -*/ template int Animation::_insert(double p_time, T &p_keys, const V &p_value) { int idx = p_keys.size(); @@ -774,45 +846,153 @@ void Animation::_clear(T &p_keys) { p_keys.clear(); } -Error Animation::transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const { +//// + +int Animation::position_track_insert_key(int p_track, double p_time, const Vector3 &p_position) { + ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, -1); + + PositionTrack *tt = static_cast(t); + + TKey tkey; + tkey.time = p_time; + tkey.value = p_position; + + int ret = _insert(p_time, tt->positions, tkey); + emit_changed(); + return ret; +} + +Error Animation::position_track_get_key(int p_track, int p_key, Vector3 *r_position) const { ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); Track *t = tracks[p_track]; - TransformTrack *tt = static_cast(t); - ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER); - ERR_FAIL_INDEX_V(p_key, tt->transforms.size(), ERR_INVALID_PARAMETER); + PositionTrack *tt = static_cast(t); + ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_key, tt->positions.size(), ERR_INVALID_PARAMETER); - if (r_loc) { - *r_loc = tt->transforms[p_key].value.loc; - } - if (r_rot) { - *r_rot = tt->transforms[p_key].value.rot; - } - if (r_scale) { - *r_scale = tt->transforms[p_key].value.scale; - } + *r_position = tt->positions[p_key].value; return OK; } -int Animation::transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot, const Vector3 &p_scale) { +Error Animation::position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER); + + PositionTrack *tt = static_cast(t); + + bool ok = false; + + Vector3 tk = _interpolate(tt->positions, p_time, tt->interpolation, tt->loop_wrap, &ok); + + if (!ok) { + return ERR_UNAVAILABLE; + } + *r_interpolation = tk; + return OK; +} + +//// + +int Animation::rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation) { ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); Track *t = tracks[p_track]; - ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, -1); + ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, -1); - TransformTrack *tt = static_cast(t); + RotationTrack *rt = static_cast(t); - TKey tkey; + TKey tkey; tkey.time = p_time; - tkey.value.loc = p_loc; - tkey.value.rot = p_rot; - tkey.value.scale = p_scale; + tkey.value = p_rotation; - int ret = _insert(p_time, tt->transforms, tkey); + int ret = _insert(p_time, rt->rotations, tkey); emit_changed(); return ret; } +Error Animation::rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + + RotationTrack *rt = static_cast(t); + ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_key, rt->rotations.size(), ERR_INVALID_PARAMETER); + + *r_rotation = rt->rotations[p_key].value; + + return OK; +} + +Error Animation::rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER); + + RotationTrack *rt = static_cast(t); + + bool ok = false; + + Quaternion tk = _interpolate(rt->rotations, p_time, rt->interpolation, rt->loop_wrap, &ok); + + if (!ok) { + return ERR_UNAVAILABLE; + } + *r_interpolation = tk; + return OK; +} + +//// + +int Animation::scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale) { + ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, -1); + + ScaleTrack *st = static_cast(t); + + TKey tkey; + tkey.time = p_time; + tkey.value = p_scale; + + int ret = _insert(p_time, st->scales, tkey); + emit_changed(); + return ret; +} + +Error Animation::scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + + ScaleTrack *st = static_cast(t); + ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_key, st->scales.size(), ERR_INVALID_PARAMETER); + + *r_scale = st->scales[p_key].value; + + return OK; +} + +Error Animation::scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); + Track *t = tracks[p_track]; + ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER); + + ScaleTrack *st = static_cast(t); + + bool ok = false; + + Vector3 tk = _interpolate(st->scales, p_time, st->interpolation, st->loop_wrap, &ok); + + if (!ok) { + return ERR_UNAVAILABLE; + } + *r_interpolation = tk; + return OK; +} + void Animation::track_remove_key_at_time(int p_track, double p_time) { int idx = track_find_key(p_track, p_time, true); ERR_FAIL_COND(idx < 0); @@ -824,10 +1004,22 @@ void Animation::track_remove_key(int p_track, int p_idx) { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast(t); - ERR_FAIL_INDEX(p_idx, tt->transforms.size()); - tt->transforms.remove(p_idx); + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast(t); + ERR_FAIL_INDEX(p_idx, tt->positions.size()); + tt->positions.remove(p_idx); + + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast(t); + ERR_FAIL_INDEX(p_idx, rt->rotations.size()); + rt->rotations.remove(p_idx); + + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast(t); + ERR_FAIL_INDEX(p_idx, st->scales.size()); + st->scales.remove(p_idx); } break; case TYPE_VALUE: { @@ -870,13 +1062,37 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast(t); - int k = _find(tt->transforms, p_time); - if (k < 0 || k >= tt->transforms.size()) { + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast(t); + int k = _find(tt->positions, p_time); + if (k < 0 || k >= tt->positions.size()) { return -1; } - if (tt->transforms[k].time != p_time && p_exact) { + if (tt->positions[k].time != p_time && p_exact) { + return -1; + } + return k; + + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast(t); + int k = _find(rt->rotations, p_time); + if (k < 0 || k >= rt->rotations.size()) { + return -1; + } + if (rt->rotations[k].time != p_time && p_exact) { + return -1; + } + return k; + + } break; + case TYPE_SCALE_3D: { + ScaleTrack *rt = static_cast(t); + int k = _find(rt->scales, p_time); + if (k < 0 || k >= rt->scales.size()) { + return -1; + } + if (rt->scales[k].time != p_time && p_exact) { return -1; } return k; @@ -952,24 +1168,21 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - Dictionary d = p_key; - Vector3 loc; - if (d.has("location")) { - loc = d["location"]; - } + case TYPE_POSITION_3D: { + ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I)); + int idx = position_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, idx, p_transition); - Quaternion rot; - if (d.has("rotation")) { - rot = d["rotation"]; - } + } break; + case TYPE_ROTATION_3D: { + ERR_FAIL_COND((p_key.get_type() != Variant::QUATERNION) && (p_key.get_type() != Variant::BASIS)); + int idx = rotation_track_insert_key(p_track, p_time, p_key); + track_set_key_transition(p_track, idx, p_transition); - Vector3 scale; - if (d.has("scale")) { - scale = d["scale"]; - } - - int idx = transform_track_insert_key(p_track, p_time, loc, rot, scale); + } break; + case TYPE_SCALE_3D: { + ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I)); + int idx = scale_track_insert_key(p_track, p_time, p_key); track_set_key_transition(p_track, idx, p_transition); } break; @@ -1054,9 +1267,17 @@ int Animation::track_get_key_count(int p_track) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast(t); - return tt->transforms.size(); + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast(t); + return tt->positions.size(); + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast(t); + return rt->rotations.size(); + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast(t); + return st->scales.size(); } break; case TYPE_VALUE: { ValueTrack *vt = static_cast(t); @@ -1089,16 +1310,23 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast(t); - ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), Variant()); + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast(t); + ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), Variant()); - Dictionary d; - d["location"] = tt->transforms[p_key_idx].value.loc; - d["rotation"] = tt->transforms[p_key_idx].value.rot; - d["scale"] = tt->transforms[p_key_idx].value.scale; + return tt->positions[p_key_idx].value; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast(t); + ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), Variant()); - return d; + return rt->rotations[p_key_idx].value; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast(t); + ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), Variant()); + + return st->scales[p_key_idx].value; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast(t); @@ -1157,10 +1385,20 @@ double Animation::track_get_key_time(int p_track, int p_key_idx) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast(t); - ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1); - return tt->transforms[p_key_idx].time; + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast(t); + ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1); + return tt->positions[p_key_idx].time; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast(t); + ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1); + return rt->rotations[p_key_idx].time; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast(t); + ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1); + return st->scales[p_key_idx].time; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast(t); @@ -1202,13 +1440,31 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast(t); - ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); - TKey key = tt->transforms[p_key_idx]; + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast(t); + ERR_FAIL_INDEX(p_key_idx, tt->positions.size()); + TKey key = tt->positions[p_key_idx]; key.time = p_time; - tt->transforms.remove(p_key_idx); - _insert(p_time, tt->transforms, key); + tt->positions.remove(p_key_idx); + _insert(p_time, tt->positions, key); + return; + } + case TYPE_ROTATION_3D: { + RotationTrack *tt = static_cast(t); + ERR_FAIL_INDEX(p_key_idx, tt->rotations.size()); + TKey key = tt->rotations[p_key_idx]; + key.time = p_time; + tt->rotations.remove(p_key_idx); + _insert(p_time, tt->rotations, key); + return; + } + case TYPE_SCALE_3D: { + ScaleTrack *tt = static_cast(t); + ERR_FAIL_INDEX(p_key_idx, tt->scales.size()); + TKey key = tt->scales[p_key_idx]; + key.time = p_time; + tt->scales.remove(p_key_idx); + _insert(p_time, tt->scales, key); return; } case TYPE_VALUE: { @@ -1266,10 +1522,20 @@ real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const { Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast(t); - ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1); - return tt->transforms[p_key_idx].transition; + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast(t); + ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1); + return tt->positions[p_key_idx].transition; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast(t); + ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1); + return rt->rotations[p_key_idx].transition; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast(t); + ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1); + return st->scales[p_key_idx].transition; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast(t); @@ -1302,21 +1568,28 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast(t); - ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); + case TYPE_POSITION_3D: { + ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I)); + PositionTrack *tt = static_cast(t); + ERR_FAIL_INDEX(p_key_idx, tt->positions.size()); - Dictionary d = p_value; + tt->positions.write[p_key_idx].value = p_value; - if (d.has("location")) { - tt->transforms.write[p_key_idx].value.loc = d["location"]; - } - if (d.has("rotation")) { - tt->transforms.write[p_key_idx].value.rot = d["rotation"]; - } - if (d.has("scale")) { - tt->transforms.write[p_key_idx].value.scale = d["scale"]; - } + } break; + case TYPE_ROTATION_3D: { + ERR_FAIL_COND((p_value.get_type() != Variant::QUATERNION) && (p_value.get_type() != Variant::BASIS)); + RotationTrack *rt = static_cast(t); + ERR_FAIL_INDEX(p_key_idx, rt->rotations.size()); + + rt->rotations.write[p_key_idx].value = p_value; + + } break; + case TYPE_SCALE_3D: { + ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I)); + ScaleTrack *st = static_cast(t); + ERR_FAIL_INDEX(p_key_idx, st->scales.size()); + + st->scales.write[p_key_idx].value = p_value; } break; case TYPE_VALUE: { @@ -1385,10 +1658,20 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr Track *t = tracks[p_track]; switch (t->type) { - case TYPE_TRANSFORM3D: { - TransformTrack *tt = static_cast(t); - ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); - tt->transforms.write[p_key_idx].transition = p_transition; + case TYPE_POSITION_3D: { + PositionTrack *tt = static_cast(t); + ERR_FAIL_INDEX(p_key_idx, tt->positions.size()); + tt->positions.write[p_key_idx].transition = p_transition; + } break; + case TYPE_ROTATION_3D: { + RotationTrack *rt = static_cast(t); + ERR_FAIL_INDEX(p_key_idx, rt->rotations.size()); + rt->rotations.write[p_key_idx].transition = p_transition; + } break; + case TYPE_SCALE_3D: { + ScaleTrack *st = static_cast(t); + ERR_FAIL_INDEX(p_key_idx, st->scales.size()); + st->scales.write[p_key_idx].transition = p_transition; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast(t); @@ -1450,15 +1733,6 @@ int Animation::_find(const Vector &p_keys, double p_time) const { return middle; } -Animation::TransformKey Animation::_interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const { - TransformKey ret; - ret.loc = _interpolate(p_a.loc, p_b.loc, p_c); - ret.rot = _interpolate(p_a.rot, p_b.rot, p_c); - ret.scale = _interpolate(p_a.scale, p_b.scale, p_c); - - return ret; -} - Vector3 Animation::_interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const { return p_a.lerp(p_b, p_c); } @@ -1477,16 +1751,6 @@ real_t Animation::_interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) return p_a * (1.0 - p_c) + p_b * p_c; } -Animation::TransformKey Animation::_cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const { - Animation::TransformKey tk; - - tk.loc = p_a.loc.cubic_interpolate(p_b.loc, p_pre_a.loc, p_post_b.loc, p_c); - tk.scale = p_a.scale.cubic_interpolate(p_b.scale, p_pre_a.scale, p_post_b.scale, p_c); - tk.rot = p_a.rot.cubic_slerp(p_b.rot, p_pre_a.rot, p_post_b.rot, p_c); - - return tk; -} - Vector3 Animation::_cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const { return p_a.cubic_interpolate(p_b, p_pre_a, p_post_b, p_c); } @@ -1729,36 +1993,6 @@ T Animation::_interpolate(const Vector> &p_keys, double p_time, Interpol // do a barrel roll } -Error Animation::transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const { - ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER); - Track *t = tracks[p_track]; - ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER); - - TransformTrack *tt = static_cast(t); - - bool ok = false; - - TransformKey tk = _interpolate(tt->transforms, p_time, tt->interpolation, tt->loop_wrap, &ok); - - if (!ok) { - return ERR_UNAVAILABLE; - } - - if (r_loc) { - *r_loc = tk.loc; - } - - if (r_rot) { - *r_rot = tk.rot; - } - - if (r_scale) { - *r_scale = tk.scale; - } - - return OK; -} - Variant Animation::value_track_interpolate(int p_track, double p_time) const { ERR_FAIL_INDEX_V(p_track, tracks.size(), 0); Track *t = tracks[p_track]; @@ -1933,10 +2167,22 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl // handle loop by splitting switch (t->type) { - case TYPE_TRANSFORM3D: { - const TransformTrack *tt = static_cast(t); - _track_get_key_indices_in_range(tt->transforms, from_time, length, p_indices); - _track_get_key_indices_in_range(tt->transforms, 0, to_time, p_indices); + case TYPE_POSITION_3D: { + const PositionTrack *tt = static_cast(t); + _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices); + _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices); + + } break; + case TYPE_ROTATION_3D: { + const RotationTrack *rt = static_cast(t); + _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices); + _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices); + + } break; + case TYPE_SCALE_3D: { + const ScaleTrack *st = static_cast(t); + _track_get_key_indices_in_range(st->scales, from_time, length, p_indices); + _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices); } break; case TYPE_VALUE: { @@ -1989,9 +2235,19 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl } switch (t->type) { - case TYPE_TRANSFORM3D: { - const TransformTrack *tt = static_cast(t); - _track_get_key_indices_in_range(tt->transforms, from_time, to_time, p_indices); + case TYPE_POSITION_3D: { + const PositionTrack *tt = static_cast(t); + _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices); + + } break; + case TYPE_ROTATION_3D: { + const RotationTrack *rt = static_cast(t); + _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices); + + } break; + case TYPE_SCALE_3D: { + const ScaleTrack *st = static_cast(t); + _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices); } break; case TYPE_VALUE: { @@ -2608,7 +2864,10 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("track_set_enabled", "track_idx", "enabled"), &Animation::track_set_enabled); ClassDB::bind_method(D_METHOD("track_is_enabled", "track_idx"), &Animation::track_is_enabled); - ClassDB::bind_method(D_METHOD("transform_track_insert_key", "track_idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key); + ClassDB::bind_method(D_METHOD("position_track_insert_key", "track_idx", "time", "position"), &Animation::position_track_insert_key); + ClassDB::bind_method(D_METHOD("rotation_track_insert_key", "track_idx", "time", "rotation"), &Animation::rotation_track_insert_key); + ClassDB::bind_method(D_METHOD("scale_track_insert_key", "track_idx", "time", "scale"), &Animation::scale_track_insert_key); + ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1)); ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key); ClassDB::bind_method(D_METHOD("track_remove_key_at_time", "track_idx", "time"), &Animation::track_remove_key_at_time); @@ -2628,7 +2887,6 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("track_set_interpolation_loop_wrap", "track_idx", "interpolation"), &Animation::track_set_interpolation_loop_wrap); ClassDB::bind_method(D_METHOD("track_get_interpolation_loop_wrap", "track_idx"), &Animation::track_get_interpolation_loop_wrap); - ClassDB::bind_method(D_METHOD("transform_track_interpolate", "track_idx", "time_sec"), &Animation::_transform_track_interpolate); ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "track_idx", "mode"), &Animation::value_track_set_update_mode); ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "track_idx"), &Animation::value_track_get_update_mode); @@ -2682,7 +2940,9 @@ void Animation::_bind_methods() { ADD_SIGNAL(MethodInfo("tracks_changed")); BIND_ENUM_CONSTANT(TYPE_VALUE); - BIND_ENUM_CONSTANT(TYPE_TRANSFORM3D); + BIND_ENUM_CONSTANT(TYPE_POSITION_3D); + BIND_ENUM_CONSTANT(TYPE_ROTATION_3D); + BIND_ENUM_CONSTANT(TYPE_SCALE_3D); BIND_ENUM_CONSTANT(TYPE_METHOD); BIND_ENUM_CONSTANT(TYPE_BEZIER); BIND_ENUM_CONSTANT(TYPE_AUDIO); @@ -2709,193 +2969,146 @@ void Animation::clear() { emit_signal(SceneStringNames::get_singleton()->tracks_changed); } -bool Animation::_transform_track_optimize_key(const TKey &t0, const TKey &t1, const TKey &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm) { - real_t c = (t1.time - t0.time) / (t2.time - t0.time); - real_t t[3] = { -1, -1, -1 }; +bool Animation::_position_track_optimize_key(const TKey &t0, const TKey &t1, const TKey &t2, real_t p_allowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm) { + const Vector3 &v0 = t0.value; + const Vector3 &v1 = t1.value; + const Vector3 &v2 = t2.value; - { //translation - - const Vector3 &v0 = t0.value.loc; - const Vector3 &v1 = t1.value.loc; - const Vector3 &v2 = t2.value.loc; - - if (v0.is_equal_approx(v2)) { - //0 and 2 are close, let's see if 1 is close - if (!v0.is_equal_approx(v1)) { - //not close, not optimizable - return false; - } - - } else { - Vector3 pd = (v2 - v0); - real_t d0 = pd.dot(v0); - real_t d1 = pd.dot(v1); - real_t d2 = pd.dot(v2); - if (d1 < d0 || d1 > d2) { - return false; - } - - Vector3 s[2] = { v0, v2 }; - real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); - - if (d > pd.length() * p_alowed_linear_err) { - return false; //beyond allowed error for collinearity - } - - if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_alowed_angular_err) { - return false; - } - - t[0] = (d1 - d0) / (d2 - d0); + if (v0.is_equal_approx(v2)) { + //0 and 2 are close, let's see if 1 is close + if (!v0.is_equal_approx(v1)) { + //not close, not optimizable + return false; } - } - { //rotation - - const Quaternion &q0 = t0.value.rot; - const Quaternion &q1 = t1.value.rot; - const Quaternion &q2 = t2.value.rot; - - //localize both to rotation from q0 - - if (q0.is_equal_approx(q2)) { - if (!q0.is_equal_approx(q1)) { - return false; - } - - } else { - Quaternion r02 = (q0.inverse() * q2).normalized(); - Quaternion r01 = (q0.inverse() * q1).normalized(); - - Vector3 v02, v01; - real_t a02, a01; - - r02.get_axis_angle(v02, a02); - r01.get_axis_angle(v01, a01); - - if (Math::abs(a02) > p_max_optimizable_angle) { - return false; - } - - if (v01.dot(v02) < 0) { - //make sure both rotations go the same way to compare - v02 = -v02; - a02 = -a02; - } - - real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized())) / Math_PI; - if (err_01 > p_alowed_angular_err) { - //not rotating in the same axis - return false; - } - - if (a01 * a02 < 0) { - //not rotating in the same direction - return false; - } - - real_t tr = a01 / a02; - if (tr < 0 || tr > 1) { - return false; //rotating too much or too less - } - - t[1] = tr; - } - } - - { //scale - - const Vector3 &v0 = t0.value.scale; - const Vector3 &v1 = t1.value.scale; - const Vector3 &v2 = t2.value.scale; - - if (v0.is_equal_approx(v2)) { - //0 and 2 are close, let's see if 1 is close - if (!v0.is_equal_approx(v1)) { - //not close, not optimizable - return false; - } - - } else { - Vector3 pd = (v2 - v0); - real_t d0 = pd.dot(v0); - real_t d1 = pd.dot(v1); - real_t d2 = pd.dot(v2); - if (d1 < d0 || d1 > d2) { - return false; //beyond segment range - } - - Vector3 s[2] = { v0, v2 }; - real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); - - if (d > pd.length() * p_alowed_linear_err) { - return false; //beyond allowed error for collinearity - } - - t[2] = (d1 - d0) / (d2 - d0); - } - } - - bool erase = false; - if (t[0] == -1 && t[1] == -1 && t[2] == -1) { - erase = true; } else { - erase = true; - real_t lt = -1.0; - for (int j = 0; j < 3; j++) { - //search for t on first, one must be it - if (t[j] != -1) { - lt = t[j]; //official t - //validate rest - for (int k = j + 1; k < 3; k++) { - if (t[k] == -1) { - continue; - } - - if (Math::abs(lt - t[k]) > p_alowed_linear_err) { - erase = false; - break; - } - } - break; - } + Vector3 pd = (v2 - v0); + real_t d0 = pd.dot(v0); + real_t d1 = pd.dot(v1); + real_t d2 = pd.dot(v2); + if (d1 < d0 || d1 > d2) { + return false; } - ERR_FAIL_COND_V(lt == -1, false); + Vector3 s[2] = { v0, v2 }; + real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); - if (erase) { - if (Math::abs(lt - c) > p_alowed_linear_err) { - //todo, evaluate changing the transition if this fails? - //this could be done as a second pass and would be - //able to optimize more - erase = false; - } + if (d > pd.length() * p_allowed_linear_err) { + return false; //beyond allowed error for collinearity + } + + if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_allowed_angular_error) { + return false; } } - return erase; + return true; } -void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) { +bool Animation::_rotation_track_optimize_key(const TKey &t0, const TKey &t1, const TKey &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle) { + const Quaternion &q0 = t0.value; + const Quaternion &q1 = t1.value; + const Quaternion &q2 = t2.value; + + //localize both to rotation from q0 + + if (q0.is_equal_approx(q2)) { + if (!q0.is_equal_approx(q1)) { + return false; + } + + } else { + Quaternion r02 = (q0.inverse() * q2).normalized(); + Quaternion r01 = (q0.inverse() * q1).normalized(); + + Vector3 v02, v01; + real_t a02, a01; + + r02.get_axis_angle(v02, a02); + r01.get_axis_angle(v01, a01); + + if (Math::abs(a02) > p_max_optimizable_angle) { + return false; + } + + if (v01.dot(v02) < 0) { + //make sure both rotations go the same way to compare + v02 = -v02; + a02 = -a02; + } + + real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized())) / Math_PI; + if (err_01 > p_allowed_angular_error) { + //not rotating in the same axis + return false; + } + + if (a01 * a02 < 0) { + //not rotating in the same direction + return false; + } + + real_t tr = a01 / a02; + if (tr < 0 || tr > 1) { + return false; //rotating too much or too less + } + } + + return true; +} + +bool Animation::_scale_track_optimize_key(const TKey &t0, const TKey &t1, const TKey &t2, real_t p_allowed_linear_error) { + const Vector3 &v0 = t0.value; + const Vector3 &v1 = t1.value; + const Vector3 &v2 = t2.value; + + if (v0.is_equal_approx(v2)) { + //0 and 2 are close, let's see if 1 is close + if (!v0.is_equal_approx(v1)) { + //not close, not optimizable + return false; + } + + } else { + Vector3 pd = (v2 - v0); + real_t d0 = pd.dot(v0); + real_t d1 = pd.dot(v1); + real_t d2 = pd.dot(v2); + if (d1 < d0 || d1 > d2) { + return false; //beyond segment range + } + + Vector3 s[2] = { v0, v2 }; + real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1); + + if (d > pd.length() * p_allowed_linear_error) { + return false; //beyond allowed error for colinearity + } + } + + return true; +} + +void Animation::_position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err) { ERR_FAIL_INDEX(p_idx, tracks.size()); - ERR_FAIL_COND(tracks[p_idx]->type != TYPE_TRANSFORM3D); - TransformTrack *tt = static_cast(tracks[p_idx]); + ERR_FAIL_COND(tracks[p_idx]->type != TYPE_POSITION_3D); + PositionTrack *tt = static_cast(tracks[p_idx]); bool prev_erased = false; - TKey first_erased; + TKey first_erased; Vector3 norm; - for (int i = 1; i < tt->transforms.size() - 1; i++) { - TKey &t0 = tt->transforms.write[i - 1]; - TKey &t1 = tt->transforms.write[i]; - TKey &t2 = tt->transforms.write[i + 1]; + for (int i = 1; i < tt->positions.size() - 1; i++) { + TKey &t0 = tt->positions.write[i - 1]; + TKey &t1 = tt->positions.write[i]; + TKey &t2 = tt->positions.write[i + 1]; - bool erase = _transform_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm); + bool erase = _position_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, norm); if (erase && !prev_erased) { - norm = (t2.value.loc - t1.value.loc).normalized(); + norm = (t2.value - t1.value).normalized(); } - if (prev_erased && !_transform_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm)) { + if (prev_erased && !_position_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err, p_allowed_angular_err, norm)) { //avoid error to go beyond first erased key erase = false; } @@ -2906,7 +3119,7 @@ void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err prev_erased = true; } - tt->transforms.remove(i); + tt->positions.remove(i); i--; } else { @@ -2916,10 +3129,82 @@ void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err } } +void Animation::_rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) { + ERR_FAIL_INDEX(p_idx, tracks.size()); + ERR_FAIL_COND(tracks[p_idx]->type != TYPE_ROTATION_3D); + RotationTrack *tt = static_cast(tracks[p_idx]); + bool prev_erased = false; + TKey first_erased; + + for (int i = 1; i < tt->rotations.size() - 1; i++) { + TKey &t0 = tt->rotations.write[i - 1]; + TKey &t1 = tt->rotations.write[i]; + TKey &t2 = tt->rotations.write[i + 1]; + + bool erase = _rotation_track_optimize_key(t0, t1, t2, p_allowed_angular_err, p_max_optimizable_angle); + + if (prev_erased && !_rotation_track_optimize_key(t0, first_erased, t2, p_allowed_angular_err, p_max_optimizable_angle)) { + //avoid error to go beyond first erased key + erase = false; + } + + if (erase) { + if (!prev_erased) { + first_erased = t1; + prev_erased = true; + } + + tt->rotations.remove(i); + i--; + + } else { + prev_erased = false; + } + } +} + +void Animation::_scale_track_optimize(int p_idx, real_t p_allowed_linear_err) { + ERR_FAIL_INDEX(p_idx, tracks.size()); + ERR_FAIL_COND(tracks[p_idx]->type != TYPE_SCALE_3D); + ScaleTrack *tt = static_cast(tracks[p_idx]); + bool prev_erased = false; + TKey first_erased; + + for (int i = 1; i < tt->scales.size() - 1; i++) { + TKey &t0 = tt->scales.write[i - 1]; + TKey &t1 = tt->scales.write[i]; + TKey &t2 = tt->scales.write[i + 1]; + + bool erase = _scale_track_optimize_key(t0, t1, t2, p_allowed_linear_err); + + if (prev_erased && !_scale_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err)) { + //avoid error to go beyond first erased key + erase = false; + } + + if (erase) { + if (!prev_erased) { + first_erased = t1; + prev_erased = true; + } + + tt->scales.remove(i); + i--; + + } else { + prev_erased = false; + } + } +} + void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) { for (int i = 0; i < tracks.size(); i++) { - if (tracks[i]->type == TYPE_TRANSFORM3D) { - _transform_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle); + if (tracks[i]->type == TYPE_POSITION_3D) { + _position_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err); + } else if (tracks[i]->type == TYPE_ROTATION_3D) { + _rotation_track_optimize(i, p_allowed_angular_err, p_max_optimizable_angle); + } else if (tracks[i]->type == TYPE_SCALE_3D) { + _scale_track_optimize(i, p_allowed_linear_err); } } } diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 9a410bd566..614e12a560 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -42,7 +42,9 @@ class Animation : public Resource { public: enum TrackType { TYPE_VALUE, ///< Set a value in a property, can be interpolated. - TYPE_TRANSFORM3D, ///< Transform a node or a bone. + TYPE_POSITION_3D, ///< Position 3D track + TYPE_ROTATION_3D, ///< Rotation 3D track + TYPE_SCALE_3D, ///< Scale 3D track TYPE_METHOD, ///< Call any method on a specific node. TYPE_BEZIER, ///< Bezier curve TYPE_AUDIO, @@ -86,21 +88,31 @@ private: T value; }; - struct TransformKey { - Vector3 loc; - Quaternion rot; - Vector3 scale; + const int32_t POSITION_TRACK_SIZE = 5; + const int32_t ROTATION_TRACK_SIZE = 6; + const int32_t SCALE_TRACK_SIZE = 5; + + /* POSITION TRACK */ + + struct PositionTrack : public Track { + Vector> 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> rotations; - struct TransformTrack : public Track { - Vector> transforms; + RotationTrack() { type = TYPE_ROTATION_3D; } + }; - TransformTrack() { type = TYPE_TRANSFORM3D; } + /* SCALE TRACK */ + + struct ScaleTrack : public Track { + Vector> scales; + ScaleTrack() { type = TYPE_SCALE_3D; } }; /* PROPERTY VALUE TRACK */ @@ -186,14 +198,11 @@ private: template inline int _find(const Vector &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 _value_track_get_key_indices(int p_track, double p_time, double p_delta) const { List 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 &t0, const TKey &t1, const TKey &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 &t0, const TKey &t1, const TKey &t2, real_t p_alowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm); + bool _rotation_track_optimize_key(const TKey &t0, const TKey &t1, const TKey &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle); + bool _scale_track_optimize_key(const TKey &t0, const TKey &t1, const TKey &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 *p_indices) const; void value_track_set_update_mode(int p_track, UpdateMode p_mode);