Browse Source

Everything compiles

Panagiotis Christopoulos Charitos 3 years ago
parent
commit
6f931b09ea

+ 2 - 1
AnKi/Core/App.cpp

@@ -359,7 +359,8 @@ Error App::initInternal(AllocAlignedCallback allocCb, void* allocCbUserData)
 	m_scene = newInstance<SceneGraph>(m_mainPool);
 
 	ANKI_CHECK(m_scene->init(m_mainPool.getAllocationCallback(), m_mainPool.getAllocationCallbackUserData(),
-							 m_threadHive, m_resources, m_input, m_script, m_ui, m_config, &m_globalTimestamp));
+							 m_threadHive, m_resources, m_input, m_script, m_ui, m_config, &m_globalTimestamp,
+							 m_unifiedGometryMemPool));
 
 	// Inform the script engine about some subsystems
 	m_script->setRenderer(m_renderer);

+ 10 - 49
AnKi/Importer/GltfImporter.cpp

@@ -701,16 +701,6 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 			HashMapRaii<CString, StringRaii>::Iterator it2;
 			const Bool selfCollision = (it2 = extras.find("collision_mesh")) != extras.getEnd() && *it2 == "self";
 
-			U32 maxLod = 0;
-			if(m_lodCount > 1 && !skipMeshLod(*node.mesh, 1))
-			{
-				maxLod = 1;
-			}
-			if(m_lodCount > 2 && !skipMeshLod(*node.mesh, 2))
-			{
-				maxLod = 2;
-			}
-
 			// Thread task
 			auto callback = [](void* userData, [[maybe_unused]] U32 threadId, [[maybe_unused]] ThreadHive& hive,
 							   [[maybe_unused]] ThreadHiveSemaphore* signalSemaphore) {
@@ -718,22 +708,9 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 
 				Error err = self.m_importer->m_errorInThread.load();
 
-				// LOD 0
 				if(!err)
 				{
-					err = self.m_importer->writeMesh(*self.m_mesh, 0, self.m_importer->computeLodFactor(0));
-				}
-
-				// LOD 1
-				if(!err && self.m_importer->m_lodCount > 1 && !self.m_importer->skipMeshLod(*self.m_mesh, 1))
-				{
-					err = self.m_importer->writeMesh(*self.m_mesh, 1, self.m_importer->computeLodFactor(1));
-				}
-
-				// LOD 2
-				if(!err && self.m_importer->m_lodCount > 2 && !self.m_importer->skipMeshLod(*self.m_mesh, 2))
-				{
-					err = self.m_importer->writeMesh(*self.m_mesh, 2, self.m_importer->computeLodFactor(2));
+					err = self.m_importer->writeMesh(*self.m_mesh);
 				}
 
 				for(U32 i = 0; i < self.m_materialCount && !err; ++i)
@@ -781,7 +758,7 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 
 				ANKI_CHECK(m_sceneFile.writeText("comp = node2:getSceneNodeBase():getBodyComponent()\n"));
 
-				const StringRaii meshFname = computeMeshResourceFilename(*node.mesh, maxLod);
+				const StringRaii meshFname = computeMeshResourceFilename(*node.mesh);
 
 				ANKI_CHECK(
 					m_sceneFile.writeTextf("comp:loadMeshResource(\"%s%s\")\n", m_rpath.cstr(), meshFname.cstr()));
@@ -851,30 +828,17 @@ Error GltfImporter::writeModel(const cgltf_mesh& mesh)
 
 	for(U32 primIdx = 0; primIdx < mesh.primitives_count; ++primIdx)
 	{
-		if(mesh.primitives_count == 1)
-		{
-			ANKI_CHECK(file.writeText("\t\t<modelPatch>\n"));
-		}
-		else
-		{
-			ANKI_CHECK(file.writeTextf("\t\t<modelPatch subMeshIndex=\"%u\">\n", primIdx));
-		}
+		ANKI_CHECK(file.writeText("\t\t<modelPatch>\n"));
 
+		const StringRaii meshFname = computeMeshResourceFilename(mesh);
+		if(mesh.primitives_count == 1)
 		{
-			const StringRaii meshFname = computeMeshResourceFilename(mesh);
 			ANKI_CHECK(file.writeTextf("\t\t\t<mesh>%s%s</mesh>\n", m_rpath.cstr(), meshFname.cstr()));
 		}
-
-		if(m_lodCount > 1 && !skipMeshLod(mesh, 1))
-		{
-			const StringRaii meshFname = computeMeshResourceFilename(mesh, 1);
-			ANKI_CHECK(file.writeTextf("\t\t\t<mesh1>%s%s</mesh1>\n", m_rpath.cstr(), meshFname.cstr()));
-		}
-
-		if(m_lodCount > 2 && !skipMeshLod(mesh, 2))
+		else
 		{
-			const StringRaii meshFname = computeMeshResourceFilename(mesh, 2);
-			ANKI_CHECK(file.writeTextf("\t\t\t<mesh2>%s%s</mesh2>\n", m_rpath.cstr(), meshFname.cstr()));
+			ANKI_CHECK(file.writeTextf("\t\t\t<mesh subMeshIndex=\"%u\">%s%s</mesh>\n", primIdx, m_rpath.cstr(),
+									   meshFname.cstr()));
 		}
 
 		HashMapRaii<CString, StringRaii> materialExtras(m_pool);
@@ -1173,14 +1137,11 @@ StringRaii GltfImporter::computeModelResourceFilename(const cgltf_mesh& mesh) co
 	return out;
 }
 
-StringRaii GltfImporter::computeMeshResourceFilename(const cgltf_mesh& mesh, U32 lod) const
+StringRaii GltfImporter::computeMeshResourceFilename(const cgltf_mesh& mesh) const
 {
 	const U64 hash = computeHash(mesh.name, strlen(mesh.name));
-
 	StringRaii out(m_pool);
-
-	out.sprintf("%.64s_lod%u_%" PRIx64 ".ankimesh", mesh.name, lod, hash); // Limit the filename size
-
+	out.sprintf("%.64s_%" PRIx64 ".ankimesh", mesh.name, hash); // Limit the filename size
 	return out;
 }
 

+ 2 - 2
AnKi/Importer/GltfImporter.h

@@ -129,13 +129,13 @@ private:
 
 	// Compute filenames for various resources. Use a hash to solve the casing issue and remove unwanted special chars
 	StringRaii computeModelResourceFilename(const cgltf_mesh& mesh) const;
-	StringRaii computeMeshResourceFilename(const cgltf_mesh& mesh, U32 lod = 0) const;
+	StringRaii computeMeshResourceFilename(const cgltf_mesh& mesh) const;
 	StringRaii computeMaterialResourceFilename(const cgltf_material& mtl) const;
 	StringRaii computeAnimationResourceFilename(const cgltf_animation& anim) const;
 	StringRaii computeSkeletonResourceFilename(const cgltf_skin& skin) const;
 
 	// Resources
-	Error writeMesh(const cgltf_mesh& mesh, U32 lod, F32 decimateFactor);
+	Error writeMesh(const cgltf_mesh& mesh);
 	Error writeMaterial(const cgltf_material& mtl, Bool writeRayTracing);
 	Error writeModel(const cgltf_mesh& mesh);
 	Error writeAnimation(const cgltf_animation& anim);

+ 153 - 201
AnKi/Importer/GltfImporterMesh.cpp

@@ -8,7 +8,7 @@
 #include <AnKi/Collision/Plane.h>
 #include <AnKi/Collision/Functions.h>
 #include <AnKi/Resource/MeshBinary.h>
-#include <AnKi/Shaders/Include/ModelTypes.h>
+#include <AnKi/Shaders/Include/MeshTypes.h>
 #include <MeshOptimizer/meshoptimizer.h>
 
 namespace anki {
@@ -93,21 +93,6 @@ static Error checkAttribute(const cgltf_attribute& attrib)
 	return Error::kNone;
 }
 
-/// Align after laying a buffer in a file.
-static Error alignBufferInFile(PtrSize bufferSize, File& file)
-{
-	const PtrSize alignedBufferSize = getAlignedRoundUp(kMeshBinaryBufferAlignment, bufferSize);
-	const PtrSize extraBytes = alignedBufferSize - bufferSize;
-
-	for(U32 i = 0; i < extraBytes; ++i)
-	{
-		U8 value = 0;
-		ANKI_CHECK(file.write(&value, sizeof(value)));
-	}
-
-	return Error::kNone;
-}
-
 class TempVertex
 {
 public:
@@ -430,6 +415,18 @@ static Bool isConvex(const List<SubMesh>& submeshes)
 	return convex;
 }
 
+static void writeVertexAttribAndBufferInfoToHeader(VertexStreamId stream, MeshBinaryHeader& header)
+{
+	MeshBinaryVertexAttribute& attrib = header.m_vertexAttributes[stream];
+	attrib.m_bufferIndex = U32(stream);
+	attrib.m_format = kMeshRelatedVertexStreamFormats[stream];
+	attrib.m_relativeOffset = 0;
+	attrib.m_scale = 1.0f;
+
+	MeshBinaryVertexBuffer& buff = header.m_vertexBuffers[stream];
+	buff.m_vertexStride = getFormatInfo(attrib.m_format).m_texelSize;
+}
+
 U32 GltfImporter::getMeshTotalVertexCount(const cgltf_mesh& mesh)
 {
 	U32 totalVertexCount = 0;
@@ -443,20 +440,16 @@ U32 GltfImporter::getMeshTotalVertexCount(const cgltf_mesh& mesh)
 	return totalVertexCount;
 }
 
-Error GltfImporter::writeMesh(const cgltf_mesh& mesh, U32 lod, F32 decimateFactor)
+Error GltfImporter::writeMesh(const cgltf_mesh& mesh)
 {
+	StringRaii meshName = computeMeshResourceFilename(mesh);
 	StringRaii fname(m_pool);
-	fname.sprintf("%s%s", m_outDir.cstr(), computeMeshResourceFilename(mesh, lod).cstr());
-	ANKI_IMPORTER_LOGV("Importing mesh (%s, decimate factor %f): %s",
-					   (m_optimizeMeshes) ? "optimize" : "WON'T optimize", decimateFactor, fname.cstr());
+	fname.sprintf("%s%s", m_outDir.cstr(), meshName.cstr());
+	ANKI_IMPORTER_LOGV("Importing mesh (%s): %s", (m_optimizeMeshes) ? "optimize" : "WON'T optimize", fname.cstr());
 
-	ListRaii<SubMesh> submeshes(m_pool);
-	U32 totalIndexCount = 0;
-	U32 totalVertexCount = 0;
+	Array<ListRaii<SubMesh>, kMaxLodCount> submeshes = {{{m_pool}, {m_pool}, {m_pool}}};
 	Vec3 aabbMin(kMaxF32);
 	Vec3 aabbMax(kMinF32);
-	F32 maxUvDistance = kMinF32;
-	F32 minUvDistance = kMaxF32;
 	Bool hasBoneWeights = false;
 
 	// Iterate primitives. Every primitive is a submesh
@@ -469,7 +462,7 @@ Error GltfImporter::writeMesh(const cgltf_mesh& mesh, U32 lod, F32 decimateFacto
 			return Error::kUserData;
 		}
 
-		SubMesh& submesh = *submeshes.emplaceBack(m_pool);
+		SubMesh& submesh = *submeshes[0].emplaceBack(m_pool);
 
 		U minVertCount = kMaxU;
 		U maxVertCount = kMinU;
@@ -518,8 +511,6 @@ Error GltfImporter::writeMesh(const cgltf_mesh& mesh, U32 lod, F32 decimateFacto
 				U32 count = 0;
 				ANKI_CHECK(checkAttribute<Vec2>(*attrib));
 				visitAccessor<Vec2>(*attrib->data, [&](const Vec2& uv) {
-					maxUvDistance = max(maxUvDistance, max(uv.x(), uv.y()));
-					minUvDistance = min(minUvDistance, min(uv.x(), uv.y()));
 					submesh.m_verts[count++].m_uv = uv;
 				});
 			}
@@ -601,7 +592,7 @@ Error GltfImporter::writeMesh(const cgltf_mesh& mesh, U32 lod, F32 decimateFacto
 		// Re-index meshes now and
 		// - before the tanget calculation because that will create many unique verts
 		// - after normal fix because that will create verts with same attributes
-		if(m_optimizeMeshes || decimateFactor < 1.0f)
+		if(m_optimizeMeshes)
 		{
 			reindexSubmesh(submesh, m_pool);
 			vertCount = submesh.m_verts.getSize();
@@ -616,230 +607,191 @@ Error GltfImporter::writeMesh(const cgltf_mesh& mesh, U32 lod, F32 decimateFacto
 			optimizeSubmesh(submesh, m_pool);
 		}
 
-		// Simplify
-		if(decimateFactor < 1.0f)
-		{
-			decimateSubmesh(decimateFactor, submesh, m_pool);
-		}
-
 		// Finalize
 		if(submesh.m_indices.getSize() == 0 || submesh.m_verts.getSize() == 0)
 		{
-			// Digenerate
-			submeshes.popBack();
-		}
-		else
-		{
-			// Finalize
-			submesh.m_firstIdx = totalIndexCount;
-			submesh.m_idxCount = submesh.m_indices.getSize();
-			totalIndexCount += submesh.m_idxCount;
-			totalVertexCount += submesh.m_verts.getSize();
+			ANKI_UTIL_LOGE("Mesh degenerate: %s", meshName.cstr());
+			return Error::kUserData;
 		}
 	}
 
-	if(submeshes.getSize() == 0)
+	// Generate submeshes for the other LODs
+	ANKI_ASSERT(m_lodCount <= kMaxLodCount && m_lodCount > 0);
+	U32 maxLod = 0;
+	for(U32 lod = 1; lod < m_lodCount; ++lod)
 	{
-		ANKI_IMPORTER_LOGE("Mesh contains degenerate geometry");
-		return Error::kUserData;
-	}
-
-	// Find if it's a convex shape
-	const Bool convex = isConvex(submeshes);
-
-	// Chose the formats of the attributes
-	MeshBinaryHeader header;
-	memset(&header, 0, sizeof(header));
-	{
-		// Positions
-		MeshBinaryVertexAttribute& posa = header.m_vertexAttributes[VertexAttributeId::kPosition];
-		posa.m_bufferBinding = 0;
-		posa.m_format = Format::kR32G32B32_Sfloat;
-		posa.m_relativeOffset = 0;
-		posa.m_scale = 1.0f;
-
-		// Normals
-		MeshBinaryVertexAttribute& na = header.m_vertexAttributes[VertexAttributeId::kNormal];
-		na.m_bufferBinding = 1;
-		na.m_format = Format::kA2B10G10R10_Snorm_Pack32;
-		na.m_relativeOffset = 0;
-		na.m_scale = 1.0f;
-
-		// Tangents
-		MeshBinaryVertexAttribute& ta = header.m_vertexAttributes[VertexAttributeId::kTangent];
-		ta.m_bufferBinding = 1;
-		ta.m_format = Format::kA2B10G10R10_Snorm_Pack32;
-		ta.m_relativeOffset = sizeof(U32);
-		ta.m_scale = 1.0f;
-
-		// UVs
-		MeshBinaryVertexAttribute& uva = header.m_vertexAttributes[VertexAttributeId::kUv0];
-		uva.m_bufferBinding = 1;
-		uva.m_format = Format::kR32G32_Sfloat;
-		uva.m_relativeOffset = sizeof(U32) * 2;
-		uva.m_scale = 1.0f;
-
-		// Bone weight
-		if(hasBoneWeights)
+		if(skipMeshLod(mesh, lod))
 		{
-			MeshBinaryVertexAttribute& bidxa = header.m_vertexAttributes[VertexAttributeId::kBoneIndices];
-			bidxa.m_bufferBinding = 2;
-			bidxa.m_format = Format::kR8G8B8A8_Uint;
-			bidxa.m_relativeOffset = 0;
-			bidxa.m_scale = 1.0f;
-
-			MeshBinaryVertexAttribute& wa = header.m_vertexAttributes[VertexAttributeId::kBoneWeights];
-			wa.m_bufferBinding = 2;
-			wa.m_format = Format::kR8G8B8A8_Unorm;
-			wa.m_relativeOffset = sizeof(U8Vec4);
-			wa.m_scale = 1.0f;
+			break;
 		}
-	}
-
-	// Arange the attributes into vert buffers
-	{
-		// First buff has positions
-		header.m_vertexBuffers[0].m_vertexStride = sizeof(Vec3);
-		++header.m_vertexBufferCount;
-
-		// 2nd buff has normal + tangent + texcoords
-		header.m_vertexBuffers[1].m_vertexStride = sizeof(MainVertex);
-		++header.m_vertexBufferCount;
 
-		// 3rd has bone weights
-		if(hasBoneWeights)
+		for(const SubMesh& lod0Submesh : submeshes[0])
 		{
-			header.m_vertexBuffers[2].m_vertexStride = sizeof(BoneInfoVertex);
-			++header.m_vertexBufferCount;
-		}
-	}
+			SubMesh& newSubmesh = *submeshes[lod].pushBack(m_pool);
+			newSubmesh = lod0Submesh; // Copy LOD0 data to new submesh
 
-	// Write some other header stuff
-	{
-		memcpy(&header.m_magic[0], kMeshMagic, 8);
-		header.m_flags = MeshBinaryFlag::kNone;
-		if(convex)
-		{
-			header.m_flags |= MeshBinaryFlag::kConvex;
+			decimateSubmesh(computeLodFactor(lod), newSubmesh, m_pool);
 		}
-		header.m_indexType = IndexType::kU16;
-		header.m_totalIndexCount = totalIndexCount;
-		header.m_totalVertexCount = totalVertexCount;
-		header.m_subMeshCount = U32(submeshes.getSize());
-		header.m_aabbMin = aabbMin;
-		header.m_aabbMax = aabbMax;
+
+		maxLod = lod;
 	}
 
-	// Open file
+	// Start writing the file
 	File file;
 	ANKI_CHECK(file.open(fname.toCString(), FileOpenFlag::kWrite | FileOpenFlag::kBinary));
 
-	// Write header
-	ANKI_CHECK(file.write(&header, sizeof(header)));
+	// Populate the header
+	MeshBinaryHeader header;
+	memset(&header, 0, sizeof(header));
+	memcpy(&header.m_magic[0], kMeshMagic, 8);
 
-	// Write sub meshes
-	for(const SubMesh& in : submeshes)
+	header.m_flags = MeshBinaryFlag::kNone;
+	if(isConvex(submeshes[0]))
 	{
-		MeshBinarySubMesh out;
-		out.m_firstIndex = in.m_firstIdx;
-		out.m_indexCount = in.m_idxCount;
-		out.m_aabbMin = in.m_aabbMin;
-		out.m_aabbMax = in.m_aabbMax;
-
-		ANKI_CHECK(file.write(&out, sizeof(out)));
+		header.m_flags |= MeshBinaryFlag::kConvex;
+	}
+	header.m_indexType = IndexType::kU16;
+	header.m_subMeshCount = submeshes.getSize();
+	header.m_aabbMin = aabbMin;
+	header.m_aabbMax = aabbMax;
+
+	writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kPosition, header);
+	writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kNormal, header);
+	writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kTangent, header);
+	writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kUv, header);
+	if(hasBoneWeights)
+	{
+		writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kBoneIds, header);
+		writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kBoneWeights, header);
 	}
 
