Browse Source

Enable positions compression

Panagiotis Christopoulos Charitos 3 years ago
parent
commit
042b1520bf

+ 1 - 1
AnKi/Core/GpuMemoryPools.h

@@ -36,7 +36,7 @@ public:
 
 	void free(PtrSize size, U32 alignment, PtrSize offset);
 
-	BufferPtr getVertexBuffer() const
+	const BufferPtr& getVertexBuffer() const
 	{
 		return m_vertBuffer;
 	}

+ 21 - 5
AnKi/Importer/GltfImporterMesh.cpp

@@ -415,13 +415,15 @@ static Bool isConvex(const List<SubMesh>& submeshes)
 	return convex;
 }
 
-static void writeVertexAttribAndBufferInfoToHeader(VertexStreamId stream, MeshBinaryHeader& header)
+static void writeVertexAttribAndBufferInfoToHeader(VertexStreamId stream, MeshBinaryHeader& header,
+												   const Vec4& scale = Vec4(1.0f), const Vec4& translation = Vec4(0.0f))
 {
 	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;
+	attrib.m_scale = {scale[0], scale[1], scale[2], scale[3]};
+	attrib.m_translation = {translation[0], translation[1], translation[2], translation[3]};
 
 	MeshBinaryVertexBuffer& buff = header.m_vertexBuffers[stream];
 	buff.m_vertexStride = getFormatInfo(attrib.m_format).m_texelSize;
@@ -656,7 +658,17 @@ Error GltfImporter::writeMesh(const cgltf_mesh& mesh) const
 	header.m_aabbMax = aabbMax;
 	header.m_lodCount = maxLod + 1;
 
-	writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kPosition, header);
+	// Compute the pos scale and transform. The scale is uniform because it's applied to the model matrix of the object
+	F32 posScale = kMinF32;
+	for(U c = 0; c < 3; c++)
+	{
+		posScale = max(posScale, aabbMax[c] - aabbMin[c]);
+	}
+	posScale = (posScale < 1.0f) ? 1.0f : (1.0f / posScale);
+	const Vec3 posTranslation = -aabbMin;
+
+	writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kPosition, header, Vec4(1.0f / posScale),
+										   (-posTranslation).xyz1());
 	writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kNormal, header);
 	writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kTangent, header);
 	writeVertexAttribAndBufferInfoToHeader(VertexStreamId::kUv, header);
@@ -722,10 +734,14 @@ Error GltfImporter::writeMesh(const cgltf_mesh& mesh) const
 		// Write positions
 		for(const SubMesh& submesh : submeshes[lod])
 		{
-			DynamicArrayRaii<Vec3> positions(m_pool, submesh.m_verts.getSize());
+			DynamicArrayRaii<U16Vec3> 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;
+				Vec3 localPos = (submesh.m_verts[v].m_position + posTranslation) * posScale;
+				localPos = localPos.clamp(0.0f, 1.0f);
+				localPos *= F32(kMaxU16);
+				localPos = localPos.round();
+				positions[v] = U16Vec3(localPos);
 			}
 
 			ANKI_CHECK(file.write(&positions[0], positions.getSizeInBytes()));

+ 10 - 5
AnKi/Resource/MeshBinary.h

