From 5b70ad9d34d2777e3abd0a13118442eb132aff70 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Mon, 6 Aug 2018 22:35:09 -0300 Subject: [PATCH] Respect process order for out of order skeleton bones (fixes GLTF2 import issues). --- editor/import/editor_scene_importer_gltf.cpp | 10 ++- editor/spatial_editor_gizmos.cpp | 12 +-- scene/3d/skeleton.cpp | 92 ++++++++++++++++++-- scene/3d/skeleton.h | 6 ++ 4 files changed, 104 insertions(+), 16 deletions(-) diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index b1c0c7bf0a4..906d902b4a2 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -1656,6 +1656,7 @@ void EditorSceneImporterGLTF::_generate_node(GLTFState &state, int p_node, Node if (n->mesh >= 0) { ERR_FAIL_INDEX(n->mesh, state.meshes.size()); MeshInstance *mi = memnew(MeshInstance); + print_line("**creating mesh for: " + n->name); GLTFMesh &mesh = state.meshes.write[n->mesh]; mi->set_mesh(mesh.mesh); 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]); p_parent_node->add_child(skeletons[i]); 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()) { Transform xform; - xform.basis = Basis(rot); - xform.basis.scale(scale); + //xform.basis = Basis(rot); + //xform.basis.scale(scale); + xform.basis.set_quat_scale(rot, scale); xform.origin = pos; Skeleton *skeleton = skeletons[node->joints[i].skin]; diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index c450c0fd4c3..9970f434ce4 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -1452,7 +1452,9 @@ void SkeletonSpatialGizmo::redraw() { Color bonecolor = Color(1.0, 0.4, 0.4, 0.3); 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); @@ -3371,10 +3373,10 @@ NavigationMeshSpatialGizmo::NavigationMeshSpatialGizmo(NavigationMeshInstance *p navmesh = p_navmesh; } -////// -/// -/// -/// + ////// + /// + /// + /// #define BODY_A_RADIUS 0.25 #define BODY_B_RADIUS 0.27 diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index f45cb022114..c796e47f256 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -131,7 +131,7 @@ void Skeleton::_get_property_list(List *p_list) const { String prep = "bones/" + itos(i) + "/"; 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::BOOL, prep + "enabled")); 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 *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) { switch (p_what) { @@ -181,19 +234,23 @@ void Skeleton::_notification(int p_what) { 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 if (rest_global_inverse_dirty) { // calculate global rests and invert them for (int i = 0; i < len; i++) { - Bone &b = bonesptr[i]; + Bone &b = bonesptr[order[i]]; if (b.parent >= 0) b.rest_global_inverse = bonesptr[b.parent].rest_global_inverse * b.rest; else b.rest_global_inverse = b.rest; } for (int i = 0; i < len; i++) { - Bone &b = bonesptr[i]; + Bone &b = bonesptr[order[i]]; b.rest_global_inverse.affine_invert(); } @@ -205,7 +262,7 @@ void Skeleton::_notification(int p_what) { for (int i = 0; i < len; i++) { - Bone &b = bonesptr[i]; + Bone &b = bonesptr[order[i]]; if (b.disable_rest) { if (b.enabled) { @@ -319,12 +376,13 @@ void Skeleton::add_bone(const String &p_name) { 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; b.name = p_name; bones.push_back(b); + process_order_dirty = true; rest_global_inverse_dirty = true; _make_dirty(); @@ -368,10 +426,11 @@ int Skeleton::get_bone_count() const { void Skeleton::set_bone_parent(int p_bone, int p_parent) { 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; rest_global_inverse_dirty = true; + process_order_dirty = true; _make_dirty(); } @@ -379,6 +438,8 @@ void Skeleton::unparent_bone_and_rest(int p_bone) { ERR_FAIL_INDEX(p_bone, bones.size()); + _update_process_order(); + int parent = bones[p_bone].parent; while (parent >= 0) { 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].rest_global_inverse = bones[p_bone].rest.affine_inverse(); //same thing + process_order_dirty = true; _make_dirty(); } @@ -489,6 +551,8 @@ void Skeleton::clear_bones() { bones.clear(); rest_global_inverse_dirty = true; + process_order_dirty = true; + _make_dirty(); } @@ -538,12 +602,21 @@ void Skeleton::_make_dirty() { 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() { - for (int i = bones.size() - 1; i >= 0; i--) { + _update_process_order(); - if (bones[i].parent >= 0) - set_bone_rest(i, bones[bones[i].parent].rest.affine_inverse() * bones[i].rest); + for (int i = bones.size() - 1; i >= 0; i--) { + 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; dirty = false; + process_order_dirty = true; skeleton = VisualServer::get_singleton()->skeleton_create(); set_notify_transform(true); } diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h index a8413ebaf26..e044e08437e 100644 --- a/scene/3d/skeleton.h +++ b/scene/3d/skeleton.h @@ -54,6 +54,7 @@ class Skeleton : public Spatial { bool enabled; int parent; + int sort_index; //used for re-sorting process order bool ignore_animation; @@ -92,6 +93,8 @@ class Skeleton : public Spatial { bool rest_global_inverse_dirty; Vector bones; + Vector process_order; + bool process_order_dirty; RID skeleton; @@ -112,6 +115,8 @@ class Skeleton : public Spatial { return bound; } + void _update_process_order(); + protected: bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); @@ -172,6 +177,7 @@ public: Transform get_bone_custom_pose(int p_bone) const; void localize_rests(); // used for loaders and tools + int get_process_order(int p_idx); #ifndef _3D_DISABLED // Physical bone API