-	// Write indices
-	U32 vertCount = 0;
-	for(const SubMesh& submesh : submeshes)
+	// Write sub meshes
+	DynamicArrayRaii<MeshBinarySubMesh> outSubmeshes(m_pool, U32(submeshes[0].getSize()));
+
+	for(U32 submeshIdx = 0; submeshIdx < outSubmeshes.getSize(); ++submeshIdx)
 	{
-		DynamicArrayRaii<U16> indices(m_pool);
-		indices.create(submesh.m_indices.getSize());
-		for(U32 i = 0; i < indices.getSize(); ++i)
+		MeshBinarySubMesh& out = outSubmeshes[submeshIdx];
+		memset(&out, 0, sizeof(out));
+
+		for(U32 lod = 0; lod <= maxLod; ++lod)
 		{
-			const U32 idx = submesh.m_indices[i] + vertCount;
-			if(idx > kMaxU16)
+			const SubMesh& inSubmesh = *(submeshes[lod].getBegin() + submeshIdx);
+
+			if(lod == 0)
 			{
-				ANKI_IMPORTER_LOGE("Only supports 16bit indices for now (%u): %s", idx, fname.cstr());
-				return Error::kUserData;
+				out.m_aabbMin = inSubmesh.m_aabbMin;
+				out.m_aabbMax = inSubmesh.m_aabbMax;
 			}
 
-			indices[i] = U16(idx);
-		}
+			out.m_firstIndices[lod] = header.m_totalIndexCounts[lod];
+			out.m_indexCounts[lod] = inSubmesh.m_indices.getSize();
 
-		ANKI_CHECK(file.write(&indices[0], indices.getSizeInBytes()));
-		vertCount += submesh.m_verts.getSize();
+			header.m_totalIndexCounts[lod] += inSubmesh.m_indices.getSize();
+			header.m_totalVertexCounts[lod] += inSubmesh.m_verts.getSize();
+		}
 	}
 
