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

Some work on the GLTF importer

Panagiotis Christopoulos Charitos 6 лет назад
Родитель
Сommit
7c248eb027
3 измененных файлов с 244 добавлено и 31 удалено
  1. 186 10
      tools/gltf_importer/Importer.cpp
  2. 54 5
      tools/gltf_importer/Importer.h
  3. 4 16
      tools/gltf_importer/ImporterMesh.cpp

+ 186 - 10
tools/gltf_importer/Importer.cpp

@@ -126,6 +126,8 @@ static ANKI_USE_RESULT Error getNodeTransform(const cgltf_node& node, Transform&
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
+const char* Importer::XML_HEADER = R"(<?xml version="1.0" encoding="UTF-8" ?>)";
+
 Importer::Importer()
 Importer::Importer()
 {
 {
 	m_hive = m_alloc.newInstance<ThreadHive>(getCpuCoresCount(), m_alloc, true);
 	m_hive = m_alloc.newInstance<ThreadHive>(getCpuCoresCount(), m_alloc, true);
@@ -573,7 +575,11 @@ Error Importer::visitNode(
 				ctx);
 				ctx);
 
 
 			ANKI_CHECK(writeMaterial(*node.mesh->primitives[0].material));
 			ANKI_CHECK(writeMaterial(*node.mesh->primitives[0].material));
-			ANKI_CHECK(writeModel(*node.mesh));
+			ANKI_CHECK(writeModel(*node.mesh, (node.skin) ? node.skin->name : CString()));
+			if(node.skin)
+			{
+				ANKI_CHECK(writeSkeleton(*node.skin));
+			}
 
 
 			ANKI_CHECK(writeModelNode(node, parentExtras));
 			ANKI_CHECK(writeModelNode(node, parentExtras));
 		}
 		}
