diff --git a/.gitmodules b/.gitmodules
index cc3bf1fcd2e1eb8117cbcc7222b04f7041fea520..ef5fa3cfcc738f2bc8dedb0ec219903e71500f49 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -28,3 +28,9 @@
 [submodule "modules/upscaling/lib/FidelityFX-FSR"]
 	path = modules/upscaling/lib/FidelityFX-FSR
 	url = https://github.com/GPUOpen-Effects/FidelityFX-FSR.git
+[submodule "lib/Vulkan-Headers"]
+	path = lib/Vulkan-Headers
+	url = https://github.com/KhronosGroup/Vulkan-Headers.git
+[submodule "lib/Vulkan-Hpp"]
+	path = lib/Vulkan-Hpp
+	url = https://github.com/KhronosGroup/Vulkan-Hpp
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 121d499fb2cd337c28524b89ecf1ab9d12607bdf..d47706415b6274a135a4c85aa1010fde2e959105 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -100,6 +100,7 @@ endif()
 include(${vkcv_config}/Sources.cmake)
 
 message(STATUS "Framework:")
+message(" - Includes: [ ${vkcv_includes} ]")
 message(" - Libraries: [ ${vkcv_libraries} ]")
 message(" - Flags: [ ${vkcv_flags} ]")
 
diff --git a/config/lib/Vulkan.cmake b/config/lib/Vulkan.cmake
index e8fe4aee3a949fa7bf9906558f8a0c558eb47cf2..5100d1b78986a7a4b4b6180b1862046cb8b4fc8c 100644
--- a/config/lib/Vulkan.cmake
+++ b/config/lib/Vulkan.cmake
@@ -2,7 +2,26 @@
 find_package(Vulkan REQUIRED)
 
 if (Vulkan_FOUND)
-    list(APPEND vkcv_includes ${Vulkan_INCLUDE_DIR})
+    if (NOT EXISTS ${Vulkan_INCLUDE_DIR}/vulkan/vulkan.h)
+        use_git_submodule("${vkcv_lib_path}/Vulkan-Headers" vulkan_headers_status)
+        
+        if (${vulkan_headers_status})
+            list(APPEND vkcv_includes ${vkcv_lib}/Vulkan-Headers/include)
+        endif()
+    else()
+        list(APPEND vkcv_includes ${Vulkan_INCLUDE_DIR})
+    endif()
+
+    if (NOT EXISTS ${Vulkan_INCLUDE_DIR}/vulkan/vulkan.hpp)
+        use_git_submodule("${vkcv_lib_path}/Vulkan-Hpp" vulkan_hpp_status)
+    
+        if (${vulkan_hpp_status})
+            list(APPEND vkcv_includes ${vkcv_lib}/Vulkan-Hpp)
+        endif()
+    else()
+        list(APPEND vkcv_includes ${Vulkan_INCLUDE_DIR})
+    endif()
+    
     list(APPEND vkcv_libraries ${Vulkan_LIBRARIES})
 
     message(${vkcv_config_msg} " Vulkan  -   ")
