Respect process order for out of order skeleton bones (fixes GLTF2 import issues).
This commit is contained in:
parent
00c573c255
commit
5b70ad9d34
4 changed files with 104 additions and 16 deletions
|
@ -1656,6 +1656,7 @@ void EditorSceneImporterGLTF::_generate_node(GLTFState &state, int p_node, Node
|
||||||
if (n->mesh >= 0) {
|
if (n->mesh >= 0) {
|
||||||
ERR_FAIL_INDEX(n->mesh, state.meshes.size());
|
ERR_FAIL_INDEX(n->mesh, state.meshes.size());
|
||||||
MeshInstance *mi = memnew(MeshInstance);
|
MeshInstance *mi = memnew(MeshInstance);
|
||||||
|
print_line("**creating mesh for: " + n->name);
|
||||||
GLTFMesh &mesh = state.meshes.write[n->mesh];
|
GLTFMesh &mesh = state.meshes.write[n->mesh];
|
||||||
mi->set_mesh(mesh.mesh);
|
mi->set_mesh(mesh.mesh);
|
||||||
if (mesh.mesh->get_name() == "") {
|
if (mesh.mesh->get_name() == "") {
|
||||||
|
@ -1730,6 +1731,10 @@ void EditorSceneImporterGLTF::_generate_bone(GLTFState &state, int p_node, Vecto
|
||||||
skeletons[i]->get_parent()->remove_child(skeletons[i]);
|
skeletons[i]->get_parent()->remove_child(skeletons[i]);
|
||||||
p_parent_node->add_child(skeletons[i]);
|
p_parent_node->add_child(skeletons[i]);
|
||||||
skeletons[i]->set_owner(owner);
|
skeletons[i]->set_owner(owner);
|
||||||
|
//may have meshes as children, set owner in them too
|
||||||
|
for (int j = 0; j < skeletons[i]->get_child_count(); j++) {
|
||||||
|
skeletons[i]->get_child(j)->set_owner(owner);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1978,8 +1983,9 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye
|
||||||
if (node->joints.size()) {
|
if (node->joints.size()) {
|
||||||
|
|
||||||
Transform xform;
|
Transform xform;
|
||||||
xform.basis = Basis(rot);
|
//xform.basis = Basis(rot);
|
||||||
xform.basis.scale(scale);
|
//xform.basis.scale(scale);
|
||||||
|
xform.basis.set_quat_scale(rot, scale);
|
||||||
xform.origin = pos;
|
xform.origin = pos;
|
||||||
|
|
||||||
Skeleton *skeleton = skeletons[node->joints[i].skin];
|
Skeleton *skeleton = skeletons[node->joints[i].skin];
|
||||||
|
|
|
@ -1452,7 +1452,9 @@ void SkeletonSpatialGizmo::redraw() {
|
||||||
Color bonecolor = Color(1.0, 0.4, 0.4, 0.3);
|
Color bonecolor = Color(1.0, 0.4, 0.4, 0.3);
|
||||||
Color rootcolor = Color(0.4, 1.0, 0.4, 0.1);
|
Color rootcolor = Color(0.4, 1.0, 0.4, 0.1);
|
||||||
|
|
||||||
for (int i = 0; i < skel->get_bone_count(); i++) {
|
for (int i_bone = 0; i_bone < skel->get_bone_count(); i_bone++) {
|
||||||
|
|
||||||
|
int i = skel->get_process_order(i_bone);
|
||||||
|
|
||||||
int parent = skel->get_bone_parent(i);
|
int parent = skel->get_bone_parent(i);
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@ void Skeleton::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
|
|
||||||
String prep = "bones/" + itos(i) + "/";
|
String prep = "bones/" + itos(i) + "/";
|
||||||
p_list->push_back(PropertyInfo(Variant::STRING, prep + "name"));
|
p_list->push_back(PropertyInfo(Variant::STRING, prep + "name"));
|
||||||
p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(i - 1) + ",1"));
|
p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1"));
|
||||||
p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "rest"));
|
p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "rest"));
|
||||||
p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled"));
|
p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled"));
|
||||||
p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
|
p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
|
||||||
|
@ -139,6 +139,59 @@ void Skeleton::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Skeleton::_update_process_order() {
|
||||||
|
|
||||||
|
if (!process_order_dirty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Bone *bonesptr = bones.ptrw();
|
||||||
|
int len = bones.size();
|
||||||
|
|
||||||
|
process_order.resize(len);
|
||||||
|
int *order = process_order.ptrw();
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
|
||||||
|
if (bonesptr[i].parent >= len) {
|
||||||
|
//validate this just in case
|
||||||
|
ERR_PRINTS("Bone " + itos(i) + " has invalid parent: " + itos(bonesptr[i].parent));
|
||||||
|
bonesptr[i].parent = -1;
|
||||||
|
}
|
||||||
|
order[i] = i;
|
||||||
|
bonesptr[i].sort_index = i;
|
||||||
|
}
|
||||||
|
//now check process order
|
||||||
|
int pass_count = 0;
|
||||||
|
while (pass_count < len * len) {
|
||||||
|
//using bubblesort because of simplicity, it wont run every frame though.
|
||||||
|
//bublesort worst case is O(n^2), and this may be an infinite loop if cyclic
|
||||||
|
bool swapped = false;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
int parent_idx = bonesptr[order[i]].parent;
|
||||||
|
if (parent_idx < 0)
|
||||||
|
continue; //do nothing because it has no parent
|
||||||
|
//swap indices
|
||||||
|
int parent_order = bonesptr[parent_idx].sort_index;
|
||||||
|
if (parent_order > i) {
|
||||||
|
bonesptr[order[i]].sort_index = parent_order;
|
||||||
|
bonesptr[parent_idx].sort_index = i;
|
||||||
|
//swap order
|
||||||
|
SWAP(order[i], order[parent_order]);
|
||||||
|
swapped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!swapped)
|
||||||
|
break;
|
||||||
|
pass_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pass_count == len * len) {
|
||||||
|
ERR_PRINT("Skeleton parenthood graph is cyclic");
|
||||||
|
}
|
||||||
|
|
||||||
|
process_order_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
void Skeleton::_notification(int p_what) {
|
void Skeleton::_notification(int p_what) {
|
||||||
|
|
||||||
switch (p_what) {
|
switch (p_what) {
|
||||||
|
@ -181,19 +234,23 @@ void Skeleton::_notification(int p_what) {
|
||||||
|
|
||||||
vs->skeleton_allocate(skeleton, len); // if same size, nothin really happens
|
vs->skeleton_allocate(skeleton, len); // if same size, nothin really happens
|
||||||
|
|
||||||
|
_update_process_order();
|
||||||
|
|
||||||
|
const int *order = process_order.ptr();
|
||||||
|
|
||||||
// pose changed, rebuild cache of inverses
|
// pose changed, rebuild cache of inverses
|
||||||
if (rest_global_inverse_dirty) {
|
if (rest_global_inverse_dirty) {
|
||||||
|
|
||||||
// calculate global rests and invert them
|
// calculate global rests and invert them
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
Bone &b = bonesptr[i];
|
Bone &b = bonesptr[order[i]];
|
||||||
if (b.parent >= 0)
|
if (b.parent >= 0)
|
||||||
b.rest_global_inverse = bonesptr[b.parent].rest_global_inverse * b.rest;
|
b.rest_global_inverse = bonesptr[b.parent].rest_global_inverse * b.rest;
|
||||||
else
|
else
|
||||||
b.rest_global_inverse = b.rest;
|
b.rest_global_inverse = b.rest;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
Bone &b = bonesptr[i];
|
Bone &b = bonesptr[order[i]];
|
||||||
b.rest_global_inverse.affine_invert();
|
b.rest_global_inverse.affine_invert();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +262,7 @@ void Skeleton::_notification(int p_what) {
|
||||||
|
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
|
|
||||||
Bone &b = bonesptr[i];
|
Bone &b = bonesptr[order[i]];
|
||||||
|
|
||||||
if (b.disable_rest) {
|
if (b.disable_rest) {
|
||||||
if (b.enabled) {
|
if (b.enabled) {
|
||||||
|
@ -319,12 +376,13 @@ void Skeleton::add_bone(const String &p_name) {
|
||||||
|
|
||||||
for (int i = 0; i < bones.size(); i++) {
|
for (int i = 0; i < bones.size(); i++) {
|
||||||
|
|
||||||
ERR_FAIL_COND(bones[i].name == "p_name");
|
ERR_FAIL_COND(bones[i].name == p_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bone b;
|
Bone b;
|
||||||
b.name = p_name;
|
b.name = p_name;
|
||||||
bones.push_back(b);
|
bones.push_back(b);
|
||||||
|
process_order_dirty = true;
|
||||||
|
|
||||||
rest_global_inverse_dirty = true;
|
rest_global_inverse_dirty = true;
|
||||||
_make_dirty();
|
_make_dirty();
|
||||||
|
@ -368,10 +426,11 @@ int Skeleton::get_bone_count() const {
|
||||||
void Skeleton::set_bone_parent(int p_bone, int p_parent) {
|
void Skeleton::set_bone_parent(int p_bone, int p_parent) {
|
||||||
|
|
||||||
ERR_FAIL_INDEX(p_bone, bones.size());
|
ERR_FAIL_INDEX(p_bone, bones.size());
|
||||||
ERR_FAIL_COND(p_parent != -1 && (p_parent < 0 || p_parent >= p_bone));
|
ERR_FAIL_COND(p_parent != -1 && (p_parent < 0));
|
||||||
|
|
||||||
bones.write[p_bone].parent = p_parent;
|
bones.write[p_bone].parent = p_parent;
|
||||||
rest_global_inverse_dirty = true;
|
rest_global_inverse_dirty = true;
|
||||||
|
process_order_dirty = true;
|
||||||
_make_dirty();
|
_make_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,6 +438,8 @@ void Skeleton::unparent_bone_and_rest(int p_bone) {
|
||||||
|
|
||||||
ERR_FAIL_INDEX(p_bone, bones.size());
|
ERR_FAIL_INDEX(p_bone, bones.size());
|
||||||
|
|
||||||
|
_update_process_order();
|
||||||
|
|
||||||
int parent = bones[p_bone].parent;
|
int parent = bones[p_bone].parent;
|
||||||
while (parent >= 0) {
|
while (parent >= 0) {
|
||||||
bones.write[p_bone].rest = bones[parent].rest * bones[p_bone].rest;
|
bones.write[p_bone].rest = bones[parent].rest * bones[p_bone].rest;
|
||||||
|
@ -387,6 +448,7 @@ void Skeleton::unparent_bone_and_rest(int p_bone) {
|
||||||
|
|
||||||
bones.write[p_bone].parent = -1;
|
bones.write[p_bone].parent = -1;
|
||||||
bones.write[p_bone].rest_global_inverse = bones[p_bone].rest.affine_inverse(); //same thing
|
bones.write[p_bone].rest_global_inverse = bones[p_bone].rest.affine_inverse(); //same thing
|
||||||
|
process_order_dirty = true;
|
||||||
|
|
||||||
_make_dirty();
|
_make_dirty();
|
||||||
}
|
}
|
||||||
|
@ -489,6 +551,8 @@ void Skeleton::clear_bones() {
|
||||||
|
|
||||||
bones.clear();
|
bones.clear();
|
||||||
rest_global_inverse_dirty = true;
|
rest_global_inverse_dirty = true;
|
||||||
|
process_order_dirty = true;
|
||||||
|
|
||||||
_make_dirty();
|
_make_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -538,12 +602,21 @@ void Skeleton::_make_dirty() {
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Skeleton::get_process_order(int p_idx) {
|
||||||
|
ERR_FAIL_INDEX_V(p_idx, bones.size(), -1);
|
||||||
|
_update_process_order();
|
||||||
|
return process_order[p_idx];
|
||||||
|
}
|
||||||
|
|
||||||
void Skeleton::localize_rests() {
|
void Skeleton::localize_rests() {
|
||||||
|
|
||||||
for (int i = bones.size() - 1; i >= 0; i--) {
|
_update_process_order();
|
||||||
|
|
||||||
if (bones[i].parent >= 0)
|
for (int i = bones.size() - 1; i >= 0; i--) {
|
||||||
set_bone_rest(i, bones[bones[i].parent].rest.affine_inverse() * bones[i].rest);
|
int idx = process_order[i];
|
||||||
|
if (bones[idx].parent >= 0) {
|
||||||
|
set_bone_rest(idx, bones[bones[idx].parent].rest.affine_inverse() * bones[idx].rest);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -752,6 +825,7 @@ Skeleton::Skeleton() {
|
||||||
|
|
||||||
rest_global_inverse_dirty = true;
|
rest_global_inverse_dirty = true;
|
||||||
dirty = false;
|
dirty = false;
|
||||||
|
process_order_dirty = true;
|
||||||
skeleton = VisualServer::get_singleton()->skeleton_create();
|
skeleton = VisualServer::get_singleton()->skeleton_create();
|
||||||
set_notify_transform(true);
|
set_notify_transform(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ class Skeleton : public Spatial {
|
||||||
|
|
||||||
bool enabled;
|
bool enabled;
|
||||||
int parent;
|
int parent;
|
||||||
|
int sort_index; //used for re-sorting process order
|
||||||
|
|
||||||
bool ignore_animation;
|
bool ignore_animation;
|
||||||
|
|
||||||
|
@ -92,6 +93,8 @@ class Skeleton : public Spatial {
|
||||||
bool rest_global_inverse_dirty;
|
bool rest_global_inverse_dirty;
|
||||||
|
|
||||||
Vector<Bone> bones;
|
Vector<Bone> bones;
|
||||||
|
Vector<int> process_order;
|
||||||
|
bool process_order_dirty;
|
||||||
|
|
||||||
RID skeleton;
|
RID skeleton;
|
||||||
|
|
||||||
|
@ -112,6 +115,8 @@ class Skeleton : public Spatial {
|
||||||
return bound;
|
return bound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _update_process_order();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool _get(const StringName &p_path, Variant &r_ret) const;
|
bool _get(const StringName &p_path, Variant &r_ret) const;
|
||||||
bool _set(const StringName &p_path, const Variant &p_value);
|
bool _set(const StringName &p_path, const Variant &p_value);
|
||||||
|
@ -172,6 +177,7 @@ public:
|
||||||
Transform get_bone_custom_pose(int p_bone) const;
|
Transform get_bone_custom_pose(int p_bone) const;
|
||||||
|
|
||||||
void localize_rests(); // used for loaders and tools
|
void localize_rests(); // used for loaders and tools
|
||||||
|
int get_process_order(int p_idx);
|
||||||
|
|
||||||
#ifndef _3D_DISABLED
|
#ifndef _3D_DISABLED
|
||||||
// Physical bone API
|
// Physical bone API
|
||||||
|
|
Loading…
Reference in a new issue