diff --git a/modules/scene/include/vkcv/scene/MeshPart.hpp b/modules/scene/include/vkcv/scene/MeshPart.hpp
index 8d77efd7a112ea92a6c4192e5a5cf43cea53e800..0114398cd60be96ee4071ab1d1afe9d711618ca0 100644
--- a/modules/scene/include/vkcv/scene/MeshPart.hpp
+++ b/modules/scene/include/vkcv/scene/MeshPart.hpp
@@ -36,11 +36,6 @@ namespace vkcv::scene {
          */
 		BufferHandle m_vertices;
 
-        /**
-         * The list of the used vertex buffer bindings.
-         */
-		std::vector<VertexBufferBinding> m_vertexBindings;
-
         /**
          * The buffer handle of its index buffer.
          */
@@ -50,6 +45,11 @@ namespace vkcv::scene {
          * The amount of indices in its index buffer.
          */
 		size_t m_indexCount;
+		
+		/**
+		 * The descriptor set handle of its vertex data.
+		 */
+		DescriptorSetHandle m_descriptorSet;
 
         /**
          * Axis aligned bounding box of the mesh part.
diff --git a/modules/scene/include/vkcv/scene/Scene.hpp b/modules/scene/include/vkcv/scene/Scene.hpp
index c482b464a8a24e83f1ba12d8294749d7b0cae48a..12323fbfbaea7c8d02d4537e97412511d81591b4 100644
--- a/modules/scene/include/vkcv/scene/Scene.hpp
+++ b/modules/scene/include/vkcv/scene/Scene.hpp
@@ -54,6 +54,11 @@ namespace vkcv::scene {
          * A list of nodes in the first level of the scene graph.
          */
 		std::vector<Node> m_nodes;
+		
+		/**
+		 * A handle to the descriptor set layout used by meshes in this scene.
+		 */
+		DescriptorSetLayoutHandle m_descriptorSetLayout;
 
         /**
          * Constructor of a scene instance with a given Core instance.
@@ -152,6 +157,13 @@ namespace vkcv::scene {
          */
 		[[nodiscard]]
 		const material::Material& getMaterial(size_t index) const;
+		
+		/**
+         * Returns the descriptor set layout handle used by the meshes in the scene.
+         * @return Descriptor set layout handle
+         */
+		[[nodiscard]]
+		const DescriptorSetLayoutHandle& getDescriptorSetLayout() const;
 
         /**
          * Record drawcalls of all meshes of this scene with CPU-side frustum culling.
diff --git a/modules/scene/src/vkcv/scene/MeshPart.cpp b/modules/scene/src/vkcv/scene/MeshPart.cpp
index 50d14ed49d43496ada3853034be1455b044bd902..e220f52e37f6ff9cba50709e0eac8efb8746d390 100644
--- a/modules/scene/src/vkcv/scene/MeshPart.cpp
+++ b/modules/scene/src/vkcv/scene/MeshPart.cpp
@@ -4,12 +4,17 @@
 
 namespace vkcv::scene {
 	
+	struct vertex_t {
+		float positionU [4];
+		float normalV [4];
+	};
+	
 	MeshPart::MeshPart(Scene& scene) :
 	m_scene(scene),
 	m_vertices(),
-	m_vertexBindings(),
 	m_indices(),
 	m_indexCount(0),
+	m_descriptorSet(),
 	m_bounds(),
 	m_materialIndex(std::numeric_limits<size_t>::max()) {}
 	
@@ -18,23 +23,51 @@ namespace vkcv::scene {
 						std::vector<DrawcallInfo>& drawcalls) {
 		Core& core = *(m_scene.m_core);
 		
-		auto vertexBuffer = core.createBuffer<uint8_t>(
-				BufferType::VERTEX, vertexGroup.vertexBuffer.data.size()
+		auto vertexBuffer = core.createBuffer<vertex_t>(
+				BufferType::STORAGE, vertexGroup.numVertices
 		);
 		
-		vertexBuffer.fill(vertexGroup.vertexBuffer.data);
-		m_vertices = vertexBuffer.getHandle();
-		
-		auto attributes = vertexGroup.vertexBuffer.attributes;
-		
-		std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
-			return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
-		});
+		std::vector<vertex_t> vertices;
+		vertices.reserve(vertexBuffer.getCount());
 		
-		for (const auto& attribute : attributes) {
-			m_vertexBindings.emplace_back(attribute.offset, vertexBuffer.getVulkanHandle());
+		for (const auto& attribute : vertexGroup.vertexBuffer.attributes) {
+			if (attribute.componentType != vkcv::asset::ComponentType::FLOAT32) {
+				continue;
+			}
+			
+			size_t offset = attribute.offset;
+			
+			for (size_t i = 0; i < vertexBuffer.getCount(); i++) {
+				const auto *data = reinterpret_cast<const float*>(
+						vertexGroup.vertexBuffer.data.data() + offset
+				);
+				
+				switch (attribute.type) {
+					case vkcv::asset::PrimitiveType::POSITION:
+						memcpy(vertices[i].positionU, data, sizeof(float) * attribute.componentCount);
+						break;
+					case vkcv::asset::PrimitiveType::NORMAL:
+						memcpy(vertices[i].normalV, data, sizeof(float) * attribute.componentCount);
+						break;
+					case vkcv::asset::PrimitiveType::TEXCOORD_0:
+						if (attribute.componentCount != 2) {
+							break;
+						}
+						
+						vertices[i].positionU[3] = data[0];
+						vertices[i].normalV[3] = data[1];
+						break;
+					default:
+						break;
+				}
+				
+				offset += attribute.stride;
+			}
 		}
 		
+		vertexBuffer.fill(vertices);
+		m_vertices = vertexBuffer.getHandle();
+		
 		auto indexBuffer = core.createBuffer<uint8_t>(
 				BufferType::INDEX, vertexGroup.indexBuffer.data.size()
 		);
@@ -43,6 +76,12 @@ namespace vkcv::scene {
 		m_indices = indexBuffer.getHandle();
 		m_indexCount = vertexGroup.numIndices;
 		
+		m_descriptorSet = core.createDescriptorSet(m_scene.getDescriptorSetLayout());
+		
+		DescriptorWrites writes;
+		writes.writeStorageBuffer(0, m_vertices);
+		core.writeDescriptorSet(m_descriptorSet, writes);
+		
 		m_bounds.setMin(glm::vec3(
 				vertexGroup.min.x,
 				vertexGroup.min.y,
@@ -74,6 +113,9 @@ namespace vkcv::scene {
 			IndexBitCount indexBitCount;
 			
 			switch (vertexGroup.indexBuffer.type) {
+				case asset::IndexType::UINT8:
+					indexBitCount = IndexBitCount::Bit8;
+					break;
 				case asset::IndexType::UINT16:
 					indexBitCount = IndexBitCount::Bit16;
 					break;
@@ -87,8 +129,11 @@ namespace vkcv::scene {
 			}
 			
 			drawcalls.push_back(DrawcallInfo(
-					vkcv::Mesh(m_vertexBindings, indexBuffer.getVulkanHandle(), m_indexCount, indexBitCount),
-					{ DescriptorSetUsage(0, material.getDescriptorSet()) }
+					vkcv::Mesh(indexBuffer.getVulkanHandle(), m_indexCount, indexBitCount),
+					{
+						DescriptorSetUsage(0, m_descriptorSet),
+						DescriptorSetUsage(1, material.getDescriptorSet())
+					}
 			));
 		}
 	}
@@ -100,9 +145,9 @@ namespace vkcv::scene {
 	MeshPart::MeshPart(const MeshPart &other) :
 			m_scene(other.m_scene),
 			m_vertices(other.m_vertices),
-			m_vertexBindings(other.m_vertexBindings),
 			m_indices(other.m_indices),
 			m_indexCount(other.m_indexCount),
+			m_descriptorSet(other.m_descriptorSet),
 			m_bounds(other.m_bounds),
 			m_materialIndex(other.m_materialIndex) {
 		m_scene.increaseMaterialUsage(m_materialIndex);
@@ -111,9 +156,9 @@ namespace vkcv::scene {
 	MeshPart::MeshPart(MeshPart &&other) noexcept :
 			m_scene(other.m_scene),
 			m_vertices(other.m_vertices),
-			m_vertexBindings(other.m_vertexBindings),
 			m_indices(other.m_indices),
 			m_indexCount(other.m_indexCount),
+			m_descriptorSet(other.m_descriptorSet),
 			m_bounds(other.m_bounds),
 			m_materialIndex(other.m_materialIndex) {
 		m_scene.increaseMaterialUsage(m_materialIndex);
@@ -125,9 +170,9 @@ namespace vkcv::scene {
 		}
 		
 		m_vertices = other.m_vertices;
-		m_vertexBindings = other.m_vertexBindings;
 		m_indices = other.m_indices;
 		m_indexCount = other.m_indexCount;
+		m_descriptorSet = other.m_descriptorSet;
 		m_bounds = other.m_bounds;
 		m_materialIndex = other.m_materialIndex;
 		
@@ -136,9 +181,9 @@ namespace vkcv::scene {
 	
 	MeshPart &MeshPart::operator=(MeshPart &&other) noexcept {
 		m_vertices = other.m_vertices;
-		m_vertexBindings = other.m_vertexBindings;
 		m_indices = other.m_indices;
 		m_indexCount = other.m_indexCount;
+		m_descriptorSet = other.m_descriptorSet;
 		m_bounds = other.m_bounds;
 		m_materialIndex = other.m_materialIndex;
 		
diff --git a/modules/scene/src/vkcv/scene/Scene.cpp b/modules/scene/src/vkcv/scene/Scene.cpp
index 9a8ded0f70880125ed1efeda9a6845e1dc735d22..629f44d95babfa0ce4f2dadd0630093c58b1cb31 100644
--- a/modules/scene/src/vkcv/scene/Scene.cpp
+++ b/modules/scene/src/vkcv/scene/Scene.cpp
@@ -8,10 +8,28 @@
 
 namespace vkcv::scene {
 	
+	static DescriptorBindings getDescriptorBindings() {
+		DescriptorBindings bindings = {};
+		
+		auto binding_0 = DescriptorBinding {
+				0,
+				DescriptorType::STORAGE_BUFFER,
+				1,
+				ShaderStage::VERTEX,
+				false,
+				false
+		};
+		
+		bindings.insert(std::make_pair(0, binding_0));
+		
+		return bindings;
+	}
+	
 	Scene::Scene(Core* core) :
 	m_core(core),
 	m_materials(),
-	m_nodes() {}
+	m_nodes(),
+	m_descriptorSetLayout(core->createDescriptorSetLayout(getDescriptorBindings())) {}
 	
 	Scene::~Scene() {
 		m_nodes.clear();
@@ -21,7 +39,8 @@ namespace vkcv::scene {
 	Scene::Scene(const Scene &other) :
 	m_core(other.m_core),
 	m_materials(other.m_materials),
-	m_nodes() {
+	m_nodes(),
+	m_descriptorSetLayout(other.m_descriptorSetLayout) {
 		m_nodes.resize(other.m_nodes.size(), Node(*this));
 		
 		for (size_t i = 0; i < m_nodes.size(); i++) {
@@ -32,7 +51,8 @@ namespace vkcv::scene {
 	Scene::Scene(Scene &&other) noexcept :
 	m_core(other.m_core),
 	m_materials(other.m_materials),
-	m_nodes() {
+	m_nodes(),
+	m_descriptorSetLayout(other.m_descriptorSetLayout) {
 		m_nodes.resize(other.m_nodes.size(), Node(*this));
 		
 		for (size_t i = 0; i < m_nodes.size(); i++) {
@@ -54,6 +74,8 @@ namespace vkcv::scene {
 			m_nodes[i] = other.m_nodes[i];
 		}
 		
+		m_descriptorSetLayout = other.m_descriptorSetLayout;
+		
 		return *this;
 	}
 	
@@ -67,6 +89,8 @@ namespace vkcv::scene {
 			m_nodes[i] = std::move(other.m_nodes[i]);
 		}
 		
+		m_descriptorSetLayout = other.m_descriptorSetLayout;
+		
 		return *this;
 	}
 	
@@ -111,6 +135,10 @@ namespace vkcv::scene {
 		return m_materials[index].m_data;
 	}
 	
+	const DescriptorSetLayoutHandle &Scene::getDescriptorSetLayout() const {
+		return m_descriptorSetLayout;
+	}
+	
 	void Scene::recordDrawcalls(CommandStreamHandle       		 &cmdStream,
 								const camera::Camera			 &camera,
 								const PassHandle                 &pass,