-	ANKI_CHECK(alignBufferInFile(header.m_totalIndexCount * sizeof(U16), file));
+	ANKI_CHECK(file.write(&header, sizeof(header)));
+	ANKI_CHECK(file.write(&outSubmeshes[0], outSubmeshes.getSizeInBytes()));
 
-	// Write position vert buffer
-	for(const SubMesh& submesh : submeshes)
+	// Write LODs
+	for(I32 lod = I32(maxLod); lod >= 0; --lod)
 	{
-		DynamicArrayRaii<Vec3> positions(m_pool);
-		positions.create(submesh.m_verts.getSize());
-		for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
+		// Write index buffer
+		U32 vertCount = 0;
+		for(const SubMesh& submesh : submeshes[lod])
 		{
-			positions[v] = submesh.m_verts[v].m_position;
-		}
-		ANKI_CHECK(file.write(&positions[0], positions.getSizeInBytes()));
-	}
+			DynamicArrayRaii<U16> indices(m_pool, submesh.m_indices.getSize());
+			for(U32 i = 0; i < indices.getSize(); ++i)
+			{
+				const U32 idx = submesh.m_indices[i] + vertCount;
+				if(idx > kMaxU16)
+				{
+					ANKI_IMPORTER_LOGE("Only supports 16bit indices for now (%u): %s", idx, fname.cstr());
+					return Error::kUserData;
+				}
 
-	ANKI_CHECK(alignBufferInFile(header.m_totalVertexCount * sizeof(Vec3), file));
+				indices[i] = U16(idx);
+			}
 
-	// Write the 2nd vert buffer
-	for(const SubMesh& submesh : submeshes)
-	{
-		DynamicArrayRaii<MainVertex> verts(m_pool);
-		verts.create(submesh.m_verts.getSize());
+			ANKI_CHECK(file.write(&indices[0], indices.getSizeInBytes()));
+			vertCount += submesh.m_verts.getSize();
+		}
 
-		for(U32 i = 0; i < verts.getSize(); ++i)
+		// Write positions
+		for(const SubMesh& submesh : submeshes[lod])
 		{
-			const Vec3& normal = submesh.m_verts[i].m_normal;
-			const Vec4& tangent = submesh.m_verts[i].m_tangent;
-			const Vec2& uv = submesh.m_verts[i].m_uv;
+			DynamicArrayRaii<Vec3> positions(m_pool, submesh.m_verts.getSize());
+			for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
+			{
+				positions[v] = submesh.m_verts[v].m_position;
+			}
 
-			verts[i].m_normal = packColorToR10G10B10A2SNorm(normal.x(), normal.y(), normal.z(), 0.0f);
-			verts[i].m_tangent = packColorToR10G10B10A2SNorm(tangent.x(), tangent.y(), tangent.z(), tangent.w());
-			verts[i].m_uv0 = uv;
+			ANKI_CHECK(file.write(&positions[0], positions.getSizeInBytes()));
 		}
 
-		ANKI_CHECK(file.write(&verts[0], verts.getSizeInBytes()));
-	}
+		// Write normals
+		for(const SubMesh& submesh : submeshes[lod])
+		{
+			DynamicArrayRaii<U32> normals(m_pool, submesh.m_verts.getSize());
+			for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
+			{
+				normals[v] = packSnorm4x8(submesh.m_verts[v].m_normal.xyz0());
+			}
 
-	ANKI_CHECK(alignBufferInFile(header.m_totalVertexCount * sizeof(MainVertex), file));
+			ANKI_CHECK(file.write(&normals[0], normals.getSizeInBytes()));
+		}
 
-	// Write 3rd vert buffer
-	if(hasBoneWeights)
-	{
-		for(const SubMesh& submesh : submeshes)
+		// Write tangent
+		for(const SubMesh& submesh : submeshes[lod])
 		{
-			DynamicArrayRaii<BoneInfoVertex> verts(m_pool);
-			verts.create(submesh.m_verts.getSize());
+			DynamicArrayRaii<U32> tangents(m_pool, submesh.m_verts.getSize());
+			for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
+			{
+				tangents[v] = packSnorm4x8(submesh.m_verts[v].m_tangent);
+			}
+
+			ANKI_CHECK(file.write(&tangents[0], tangents.getSizeInBytes()));
+		}
 
-			for(U32 i = 0; i < verts.getSize(); ++i)
+		// Write UV
+		for(const SubMesh& submesh : submeshes[lod])
+		{
+			DynamicArrayRaii<Vec2> uvs(m_pool, submesh.m_verts.getSize());
+			for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
 			{
-				BoneInfoVertex vert;
+				uvs[v] = submesh.m_verts[v].m_uv;
+			}
 
-				for(U32 c = 0; c < 4; ++c)
-				{
-					if(submesh.m_verts[i].m_boneIds[c] > 0XFF)
-					{
-						ANKI_IMPORTER_LOGE("Only 256 bones are supported");
-						return Error::kUserData;
-					}
+			ANKI_CHECK(file.write(&uvs[0], uvs.getSizeInBytes()));
+		}
 
-					vert.m_boneIndices[c] = U8(submesh.m_verts[i].m_boneIds[c]);
-					vert.m_boneWeights[c] = U8(submesh.m_verts[i].m_boneWeights[c] * F32(kMaxU8));
+		if(hasBoneWeights)
+		{
+			// Bone IDs
+			for(const SubMesh& submesh : submeshes[lod])
+			{
+				DynamicArrayRaii<UVec4> boneids(m_pool, submesh.m_verts.getSize());
+				for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
+				{
+					boneids[v] = UVec4(submesh.m_verts[v].m_boneIds);
 				}
 
-				verts[i] = vert;
+				ANKI_CHECK(file.write(&boneids[0], boneids.getSizeInBytes()));
 			}
 
-			ANKI_CHECK(file.write(&verts[0], verts.getSizeInBytes()));
-		}
+			// Bone weights
+			for(const SubMesh& submesh : submeshes[lod])
+			{
+				DynamicArrayRaii<U32> boneWeights(m_pool, submesh.m_verts.getSize());
+				for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
+				{
+					boneWeights[v] = packSnorm4x8(submesh.m_verts[v].m_boneWeights);
+				}
 
-		ANKI_CHECK(alignBufferInFile(header.m_totalVertexCount * sizeof(BoneInfoVertex), file));
+				ANKI_CHECK(file.write(&boneWeights[0], boneWeights.getSizeInBytes()));
+			}
+		}
 	}
 
 	return Error::kNone;

+ 19 - 0
AnKi/Math/Functions.h

@@ -242,6 +242,25 @@ inline U32 packColorToR10G10B10A2SNorm(F32 r, F32 g, F32 b, F32 a)
 	return out.m_packed;
 }
 
+template<typename TVec4>
+inline U32 packSnorm4x8(const TVec4& v)
+{
+	union
+	{
+		I8 in[4];
+		U32 out;
+	} u;
+
+	const TVec4 result = (v.clamp(-1.0f, 1.0f) * 127.0f).round();
+
+	u.in[0] = I8(result[0]);
+	u.in[1] = I8(result[1]);
+	u.in[2] = I8(result[2]);
+	u.in[3] = I8(result[3]);
+
+	return u.out;
+}
+
 /// Compute the abs triangle area.
 template<typename TVec>
 inline F32 computeTriangleArea(const TVec& a, const TVec& b, const TVec& c)

+ 92 - 90
AnKi/Math/Mat.h

@@ -15,33 +15,35 @@ namespace anki {
 
 /// Matrix type.
 /// @tparam T The scalar type. Eg float.
-/// @tparam J The number of rows.
-/// @tparam I The number of columns.
-template<typename T, U J, U I>
-class alignas(MathSimd<T, I>::kAlignment) TMat
+/// @tparam kTRowCount The number of rows.
+/// @tparam kTColumnCount The number of columns.
+template<typename T, U kTRowCount, U kTColumnCount>
+class alignas(MathSimd<T, kTColumnCount>::kAlignment) TMat
 {
 public:
 	using Scalar = T;
-	using Simd = typename MathSimd<T, I>::Type;
+	using Simd = typename MathSimd<T, kTColumnCount>::Type;
 
 #if ANKI_COMPILER_GCC_COMPATIBLE
 #	pragma GCC diagnostic push
 #	pragma GCC diagnostic ignored "-Wignored-attributes"
 #endif
-	using SimdArray = Array<Simd, J>;
+	using SimdArray = Array<Simd, kTRowCount>;
 #if ANKI_COMPILER_GCC_COMPATIBLE
 #	pragma GCC diagnostic pop
 #endif
 
-	using RowVec = TVec<T, I>;
-	using ColumnVec = TVec<T, J>;
+	using RowVec = TVec<T, kTColumnCount>;
+	using ColumnVec = TVec<T, kTRowCount>;
 
-	static constexpr U kRowCount = J; ///< Number of rows
-	static constexpr U kColumnCount = I; ///< Number of columns
-	static constexpr U kSize = J * I; ///< Number of total elements
-	static constexpr Bool kHasSIMD = I == 4 && std::is_same<T, F32>::value && ANKI_ENABLE_SIMD;
-	static constexpr Bool kHasMat4SIMD = J == 4 && I == 4 && std::is_same<T, F32>::value && ANKI_ENABLE_SIMD;
-	static constexpr Bool kHasMat3x4SIMD = J == 3 && I == 4 && std::is_same<T, F32>::value && ANKI_ENABLE_SIMD;
+	static constexpr U kRowCount = kTRowCount; ///< Number of rows
+	static constexpr U kColumnCount = kTColumnCount; ///< Number of columns
+	static constexpr U kSize = kTRowCount * kTColumnCount; ///< Number of total elements
+	static constexpr Bool kHasSIMD = kTColumnCount == 4 && std::is_same<T, F32>::value && ANKI_ENABLE_SIMD;
+	static constexpr Bool kHasMat4SIMD =
+		kTRowCount == 4 && kTColumnCount == 4 && std::is_same<T, F32>::value && ANKI_ENABLE_SIMD;
+	static constexpr Bool kHasMat3x4SIMD =
+		kTRowCount == 3 && kTColumnCount == 4 && std::is_same<T, F32>::value && ANKI_ENABLE_SIMD;
 
 	/// @name Constructors
 	/// @{
@@ -76,7 +78,7 @@ public:
 
 	// 3x3 specific constructors
 
-	ANKI_ENABLE_METHOD(J == 3 && I == 3)
+	ANKI_ENABLE_METHOD(kTRowCount == 3 && kTColumnCount == 3)
 	constexpr TMat(T m00, T m01, T m02, T m10, T m11, T m12, T m20, T m21, T m22)
 	{
 		auto& m = *this;
@@ -91,19 +93,19 @@ public:
 		m(2, 2) = m22;
 	}
 
-	ANKI_ENABLE_METHOD(J == 3 && I == 3)
+	ANKI_ENABLE_METHOD(kTRowCount == 3 && kTColumnCount == 3)
 	explicit constexpr TMat(const TQuat<T>& q)
 	{
 		setRotationPart(q);
 	}
 
-	ANKI_ENABLE_METHOD(J == 3 && I == 3)
+	ANKI_ENABLE_METHOD(kTRowCount == 3 && kTColumnCount == 3)
 	explicit constexpr TMat(const TEuler<T>& e)
 	{
 		setRotationPart(e);
 	}
 
-	ANKI_ENABLE_METHOD(J == 3 && I == 3)
+	ANKI_ENABLE_METHOD(kTRowCount == 3 && kTColumnCount == 3)
 	explicit constexpr TMat(const TAxisang<T>& axisang)
 	{
 		setRotationPart(axisang);
@@ -111,7 +113,7 @@ public:
 
 	// 4x4 specific constructors
 
-	ANKI_ENABLE_METHOD(J == 4 && I == 4)
+	ANKI_ENABLE_METHOD(kTRowCount == 4 && kTColumnCount == 4)
 	constexpr TMat(T m00, T m01, T m02, T m03, T m10, T m11, T m12, T m13, T m20, T m21, T m22, T m23, T m30, T m31,
 				   T m32, T m33)
 	{
@@ -134,7 +136,7 @@ public:
 		m(3, 3) = m33;
 	}
 
-	ANKI_ENABLE_METHOD(J == 4 && I == 4)
+	ANKI_ENABLE_METHOD(kTRowCount == 4 && kTColumnCount == 4)
 	constexpr TMat(const TVec<T, 4>& translation, const TMat<T, 3, 3>& rotation, const T scale = T(1))
 	{
 		if(isZero<T>(scale - T(1)))
@@ -152,14 +154,14 @@ public:
 		m(3, 0) = m(3, 1) = m(3, 2) = T(0);
 	}
 
-	ANKI_ENABLE_METHOD(J == 4 && I == 4)
+	ANKI_ENABLE_METHOD(kTRowCount == 4 && kTColumnCount == 4)
 	explicit constexpr TMat(const TTransform<T>& t)
 		: TMat(t.getOrigin().xyz1(), t.getRotation().getRotationPart(), t.getScale())
 	{
 	}
 
 	/// Set a 4x4 matrix using a 3x4 for the first 3 rows and a vec4 for the 4rth row.
-	ANKI_ENABLE_METHOD(J == 4 && I == 4)
+	ANKI_ENABLE_METHOD(kTRowCount == 4 && kTColumnCount == 4)
 	explicit constexpr TMat(const TMat<T, 3, 4>& m3, const TVec<T, 4>& row3)
 	{
 		setRow(0, m3.getRow(0));
@@ -170,7 +172,7 @@ public:
 
 	// 3x4 specific constructors
 
-	ANKI_ENABLE_METHOD(J == 3 && I == 4)
+	ANKI_ENABLE_METHOD(kTRowCount == 3 && kTColumnCount == 4)
 	constexpr TMat(T m00, T m01, T m02, T m03, T m10, T m11, T m12, T m13, T m20, T m21, T m22, T m23)
 	{
 		auto& m = *this;
@@ -188,7 +190,7 @@ public:
 		m(2, 3) = m23;
 	}
 
-	ANKI_ENABLE_METHOD(J == 3 && I == 4)
+	ANKI_ENABLE_METHOD(kTRowCount == 3 && kTColumnCount == 4)
 	explicit constexpr TMat(const TMat<T, 4, 4>& m4)
 	{
 		auto& m = *this;
@@ -206,7 +208,7 @@ public:
 		m(2, 3) = m4(2, 3);
 	}
 
-	ANKI_ENABLE_METHOD(J == 3 && I == 4)
+	ANKI_ENABLE_METHOD(kTRowCount == 3 && kTColumnCount == 4)
 	explicit constexpr TMat(const TVec<T, 3>& translation, const TMat<T, 3, 3>& rotation, const T scale = T(1))
 	{
 		if(isZero<T>(scale - T(1)))
@@ -221,25 +223,25 @@ public:
 		setTranslationPart(translation);
 	}
 
-	ANKI_ENABLE_METHOD(J == 3 && I == 4)
+	ANKI_ENABLE_METHOD(kTRowCount == 3 && kTColumnCount == 4)
 	explicit constexpr TMat(const TVec<T, 3>& translation, const TQuat<T>& q, const T scale = T(1))
 		: TMat(translation, TMat<T, 3, 3>(q), scale)
 	{
 	}
 
-	ANKI_ENABLE_METHOD(J == 3 && I == 4)
+	ANKI_ENABLE_METHOD(kTRowCount == 3 && kTColumnCount == 4)
 	explicit constexpr TMat(const TVec<T, 3>& translation, const TEuler<T>& b, const T scale = T(1))
 		: TMat(translation, TMat<T, 3, 3>(b), scale)
 	{
 	}
 
-	ANKI_ENABLE_METHOD(J == 3 && I == 4)
+	ANKI_ENABLE_METHOD(kTRowCount == 3 && kTColumnCount == 4)
 	explicit constexpr TMat(const TVec<T, 3>& translation, const TAxisang<T>& b, const T scale = T(1))
 		: TMat(translation, TMat<T, 3, 3>(b), scale)
 	{
 	}
 
-	ANKI_ENABLE_METHOD(J == 3 && I == 4)
+	ANKI_ENABLE_METHOD(kTRowCount == 3 && kTColumnCount == 4)
 	explicit constexpr TMat(const TTransform<T>& t)
 		: TMat(t.getOrigin().xyz(), t.getRotation().getRotationPart(), t.getScale())
 	{
@@ -320,17 +322,17 @@ public:
 		return *this;
 	}
 
-	ANKI_ENABLE_METHOD(J == I && !kHasMat4SIMD)
+	ANKI_ENABLE_METHOD(kTRowCount == kTColumnCount && !kHasMat4SIMD)
 	TMat operator*(const TMat& b) const
 	{
 		TMat out;
 		const TMat& a = *this;
-		for(U j = 0; j < J; j++)
+		for(U j = 0; j < kTRowCount; j++)
 		{
-			for(U i = 0; i < I; i++)
+			for(U i = 0; i < kTColumnCount; i++)
 			{
 				out(j, i) = T(0);
-				for(U k = 0; k < I; k++)
+				for(U k = 0; k < kTColumnCount; k++)
 				{
 					out(j, i) += a(j, k) * b(k, i);
 				}
@@ -499,10 +501,10 @@ public:
 	{
 		const TMat& m = *this;
 		ColumnVec out;
-		for(U j = 0; j < J; j++)
+		for(U j = 0; j < kTRowCount; j++)
 		{
 			T sum = T(0);
-			for(U i = 0; i < I; i++)
+			for(U i = 0; i < kTColumnCount; i++)
 			{
 				sum += m(j, i) * v[i];
 			}
@@ -517,12 +519,12 @@ public:
 	{
 		ColumnVec out;
 #	if ANKI_SIMD_SSE
-		for(U i = 0; i < J; i++)
+		for(U i = 0; i < kTRowCount; i++)
 		{
 			_mm_store_ss(&out[i], _mm_dp_ps(m_simd[i], v.getSimd(), 0xF1));
 		}
 #	else
-		for(U i = 0; i < J; i++)
+		for(U i = 0; i < kTRowCount; i++)
 		{
 			out[i] = RowVec(m_simd[i]).dot(v);
 		}
@@ -546,7 +548,7 @@ public:
 		setRow(2, c);
 	}
 
-	ANKI_ENABLE_METHOD(J > 3)
+	ANKI_ENABLE_METHOD(kTRowCount > 3)
 	void setRows(const RowVec& a, const RowVec& b, const RowVec& c, const RowVec& d)
 	{
 		setRows(a, b, c);
@@ -565,7 +567,7 @@ public:
 		c = getRow(2);
 	}
 
-	ANKI_ENABLE_METHOD(J > 3)
+	ANKI_ENABLE_METHOD(kTRowCount > 3)
 	void getRows(RowVec& a, RowVec& b, RowVec& c, RowVec& d) const
 	{
 		getRows(a, b, c);
@@ -574,7 +576,7 @@ public:
 
 	void setColumn(const U i, const ColumnVec& v)
 	{
-		for(U j = 0; j < J; j++)
+		for(U j = 0; j < kTRowCount; j++)
 		{
 			m_arr2[j][i] = v[j];
 		}
@@ -587,7 +589,7 @@ public:
 		setColumn(2, c);
 	}
 
-	ANKI_ENABLE_METHOD(I > 3)
+	ANKI_ENABLE_METHOD(kTColumnCount > 3)
 	void setColumns(const ColumnVec& a, const ColumnVec& b, const ColumnVec& c, const ColumnVec& d)
 	{
 		setColumns(a, b, c);
@@ -597,7 +599,7 @@ public:
 	ColumnVec getColumn(const U i) const
 	{
 		ColumnVec out;
-		for(U j = 0; j < J; j++)
+		for(U j = 0; j < kTRowCount; j++)
 		{
 			out[j] = m_arr2[j][i];
 		}
@@ -611,7 +613,7 @@ public:
 		c = getColumn(2);
 	}
 
-	ANKI_ENABLE_METHOD(I > 3)
+	ANKI_ENABLE_METHOD(kTColumnCount > 3)
 	void getColumns(ColumnVec& a, ColumnVec& b, ColumnVec& c, ColumnVec& d) const
 	{
 		getColumns(a, b, c);
@@ -927,12 +929,12 @@ public:
 		setColumns(xAxis, yAxis, zAxis);
 	}
 
-	ANKI_ENABLE_METHOD(J == I && !kHasSIMD)
+	ANKI_ENABLE_METHOD(kTRowCount == kTColumnCount && !kHasSIMD)
 	void transpose()
 	{
-		for(U j = 0; j < J; j++)
+		for(U j = 0; j < kTRowCount; j++)
 		{
-			for(U i = j + 1; i < I; i++)
+			for(U i = j + 1; i < kTColumnCount; i++)
 			{
 				T tmp = m_arr2[j][i];
 				m_arr2[j][i] = m_arr2[i][j];
@@ -942,7 +944,7 @@ public:
 	}
 
 #if ANKI_ENABLE_SIMD
-	ANKI_ENABLE_METHOD(J == I && kHasSIMD)
+	ANKI_ENABLE_METHOD(kTRowCount == kTColumnCount && kHasSIMD)
 	void transpose()
 	{
 #	if ANKI_SIMD_SSE
@@ -971,13 +973,13 @@ public:
 		}
 	}
 
-	ANKI_ENABLE_METHOD(I == J)
+	ANKI_ENABLE_METHOD(kTColumnCount == kTRowCount)
 	TMat getTransposed() const
 	{
 		TMat out;
-		for(U j = 0; j < J; j++)
+		for(U j = 0; j < kTRowCount; j++)
 		{
-			for(U i = 0; i < I; i++)
+			for(U i = 0; i < kTColumnCount; i++)
 			{
 				out.m_arr2[i][j] = m_arr2[j][i];
 			}
@@ -985,7 +987,7 @@ public:
 		return out;
 	}
 
-	ANKI_ENABLE_METHOD(I == 3 && J == 3)
+	ANKI_ENABLE_METHOD(kTColumnCount == 3 && kTRowCount == 3)
 	T getDet() const
 	{
 		const auto& m = *this;
@@ -994,7 +996,7 @@ public:
 			   + m(0, 2) * (m(0, 1) * m(2, 1) - m(1, 1) * m(2, 0));
 	}
 
-	ANKI_ENABLE_METHOD(I == 4 && J == 4)
+	ANKI_ENABLE_METHOD(kTColumnCount == 4 && kTRowCount == 4)
 	T getDet() const
 	{
 		const auto& t = *this;
@@ -1012,7 +1014,7 @@ public:
 			   - t(0, 1) * t(1, 0) * t(2, 2) * t(3, 3) + t(0, 0) * t(1, 1) * t(2, 2) * t(3, 3);
 	}
 
-	ANKI_ENABLE_METHOD(I == 3 && J == 3)
+	ANKI_ENABLE_METHOD(kTColumnCount == 3 && kTRowCount == 3)
 	TMat getInverse() const
 	{
 		// Using Gramer's method Inv(A) = (1 / getDet(A)) * Adj(A)
@@ -1045,7 +1047,7 @@ public:
 	}
 
 	/// Invert using Cramer's rule
-	ANKI_ENABLE_METHOD(I == 4 && J == 4)
+	ANKI_ENABLE_METHOD(kTColumnCount == 4 && kTRowCount == 4)
 	TMat getInverse() const
 	{
 		Array<T, 12> tmp;
@@ -1121,14 +1123,14 @@ public:
 	}
 
 	/// See getInverse
-	ANKI_ENABLE_METHOD((I == 4 && J == 4) || (I == 3 && J == 3))
+	ANKI_ENABLE_METHOD((kTColumnCount == 4 && kTRowCount == 4) || (kTColumnCount == 3 && kTRowCount == 3))
 	void invert()
 	{
 		(*this) = getInverse();
 	}
 
 	/// 12 muls, 27 adds. Something like m4 = m0 * m1 but without touching the 4rth row and allot faster
-	ANKI_ENABLE_METHOD(I == 4 && J == 4)
+	ANKI_ENABLE_METHOD(kTColumnCount == 4 && kTRowCount == 4)
 	static TMat combineTransformations(const TMat& m0, const TMat& m1)
 	{
 		// See the clean code in < r664
@@ -1162,7 +1164,7 @@ public:
 	}
 
 	/// Create a new matrix that is equivalent to Mat4(this)*Mat4(b)
-	ANKI_ENABLE_METHOD(J == 3 && I == 4 && !kHasSIMD)
+	ANKI_ENABLE_METHOD(kTRowCount == 3 && kTColumnCount == 4 && !kHasSIMD)
 	TMat combineTransformations(const TMat& b) const
 	{
 		const auto& a = *this;
@@ -1188,7 +1190,7 @@ public:
 	}
 
 #if ANKI_ENABLE_SIMD
-	ANKI_ENABLE_METHOD(J == 3 && I == 4 && kHasSIMD)
+	ANKI_ENABLE_METHOD(kTRowCount == 3 && kTColumnCount == 4 && kHasSIMD)
 	TMat combineTransformations(const TMat& b) const
 	{
 		TMat c;
@@ -1234,7 +1236,7 @@ public:
 #endif
 
 	/// Calculate a perspective projection matrix. The z is mapped in [0, 1] range just like DX and Vulkan.
-	ANKI_ENABLE_METHOD(I == 4 && J == 4)
+	ANKI_ENABLE_METHOD(kTColumnCount == 4 && kTRowCount == 4)
 	[[nodiscard]] static TMat calculatePerspectiveProjectionMatrix(T fovX, T fovY, T near, T far)
 	{
 		ANKI_ASSERT(fovX > T(0) && fovY > T(0) && near > T(0) && far > T(0));
@@ -1263,7 +1265,7 @@ public:
 	}
 
 	/// Calculate an orthographic projection matrix. The z is mapped in [0, 1] range just like DX and Vulkan.
-	ANKI_ENABLE_METHOD(I == 4 && J == 4)
+	ANKI_ENABLE_METHOD(kTColumnCount == 4 && kTRowCount == 4)
 	[[nodiscard]] static TMat calculateOrthographicProjectionMatrix(T right, T left, T top, T bottom, T near, T far)
 	{
 		ANKI_ASSERT(right != T(0) && left != T(0) && top != T(0) && bottom != T(0) && near != T(0) && far != T(0));
@@ -1303,7 +1305,7 @@ public:
 	/// Vec2 xy = ndc.xy() * unprojParams.xy() * z;
 	/// Vec3 posViewSpace(xy, z);
 	/// @endcode
-	ANKI_ENABLE_METHOD(I == 4 && J == 4)
+	ANKI_ENABLE_METHOD(kTColumnCount == 4 && kTRowCount == 4)
 	static TVec<T, 4> calculatePerspectiveUnprojectionParams(T fovX, T fovY, T near, T far)
 	{
 		TVec<T, 4> out;
@@ -1336,7 +1338,7 @@ public:
 
 	/// Assuming this is a projection matrix extract the unprojection parameters. See
 	/// calculatePerspectiveUnprojectionParams for more info.
-	ANKI_ENABLE_METHOD(I == 4 && J == 4)
+	ANKI_ENABLE_METHOD(kTColumnCount == 4 && kTRowCount == 4)
 	TVec<T, 4> extractPerspectiveUnprojectionParams() const
 	{
 		TVec<T, 4> out;
@@ -1349,7 +1351,7 @@ public:
 	}
 
 	/// If we suppose this matrix represents a transformation, return the inverted transformation
-	ANKI_ENABLE_METHOD(I == 4 && J == 4)
+	ANKI_ENABLE_METHOD(kTColumnCount == 4 && kTRowCount == 4)
 	TMat getInverseTransformation() const
 	{
 		const TMat<T, 3, 3> invertedRot = getRotationPart().getTransposed();
@@ -1359,7 +1361,7 @@ public:
 	}
 
 	/// @note 9 muls, 9 adds
-	ANKI_ENABLE_METHOD(I == 4 && J == 4)
+	ANKI_ENABLE_METHOD(kTColumnCount == 4 && kTRowCount == 4)
 	TVec<T, 3> transform(const TVec<T, 3>& v) const
 	{
 		const auto& m = *this;
@@ -1369,7 +1371,7 @@ public:
 	}
 
 	/// Create a new transform matrix position at eye and looking at refPoint.
-	template<U VEC_DIMS, ANKI_ENABLE(J == 3 && I == 4 && VEC_DIMS >= 3)>
+	template<U VEC_DIMS, ANKI_ENABLE(kTRowCount == 3 && kTColumnCount == 4 && VEC_DIMS >= 3)>
 	static TMat lookAt(const TVec<T, VEC_DIMS>& eye, const TVec<T, VEC_DIMS>& refPoint, const TVec<T, VEC_DIMS>& up)
 	{
 		const TVec<T, 3> vdir = (refPoint.xyz() - eye.xyz()).getNormalized();
@@ -1381,7 +1383,7 @@ public:
 	}
 
 	/// Create a new transform matrix position at eye and looking at refPoint.
-	template<U VEC_DIMS, ANKI_ENABLE(J == 4 && I == 4 && VEC_DIMS >= 3)>
+	template<U VEC_DIMS, ANKI_ENABLE(kTRowCount == 4 && kTColumnCount == 4 && VEC_DIMS >= 3)>
 	static TMat lookAt(const TVec<T, VEC_DIMS>& eye, const TVec<T, VEC_DIMS>& refPoint, const TVec<T, VEC_DIMS>& up)
 	{
 		const TVec<T, 4> vdir = (refPoint.xyz0() - eye.xyz0()).getNormalized();
@@ -1407,19 +1409,19 @@ public:
 		*this = getZero();
 	}
 
-	ANKI_ENABLE_METHOD(I == 3 && J == 3)
+	ANKI_ENABLE_METHOD(kTColumnCount == 3 && kTRowCount == 3)
 	static TMat getIdentity()
 	{
 		return TMat(T(1), T(0), T(0), T(0), T(1), T(0), T(0), T(0), T(1));
 	}
 
-	ANKI_ENABLE_METHOD(I == 4 && J == 4)
+	ANKI_ENABLE_METHOD(kTColumnCount == 4 && kTRowCount == 4)
 	static TMat getIdentity()
 	{
 		return TMat(T(1), T(0), T(0), T(0), T(0), T(1), T(0), T(0), T(0), T(0), T(1), T(0), T(0), T(0), T(0), T(1));
 	}
 
-	ANKI_ENABLE_METHOD(I == 4 && J == 3)
+	ANKI_ENABLE_METHOD(kTColumnCount == 4 && kTRowCount == 3)
 	static TMat getIdentity()
 	{
 		return TMat(T(1), T(0), T(0), T(0), T(0), T(1), T(0), T(0), T(0), T(0), T(1), T(0));
@@ -1432,22 +1434,22 @@ public:
 
 	static constexpr U8 getSize()
 	{
-		return U8(I * J);
+		return U8(kTColumnCount * kTRowCount);
 	}
 
 	ANKI_ENABLE_METHOD(std::is_floating_point<T>::value)
 	void toString(StringRaii& str) const
 	{
-		for(U j = 0; j < J; ++j)
+		for(U j = 0; j < kTRowCount; ++j)
 		{
-			for(U i = 0; i < I; ++i)
+			for(U i = 0; i < kTColumnCount; ++i)
 			{
 				CString fmt;
-				if(i == I - 1 && j == J - 1)
+				if(i == kTColumnCount - 1 && j == kTRowCount - 1)
 				{
 					fmt = "%f";
 				}
-				else if(i == I - 1)
+				else if(i == kTColumnCount - 1)
 				{
 					fmt = "%f\n";
 				}
@@ -1462,35 +1464,35 @@ public:
 	/// @}
 
 protected:
-	static constexpr U N = I * J;
+	static constexpr U N = kTColumnCount * kTRowCount;
 
 	/// @name Data members
 	/// @{
 	union
 	{
 		T m_carr1[N]; ///< For easier debugging with gdb
-		T m_carr2[J][I]; ///< For easier debugging with gdb
+		T m_carr2[kTRowCount][kTColumnCount]; ///< For easier debugging with gdb
 		Array<T, N> m_arr1;
-		Array2d<T, J, I> m_arr2;
+		Array2d<T, kTRowCount, kTColumnCount> m_arr2;
 		SimdArray m_simd;
-		Array<RowVec, J> m_rows;
+		Array<RowVec, kTRowCount> m_rows;
 	};
 	/// @}
 };
 
 /// @memberof TMat
-template<typename T, U J, U I>
-TMat<T, J, I> operator+(const T f, const TMat<T, J, I>& m)
+template<typename T, U kTRowCount, U kTColumnCount>
+TMat<T, kTRowCount, kTColumnCount> operator+(const T f, const TMat<T, kTRowCount, kTColumnCount>& m)
 {
 	return m + f;
 }
 
 /// @memberof TMat
-template<typename T, U J, U I>
-TMat<T, J, I> operator-(const T f, const TMat<T, J, I>& m)
+template<typename T, U kTRowCount, U kTColumnCount>
+TMat<T, kTRowCount, kTColumnCount> operator-(const T f, const TMat<T, kTRowCount, kTColumnCount>& m)
 {
-	TMat<T, J, I> out;
-	for(U i = 0; i < J * I; i++)
+	TMat<T, kTRowCount, kTColumnCount> out;
+	for(U i = 0; i < kTRowCount * kTColumnCount; i++)
 	{
 		out[i] = f - m[i];
 	}
@@ -1498,18 +1500,18 @@ TMat<T, J, I> operator-(const T f, const TMat<T, J, I>& m)
 }
 
 /// @memberof TMat
-template<typename T, U J, U I>
-TMat<T, J, I> operator*(const T f, const TMat<T, J, I>& m)
+template<typename T, U kTRowCount, U kTColumnCount>
+TMat<T, kTRowCount, kTColumnCount> operator*(const T f, const TMat<T, kTRowCount, kTColumnCount>& m)
 {
 	return m * f;
 }
 
 /// @memberof TMat
-template<typename T, U J, U I>
-TMat<T, J, I> operator/(const T f, const TMat<T, 3, 3>& m3)
+template<typename T, U kTRowCount, U kTColumnCount>
+TMat<T, kTRowCount, kTColumnCount> operator/(const T f, const TMat<T, 3, 3>& m3)
 {
-	TMat<T, J, I> out;
-	for(U i = 0; i < J * I; i++)
+	TMat<T, kTRowCount, kTColumnCount> out;
+	for(U i = 0; i < kTRowCount * kTColumnCount; i++)
 	{
 		ANKI_ASSERT(m3[i] != T(0));
 		out[i] = f / m3[i];

File diff suppressed because it is too large
+ 366 - 366
AnKi/Math/Vec.h


+ 8 - 11
AnKi/Renderer/TraditionalDeferredShading.cpp

@@ -84,23 +84,20 @@ void TraditionalDeferredLightShading::bindVertexIndexBuffers(MeshResourcePtr& me
 															 U32& indexCount)
 {
 	// Attrib
-	U32 bufferBinding;
-	Format fmt;
-	U32 relativeOffset;
-	mesh->getVertexAttributeInfo(VertexAttributeId::kPosition, bufferBinding, fmt, relativeOffset);
+	const Format fmt = kMeshRelatedVertexStreamFormats[VertexStreamId::kPosition];
+	PtrSize offset;
+	U32 vertCount;
+	mesh->getVertexStreamInfo(0, VertexStreamId::kPosition, offset, vertCount);
 
-	cmdb->setVertexAttribute(0, 0, fmt, relativeOffset);
+	cmdb->setVertexAttribute(0, 0, fmt, 0);
 
 	// Vert buff
-	BufferPtr buff;
-	PtrSize offset, stride;
-	mesh->getVertexBufferInfo(bufferBinding, buff, offset, stride);
-
-	cmdb->bindVertexBuffer(0, buff, offset, stride);
+	BufferPtr buff = mesh->getManager().getUnifiedGeometryMemoryPool().getVertexBuffer();
+	cmdb->bindVertexBuffer(0, buff, offset, sizeof(Vec3));
 
 	// Idx buff
 	IndexType idxType;
-	mesh->getIndexBufferInfo(buff, offset, indexCount, idxType);
+	mesh->getIndexBufferInfo(0, offset, indexCount, idxType);
 
 	cmdb->bindIndexBuffer(buff, offset, idxType);
 }

+ 20 - 23
AnKi/Scene/ModelNode.cpp

@@ -262,24 +262,30 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 			ConstWeakArray<Mat3x4>(&trfs[0], instanceCount), ConstWeakArray<Mat3x4>(&prevTrfs[0], instanceCount),
 			*ctx.m_stagingGpuAllocator);
 
-		// Set attributes
-		for(U i = 0; i < modelInf.m_vertexAttributeCount; ++i)
+		// Bind attributes & vertex buffers
+		for(VertexStreamId streamId :
+			EnumIterable<VertexStreamId>(VertexStreamId::kMeshRelatedFirst, VertexStreamId::kMeshRelatedCount))
 		{
-			const ModelVertexAttribute& attrib = modelInf.m_vertexAttributes[i];
-			ANKI_ASSERT(attrib.m_format != Format::kNone);
-			cmdb->setVertexAttribute(U32(attrib.m_location), attrib.m_bufferBinding, attrib.m_format,
-									 attrib.m_relativeOffset);
-		}
+			if(modelInf.m_vertexBufferOffsets[streamId] == kMaxPtrSize)
+			{
+				continue;
+			}
 
-		// Set vertex buffers
-		for(U32 i = 0; i < modelInf.m_vertexBufferBindingCount; ++i)
-		{
-			const ModelVertexBufferBinding& binding = modelInf.m_vertexBufferBindings[i];
-			cmdb->bindVertexBuffer(i, binding.m_buffer, binding.m_offset, binding.m_stride, VertexStepRate::kVertex);
+			const U32 attribLocation = U32(streamId);
+			const U32 bufferBinding = U32(streamId);
+			const Format fmt = kMeshRelatedVertexStreamFormats[streamId];
+			const U32 relativeOffset = 0;
+			const U32 vertexStride = getFormatInfo(fmt).m_texelSize;
+
+			cmdb->setVertexAttribute(attribLocation, bufferBinding, fmt, relativeOffset);
+
+			cmdb->bindVertexBuffer(bufferBinding, getUnifiedGeometryMemoryPool().getVertexBuffer(),
+								   modelInf.m_vertexBufferOffsets[streamId], vertexStride, VertexStepRate::kVertex);
 		}
 
-		// Index buffer
-		cmdb->bindIndexBuffer(modelInf.m_indexBuffer, modelInf.m_indexBufferOffset, IndexType::kU16);
+		// Bind index buffer
+		cmdb->bindIndexBuffer(getUnifiedGeometryMemoryPool().getVertexBuffer(), modelInf.m_indexBufferOffset,
+							  IndexType::kU16);
 
 		// Draw
 		cmdb->drawElements(PrimitiveTopology::kTriangles, modelInf.m_indexCount, instanceCount, modelInf.m_firstIndex,
@@ -402,15 +408,6 @@ void ModelNode::setupRayTracingInstanceQueueElement(U32 lod, U32 modelPatchIdx,
 	el.m_transform = Mat3x4(movec.getWorldTransform());
 
 	el.m_shaderGroupHandleIndex = info.m_shaderGroupHandleIndex;
-
-	// References
-	el.m_grObjectCount = info.m_grObjectReferences.getSize();
-	for(U32 i = 0; i < el.m_grObjectCount; ++i)
-	{
-		// const_cast hack follows. To avoid the const you could copy m_grObjectReferences[i] to a GrObjectPtr and then
-		// call get() on that. But that will cost 2 atomic operations
-		el.m_grObjects[i] = const_cast<GrObject*>(info.m_grObjectReferences[i].get());
-	}
 }
 
 } // end namespace anki

+ 3 - 1
AnKi/Scene/SceneGraph.cpp

@@ -54,7 +54,8 @@ SceneGraph::~SceneGraph()
 
 Error SceneGraph::init(AllocAlignedCallback allocCb, void* allocCbData, ThreadHive* threadHive,
 					   ResourceManager* resources, Input* input, ScriptManager* scriptManager, UiManager* uiManager,
-					   ConfigSet* config, const Timestamp* globalTimestamp)
+					   ConfigSet* config, const Timestamp* globalTimestamp,
+					   UnifiedGeometryMemoryPool* unifiedGeometryMemPool)
 {
 	m_globalTimestamp = globalTimestamp;
 	m_threadHive = threadHive;
@@ -65,6 +66,7 @@ Error SceneGraph::init(AllocAlignedCallback allocCb, void* allocCbData, ThreadHi
 	m_scriptManager = scriptManager;
 	m_uiManager = uiManager;
 	m_config = config;
+	m_unifiedGeometryMemPool = unifiedGeometryMemPool;
 
 	m_pool.init(allocCb, allocCbData);
 	m_framePool.init(allocCb, allocCbData, 1 * 1024 * 1024);

+ 3 - 1
AnKi/Scene/SceneGraph.h

@@ -24,6 +24,7 @@ class ConfigSet;
 class PerspectiveCameraNode;
 class Octree;
 class UiManager;
+class UnifiedGeometryMemoryPool;
 
 /// @addtogroup scene
 /// @{
@@ -50,7 +51,7 @@ public:
 
 	Error init(AllocAlignedCallback allocCb, void* allocCbData, ThreadHive* threadHive, ResourceManager* resources,
 			   Input* input, ScriptManager* scriptManager, UiManager* uiManager, ConfigSet* config,
-			   const Timestamp* globalTimestamp);
+			   const Timestamp* globalTimestamp, UnifiedGeometryMemoryPool* unifiedGeometryMemPool);
 
 	Timestamp getGlobalTimestamp() const
 	{
@@ -236,6 +237,7 @@ private:
 	ScriptManager* m_scriptManager = nullptr;
 	UiManager* m_uiManager = nullptr;
 	ConfigSet* m_config = nullptr;
+	UnifiedGeometryMemoryPool* m_unifiedGeometryMemPool = nullptr;
 
 	mutable HeapMemoryPool m_pool;
 	mutable StackMemoryPool m_framePool;

+ 5 - 0
AnKi/Scene/SceneNode.cpp

@@ -77,4 +77,9 @@ const ConfigSet& SceneNode::getConfig() const
 	return m_scene->getConfig();
 }
 
+const UnifiedGeometryMemoryPool& SceneNode::getUnifiedGeometryMemoryPool() const
+{
+	return *m_scene->m_unifiedGeometryMemPool;
+}
+
 } // end namespace anki

+ 3 - 0
AnKi/Scene/SceneNode.h

@@ -17,6 +17,7 @@ namespace anki {
 // Forward
 class ResourceManager;
 class ConfigSet;
+class UnifiedGeometryMemoryPool;
 
 /// @addtogroup scene
 /// @{
@@ -73,6 +74,8 @@ public:
 
 	Timestamp getGlobalTimestamp() const;
 
+	const UnifiedGeometryMemoryPool& getUnifiedGeometryMemoryPool() const;
+
 	Timestamp getComponentMaxTimestamp() const
 	{
 		return m_maxComponentTimestamp;

Some files were not shown because too many files changed in this diff