Parcourir la source

Merge pull request #36415 from reduz/skeleton-skin-named

Add support for named binds in Skin.
Rémi Verschelde il y a 5 ans
Parent
commit
7ac0973e9a

+ 12 - 3
editor/import/editor_scene_importer_gltf.cpp

@@ -2183,6 +2183,8 @@ Error EditorSceneImporterGLTF::_map_skin_joints_indices_to_skeleton_bone_indices
 			const GLTFNodeIndex node_i = skin.joints_original[joint_index];
 			const GLTFNode *node = state.nodes[node_i];
 
+			skin.joint_i_to_name.insert(joint_index, node->name);
+
 			const int bone_index = skeleton.godot_skeleton->find_bone(node->name);
 			ERR_FAIL_COND_V(bone_index < 0, FAILED);
 
@@ -2204,12 +2206,18 @@ Error EditorSceneImporterGLTF::_create_skins(GLTFState &state) {
 		const bool has_ibms = !gltf_skin.inverse_binds.empty();
 
 		for (int joint_i = 0; joint_i < gltf_skin.joints_original.size(); ++joint_i) {
-			int bone_i = gltf_skin.joint_i_to_bone_i[joint_i];
 
+			Transform xform;
 			if (has_ibms) {
-				skin->add_bind(bone_i, gltf_skin.inverse_binds[joint_i]);
+				xform = gltf_skin.inverse_binds[joint_i];
+			}
+
+			if (state.use_named_skin_binds) {
+				StringName name = gltf_skin.joint_i_to_name[joint_i];
+				skin->add_named_bind(name, xform);
 			} else {
-				skin->add_bind(bone_i, Transform());
+				int bone_i = gltf_skin.joint_i_to_bone_i[joint_i];
+				skin->add_bind(bone_i, xform);
 			}
 		}
 
@@ -2995,6 +3003,7 @@ Node *EditorSceneImporterGLTF::import_scene(const String &p_path, uint32_t p_fla
 
 	state.major_version = version.get_slice(".", 0).to_int();
 	state.minor_version = version.get_slice(".", 1).to_int();
+	state.use_named_skin_binds = p_flags & IMPORT_USE_NAMED_SKIN_BINDS;
 
 	/* STEP 0 PARSE SCENE */
 	Error err = _parse_scenes(state);

+ 3 - 0
editor/import/editor_scene_importer_gltf.h

@@ -231,6 +231,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
 		// A mapping from the joint indices (in the order of joints_original) to the
 		// Godot Skeleton's bone_indices
 		Map<int, int> joint_i_to_bone_i;
+		Map<int, StringName> joint_i_to_name;
 
 		// The Actual Skin that will be created as a mapping between the IBM's of this skin
 		// to the generated skeleton for the mesh instances.
@@ -298,6 +299,8 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
 		int minor_version;
 		Vector<uint8_t> glb_data;
 
+		bool use_named_skin_binds;
+
 		Vector<GLTFNode *> nodes;
 		Vector<Vector<uint8_t> > buffers;
 		Vector<GLTFBufferView> buffer_views;

+ 4 - 0
editor/import/resource_importer_scene.cpp

@@ -1171,6 +1171,7 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/storage", PROPERTY_HINT_ENUM, "Built-In,Files (.mesh),Files (.tres)"), meshes_out ? 1 : 0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Enable,Gen Lightmaps", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "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, "external_files/store_in_subdir"), false));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::REAL, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15));
@@ -1313,6 +1314,9 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
 	if (int(p_options["materials/location"]) == 0)
 		import_flags |= EditorSceneImporter::IMPORT_MATERIALS_IN_INSTANCES;
 
+	if (bool(p_options["skins/use_named_skins"]))
+		import_flags |= EditorSceneImporter::IMPORT_USE_NAMED_SKIN_BINDS;
+
 	Error err = OK;
 	List<String> missing_deps; // for now, not much will be done with this
 	Node *scene = importer->import_scene(src_path, import_flags, fps, &missing_deps, &err);

+ 2 - 1
editor/import/resource_importer_scene.h

@@ -59,7 +59,8 @@ public:
 		IMPORT_GENERATE_TANGENT_ARRAYS = 256,
 		IMPORT_FAIL_ON_MISSING_DEPENDENCIES = 512,
 		IMPORT_MATERIALS_IN_INSTANCES = 1024,
-		IMPORT_USE_COMPRESSION = 2048
+		IMPORT_USE_COMPRESSION = 2048,
+		IMPORT_USE_NAMED_SKIN_BINDS = 4096,
 
 	};
 

+ 44 - 2
scene/3d/skeleton.cpp

@@ -41,6 +41,7 @@ void SkinReference::_skin_changed() {
 	if (skeleton_node) {
 		skeleton_node->_make_dirty();
 	}
+	skeleton_version = 0;
 }
 
 void SkinReference::_bind_methods() {
@@ -322,10 +323,49 @@ void Skeleton::_notification(int p_what) {
 				if (E->get()->bind_count != bind_count) {
 					VS::get_singleton()->skeleton_allocate(skeleton, bind_count);
 					E->get()->bind_count = bind_count;
+					E->get()->skin_bone_indices.resize(bind_count);
+					E->get()->skin_bone_indices_ptrs = E->get()->skin_bone_indices.ptrw();
+				}
+
+				if (E->get()->skeleton_version != version) {
+
+					for (uint32_t i = 0; i < bind_count; i++) {
+						StringName bind_name = skin->get_bind_name(i);
+
+						if (bind_name != StringName()) {
+							//bind name used, use this
+							bool found = false;
+							for (int j = 0; j < len; j++) {
+								if (bonesptr[j].name == bind_name) {
+									E->get()->skin_bone_indices_ptrs[i] = j;
+									found = true;
+									break;
+								}
+							}
+
+							if (!found) {
+								ERR_PRINT("Skin bind #" + itos(i) + " contains named bind '" + String(bind_name) + "' but Skeleton has no bone by that name.");
+								E->get()->skin_bone_indices_ptrs[i] = 0;
+							}
+						} else if (skin->get_bind_bone(i) >= 0) {
+							int bind_index = skin->get_bind_bone(i);
+							if (bind_index >= len) {
+								ERR_PRINT("Skin bind #" + itos(i) + " contains bone index bind: " + itos(bind_index) + " , which is greater than the skeleton bone count: " + itos(len) + ".");
+								E->get()->skin_bone_indices_ptrs[i] = 0;
+							} else {
+								E->get()->skin_bone_indices_ptrs[i] = bind_index;
+							}
+						} else {
+							ERR_PRINT("Skin bind #" + itos(i) + " does not contain a name nor a bone index.");
+							E->get()->skin_bone_indices_ptrs[i] = 0;
+						}
+					}
+
+					E->get()->skeleton_version = version;
 				}
 
 				for (uint32_t i = 0; i < bind_count; i++) {
-					uint32_t bone_index = skin->get_bind_bone(i);
+					uint32_t bone_index = E->get()->skin_bone_indices_ptrs[i];
 					ERR_CONTINUE(bone_index >= (uint32_t)len);
 					vs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i));
 				}
@@ -388,6 +428,7 @@ void Skeleton::add_bone(const String &p_name) {
 	b.name = p_name;
 	bones.push_back(b);
 	process_order_dirty = true;
+	version++;
 	_make_dirty();
 	update_gizmo();
 }
@@ -539,7 +580,7 @@ void Skeleton::clear_bones() {
 
 	bones.clear();
 	process_order_dirty = true;
-
+	version++;
 	_make_dirty();
 }
 
@@ -894,6 +935,7 @@ Skeleton::Skeleton() {
 
 	animate_physical_bones = true;
 	dirty = false;
+	version = 1;
 	process_order_dirty = true;
 }
 

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

@@ -51,6 +51,9 @@ class SkinReference : public Reference {
 	RID skeleton;
 	Ref<Skin> skin;
 	uint32_t bind_count = 0;
+	uint64_t skeleton_version = 0;
+	Vector<uint32_t> skin_bone_indices;
+	uint32_t *skin_bone_indices_ptrs;
 	void _skin_changed();
 
 protected:
@@ -123,6 +126,8 @@ private:
 	void _make_dirty();
 	bool dirty;
 
+	uint64_t version;
+
 	// bind helpers
 	Array _get_bound_child_nodes_to_bone(int p_bone) const {
 

+ 29 - 1
scene/resources/skin.cpp

@@ -45,6 +45,24 @@ void Skin::add_bind(int p_bone, const Transform &p_pose) {
 	set_bind_pose(index, p_pose);
 }
 
+void Skin::add_named_bind(const String &p_name, const Transform &p_pose) {
+
+	uint32_t index = bind_count;
+	set_bind_count(bind_count + 1);
+	set_bind_name(index, p_name);
+	set_bind_pose(index, p_pose);
+}
+
+void Skin::set_bind_name(int p_index, const StringName &p_name) {
+	ERR_FAIL_INDEX(p_index, bind_count);
+	bool notify_change = (binds_ptr[p_index].name != StringName()) != (p_name != StringName());
+	binds_ptr[p_index].name = p_name;
+	emit_changed();
+	if (notify_change) {
+		_change_notify();
+	}
+}
+
 void Skin::set_bind_bone(int p_index, int p_bone) {
 	ERR_FAIL_INDEX(p_index, bind_count);
 	binds_ptr[p_index].bone = p_bone;
@@ -75,6 +93,9 @@ bool Skin::_set(const StringName &p_name, const Variant &p_value) {
 		if (what == "bone") {
 			set_bind_bone(index, p_value);
 			return true;
+		} else if (what == "name") {
+			set_bind_name(index, p_value);
+			return true;
 		} else if (what == "pose") {
 			set_bind_pose(index, p_value);
 			return true;
@@ -95,6 +116,9 @@ bool Skin::_get(const StringName &p_name, Variant &r_ret) const {
 		if (what == "bone") {
 			r_ret = get_bind_bone(index);
 			return true;
+		} else if (what == "name") {
+			r_ret = get_bind_name(index);
+			return true;
 		} else if (what == "pose") {
 			r_ret = get_bind_pose(index);
 			return true;
@@ -105,7 +129,8 @@ bool Skin::_get(const StringName &p_name, Variant &r_ret) const {
 void Skin::_get_property_list(List<PropertyInfo> *p_list) const {
 	p_list->push_back(PropertyInfo(Variant::INT, "bind_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"));
 	for (int i = 0; i < get_bind_count(); i++) {
-		p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"));
+		p_list->push_back(PropertyInfo(Variant::STRING, "bind/" + itos(i) + "/name", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"));
+		p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater", get_bind_name(i) != StringName() ? PROPERTY_USAGE_NOEDITOR : PROPERTY_USAGE_DEFAULT));
 		p_list->push_back(PropertyInfo(Variant::TRANSFORM, "bind/" + itos(i) + "/pose"));
 	}
 }
@@ -120,6 +145,9 @@ void Skin::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_bind_pose", "bind_index", "pose"), &Skin::set_bind_pose);
 	ClassDB::bind_method(D_METHOD("get_bind_pose", "bind_index"), &Skin::get_bind_pose);
 
+	ClassDB::bind_method(D_METHOD("set_bind_name", "bind_index", "name"), &Skin::set_bind_name);
+	ClassDB::bind_method(D_METHOD("get_bind_name", "bind_index"), &Skin::get_bind_name);
+
 	ClassDB::bind_method(D_METHOD("set_bind_bone", "bind_index", "bone"), &Skin::set_bind_bone);
 	ClassDB::bind_method(D_METHOD("get_bind_bone", "bind_index"), &Skin::get_bind_bone);
 

+ 11 - 1
scene/resources/skin.h

@@ -37,7 +37,8 @@ class Skin : public Resource {
 	GDCLASS(Skin, Resource)
 
 	struct Bind {
-		int bone;
+		int bone = -1;
+		StringName name;
 		Transform pose;
 	};
 
@@ -58,9 +59,11 @@ public:
 	inline int get_bind_count() const { return bind_count; }
 
 	void add_bind(int p_bone, const Transform &p_pose);
+	void add_named_bind(const String &p_name, const Transform &p_pose);
 
 	void set_bind_bone(int p_index, int p_bone);
 	void set_bind_pose(int p_index, const Transform &p_pose);
+	void set_bind_name(int p_index, const StringName &p_name);
 
 	inline int get_bind_bone(int p_index) const {
 #ifdef DEBUG_ENABLED
@@ -69,6 +72,13 @@ public:
 		return binds_ptr[p_index].bone;
 	}
 
+	inline StringName get_bind_name(int p_index) const {
+#ifdef DEBUG_ENABLED
+		ERR_FAIL_INDEX_V(p_index, bind_count, StringName());
+#endif
+		return binds_ptr[p_index].name;
+	}
+
 	inline Transform get_bind_pose(int p_index) const {
 #ifdef DEBUG_ENABLED
 		ERR_FAIL_INDEX_V(p_index, bind_count, Transform());