Procházet zdrojové kódy

Add automatic LOD-ing in GLTF importer

Panagiotis Christopoulos Charitos před 6 roky
rodič
revize
ef7b3d5e6c

+ 12 - 5
src/anki/importer/GltfImporter.cpp

@@ -612,7 +612,15 @@ Error GltfImporter::visitNode(
 			auto callback = [](void* userData, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* signalSemaphore) {
 			auto callback = [](void* userData, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* signalSemaphore) {
 				Ctx& self = *static_cast<Ctx*>(userData);
 				Ctx& self = *static_cast<Ctx*>(userData);
 
 
-				Error err = self.m_importer->writeMesh(*self.m_mesh);
+				Error err = self.m_importer->writeMesh(*self.m_mesh, CString(), 1.0f);
+
+				// LOD 1
+				if(!err)
+				{
+					StringAuto name(self.m_importer->m_alloc);
+					name.sprintf("%s_lod1", self.m_mesh->name);
+					err = self.m_importer->writeMesh(*self.m_mesh, name, 0.5f);
+				}
 
 
 				if(!err)
 				if(!err)
 				{
 				{
@@ -637,7 +645,6 @@ Error GltfImporter::visitNode(
 				if(err)
 				if(err)
 				{
 				{
 					self.m_importer->m_errorInThread.store(err._getCode());
 					self.m_importer->m_errorInThread.store(err._getCode());
-					printf("aaaaaaaaaaaaaaa\n");
 				}
 				}
 
 
 				self.m_importer->m_alloc.deleteInstance(&self);
 				self.m_importer->m_alloc.deleteInstance(&self);
@@ -737,10 +744,10 @@ Error GltfImporter::writeModel(const cgltf_mesh& mesh, CString skinName)
 
 
 	ANKI_CHECK(file.writeText("\t\t\t<mesh>%s%s.ankimesh</mesh>\n", m_rpath.cstr(), mesh.name));
 	ANKI_CHECK(file.writeText("\t\t\t<mesh>%s%s.ankimesh</mesh>\n", m_rpath.cstr(), mesh.name));
 
 
-	auto lod1 = extras.find("lod1");
-	if(lod1 != extras.getEnd())
 	{
 	{
-		ANKI_CHECK(file.writeText("\t\t\t<mesh1>%s%s</mesh1>\n", m_rpath.cstr(), lod1->cstr()));
+		StringAuto name(m_alloc);
+		name.sprintf("%s_lod1", mesh.name);
+		ANKI_CHECK(file.writeText("\t\t\t<mesh1>%s%s.ankimesh</mesh1>\n", m_rpath.cstr(), name.cstr()));
 	}
 	}
 
 
 	auto mtlOverride = extras.find("material_override");
 	auto mtlOverride = extras.find("material_override");

+ 1 - 1
src/anki/importer/GltfImporter.h

@@ -91,7 +91,7 @@ private:
 	}
 	}
 
 
 	// Resources
 	// Resources
-	ANKI_USE_RESULT Error writeMesh(const cgltf_mesh& mesh);
+	ANKI_USE_RESULT Error writeMesh(const cgltf_mesh& mesh, CString nameOverride, F32 decimateFactor);
 	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, CString skinName);
 	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);

+ 58 - 2
src/anki/importer/GltfImporterMesh.cpp

@@ -213,10 +213,61 @@ static void optimizeSubmesh(SubMesh& submesh, GenericMemoryPoolAllocator<U8> all
 	}
 	}
 }
 }
 
 
