Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone. * Added POSITION_3D, ROTATION_3D, SCALE_3D tracks. * GLTF2, Collada, FBX importers will only import the track types found. * Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed. * AnimationPlayer and AnimationTree animate these tracks separately, only when found. * Removed BakeReset code, is useless with these changes. This is the first in a series of commits designed to make the animation system in Godot more useful, which includes: * Better compatibility with Autodesk products * Better reusability of animations across models (including retargeting). * Proper animation compression. * etc. *Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
This commit is contained in:
@@ -91,8 +91,8 @@ struct ColladaImport {
|
||||
Error _create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform3D &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<ImporterMesh>> p_morph_meshes = Vector<Ref<ImporterMesh>>(), bool p_use_compression = false, bool p_use_mesh_material = false);
|
||||
Error load(const String &p_path, int p_flags, bool p_force_make_tangents = false, bool p_use_compression = false);
|
||||
void _fix_param_animation_tracks();
|
||||
void create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks);
|
||||
void create_animations(bool p_make_tracks_in_all_bones, bool p_import_value_tracks);
|
||||
void create_animation(int p_clip, bool p_import_value_tracks);
|
||||
void create_animations(bool p_import_value_tracks);
|
||||
|
||||
Set<String> tracks_in_clips;
|
||||
Vector<String> missing_textures;
|
||||
@@ -1384,7 +1384,7 @@ void ColladaImport::_fix_param_animation_tracks() {
|
||||
}
|
||||
}
|
||||
|
||||
void ColladaImport::create_animations(bool p_make_tracks_in_all_bones, bool p_import_value_tracks) {
|
||||
void ColladaImport::create_animations(bool p_import_value_tracks) {
|
||||
_fix_param_animation_tracks();
|
||||
for (int i = 0; i < collada.state.animation_clips.size(); i++) {
|
||||
for (int j = 0; j < collada.state.animation_clips[i].tracks.size(); j++) {
|
||||
@@ -1417,13 +1417,13 @@ void ColladaImport::create_animations(bool p_make_tracks_in_all_bones, bool p_im
|
||||
}
|
||||
}
|
||||
|
||||
create_animation(-1, p_make_tracks_in_all_bones, p_import_value_tracks);
|
||||
create_animation(-1, p_import_value_tracks);
|
||||
for (int i = 0; i < collada.state.animation_clips.size(); i++) {
|
||||
create_animation(i, p_make_tracks_in_all_bones, p_import_value_tracks);
|
||||
create_animation(i, p_import_value_tracks);
|
||||
}
|
||||
}
|
||||
|
||||
void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks) {
|
||||
void ColladaImport::create_animation(int p_clip, bool p_import_value_tracks) {
|
||||
Ref<Animation> animation = Ref<Animation>(memnew(Animation));
|
||||
|
||||
if (p_clip == -1) {
|
||||
@@ -1522,10 +1522,55 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
|
||||
continue;
|
||||
}
|
||||
|
||||
animation->add_track(Animation::TYPE_TRANSFORM3D);
|
||||
int track = animation->get_track_count() - 1;
|
||||
animation->track_set_path(track, path);
|
||||
animation->track_set_imported(track, true); //helps merging later
|
||||
bool has_position = false;
|
||||
bool has_rotation = false;
|
||||
bool has_scale = false;
|
||||
|
||||
for (int i = 0; cn->xform_list.size(); i++) {
|
||||
switch (cn->xform_list[i].op) {
|
||||
case Collada::Node::XForm::OP_ROTATE: {
|
||||
has_rotation = true;
|
||||
} break;
|
||||
case Collada::Node::XForm::OP_SCALE: {
|
||||
has_scale = true;
|
||||
} break;
|
||||
case Collada::Node::XForm::OP_TRANSLATE: {
|
||||
has_position = true;
|
||||
} break;
|
||||
case Collada::Node::XForm::OP_MATRIX: {
|
||||
has_position = true;
|
||||
has_rotation = true;
|
||||
has_scale = true;
|
||||
} break;
|
||||
case Collada::Node::XForm::OP_VISIBILITY: {
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
int base_track = animation->get_track_count();
|
||||
int position_idx = -1;
|
||||
if (has_position) {
|
||||
position_idx = animation->get_track_count();
|
||||
animation->add_track(Animation::TYPE_POSITION_3D);
|
||||
animation->track_set_path(position_idx, path);
|
||||
animation->track_set_imported(position_idx, true); //helps merging later
|
||||
}
|
||||
|
||||
int rotation_idx = -1;
|
||||
if (has_rotation) {
|
||||
rotation_idx = animation->get_track_count();
|
||||
animation->add_track(Animation::TYPE_ROTATION_3D);
|
||||
animation->track_set_path(rotation_idx, path);
|
||||
animation->track_set_imported(rotation_idx, true); //helps merging later
|
||||
}
|
||||
|
||||
int scale_idx = -1;
|
||||
if (has_scale) {
|
||||
scale_idx = animation->get_track_count();
|
||||
animation->add_track(Animation::TYPE_SCALE_3D);
|
||||
animation->track_set_path(scale_idx, path);
|
||||
animation->track_set_imported(scale_idx, true); //helps merging later
|
||||
}
|
||||
|
||||
Vector<real_t> snapshots = base_snapshots;
|
||||
|
||||
@@ -1606,10 +1651,19 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
|
||||
|
||||
Vector3 s = xform.basis.get_scale();
|
||||
bool singular_matrix = Math::is_zero_approx(s.x) || Math::is_zero_approx(s.y) || Math::is_zero_approx(s.z);
|
||||
Quaternion q = singular_matrix ? Quaternion() : xform.basis.get_rotation_quaternion();
|
||||
Quaternion q = singular_matrix ? Quaternion() :
|
||||
xform.basis.get_rotation_quaternion();
|
||||
Vector3 l = xform.origin;
|
||||
|
||||
animation->transform_track_insert_key(track, snapshots[i], l, q, s);
|
||||
if (position_idx >= 0) {
|
||||
animation->position_track_insert_key(position_idx, snapshots[i], l);
|
||||
}
|
||||
if (rotation_idx >= 0) {
|
||||
animation->rotation_track_insert_key(rotation_idx, snapshots[i], q);
|
||||
}
|
||||
if (scale_idx >= 0) {
|
||||
animation->scale_track_insert_key(scale_idx, snapshots[i], s);
|
||||
}
|
||||
}
|
||||
|
||||
if (nm.bone >= 0) {
|
||||
@@ -1621,48 +1675,15 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
|
||||
if (found_anim) {
|
||||
tracks_found = true;
|
||||
} else {
|
||||
animation->remove_track(track);
|
||||
}
|
||||
}
|
||||
|
||||
if (p_make_tracks_in_all_bones) {
|
||||
//some bones may lack animation, but since we don't store pose as a property, we must add keyframes!
|
||||
for (const KeyValue<String, bool> &E : bones_with_animation) {
|
||||
if (E.value) {
|
||||
continue;
|
||||
if (position_idx >= 0) {
|
||||
animation->remove_track(base_track);
|
||||
}
|
||||
|
||||
NodeMap &nm = node_map[E.key];
|
||||
String path = scene->get_path_to(nm.node);
|
||||
ERR_CONTINUE(nm.bone < 0);
|
||||
Skeleton3D *sk = static_cast<Skeleton3D *>(nm.node);
|
||||
String name = sk->get_bone_name(nm.bone);
|
||||
path = path + ":" + name;
|
||||
|
||||
Collada::Node *cn = collada.state.scene_map[E.key];
|
||||
if (cn->ignore_anim) {
|
||||
WARN_PRINT("Collada: Ignoring animation on node: " + path);
|
||||
continue;
|
||||
if (rotation_idx >= 0) {
|
||||
animation->remove_track(base_track);
|
||||
}
|
||||
if (scale_idx >= 0) {
|
||||
animation->remove_track(base_track);
|
||||
}
|
||||
|
||||
animation->add_track(Animation::TYPE_TRANSFORM3D);
|
||||
int track = animation->get_track_count() - 1;
|
||||
animation->track_set_path(track, path);
|
||||
animation->track_set_imported(track, true); //helps merging later
|
||||
|
||||
Transform3D xform = cn->compute_transform(collada);
|
||||
xform = collada.fix_transform(xform) * cn->post_transform;
|
||||
|
||||
xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform;
|
||||
|
||||
Vector3 s = xform.basis.get_scale();
|
||||
bool singular_matrix = Math::is_zero_approx(s.x) || Math::is_zero_approx(s.y) || Math::is_zero_approx(s.z);
|
||||
Quaternion q = singular_matrix ? Quaternion() : xform.basis.get_rotation_quaternion();
|
||||
Vector3 l = xform.origin;
|
||||
|
||||
animation->transform_track_insert_key(track, 0, l, q, s);
|
||||
|
||||
tracks_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1773,7 +1794,7 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_
|
||||
}
|
||||
|
||||
if (p_flags & IMPORT_ANIMATION) {
|
||||
state.create_animations(true, true);
|
||||
state.create_animations(true);
|
||||
AnimationPlayer *ap = memnew(AnimationPlayer);
|
||||
for (int i = 0; i < state.animations.size(); i++) {
|
||||
String name;
|
||||
@@ -1800,7 +1821,7 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String &p_path
|
||||
Error err = state.load(p_path, Collada::IMPORT_FLAG_ANIMATION, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load animation from file '" + p_path + "'.");
|
||||
|
||||
state.create_animations(true, true);
|
||||
state.create_animations(true);
|
||||
if (state.scene) {
|
||||
memdelete(state.scene);
|
||||
}
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* editor_importer_bake_reset.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "editor/import/editor_importer_bake_reset.h"
|
||||
|
||||
#include "core/error/error_list.h"
|
||||
#include "core/error/error_macros.h"
|
||||
#include "core/math/transform_3d.h"
|
||||
#include "resource_importer_scene.h"
|
||||
#include "scene/3d/importer_mesh_instance_3d.h"
|
||||
#include "scene/3d/mesh_instance_3d.h"
|
||||
#include "scene/3d/node_3d.h"
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
|
||||
// Given that an engineering team has made a reference character, one wants ten animators to create animations.
|
||||
// Currently, a tech artist needs to combine the ten files into one exported gltf2 to import into Godot Engine.
|
||||
// We bake the RESET animation and then set it to identity,
|
||||
// so that rigs with corresponding RESET animation can have their animations transferred with ease.
|
||||
//
|
||||
// The original algorithm for the code was used to change skeleton bone rolls to be parent to child.
|
||||
//
|
||||
// Reference https://github.com/godotengine/godot-proposals/issues/2961
|
||||
void BakeReset::_bake_animation_pose(Node *scene, const String &p_bake_anim) {
|
||||
Map<StringName, BakeResetRestBone> r_rest_bones;
|
||||
Vector<Node3D *> r_meshes;
|
||||
List<Node *> queue;
|
||||
queue.push_back(scene);
|
||||
while (!queue.is_empty()) {
|
||||
List<Node *>::Element *E = queue.front();
|
||||
Node *node = E->get();
|
||||
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(node);
|
||||
// Step 1: import scene with animations into the rest bones data structure.
|
||||
_fetch_reset_animation(ap, r_rest_bones, p_bake_anim);
|
||||
|
||||
int child_count = node->get_child_count();
|
||||
for (int i = 0; i < child_count; i++) {
|
||||
queue.push_back(node->get_child(i));
|
||||
}
|
||||
queue.pop_front();
|
||||
}
|
||||
|
||||
queue.push_back(scene);
|
||||
while (!queue.is_empty()) {
|
||||
List<Node *>::Element *E = queue.front();
|
||||
Node *node = E->get();
|
||||
ImporterMeshInstance3D *editor_mesh_3d = scene->cast_to<ImporterMeshInstance3D>(node);
|
||||
MeshInstance3D *mesh_3d = scene->cast_to<MeshInstance3D>(node);
|
||||
if (scene->cast_to<Skeleton3D>(node)) {
|
||||
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
|
||||
|
||||
// Step 2: Bake the RESET animation from the RestBone to the skeleton.
|
||||
_fix_skeleton(skeleton, r_rest_bones);
|
||||
}
|
||||
if (editor_mesh_3d) {
|
||||
NodePath path = editor_mesh_3d->get_skeleton_path();
|
||||
if (!path.is_empty() && editor_mesh_3d->get_node_or_null(path) && Object::cast_to<Skeleton3D>(editor_mesh_3d->get_node_or_null(path))) {
|
||||
r_meshes.push_back(editor_mesh_3d);
|
||||
}
|
||||
} else if (mesh_3d) {
|
||||
NodePath path = mesh_3d->get_skeleton_path();
|
||||
if (!path.is_empty() && mesh_3d->get_node_or_null(path) && Object::cast_to<Skeleton3D>(mesh_3d->get_node_or_null(path))) {
|
||||
r_meshes.push_back(mesh_3d);
|
||||
}
|
||||
}
|
||||
int child_count = node->get_child_count();
|
||||
for (int i = 0; i < child_count; i++) {
|
||||
queue.push_back(node->get_child(i));
|
||||
}
|
||||
queue.pop_front();
|
||||
}
|
||||
|
||||
queue.push_back(scene);
|
||||
while (!queue.is_empty()) {
|
||||
List<Node *>::Element *E = queue.front();
|
||||
Node *node = E->get();
|
||||
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(node);
|
||||
if (ap) {
|
||||
// Step 3: Key all RESET animation frames to identity.
|
||||
_align_animations(ap, r_rest_bones);
|
||||
}
|
||||
|
||||
int child_count = node->get_child_count();
|
||||
for (int i = 0; i < child_count; i++) {
|
||||
queue.push_back(node->get_child(i));
|
||||
}
|
||||
queue.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void BakeReset::_align_animations(AnimationPlayer *p_ap, const Map<StringName, BakeResetRestBone> &r_rest_bones) {
|
||||
ERR_FAIL_NULL(p_ap);
|
||||
List<StringName> anim_names;
|
||||
p_ap->get_animation_list(&anim_names);
|
||||
for (List<StringName>::Element *anim_i = anim_names.front(); anim_i; anim_i = anim_i->next()) {
|
||||
Ref<Animation> a = p_ap->get_animation(anim_i->get());
|
||||
ERR_CONTINUE(a.is_null());
|
||||
for (const KeyValue<StringName, BakeResetRestBone> &rest_bone_i : r_rest_bones) {
|
||||
int track = a->find_track(NodePath(rest_bone_i.key));
|
||||
if (track == -1) {
|
||||
continue;
|
||||
}
|
||||
int new_track = a->add_track(Animation::TYPE_TRANSFORM3D);
|
||||
NodePath new_path = NodePath(rest_bone_i.key);
|
||||
const BakeResetRestBone rest_bone = rest_bone_i.value;
|
||||
a->track_set_path(new_track, new_path);
|
||||
for (int key_i = 0; key_i < a->track_get_key_count(track); key_i++) {
|
||||
Vector3 loc;
|
||||
Quaternion rot;
|
||||
Vector3 scale;
|
||||
Error err = a->transform_track_get_key(track, key_i, &loc, &rot, &scale);
|
||||
ERR_CONTINUE(err);
|
||||
real_t time = a->track_get_key_time(track, key_i);
|
||||
rot.normalize();
|
||||
loc = loc - rest_bone.loc;
|
||||
rot = rest_bone.rest_delta.get_rotation_quaternion().inverse() * rot;
|
||||
rot.normalize();
|
||||
scale = Vector3(1, 1, 1) - (rest_bone.rest_delta.get_scale() - scale);
|
||||
// Apply the reverse of the rest changes to make the key be close to identity transform.
|
||||
a->transform_track_insert_key(new_track, time, loc, rot, scale);
|
||||
}
|
||||
a->remove_track(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BakeReset::_fetch_reset_animation(AnimationPlayer *p_ap, Map<StringName, BakeResetRestBone> &r_rest_bones, const String &p_bake_anim) {
|
||||
if (!p_ap) {
|
||||
return;
|
||||
}
|
||||
List<StringName> anim_names;
|
||||
p_ap->get_animation_list(&anim_names);
|
||||
Node *root = p_ap->get_owner();
|
||||
ERR_FAIL_NULL(root);
|
||||
if (!p_ap->has_animation(p_bake_anim)) {
|
||||
return;
|
||||
}
|
||||
Ref<Animation> a = p_ap->get_animation(p_bake_anim);
|
||||
if (a.is_null()) {
|
||||
return;
|
||||
}
|
||||
for (int32_t track = 0; track < a->get_track_count(); track++) {
|
||||
NodePath path = a->track_get_path(track);
|
||||
String string_path = path;
|
||||
Skeleton3D *skeleton = root->cast_to<Skeleton3D>(root->get_node(string_path.get_slice(":", 0)));
|
||||
if (!skeleton) {
|
||||
continue;
|
||||
}
|
||||
String bone_name = string_path.get_slice(":", 1);
|
||||
for (int key_i = 0; key_i < a->track_get_key_count(track); key_i++) {
|
||||
Vector3 loc;
|
||||
Quaternion rot;
|
||||
Vector3 scale;
|
||||
Error err = a->transform_track_get_key(track, key_i, &loc, &rot, &scale);
|
||||
if (err != OK) {
|
||||
ERR_PRINT_ONCE("Reset animation baker can't get key.");
|
||||
continue;
|
||||
}
|
||||
rot.normalize();
|
||||
Basis rot_basis = Basis(rot, scale);
|
||||
BakeResetRestBone rest_bone;
|
||||
rest_bone.rest_delta = rot_basis;
|
||||
rest_bone.loc = loc;
|
||||
// Store the animation into the RestBone.
|
||||
r_rest_bones[StringName(String(skeleton->get_owner()->get_path_to(skeleton)) + ":" + bone_name)] = rest_bone;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BakeReset::_fix_skeleton(Skeleton3D *p_skeleton, Map<StringName, BakeReset::BakeResetRestBone> &r_rest_bones) {
|
||||
int bone_count = p_skeleton->get_bone_count();
|
||||
|
||||
// First iterate through all the bones and update the RestBone.
|
||||
for (int j = 0; j < bone_count; j++) {
|
||||
StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(j);
|
||||
BakeResetRestBone &rest_bone = r_rest_bones[final_path];
|
||||
rest_bone.rest_local = p_skeleton->get_bone_rest(j);
|
||||
}
|
||||
for (int i = 0; i < bone_count; i++) {
|
||||
int parent_bone = p_skeleton->get_bone_parent(i);
|
||||
String path = p_skeleton->get_owner()->get_path_to(p_skeleton);
|
||||
StringName final_path = String(path) + String(":") + p_skeleton->get_bone_name(parent_bone);
|
||||
if (parent_bone >= 0) {
|
||||
r_rest_bones[path].children.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
// When we apply transform to a bone, we also have to move all of its children in the opposite direction.
|
||||
for (int i = 0; i < bone_count; i++) {
|
||||
StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(i);
|
||||
r_rest_bones[final_path].rest_local = r_rest_bones[final_path].rest_local * Transform3D(r_rest_bones[final_path].rest_delta, r_rest_bones[final_path].loc);
|
||||
// Iterate through the children and move in the opposite direction.
|
||||
for (int j = 0; j < r_rest_bones[final_path].children.size(); j++) {
|
||||
int child_index = r_rest_bones[final_path].children[j];
|
||||
StringName children_path = String(p_skeleton->get_name()) + String(":") + p_skeleton->get_bone_name(child_index);
|
||||
r_rest_bones[children_path].rest_local = Transform3D(r_rest_bones[final_path].rest_delta, r_rest_bones[final_path].loc).affine_inverse() * r_rest_bones[children_path].rest_local;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < bone_count; i++) {
|
||||
StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(i);
|
||||
ERR_CONTINUE(!r_rest_bones.has(final_path));
|
||||
Transform3D rest_transform = r_rest_bones[final_path].rest_local;
|
||||
p_skeleton->set_bone_rest(i, rest_transform);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* editor_importer_bake_reset.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef RESOURCE_IMPORTER_BAKE_RESET_H
|
||||
#define RESOURCE_IMPORTER_BAKE_RESET_H
|
||||
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class Skeleton3D;
|
||||
class AnimationPlayer;
|
||||
class BakeReset {
|
||||
struct BakeResetRestBone {
|
||||
Transform3D rest_local;
|
||||
Basis rest_delta;
|
||||
Vector3 loc;
|
||||
Vector<int> children;
|
||||
};
|
||||
|
||||
public:
|
||||
void _bake_animation_pose(Node *scene, const String &p_bake_anim);
|
||||
|
||||
private:
|
||||
void _fix_skeleton(Skeleton3D *p_skeleton, Map<StringName, BakeReset::BakeResetRestBone> &r_rest_bones);
|
||||
void _align_animations(AnimationPlayer *p_ap, const Map<StringName, BakeResetRestBone> &r_rest_bones);
|
||||
void _fetch_reset_animation(AnimationPlayer *p_ap, Map<StringName, BakeResetRestBone> &r_rest_bones, const String &p_bake_anim);
|
||||
};
|
||||
#endif
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/import/editor_importer_bake_reset.h"
|
||||
|
||||
#include "editor/import/scene_import_settings.h"
|
||||
#include "scene/3d/area_3d.h"
|
||||
#include "scene/3d/collision_shape_3d.h"
|
||||
@@ -851,42 +851,57 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
|
||||
new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
|
||||
|
||||
if (kt > (from + 0.01) && k > 0) {
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
|
||||
Quaternion q;
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
|
||||
Vector3 p;
|
||||
default_anim->position_track_interpolate(j, from, &p);
|
||||
new_anim->position_track_insert_key(dtrack, 0, p);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
|
||||
Quaternion r;
|
||||
default_anim->rotation_track_interpolate(j, from, &r);
|
||||
new_anim->rotation_track_insert_key(dtrack, 0, r);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
|
||||
Vector3 s;
|
||||
default_anim->transform_track_interpolate(j, from, &p, &q, &s);
|
||||
new_anim->transform_track_insert_key(dtrack, 0, p, q, s);
|
||||
}
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
default_anim->scale_track_interpolate(j, from, &s);
|
||||
new_anim->scale_track_insert_key(dtrack, 0, s);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
Variant var = default_anim->value_track_interpolate(j, from);
|
||||
new_anim->track_insert_key(dtrack, 0, var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
|
||||
Quaternion q;
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
|
||||
Vector3 p;
|
||||
default_anim->position_track_get_key(j, k, &p);
|
||||
new_anim->position_track_insert_key(dtrack, kt - from, p);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
|
||||
Quaternion r;
|
||||
default_anim->rotation_track_get_key(j, k, &r);
|
||||
new_anim->rotation_track_insert_key(dtrack, kt - from, r);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
|
||||
Vector3 s;
|
||||
default_anim->transform_track_get_key(j, k, &p, &q, &s);
|
||||
new_anim->transform_track_insert_key(dtrack, kt - from, p, q, s);
|
||||
}
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
default_anim->scale_track_get_key(j, k, &s);
|
||||
new_anim->scale_track_insert_key(dtrack, kt - from, s);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
Variant var = default_anim->track_get_key_value(j, k);
|
||||
new_anim->track_insert_key(dtrack, kt - from, var);
|
||||
}
|
||||
}
|
||||
|
||||
if (dtrack != -1 && kt >= to) {
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
|
||||
Quaternion q;
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
|
||||
Vector3 p;
|
||||
default_anim->position_track_interpolate(j, to, &p);
|
||||
new_anim->position_track_insert_key(dtrack, to - from, p);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
|
||||
Quaternion r;
|
||||
default_anim->rotation_track_interpolate(j, to, &r);
|
||||
new_anim->rotation_track_insert_key(dtrack, to - from, r);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
|
||||
Vector3 s;
|
||||
default_anim->transform_track_interpolate(j, to, &p, &q, &s);
|
||||
new_anim->transform_track_insert_key(dtrack, to - from, p, q, s);
|
||||
}
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
default_anim->scale_track_interpolate(j, to, &s);
|
||||
new_anim->scale_track_insert_key(dtrack, to - from, s);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
Variant var = default_anim->value_track_interpolate(j, to);
|
||||
new_anim->track_insert_key(dtrack, to - from, var);
|
||||
}
|
||||
@@ -897,16 +912,25 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
|
||||
new_anim->add_track(default_anim->track_get_type(j));
|
||||
dtrack = new_anim->get_track_count() - 1;
|
||||
new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
|
||||
Quaternion q;
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_POSITION_3D) {
|
||||
Vector3 p;
|
||||
default_anim->position_track_interpolate(j, from, &p);
|
||||
new_anim->position_track_insert_key(dtrack, 0, p);
|
||||
default_anim->position_track_interpolate(j, to, &p);
|
||||
new_anim->position_track_insert_key(dtrack, to - from, p);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_ROTATION_3D) {
|
||||
Quaternion r;
|
||||
default_anim->rotation_track_interpolate(j, from, &r);
|
||||
new_anim->rotation_track_insert_key(dtrack, 0, r);
|
||||
default_anim->rotation_track_interpolate(j, to, &r);
|
||||
new_anim->rotation_track_insert_key(dtrack, to - from, r);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_SCALE_3D) {
|
||||
Vector3 s;
|
||||
default_anim->transform_track_interpolate(j, from, &p, &q, &s);
|
||||
new_anim->transform_track_insert_key(dtrack, 0, p, q, s);
|
||||
default_anim->transform_track_interpolate(j, to, &p, &q, &s);
|
||||
new_anim->transform_track_insert_key(dtrack, to - from, p, q, s);
|
||||
}
|
||||
if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
default_anim->scale_track_interpolate(j, from, &s);
|
||||
new_anim->scale_track_insert_key(dtrack, 0, s);
|
||||
default_anim->scale_track_interpolate(j, to, &s);
|
||||
new_anim->scale_track_insert_key(dtrack, to - from, s);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
Variant var = default_anim->value_track_interpolate(j, from);
|
||||
new_anim->track_insert_key(dtrack, 0, var);
|
||||
Variant to_var = default_anim->value_track_interpolate(j, to);
|
||||
@@ -1000,6 +1024,9 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_linear_error"), 0.05));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error"), 0.01));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angle"), 22));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/position", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/rotation", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/scale", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Always,Never"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/amount", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
@@ -1171,7 +1198,6 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/bake_reset_animation"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));
|
||||
|
||||
@@ -1533,11 +1559,6 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
|
||||
|
||||
_pre_fix_node(scene, scene, collision_map);
|
||||
_post_fix_node(scene, scene, collision_map, scanned_meshes, node_data, material_data, animation_data, fps);
|
||||
bool use_bake_reset_animation = p_options["animation/bake_reset_animation"];
|
||||
if (use_bake_reset_animation) {
|
||||
BakeReset bake_reset;
|
||||
bake_reset._bake_animation_pose(scene, "RESET");
|
||||
}
|
||||
|
||||
String root_type = p_options["nodes/root_type"];
|
||||
root_type = root_type.split(" ")[0]; // full root_type is "ClassName (filename.gd)" for a script global class.
|
||||
|
||||
@@ -65,6 +65,13 @@ public:
|
||||
IMPORT_USE_NAMED_SKIN_BINDS = 16,
|
||||
};
|
||||
|
||||
enum AnimationImportBoneTracks {
|
||||
ANIMATION_IMPORT_BONE_TRACKS_IF_PRESENT,
|
||||
ANIMATION_IMPORT_BONE_TRACKS_IF_PRESENT_FOR_ALL,
|
||||
ANIMATION_IMPORT_BONE_TRACKS_ALWAYS,
|
||||
ANIMATION_IMPORT_BONE_TRACKS_NEVER,
|
||||
};
|
||||
|
||||
virtual uint32_t get_import_flags() const;
|
||||
virtual void get_extensions(List<String> *r_extensions) const;
|
||||
virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr);
|
||||
|
||||
Reference in New Issue
Block a user