@@ -16,9 +16,7 @@ namespace anki {
 /// @addtogroup resource
 /// @{
 
-inline constexpr const char* kMeshMagic = "ANKIMES6";
-
-constexpr U32 kMeshBinaryBufferAlignment = 16;
+inline constexpr const char* kMeshMagic = "ANKIMES7";
 
 enum class MeshBinaryFlag : U32
 {
@@ -66,7 +64,12 @@ public:
 	Format m_format;
 
 	U32 m_relativeOffset;
-	F32 m_scale;
+
+	/// Attribute is compressed and needs to be scaled.
+	Array<F32, 4> m_scale;
+
+	/// Attribute is compressed and needs to be translated.
+	Array<F32, 4> m_translation;
 
 	template<typename TSerializer, typename TClass>
 	static void serializeCommon(TSerializer& s, TClass self)
@@ -74,7 +77,9 @@ public:
 		s.doValue("m_bufferIndex", offsetof(MeshBinaryVertexAttribute, m_bufferIndex), self.m_bufferIndex);
 		s.doValue("m_format", offsetof(MeshBinaryVertexAttribute, m_format), self.m_format);
 		s.doValue("m_relativeOffset", offsetof(MeshBinaryVertexAttribute, m_relativeOffset), self.m_relativeOffset);
-		s.doValue("m_scale", offsetof(MeshBinaryVertexAttribute, m_scale), self.m_scale);
+		s.doArray("m_scale", offsetof(MeshBinaryVertexAttribute, m_scale), &self.m_scale[0], self.m_scale.getSize());
+		s.doArray("m_translation", offsetof(MeshBinaryVertexAttribute, m_translation), &self.m_translation[0],
+				  self.m_translation.getSize());
 	}
 
 	template<typename TDeserializer>

+ 3 - 4
AnKi/Resource/MeshBinary.xml

@@ -8,9 +8,7 @@
 	<doxygen_group name="resource"/>
 
 	<prefix_code><![CDATA[
-inline constexpr const char* kMeshMagic = "ANKIMES6";
-
-constexpr U32 kMeshBinaryBufferAlignment = 16;
+inline constexpr const char* kMeshMagic = "ANKIMES7";
 
 enum class MeshBinaryFlag : U32
 {
@@ -35,7 +33,8 @@ ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(MeshBinaryFlag)
 				<member name="m_bufferIndex" type="U32"/>
 				<member name="m_format" type="Format" comment="If the format is kNone then the attribute is not present"/>
 				<member name="m_relativeOffset" type="U32"/>
-				<member name="m_scale" type="F32"/>
+				<member name="m_scale" type="F32" array_size="4" comment="Attribute is compressed and needs to be scaled"/>
+				<member name="m_translation" type="F32" array_size="4" comment="Attribute is compressed and needs to be translated"/>
 			</members>
 		</class>
 

+ 28 - 9
AnKi/Resource/MeshBinaryLoader.cpp

@@ -71,7 +71,7 @@ Error MeshBinaryLoader::loadSubmeshes()
 	return Error::kNone;
 }
 
-Error MeshBinaryLoader::checkFormat(VertexStreamId stream, Bool isOptional) const
+Error MeshBinaryLoader::checkFormat(VertexStreamId stream, Bool isOptional, Bool canBeTransformed) const
 {
 	const U32 vertexAttribIdx = U32(stream);
 	const U32 vertexBufferIdx = U32(stream);
@@ -104,13 +104,32 @@ Error MeshBinaryLoader::checkFormat(VertexStreamId stream, Bool isOptional) cons
 		return Error::kUserData;
 	}
 
-	// Scale should be 1.0 for now
-	if(attrib.m_scale != 1.0f)
+	if(!canBeTransformed && Vec4(attrib.m_scale) != Vec4(1.0f))
 	{
 		ANKI_RESOURCE_LOGE("Vertex attribute %u should have 1.0 scale", vertexAttribIdx);
 		return Error::kUserData;
 	}
 
+	if(canBeTransformed && Vec4(attrib.m_scale) <= kEpsilonf)
+	{
+		ANKI_RESOURCE_LOGE("Vertex attribute %u should have positive scale", vertexAttribIdx);
+		return Error::kUserData;
+	}
+
+	if(canBeTransformed
+	   && (attrib.m_scale[0] != attrib.m_scale[1] || attrib.m_scale[0] != attrib.m_scale[2]
+		   || attrib.m_scale[0] != attrib.m_scale[3]))
+	{
+		ANKI_RESOURCE_LOGE("Vertex attribute %u should have uniform scale", vertexAttribIdx);
+		return Error::kUserData;
+	}
+
+	if(!canBeTransformed && Vec4(attrib.m_translation) != Vec4(0.0f))
+	{
+		ANKI_RESOURCE_LOGE("Vertex attribute %u should have 0.0 translation", vertexAttribIdx);
+		return Error::kUserData;
+	}
+
 	const U32 vertexBufferStride = getFormatInfo(attrib.m_format).m_texelSize;
 	if(m_header.m_vertexBuffers[vertexBufferIdx].m_vertexStride != vertexBufferStride)
 	{
@@ -141,12 +160,12 @@ Error MeshBinaryLoader::checkHeader() const
 	}
 
 	// Attributes
-	ANKI_CHECK(checkFormat(VertexStreamId::kPosition, false));
-	ANKI_CHECK(checkFormat(VertexStreamId::kNormal, false));
-	ANKI_CHECK(checkFormat(VertexStreamId::kTangent, false));
-	ANKI_CHECK(checkFormat(VertexStreamId::kUv, false));
-	ANKI_CHECK(checkFormat(VertexStreamId::kBoneIds, true));
-	ANKI_CHECK(checkFormat(VertexStreamId::kBoneWeights, true));
+	ANKI_CHECK(checkFormat(VertexStreamId::kPosition, false, true));
+	ANKI_CHECK(checkFormat(VertexStreamId::kNormal, false, false));
+	ANKI_CHECK(checkFormat(VertexStreamId::kTangent, false, false));
+	ANKI_CHECK(checkFormat(VertexStreamId::kUv, false, false));
+	ANKI_CHECK(checkFormat(VertexStreamId::kBoneIds, true, false));
+	ANKI_CHECK(checkFormat(VertexStreamId::kBoneWeights, true, false));
 
 	// Vertex buffers
 	const Format boneIdxFormat = m_header.m_vertexAttributes[VertexStreamId::kBoneIds].m_format;

+ 1 - 1
AnKi/Resource/MeshBinaryLoader.h

@@ -88,7 +88,7 @@ private:
 	PtrSize getLodBuffersSize(U32 lod) const;
 
 	Error checkHeader() const;
-	Error checkFormat(VertexStreamId stream, Bool isOptional) const;
+	Error checkFormat(VertexStreamId stream, Bool isOptional, Bool canBeTransformed) const;
 	Error loadSubmeshes();
 };
 /// @}

+ 4 - 2
AnKi/Resource/MeshResource.cpp

@@ -114,6 +114,8 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 	m_indexType = header.m_indexType;
 	m_aabb.setMin(header.m_aabbMin);
 	m_aabb.setMax(header.m_aabbMax);
+	m_positionsScale = header.m_vertexAttributes[VertexStreamId::kPosition].m_scale[0];
+	m_positionsTranslation = Vec3(&header.m_vertexAttributes[VertexStreamId::kPosition].m_translation[0]);
 
 	// Submeshes
 	m_subMeshes.create(getMemoryPool(), header.m_subMeshCount);
@@ -152,9 +154,9 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 
 			const U32 texelSize = getFormatInfo(kMeshRelatedVertexStreamFormats[stream]).m_texelSize;
 			const PtrSize vertexBufferSize = PtrSize(lod.m_vertexCount) * texelSize;
-
+			const U32 alignment = 4;
 			ANKI_CHECK(getManager().getUnifiedGeometryMemoryPool().allocate(
-				vertexBufferSize, texelSize, lod.m_unifiedGeometryVertBufferOffsets[stream]));
+				vertexBufferSize, alignment, lod.m_unifiedGeometryVertBufferOffsets[stream]));
 		}
 
 		// BLAS