-Error GltfImporter::writeMesh(const cgltf_mesh& mesh)
+/// Decimate a submesh using meshoptimizer.
+static void decimateSubmesh(F32 factor, SubMesh& submesh, GenericMemoryPoolAllocator<U8> alloc)
+{
+	ANKI_ASSERT(factor > 0.0f && factor < 1.0f);
+	const PtrSize targetIndexCount = PtrSize(F32(submesh.m_indices.getSize() / 3) * factor) * 3;
+	if(targetIndexCount == 0)
+	{
+		return;
+	}
+
+	// Decimate
+	DynamicArrayAuto<U32> newIndices(alloc, targetIndexCount);
+	newIndices.resize(meshopt_simplify(&newIndices[0],
+		&submesh.m_indices[0],
+		submesh.m_indices.getSize(),
+		&submesh.m_verts[0].m_position.x(),
+		submesh.m_verts.getSize(),
+		sizeof(TempVertex),
+		targetIndexCount,
+		1e-2f));
+
+	// Re-pack
+	DynamicArrayAuto<U32> reindexedIndices(alloc);
+	DynamicArrayAuto<TempVertex> newVerts(alloc);
+	HashMapAuto<U32, U32> vertexStored(alloc);
+	for(PtrSize idx = 0; idx < newIndices.getSize(); ++idx)
+	{
+		U32 newIdx;
+		auto it = vertexStored.find(newIndices[idx]);
+		if(it == vertexStored.getEnd())
+		{
+			// Store the vertex
+			newVerts.emplaceBack(submesh.m_verts[newIndices[idx]]);
+			newIdx = U32(newVerts.getSize() - 1);
+			vertexStored.emplace(newIndices[idx], newIdx);
+		}
+		else
+		{
+			// Already stored
+			newIdx = *it;
+		}
+
+		// Store the new index
+		reindexedIndices.emplaceBack(newIdx);
+	}
+
+	// Move back
+	submesh.m_indices = std::move(reindexedIndices);
+	submesh.m_verts = std::move(newVerts);
+}
+
+Error GltfImporter::writeMesh(const cgltf_mesh& mesh, CString nameOverride, F32 decimateFactor)
 {
 {
 	StringAuto fname(m_alloc);
 	StringAuto fname(m_alloc);
-	fname.sprintf("%s%s.ankimesh", m_outDir.cstr(), mesh.name);
+	fname.sprintf("%s%s.ankimesh", m_outDir.cstr(), (nameOverride.isEmpty()) ? mesh.name : nameOverride.cstr());
 	ANKI_GLTF_LOGI("Importing mesh%s%s", (m_optimizeMeshes) ? " (will also optimize) " : " ", fname.cstr());
 	ANKI_GLTF_LOGI("Importing mesh%s%s", (m_optimizeMeshes) ? " (will also optimize) " : " ", fname.cstr());
 
 
 	ListAuto<SubMesh> submeshes(m_alloc);
 	ListAuto<SubMesh> submeshes(m_alloc);
@@ -470,6 +521,11 @@ Error GltfImporter::writeMesh(const cgltf_mesh& mesh)
 			optimizeSubmesh(submesh, m_alloc);
 			optimizeSubmesh(submesh, m_alloc);
 		}
 		}
 
 
+		if(decimateFactor < 1.0f)
+		{
+			decimateSubmesh(decimateFactor, submesh, m_alloc);
+		}
+
 		// Finalize
 		// Finalize
 		if(submesh.m_indices.getSize() == 0 || submesh.m_verts.getSize() == 0)
 		if(submesh.m_indices.getSize() == 0 || submesh.m_verts.getSize() == 0)
 		{
 		{

+ 0 - 6
src/anki/util/Allocator.h

@@ -118,12 +118,6 @@ public:
 		return *this;
 		return *this;
 	}
 	}
 
 
-	/// Check if it's initialized.
-	operator Bool() const
-	{
-		return m_pool != nullptr;
-	}
-
 	/// Get the address of a reference
 	/// Get the address of a reference
 	pointer address(reference x) const
 	pointer address(reference x) const
 	{
 	{

+ 9 - 0
src/anki/util/DynamicArray.h

@@ -305,6 +305,15 @@ public:
 	{
 	{
 	}
 	}
 
 
+	/// And resize
+	template<typename TAllocator>
+	DynamicArrayAuto(TAllocator alloc, PtrSize size)
+		: Base()
+		, m_alloc(alloc)
+	{
+		resize(size);
+	}
+
 	/// Move.
 	/// Move.
 	DynamicArrayAuto(DynamicArrayAuto&& b)
 	DynamicArrayAuto(DynamicArrayAuto&& b)
 		: Base()
 		: Base()

+ 10 - 0
src/anki/util/HashMap.h

@@ -55,6 +55,16 @@ public:
 	}
 	}
 };
 };
 
 
+template<>
+class DefaultHasher<U32>
+{
+public:
+	U64 operator()(const U32 a) const
+	{
+		return computeHash(&a, sizeof(a));
+	}
+};
+
 /// Hash map template.
 /// Hash map template.
 template<typename TKey, typename TValue, typename THasher = DefaultHasher<TKey>>
 template<typename TKey, typename TValue, typename THasher = DefaultHasher<TKey>>
 class HashMap
 class HashMap