diff --git a/lib/Vulkan-Headers b/lib/Vulkan-Headers
new file mode 160000
index 0000000000000000000000000000000000000000..8ba8294c86d0e99fcb457bedbd573dd678ccc9b3
--- /dev/null
+++ b/lib/Vulkan-Headers
@@ -0,0 +1 @@
+Subproject commit 8ba8294c86d0e99fcb457bedbd573dd678ccc9b3
diff --git a/lib/Vulkan-Hpp b/lib/Vulkan-Hpp
new file mode 160000
index 0000000000000000000000000000000000000000..ae1b0c36df0943795cd621a37e7f7bfd00ac958a
--- /dev/null
+++ b/lib/Vulkan-Hpp
@@ -0,0 +1 @@
+Subproject commit ae1b0c36df0943795cd621a37e7f7bfd00ac958a
diff --git a/lib/VulkanMemoryAllocator-Hpp b/lib/VulkanMemoryAllocator-Hpp
index c6c3c665b6a29ae546bdec60606a3ef0757ea108..da6ea76eecf12a1decc76f58a3e096bcc555bd94 160000
--- a/lib/VulkanMemoryAllocator-Hpp
+++ b/lib/VulkanMemoryAllocator-Hpp
@@ -1 +1 @@
-Subproject commit c6c3c665b6a29ae546bdec60606a3ef0757ea108
+Subproject commit da6ea76eecf12a1decc76f58a3e096bcc555bd94
diff --git a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp
index f3f55f6514a58e776abf226fd80805fbf656a809..110a941ff6703e989174eaf311118b10b41491d4 100644
--- a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp
+++ b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp
@@ -171,7 +171,7 @@ namespace vkcv::asset {
 	static std::array<float, 16> calculateModelMatrix(const std::array<float, 3>& translation,
 													  const std::array<float, 3>& scale,
 													  const std::array<float, 4>& rotation,
-													  const std::array<float, 16>& matrix){
+													  const std::array<float, 16>& matrix) {
 		std::array<float, 16> modelMatrix = {
 				1,0,0,0,
 				0,1,0,0,
@@ -185,32 +185,55 @@ namespace vkcv::asset {
 			// translation
 			modelMatrix[3] = translation[0];
 			modelMatrix[7] = translation[1];
-			modelMatrix[11] = translation[2];
+			modelMatrix[11] = -translation[2]; // flip Z to convert from GLTF to Vulkan
 			
 			// rotation and scale
-			auto a = rotation[0];
-			auto q1 = rotation[1];
-			auto q2 = rotation[2];
-			auto q3 = rotation[3];
-	
-			modelMatrix[0] = (2 * (a * a + q1 * q1) - 1) * scale[0];
-			modelMatrix[1] = (2 * (q1 * q2 - a * q3)) * scale[1];
-			modelMatrix[2] = (2 * (q1 * q3 + a * q2)) * scale[2];
-	
-			modelMatrix[4] = (2 * (q1 * q2 + a * q3)) * scale[0];
-			modelMatrix[5] = (2 * (a * a + q2 * q2) - 1) * scale[1];
-			modelMatrix[6] = (2 * (q2 * q3 - a * q1)) * scale[2];
-	
-			modelMatrix[8] = (2 * (q1 * q3 - a * q2)) * scale[0];
-			modelMatrix[9] = (2 * (q2 * q3 + a * q1)) * scale[1];
-			modelMatrix[10] = (2 * (a * a + q3 * q3) - 1) * scale[2];
-	
-			// flip y, because GLTF uses y up, but vulkan -y up
-			modelMatrix[5] *= -1;
+			auto a = rotation[3];
+			auto q1 = rotation[0];
+			auto q2 = rotation[1];
+			auto q3 = rotation[2];
+			
+			auto s = 2 / (a * a + q1 * q1 + q2 * q2 + q3 * q3);
+			
+			auto s1 = scale[0];
+			auto s2 = scale[1];
+			auto s3 = -scale[2]; // flip Z to convert from GLTF to Vulkan
+			
+			modelMatrix[0]  = (1 - s * (q2 * q2 + q3 * q3)) * s1;
+			modelMatrix[1]  = (    s * (q1 * q2 - q3 *  a)) * s2;
+			modelMatrix[2]  = (    s * (q1 * q3 + q2 *  a)) * s3;
+			
+			modelMatrix[4]  = (    s * (q1 * q2 + q3 *  a)) * s1;
+			modelMatrix[5]  = (1 - s * (q1 * q1 + q3 * q3)) * s2;
+			modelMatrix[6]  = (    s * (q2 * q3 - q1 *  a)) * s3;
+			
+			modelMatrix[8]  = (    s * (q1 * q3 - q2 *  a)) * s1;
+			modelMatrix[9]  = (    s * (q2 * q3 + q1 *  a)) * s2;
+			modelMatrix[10] = (1 - s * (q1 * q1 + q2 * q2)) * s3;
 	
 			return modelMatrix;
 		}
 	}
+	
+	static std::array<float, 16> multiplyMatrix(const std::array<float, 16>& matrix,
+												const std::array<float, 16>& other) {
+		std::array<float, 16> result;
+		size_t i, j, k;
+		
+		for (i = 0; i < 4; i++) {
+			for (j = 0; j < 4; j++) {
+				float sum = 0.0f;
+				
+				for (k = 0; k < 4; k++) {
+					sum += matrix[i * 4 + k] * other[k * 4 + j];
+				}
+				
+				result[i * 4 + j] = sum;
+			}
+		}
+		
+		return result;
+	}
 
 	bool Material::hasTexture(const PBRTextureTarget target) const {
 		return textureMask & bitflag(target);
@@ -496,9 +519,23 @@ namespace vkcv::asset {
 	
 		try {
 			if (path.extension() == ".glb") {
-				sceneObjects = fx::gltf::LoadFromBinary(path.string());
+				sceneObjects = fx::gltf::LoadFromBinary(
+						path.string(),
+						{
+								fx::gltf::detail::DefaultMaxBufferCount,
+								fx::gltf::detail::DefaultMaxMemoryAllocation * 16,
+								fx::gltf::detail::DefaultMaxMemoryAllocation * 8
+						}
+				);
 			} else {
-				sceneObjects = fx::gltf::LoadFromText(path.string());
+				sceneObjects = fx::gltf::LoadFromText(
+						path.string(),
+						{
+							fx::gltf::detail::DefaultMaxBufferCount,
+							fx::gltf::detail::DefaultMaxMemoryAllocation,
+							fx::gltf::detail::DefaultMaxMemoryAllocation * 8
+						}
+				);
 			}
 		} catch (const std::system_error& err) {
 			recurseExceptionPrint(err, path.string());
@@ -536,15 +573,54 @@ namespace vkcv::asset {
 				scene.meshes.push_back(mesh);
 			}
 			
-			// This only works if the node has a mesh and it only loads the meshes and ignores cameras and lights
-			for (const auto& node : sceneObjects.nodes) {
+			std::vector< std::array<float, 16> > matrices;
+			std::vector< int32_t > parents;
+			
+			matrices.reserve(sceneObjects.nodes.size());
+			parents.resize(sceneObjects.nodes.size(), -1);
+			
+			for (size_t i = 0; i < sceneObjects.nodes.size(); i++) {
+				const auto &node = sceneObjects.nodes[i];
+				
+				matrices.push_back(calculateModelMatrix(
+						node.translation,
+						node.scale,
+						node.rotation,
+						node.matrix
+				));
+				
+				for (int32_t child : node.children)
+					if ((child >= 0) && (child < parents.size()))
+						parents[child] = static_cast<int32_t>(i);
+			}
+			
+			std::vector< std::array<float, 16> > final_matrices;
+			final_matrices.reserve(matrices.size());
+			
+			for (size_t i = 0; i < matrices.size(); i++) {
+				std::vector<int> order;
+				order.push_back(static_cast<int32_t>(i));
+				
+				while (parents[ order[order.size() - 1] ] >= 0)
+					order.push_back(parents[ order[order.size() - 1] ]);
+				
+				std::array<float, 16> matrix = matrices[ order[order.size() - 1] ];
+				
+				for (size_t j = order.size() - 1; j > 0; j--) {
+					const auto id = order[j - 1];
+					const std::array<float, 16> matrix_other = matrices[ id ];
+					
+					matrix = multiplyMatrix(matrix, matrix_other);
+				}
+				
+				final_matrices.push_back(matrix);
+			}
+			
+			for (size_t i = 0; i < sceneObjects.nodes.size(); i++) {
+				const auto &node = sceneObjects.nodes[i];
+				
 				if ((node.mesh >= 0) && (node.mesh < scene.meshes.size())) {
-					scene.meshes[node.mesh].modelMatrix = calculateModelMatrix(
-							node.translation,
-							node.scale,
-							node.rotation,
-							node.matrix
-					);
+					scene.meshes[node.mesh].modelMatrix = final_matrices[i];
 				}
 			}
 		}
diff --git a/modules/scene/src/vkcv/scene/MeshPart.cpp b/modules/scene/src/vkcv/scene/MeshPart.cpp
index f9c87f0a0587bd8a897e42d5b8536fe07c61c749..50d14ed49d43496ada3853034be1455b044bd902 100644
--- a/modules/scene/src/vkcv/scene/MeshPart.cpp
+++ b/modules/scene/src/vkcv/scene/MeshPart.cpp
@@ -71,8 +71,23 @@ namespace vkcv::scene {
 		if (*this) {
 			const auto& material = getMaterial();
 			
+			IndexBitCount indexBitCount;
+			
+			switch (vertexGroup.indexBuffer.type) {
+				case asset::IndexType::UINT16:
+					indexBitCount = IndexBitCount::Bit16;
+					break;
+				case asset::IndexType::UINT32:
+					indexBitCount = IndexBitCount::Bit32;
+					break;
+				default:
+					indexBitCount = IndexBitCount::Bit16;
+					vkcv_log(LogLevel::WARNING, "Unsupported index type!");
+					break;
+			}
+			
 			drawcalls.push_back(DrawcallInfo(
-					vkcv::Mesh(m_vertexBindings, indexBuffer.getVulkanHandle(), m_indexCount),
+					vkcv::Mesh(m_vertexBindings, indexBuffer.getVulkanHandle(), m_indexCount, indexBitCount),
 					{ DescriptorSetUsage(0, material.getDescriptorSet()) }
 			));
 		}
diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt
index ffe8546c961697869354993539697b118fa8d6be..997c907fb26db2344f028ff5eaec8de3f69038b7 100644
--- a/projects/CMakeLists.txt
+++ b/projects/CMakeLists.txt
@@ -5,6 +5,7 @@ include(${vkcv_config_ext}/ProjectFix.cmake)
 add_subdirectory(first_triangle)
 add_subdirectory(first_mesh)
 add_subdirectory(first_scene)
+add_subdirectory(head_demo)
 add_subdirectory(particle_simulation)
 add_subdirectory(rtx_ambient_occlusion)
 add_subdirectory(sph)
diff --git a/projects/head_demo/.gitignore b/projects/head_demo/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..151f56c5bb7098a3beb7b8e049c0ade02914cd91
--- /dev/null
+++ b/projects/head_demo/.gitignore
@@ -0,0 +1 @@
+head_demo
\ No newline at end of file
diff --git a/projects/head_demo/CMakeLists.txt b/projects/head_demo/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..09e5f693d6c6ddc243d884a74981bdb980fb9065
--- /dev/null
+++ b/projects/head_demo/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 3.16)
+project(head_demo)
+
+# setting c++ standard for the project
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+# this should fix the execution path to load local files from the project
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+
+# adding source files to the project
+add_executable(head_demo src/main.cpp)
+fix_project(head_demo)
+
+# including headers of dependencies and the VkCV framework
+target_include_directories(head_demo SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_scene_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include})
+
+# linking with libraries from all dependencies and the VkCV framework
+target_link_libraries(head_demo vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_scene vkcv_shader_compiler vkcv_gui)
diff --git a/projects/head_demo/assets/shaders/clip.inc b/projects/head_demo/assets/shaders/clip.inc
new file mode 100644
index 0000000000000000000000000000000000000000..edb5ac3ccfe5d7f6ad1e59dd88bee4446b85118f
--- /dev/null
+++ b/projects/head_demo/assets/shaders/clip.inc
@@ -0,0 +1,19 @@
+
+#define CLIP_SCALE 10000.0f
+
+vec4 clipPosition(vec4 pos) {
+    return vec4(
+        max(clipX, pos.x),
+        max(clipY, pos.y),
+        max(clipZ, pos.z),
+        1.0f / CLIP_SCALE
+    );
+}
+
+vec4 clipByLimit(vec4 pos) {
+    if (pos.x / pos.w < clipLimit) {
+        return vec4(pos.xyz / pos.w, 1.0f);
+    } else {
+        return vec4(clipLimit, pos.y / pos.w, pos.z / pos.w, 1.0f);
+    }
+}
diff --git a/projects/head_demo/assets/shaders/red.frag b/projects/head_demo/assets/shaders/red.frag
new file mode 100644
index 0000000000000000000000000000000000000000..3991f9396323bb25c57f03e5ba7b5f9a280ed556
--- /dev/null
+++ b/projects/head_demo/assets/shaders/red.frag
@@ -0,0 +1,10 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec3 passNormal;
+
+layout(location = 0) out vec3 outColor;
+
+void main()	{
+    outColor = (vec3(0.3f, 0, 0) + max(dot(passNormal, vec3(1.0f, -1.0f, 0.5f)), 0.0f) * vec3(0.7f, 0, 0));
+}
\ No newline at end of file
diff --git a/projects/head_demo/assets/shaders/shader.frag b/projects/head_demo/assets/shaders/shader.frag
new file mode 100644
index 0000000000000000000000000000000000000000..b8756a6b84af4e93f9ac932ec01d28e4f2e9638b
--- /dev/null
+++ b/projects/head_demo/assets/shaders/shader.frag
@@ -0,0 +1,10 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec3 passNormal;
+
+layout(location = 0) out vec3 outColor;
+
+void main()	{
+	outColor = (vec3(0.3f) + max(dot(passNormal, vec3(1.0f, -1.0f, 0.5f)), 0.0f) * vec3(0.7f));
+}
\ No newline at end of file
diff --git a/projects/head_demo/assets/shaders/shader.geom b/projects/head_demo/assets/shaders/shader.geom
new file mode 100644
index 0000000000000000000000000000000000000000..275b300ee3466e117876aa46a6ea1cde11f6f17d
--- /dev/null
+++ b/projects/head_demo/assets/shaders/shader.geom
@@ -0,0 +1,53 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_GOOGLE_include_directive : enable
+
+layout(triangles) in;
+layout(triangle_strip, max_vertices = 3) out;
+
+layout(location = 0) in vec3 geomNormal[];
+
+layout(location = 0) out vec3 passNormal;
+
+layout(set=1, binding=0) uniform clipBuffer {
+    float clipLimit;
+    float clipX;
+    float clipY;
+    float clipZ;
+};
+
+layout( push_constant ) uniform constants{
+    mat4 mvp;
+};
+
+#include "clip.inc"
+
+void main()	{
+    vec4 v0 = gl_in[0].gl_Position;
+    vec4 v1 = gl_in[1].gl_Position;
+    vec4 v2 = gl_in[2].gl_Position;
+
+    v0 = clipPosition(v0 / CLIP_SCALE);
+    v1 = clipPosition(v1 / CLIP_SCALE);
+    v2 = clipPosition(v2 / CLIP_SCALE);
+
+    float dx = abs(v0.x - clipX) + abs(v1.x - clipX) + abs(v2.x - clipX);
+    float dy = abs(v0.y - clipY) + abs(v1.y - clipY) + abs(v2.y - clipY);
+    float dz = abs(v0.z - clipZ) + abs(v1.z - clipZ) + abs(v2.z - clipZ);
+
+    if (dx * dy * dz > 0.0f) {
+        gl_Position = mvp * (v0 * CLIP_SCALE);
+        passNormal = geomNormal[0];
+        EmitVertex();
+
+        gl_Position = mvp * (v1 * CLIP_SCALE);
+        passNormal = geomNormal[1];
+        EmitVertex();
+
+        gl_Position = mvp * (v2 * CLIP_SCALE);
+        passNormal = geomNormal[2];
+        EmitVertex();
+
+        EndPrimitive();
+    }
+}
diff --git a/projects/head_demo/assets/shaders/shader.vert b/projects/head_demo/assets/shaders/shader.vert
new file mode 100644
index 0000000000000000000000000000000000000000..26e43e9c89dc2ffb2355d60be8288fa9832f8baa
--- /dev/null
+++ b/projects/head_demo/assets/shaders/shader.vert
@@ -0,0 +1,12 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec3 inPosition;
+layout(location = 1) in vec3 inNormal;
+
+layout(location = 0) out vec3 geomNormal;
+
+void main()	{
+	gl_Position = vec4(inPosition, 1.0);
+	geomNormal  = inNormal;
+}
\ No newline at end of file
diff --git a/projects/head_demo/assets/shaders/wired.geom b/projects/head_demo/assets/shaders/wired.geom
new file mode 100644
index 0000000000000000000000000000000000000000..0063360547b859fc7a305b5786532654b5b35f34
--- /dev/null
+++ b/projects/head_demo/assets/shaders/wired.geom
@@ -0,0 +1,51 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_GOOGLE_include_directive : enable
+
+layout(triangles) in;
+layout(points, max_vertices = 1) out;
+
+layout(location = 0) in vec3 geomNormal[];
+
+layout(location = 0) out vec3 passNormal;
+
+layout(set=1, binding=0) uniform clipBuffer {
+    float clipLimit;
+    float clipX;
+    float clipY;
+    float clipZ;
+};
+
+layout( push_constant ) uniform constants{
+    mat4 mvp;
+};
+
+#include "clip.inc"
+
+void main()	{
+    vec4 v0 = gl_in[0].gl_Position;
+    vec4 v1 = gl_in[1].gl_Position;
+    vec4 v2 = gl_in[2].gl_Position;
+
+    v0 = clipPosition(v0 / CLIP_SCALE);
+    v1 = clipPosition(v1 / CLIP_SCALE);
+    v2 = clipPosition(v2 / CLIP_SCALE);
+
+    float dx = abs(v0.x - clipX) + abs(v1.x - clipX) + abs(v2.x - clipX);
+    float dy = abs(v0.y - clipY) + abs(v1.y - clipY) + abs(v2.y - clipY);
+    float dz = abs(v0.z - clipZ) + abs(v1.z - clipZ) + abs(v2.z - clipZ);
+
+    if (dx * dy * dz <= 0.0f) {
+        v0 = clipByLimit(mvp * gl_in[0].gl_Position);
+        v1 = clipByLimit(mvp * gl_in[1].gl_Position);
+        v2 = clipByLimit(mvp * gl_in[2].gl_Position);
+
+        if ((v0.x < clipLimit) || (v1.x < clipLimit) || (v2.x < clipLimit)) {
+            gl_Position = (v0 + v1 + v2) / 3;
+            passNormal = (geomNormal[0] + geomNormal[1] + geomNormal[2]) / 3;
+            EmitVertex();
+
+            EndPrimitive();
+        }
+    }
+}
diff --git a/projects/head_demo/assets/skull/license.txt b/projects/head_demo/assets/skull/license.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6816d6d659087b54dfc6b8e0c2c0e158daa001f0
--- /dev/null
+++ b/projects/head_demo/assets/skull/license.txt
@@ -0,0 +1,11 @@
+Model Information:
+* title:	The Anatomy of the Human Skull
+* source:	https://sketchfab.com/3d-models/the-anatomy-of-the-human-skull-baf6ac7b781a46218dca2b59dee58817
+* author:	HannahNewey (https://sketchfab.com/HannahNewey)
+
+Model License:
+* license type:	CC-BY-NC-SA-4.0 (http://creativecommons.org/licenses/by-nc-sa/4.0/)
+* requirements:	Author must be credited. No commercial use. Modified versions must have the same license.
+
+If you use this 3D model in your project be sure to copy paste this credit wherever you share it:
+This work is based on "The Anatomy of the Human Skull" (https://sketchfab.com/3d-models/the-anatomy-of-the-human-skull-baf6ac7b781a46218dca2b59dee58817) by HannahNewey (https://sketchfab.com/HannahNewey) licensed under CC-BY-NC-SA-4.0 (http://creativecommons.org/licenses/by-nc-sa/4.0/)
\ No newline at end of file
diff --git a/projects/head_demo/assets/skull/scene.bin b/projects/head_demo/assets/skull/scene.bin
new file mode 100644
index 0000000000000000000000000000000000000000..e995091c9d2f10b15959a65d3b0e0de3c9edb0e4
--- /dev/null
+++ b/projects/head_demo/assets/skull/scene.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:df3cb5e715426a8e7a3c4883eb7c53022e09011ded9ae385cd6bc59824129ed8
+size 78176672
diff --git a/projects/head_demo/assets/skull/scene.gltf b/projects/head_demo/assets/skull/scene.gltf
new file mode 100644
index 0000000000000000000000000000000000000000..6a2b75fa5af1e826bf3851db576dd3051e7b77c3
--- /dev/null
+++ b/projects/head_demo/assets/skull/scene.gltf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8cd4f673e0a555dc138b96e07be60546a799f9ef11837870eae27b16ffd5c1c7
+size 36442
diff --git a/projects/head_demo/assets/skull_scaled/scene.bin b/projects/head_demo/assets/skull_scaled/scene.bin
new file mode 100644
index 0000000000000000000000000000000000000000..3edc8865ed5465c33cb8c9d2f2b0eb83b1a5b599
--- /dev/null
+++ b/projects/head_demo/assets/skull_scaled/scene.bin
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:685e971ce4e99be286464c56a373e528dc507ce18b5bfdff45f90db27e49176b
+size 56596296
diff --git a/projects/head_demo/assets/skull_scaled/scene.gltf b/projects/head_demo/assets/skull_scaled/scene.gltf
new file mode 100644
index 0000000000000000000000000000000000000000..72087f182e4cd1ce1a73f7af9db1d36608df2029
--- /dev/null
+++ b/projects/head_demo/assets/skull_scaled/scene.gltf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f05a4c8f979dab2793540bafe2b392133d64af028a0ff97f0159dbc52fc20230
+size 4843
diff --git a/projects/head_demo/src/main.cpp b/projects/head_demo/src/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c25ed49f989f47b63e4e7f828550016d6a2fbaeb
--- /dev/null
+++ b/projects/head_demo/src/main.cpp
@@ -0,0 +1,260 @@
+#include <iostream>
+#include <vkcv/Core.hpp>
+#include <GLFW/glfw3.h>
+#include <vkcv/camera/CameraManager.hpp>
+#include <vkcv/gui/GUI.hpp>
+#include <chrono>
+#include <vkcv/asset/asset_loader.hpp>
+#include <vkcv/shader/GLSLCompiler.hpp>
+#include <vkcv/scene/Scene.hpp>
+
+int main(int argc, const char** argv) {
+	const char* applicationName = "First Scene";
+	
+	uint32_t windowWidth = 800;
+	uint32_t windowHeight = 600;
+	
+	vkcv::Core core = vkcv::Core::create(
+			applicationName,
+			VK_MAKE_VERSION(0, 0, 1),
+			{vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute},
+			{ VK_KHR_SWAPCHAIN_EXTENSION_NAME }
+	);
+	vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, false);
+	vkcv::Window& window = core.getWindow(windowHandle);
+	vkcv::camera::CameraManager cameraManager(window);
+	
+	vkcv::gui::GUI gui (core, windowHandle);
+	
+	uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
+	uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL);
+	
+	cameraManager.getCamera(camIndex0).setPosition(glm::vec3(15.5f, 0, 0));
+	cameraManager.getCamera(camIndex0).setNearFar(0.1f, 30.0f);
+	
+	cameraManager.getCamera(camIndex1).setNearFar(0.1f, 30.0f);
+	
+	vkcv::scene::Scene scene = vkcv::scene::Scene::load(core, std::filesystem::path(
+			argc > 1 ? argv[1] : "assets/skull_scaled/scene.gltf"
+	));
+	
+	const vkcv::AttachmentDescription present_color_attachment0(
+			vkcv::AttachmentOperation::STORE,
+			vkcv::AttachmentOperation::CLEAR,
+			core.getSwapchain(windowHandle).getFormat()
+	);
+	
+	const vkcv::AttachmentDescription depth_attachment0(
+			vkcv::AttachmentOperation::STORE,
+			vkcv::AttachmentOperation::CLEAR,
+			vk::Format::eD32Sfloat
+	);
+	
+	vkcv::PassConfig scenePassDefinition({ present_color_attachment0, depth_attachment0 });
+	vkcv::PassHandle scenePass = core.createPass(scenePassDefinition);
+	
+	const vkcv::AttachmentDescription present_color_attachment1(
+			vkcv::AttachmentOperation::STORE,
+			vkcv::AttachmentOperation::LOAD,
+			core.getSwapchain(windowHandle).getFormat()
+	);
+	
+	const vkcv::AttachmentDescription depth_attachment1(
+			vkcv::AttachmentOperation::STORE,
+			vkcv::AttachmentOperation::LOAD,
+			vk::Format::eD32Sfloat
+	);
+	
+	vkcv::PassConfig linePassDefinition({ present_color_attachment1, depth_attachment1 });
+	vkcv::PassHandle linePass = core.createPass(linePassDefinition);
+	
+	if ((!scenePass) || (!linePass)) {
+		std::cout << "Error. Could not create renderpass. Exiting." << std::endl;
+		return EXIT_FAILURE;
+	}
+	
+	vkcv::ShaderProgram sceneShaderProgram;
+	vkcv::ShaderProgram lineShaderProgram;
+	vkcv::shader::GLSLCompiler compiler;
+	
+	compiler.compileProgram(sceneShaderProgram, {
+			{ vkcv::ShaderStage::VERTEX, "assets/shaders/shader.vert" },
+			{ vkcv::ShaderStage::GEOMETRY, "assets/shaders/shader.geom" },
+			{ vkcv::ShaderStage::FRAGMENT, "assets/shaders/shader.frag" }
+	}, nullptr);
+	
+	compiler.compileProgram(lineShaderProgram, {
+			{ vkcv::ShaderStage::VERTEX, "assets/shaders/shader.vert" },
+			{ vkcv::ShaderStage::GEOMETRY, "assets/shaders/wired.geom" },
+			{ vkcv::ShaderStage::FRAGMENT, "assets/shaders/red.frag" }
+	}, nullptr);
+	
+	const std::vector<vkcv::VertexAttachment> vertexAttachments = sceneShaderProgram.getVertexAttachments();
+	std::vector<vkcv::VertexBinding> bindings;
+	for (size_t i = 0; i < vertexAttachments.size(); i++) {
+		bindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] }));
+	}
+	
+	const auto& clipBindings = sceneShaderProgram.getReflectedDescriptors().at(1);
+	
+	auto clipDescriptorSetLayout = core.createDescriptorSetLayout(clipBindings);
+	auto clipDescriptorSet = core.createDescriptorSet(clipDescriptorSetLayout);
+	
+	float clipLimit = 1.0f;
+	float clipX = 0.0f;
+	float clipY = 0.0f;
+	float clipZ = 0.0f;
+	
+	auto clipBuffer = core.createBuffer<float>(vkcv::BufferType::UNIFORM, 4);
+	clipBuffer.fill({ clipLimit, -clipX, -clipY, -clipZ });
+	
+	vkcv::DescriptorWrites clipWrites;
+	clipWrites.uniformBufferWrites = {
+			vkcv::BufferDescriptorWrite(0, clipBuffer.getHandle())
+	};
+	
+	core.writeDescriptorSet(clipDescriptorSet, clipWrites);
+	
+	float mouseX = -0.0f;
+	bool dragLimit = false;
+	
+	window.e_mouseMove.add([&](double x, double y) {
+		double cx = (x - window.getWidth() * 0.5);
+		double dx = cx / window.getWidth();
+		
+		mouseX = 2.0f * static_cast<float>(dx);
+		
+		if (dragLimit) {
+			clipLimit = mouseX;
+		}
+	});
+	
+	window.e_mouseButton.add([&](int button, int action, int mods) {
+		if ((std::abs(mouseX - clipLimit) < 0.1f) && (action == GLFW_PRESS)) {
+			dragLimit = true;
+		} else {
+			dragLimit = false;
+		}
+	});
+	
+	const vkcv::VertexLayout sceneLayout(bindings);
+	
+	const auto& material0 = scene.getMaterial(0);
+	
+	const vkcv::GraphicsPipelineConfig scenePipelineDefinition{
+			sceneShaderProgram,
+			UINT32_MAX,
+			UINT32_MAX,
+			scenePass,
+			{sceneLayout},
+			{ material0.getDescriptorSetLayout(), clipDescriptorSetLayout },
+			true
+	};
+	
+	const vkcv::GraphicsPipelineConfig linePipelineDefinition{
+			lineShaderProgram,
+			UINT32_MAX,
+			UINT32_MAX,
+			linePass,
+			{sceneLayout},
+			{ material0.getDescriptorSetLayout(), clipDescriptorSetLayout },
+			true
+	};
+	
+	vkcv::GraphicsPipelineHandle scenePipeline = core.createGraphicsPipeline(scenePipelineDefinition);
+	vkcv::GraphicsPipelineHandle linePipeline = core.createGraphicsPipeline(linePipelineDefinition);
+	
+	if ((!scenePipeline) || (!linePipeline)) {
+		std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl;
+		return EXIT_FAILURE;
+	}
+	
+	auto swapchainExtent = core.getSwapchain(windowHandle).getExtent();
+	
+	vkcv::ImageHandle depthBuffer = core.createImage(
+			vk::Format::eD32Sfloat,
+			swapchainExtent.width,
+			swapchainExtent.height
+	).getHandle();
+	
+	const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
+	
+	auto start = std::chrono::system_clock::now();
+	while (vkcv::Window::hasOpenWindow()) {
+		vkcv::Window::pollEvents();
+		
+		if(window.getHeight() == 0 || window.getWidth() == 0)
+			continue;
+		
+		uint32_t swapchainWidth, swapchainHeight;
+		if (!core.beginFrame(swapchainWidth, swapchainHeight,windowHandle)) {
+			continue;
+		}
+		
+		if ((swapchainWidth != swapchainExtent.width) || ((swapchainHeight != swapchainExtent.height))) {
+			depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight).getHandle();
+			
+			swapchainExtent.width = swapchainWidth;
+			swapchainExtent.height = swapchainHeight;
+		}
+		
+		auto end = std::chrono::system_clock::now();
+		auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
+		
+		start = end;
+		cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
+		
+		clipBuffer.fill({ clipLimit, -clipX, -clipY, -clipZ });
+		
+		const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
+		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
+		
+		auto recordMesh = [&](const glm::mat4& MVP, const glm::mat4& M,
+							 vkcv::PushConstants &pushConstants,
+							 vkcv::DrawcallInfo& drawcallInfo) {
+			pushConstants.appendDrawcall(MVP);
+			drawcallInfo.descriptorSets.push_back(
+					vkcv::DescriptorSetUsage(1, clipDescriptorSet)
+			);
+		};
+		
+		scene.recordDrawcalls(cmdStream,
+							  cameraManager.getActiveCamera(),
+							  scenePass,
+							  scenePipeline,
+							  sizeof(glm::mat4),
+							  recordMesh,
+							  renderTargets,
+							  windowHandle);
+		
+		scene.recordDrawcalls(cmdStream,
+							  cameraManager.getActiveCamera(),
+							  linePass,
+							  linePipeline,
+							  sizeof(glm::mat4),
+							  recordMesh,
+							  renderTargets,
+							  windowHandle);
+		
+		core.prepareSwapchainImageForPresent(cmdStream);
+		core.submitCommandStream(cmdStream);
+		
+		auto stop = std::chrono::system_clock::now();
+		auto kektime = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
+		
+		gui.beginGUI();
+		
+		ImGui::Begin("Settings");
+		ImGui::SliderFloat("Clip X", &clipX, -1.0f, 1.0f);
+		ImGui::SliderFloat("Clip Y", &clipY, -1.0f, 1.0f);
+		ImGui::SliderFloat("Clip Z", &clipZ, -1.0f, 1.0f);
+		ImGui::Text("Mesh by HannahNewey (https://sketchfab.com/HannahNewey)");
+		ImGui::End();
+		
+		gui.endGUI();
+		
+		core.endFrame(windowHandle);
+	}
+	
+	return 0;
+}
diff --git a/src/vkcv/Context.cpp b/src/vkcv/Context.cpp
index f7cd67eb756f353d9eb98c4768e2b0d74bbc1ab6..8988e479f0f57693c49ab3dbfc40f056a4cc4d8c 100644
--- a/src/vkcv/Context.cpp
+++ b/src/vkcv/Context.cpp
@@ -341,25 +341,16 @@ namespace vkcv
 
 		vma::AllocatorCreateFlags vmaFlags;
 		const vma::AllocatorCreateInfo allocatorCreateInfo (
-				vma::AllocatorCreateFlags(),
+				vmaFlags,
 				physicalDevice,
 				device,
 				0,
 				nullptr,
 				nullptr,
-				0,
-				nullptr,
 				nullptr,
 				nullptr,
 				instance,
-				
-				/* Uses default version when set to 0 (currently VK_VERSION_1_0):
-				 *
-				 * The reason for this is that the allocator restricts the allowed version
-				 * to be at maximum VK_VERSION_1_1 which is already less than
-				 * VK_HEADER_VERSION_COMPLETE at most platforms.
-				 * */
-				0
+				VK_HEADER_VERSION_COMPLETE
 		);
 		
 		vma::Allocator allocator = vma::createAllocator(allocatorCreateInfo);