+ 13 - 0
AnKi/Resource/MeshResource.h

@@ -84,6 +84,16 @@ public:
 		return m_lods.getSize();
 	}
 
+	F32 getPositionsScale() const
+	{
+		return m_positionsScale;
+	}
+
+	Vec3 getPositionsTranslation() const
+	{
+		return m_positionsTranslation;
+	}
+
 private:
 	class LoadTask;
 	class LoadContext;
@@ -120,6 +130,9 @@ private:
 	IndexType m_indexType;
 	VertexStreamMask m_presentVertStreams = VertexStreamMask::kNone;
 
+	F32 m_positionsScale = 0.0f;
+	Vec3 m_positionsTranslation = Vec3(0.0f);
+
 	Error loadAsync(MeshBinaryLoader& loader) const;
 };
 /// @}

+ 25 - 8
AnKi/Scene/ModelNode.cpp

@@ -43,6 +43,10 @@ class ModelNode::RenderProxy
 {
 public:
 	ModelNode* m_node = nullptr;
+
+	/// Uncompresses the mesh positions to the local view. The scale should be uniform because it will be applied to
+	/// normals and tangents and non-uniform data will cause problems.
+	Mat4 m_compressedToModelTransform = Mat4::getIdentity();
 };
 
 ModelNode::ModelNode(SceneGraph* scene, CString name)
@@ -165,6 +169,7 @@ void ModelNode::initRenderComponents()
 
 	for(U32 patchIdx = 0; patchIdx < model->getModelPatches().getSize(); ++patchIdx)
 	{
+		const ModelPatch& modelPatch = model->getModelPatches()[patchIdx];
 		RenderComponent& rc = getNthComponentOfType<RenderComponent>(patchIdx);
 		rc.initRaster(
 			[](RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) {
@@ -174,10 +179,9 @@ void ModelNode::initRenderComponents()
 			},
 			&m_renderProxies[patchIdx], modelc.getRenderMergeKeys()[patchIdx]);
 
-		rc.setFlagsFromMaterial(model->getModelPatches()[patchIdx].getMaterial());
+		rc.setFlagsFromMaterial(modelPatch.getMaterial());
 
-		if(!!(model->getModelPatches()[patchIdx].getMaterial()->getRenderingTechniques()
-			  & RenderingTechniqueBit::kAllRt))
+		if(!!(modelPatch.getMaterial()->getRenderingTechniques() & RenderingTechniqueBit::kAllRt))
 		{
 			rc.initRayTracing(
 				[](U32 lod, const void* userData, RayTracingInstanceQueueElement& el) {
@@ -188,7 +192,15 @@ void ModelNode::initRenderComponents()
 				&m_renderProxies[patchIdx]);
 		}
 
-		m_renderProxies[patchIdx].m_node = this;
+		// Init the proxy
+		RenderProxy& proxy = m_renderProxies[patchIdx];
+		proxy.m_node = this;
+
+		const MeshResource& meshResource = *modelPatch.getMesh();
+		proxy.m_compressedToModelTransform.setTranslationPart(meshResource.getPositionsTranslation().xyz1());
+		proxy.m_compressedToModelTransform(0, 0) = meshResource.getPositionsScale();
+		proxy.m_compressedToModelTransform(1, 1) = meshResource.getPositionsScale();
+		proxy.m_compressedToModelTransform(2, 2) = meshResource.getPositionsScale();
 	}
 }
 
@@ -207,11 +219,16 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 		const SkinComponent& skinc = getFirstComponentOfType<SkinComponent>();
 
 		// Transforms
+		auto computeTranform = [&](const Transform& trf) -> Mat3x4 {
+			const Mat4 m4 = Mat4(trf) * m_renderProxies[modelPatchIdx].m_compressedToModelTransform;
+			const Mat3x4 out(m4);
+			return out;
+		};
 		Array<Mat3x4, kMaxInstanceCount> trfs;
 		Array<Mat3x4, kMaxInstanceCount> prevTrfs;
 		const MoveComponent& movec = getFirstComponentOfType<MoveComponent>();