@@ -622,7 +628,7 @@ Error Importer::writeTransform(const Transform& trf)
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
-Error Importer::writeModel(const cgltf_mesh& mesh)
+Error Importer::writeModel(const cgltf_mesh& mesh, CString skinName)
 {
 {
 	StringAuto modelFname(m_alloc);
 	StringAuto modelFname(m_alloc);
 	modelFname.sprintf("%s%s_%s.ankimdl", m_outDir.cstr(), mesh.name, mesh.primitives[0].material->name);
 	modelFname.sprintf("%s%s_%s.ankimdl", m_outDir.cstr(), mesh.name, mesh.primitives[0].material->name);
@@ -666,9 +672,13 @@ Error Importer::writeModel(const cgltf_mesh& mesh)
 
 
 	ANKI_CHECK(file.writeText("\t\t</modelPatch>\n"));
 	ANKI_CHECK(file.writeText("\t\t</modelPatch>\n"));
 
 
-	// TODO: Skeleton
-
 	ANKI_CHECK(file.writeText("\t</modelPatches>\n"));
 	ANKI_CHECK(file.writeText("\t</modelPatches>\n"));
+
+	if(skinName)
+	{
+		ANKI_CHECK(file.writeText("\t<skeleton>%s%s.ankiskel</skeleton>\n", m_rpath.cstr(), skinName.cstr()));
+	}
+
 	ANKI_CHECK(file.writeText("</model>\n"));
 	ANKI_CHECK(file.writeText("</model>\n"));
 
 
 	return Error::NONE;
 	return Error::NONE;
@@ -680,21 +690,130 @@ Error Importer::writeAnimation(const cgltf_animation& anim)
 	fname.sprintf("%s%s.ankianim", m_outDir.cstr(), anim.name);
 	fname.sprintf("%s%s.ankianim", m_outDir.cstr(), anim.name);
 	ANKI_GLTF_LOGI("Importing animation %s", fname.cstr());
 	ANKI_GLTF_LOGI("Importing animation %s", fname.cstr());
 
 
+	// Gather the channels
+	HashMapAuto<CString, Array<const cgltf_animation_channel*, 3>> channelMap(m_alloc);
+
+	for(U i = 0; i < anim.channels_count; ++i)
+	{
+		const cgltf_animation_channel& channel = anim.channels[i];
+		StringAuto channelName = getNodeName(*channel.target_node);
+
+		U idx;
+		switch(channel.target_path)
+		{
+		case cgltf_animation_path_type_translation:
+			idx = 0;
+			break;
+		case cgltf_animation_path_type_rotation:
+			idx = 1;
+			break;
+		case cgltf_animation_path_type_scale:
+			idx = 2;
+			break;
+		default:
+			ANKI_ASSERT(0);
+		}
+
+		auto it = channelMap.find(channelName.toCString());
+		if(it != channelMap.getEnd())
+		{
+			(*it)[idx] = &channel;
+		}
+		else
+		{
+			Array<const cgltf_animation_channel*, 3> arr = {};
+			arr[idx] = &channel;
+			channelMap.emplace(channelName.toCString(), arr);
+		}
+	}
+
+	// Write file
 	File file;
 	File file;
 	ANKI_CHECK(file.open(fname.toCString(), FileOpenFlag::WRITE));
 	ANKI_CHECK(file.open(fname.toCString(), FileOpenFlag::WRITE));
 
 
-	ANKI_CHECK(file.writeText("<animation>\n"));
+	ANKI_CHECK(file.writeText("%s\n<animation>\n", XML_HEADER));
 	ANKI_CHECK(file.writeText("\t<channels>\n"));
 	ANKI_CHECK(file.writeText("\t<channels>\n"));
 
 
-	for(U i = 0; i < anim.channels_count; ++i)
+	for(auto it = channelMap.getBegin(); it != channelMap.getEnd(); ++it)
 	{
 	{
-		const cgltf_animation_channel& channel = anim.channels[i];
+		Array<const cgltf_animation_channel*, 3> arr = *it;
+		const cgltf_animation_channel& channel = (arr[0]) ? *arr[0] : ((arr[1]) ? *arr[1] : *arr[2]);
+		StringAuto channelName = getNodeName(*channel.target_node);
+
+		ANKI_CHECK(file.writeText("\t\t<channel name=\"%s\">\n", channelName.cstr()));
+
+		// Positions
+		ANKI_CHECK(file.writeText("\t\t\t<positionKeys>\n"));
+		if(arr[0])
+		{
+			const cgltf_animation_channel& channel = *arr[0];
+			DynamicArrayAuto<F32> keys(m_alloc);
+			readAccessor(*channel.sampler->input, keys);
+			DynamicArrayAuto<Vec3> positions(m_alloc);
+			readAccessor(*channel.sampler->output, positions);
+			ANKI_GLTF_ASSERT(keys.getSize() == positions.getSize());
+
+			for(U i = 0; i < keys.getSize(); ++i)
+			{
+				ANKI_CHECK(file.writeText("\t\t\t\t<key time=\"%f\">%f %f %f</key>\n",
+					keys[i],
+					positions[i].x(),
+					positions[i].y(),
+					positions[i].z()));
+			}
+		}
+		ANKI_CHECK(file.writeText("\t\t\t</positionKeys>\n"));
 
 
-		ANKI_CHECK(file.writeText("\t\t<channel>\n"));
+		// Rotations
+		ANKI_CHECK(file.writeText("\t\t\t<rotationKeys>\n"));
+		if(arr[1])
+		{
+			const cgltf_animation_channel& channel = *arr[1];
+			DynamicArrayAuto<F32> keys(m_alloc);
+			readAccessor(*channel.sampler->input, keys);
+			DynamicArrayAuto<Quat> rotations(m_alloc);
+			readAccessor(*channel.sampler->output, rotations);
+			ANKI_GLTF_ASSERT(keys.getSize() == rotations.getSize());
+
+			for(U i = 0; i < keys.getSize(); ++i)
+			{
+				ANKI_CHECK(file.writeText("\t\t\t\t<key time=\"%f\">%f %f %f %f</key>\n",
+					keys[i],
+					rotations[i].x(),
+					rotations[i].y(),
+					rotations[i].z(),
+					rotations[i].w()));
+			}
+		}
+		ANKI_CHECK(file.writeText("\t\t\t</rotationKeys>\n"));
 
 
-		ANKI_CHECK(file.writeText("\t\t\t<name>%s</name>\n", getNodeName(*channel.target_node).cstr()));
+		// Scales
+		ANKI_CHECK(file.writeText("\t\t\t<scaleKeys>\n"));
+		if(arr[2])
+		{
+			const cgltf_animation_channel& channel = *arr[2];
+			DynamicArrayAuto<F32> keys(m_alloc);
+			readAccessor(*channel.sampler->input, keys);
+			DynamicArrayAuto<Vec3> scales(m_alloc);
+			readAccessor(*channel.sampler->output, scales);
+			ANKI_GLTF_ASSERT(keys.getSize() == scales.getSize());
+
+			for(U i = 0; i < keys.getSize(); ++i)
+			{
+				const F32 scaleEpsilon = 0.0001f;
+				if(absolute(scales[i][0] - scales[i][1]) > scaleEpsilon
+					|| absolute(scales[i][0] - scales[i][2]) > scaleEpsilon)
+				{
+					ANKI_GLTF_LOGE("Expecting uniform scale");
+					return Error::USER_DATA;
+				}
+
+				ANKI_CHECK(file.writeText("\t\t\t\t<key time=\"%f\">%f</key>\n", keys[i], scales[i].x()));
+			}
+		}
+		ANKI_CHECK(file.writeText("\t\t\t</scaleKeys>\n"));
 
 
-		ANKI_CHECK(file.writeText("\t\t<channel>\n"));
+		ANKI_CHECK(file.writeText("\t\t</channel>\n"));
 	}
 	}
 
 
 	ANKI_CHECK(file.writeText("\t</channels>\n"));
 	ANKI_CHECK(file.writeText("\t</channels>\n"));
@@ -703,6 +822,63 @@ Error Importer::writeAnimation(const cgltf_animation& anim)
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
+Error Importer::writeSkeleton(const cgltf_skin& skin)
+{
+	StringAuto fname(m_alloc);
+	fname.sprintf("%s%s.ankiskel", m_outDir.cstr(), skin.name);
+	ANKI_GLTF_LOGI("Importing skeleton %s", fname.cstr());
+
+	// Get matrices
+	DynamicArrayAuto<Mat4> boneMats(m_alloc);
+	readAccessor(*skin.inverse_bind_matrices, boneMats);
+	ANKI_GLTF_ASSERT(boneMats.getSize() == skin.joints_count);
+
+	// Write file
+	File file;
+	ANKI_CHECK(file.open(fname.toCString(), FileOpenFlag::WRITE));
+
+	ANKI_CHECK(file.writeText("%s\n<skeleton>\n", XML_HEADER));
+
+	for(U i = 0; i < skin.joints_count; ++i)
+	{
+		const cgltf_node& boneNode = *skin.joints[i];
+
+		StringAuto parent(m_alloc);
+
+		// Name & parent
+		ANKI_CHECK(file.writeText("\t<bone name=\"%s\" ", getNodeName(boneNode).cstr()));
+		if(boneNode.parent)
+		{
+			ANKI_CHECK(file.writeText("parent=\"%s\" ", getNodeName(*boneNode.parent).cstr()));
+		}
+
+		// Bone transform
+		ANKI_CHECK(file.writeText("boneTransform=\""));
+		for(U j = 0; j < 16; j++)
+		{
+			ANKI_CHECK(file.writeText("%f ", boneMats[i][j]));
+		}
+		ANKI_CHECK(file.writeText("\" "));
+
+		// Transform
+		Transform trf;
+		ANKI_CHECK(getNodeTransform(boneNode, trf));
+		Mat4 mat{trf};
+		ANKI_CHECK(file.writeText("tansform=\""));
+		for(U j = 0; j < 16; j++)
+		{
+			ANKI_CHECK(file.writeText("%f ", mat[j]));
+		}
+		ANKI_CHECK(file.writeText("\" "));
+
+		ANKI_CHECK(file.writeText("/>\n"));
+	}
+
+	ANKI_CHECK(file.writeText("</skeleton>\n"));
+
+	return Error::NONE;
+}
+
 Error Importer::writeLight(const cgltf_node& node, const HashMapAuto<CString, StringAuto>& parentExtras)
 Error Importer::writeLight(const cgltf_node& node, const HashMapAuto<CString, StringAuto>& parentExtras)
 {
 {
 	const cgltf_light& light = *node.light;
 	const cgltf_light& light = *node.light;

+ 54 - 5
tools/gltf_importer/Importer.h

@@ -11,6 +11,18 @@
 namespace anki
 namespace anki
 {
 {
 
 
+#define ANKI_GLTF_LOGI(...) ANKI_LOG("GLTF", NORMAL, __VA_ARGS__)
+#define ANKI_GLTF_LOGE(...) ANKI_LOG("GLTF", ERROR, __VA_ARGS__)
+#define ANKI_GLTF_LOGW(...) ANKI_LOG("GLTF", WARNING, __VA_ARGS__)
+#define ANKI_GLTF_LOGF(...) ANKI_LOG("GLTF", FATAL, __VA_ARGS__)
+
+#define ANKI_GLTF_ASSERT(expr) \
+	if(!(expr)) \
+	{ \
+		ANKI_GLTF_LOGE(#expr); \
+		return Error::USER_DATA; \
+	}
+
 /// Import GLTF and spit AnKi scenes.
 /// Import GLTF and spit AnKi scenes.
 class Importer
 class Importer
 {
 {
@@ -24,6 +36,8 @@ public:
 	Error writeAll();
 	Error writeAll();
 
 
 private:
 private:
+	static const char* XML_HEADER;
+
 	HeapAllocator<U8> m_alloc{allocAligned, nullptr};
 	HeapAllocator<U8> m_alloc{allocAligned, nullptr};
 
 
 	StringAuto m_inputFname = {m_alloc};
 	StringAuto m_inputFname = {m_alloc};
@@ -52,6 +66,7 @@ private:
 
 
 	HashMapAuto<const void*, U32, PtrHasher> m_nodePtrToIdx{m_alloc}; ///< Need an index for the unnamed nodes.
 	HashMapAuto<const void*, U32, PtrHasher> m_nodePtrToIdx{m_alloc}; ///< Need an index for the unnamed nodes.
 
 
+	// Misc
 	ANKI_USE_RESULT Error getExtras(const cgltf_extras& extras, HashMapAuto<CString, StringAuto>& out);
 	ANKI_USE_RESULT Error getExtras(const cgltf_extras& extras, HashMapAuto<CString, StringAuto>& out);
 	ANKI_USE_RESULT Error parseArrayOfNumbers(
 	ANKI_USE_RESULT Error parseArrayOfNumbers(
 		CString str, DynamicArrayAuto<F64>& out, const U* expectedArraySize = nullptr);
 		CString str, DynamicArrayAuto<F64>& out, const U* expectedArraySize = nullptr);
@@ -59,10 +74,23 @@ private:
 	void populateNodePtrToIdx(const cgltf_node& node, U& idx);
 	void populateNodePtrToIdx(const cgltf_node& node, U& idx);
 	StringAuto getNodeName(const cgltf_node& node);
 	StringAuto getNodeName(const cgltf_node& node);
 
 
+	template<typename T, typename TFunc>
+	static void readAccessor(const cgltf_accessor& accessor, DynamicArrayAuto<T>& out, TFunc func);
+
+	template<typename T>
+	static void readAccessor(const cgltf_accessor& accessor, DynamicArrayAuto<T>& out)
+	{
+		readAccessor(accessor, out, [](const T&) {});
+	}
+
+	// Resources
 	ANKI_USE_RESULT Error writeMesh(const cgltf_mesh& mesh);
 	ANKI_USE_RESULT Error writeMesh(const cgltf_mesh& mesh);
 	ANKI_USE_RESULT Error writeMaterial(const cgltf_material& mtl);
 	ANKI_USE_RESULT Error writeMaterial(const cgltf_material& mtl);
-	ANKI_USE_RESULT Error writeModel(const cgltf_mesh& mesh);
+	ANKI_USE_RESULT Error writeModel(const cgltf_mesh& mesh, CString skinName);
 	ANKI_USE_RESULT Error writeAnimation(const cgltf_animation& anim);
 	ANKI_USE_RESULT Error writeAnimation(const cgltf_animation& anim);
+	ANKI_USE_RESULT Error writeSkeleton(const cgltf_skin& skin);
+	template<typename T, typename TFunc>
+	static Error appendAttribute(const cgltf_attribute& attrib, DynamicArrayAuto<T>& out, TFunc func);
 
 
 	// Scene
 	// Scene
 	ANKI_USE_RESULT Error writeTransform(const Transform& trf);
 	ANKI_USE_RESULT Error writeTransform(const Transform& trf);
@@ -73,9 +101,30 @@ private:
 	ANKI_USE_RESULT Error writeModelNode(const cgltf_node& node, const HashMapAuto<CString, StringAuto>& parentExtras);
 	ANKI_USE_RESULT Error writeModelNode(const cgltf_node& node, const HashMapAuto<CString, StringAuto>& parentExtras);
 };
 };
 
 
-#define ANKI_GLTF_LOGI(...) ANKI_LOG("GLTF", NORMAL, __VA_ARGS__)
-#define ANKI_GLTF_LOGE(...) ANKI_LOG("GLTF", ERROR, __VA_ARGS__)
-#define ANKI_GLTF_LOGW(...) ANKI_LOG("GLTF", WARNING, __VA_ARGS__)
-#define ANKI_GLTF_LOGF(...) ANKI_LOG("GLTF", FATAL, __VA_ARGS__)
+template<typename T, typename TFunc>
+void Importer::readAccessor(const cgltf_accessor& accessor, DynamicArrayAuto<T>& out, TFunc func)
+{
+	const U8* base =
+		static_cast<const U8*>(accessor.buffer_view->buffer->data) + accessor.offset + accessor.buffer_view->offset;
+
+	PtrSize stride = accessor.buffer_view->stride;
+	if(stride == 0)
+	{
+		stride = accessor.stride;
+	}
+	ANKI_ASSERT(stride);
+	ANKI_ASSERT(stride >= sizeof(T));
+
+	const U count = accessor.count;
+
+	for(U i = 0; i < count; ++i)
+	{
+		const U8* ptr = base + stride * i;
+		T val;
+		memcpy(&val, ptr, sizeof(T)); // Memcpy because it might not be aligned
+		func(val);
+		out.emplaceBack(val);
+	}
+}
 
 
 } // end namespace anki
 } // end namespace anki

+ 4 - 16
tools/gltf_importer/ImporterMesh.cpp

@@ -52,13 +52,15 @@ static U cgltfComponentSize(cgltf_component_type type)
 	return out;
 	return out;
 }
 }
 
 
+#if 0
 static U calcImplicitStride(const cgltf_attribute& attrib)
 static U calcImplicitStride(const cgltf_attribute& attrib)
 {
 {
 	return cgltfComponentCount(attrib.data->type) * cgltfComponentSize(attrib.data->component_type);
 	return cgltfComponentCount(attrib.data->type) * cgltfComponentSize(attrib.data->component_type);
 }
 }
+#endif
 
 
 template<typename T, typename TFunc>
 template<typename T, typename TFunc>
-static Error appendAttribute(const cgltf_attribute& attrib, DynamicArrayAuto<T>& out, TFunc func)
+Error Importer::appendAttribute(const cgltf_attribute& attrib, DynamicArrayAuto<T>& out, TFunc func)
 {
 {
 	if(cgltfComponentCount(attrib.data->type) != T::COMPONENT_COUNT)
 	if(cgltfComponentCount(attrib.data->type) != T::COMPONENT_COUNT)
 	{
 	{
@@ -73,28 +75,14 @@ static Error appendAttribute(const cgltf_attribute& attrib, DynamicArrayAuto<T>&
 	}
 	}
 
 
 	ANKI_ASSERT(attrib.data);
 	ANKI_ASSERT(attrib.data);
-
-	const U8* base = static_cast<const U8*>(attrib.data->buffer_view->buffer->data) + attrib.data->offset
-					 + attrib.data->buffer_view->offset;
-
-	const PtrSize stride = (attrib.data->stride == 0) ? calcImplicitStride(attrib) : attrib.data->stride;
-
 	const U count = attrib.data->count;
 	const U count = attrib.data->count;
-
 	if(count == 0)
 	if(count == 0)
 	{
 	{
 		ANKI_GLTF_LOGE("Zero vertex count");
 		ANKI_GLTF_LOGE("Zero vertex count");
 		return Error::USER_DATA;
 		return Error::USER_DATA;
 	}
 	}
 
 
-	for(U i = 0; i < count; ++i)
-	{
-		const U8* ptr = base + stride * i;
-		T val;
-		memcpy(&val, ptr, sizeof(T)); // Memcpy because it might not be aligned
-		func(val);
-		out.emplaceBack(val);
-	}
+	readAccessor(*attrib.data, out, func);
 
 
 	return Error::NONE;
 	return Error::NONE;
 }
 }