From aa0c6c36f2373015ccf355ba75dad066fe18db14 Mon Sep 17 00:00:00 2001
From: Tobias Frisch <tfrisch@uni-koblenz.de>
Date: Tue, 29 Jun 2021 19:17:58 +0200
Subject: [PATCH] [#56] Basic scene loading from assets

Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de>
---
 include/vkcv/Image.hpp                        |   2 +-
 .../include/vkcv/material/Material.hpp        |  26 +--
 .../material/src/vkcv/material/Material.cpp   | 100 +++++++-----
 modules/scene/CMakeLists.txt                  |   9 ++
 modules/scene/include/vkcv/scene/Mesh.hpp     |  37 ++++-
 modules/scene/include/vkcv/scene/Node.hpp     |  22 ++-
 modules/scene/include/vkcv/scene/Scene.hpp    |  13 +-
 modules/scene/src/vkcv/scene/Bounds.cpp       |  10 +-
 modules/scene/src/vkcv/scene/Mesh.cpp         | 152 +++++++++++++++++-
 modules/scene/src/vkcv/scene/Node.cpp         |  17 +-
 modules/scene/src/vkcv/scene/Scene.cpp        |  23 ++-
 src/vkcv/Image.cpp                            |   2 +-
 src/vkcv/ImageManager.cpp                     |   2 +-
 src/vkcv/ImageManager.hpp                     |   2 +-
 14 files changed, 334 insertions(+), 83 deletions(-)

diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp
index eac96975..33f0047c 100644
--- a/include/vkcv/Image.hpp
+++ b/include/vkcv/Image.hpp
@@ -38,7 +38,7 @@ namespace vkcv {
 
 		void switchLayout(vk::ImageLayout newLayout);
 		
-		void fill(void* data, size_t size = SIZE_MAX);
+		void fill(const void* data, size_t size = SIZE_MAX);
 		void generateMipChainImmediate();
 		void recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream);
 	private:
diff --git a/modules/material/include/vkcv/material/Material.hpp b/modules/material/include/vkcv/material/Material.hpp
index f5bd0e1f..a3b97823 100644
--- a/modules/material/include/vkcv/material/Material.hpp
+++ b/modules/material/include/vkcv/material/Material.hpp
@@ -43,25 +43,25 @@ namespace vkcv::material {
 		
 		bool operator!() const;
 		
-		static const std::vector<DescriptorBinding>& getPBRDescriptorBindings() noexcept;
+		static const std::vector<DescriptorBinding>& getDescriptorBindings(MaterialType type);
 		
 		static Material createPBR(Core &core,
-								  ImageHandle &colorImg,
-								  SamplerHandle &colorSmp,
-								  ImageHandle &normalImg,
-								  SamplerHandle &normalSmp,
-								  ImageHandle &metRoughImg,
-								  SamplerHandle &metRoughSmp,
-								  ImageHandle &occlusionImg,
-								  SamplerHandle &occlusionSmp,
-								  ImageHandle &emissiveImg,
-								  SamplerHandle &emissiveSmp,
-								  float baseColorFactor [4],
+								  const ImageHandle &colorImg,
+								  const SamplerHandle &colorSmp,
+								  const ImageHandle &normalImg,
+								  const SamplerHandle &normalSmp,
+								  const ImageHandle &metRoughImg,
+								  const SamplerHandle &metRoughSmp,
+								  const ImageHandle &occlusionImg,
+								  const SamplerHandle &occlusionSmp,
+								  const ImageHandle &emissiveImg,
+								  const SamplerHandle &emissiveSmp,
+								  const float baseColorFactor [4],
 								  float metallicFactor,
 								  float roughnessFactor,
 								  float normalScale,
 								  float occlusionStrength,
-								  float emissiveFactor [3]);
+								  const float emissiveFactor [3]);
 	
 	};
 	
diff --git a/modules/material/src/vkcv/material/Material.cpp b/modules/material/src/vkcv/material/Material.cpp
index 86c6b7ae..ba6411cc 100644
--- a/modules/material/src/vkcv/material/Material.cpp
+++ b/modules/material/src/vkcv/material/Material.cpp
@@ -19,75 +19,89 @@ namespace vkcv::material {
 		return (m_Type == MaterialType::UNKNOWN);
 	}
 	
-	const std::vector<DescriptorBinding>& Material::getPBRDescriptorBindings() noexcept
+	const std::vector<DescriptorBinding>& Material::getDescriptorBindings(MaterialType type)
 	{
-		static std::vector<DescriptorBinding> bindings;
-		
-		if (bindings.empty()) {
-			bindings.emplace_back(0, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
-			bindings.emplace_back(1, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
-			bindings.emplace_back(2, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
-			bindings.emplace_back(3, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
-			bindings.emplace_back(4, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
-			bindings.emplace_back(5, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
-			bindings.emplace_back(6, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
-			bindings.emplace_back(7, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
-			bindings.emplace_back(8, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
-			bindings.emplace_back(9, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
+		switch (type) {
+			case MaterialType::PBR_MATERIAL:
+				static std::vector<DescriptorBinding> pbr_bindings;
+				
+				if (pbr_bindings.empty()) {
+					pbr_bindings.emplace_back(0, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
+					pbr_bindings.emplace_back(1, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
+					pbr_bindings.emplace_back(2, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
+					pbr_bindings.emplace_back(3, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
+					pbr_bindings.emplace_back(4, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
+					pbr_bindings.emplace_back(5, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
+					pbr_bindings.emplace_back(6, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
+					pbr_bindings.emplace_back(7, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
+					pbr_bindings.emplace_back(8, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT);
+					pbr_bindings.emplace_back(9, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT);
+				}
+				
+				return pbr_bindings;
+			default:
+				static std::vector<DescriptorBinding> default_bindings;
+				return default_bindings;
 		}
-		
-		return bindings;
 	}
 	
 	Material Material::createPBR(Core &core,
-								 ImageHandle &colorImg, SamplerHandle &colorSmp,
-								 ImageHandle &normalImg, SamplerHandle &normalSmp,
-								 ImageHandle &metRoughImg, SamplerHandle &metRoughSmp,
-								 ImageHandle &occlusionImg, SamplerHandle &occlusionSmp,
-								 ImageHandle &emissiveImg, SamplerHandle &emissiveSmp,
-								 float baseColorFactor [4],
+								 const ImageHandle &colorImg, const SamplerHandle &colorSmp,
+								 const ImageHandle &normalImg, const SamplerHandle &normalSmp,
+								 const ImageHandle &metRoughImg, const SamplerHandle &metRoughSmp,
+								 const ImageHandle &occlusionImg, const SamplerHandle &occlusionSmp,
+								 const ImageHandle &emissiveImg, const SamplerHandle &emissiveSmp,
+								 const float baseColorFactor [4],
 								 float metallicFactor,
 								 float roughnessFactor,
 								 float normalScale,
 								 float occlusionStrength,
-								 float emissiveFactor [3]) {
+								 const float emissiveFactor [3]) {
+		ImageHandle images [5] = {
+				colorImg, normalImg, metRoughImg, occlusionImg, emissiveImg
+		};
+		
+		SamplerHandle samplers [5] = {
+				colorSmp, normalSmp, metRoughSmp, occlusionSmp, emissiveSmp
+		};
+		
 		if (!colorImg) {
 			vkcv::Image defaultColor = core.createImage(vk::Format::eR8G8B8A8Srgb, 1, 1);
 			float colorData [4] = { 228, 51, 255, 1 };
 			defaultColor.fill(&colorData);
-			colorImg = defaultColor.getHandle();
+			images[0] = defaultColor.getHandle();
 		}
 		
 		if (!normalImg) {
 			vkcv::Image defaultNormal = core.createImage(vk::Format::eR8G8B8A8Srgb, 1, 1);
 			float normalData [4] = { 0, 0, 1, 0 };
 			defaultNormal.fill(&normalData);
-			normalImg = defaultNormal.getHandle();
+			images[1] = defaultNormal.getHandle();
 		}
 		
 		if (!metRoughImg) {
 			vkcv::Image defaultRough = core.createImage(vk::Format::eR8G8B8A8Srgb, 1, 1);
 			float roughData [4] = { 228, 51, 255, 1 };
 			defaultRough.fill(&roughData);
-			metRoughImg = defaultRough.getHandle();
+			images[2] = defaultRough.getHandle();
 		}
 		
 		if (!occlusionImg) {
 			vkcv::Image defaultOcclusion = core.createImage(vk::Format::eR8G8B8A8Srgb, 1, 1);
 			float occlusionData [4] = { 228, 51, 255, 1 };
 			defaultOcclusion.fill(&occlusionData);
-			occlusionImg = defaultOcclusion.getHandle();
+			images[3] = defaultOcclusion.getHandle();
 		}
 		
 		if (!emissiveImg) {
 			vkcv::Image defaultEmissive = core.createImage(vk::Format::eR8G8B8A8Srgb, 1, 1);
 			float emissiveData [4] = { 0, 0, 0, 1 };
 			defaultEmissive.fill(&emissiveData);
-			emissiveImg = defaultEmissive.getHandle();
+			images[4] = defaultEmissive.getHandle();
 		}
 		
 		if (!colorSmp) {
-			colorSmp = core.createSampler(
+			samplers[0] = core.createSampler(
 					vkcv::SamplerFilterType::LINEAR,
 					vkcv::SamplerFilterType::LINEAR,
 					vkcv::SamplerMipmapMode::LINEAR,
@@ -96,7 +110,7 @@ namespace vkcv::material {
 		}
 		
 		if (!normalSmp) {
-			normalSmp = core.createSampler(
+			samplers[1] = core.createSampler(
 					vkcv::SamplerFilterType::LINEAR,
 					vkcv::SamplerFilterType::LINEAR,
 					vkcv::SamplerMipmapMode::LINEAR,
@@ -105,7 +119,7 @@ namespace vkcv::material {
 		}
 		
 		if (!metRoughSmp) {
-			metRoughSmp = core.createSampler(
+			samplers[2] = core.createSampler(
 					vkcv::SamplerFilterType::LINEAR,
 					vkcv::SamplerFilterType::LINEAR,
 					vkcv::SamplerMipmapMode::LINEAR,
@@ -114,7 +128,7 @@ namespace vkcv::material {
 		}
 		
 		if (!occlusionSmp) {
-			occlusionSmp = core.createSampler(
+			samplers[3] = core.createSampler(
 					vkcv::SamplerFilterType::LINEAR,
 					vkcv::SamplerFilterType::LINEAR,
 					vkcv::SamplerMipmapMode::LINEAR,
@@ -123,7 +137,7 @@ namespace vkcv::material {
 		}
 		
 		if (!emissiveSmp) {
-			emissiveSmp = core.createSampler(
+			samplers[4] = core.createSampler(
 					vkcv::SamplerFilterType::LINEAR,
 					vkcv::SamplerFilterType::LINEAR,
 					vkcv::SamplerMipmapMode::LINEAR,
@@ -131,20 +145,18 @@ namespace vkcv::material {
 			);
 		}
 		
-		const auto& bindings = getPBRDescriptorBindings();
-		vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(bindings);
-		
-		
 		Material material;
 		material.m_Type = MaterialType::PBR_MATERIAL;
-		material.m_DescriptorSet = descriptorSet;
+		
+		const auto& bindings = getDescriptorBindings(material.m_Type);
+		material.m_DescriptorSet = core.createDescriptorSet(bindings);;
 		
 		material.m_Textures.reserve(bindings.size());
-		material.m_Textures.push_back({ colorImg, colorSmp, std::vector<float>(baseColorFactor, baseColorFactor+4) });
-		material.m_Textures.push_back({ normalImg, normalSmp, std::vector<float>(&normalScale, &normalScale+1) });
-		material.m_Textures.push_back({ metRoughImg, metRoughSmp, std::vector<float>(&metallicFactor, &metallicFactor+1) });
-		material.m_Textures.push_back({ occlusionImg, occlusionSmp, std::vector<float>(&occlusionStrength, &occlusionStrength+1) });
-		material.m_Textures.push_back({ emissiveImg, emissiveSmp, std::vector<float>(emissiveFactor, emissiveFactor+3) });
+		material.m_Textures.push_back({ images[0], samplers[0], std::vector<float>(baseColorFactor, baseColorFactor+4) });
+		material.m_Textures.push_back({ images[1], samplers[1], { normalScale } });
+		material.m_Textures.push_back({ images[2], samplers[2], { metallicFactor, roughnessFactor } });
+		material.m_Textures.push_back({ images[3], samplers[3], { occlusionStrength } });
+		material.m_Textures.push_back({ images[4], samplers[4], std::vector<float>(emissiveFactor, emissiveFactor+3) });
 		
 		vkcv::DescriptorWrites setWrites;
 		
@@ -153,7 +165,7 @@ namespace vkcv::material {
 			setWrites.samplerWrites.emplace_back(i * 2 + 1, material.m_Textures[i].m_Sampler);
 		}
 		
-		core.writeDescriptorSet(descriptorSet, setWrites);
+		core.writeDescriptorSet(material.m_DescriptorSet, setWrites);
 		return material;
 	}
 
diff --git a/modules/scene/CMakeLists.txt b/modules/scene/CMakeLists.txt
index 189eb7f6..32a49ca3 100644
--- a/modules/scene/CMakeLists.txt
+++ b/modules/scene/CMakeLists.txt
@@ -10,6 +10,15 @@ set(vkcv_scene_include ${PROJECT_SOURCE_DIR}/include)
 
 # Add source and header files to the module
 set(vkcv_scene_sources
+		${vkcv_scene_include}/vkcv/scene/Bounds.hpp
+		${vkcv_scene_source}/vkcv/scene/Bounds.cpp
+		
+		${vkcv_scene_include}/vkcv/scene/Mesh.hpp
+		${vkcv_scene_source}/vkcv/scene/Mesh.cpp
+		
+		${vkcv_scene_include}/vkcv/scene/Node.hpp
+		${vkcv_scene_source}/vkcv/scene/Node.cpp
+		
 		${vkcv_scene_include}/vkcv/scene/Scene.hpp
 		${vkcv_scene_source}/vkcv/scene/Scene.cpp
 )
diff --git a/modules/scene/include/vkcv/scene/Mesh.hpp b/modules/scene/include/vkcv/scene/Mesh.hpp
index 1a1c1a86..ca623daf 100644
--- a/modules/scene/include/vkcv/scene/Mesh.hpp
+++ b/modules/scene/include/vkcv/scene/Mesh.hpp
@@ -11,16 +11,19 @@
 
 namespace vkcv::scene {
 	
+	class Scene;
+	
 	class MeshPart {
 		friend class Mesh;
 		
 	private:
-		material::Material* m_material;
-		Buffer<uint8_t> m_vertices;
-		Buffer<uint8_t> m_indices;
+		Scene* m_scene;
+		BufferHandle m_vertices;
+		BufferHandle m_indices;
 		Bounds m_bounds;
+		size_t m_materialIndex;
 		
-		MeshPart();
+		explicit MeshPart(Scene* scene);
 		
 		void load(const asset::Scene& scene,
 				  const asset::VertexGroup& vertexGroup);
@@ -28,13 +31,39 @@ namespace vkcv::scene {
 	public:
 		~MeshPart();
 		
+		MeshPart(const MeshPart& other) = default;
+		MeshPart(MeshPart&& other) = default;
+		
+		MeshPart& operator=(const MeshPart& other) = default;
+		MeshPart& operator=(MeshPart&& other) = default;
+		
+		[[nodiscard]]
+		const material::Material& getMaterial() const;
+		
 	};
 	
+	class Node;
+	
 	class Mesh {
+		friend class Node;
+		
 	private:
+		Scene* m_scene;
 		std::vector<MeshPart> m_parts;
+		
+		explicit Mesh(Scene* scene);
+		
+		void load(const asset::Scene& scene,
+				  const asset::Mesh& mesh);
 	
 	public:
+		~Mesh() = default;
+		
+		Mesh(const Mesh& other) = default;
+		Mesh(Mesh&& other) = default;
+		
+		Mesh& operator=(const Mesh& other) = default;
+		Mesh& operator=(Mesh&& other) = default;
 	
 	};
 	
diff --git a/modules/scene/include/vkcv/scene/Node.hpp b/modules/scene/include/vkcv/scene/Node.hpp
index ef21061f..c5d24fc1 100644
--- a/modules/scene/include/vkcv/scene/Node.hpp
+++ b/modules/scene/include/vkcv/scene/Node.hpp
@@ -6,12 +6,32 @@
 
 namespace vkcv::scene {
 	
+	class Scene;
+	
 	class Node {
+		friend class Scene;
+		
 	private:
-		std::vector<Mesh> m_models;
+		Scene* m_scene;
+		
+		std::vector<Mesh> m_meshes;
 		std::vector<Node> m_nodes;
+		
+		explicit Node(Scene* scene);
+		
+		void loadMesh(const asset::Scene& asset_scene, const asset::Mesh& asset_mesh);
 	
 	public:
+		~Node() = default;
+		
+		Node(const Node& other) = default;
+		Node(Node&& other) = default;
+		
+		Node& operator=(const Node& other) = default;
+		Node& operator=(Node&& other) = default;
+		
+		Node& addNode();
+		
 	};
 	
 }
diff --git a/modules/scene/include/vkcv/scene/Scene.hpp b/modules/scene/include/vkcv/scene/Scene.hpp
index db3c303a..c41fada0 100644
--- a/modules/scene/include/vkcv/scene/Scene.hpp
+++ b/modules/scene/include/vkcv/scene/Scene.hpp
@@ -2,6 +2,7 @@
 
 #include <filesystem>
 
+#include <vkcv/Core.hpp>
 #include <vkcv/material/Material.hpp>
 
 #include "Node.hpp"
@@ -9,16 +10,20 @@
 namespace vkcv::scene {
 	
 	class Scene {
+		friend class MeshPart;
+		
 	private:
 		struct Material {
 			size_t m_usages;
 			material::Material m_data;
 		};
 		
+		Core* m_core;
+		
 		std::vector<Node> m_nodes;
 		std::vector<Material> m_materials;
 		
-		Scene() = default;
+		explicit Scene(Core* core);
 		
 	public:
 		~Scene() = default;
@@ -29,9 +34,11 @@ namespace vkcv::scene {
 		Scene& operator=(const Scene& other) = default;
 		Scene& operator=(Scene&& other) = default;
 		
-		static Scene create();
+		Node& addNode();
+		
+		static Scene create(Core& core);
 		
-		static Scene load(const std::filesystem::path &path);
+		static Scene load(Core& core, const std::filesystem::path &path);
 		
 	};
 	
diff --git a/modules/scene/src/vkcv/scene/Bounds.cpp b/modules/scene/src/vkcv/scene/Bounds.cpp
index e49a20c5..a25a825c 100644
--- a/modules/scene/src/vkcv/scene/Bounds.cpp
+++ b/modules/scene/src/vkcv/scene/Bounds.cpp
@@ -21,18 +21,18 @@ namespace vkcv::scene {
 	
 	void Bounds::setCenter(const glm::vec3 &center) {
 		const glm::vec3 size = getSize();
-		m_min = center - size / 2;
-		m_max = center + size / 2;
+		m_min = center - size / 2.0f;
+		m_max = center + size / 2.0f;
 	}
 	
 	glm::vec3 Bounds::getCenter() const {
-		return (m_min + m_max) / 2;
+		return (m_min + m_max) / 2.0f;
 	}
 	
 	void Bounds::setSize(const glm::vec3 &size) {
 		const glm::vec3 center = getCenter();
-		m_min = center - size / 2;
-		m_max = center + size / 2;
+		m_min = center - size / 2.0f;
+		m_max = center + size / 2.0f;
 	}
 	
 	glm::vec3 Bounds::getSize() const {
diff --git a/modules/scene/src/vkcv/scene/Mesh.cpp b/modules/scene/src/vkcv/scene/Mesh.cpp
index 02bc4aae..3a2dd9c7 100644
--- a/modules/scene/src/vkcv/scene/Mesh.cpp
+++ b/modules/scene/src/vkcv/scene/Mesh.cpp
@@ -1,10 +1,120 @@
 
 #include "vkcv/scene/Mesh.hpp"
+#include "vkcv/scene/Scene.hpp"
 
 namespace vkcv::scene {
 	
+	MeshPart::MeshPart(Scene* scene) :
+	m_scene(scene) {}
+	
+	void loadImage(Core& core, const asset::Scene& asset_scene,
+				   const asset::Texture& asset_texture,
+				   const vk::Format& format,
+				   ImageHandle& image, SamplerHandle& sampler) {
+		asset::Sampler* asset_sampler = nullptr;
+		
+		if ((asset_texture.sampler >= 0) && (asset_texture.sampler < asset_scene.samplers.size())) {
+			//asset_sampler = &(asset_scene.samplers[asset_texture.sampler]); // TODO
+		}
+		
+		Image img = core.createImage(format, asset_texture.w, asset_texture.h, 1, true);
+		img.fill(asset_texture.data.data());
+		image = img.getHandle();
+		
+		if (asset_sampler) {
+			//sampler = core.createSampler(asset_sampler) // TODO
+		}
+	}
+	
+	material::Material loadMaterial(Core& core, const asset::Scene& scene,
+					  				const asset::Material& material) {
+		ImageHandle diffuseImg;
+		SamplerHandle diffuseSmp;
+		
+		if ((material.baseColor >= 0) && (material.baseColor < scene.textures.size())) {
+			loadImage(core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Srgb,
+					  diffuseImg,diffuseSmp);
+		}
+		
+		ImageHandle normalImg;
+		SamplerHandle normalSmp;
+		
+		if ((material.baseColor >= 0) && (material.baseColor < scene.textures.size())) {
+			loadImage(core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Srgb,
+					  diffuseImg,diffuseSmp);
+		}
+		
+		ImageHandle metalRoughImg;
+		SamplerHandle metalRoughSmp;
+		
+		if ((material.baseColor >= 0) && (material.baseColor < scene.textures.size())) {
+			loadImage(core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Srgb,
+					  diffuseImg,diffuseSmp);
+		}
+		
+		ImageHandle occlusionImg;
+		SamplerHandle occlusionSmp;
+		
+		if ((material.baseColor >= 0) && (material.baseColor < scene.textures.size())) {
+			loadImage(core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Srgb,
+					  diffuseImg,diffuseSmp);
+		}
+		
+		ImageHandle emissionImg;
+		SamplerHandle emissionSmp;
+		
+		if ((material.baseColor >= 0) && (material.baseColor < scene.textures.size())) {
+			loadImage(core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Srgb,
+					  diffuseImg,diffuseSmp);
+		}
+		
+		const float colorFactors [4] = {
+				material.baseColorFactor.r,
+				material.baseColorFactor.g,
+				material.baseColorFactor.b,
+				material.baseColorFactor.a
+		};
+		
+		const float emissionFactors[4] = {
+				material.emissiveFactor.r,
+				material.emissiveFactor.g,
+				material.emissiveFactor.b
+		};
+		
+		return material::Material::createPBR(
+				core,
+				diffuseImg, diffuseSmp,
+				normalImg, normalSmp,
+				metalRoughImg, metalRoughSmp,
+				occlusionImg, occlusionSmp,
+				emissionImg, emissionSmp,
+				colorFactors,
+				material.normalScale,
+				material.metallicFactor,
+				material.roughnessFactor,
+				material.occlusionStrength,
+				emissionFactors
+		);
+	}
+	
 	void MeshPart::load(const asset::Scene& scene,
 						const asset::VertexGroup &vertexGroup) {
+		Core& core = *(m_scene->m_core);
+		
+		auto vertexBuffer = core.createBuffer<uint8_t>(
+				BufferType::VERTEX, vertexGroup.vertexBuffer.data.size()
+		);
+		
+		vertexBuffer.fill(vertexGroup.vertexBuffer.data);
+		m_vertices = vertexBuffer.getHandle();
+		
+		auto indexBuffer = core.createBuffer<uint8_t>(
+				BufferType::INDEX, vertexGroup.indexBuffer.data.size()
+		);
+		
+		indexBuffer.fill(vertexGroup.indexBuffer.data);
+		m_indices = indexBuffer.getHandle();
+		
 		m_bounds.setMin(glm::vec3(
 				vertexGroup.min.x,
 				vertexGroup.min.y,
@@ -19,9 +129,49 @@ namespace vkcv::scene {
 		
 		if ((vertexGroup.materialIndex >= 0) &&
 			(vertexGroup.materialIndex < scene.materials.size())) {
-			const asset::Material& material = scene.materials[vertexGroup.materialIndex];
+			m_materialIndex = vertexGroup.materialIndex;
+			
+			auto& material = m_scene->m_materials[m_materialIndex];
 			
+			if (0 == material.m_usages++) {
+				material.m_data = loadMaterial(core, scene, scene.materials[vertexGroup.materialIndex]);
+			}
+		} else {
+			m_materialIndex = std::numeric_limits<size_t>::max();
+		}
+	}
+	
+	MeshPart::~MeshPart() {
+		if (m_materialIndex < std::numeric_limits<size_t>::max()) {
+			auto& material = m_scene->m_materials[m_materialIndex];
+			
+			if (material.m_usages > 0) {
+				material.m_usages--;
+			}
+		}
+	}
+	
+	const material::Material & MeshPart::getMaterial() const {
+		if (m_materialIndex < std::numeric_limits<size_t>::max()) {
+			return m_scene->m_materials[m_materialIndex].m_data;
+		} else {
+			static material::Material noMaterial;
+			return noMaterial;
+		}
+	}
+	
+	Mesh::Mesh(Scene* scene) :
+	m_scene(scene) {}
+	
+	void Mesh::load(const asset::Scene &scene, const asset::Mesh &mesh) {
+		for (const auto& vertexGroupIndex : mesh.vertexGroups) {
+			if ((vertexGroupIndex < 0) || (vertexGroupIndex >= scene.vertexGroups.size())) {
+				continue;
+			}
 			
+			MeshPart part (m_scene);
+			part.load(scene, scene.vertexGroups[vertexGroupIndex]);
+			m_parts.push_back(part);
 		}
 	}
 
diff --git a/modules/scene/src/vkcv/scene/Node.cpp b/modules/scene/src/vkcv/scene/Node.cpp
index 51a2497d..c88c9141 100644
--- a/modules/scene/src/vkcv/scene/Node.cpp
+++ b/modules/scene/src/vkcv/scene/Node.cpp
@@ -2,7 +2,20 @@
 #include "vkcv/scene/Node.hpp"
 
 namespace vkcv::scene {
-
-
+	
+	Node::Node(Scene* scene) :
+	m_scene(scene) {}
+	
+	void Node::loadMesh(const asset::Scene &asset_scene, const asset::Mesh &asset_mesh) {
+		Mesh mesh (m_scene);
+		mesh.load(asset_scene, asset_mesh);
+		m_meshes.push_back(mesh);
+	}
+	
+	Node& Node::addNode() {
+		Node node (this->m_scene);
+		m_nodes.push_back(node);
+		return m_nodes.back();
+	}
 
 }
diff --git a/modules/scene/src/vkcv/scene/Scene.cpp b/modules/scene/src/vkcv/scene/Scene.cpp
index a6512e42..8ee56ca7 100644
--- a/modules/scene/src/vkcv/scene/Scene.cpp
+++ b/modules/scene/src/vkcv/scene/Scene.cpp
@@ -6,19 +6,28 @@
 
 namespace vkcv::scene {
 	
-	Scene Scene::create() {
-		return Scene();
+	Scene::Scene(Core* core) :
+	m_core(core) {}
+	
+	Node& Scene::addNode() {
+		Node node (this);
+		m_nodes.push_back(node);
+		return m_nodes.back();
+	}
+	
+	Scene Scene::create(Core& core) {
+		return Scene(&core);
 	}
 	
-	Scene Scene::load(const std::filesystem::path &path) {
+	Scene Scene::load(Core& core, const std::filesystem::path &path) {
 		asset::Scene asset_scene;
 		
 		if (!asset::loadScene(path.string(), asset_scene)) {
 			vkcv_log(LogLevel::WARNING, "Scene could not be loaded")
-			return create();
+			return create(core);
 		}
 		
-		Scene scene = create();
+		Scene scene = create(core);
 		
 		for (const auto& material : asset_scene.materials) {
 			scene.m_materials.push_back({
@@ -26,8 +35,10 @@ namespace vkcv::scene {
 			});
 		}
 		
+		Node& node = scene.addNode();
+		
 		for (const auto& mesh : asset_scene.meshes) {
-			//TODO
+			node.loadMesh(asset_scene, mesh);
 		}
 		
 		return scene;
diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp
index c48b0153..4b43b129 100644
--- a/src/vkcv/Image.cpp
+++ b/src/vkcv/Image.cpp
@@ -61,7 +61,7 @@ namespace vkcv{
 		return m_manager->getImageMipCount(m_handle);
 	}
 
-	void Image::fill(void *data, size_t size) {
+	void Image::fill(const void *data, size_t size) {
 		m_manager->fillImage(m_handle, data, size);
 	}
 
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index a3364ce0..48fe63f7 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -358,7 +358,7 @@ namespace vkcv {
 		}
 	}
 	
-	void ImageManager::fillImage(const ImageHandle& handle, void* data, size_t size)
+	void ImageManager::fillImage(const ImageHandle& handle, const void* data, size_t size)
 	{
 		const uint64_t id = handle.getId();
 		
diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp
index ecba7eb5..2e04a447 100644
--- a/src/vkcv/ImageManager.hpp
+++ b/src/vkcv/ImageManager.hpp
@@ -101,7 +101,7 @@ namespace vkcv {
 			const ImageHandle& handle,
 			vk::CommandBuffer cmdBuffer);
 
-		void fillImage(const ImageHandle& handle, void* data, size_t size);
+		void fillImage(const ImageHandle& handle, const void* data, size_t size);
 		void generateImageMipChainImmediate(const ImageHandle& handle);
 		void recordImageMipChainGenerationToCmdStream(const vkcv::CommandStreamHandle& cmdStream, const ImageHandle& handle);
 		
-- 
GitLab