Browse Source

Respect process order for out of order skeleton bones (fixes GLTF2 import issues).

Juan Linietsky 7 years ago
parent
commit
5b70ad9d34

+ 8 - 2
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) {
 	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.scale(scale);
+						//xform.basis = Basis(rot);
+						//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];

+ 7 - 5
editor/spatial_editor_gizmos.cpp

@@ -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);
 
 
@@ -3371,10 +3373,10 @@ NavigationMeshSpatialGizmo::NavigationMeshSpatialGizmo(NavigationMeshInstance *p
 	navmesh = p_navmesh;
 	navmesh = p_navmesh;
 }
 }
 
 
-//////
-///
-///
-///
+	//////
+	///
+	///
+	///
 
 
 #define BODY_A_RADIUS 0.25
 #define BODY_A_RADIUS 0.25
 #define BODY_B_RADIUS 0.27
 #define BODY_B_RADIUS 0.27

+ 83 - 9
scene/3d/skeleton.cpp

@@ -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)
-			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;
 	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);
 }
 }

+ 6 - 0
scene/3d/skeleton.h

@@ -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