Convert 3.x `transform` animation tracks
This commit is contained in:
parent
a22e2c2f57
commit
324ad7571a
|
@ -3270,11 +3270,43 @@ void EditorSceneFormatImporterESCN::get_extensions(List<String> *r_extensions) c
|
||||||
r_extensions->push_back("escn");
|
r_extensions->push_back("escn");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int get_text_format_version(String p_path) {
|
||||||
|
Error error;
|
||||||
|
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &error);
|
||||||
|
ERR_FAIL_COND_V_MSG(error != OK || f.is_null(), -1, "Cannot open file '" + p_path + "'.");
|
||||||
|
String line = f->get_line().strip_edges();
|
||||||
|
// skip empty lines and comments
|
||||||
|
while (line.is_empty() || line.begins_with(";")) {
|
||||||
|
line = f->get_line().strip_edges();
|
||||||
|
if (f->eof_reached()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int format_index = line.find("format");
|
||||||
|
ERR_FAIL_COND_V_MSG(format_index == -1, -1, "No format specifier in file '" + p_path + "'.");
|
||||||
|
String format_str = line.substr(format_index).get_slicec('=', 1).strip_edges();
|
||||||
|
ERR_FAIL_COND_V_MSG(!format_str.substr(0, 1).is_numeric(), -1, "Invalid format in file '" + p_path + "'.");
|
||||||
|
int format = format_str.to_int();
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
Node *EditorSceneFormatImporterESCN::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) {
|
Node *EditorSceneFormatImporterESCN::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) {
|
||||||
Error error;
|
Error error;
|
||||||
|
int format = get_text_format_version(p_path);
|
||||||
|
ERR_FAIL_COND_V(format == -1, nullptr);
|
||||||
Ref<PackedScene> ps = ResourceFormatLoaderText::singleton->load(p_path, p_path, &error);
|
Ref<PackedScene> ps = ResourceFormatLoaderText::singleton->load(p_path, p_path, &error);
|
||||||
ERR_FAIL_COND_V_MSG(!ps.is_valid(), nullptr, "Cannot load scene as text resource from path '" + p_path + "'.");
|
ERR_FAIL_COND_V_MSG(!ps.is_valid(), nullptr, "Cannot load scene as text resource from path '" + p_path + "'.");
|
||||||
Node *scene = ps->instantiate();
|
Node *scene = ps->instantiate();
|
||||||
|
ERR_FAIL_COND_V(!scene, nullptr);
|
||||||
|
if (format == 2) {
|
||||||
|
TypedArray<Node> skel_nodes = scene->find_children("*", "AnimationPlayer");
|
||||||
|
for (int32_t node_i = 0; node_i < skel_nodes.size(); node_i++) {
|
||||||
|
// Force re-compute animation tracks.
|
||||||
|
AnimationPlayer *player = cast_to<AnimationPlayer>(skel_nodes[node_i]);
|
||||||
|
ERR_CONTINUE(!player);
|
||||||
|
player->advance(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
TypedArray<Node> nodes = scene->find_children("*", "MeshInstance3D");
|
TypedArray<Node> nodes = scene->find_children("*", "MeshInstance3D");
|
||||||
for (int32_t node_i = 0; node_i < nodes.size(); node_i++) {
|
for (int32_t node_i = 0; node_i < nodes.size(); node_i++) {
|
||||||
MeshInstance3D *mesh_3d = cast_to<MeshInstance3D>(nodes[node_i]);
|
MeshInstance3D *mesh_3d = cast_to<MeshInstance3D>(nodes[node_i]);
|
||||||
|
@ -3283,9 +3315,22 @@ Node *EditorSceneFormatImporterESCN::import_scene(const String &p_path, uint32_t
|
||||||
// Ignore the aabb, it will be recomputed.
|
// Ignore the aabb, it will be recomputed.
|
||||||
ImporterMeshInstance3D *importer_mesh_3d = memnew(ImporterMeshInstance3D);
|
ImporterMeshInstance3D *importer_mesh_3d = memnew(ImporterMeshInstance3D);
|
||||||
importer_mesh_3d->set_name(mesh_3d->get_name());
|
importer_mesh_3d->set_name(mesh_3d->get_name());
|
||||||
importer_mesh_3d->set_transform(mesh_3d->get_relative_transform(mesh_3d->get_parent()));
|
Node *parent = mesh_3d->get_parent();
|
||||||
importer_mesh_3d->set_skin(mesh_3d->get_skin());
|
Transform3D rel_transform = mesh_3d->get_relative_transform(parent);
|
||||||
|
if (rel_transform == Transform3D() && parent && parent != mesh_3d) {
|
||||||
|
// If we're here, we probably got a "data.parent is null" error
|
||||||
|
// Node3D.data.parent hasn't been set yet but Node.data.parent has, so we need to get the transform manually
|
||||||
|
Node3D *parent_3d = mesh_3d->get_parent_node_3d();
|
||||||
|
if (parent == parent_3d) {
|
||||||
|
rel_transform = mesh_3d->get_transform();
|
||||||
|
} else if (parent_3d) {
|
||||||
|
rel_transform = parent_3d->get_relative_transform(parent) * mesh_3d->get_transform();
|
||||||
|
} // Otherwise, parent isn't a Node3D.
|
||||||
|
}
|
||||||
|
importer_mesh_3d->set_transform(rel_transform);
|
||||||
|
Ref<Skin> skin = mesh_3d->get_skin();
|
||||||
importer_mesh_3d->set_skeleton_path(mesh_3d->get_skeleton_path());
|
importer_mesh_3d->set_skeleton_path(mesh_3d->get_skeleton_path());
|
||||||
|
importer_mesh_3d->set_skin(skin);
|
||||||
Ref<ArrayMesh> array_mesh_3d_mesh = mesh_3d->get_mesh();
|
Ref<ArrayMesh> array_mesh_3d_mesh = mesh_3d->get_mesh();
|
||||||
if (array_mesh_3d_mesh.is_valid()) {
|
if (array_mesh_3d_mesh.is_valid()) {
|
||||||
// For the MeshInstance3D nodes, we need to convert the ArrayMesh to an ImporterMesh specially.
|
// For the MeshInstance3D nodes, we need to convert the ArrayMesh to an ImporterMesh specially.
|
||||||
|
@ -3326,7 +3371,5 @@ Node *EditorSceneFormatImporterESCN::import_scene(const String &p_path, uint32_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ERR_FAIL_NULL_V(scene, nullptr);
|
|
||||||
|
|
||||||
return scene;
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,6 +242,97 @@ TypedArray<StringName> AnimationMixer::_get_animation_library_list() const {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(_3D_DISABLED) || !defined(DISABLE_DEPRECATED)
|
||||||
|
bool AnimationMixer::_recalc_animation(Ref<Animation> &anim) {
|
||||||
|
HashMap<int, Vector<real_t>> new_track_values_map;
|
||||||
|
Node *parent = get_node_or_null(root_node);
|
||||||
|
if (!parent) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < anim->get_track_count(); i++) {
|
||||||
|
int track_type = anim->track_get_type(i);
|
||||||
|
if (track_type == Animation::TYPE_POSITION_3D || track_type == Animation::TYPE_ROTATION_3D || track_type == Animation::TYPE_SCALE_3D) {
|
||||||
|
NodePath path = anim->track_get_path(i);
|
||||||
|
Node *node = parent->get_node(path);
|
||||||
|
ERR_FAIL_COND_V(!node, false);
|
||||||
|
Skeleton3D *skel = Object::cast_to<Skeleton3D>(node);
|
||||||
|
ERR_FAIL_COND_V(!skel, false);
|
||||||
|
|
||||||
|
StringName bone = path.get_subname(0);
|
||||||
|
int bone_idx = skel->find_bone(bone);
|
||||||
|
if (bone_idx == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Transform3D rest = skel->get_bone_rest(bone_idx);
|
||||||
|
new_track_values_map[i] = Vector<real_t>();
|
||||||
|
const int32_t POSITION_TRACK_SIZE = 5;
|
||||||
|
const int32_t ROTATION_TRACK_SIZE = 6;
|
||||||
|
const int32_t SCALE_TRACK_SIZE = 5;
|
||||||
|
int32_t track_size = POSITION_TRACK_SIZE;
|
||||||
|
if (track_type == Animation::TYPE_ROTATION_3D) {
|
||||||
|
track_size = ROTATION_TRACK_SIZE;
|
||||||
|
}
|
||||||
|
new_track_values_map[i].resize(track_size * anim->track_get_key_count(i));
|
||||||
|
real_t *r = new_track_values_map[i].ptrw();
|
||||||
|
for (int j = 0; j < anim->track_get_key_count(i); j++) {
|
||||||
|
real_t time = anim->track_get_key_time(i, j);
|
||||||
|
real_t transition = anim->track_get_key_transition(i, j);
|
||||||
|
if (track_type == Animation::TYPE_POSITION_3D) {
|
||||||
|
Vector3 a_pos = anim->track_get_key_value(i, j);
|
||||||
|
Transform3D t = Transform3D();
|
||||||
|
t.set_origin(a_pos);
|
||||||
|
Vector3 new_a_pos = (rest * t).origin;
|
||||||
|
|
||||||
|
real_t *ofs = &r[j * POSITION_TRACK_SIZE];
|
||||||
|
ofs[0] = time;
|
||||||
|
ofs[1] = transition;
|
||||||
|
ofs[2] = new_a_pos.x;
|
||||||
|
ofs[3] = new_a_pos.y;
|
||||||
|
ofs[4] = new_a_pos.z;
|
||||||
|
} else if (track_type == Animation::TYPE_ROTATION_3D) {
|
||||||
|
Quaternion q = anim->track_get_key_value(i, j);
|
||||||
|
Transform3D t = Transform3D();
|
||||||
|
t.basis.rotate(q);
|
||||||
|
Quaternion new_q = (rest * t).basis.get_rotation_quaternion();
|
||||||
|
real_t *ofs = &r[j * ROTATION_TRACK_SIZE];
|
||||||
|
ofs[0] = time;
|
||||||
|
ofs[1] = transition;
|
||||||
|
ofs[2] = new_q.x;
|
||||||
|
ofs[3] = new_q.y;
|
||||||
|
ofs[4] = new_q.z;
|
||||||
|
ofs[5] = new_q.w;
|
||||||
|
} else if (track_type == Animation::TYPE_SCALE_3D) {
|
||||||
|
Vector3 v = anim->track_get_key_value(i, j);
|
||||||
|
Transform3D t = Transform3D();
|
||||||
|
t.scale(v);
|
||||||
|
Vector3 new_v = (rest * t).basis.get_scale();
|
||||||
|
|
||||||
|
real_t *ofs = &r[j * SCALE_TRACK_SIZE];
|
||||||
|
ofs[0] = time;
|
||||||
|
ofs[1] = transition;
|
||||||
|
ofs[2] = new_v.x;
|
||||||
|
ofs[3] = new_v.y;
|
||||||
|
ofs[4] = new_v.z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (new_track_values_map.is_empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < anim->get_track_count(); i++) {
|
||||||
|
if (!new_track_values_map.has(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
anim->set("tracks/" + itos(i) + "/keys", new_track_values_map[i]);
|
||||||
|
anim->set("tracks/" + itos(i) + "/relative_to_rest", false);
|
||||||
|
}
|
||||||
|
anim->emit_changed();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif // !defined(_3D_DISABLED) || !defined(DISABLE_DEPRECATED)
|
||||||
|
|
||||||
void AnimationMixer::get_animation_library_list(List<StringName> *p_libraries) const {
|
void AnimationMixer::get_animation_library_list(List<StringName> *p_libraries) const {
|
||||||
for (const AnimationLibraryData &lib : animation_libraries) {
|
for (const AnimationLibraryData &lib : animation_libraries) {
|
||||||
p_libraries->push_back(lib.name);
|
p_libraries->push_back(lib.name);
|
||||||
|
@ -986,6 +1077,18 @@ void AnimationMixer::_blend_init() {
|
||||||
root_motion_scale_accumulator = Vector3(1, 1, 1);
|
root_motion_scale_accumulator = Vector3(1, 1, 1);
|
||||||
|
|
||||||
if (!cache_valid) {
|
if (!cache_valid) {
|
||||||
|
#if !defined(_3D_DISABLED) || !defined(DISABLE_DEPRECATED)
|
||||||
|
List<StringName> sname;
|
||||||
|
get_animation_list(&sname);
|
||||||
|
|
||||||
|
for (const StringName &E : sname) {
|
||||||
|
Ref<Animation> anim = get_animation(E);
|
||||||
|
|
||||||
|
if (anim->has_tracks_relative_to_rest()) {
|
||||||
|
_recalc_animation(anim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (!_update_caches()) {
|
if (!_update_caches()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -317,6 +317,10 @@ protected:
|
||||||
void _init_root_motion_cache();
|
void _init_root_motion_cache();
|
||||||
bool _update_caches();
|
bool _update_caches();
|
||||||
|
|
||||||
|
#if !defined(_3D_DISABLED) || !defined(DISABLE_DEPRECATED)
|
||||||
|
bool _recalc_animation(Ref<Animation> &p_anim);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ---- Audio ---- */
|
/* ---- Audio ---- */
|
||||||
AudioServer::PlaybackType playback_type;
|
AudioServer::PlaybackType playback_type;
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,10 @@
|
||||||
#include "core/io/marshalls.h"
|
#include "core/io/marshalls.h"
|
||||||
#include "core/math/geometry_3d.h"
|
#include "core/math/geometry_3d.h"
|
||||||
|
|
||||||
|
#define _LOAD_META_PROPERTY "_load"
|
||||||
|
#define _TRANSFORM_TRACK_LIST_META_PROPERTY "_transform_track_list"
|
||||||
|
#define _TRANSFORM_TRACK_DATA_META_PROPERTY(m_track_idx) "_transform_track_data__" + String::num(m_track_idx)
|
||||||
|
|
||||||
bool Animation::_set(const StringName &p_name, const Variant &p_value) {
|
bool Animation::_set(const StringName &p_name, const Variant &p_value) {
|
||||||
String prop_name = p_name;
|
String prop_name = p_name;
|
||||||
|
|
||||||
|
@ -89,6 +93,23 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
|
||||||
} else if (type == "animation") {
|
} else if (type == "animation") {
|
||||||
add_track(TYPE_ANIMATION);
|
add_track(TYPE_ANIMATION);
|
||||||
} else {
|
} else {
|
||||||
|
#ifndef DISABLE_DEPRECATED
|
||||||
|
// for compatibility with 3.x animations
|
||||||
|
if (get_meta(_LOAD_META_PROPERTY, false)) { // Only do compatibility conversions if we are loading a resource.
|
||||||
|
if (type == "transform") {
|
||||||
|
WARN_DEPRECATED_MSG("Animation uses old 'transform' track types, which is deprecated (and loads slower). Consider re-importing or re-saving the resource.");
|
||||||
|
PackedInt32Array track_list = get_meta(_TRANSFORM_TRACK_LIST_META_PROPERTY, PackedInt32Array());
|
||||||
|
track_list.push_back(track);
|
||||||
|
set_meta(_TRANSFORM_TRACK_LIST_META_PROPERTY, track_list);
|
||||||
|
Dictionary track_data;
|
||||||
|
track_data["type"] = "transform";
|
||||||
|
set_meta(_TRANSFORM_TRACK_DATA_META_PROPERTY(track), track_data);
|
||||||
|
// Add an empty track that will get replaced after we are finished loading, so we don't throw off the track indices.
|
||||||
|
add_track(TYPE_ANIMATION, track);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DISABLE_DEPRECATED
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,6 +118,42 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
|
||||||
|
|
||||||
ERR_FAIL_INDEX_V(track, tracks.size(), false);
|
ERR_FAIL_INDEX_V(track, tracks.size(), false);
|
||||||
|
|
||||||
|
#ifndef DISABLE_DEPRECATED
|
||||||
|
if (what == "relative_to_rest") {
|
||||||
|
Track *t = tracks[track];
|
||||||
|
switch (t->type) {
|
||||||
|
case TYPE_POSITION_3D: {
|
||||||
|
PositionTrack *tt = static_cast<PositionTrack *>(t);
|
||||||
|
tt->relative_to_rest = p_value;
|
||||||
|
} break;
|
||||||
|
case TYPE_ROTATION_3D: {
|
||||||
|
RotationTrack *rt = static_cast<RotationTrack *>(t);
|
||||||
|
rt->relative_to_rest = p_value;
|
||||||
|
} break;
|
||||||
|
case TYPE_SCALE_3D: {
|
||||||
|
ScaleTrack *st = static_cast<ScaleTrack *>(t);
|
||||||
|
st->relative_to_rest = p_value;
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If we have a transform track, we need to store the data in the metadata to be able to convert it to the new format after the load is finished.
|
||||||
|
if (get_meta(_LOAD_META_PROPERTY, false)) { // Only do this on resource loads, not on editor changes
|
||||||
|
// check the metadata to see if this track is a transform track that we are holding on to
|
||||||
|
PackedInt32Array transform_tracks = get_meta(_TRANSFORM_TRACK_LIST_META_PROPERTY, PackedInt32Array());
|
||||||
|
if (transform_tracks.has(track)) {
|
||||||
|
// get the track data
|
||||||
|
Dictionary track_data = get_meta(_TRANSFORM_TRACK_DATA_META_PROPERTY(track), Dictionary());
|
||||||
|
track_data[what] = p_value;
|
||||||
|
set_meta(_TRANSFORM_TRACK_DATA_META_PROPERTY(track), track_data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DISABLE_DEPRECATED
|
||||||
|
|
||||||
if (what == "path") {
|
if (what == "path") {
|
||||||
track_set_path(track, p_value);
|
track_set_path(track, p_value);
|
||||||
} else if (what == "compressed_track") {
|
} else if (what == "compressed_track") {
|
||||||
|
@ -850,6 +907,11 @@ void Animation::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/interp", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
|
p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/interp", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
|
||||||
p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
|
p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
|
||||||
p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
|
p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
|
||||||
|
#ifndef DISABLE_DEPRECATED
|
||||||
|
if (track_is_relative_to_rest(i)) {
|
||||||
|
p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/relative_to_rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
|
||||||
|
}
|
||||||
|
#endif // DISABLE_DEPRECATED
|
||||||
}
|
}
|
||||||
if (track_get_type(i) == TYPE_AUDIO) {
|
if (track_get_type(i) == TYPE_AUDIO) {
|
||||||
p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/use_blend", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
|
p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/use_blend", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
|
||||||
|
@ -1093,6 +1155,63 @@ void Animation::_clear(T &p_keys) {
|
||||||
}
|
}
|
||||||
|
|
||||||
////
|
////
|
||||||
|
#ifndef DISABLE_DEPRECATED
|
||||||
|
bool Animation::has_tracks_relative_to_rest() const {
|
||||||
|
for (int i = 0; i < tracks.size(); i++) {
|
||||||
|
if (track_is_relative_to_rest(i)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Animation::track_is_relative_to_rest(int p_track) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_track, tracks.size(), false);
|
||||||
|
Track *t = tracks[p_track];
|
||||||
|
|
||||||
|
switch (t->type) {
|
||||||
|
case TYPE_POSITION_3D: {
|
||||||
|
PositionTrack *tt = static_cast<PositionTrack *>(t);
|
||||||
|
return tt->relative_to_rest;
|
||||||
|
} break;
|
||||||
|
case TYPE_ROTATION_3D: {
|
||||||
|
RotationTrack *rt = static_cast<RotationTrack *>(t);
|
||||||
|
return rt->relative_to_rest;
|
||||||
|
} break;
|
||||||
|
case TYPE_SCALE_3D: {
|
||||||
|
ScaleTrack *st = static_cast<ScaleTrack *>(t);
|
||||||
|
return st->relative_to_rest;
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
return false; // Animation does not really use transitions.
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Animation::track_set_relative_to_rest(int p_track, bool p_relative_to_rest) {
|
||||||
|
ERR_FAIL_INDEX(p_track, tracks.size());
|
||||||
|
Track *t = tracks[p_track];
|
||||||
|
|
||||||
|
switch (t->type) {
|
||||||
|
case TYPE_POSITION_3D: {
|
||||||
|
PositionTrack *tt = static_cast<PositionTrack *>(t);
|
||||||
|
tt->relative_to_rest = p_relative_to_rest;
|
||||||
|
} break;
|
||||||
|
case TYPE_ROTATION_3D: {
|
||||||
|
RotationTrack *rt = static_cast<RotationTrack *>(t);
|
||||||
|
rt->relative_to_rest = p_relative_to_rest;
|
||||||
|
} break;
|
||||||
|
case TYPE_SCALE_3D: {
|
||||||
|
ScaleTrack *st = static_cast<ScaleTrack *>(t);
|
||||||
|
st->relative_to_rest = p_relative_to_rest;
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
return; // Animation does not really use transitions.
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
emit_changed();
|
||||||
|
}
|
||||||
|
#endif // DISABLE_DEPRECATED
|
||||||
|
|
||||||
int Animation::position_track_insert_key(int p_track, double p_time, const Vector3 &p_position) {
|
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);
|
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
|
||||||
|
@ -5070,6 +5189,116 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Animation::_start_load(const StringName &p_res_format_type, int p_res_format_version) {
|
||||||
|
#ifndef DISABLE_DEPRECATED
|
||||||
|
set_meta(_LOAD_META_PROPERTY, true);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Animation::_finish_load(const StringName &p_res_format_type, int p_res_format_version) {
|
||||||
|
#ifndef DISABLE_DEPRECATED // 3.x compatibility, convert transform tracks to separate tracks
|
||||||
|
if (!has_meta(_LOAD_META_PROPERTY)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
set_meta(_LOAD_META_PROPERTY, Variant());
|
||||||
|
if (!has_meta(_TRANSFORM_TRACK_LIST_META_PROPERTY)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PackedInt32Array transform_track_list = get_meta(_TRANSFORM_TRACK_LIST_META_PROPERTY, PackedInt32Array());
|
||||||
|
set_meta(_TRANSFORM_TRACK_LIST_META_PROPERTY, Variant());
|
||||||
|
if (transform_track_list.is_empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int offset = 0;
|
||||||
|
for (int track_idx : transform_track_list) {
|
||||||
|
// now that we have all the tracks, we need to split the transform tracks into separate tracks
|
||||||
|
// this is because the current animation player doesn't support transform tracks
|
||||||
|
// so we need to split them into separate position, rotation, and scale tracks
|
||||||
|
// No need to worry about compression here; this was added in 4.x and wasn't backported to 3.x.
|
||||||
|
Dictionary track_data = get_meta(_TRANSFORM_TRACK_DATA_META_PROPERTY(track_idx), Dictionary());
|
||||||
|
if (track_data.is_empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// split the transform track into separate tracks
|
||||||
|
|
||||||
|
// Old scene format only used 32-bit floats, did not have configurable real_t.
|
||||||
|
Vector<float> keys = track_data["keys"];
|
||||||
|
int vcount = keys.size();
|
||||||
|
int tcount = vcount / 12;
|
||||||
|
ERR_CONTINUE_MSG((vcount % 12) != 0, "Failed to convert transform track: invalid number of key frames.");
|
||||||
|
|
||||||
|
Vector<real_t> position_keys;
|
||||||
|
Vector<real_t> rotation_keys;
|
||||||
|
Vector<real_t> scale_keys;
|
||||||
|
position_keys.resize(tcount * 5); // time + transition + xyz
|
||||||
|
rotation_keys.resize(tcount * 6); // time + transition + xyzw
|
||||||
|
scale_keys.resize(tcount * 5); // time + transition + xyz
|
||||||
|
// split the keys into separate tracks
|
||||||
|
for (int j = 0; j < tcount; j++) {
|
||||||
|
// it's position (Vector3, xyz), then rotation (Quaternion, xyzw), then scale (Vector3, xyz)
|
||||||
|
// each track has time and transition values, so get those
|
||||||
|
const float *ofs = &(keys.ptr()[j * 12]);
|
||||||
|
float time = ofs[0];
|
||||||
|
float transition = ofs[1];
|
||||||
|
|
||||||
|
position_keys.write[j * 5 + 0] = time;
|
||||||
|
position_keys.write[j * 5 + 1] = transition;
|
||||||
|
position_keys.write[j * 5 + 2] = ofs[2]; // x
|
||||||
|
position_keys.write[j * 5 + 3] = ofs[3]; // y
|
||||||
|
position_keys.write[j * 5 + 4] = ofs[4]; // z
|
||||||
|
|
||||||
|
rotation_keys.write[j * 6 + 0] = time;
|
||||||
|
rotation_keys.write[j * 6 + 1] = transition;
|
||||||
|
rotation_keys.write[j * 6 + 2] = ofs[5]; // x
|
||||||
|
rotation_keys.write[j * 6 + 3] = ofs[6]; // y
|
||||||
|
rotation_keys.write[j * 6 + 4] = ofs[7]; // z
|
||||||
|
rotation_keys.write[j * 6 + 5] = ofs[8]; // w
|
||||||
|
|
||||||
|
scale_keys.write[j * 5 + 0] = time;
|
||||||
|
scale_keys.write[j * 5 + 1] = transition;
|
||||||
|
scale_keys.write[j * 5 + 2] = ofs[9]; // x
|
||||||
|
scale_keys.write[j * 5 + 3] = ofs[10]; // y
|
||||||
|
scale_keys.write[j * 5 + 4] = ofs[11]; // z
|
||||||
|
}
|
||||||
|
Array c_track_keys = track_data.keys();
|
||||||
|
c_track_keys.erase("type");
|
||||||
|
c_track_keys.erase("keys");
|
||||||
|
remove_track(track_idx + offset); // remove dummy track
|
||||||
|
|
||||||
|
add_track(TYPE_POSITION_3D, track_idx + offset);
|
||||||
|
for (int j = 0; j < c_track_keys.size(); j++) {
|
||||||
|
String key = c_track_keys[j];
|
||||||
|
_set("tracks/" + itos(track_idx + offset) + "/" + key, track_data[key]);
|
||||||
|
}
|
||||||
|
_set("tracks/" + itos(track_idx + offset) + "/keys", position_keys);
|
||||||
|
_set("tracks/" + itos(track_idx + offset) + "/relative_to_rest", true);
|
||||||
|
offset++;
|
||||||
|
|
||||||
|
add_track(TYPE_ROTATION_3D, track_idx + offset);
|
||||||
|
for (int j = 0; j < c_track_keys.size(); j++) {
|
||||||
|
String key = c_track_keys[j];
|
||||||
|
_set("tracks/" + itos(track_idx + offset) + "/" + key, track_data[key]);
|
||||||
|
}
|
||||||
|
_set("tracks/" + itos(track_idx + offset) + "/keys", rotation_keys);
|
||||||
|
_set("tracks/" + itos(track_idx + offset) + "/relative_to_rest", true);
|
||||||
|
offset++;
|
||||||
|
add_track(TYPE_SCALE_3D, track_idx + offset);
|
||||||
|
for (int j = 0; j < c_track_keys.size(); j++) {
|
||||||
|
String key = c_track_keys[j];
|
||||||
|
_set("tracks/" + itos(track_idx + offset) + "/" + key, track_data[key]);
|
||||||
|
}
|
||||||
|
_set("tracks/" + itos(track_idx + offset) + "/keys", scale_keys);
|
||||||
|
_set("tracks/" + itos(track_idx + offset) + "/relative_to_rest", true);
|
||||||
|
offset++;
|
||||||
|
offset--; // subtract 1 because we removed the dummy track
|
||||||
|
// erase the track data
|
||||||
|
set_meta(_TRANSFORM_TRACK_DATA_META_PROPERTY(track_idx), Variant());
|
||||||
|
}
|
||||||
|
// erase the track list
|
||||||
|
set_meta(_TRANSFORM_TRACK_LIST_META_PROPERTY, Variant());
|
||||||
|
#endif // DISABLE_DEPRECATED
|
||||||
|
}
|
||||||
|
|
||||||
bool Animation::_rotation_interpolate_compressed(uint32_t p_compressed_track, double p_time, Quaternion &r_ret) const {
|
bool Animation::_rotation_interpolate_compressed(uint32_t p_compressed_track, double p_time, Quaternion &r_ret) const {
|
||||||
Vector3i current;
|
Vector3i current;
|
||||||
Vector3i next;
|
Vector3i next;
|
||||||
|
|
|
@ -138,6 +138,9 @@ private:
|
||||||
struct PositionTrack : public Track {
|
struct PositionTrack : public Track {
|
||||||
Vector<TKey<Vector3>> positions;
|
Vector<TKey<Vector3>> positions;
|
||||||
int32_t compressed_track = -1;
|
int32_t compressed_track = -1;
|
||||||
|
#ifndef DISABLE_DEPRECATED
|
||||||
|
bool relative_to_rest = false;
|
||||||
|
#endif
|
||||||
PositionTrack() { type = TYPE_POSITION_3D; }
|
PositionTrack() { type = TYPE_POSITION_3D; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -146,6 +149,9 @@ private:
|
||||||
struct RotationTrack : public Track {
|
struct RotationTrack : public Track {
|
||||||
Vector<TKey<Quaternion>> rotations;
|
Vector<TKey<Quaternion>> rotations;
|
||||||
int32_t compressed_track = -1;
|
int32_t compressed_track = -1;
|
||||||
|
#ifndef DISABLE_DEPRECATED
|
||||||
|
bool relative_to_rest = false;
|
||||||
|
#endif
|
||||||
RotationTrack() { type = TYPE_ROTATION_3D; }
|
RotationTrack() { type = TYPE_ROTATION_3D; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -154,6 +160,9 @@ private:
|
||||||
struct ScaleTrack : public Track {
|
struct ScaleTrack : public Track {
|
||||||
Vector<TKey<Vector3>> scales;
|
Vector<TKey<Vector3>> scales;
|
||||||
int32_t compressed_track = -1;
|
int32_t compressed_track = -1;
|
||||||
|
#ifndef DISABLE_DEPRECATED
|
||||||
|
bool relative_to_rest = false;
|
||||||
|
#endif
|
||||||
ScaleTrack() { type = TYPE_SCALE_3D; }
|
ScaleTrack() { type = TYPE_SCALE_3D; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -434,7 +443,11 @@ public:
|
||||||
double track_get_key_time(int p_track, int p_key_idx) const;
|
double track_get_key_time(int p_track, int p_key_idx) const;
|
||||||
real_t track_get_key_transition(int p_track, int p_key_idx) const;
|
real_t track_get_key_transition(int p_track, int p_key_idx) const;
|
||||||
bool track_is_compressed(int p_track) const;
|
bool track_is_compressed(int p_track) const;
|
||||||
|
#ifndef DISABLE_DEPRECATED
|
||||||
|
bool has_tracks_relative_to_rest() const;
|
||||||
|
bool track_is_relative_to_rest(int p_track) const;
|
||||||
|
void track_set_relative_to_rest(int p_track, bool p_relative_to_rest);
|
||||||
|
#endif
|
||||||
int position_track_insert_key(int p_track, double p_time, const Vector3 &p_position);
|
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_get_key(int p_track, int p_key, Vector3 *r_position) const;
|
||||||
Error try_position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation, bool p_backward = false) const;
|
Error try_position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation, bool p_backward = false) const;
|
||||||
|
@ -515,6 +528,9 @@ public:
|
||||||
void optimize(real_t p_allowed_velocity_err = 0.01, real_t p_allowed_angular_err = 0.01, int p_precision = 3);
|
void optimize(real_t p_allowed_velocity_err = 0.01, real_t p_allowed_angular_err = 0.01, int p_precision = 3);
|
||||||
void compress(uint32_t p_page_size = 8192, uint32_t p_fps = 120, float p_split_tolerance = 4.0); // 4.0 seems to be the split tolerance sweet spot from many tests.
|
void compress(uint32_t p_page_size = 8192, uint32_t p_fps = 120, float p_split_tolerance = 4.0); // 4.0 seems to be the split tolerance sweet spot from many tests.
|
||||||
|
|
||||||
|
virtual void _start_load(const StringName &p_res_format_type, int p_res_format_version) override;
|
||||||
|
virtual void _finish_load(const StringName &p_res_format_type, int p_res_format_version) override;
|
||||||
|
|
||||||
// Helper functions for Variant.
|
// Helper functions for Variant.
|
||||||
static bool is_variant_interpolatable(const Variant p_value);
|
static bool is_variant_interpolatable(const Variant p_value);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue