Browse Source

The new mesh format is feature complete

Panagiotis Christopoulos Charitos 7 years ago
parent
commit
2cdda78f62

+ 2 - 2
src/anki/physics/PhysicsCollisionShape.cpp

@@ -50,7 +50,7 @@ Error PhysicsBox::create(PhysicsCollisionShapeInitInfo& init, const Vec3& extend
 Error PhysicsTriangleSoup::create(PhysicsCollisionShapeInitInfo& init,
 Error PhysicsTriangleSoup::create(PhysicsCollisionShapeInitInfo& init,
 	const Vec3* positions,
 	const Vec3* positions,
 	U32 positionsStride,
 	U32 positionsStride,
-	const U16* indices,
+	const U32* indices,
 	U32 indicesCount)
 	U32 indicesCount)
 {
 {
 	m_shape = NewtonCreateTreeCollision(m_world->getNewtonWorld(), 0);
 	m_shape = NewtonCreateTreeCollision(m_world->getNewtonWorld(), 0);
@@ -63,7 +63,7 @@ Error PhysicsTriangleSoup::create(PhysicsCollisionShapeInitInfo& init,
 	NewtonTreeCollisionBeginBuild(m_shape);
 	NewtonTreeCollisionBeginBuild(m_shape);
 
 
 	// Iterate index array
 	// Iterate index array
-	const U16* indicesEnd = indices + indicesCount;
+	const U32* indicesEnd = indices + indicesCount;
 	for(; indices != indicesEnd; indices += 3)
 	for(; indices != indicesEnd; indices += 3)
 	{
 	{
 		Array<Vec3, 3> facePos;
 		Array<Vec3, 3> facePos;

+ 1 - 1
src/anki/physics/PhysicsCollisionShape.h

@@ -117,7 +117,7 @@ public:
 	ANKI_USE_RESULT Error create(PhysicsCollisionShapeInitInfo& init,
 	ANKI_USE_RESULT Error create(PhysicsCollisionShapeInitInfo& init,
 		const Vec3* positions,
 		const Vec3* positions,
 		U32 positionsStride,
 		U32 positionsStride,
-		const U16* indices,
+		const U32* indices,
 		U32 indicesCount);
 		U32 indicesCount);
 };
 };
 /// @}
 /// @}

+ 32 - 71
src/anki/renderer/Indirect.cpp

@@ -10,7 +10,7 @@
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/core/Config.h>
 #include <anki/core/Config.h>
 #include <anki/core/Trace.h>
 #include <anki/core/Trace.h>
-#include <anki/resource/MeshLoader.h>
+#include <anki/resource/MeshResource.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -88,59 +88,6 @@ Error Indirect::initInternal(const ConfigSet& config)
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
-Error Indirect::loadMesh(CString fname, BufferPtr& vert, BufferPtr& idx, U32& idxCount)
-{
-	MeshLoader loader(&getResourceManager());
-	ANKI_CHECK(loader.load(fname));
-
-	PtrSize vertBuffSize = loader.getHeader().m_totalVerticesCount * sizeof(Vec3);
-	vert = getGrManager().newBuffer(BufferInitInfo(vertBuffSize,
-		BufferUsageBit::VERTEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION,
-		BufferMapAccessBit::NONE,
-		"IndirectMesh"));
-
-	idx = getGrManager().newBuffer(BufferInitInfo(loader.getIndexDataSize(),
-		BufferUsageBit::INDEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION,
-		BufferMapAccessBit::NONE,
-		"IndirectMesh"));
-
-	// Upload data
-	CommandBufferInitInfo init;
-	init.m_flags = CommandBufferFlag::SMALL_BATCH;
-	CommandBufferPtr cmdb = getGrManager().newCommandBuffer(init);
-
-	TransferGpuAllocatorHandle handle;
-	ANKI_CHECK(m_r->getResourceManager().getTransferGpuAllocator().allocate(vertBuffSize, handle));
-
-	Vec3* verts = static_cast<Vec3*>(handle.getMappedMemory());
-
-	const U8* ptr = loader.getVertexData();
-	for(U i = 0; i < loader.getHeader().m_totalVerticesCount; ++i)
-	{
-		*verts = *reinterpret_cast<const Vec3*>(ptr);
-		++verts;
-		ptr += loader.getVertexSize();
-	}
-
-	cmdb->copyBufferToBuffer(handle.getBuffer(), handle.getOffset(), vert, 0, handle.getRange());
-
-	TransferGpuAllocatorHandle handle2;
-	ANKI_CHECK(m_r->getResourceManager().getTransferGpuAllocator().allocate(loader.getIndexDataSize(), handle2));
-	void* cpuIds = handle2.getMappedMemory();
-
-	memcpy(cpuIds, loader.getIndexData(), loader.getIndexDataSize());
-
-	cmdb->copyBufferToBuffer(handle2.getBuffer(), handle2.getOffset(), idx, 0, handle2.getRange());
-	idxCount = loader.getHeader().m_totalIndicesCount;
-
-	FencePtr fence;
-	cmdb->flush(&fence);
-	m_r->getResourceManager().getTransferGpuAllocator().release(handle, fence);
-	m_r->getResourceManager().getTransferGpuAllocator().release(handle2, fence);
-
-	return Error::NONE;
-}
-
 Error Indirect::initGBuffer(const ConfigSet& config)
 Error Indirect::initGBuffer(const ConfigSet& config)
 {
 {
 	m_gbuffer.m_tileSize = config.getNumber("r.indirect.reflectionResolution");
 	m_gbuffer.m_tileSize = config.getNumber("r.indirect.reflectionResolution");
@@ -229,16 +176,9 @@ Error Indirect::initLightShading(const ConfigSet& config)
 		m_lightShading.m_slightGrProg = variant->getProgram();
 		m_lightShading.m_slightGrProg = variant->getProgram();
 	}
 	}
 
 
-	// Init vert/idx buffers
-	ANKI_CHECK(loadMesh("engine_data/Plight.ankimesh",
-		m_lightShading.m_plightPositions,
-		m_lightShading.m_plightIndices,
-		m_lightShading.m_plightIdxCount));
-
-	ANKI_CHECK(loadMesh("engine_data/Slight.ankimesh",
-		m_lightShading.m_slightPositions,
-		m_lightShading.m_slightIndices,
-		m_lightShading.m_slightIdxCount));
+	// Init meshes
+	ANKI_CHECK(getResourceManager().loadResource("engine_data/Plight.ankimesh", m_lightShading.m_plightMesh, false));
+	ANKI_CHECK(getResourceManager().loadResource("engine_data/Slight.ankimesh", m_lightShading.m_slightMesh, false));
 
 
 	return Error::NONE;
 	return Error::NONE;
 }
 }
@@ -440,6 +380,29 @@ void Indirect::runGBuffer(CommandBufferPtr& cmdb)
 	cmdb->setScissor(0, 0, MAX_U32, MAX_U32);
 	cmdb->setScissor(0, 0, MAX_U32, MAX_U32);
 }
 }
 
 
+void Indirect::bindVertexIndexBuffers(MeshResourcePtr& mesh, CommandBufferPtr& cmdb, U32& indexCount)
+{
+	// Attrib
+	U32 bufferBinding;
+	Format fmt;
+	PtrSize relativeOffset;
+	mesh->getVertexAttributeInfo(VertexAttributeLocation::POSITION, bufferBinding, fmt, relativeOffset);
+
+	cmdb->setVertexAttribute(0, 0, fmt, relativeOffset);
+
+	// Vert buff
+	BufferPtr buff;
+	PtrSize offset, stride;
+	mesh->getVertexBufferInfo(bufferBinding, buff, offset, stride);
+
+	cmdb->bindVertexBuffer(0, buff, offset, stride);
+
+	// Idx buff
+	mesh->getIndexBufferInfo(buff, offset, indexCount, fmt);
+
+	cmdb->bindIndexBuffer(buff, offset, (fmt == Format::R32_UINT) ? IndexType::U32 : IndexType::U16);
+}
+
 void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 {
 {
 	ANKI_ASSERT(faceIdx <= 6);
 	ANKI_ASSERT(faceIdx <= 6);
@@ -461,7 +424,6 @@ void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 		m_ctx.m_gbufferDepthRt,
 		m_ctx.m_gbufferDepthRt,
 		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
 		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
 		m_r->getNearestSampler());
 		m_r->getNearestSampler());
-	cmdb->setVertexAttribute(0, 0, Format::R32G32B32_SFLOAT, 0);
 	cmdb->setViewport(0, 0, m_lightShading.m_tileSize, m_lightShading.m_tileSize);
 	cmdb->setViewport(0, 0, m_lightShading.m_tileSize, m_lightShading.m_tileSize);
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ONE);
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ONE);
 	cmdb->setCullMode(FaceSelectionBit::FRONT);
 	cmdb->setCullMode(FaceSelectionBit::FRONT);
@@ -475,9 +437,9 @@ void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 		const Mat4 invViewProjMat = rqueue.m_viewProjectionMatrix.getInverse();
 		const Mat4 invViewProjMat = rqueue.m_viewProjectionMatrix.getInverse();
 
 
 		// Do point lights
 		// Do point lights
+		U32 indexCount;
+		bindVertexIndexBuffers(m_lightShading.m_plightMesh, cmdb, indexCount);
 		cmdb->bindShaderProgram(m_lightShading.m_plightGrProg);
 		cmdb->bindShaderProgram(m_lightShading.m_plightGrProg);
-		cmdb->bindVertexBuffer(0, m_lightShading.m_plightPositions, 0, sizeof(F32) * 3);
-		cmdb->bindIndexBuffer(m_lightShading.m_plightIndices, 0, IndexType::U16);
 
 
 		const PointLightQueueElement* plightEl = rqueue.m_pointLights.getBegin();
 		const PointLightQueueElement* plightEl = rqueue.m_pointLights.getBegin();
 		while(plightEl != rqueue.m_pointLights.getEnd())
 		while(plightEl != rqueue.m_pointLights.getEnd())
@@ -502,15 +464,14 @@ void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 			light->m_specularColorPad1 = plightEl->m_specularColor.xyz0();
 			light->m_specularColorPad1 = plightEl->m_specularColor.xyz0();
 
 
 			// Draw
 			// Draw
-			cmdb->drawElements(PrimitiveTopology::TRIANGLES, m_lightShading.m_plightIdxCount);
+			cmdb->drawElements(PrimitiveTopology::TRIANGLES, indexCount);
 
 
 			++plightEl;
 			++plightEl;
 		}
 		}
 
 
 		// Do spot lights
 		// Do spot lights
+		bindVertexIndexBuffers(m_lightShading.m_slightMesh, cmdb, indexCount);
 		cmdb->bindShaderProgram(m_lightShading.m_slightGrProg);
 		cmdb->bindShaderProgram(m_lightShading.m_slightGrProg);
-		cmdb->bindVertexBuffer(0, m_lightShading.m_slightPositions, 0, sizeof(F32) * 3);
-		cmdb->bindIndexBuffer(m_lightShading.m_slightIndices, 0, IndexType::U16);
 
 
 		const SpotLightQueueElement* splightEl = rqueue.m_spotLights.getBegin();
 		const SpotLightQueueElement* splightEl = rqueue.m_spotLights.getBegin();
 		while(splightEl != rqueue.m_spotLights.getEnd())
 		while(splightEl != rqueue.m_spotLights.getEnd())
@@ -553,7 +514,7 @@ void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 			light->m_lightDirPad1 = lightDir.xyz0();
 			light->m_lightDirPad1 = lightDir.xyz0();
 
 
 			// Draw
 			// Draw
-			cmdb->drawElements(PrimitiveTopology::TRIANGLES, m_lightShading.m_slightIdxCount);
+			cmdb->drawElements(PrimitiveTopology::TRIANGLES, indexCount);
 
 
 			++splightEl;
 			++splightEl;
 		}
 		}

+ 4 - 8
src/anki/renderer/Indirect.h

@@ -81,14 +81,10 @@ private:
 		ShaderProgramPtr m_plightGrProg;
 		ShaderProgramPtr m_plightGrProg;
 		ShaderProgramPtr m_slightGrProg;
 		ShaderProgramPtr m_slightGrProg;
 
 
-		/// @name Vertex & index buffer of light volumes.
+		/// @name Meshes of light volumes.
 		/// @{
 		/// @{
-		BufferPtr m_plightPositions;
-		BufferPtr m_plightIndices;
-		U32 m_plightIdxCount;
-		BufferPtr m_slightPositions;
-		BufferPtr m_slightIndices;
-		U32 m_slightIdxCount;
+		MeshResourcePtr m_plightMesh;
+		MeshResourcePtr m_slightMesh;
 		/// @}
 		/// @}
 	} m_lightShading; ///< Light shading.
 	} m_lightShading; ///< Light shading.
 
 
@@ -136,7 +132,6 @@ private:
 	ANKI_USE_RESULT Error initGBuffer(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initGBuffer(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initLightShading(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initLightShading(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initIrradiance(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initIrradiance(const ConfigSet& cfg);
-	ANKI_USE_RESULT Error loadMesh(CString fname, BufferPtr& vert, BufferPtr& idx, U32& idxCount);
 
 
 	/// Lazily init the cache entry
 	/// Lazily init the cache entry
 	void initCacheEntry(U32 cacheEntryIdx);
 	void initCacheEntry(U32 cacheEntryIdx);
@@ -151,6 +146,7 @@ private:
 	void runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
 	void runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
 	void runMipmappingOfLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
 	void runMipmappingOfLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
 	void runIrradiance(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
 	void runIrradiance(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
+	static void bindVertexIndexBuffers(MeshResourcePtr& mesh, CommandBufferPtr& cmdb, U32& indexCount);
 
 
 	// A RenderPassWorkCallback for G-buffer pass
 	// A RenderPassWorkCallback for G-buffer pass
 	static void runGBufferCallback(RenderPassWorkContext& rgraphCtx)
 	static void runGBufferCallback(RenderPassWorkContext& rgraphCtx)

+ 7 - 6
src/anki/resource/CollisionResource.cpp

@@ -48,14 +48,15 @@ Error CollisionResource::load(const ResourceFilename& filename, Bool async)
 		CString meshfname;
 		CString meshfname;
 		ANKI_CHECK(valEl.getText(meshfname));
 		ANKI_CHECK(valEl.getText(meshfname));
 
 
-		MeshLoader loader(&getManager());
+		MeshLoader loader(&getManager(), getTempAllocator());
 		ANKI_CHECK(loader.load(meshfname));
 		ANKI_CHECK(loader.load(meshfname));
 
 
-		m_physicsShape = physics.newInstance<PhysicsTriangleSoup>(csInit,
-			reinterpret_cast<const Vec3*>(loader.getVertexData()),
-			loader.getVertexSize(),
-			reinterpret_cast<const U16*>(loader.getIndexData()),
-			loader.getHeader().m_totalIndicesCount);
+		DynamicArrayAuto<U32> indices(getTempAllocator());
+		DynamicArrayAuto<Vec3> positions(getTempAllocator());
+		ANKI_CHECK(loader.storeIndicesAndPosition(indices, positions));
+
+		m_physicsShape = physics.newInstance<PhysicsTriangleSoup>(
+			csInit, &positions[0], sizeof(Vec3), &indices[0], indices.getSize());
 	}
 	}
 	else
 	else
 	{
 	{

+ 71 - 2
src/anki/resource/MeshLoader.cpp

@@ -100,7 +100,7 @@ Error MeshLoader::load(const ResourceFilename& filename)
 		totalSize += sizeof(MeshBinaryFile::SubMesh) * m_header.m_subMeshCount;
 		totalSize += sizeof(MeshBinaryFile::SubMesh) * m_header.m_subMeshCount;
 		totalSize += getIndexBufferSize();
 		totalSize += getIndexBufferSize();
 
 
-		for(U i = 0; i < header.m_vertexBufferCount; ++i)
+		for(U i = 0; i < m_header.m_vertexBufferCount; ++i)
 		{
 		{
 			totalSize += m_header.m_vertexBuffers[i].m_vertexStride * m_header.m_totalVertexCount;
 			totalSize += m_header.m_vertexBuffers[i].m_vertexStride * m_header.m_totalVertexCount;
 		}
 		}
@@ -209,7 +209,7 @@ Error MeshLoader::checkHeader() const
 	// AABB
 	// AABB
 	for(U d = 0; d < 3; ++d)
 	for(U d = 0; d < 3; ++d)
 	{
 	{
-		if(h.m_aabbMin[i] >= h.m_aabbMax[i])
+		if(h.m_aabbMin[d] >= h.m_aabbMax[d])
 		{
 		{
 			ANKI_RESOURCE_LOGE("Wrong bounding box");
 			ANKI_RESOURCE_LOGE("Wrong bounding box");
 			return Error::USER_DATA;
 			return Error::USER_DATA;
@@ -258,4 +258,73 @@ Error MeshLoader::storeVertexBuffer(U32 bufferIdx, void* ptr, PtrSize size)
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
+Error MeshLoader::storeIndicesAndPosition(DynamicArrayAuto<U32>& indices, DynamicArrayAuto<Vec3>& positions)
+{
+	// Store indices
+	{
+		indices.resize(m_header.m_totalIndexCount);
+
+		// Create staging buff
+		const PtrSize idxBufferSize = getIndexBufferSize();
+		DynamicArrayAuto<U8> staging(m_alloc);
+		staging.create(idxBufferSize);
+
+		// Store to staging buff
+		ANKI_CHECK(storeIndexBuffer(&staging[0], staging.getSizeInBytes()));
+
+		// Copy
+		for(U i = 0; i < m_header.m_totalIndexCount; ++i)
+		{
+			if(m_header.m_indicesFormat == Format::R32_UINT)
+			{
+				indices[i] = *reinterpret_cast<U32*>(&staging[i * 4]);
+			}
+			else
+			{
+				ANKI_ASSERT(m_header.m_indicesFormat == Format::R16_UINT);
+				indices[i] = *reinterpret_cast<U16*>(&staging[i * 2]);
+			}
+		}
+	}
+
+	// Store positions
+	{
+		positions.resize(m_header.m_totalVertexCount);
+
+		const MeshBinaryFile::VertexAttribute& attrib = m_header.m_vertexAttributes[VertexAttributeLocation::POSITION];
+		const MeshBinaryFile::VertexBuffer& buffInfo = m_header.m_vertexBuffers[attrib.m_bufferBinding];
+
+		// Create staging buff
+		const PtrSize vertBuffSize = m_header.m_totalVertexCount * buffInfo.m_vertexStride;
+		DynamicArrayAuto<U8> staging(m_alloc);
+		staging.create(vertBuffSize);
+
+		// Store to staging buff
+		ANKI_CHECK(storeVertexBuffer(attrib.m_bufferBinding, &staging[0], staging.getSizeInBytes()));
+
+		// Copy
+		for(U i = 0; i < m_header.m_totalVertexCount; ++i)
+		{
+			Vec3 vert;
+			if(attrib.m_format == Format::R32G32B32_SFLOAT)
+			{
+				vert = *reinterpret_cast<Vec3*>(&staging[i * buffInfo.m_vertexStride + attrib.m_relativeOffset]);
+			}
+			else
+			{
+				ANKI_ASSERT(attrib.m_format == Format::R16G16B16_SFLOAT);
+				F16* f16 = reinterpret_cast<F16*>(&staging[i * buffInfo.m_vertexStride + attrib.m_relativeOffset]);
+
+				vert[0] = f16[0].toF32();
+				vert[1] = f16[1].toF32();
+				vert[2] = f16[2].toF32();
+			}
+
+			positions[i] = vert;
+		}
+	}
+
+	return Error::NONE;
+}
+
 } // end namespace anki
 } // end namespace anki

+ 4 - 1
src/anki/resource/MeshLoader.h

@@ -94,6 +94,9 @@ public:
 
 
 	ANKI_USE_RESULT Error storeVertexBuffer(U32 bufferIdx, void* ptr, PtrSize size);
 	ANKI_USE_RESULT Error storeVertexBuffer(U32 bufferIdx, void* ptr, PtrSize size);
 
 
+	/// Instead of calling storeIndexBuffer and storeVertexBuffer use this method to get those buffers into the CPU.
+	ANKI_USE_RESULT Error storeIndicesAndPosition(DynamicArrayAuto<U32>& indices, DynamicArrayAuto<Vec3>& positions);
+
 	const MeshBinaryFile::Header& getHeader() const
 	const MeshBinaryFile::Header& getHeader() const
 	{
 	{
 		ANKI_ASSERT(isLoaded());
 		ANKI_ASSERT(isLoaded());
@@ -128,7 +131,7 @@ private:
 		return m_file.get() != nullptr;
 		return m_file.get() != nullptr;
 	}
 	}
 
 
-	U32 getIndexBufferSize() const
+	PtrSize getIndexBufferSize() const
 	{
 	{
 		return m_header.m_totalIndexCount * ((m_header.m_indicesFormat == Format::R16_UINT) ? 2 : 4);
 		return m_header.m_totalIndexCount * ((m_header.m_indicesFormat == Format::R16_UINT) ? 2 : 4);
 	}
 	}

+ 6 - 9
src/anki/resource/MeshResource.h

@@ -41,15 +41,12 @@ public:
 	}
 	}
 
 
 	/// Get submesh info.
 	/// Get submesh info.
-	void getSubMeshInfo(U subMeshId, U32& firstIndex, U32& indexCount, Obb* obb) const
+	void getSubMeshInfo(U subMeshId, U32& firstIndex, U32& indexCount, const Obb*& obb) const
 	{
 	{
 		const SubMesh& sm = m_subMeshes[subMeshId];
 		const SubMesh& sm = m_subMeshes[subMeshId];
 		firstIndex = sm.m_firstIndex;
 		firstIndex = sm.m_firstIndex;
 		indexCount = sm.m_indexCount;
 		indexCount = sm.m_indexCount;
-		if(obb)
-		{
-			*obb = sm.m_obb;
-		}
+		obb = &sm.m_obb;
 	}
 	}
 
 
 	U32 getSubMeshCount() const
 	U32 getSubMeshCount() const
@@ -58,7 +55,7 @@ public:
 	}
 	}
 
 
 	/// Get all info around vertex indices.
 	/// Get all info around vertex indices.
-	void getIndexBufferInfo(BufferPtr& buff, U32& buffOffset, U32& indexCount, Format& indexFormat) const
+	void getIndexBufferInfo(BufferPtr& buff, PtrSize& buffOffset, U32& indexCount, Format& indexFormat) const
 	{
 	{
 		buff = m_indexBuff;
 		buff = m_indexBuff;
 		buffOffset = 0;
 		buffOffset = 0;
@@ -73,7 +70,7 @@ public:
 	}
 	}
 
 
 	/// Get vertex buffer info.
 	/// Get vertex buffer info.
-	void getVertexBufferInfo(const U32 buffIdx, BufferPtr& buff, U32& offset, U32& stride) const
+	void getVertexBufferInfo(const U32 buffIdx, BufferPtr& buff, PtrSize& offset, PtrSize& stride) const
 	{
 	{
 		buff = m_vertBuff;
 		buff = m_vertBuff;
 		offset = m_vertBufferInfos[buffIdx].m_offset;
 		offset = m_vertBufferInfos[buffIdx].m_offset;
@@ -81,8 +78,8 @@ public:
 	}
 	}
 
 
 	/// Get attribute info. You need to check if the attribute is preset first (isVertexAttributePresent)
 	/// Get attribute info. You need to check if the attribute is preset first (isVertexAttributePresent)
-	void getVerteAttributeInfo(
-		const VertexAttributeLocation attrib, U32& bufferIdx, Format& format, U32& relativeOffset) const
+	void getVertexAttributeInfo(
+		const VertexAttributeLocation attrib, U32& bufferIdx, Format& format, PtrSize& relativeOffset) const
 	{
 	{
 		ANKI_ASSERT(!!m_attribs[attrib].m_fmt);
 		ANKI_ASSERT(!!m_attribs[attrib].m_fmt);
 		bufferIdx = m_attribs[attrib].m_buffIdx;
 		bufferIdx = m_attribs[attrib].m_buffIdx;

+ 50 - 53
src/anki/resource/ModelResource.cpp

@@ -41,81 +41,78 @@ void ModelPatch::getRenderingDataSub(
 		inf.m_program = variant.getShaderProgram();
 		inf.m_program = variant.getShaderProgram();
 	}
 	}
 
 
-	// Vertex info
+	// Vertex attributes
+	U32 positionBinding = MAX_U32;
 	{
 	{
-		inf.m_vertexBufferBindingCount = 1;
-		VertexBufferBinding& vertBuffBinding = inf.m_vertexBufferBindings[0];
-		vertBuffBinding.m_buffer = mesh.getVertexBuffer();
-		vertBuffBinding.m_offset = 0;
-		vertBuffBinding.m_stride = mesh.getVertexSize();
+		if(key.m_pass == Pass::GB_FS)
+		{
+			// All attributes
 
 
-		auto& attribs = inf.m_vertexAttributes;
+			for(VertexAttributeLocation loc = VertexAttributeLocation::FIRST; loc < VertexAttributeLocation::COUNT;
+				++loc)
+			{
+				if(!mesh.isVertexAttributePresent(loc))
+				{
+					continue;
+				}
 
 
-		U relativeOffset = 0;
+				VertexAttributeInfo& out = inf.m_vertexAttributes[inf.m_vertexAttributeCount++];
 
 
-		attribs[0].m_bufferBinding = 0;
-		attribs[0].m_format = Format::R32G32B32_SFLOAT;
-		attribs[0].m_relativeOffset = relativeOffset;
-		relativeOffset += sizeof(Vec3);
+				out.m_location = loc;
+				mesh.getVertexAttributeInfo(loc, out.m_bufferBinding, out.m_format, out.m_relativeOffset);
+			}
+		}
+		else
+		{
+			// Only position
 
 
-		attribs[1].m_bufferBinding = 0;
-		attribs[1].m_format = Format::R16G16_SFLOAT;
-		attribs[1].m_relativeOffset = relativeOffset;
-		relativeOffset += sizeof(U16) * 2;
+			inf.m_vertexAttributeCount = 1;
 
 
-		inf.m_vertexAttributeCount = 2;
+			VertexAttributeInfo& out = inf.m_vertexAttributes[0];
+			out.m_location = VertexAttributeLocation::POSITION;
+
+			mesh.getVertexAttributeInfo(out.m_location, out.m_bufferBinding, out.m_format, out.m_relativeOffset);
+
+			// Rewrite the binding just so we can keep it in a low binding location
+			positionBinding = out.m_bufferBinding;
+			out.m_bufferBinding = 0;
+		}
+	}
 
 
+	// Vertex buffers
+	{
 		if(key.m_pass == Pass::GB_FS)
 		if(key.m_pass == Pass::GB_FS)
 		{
 		{
-			attribs[2].m_bufferBinding = 0;
-			attribs[2].m_format = Format::A2B10G10R10_SNORM_PACK32;
-			attribs[2].m_relativeOffset = relativeOffset;
-			relativeOffset += sizeof(U32);
+			// All attributes
 
 
-			attribs[3].m_bufferBinding = 0;
-			attribs[3].m_format = Format::A2B10G10R10_SNORM_PACK32;
-			attribs[3].m_relativeOffset = relativeOffset;
-			relativeOffset += sizeof(U32);
+			inf.m_vertexBufferBindingCount = mesh.getVertexBufferCount();
 
 
-			inf.m_vertexAttributeCount = 4;
+			for(U i = 0; i < inf.m_vertexBufferBindingCount; ++i)
+			{
+				VertexBufferBinding& out = inf.m_vertexBufferBindings[i];
+				mesh.getVertexBufferInfo(i, out.m_buffer, out.m_offset, out.m_stride);
+			}
 		}
 		}
 		else
 		else
 		{
 		{
-			relativeOffset += sizeof(U32) * 2;
-		}
+			// Only position
 
 
-		if(mesh.hasBoneWeights())
-		{
-			attribs[4].m_bufferBinding = 0;
-			attribs[4].m_format = Format::R8G8B8A8_UNORM;
-			attribs[4].m_relativeOffset = relativeOffset;
-			relativeOffset += sizeof(U8) * 4;
+			inf.m_vertexBufferBindingCount = 1;
 
 
-			attribs[5].m_bufferBinding = 0;
-			attribs[5].m_format = Format::R16G16B16A16_UINT;
-			attribs[5].m_relativeOffset = relativeOffset;
-			relativeOffset += sizeof(U16) * 4;
-
-			inf.m_vertexAttributeCount = 6;
+			VertexBufferBinding& out = inf.m_vertexBufferBindings[0];
+			mesh.getVertexBufferInfo(positionBinding, out.m_buffer, out.m_offset, out.m_stride);
 		}
 		}
-
-		ANKI_ASSERT(relativeOffset == mesh.getVertexSize());
 	}
 	}
 
 
 	// Index buff
 	// Index buff
-	inf.m_indexBuffer = mesh.getIndexBuffer();
+	U32 indexCount;
+	mesh.getIndexBufferInfo(inf.m_indexBuffer, inf.m_indexBufferOffset, indexCount, inf.m_indexBufferFormat);
 
 
 	// Other
 	// Other
-	if(subMeshIndicesArray.getSize() == 0 || mesh.getSubMeshesCount() == 0)
-	{
-		inf.m_drawcallCount = 1;
-		inf.m_indicesOffsetArray[0] = 0;
-		inf.m_indicesCountArray[0] = mesh.getIndicesCount();
-	}
-	else
-	{
-		ANKI_ASSERT(!"TODO");
-	}
+	ANKI_ASSERT(subMeshIndicesArray.getSize() == 1 && mesh.getSubMeshCount() == 1 && "Not supported ATM");
+	inf.m_drawcallCount = 1;
+	inf.m_indicesOffsetArray[0] = 0;
+	inf.m_indicesCountArray[0] = indexCount;
 }
 }
 
 
 U ModelPatch::getLodCount() const
 U ModelPatch::getLodCount() const

+ 11 - 4
src/anki/resource/ModelResource.h

@@ -45,12 +45,14 @@ class VertexAttributeInfo
 {
 {
 public:
 public:
 	U32 m_bufferBinding;
 	U32 m_bufferBinding;
+	VertexAttributeLocation m_location;
 	Format m_format;
 	Format m_format;
 	PtrSize m_relativeOffset;
 	PtrSize m_relativeOffset;
 
 
 	Bool operator==(const VertexAttributeInfo& b) const
 	Bool operator==(const VertexAttributeInfo& b) const
 	{
 	{
-		return m_bufferBinding == b.m_bufferBinding && m_format == b.m_format && m_relativeOffset == b.m_relativeOffset;
+		return m_bufferBinding == b.m_bufferBinding && m_format == b.m_format && m_relativeOffset == b.m_relativeOffset
+			   && m_location == b.m_location;
 	}
 	}
 
 
 	Bool operator!=(const VertexAttributeInfo& b) const
 	Bool operator!=(const VertexAttributeInfo& b) const
@@ -74,6 +76,8 @@ public:
 	U32 m_vertexAttributeCount;
 	U32 m_vertexAttributeCount;
 
 
 	BufferPtr m_indexBuffer;
 	BufferPtr m_indexBuffer;
+	Format m_indexBufferFormat;
+	PtrSize m_indexBufferOffset;
 };
 };
 
 
 /// Model patch interface class. Its very important class and it binds the material with the mesh
 /// Model patch interface class. Its very important class and it binds the material with the mesh
@@ -107,12 +111,15 @@ public:
 
 
 	const Obb& getBoundingShapeSub(U32 subMeshId) const
 	const Obb& getBoundingShapeSub(U32 subMeshId) const
 	{
 	{
-		return m_meshes[0]->getBoundingShapeSub(subMeshId);
+		U32 firstIdx, idxCount;
+		const Obb* obb;
+		m_meshes[0]->getSubMeshInfo(subMeshId, firstIdx, idxCount, obb);
+		return *obb;
 	}
 	}
 
 
-	U32 getSubMeshesCount() const
+	U32 getSubMeshCount() const
 	{
 	{
-		return m_meshes[0]->getSubMeshesCount();
+		return m_meshes[0]->getSubMeshCount();
 	}
 	}
 
 
 	ANKI_USE_RESULT Error create(
 	ANKI_USE_RESULT Error create(

+ 4 - 3
src/anki/scene/ModelNode.cpp

@@ -76,7 +76,7 @@ void ModelPatchNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<vo
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
 
 	// That will not work on multi-draw and instanced at the same time. Make sure that there is no multi-draw anywhere
 	// That will not work on multi-draw and instanced at the same time. Make sure that there is no multi-draw anywhere
-	ANKI_ASSERT(self.m_modelPatch->getSubMeshesCount() == 0);
+	ANKI_ASSERT(self.m_modelPatch->getSubMeshCount() == 1);
 
 
 	ModelRenderingInfo modelInf;
 	ModelRenderingInfo modelInf;
 	self.m_modelPatch->getRenderingDataSub(ctx.m_key, WeakArray<U8>(), modelInf);
 	self.m_modelPatch->getRenderingDataSub(ctx.m_key, WeakArray<U8>(), modelInf);
@@ -88,7 +88,8 @@ void ModelPatchNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<vo
 	for(U i = 0; i < modelInf.m_vertexAttributeCount; ++i)
 	for(U i = 0; i < modelInf.m_vertexAttributeCount; ++i)
 	{
 	{
 		const VertexAttributeInfo& attrib = modelInf.m_vertexAttributes[i];
 		const VertexAttributeInfo& attrib = modelInf.m_vertexAttributes[i];
-		cmdb->setVertexAttribute(i, attrib.m_bufferBinding, attrib.m_format, attrib.m_relativeOffset);
+		cmdb->setVertexAttribute(
+			U(attrib.m_location), attrib.m_bufferBinding, attrib.m_format, attrib.m_relativeOffset);
 	}
 	}
 
 
 	// Set vertex buffers
 	// Set vertex buffers
@@ -263,7 +264,7 @@ void ModelNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*>
 
 
 		// That will not work on multi-draw and instanced at the same time. Make sure that there is no multi-draw
 		// That will not work on multi-draw and instanced at the same time. Make sure that there is no multi-draw
 		// anywhere
 		// anywhere
-		ANKI_ASSERT(patch->getSubMeshesCount() == 0);
+		ANKI_ASSERT(patch->getSubMeshCount() == 1);
 
 
 		ModelRenderingInfo modelInf;
 		ModelRenderingInfo modelInf;
 		patch->getRenderingDataSub(ctx.m_key, WeakArray<U8>(), modelInf);
 		patch->getRenderingDataSub(ctx.m_key, WeakArray<U8>(), modelInf);

+ 7 - 8
src/anki/scene/OccluderNode.cpp

@@ -48,18 +48,17 @@ Error OccluderNode::init(const CString& meshFname)
 	MeshLoader loader(&getSceneGraph().getResourceManager());
 	MeshLoader loader(&getSceneGraph().getResourceManager());
 	ANKI_CHECK(loader.load(meshFname));
 	ANKI_CHECK(loader.load(meshFname));
 
 
-	const U16* indices = reinterpret_cast<const U16*>(loader.getIndexData());
-	U indexCount = loader.getIndexDataSize() / sizeof(U16);
-	U vertSize = loader.getVertexSize();
-
+	const U indexCount = loader.getHeader().m_totalIndexCount;
 	m_vertsL.create(getSceneAllocator(), indexCount);
 	m_vertsL.create(getSceneAllocator(), indexCount);
 	m_vertsW.create(getSceneAllocator(), indexCount);
 	m_vertsW.create(getSceneAllocator(), indexCount);
 
 
-	for(U i = 0; i < indexCount; ++i)
+	DynamicArrayAuto<Vec3> positions(getSceneAllocator());
+	DynamicArrayAuto<U32> indices(getSceneAllocator());
+	ANKI_CHECK(loader.storeIndicesAndPosition(indices, positions));
+
+	for(U i = 0; i < indices.getSize(); ++i)
 	{
 	{
-		U idx = indices[i];
-		const Vec3* vert = reinterpret_cast<const Vec3*>(loader.getVertexData() + idx * vertSize);
-		m_vertsL[i] = *vert;
+		m_vertsL[i] = positions[indices[i]];
 	}
 	}
 
 
 	// Create the components
 	// Create the components

+ 14 - 18
src/anki/scene/SectorNode.cpp

@@ -75,35 +75,31 @@ Error PortalSectorBase::init(const CString& meshFname, Bool isSector)
 	MeshLoader loader(&getSceneGraph().getResourceManager());
 	MeshLoader loader(&getSceneGraph().getResourceManager());
 	ANKI_CHECK(loader.load(meshFname));
 	ANKI_CHECK(loader.load(meshFname));
 
 
-	// Convert Vec3 positions to Vec4
-	const MeshLoader::Header& header = loader.getHeader();
-	U vertsCount = header.m_totalVerticesCount;
-	PtrSize vertSize = loader.getVertexSize();
+	DynamicArrayAuto<U32> indices(getSceneAllocator());
+	DynamicArrayAuto<Vec3> positions(getSceneAllocator());
 
 
-	auto alloc = getSceneAllocator();
-	m_shapeStorageLSpace.create(alloc, vertsCount);
-	m_shapeStorageWSpace.create(alloc, vertsCount);
+	ANKI_CHECK(loader.storeIndicesAndPosition(indices, positions));
 
 
-	for(U i = 0; i < vertsCount; ++i)
-	{
-		const Vec3& pos = *reinterpret_cast<const Vec3*>(loader.getVertexData() + vertSize * i);
+	// Convert Vec3 positions to Vec4
+	m_shapeStorageLSpace.create(getSceneAllocator(), positions.getSize());
+	m_shapeStorageWSpace.create(getSceneAllocator(), positions.getSize());
 
 
-		m_shapeStorageLSpace[i] = Vec4(pos, 0.0);
+	for(U i = 0; i < positions.getSize(); ++i)
+	{
+		m_shapeStorageLSpace[i] = Vec4(positions[i], 0.0f);
 	}
 	}
 
 
 	// Create shape
 	// Create shape
-	ConvexHullShape* hull = alloc.newInstance<ConvexHullShape>();
+	ConvexHullShape* hull = getSceneAllocator().newInstance<ConvexHullShape>();
 	m_shape = hull;
 	m_shape = hull;
-	hull->initStorage(&m_shapeStorageWSpace[0], vertsCount);
+	hull->initStorage(&m_shapeStorageWSpace[0], m_shapeStorageWSpace.getSize());
 	updateTransform(Transform::getIdentity());
 	updateTransform(Transform::getIdentity());
 
 
 	// Store indices
 	// Store indices
-	ANKI_ASSERT(header.m_totalIndicesCount * sizeof(U16) == loader.getIndexDataSize());
-	m_vertIndices.create(alloc, header.m_totalIndicesCount);
-	const U16* indicesIn = reinterpret_cast<const U16*>(loader.getIndexData());
-	for(U i = 0; i < header.m_totalIndicesCount; ++i)
+	m_vertIndices.create(getSceneAllocator(), indices.getSize());
+	for(U i = 0; i < indices.getSize(); ++i)
 	{
 	{
-		m_vertIndices[i] = indicesIn[i];
+		m_vertIndices[i] = indices[i];
 	}
 	}
 
 
 	return Error::NONE;
 	return Error::NONE;

+ 1 - 1
src/anki/scene/StaticGeometryNode.cpp

@@ -45,7 +45,7 @@ Error StaticGeometryPatchNode::init(const ModelPatch* modelPatch)
 	m_modelPatch = modelPatch;
 	m_modelPatch = modelPatch;
 
 
 	// Create spatial components
 	// Create spatial components
-	for(U i = 1; i < m_modelPatch->getSubMeshesCount(); i++)
+	for(U i = 1; i < m_modelPatch->getSubMeshCount(); i++)
 	{
 	{
 		SpatialComponent* spatial = newComponent<SpatialComponent>(this, &m_modelPatch->getBoundingShapeSub(i));
 		SpatialComponent* spatial = newComponent<SpatialComponent>(this, &m_modelPatch->getBoundingShapeSub(i));
 
 

+ 9 - 10
tools/scene/ExporterMesh.cpp

@@ -180,28 +180,28 @@ void Exporter::exportMesh(const aiMesh& mesh, const aiMatrix4x4* transform, unsi
 	// Chose the formats of the attributes
 	// Chose the formats of the attributes
 	{
 	{
 		// Positions
 		// Positions
-		auto& posa = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::POSITION];
+		auto& posa = header.m_vertexAttributes[anki::VertexAttributeLocation::POSITION];
 		posa.m_bufferBinding = 0;
 		posa.m_bufferBinding = 0;
 		posa.m_format = (maxPositionDistance < 2.0) ? anki::Format::R16G16B16_SFLOAT : anki::Format::R32G32B32_SFLOAT;
 		posa.m_format = (maxPositionDistance < 2.0) ? anki::Format::R16G16B16_SFLOAT : anki::Format::R32G32B32_SFLOAT;
 		posa.m_relativeOffset = 0;
 		posa.m_relativeOffset = 0;
 		posa.m_scale = 1.0;
 		posa.m_scale = 1.0;
 
 
 		// Normals
 		// Normals
-		auto& na = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::NORMAL];
+		auto& na = header.m_vertexAttributes[anki::VertexAttributeLocation::NORMAL];
 		na.m_bufferBinding = 1;
 		na.m_bufferBinding = 1;
 		na.m_format = anki::Format::A2B10G10R10_SNORM_PACK32;
 		na.m_format = anki::Format::A2B10G10R10_SNORM_PACK32;
 		na.m_relativeOffset = 0;
 		na.m_relativeOffset = 0;
 		na.m_scale = 1.0;
 		na.m_scale = 1.0;
 
 
 		// Tangents
 		// Tangents
-		auto& ta = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::TANGENT];
+		auto& ta = header.m_vertexAttributes[anki::VertexAttributeLocation::TANGENT];
 		ta.m_bufferBinding = 1;
 		ta.m_bufferBinding = 1;
 		ta.m_format = anki::Format::A2B10G10R10_SNORM_PACK32;
 		ta.m_format = anki::Format::A2B10G10R10_SNORM_PACK32;
 		ta.m_relativeOffset = sizeof(uint32_t);
 		ta.m_relativeOffset = sizeof(uint32_t);
 		ta.m_scale = 1.0;
 		ta.m_scale = 1.0;
 
 
 		// UVs
 		// UVs
-		auto& uva = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::UV];
+		auto& uva = header.m_vertexAttributes[anki::VertexAttributeLocation::UV];
 		uva.m_bufferBinding = 1;
 		uva.m_bufferBinding = 1;
 		if(minUvDistance >= 0.0 && maxUvDistance <= 1.0)
 		if(minUvDistance >= 0.0 && maxUvDistance <= 1.0)
 		{
 		{
@@ -217,13 +217,13 @@ void Exporter::exportMesh(const aiMesh& mesh, const aiMatrix4x4* transform, unsi
 		// Bone weight
 		// Bone weight
 		if(hasBoneWeights)
 		if(hasBoneWeights)
 		{
 		{
-			auto& bidxa = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::BONE_INDICES];
+			auto& bidxa = header.m_vertexAttributes[anki::VertexAttributeLocation::BONE_INDICES];
 			bidxa.m_bufferBinding = 2;
 			bidxa.m_bufferBinding = 2;
 			bidxa.m_format = anki::Format::R16G16B16A16_UINT;
 			bidxa.m_format = anki::Format::R16G16B16A16_UINT;
 			bidxa.m_relativeOffset = 0;
 			bidxa.m_relativeOffset = 0;
 			bidxa.m_scale = 1.0;
 			bidxa.m_scale = 1.0;
 
 
-			auto& wa = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::BONE_WEIGHTS];
+			auto& wa = header.m_vertexAttributes[anki::VertexAttributeLocation::BONE_WEIGHTS];
 			wa.m_bufferBinding = 2;
 			wa.m_bufferBinding = 2;
 			wa.m_format = anki::Format::R8G8B8A8_UNORM;
 			wa.m_format = anki::Format::R8G8B8A8_UNORM;
 			wa.m_relativeOffset = sizeof(uint16_t) * 4;
 			wa.m_relativeOffset = sizeof(uint16_t) * 4;
@@ -236,7 +236,7 @@ void Exporter::exportMesh(const aiMesh& mesh, const aiMatrix4x4* transform, unsi
 		header.m_vertexBufferCount = 2;
 		header.m_vertexBufferCount = 2;
 
 
 		// First buff has positions
 		// First buff has positions
-		const auto& posa = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::POSITION];
+		const auto& posa = header.m_vertexAttributes[anki::VertexAttributeLocation::POSITION];
 		if(posa.m_format == anki::Format::R32G32B32_SFLOAT)
 		if(posa.m_format == anki::Format::R32G32B32_SFLOAT)
 		{
 		{
 			header.m_vertexBuffers[0].m_vertexStride = sizeof(float) * 3;
 			header.m_vertexBuffers[0].m_vertexStride = sizeof(float) * 3;
@@ -314,7 +314,7 @@ void Exporter::exportMesh(const aiMesh& mesh, const aiMatrix4x4* transform, unsi
 
 
 	// Write first vert buffer
 	// Write first vert buffer
 	{
 	{
-		const auto& posa = header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::POSITION];
+		const auto& posa = header.m_vertexAttributes[anki::VertexAttributeLocation::POSITION];
 		if(posa.m_format == anki::Format::R32G32B32_SFLOAT)
 		if(posa.m_format == anki::Format::R32G32B32_SFLOAT)
 		{
 		{
 			file.write(reinterpret_cast<char*>(&positions[0]), positions.size() * sizeof(positions[0]));
 			file.write(reinterpret_cast<char*>(&positions[0]), positions.size() * sizeof(positions[0]));
@@ -358,8 +358,7 @@ void Exporter::exportMesh(const aiMesh& mesh, const aiMatrix4x4* transform, unsi
 				anki::packColorToR10G10B10A2SNorm(inVert.m_t[0], inVert.m_t[1], inVert.m_t[2], inVert.m_t[3]);
 				anki::packColorToR10G10B10A2SNorm(inVert.m_t[0], inVert.m_t[1], inVert.m_t[2], inVert.m_t[3]);
 
 
 			const float uv[2] = {inVert.m_uv[0], inVert.m_uv[1]};
 			const float uv[2] = {inVert.m_uv[0], inVert.m_uv[1]};
-			const anki::Format uvfmt =
-				header.m_vertexAttributes[anki::MeshBinaryFile::VertexAttributeType::UV].m_format;
+			const anki::Format uvfmt = header.m_vertexAttributes[anki::VertexAttributeLocation::UV].m_format;
 			if(uvfmt == anki::Format::R16G16_UNORM)
 			if(uvfmt == anki::Format::R16G16_UNORM)
 			{
 			{
 				assert(uv[0] <= 1.0 && uv[0] >= 0.0 && uv[1] <= 1.0 && uv[1] >= 0.0);
 				assert(uv[0] <= 1.0 && uv[0] >= 0.0 && uv[1] <= 1.0 && uv[1] >= 0.0);