Просмотр исходного кода

resource: fbx: read bone IDs and weights

Part-of: #276
Daniele Bartolini 1 год назад
Родитель
Сommit
cc165e56e8
3 измененных файлов с 112 добавлено и 0 удалено
  1. 65 0
      src/resource/fbx_document.cpp
  2. 8 0
      src/resource/fbx_document.h
  3. 39 0
      src/resource/mesh_fbx.cpp

+ 65 - 0
src/resource/fbx_document.cpp

@@ -16,6 +16,55 @@ namespace crown
 {
 namespace fbx
 {
+	static ufbx_node *find_first_non_bone_parent(ufbx_node *bone_node)
+	{
+		while (bone_node && bone_node->bone != NULL)
+			bone_node = bone_node->parent;
+
+		return bone_node;
+	}
+
+	static ufbx_node *find_skeleton_root(ufbx_node *node)
+	{
+		if (node->bone != NULL)
+			return node;
+
+		for (size_t i = 0; i < node->children.count; ++i) {
+			ufbx_node *n = find_skeleton_root(node->children.data[i]);
+			if (n != NULL)
+				return find_first_non_bone_parent(n);
+		}
+
+		return NULL;
+	}
+
+	u16 bone_id(const FBXDocument &fbx, StringId32 bone_name)
+	{
+		u16 deffault_bone_id = UINT16_MAX;
+		return hash_map::get(fbx.bone_ids, bone_name, deffault_bone_id);
+	}
+
+	u16 bone_id(const FBXDocument &fbx, const char *bone_name)
+	{
+		return bone_id(fbx, StringId32(bone_name));
+	}
+
+	static s32 populate_bone_ids(FBXDocument &fbx, ufbx_node *bone, CompileOptions &opts, u32 *debug_num_bones)
+	{
+		u16 bone_id = (u16)hash_map::size(fbx.bone_ids);
+		StringId32 bone_name = StringId32(bone->name.data);
+		hash_map::set(fbx.bone_ids, bone_name, bone_id);
+		(*debug_num_bones)++;
+
+		for (size_t i = 0; i < bone->children.count; ++i) {
+			ufbx_node *child = bone->children.data[i];
+			s32 err = populate_bone_ids(fbx, child, opts, debug_num_bones);
+			ENSURE_OR_RETURN(err == 0, opts);
+		}
+
+		return 0;
+	}
+
 	s32 parse(FBXDocument &fbx, Buffer &buf, CompileOptions &opts)
 	{
 		// Keep in sync with mesh_resource_fbx.vala!
@@ -47,6 +96,20 @@ namespace fbx
 			, "ufbx: %s"
 			, error.description.data
 			);
+
+		fbx.skeleton_root_node = find_skeleton_root(fbx.scene->root_node);
+		if (fbx.skeleton_root_node != NULL) {
+			u32 debug_num_bones = 0;
+			s32 err = populate_bone_ids(fbx, fbx.skeleton_root_node, opts, &debug_num_bones);
+			ENSURE_OR_RETURN(err == 0, opts);
+			RETURN_IF_FALSE(debug_num_bones == hash_map::size(fbx.bone_ids)
+				, opts
+				, "Bone mismatch expected/actual %u/%u"
+				, debug_num_bones
+				, hash_map::size(fbx.bone_ids)
+				);
+		}
+
 		return 0;
 	}
 
@@ -62,6 +125,8 @@ namespace fbx
 
 FBXDocument::FBXDocument(Allocator &a)
 	: scene(NULL)
+	, skeleton_root_node(NULL)
+	, bone_ids(a)
 {
 }
 

+ 8 - 0
src/resource/fbx_document.h

@@ -22,6 +22,8 @@ namespace crown
 struct FBXDocument
 {
 	ufbx_scene *scene;
+	ufbx_node *skeleton_root_node;
+	HashMap<StringId32, u16> bone_ids;
 
 	///
 	explicit FBXDocument(Allocator &a);
@@ -32,6 +34,12 @@ struct FBXDocument
 
 namespace fbx
 {
+	/// Returns the node ID for @a bone_name.
+	u16 bone_id(const FBXDocument &fbx, StringId32 bone_name);
+
+	/// Returns the node ID for @a bone_name.
+	u16 bone_id(const FBXDocument &fbx, const char *bone_name);
+
 	///
 	s32 parse(FBXDocument &fbx, Buffer &buf, CompileOptions &opts);
 

+ 39 - 0
src/resource/mesh_fbx.cpp

@@ -68,6 +68,38 @@ namespace fbx
 				array::push_back(g._positions, (f32)v.y);
 				array::push_back(g._positions, (f32)v.z);
 
+				// See: https://ufbx.github.io/elements/deformers/
+				if (mesh->skin_deformers.count == 1) {
+					ufbx_skin_deformer *skin = mesh->skin_deformers.data[0];
+
+					uint32_t vertex = mesh->vertex_indices.data[index];
+					ufbx_skin_vertex skin_vertex = skin->vertices.data[vertex];
+					f32 bones[Geometry::MAX_BONE_WEIGHTS] = { 0 };
+					f32 weights[Geometry::MAX_BONE_WEIGHTS] = { 0.0f };
+					u32 num_weights = min(u32(skin_vertex.num_weights), u32(Geometry::MAX_BONE_WEIGHTS));
+
+					// Read bone ID and weight.
+					f32 total_weight = 0.0f;
+					for (u32 i = 0; i < num_weights; i++) {
+						ufbx_skin_weight skin_weight = skin->weights.data[skin_vertex.weight_begin + i];
+						ufbx_skin_cluster *cluster = skin->clusters.data[skin_weight.cluster_index];
+
+						bones[i] = (f32)fbx::bone_id(fbx, cluster->bone_node->name.data);
+						weights[i] = (f32)skin_weight.weight;
+						total_weight += weights[i];
+					}
+
+					// FBX does not guarantee that skin weights are normalized, and we may even
+					// be dropping some, so we must renormalize them.
+					for (u32 i = 0; i < num_weights; i++)
+						weights[i] /= total_weight;
+
+					for (u32 i = 0; i < countof(bones); ++i) {
+						array::push_back(g._bones, bones[i]);
+						array::push_back(g._weights, weights[i]);
+					}
+				}
+
 				if (mesh->vertex_normal.exists) {
 					ufbx_vec3 v = ufbx_get_vertex_vec3(&mesh->vertex_normal, index);
 					array::push_back(g._normals, (f32)v.x);
@@ -133,6 +165,13 @@ namespace fbx
 			generate_indices(g._bitangent_indices, g._bitangents, sizeof(f32)*3);
 		}
 
+		if (mesh::has_bones(g)) {
+			array::resize(g._bone_indices, (u32)num_indices);
+			array::resize(g._weight_indices, (u32)num_indices);
+			generate_indices(g._bone_indices, g._bones, sizeof(f32)*4);
+			generate_indices(g._weight_indices, g._weights, sizeof(f32)*4);
+		}
+
 		return 0;
 	}