-		trfs[0] = Mat3x4(movec.getWorldTransform());
-		prevTrfs[0] = Mat3x4(movec.getPreviousWorldTransform());
+		trfs[0] = computeTranform(movec.getWorldTransform());
+		prevTrfs[0] = computeTranform(movec.getPreviousWorldTransform());
 		Bool moved = trfs[0] != prevTrfs[0];
 		for(U32 i = 1; i < instanceCount; ++i)
 		{
@@ -222,8 +239,8 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 			ANKI_ASSERT(otherNodeModelPatchIdx == modelPatchIdx);
 
 			const MoveComponent& otherNodeMovec = otherNode.getFirstComponentOfType<MoveComponent>();
-			trfs[i] = Mat3x4(otherNodeMovec.getWorldTransform());
-			prevTrfs[i] = Mat3x4(otherNodeMovec.getPreviousWorldTransform());
+			trfs[i] = computeTranform(otherNodeMovec.getWorldTransform());
+			prevTrfs[i] = computeTranform(otherNodeMovec.getPreviousWorldTransform());
 
 			moved = moved || (trfs[i] != prevTrfs[i]);
 		}

+ 2 - 2
AnKi/Shaders/Include/MeshTypes.h

@@ -54,8 +54,8 @@ enum class VertexStreamMask : U8
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(VertexStreamMask)
 
 inline constexpr Array<Format, U32(VertexStreamId::kMeshRelatedCount)> kMeshRelatedVertexStreamFormats = {
-	Format::kR32G32B32_Sfloat, Format::kR8G8B8A8_Snorm, Format::kR8G8B8A8_Snorm,
-	Format::kR32G32_Sfloat,    Format::kR8G8B8A8_Uint,  Format::kR8G8B8A8_Snorm};
+	Format::kR16G16B16_Unorm, Format::kR8G8B8A8_Snorm, Format::kR8G8B8A8_Snorm,
+	Format::kR32G32_Sfloat,   Format::kR8G8B8A8_Uint,  Format::kR8G8B8A8_Snorm};
 
 #else
 

BIN
EngineAssets/LightMeshes.blend


BIN
Samples/SimpleScene/Assets/Mesh_0_d56f58fc33de003f.ankimesh


BIN
Samples/SimpleScene/Assets/Mesh_1_266a0dd9d2092f46.ankimesh


BIN
Samples/SimpleScene/Assets/Mesh_2_be53007bec464649.ankimesh


BIN
Samples/SimpleScene/Assets/Mesh_3_c026fdb5b74773ed.ankimesh


BIN
Samples/SimpleScene/Assets/Mesh_4_4d4aae6c030c4fd5.ankimesh


BIN
Samples/SimpleScene/Assets/Mesh_5_629309b27fa549a7.ankimesh


BIN
Samples/SimpleScene/Assets/Mesh_6_a078cf217893be6f.ankimesh


BIN
Samples/SimpleScene/Assets/Mesh_7_4b76b132380d8a62.ankimesh


+ 1 - 1
Samples/SimpleScene/Assets/Scene.lua

@@ -1,4 +1,4 @@
--- Generated by: C:\Users\godli\src\anki\out\build\x64-Debug\Bin\GltfImporter.exe CornellBox.gltf .. -rpath Assets -texrpath Assets -v
+-- Generated by: C:\Users\godli\src\anki\out\build\x64-Release\Bin\GltfImporter.exe CornellBox.gltf .. -rpath Assets -texrpath Assets -v -j 0
 local scene = getSceneGraph()
 local events = getEventManager()
 

+ 2 - 1
Samples/SimpleScene/Main.cpp

@@ -21,7 +21,8 @@ public:
 	}
 };
 
-int main(int argc, char* argv[])
+ANKI_MAIN_FUNCTION(myMain)
+int myMain(int argc, char* argv[])
 {
 	Error err = Error::kNone;
 

+ 2 - 2
Sandbox/Main.cpp

@@ -373,8 +373,8 @@ Error MyApp::userMainLoop(Bool& quit, Second elapsedTime)
 
 	if(in.getKey(KeyCode::kH) == 1)
 	{
-		renderer.setCurrentDebugRenderTarget((renderer.getCurrentDebugRenderTarget() == "RtShadows") ? ""
-																									 : "RtShadows");
+		renderer.setCurrentDebugRenderTarget(
+			(renderer.getCurrentDebugRenderTarget() == "GBufferNormals") ? "" : "GBufferNormals");
 	}
 
 	/*if(in.getKey(KeyCode::J) == 1)