From 2e977ce75b5ebd6ce2223b7d55a36ec42eab3b54 Mon Sep 17 00:00:00 2001
From: Tobias Frisch <tfrisch@uni-koblenz.de>
Date: Tue, 29 Nov 2022 00:54:32 +0100
Subject: [PATCH] First changes in scene module to allow creation of
 acceleration structures from static scene graph

Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de>
---
 modules/scene/include/vkcv/scene/Mesh.hpp     | 10 ++++++
 modules/scene/include/vkcv/scene/MeshPart.hpp |  5 +++
 modules/scene/include/vkcv/scene/Node.hpp     | 10 ++++++
 modules/scene/include/vkcv/scene/Scene.hpp    |  9 +++++
 modules/scene/src/vkcv/scene/Mesh.cpp         | 23 ++++++++++++
 modules/scene/src/vkcv/scene/MeshPart.cpp     | 35 +++++++++++++++++++
 modules/scene/src/vkcv/scene/Node.cpp         | 11 ++++++
 modules/scene/src/vkcv/scene/Scene.cpp        | 10 ++++++
 projects/rt_ambient_occlusion/CMakeLists.txt  |  6 ++--
 .../resources/shaders/ambientOcclusion.rchit  |  4 ++-
 projects/rt_ambient_occlusion/src/main.cpp    | 15 +++++++-
 src/vkcv/AccelerationStructureManager.cpp     | 28 +++++++++++++--
 src/vkcv/AccelerationStructureManager.hpp     |  1 +
 src/vkcv/GeometryData.cpp                     |  2 +-
 14 files changed, 161 insertions(+), 8 deletions(-)

diff --git a/modules/scene/include/vkcv/scene/Mesh.hpp b/modules/scene/include/vkcv/scene/Mesh.hpp
index 059eb6bc..d0eb3597 100644
--- a/modules/scene/include/vkcv/scene/Mesh.hpp
+++ b/modules/scene/include/vkcv/scene/Mesh.hpp
@@ -85,6 +85,16 @@ namespace vkcv::scene {
 							 PushConstants& pushConstants,
 							 std::vector<InstanceDrawcall>& drawcalls,
 							 const RecordMeshDrawcallFunction& record);
+		
+		/**
+		 * Creates acceleration structures for the whole geometry of this mesh and
+		 * appends it to a given list.
+		 *
+		 * @param[in,out] core Core instance
+		 * @param[out] accelerationStructures List of acceleration structures
+		 */
+		void appendAccelerationStructures(Core& core,
+				std::vector<AccelerationStructureHandle>&accelerationStructures) const;
 
         /**
          * Return the amount of drawcalls of the mesh
diff --git a/modules/scene/include/vkcv/scene/MeshPart.hpp b/modules/scene/include/vkcv/scene/MeshPart.hpp
index 72fd6151..a0529100 100644
--- a/modules/scene/include/vkcv/scene/MeshPart.hpp
+++ b/modules/scene/include/vkcv/scene/MeshPart.hpp
@@ -35,6 +35,11 @@ namespace vkcv::scene {
          * The vertex data containing its part of the mesh.
          */
 		VertexData m_data;
+		
+		/**
+		 * The geometry data describing its part of the mesh.
+		 */
+		GeometryData m_geometry;
 
         /**
          * Axis aligned bounding box of the mesh part.
diff --git a/modules/scene/include/vkcv/scene/Node.hpp b/modules/scene/include/vkcv/scene/Node.hpp
index de40adcf..ff38328a 100644
--- a/modules/scene/include/vkcv/scene/Node.hpp
+++ b/modules/scene/include/vkcv/scene/Node.hpp
@@ -81,6 +81,16 @@ namespace vkcv::scene {
 							 PushConstants& pushConstants,
 							 std::vector<InstanceDrawcall>& drawcalls,
 							 const RecordMeshDrawcallFunction& record);
+		
+		/**
+		 * Creates acceleration structures for all meshes of this node and its child
+		 * nodes to append them to a given list.
+		 *
+		 * @param[in,out] core Core instance
+		 * @param[out] accelerationStructures List of acceleration structures
+		 */
+		void appendAccelerationStructures(Core& core,
+				std::vector<AccelerationStructureHandle>&accelerationStructures) const;
 
         /**
          * Splits child nodes into tree based graphs of nodes
diff --git a/modules/scene/include/vkcv/scene/Scene.hpp b/modules/scene/include/vkcv/scene/Scene.hpp
index d6db062c..7c1806e5 100644
--- a/modules/scene/include/vkcv/scene/Scene.hpp
+++ b/modules/scene/include/vkcv/scene/Scene.hpp
@@ -187,6 +187,15 @@ namespace vkcv::scene {
 							 const RecordMeshDrawcallFunction &record,
 							 const std::vector<ImageHandle>   &renderTargets,
 							 const WindowHandle               &windowHandle);
+		
+		/**
+		 * Create a top-level acceleration structure representing the scene in current state
+		 * which contains all of its meshes as bottom-level acceleration structures with
+		 * its given geometry.
+		 *
+		 * @return Acceleration structure
+		 */
+		AccelerationStructureHandle createAccelerationStructure() const;
 
         /**
          * Instantiation function to create a new scene instance.
diff --git a/modules/scene/src/vkcv/scene/Mesh.cpp b/modules/scene/src/vkcv/scene/Mesh.cpp
index 3c6249fb..09358824 100644
--- a/modules/scene/src/vkcv/scene/Mesh.cpp
+++ b/modules/scene/src/vkcv/scene/Mesh.cpp
@@ -123,6 +123,29 @@ namespace vkcv::scene {
 		}
 	}
 	
+	void Mesh::appendAccelerationStructures(Core &core,
+			std::vector<AccelerationStructureHandle> &accelerationStructures) const {
+		std::vector<GeometryData> geometryData;
+		geometryData.reserve(m_parts.size());
+		
+		for (auto& part : m_parts) {
+			if (part.m_geometry.isValid()) {
+				geometryData.push_back(part.m_geometry);
+				break; // TODO: Ensure safe support of multiple geometry data structures to build bottom-level acceleration structures first! Avoid crashes!
+			}
+		}
+		
+		if (geometryData.empty()) {
+			return;
+		}
+		
+		const AccelerationStructureHandle handle = core.createAccelerationStructure(geometryData);
+		
+		if (handle) {
+			accelerationStructures.push_back(handle);
+		}
+	}
+	
 	size_t Mesh::getDrawcallCount() const {
 		return m_drawcalls.size();
 	}
diff --git a/modules/scene/src/vkcv/scene/MeshPart.cpp b/modules/scene/src/vkcv/scene/MeshPart.cpp
index 959148b0..88070f02 100644
--- a/modules/scene/src/vkcv/scene/MeshPart.cpp
+++ b/modules/scene/src/vkcv/scene/MeshPart.cpp
@@ -9,6 +9,7 @@ namespace vkcv::scene {
 	MeshPart::MeshPart(Scene& scene) :
 	m_scene(scene),
 	m_data(),
+	m_geometry(),
 	m_bounds(),
 	m_materialIndex(std::numeric_limits<size_t>::max()) {}
 	
@@ -32,6 +33,27 @@ namespace vkcv::scene {
 				)
 		);
 		
+		size_t positionAttributeIndex = types.size();
+		for (size_t i = 0; i < types.size(); i++) {
+			if (types[i] == asset::PrimitiveType::POSITION) {
+				positionAttributeIndex = i;
+				break;
+			}
+		}
+		
+		const uint32_t stride = vertexGroup.numVertices > 0? static_cast<uint32_t>(
+				vertexGroup.vertexBuffer.data.size() / vertexGroup.numVertices
+		) : 0;
+		
+		if ((positionAttributeIndex < m_data.getVertexBufferBindings().size()) &&
+			(stride > 0)) {
+			m_geometry = GeometryData(
+					m_data.getVertexBufferBindings()[positionAttributeIndex],
+					stride,
+					GeometryVertexType::POSITION_FLOAT3
+			);
+		}
+		
 		if (!vertexGroup.indexBuffer.data.empty()) {
 			auto indexBuffer = buffer<uint8_t>(
 					core, BufferType::INDEX, vertexGroup.indexBuffer.data.size()
@@ -58,8 +80,17 @@ namespace vkcv::scene {
 			
 			m_data.setIndexBuffer(indexBuffer.getHandle(), indexBitCount);
 			m_data.setCount(vertexGroup.numIndices);
+			
+			if (m_geometry.isValid()) {
+				m_geometry.setIndexBuffer(indexBuffer.getHandle(), indexBitCount);
+				m_geometry.setCount(vertexGroup.numIndices);
+			}
 		} else {
 			m_data.setCount(vertexGroup.numVertices);
+			
+			if (m_geometry.isValid()) {
+				m_geometry.setCount(vertexGroup.numIndices);
+			}
 		}
 		
 		m_bounds.setMin(glm::vec3(
@@ -103,6 +134,7 @@ namespace vkcv::scene {
 	MeshPart::MeshPart(const MeshPart &other) :
 			m_scene(other.m_scene),
 			m_data(other.m_data),
+			m_geometry(other.m_geometry),
 			m_bounds(other.m_bounds),
 			m_materialIndex(other.m_materialIndex) {
 		m_scene.increaseMaterialUsage(m_materialIndex);
@@ -111,6 +143,7 @@ namespace vkcv::scene {
 	MeshPart::MeshPart(MeshPart &&other) noexcept :
 			m_scene(other.m_scene),
 			m_data(other.m_data),
+			m_geometry(other.m_geometry),
 			m_bounds(other.m_bounds),
 			m_materialIndex(other.m_materialIndex) {
 		m_scene.increaseMaterialUsage(m_materialIndex);
@@ -122,6 +155,7 @@ namespace vkcv::scene {
 		}
 		
 		m_data = other.m_data;
+		m_geometry = other.m_geometry;
 		m_bounds = other.m_bounds;
 		m_materialIndex = other.m_materialIndex;
 		
@@ -130,6 +164,7 @@ namespace vkcv::scene {
 	
 	MeshPart &MeshPart::operator=(MeshPart &&other) noexcept {
 		m_data = other.m_data;
+		m_geometry = other.m_geometry;
 		m_bounds = other.m_bounds;
 		m_materialIndex = other.m_materialIndex;
 		
diff --git a/modules/scene/src/vkcv/scene/Node.cpp b/modules/scene/src/vkcv/scene/Node.cpp
index f5234dd4..550a02c0 100644
--- a/modules/scene/src/vkcv/scene/Node.cpp
+++ b/modules/scene/src/vkcv/scene/Node.cpp
@@ -109,6 +109,17 @@ namespace vkcv::scene {
 		}
 	}
 	
+	void Node::appendAccelerationStructures(Core& core,
+			std::vector<AccelerationStructureHandle> &accelerationStructures) const {
+		for (auto& mesh : m_meshes) {
+			mesh.appendAccelerationStructures(core, accelerationStructures);
+		}
+		
+		for (auto& node : m_nodes) {
+			node.appendAccelerationStructures(core, accelerationStructures);
+		}
+	}
+	
 	void Node::splitMeshesToSubNodes(size_t maxMeshesPerNode) {
 		if (m_meshes.size() <= maxMeshesPerNode) {
 			return;
diff --git a/modules/scene/src/vkcv/scene/Scene.cpp b/modules/scene/src/vkcv/scene/Scene.cpp
index dfe3ac54..abd0774e 100644
--- a/modules/scene/src/vkcv/scene/Scene.cpp
+++ b/modules/scene/src/vkcv/scene/Scene.cpp
@@ -150,6 +150,16 @@ namespace vkcv::scene {
 		m_core->recordEndDebugLabel(cmdStream);
 	}
 	
+	AccelerationStructureHandle Scene::createAccelerationStructure() const {
+		std::vector<AccelerationStructureHandle> accelerationStructures;
+		
+		for (auto& node : m_nodes) {
+			node.appendAccelerationStructures(*m_core, accelerationStructures);
+		}
+		
+		return m_core->createAccelerationStructure(accelerationStructures);
+	}
+	
 	Scene Scene::create(Core& core) {
 		return Scene(&core);
 	}
diff --git a/projects/rt_ambient_occlusion/CMakeLists.txt b/projects/rt_ambient_occlusion/CMakeLists.txt
index a20dab4d..6ed4c40c 100644
--- a/projects/rt_ambient_occlusion/CMakeLists.txt
+++ b/projects/rt_ambient_occlusion/CMakeLists.txt
@@ -16,7 +16,8 @@ target_include_directories(rt_ambient_occlusion SYSTEM BEFORE PRIVATE
 		${vkcv_camera_include}
 		${vkcv_geometry_include}
 		${vkcv_scene_include}
-		${vkcv_shader_compiler_include})
+		${vkcv_shader_compiler_include}
+		${vkcv_scene_include})
 
 # linking with libraries from all dependencies and the VkCV framework
 target_link_libraries(rt_ambient_occlusion
@@ -25,4 +26,5 @@ target_link_libraries(rt_ambient_occlusion
 		vkcv_camera
 		vkcv_geometry
 		vkcv_scene
-		vkcv_shader_compiler)
+		vkcv_shader_compiler
+		vkcv_scene)
diff --git a/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rchit b/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rchit
index 807fe544..5f578536 100644
--- a/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rchit
+++ b/projects/rt_ambient_occlusion/resources/shaders/ambientOcclusion.rchit
@@ -24,6 +24,8 @@ layout(binding = 3, set = 0, scalar) buffer rtIndices
 void main() {
     payload.worldPosition = vec3(1.0, 0.0, 0.5);
 
+    payload.worldNormal = vec3(1.0 - attributes.x - attributes.y, attributes.xy);
+/*
     ivec3 indicesVec = ivec3(indices[3 * gl_PrimitiveID + 0], indices[3 * gl_PrimitiveID + 1], indices[3 * gl_PrimitiveID + 2]);
 
     // current triangle
@@ -42,6 +44,6 @@ void main() {
     payload.worldNormal = normalize((objectNormal * gl_WorldToObjectEXT).xyz);
 
     payload.worldNormal = faceforward(payload.worldNormal, gl_WorldRayDirectionEXT, payload.worldNormal);
-
+*/
     payload.hitSky = 0.0f;
 }
diff --git a/projects/rt_ambient_occlusion/src/main.cpp b/projects/rt_ambient_occlusion/src/main.cpp
index fd722518..608b8a5e 100644
--- a/projects/rt_ambient_occlusion/src/main.cpp
+++ b/projects/rt_ambient_occlusion/src/main.cpp
@@ -2,6 +2,7 @@
 #include <vkcv/camera/CameraManager.hpp>
 #include <vkcv/geometry/Teapot.hpp>
 #include <vkcv/shader/GLSLCompiler.hpp>
+#include <vkcv/scene/Scene.hpp>
 
 /**
  * Note: This project is based on the following tutorial https://github.com/Apress/Ray-Tracing-Gems-II/tree/main/Chapter_16.
@@ -50,6 +51,16 @@ int main(int argc, const char** argv) {
 
 	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, 800, 600, true);
 	
+	vkcv::scene::Scene scene = vkcv::scene::Scene::load(
+			core,
+			"../first_scene/assets/Sponza/Sponza.gltf",
+			{
+					vkcv::asset::PrimitiveType::POSITION,
+					vkcv::asset::PrimitiveType::NORMAL,
+					vkcv::asset::PrimitiveType::TEXCOORD_0
+			}
+	);
+	
 	vkcv::geometry::Teapot teapot (glm::vec3(0.0f), 1.0f);
 	vkcv::VertexData vertexData = teapot.generateVertexData(core);
 	vkcv::GeometryData geometryData = teapot.extractGeometryData(vertexData);
@@ -89,9 +100,11 @@ int main(int argc, const char** argv) {
 	vkcv::AccelerationStructureHandle blas = core.createAccelerationStructure({ geometryData });
 	vkcv::AccelerationStructureHandle tlas = core.createAccelerationStructure({ blas });
 	
+	vkcv::AccelerationStructureHandle scene_tlas = scene.createAccelerationStructure();
+	
 	{
 		vkcv::DescriptorWrites writes;
-		writes.writeAcceleration(1, { core.getVulkanAccelerationStructure(tlas) });
+		writes.writeAcceleration(1, { core.getVulkanAccelerationStructure(scene_tlas) });
 		writes.writeStorageBuffer(2, geometryData.getVertexBufferBinding().buffer);
 		writes.writeStorageBuffer(3, geometryData.getIndexBuffer());
 		core.writeDescriptorSet(descriptorSetHandles[0], writes);
diff --git a/src/vkcv/AccelerationStructureManager.cpp b/src/vkcv/AccelerationStructureManager.cpp
index 919a0677..1dafe9f3 100644
--- a/src/vkcv/AccelerationStructureManager.cpp
+++ b/src/vkcv/AccelerationStructureManager.cpp
@@ -40,6 +40,10 @@ namespace vkcv {
 		if (accelerationStructure.m_storageBuffer) {
 			accelerationStructure.m_storageBuffer = BufferHandle();
 		}
+		
+		if (!accelerationStructure.m_children.empty()) {
+			accelerationStructure.m_children.clear();
+		}
 	}
 	
 	const BufferManager &AccelerationStructureManager::getBufferManager() const {
@@ -210,7 +214,8 @@ namespace vkcv {
 		
 		return {
 			accelerationStructure,
-			asStorageBuffer
+			asStorageBuffer,
+			{}
 		};
 	}
 	
@@ -220,6 +225,17 @@ namespace vkcv {
 		std::vector<vk::AccelerationStructureBuildGeometryInfoKHR> geometryInfos;
 		std::vector<std::vector<vk::AccelerationStructureBuildRangeInfoKHR>> rangeInfos;
 		
+		if (geometryData.empty()) {
+			return {};
+		}
+		
+		for (const auto& geometry : geometryData) {
+			if (!geometry.isValid()) {
+				vkcv_log(LogLevel::ERROR, "Invalid geometry used for acceleration structure")
+				return {};
+			}
+		}
+		
 		auto& bufferManager = getBufferManager();
 		
 		vk::DeviceSize accelerationStructureSize = 0;
@@ -326,10 +342,14 @@ namespace vkcv {
 	AccelerationStructureHandle AccelerationStructureManager::createAccelerationStructure(
 			const std::vector<AccelerationStructureHandle> &accelerationStructures) {
 		std::vector<vk::AccelerationStructureInstanceKHR> asInstances;
+		
+		if (accelerationStructures.empty()) {
+			return {};
+		}
+		
 		asInstances.reserve(accelerationStructures.size());
 		
 		auto& bufferManager = getBufferManager();
-		
 		const auto &dynamicDispatch = getCore().getContext().getDispatchLoaderDynamic();
 		
 		for (const auto& accelerationStructure : accelerationStructures) {
@@ -443,7 +463,7 @@ namespace vkcv {
 				dynamicDispatch
 		);
 		
-		const auto entry = buildAccelerationStructure(
+		auto entry = buildAccelerationStructure(
 				getCore(),
 				bufferManager,
 				asBuildGeometryInfos,
@@ -457,6 +477,8 @@ namespace vkcv {
 			return {};
 		}
 		
+		entry.m_children = accelerationStructures;
+		
 		return add(entry);
 	}
 	
diff --git a/src/vkcv/AccelerationStructureManager.hpp b/src/vkcv/AccelerationStructureManager.hpp
index d7cc67f1..5e773669 100644
--- a/src/vkcv/AccelerationStructureManager.hpp
+++ b/src/vkcv/AccelerationStructureManager.hpp
@@ -21,6 +21,7 @@ namespace vkcv {
 	struct AccelerationStructureEntry {
 		vk::AccelerationStructureKHR m_accelerationStructure;
 		BufferHandle m_storageBuffer;
+		std::vector<AccelerationStructureHandle> m_children;
 	};
 	
 	/**
diff --git a/src/vkcv/GeometryData.cpp b/src/vkcv/GeometryData.cpp
index 6a5cc577..35d29128 100644
--- a/src/vkcv/GeometryData.cpp
+++ b/src/vkcv/GeometryData.cpp
@@ -22,7 +22,7 @@ namespace vkcv {
 		m_count(0) {}
 	
 	bool GeometryData::isValid() const {
-		return m_vertexType == GeometryVertexType::UNDEFINED;
+		return m_vertexType != GeometryVertexType::UNDEFINED;
 	}
 	
 	const VertexBufferBinding &GeometryData::getVertexBufferBinding() const {
-- 
GitLab