From 20b5b1164066ea485e3a173fd67293bfec2f5e74 Mon Sep 17 00:00:00 2001
From: Tobias Frisch <tfrisch@uni-koblenz.de>
Date: Mon, 5 Sep 2022 15:22:03 +0200
Subject: [PATCH] Get rid of attribute hack with sort to adjust their order

Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de>
---
 include/vkcv/VertexData.hpp                   |  8 ++-
 include/vkcv/VertexLayout.hpp                 | 11 +++-
 .../include/vkcv/asset/asset_loader.hpp       | 30 +++++++---
 .../src/vkcv/asset/asset_loader.cpp           | 26 +++++++++
 modules/scene/include/vkcv/scene/Mesh.hpp     | 13 ++++-
 modules/scene/include/vkcv/scene/MeshPart.hpp | 12 ++++
 modules/scene/include/vkcv/scene/Node.hpp     | 19 ++++++-
 modules/scene/include/vkcv/scene/Scene.hpp    | 27 +++++++--
 modules/scene/src/vkcv/scene/Mesh.cpp         |  6 +-
 modules/scene/src/vkcv/scene/MeshPart.cpp     | 15 ++---
 modules/scene/src/vkcv/scene/Node.cpp         |  6 +-
 modules/scene/src/vkcv/scene/Scene.cpp        |  6 +-
 projects/bindless_textures/src/main.cpp       | 31 +++++------
 projects/first_mesh/src/main.cpp              | 28 ++++------
 projects/first_scene/src/main.cpp             | 12 +++-
 projects/head_demo/src/main.cpp               | 11 +++-
 projects/indirect_dispatch/src/AppSetup.cpp   | 23 ++++----
 projects/indirect_draw/src/main.cpp           | 55 ++++++++++---------
 projects/mesh_shader/src/main.cpp             | 48 +++++++++++-----
 projects/voxelization/src/main.cpp            | 32 ++++-------
 src/vkcv/VertexLayout.cpp                     | 13 ++++-
 21 files changed, 286 insertions(+), 146 deletions(-)

diff --git a/include/vkcv/VertexData.hpp b/include/vkcv/VertexData.hpp
index 8e69c85e..001dcfcf 100644
--- a/include/vkcv/VertexData.hpp
+++ b/include/vkcv/VertexData.hpp
@@ -22,6 +22,8 @@ namespace vkcv {
 	VertexBufferBinding vertexBufferBinding(const BufferHandle &buffer,
 											size_t offset = 0);
 	
+	typedef std::vector<VertexBufferBinding> VertexBufferBindings;
+	
 	/**
 	 * @brief Enum class to specify the size of indexes.
 	 */
@@ -33,13 +35,13 @@ namespace vkcv {
 	
 	class VertexData {
 	private:
-		std::vector<VertexBufferBinding> m_bindings;
+		VertexBufferBindings m_bindings;
 		BufferHandle m_indices;
 		IndexBitCount m_indexBitCount;
 		size_t m_count;
 	
 	public:
-		explicit VertexData(const std::vector<VertexBufferBinding> &bindings = {});
+		explicit VertexData(const VertexBufferBindings &bindings = {});
 		
 		VertexData(const VertexData& other) = default;
 		VertexData(VertexData&& other) noexcept = default;
@@ -50,7 +52,7 @@ namespace vkcv {
 		VertexData& operator=(VertexData&& other) noexcept = default;
 		
 		[[nodiscard]]
-		const std::vector<VertexBufferBinding>& getVertexBufferBindings() const;
+		const VertexBufferBindings& getVertexBufferBindings() const;
 		
 		void setIndexBuffer(const BufferHandle& indices,
 							IndexBitCount indexBitCount = IndexBitCount::Bit16);
diff --git a/include/vkcv/VertexLayout.hpp b/include/vkcv/VertexLayout.hpp
index 330be708..8e677d11 100644
--- a/include/vkcv/VertexLayout.hpp
+++ b/include/vkcv/VertexLayout.hpp
@@ -70,11 +70,20 @@ namespace vkcv {
 	 *
 	 * @param[in] bindingLocation Its entry in the buffers that make up the whole vertex buffer.
 	 * @param[in] attachments The vertex input attachments this specific buffer layout contains.
-	 * @return Vertex buffer binding with calculated stride
+	 * @return Vertex binding with calculated stride
 	 */
 	VertexBinding createVertexBinding(uint32_t bindingLocation, const VertexAttachments &attachments);
 	
 	typedef std::vector<VertexBinding> VertexBindings;
+	
+	/**
+	 * Creates vertex bindings in a very simplified way with one vertex binding for
+	 * each attachment.
+	 *
+	 * @param[in] attachments The vertex input attachments.
+	 * @return Vertex bindings with calculated stride
+	 */
+	VertexBindings createVertexBindings(const VertexAttachments &attachments);
 
 	/**
 	 * @brief Structure to store the details of a vertex layout.
diff --git a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp
index 2362e560..d6b5cf5d 100644
--- a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp
+++ b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp
@@ -11,6 +11,8 @@
 #include <cstdint>
 #include <filesystem>
 
+#include "vkcv/VertexData.hpp"
+
 /* LOADING MESHES
  * The description of meshes is a hierarchy of structures with the Mesh at the
  * top.
@@ -279,8 +281,8 @@ struct Scene {
  * Note that for URIs only (local) filesystem paths are supported, no
  * URLs using network protocols etc.
  *
- * @param path	must be the path to a glTF- or glb-file.
- * @param scene	is a reference to a Scene struct that will be filled with the
+ * @param[in] path must be the path to a glTF- or glb-file.
+ * @param[out] scene is a reference to a Scene struct that will be filled with the
  * 	meta-data of all objects described in the glTF file.
  * @return ASSET_ERROR on failure, otherwise ASSET_SUCCESS
  */
@@ -295,8 +297,8 @@ int probeScene(const std::filesystem::path &path, Scene &scene);
  * Besides the mesh, this function will also add any associated data to the
  * Scene struct such as Materials and Textures required by the Mesh.
  *
- * @param path	must be the path to a glTF- or glb-file.
- * @param scene	is the scene struct to which the results will be written.
+ * @param[in,out] scene	is the scene struct to which the results will be written.
+ * @param[in] mesh_index Index of the mesh to load
  * @return ASSET_ERROR on failure, otherwise ASSET_SUCCESS
  */
 int loadMesh(Scene &scene, int mesh_index);
@@ -305,8 +307,8 @@ int loadMesh(Scene &scene, int mesh_index);
  * Load every mesh from the glTF file, as well as materials, textures and other
  * associated objects.
  *
- * @param path	must be the path to a glTF- or glb-file.
- * @param scene is a reference to a Scene struct that will be filled with the
+ * @param[in] path	must be the path to a glTF- or glb-file.
+ * @param[out] scene is a reference to a Scene struct that will be filled with the
  * 	content of the glTF file being loaded.
  * @return ASSET_ERROR on failure, otherwise ASSET_SUCCESS
  */
@@ -322,11 +324,25 @@ int loadScene(const std::filesystem::path &path, Scene &scene);
  * will be cleared to all 0 with path and data being empty; make sure to always
  * check that !data.empty() before using the struct.
  *
- * @param path	must be the path to an image file.
+ * @param[in] path	must be the path to an image file.
  * @return	Texture struct describing the loaded image.
  */
 Texture loadTexture(const std::filesystem::path& path);
 
+/**
+ * Loads up the vertex attributes and creates usable vertex buffer bindings
+ * to match the desired order of primitive types as used in the vertex
+ * shader.
+ *
+ * @param[in] attributes Vertex attributes
+ * @param[in] buffer Buffer handle
+ * @param[in] types Primitive type order
+ * @return Vertex buffer bindings
+ */
+VertexBufferBindings loadVertexBufferBindings(const std::vector<VertexAttribute> &attributes,
+											  const BufferHandle &buffer,
+											  const std::vector<PrimitiveType> &types);
+
 /** @} */
 
 }	// end namespace vkcv::asset
diff --git a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp
index 110a941f..d8f2563d 100644
--- a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp
+++ b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp
@@ -856,5 +856,31 @@ namespace vkcv::asset {
 		}
 		return texture;
 	}
+	
+	VertexBufferBindings loadVertexBufferBindings(const std::vector<VertexAttribute> &attributes,
+												  const BufferHandle &buffer,
+												  const std::vector<PrimitiveType> &types) {
+		VertexBufferBindings bindings;
+		
+		for (const auto& type : types) {
+			const VertexAttribute* attribute = nullptr;
+			
+			for (const auto& attr : attributes) {
+				if (type == attr.type) {
+					attribute = &(attr);
+					break;
+				}
+			}
+			
+			if (!attribute) {
+				vkcv_log(LogLevel::ERROR, "Missing primitive type in vertex attributes");
+				break;
+			}
+			
+			bindings.push_back(vkcv::vertexBufferBinding(buffer, attribute->offset));
+		}
+		
+		return bindings;
+	}
 
 }
diff --git a/modules/scene/include/vkcv/scene/Mesh.hpp b/modules/scene/include/vkcv/scene/Mesh.hpp
index 6235fa01..65cdb3a4 100644
--- a/modules/scene/include/vkcv/scene/Mesh.hpp
+++ b/modules/scene/include/vkcv/scene/Mesh.hpp
@@ -55,6 +55,7 @@ namespace vkcv::scene {
 
         /**
          * Constructor of a new mesh with a given scene as parent.
+         *
          * @param[in,out] scene Scene
          */
 		explicit Mesh(Scene& scene);
@@ -62,14 +63,18 @@ namespace vkcv::scene {
         /**
          * Load mesh data from a scene structure from the asset loader
          * creating and loading all mesh parts being required.
+         *
          * @param[in] scene Scene structure from asset loader
          * @param[in] mesh Mesh structure from asset loader
+         * @param[in] types Primitive type order of vertex attributes
          */
 		void load(const asset::Scene& scene,
-				  const asset::Mesh& mesh);
+				  const asset::Mesh& mesh,
+				  const std::vector<asset::PrimitiveType>& types);
 
         /**
          * Record drawcalls of the mesh, equally to all its visible parts.
+         *
          * @param[in] viewProjection View-transformation and projection as 4x4 matrix
          * @param[out] pushConstants Structure to store push constants per drawcall
          * @param[out] drawcalls List of drawcall structures
@@ -83,6 +88,7 @@ namespace vkcv::scene {
         /**
          * Return the amount of drawcalls of the mesh
          * as sum of all its parts.
+         *
          * @return Amount of drawcalls
          */
 		[[nodiscard]]
@@ -96,18 +102,21 @@ namespace vkcv::scene {
 
         /**
          * Copy-constructor of a mesh.
+         *
          * @param[in] other Other mesh instance
          */
 		Mesh(const Mesh& other) = default;
 
         /**
          * Move-constructor of a mesh.
+         *
          * @param[in,out] other Other mesh instance
          */
         Mesh(Mesh&& other) = default;
 
         /**
          * Copy-operator of a mesh.
+         *
          * @param[in] other Other mesh instance
          * @return Reference to this mesh instance
          */
@@ -115,6 +124,7 @@ namespace vkcv::scene {
 
         /**
          * Move-operator of a mesh.
+         *
          * @param[in,out] other Other mesh instance
          * @return Reference to this mesh instance
          */
@@ -122,6 +132,7 @@ namespace vkcv::scene {
 
         /**
          * Return the axis aligned bounding box of the mesh.
+         *
          * @return Axis aligned bounding box of this mesh
          */
 		[[nodiscard]]
diff --git a/modules/scene/include/vkcv/scene/MeshPart.hpp b/modules/scene/include/vkcv/scene/MeshPart.hpp
index eef68a00..61ed8fc3 100644
--- a/modules/scene/include/vkcv/scene/MeshPart.hpp
+++ b/modules/scene/include/vkcv/scene/MeshPart.hpp
@@ -64,6 +64,7 @@ namespace vkcv::scene {
 
         /**
          * Constructor of a new mesh part with a given scene as parent.
+         *
          * @param[in,out] scene Scene
          */
 		explicit MeshPart(Scene& scene);
@@ -71,12 +72,15 @@ namespace vkcv::scene {
         /**
          * Load vertex and index data from structures provided by the asset loader
          * and add a matching drawcall to the list if the loaded mesh part is valid.
+         *
          * @param[in] scene Scene structure from asset loader
          * @param[in] vertexGroup Vertex group structure from asset loader
+         * @param[in] types Primitive type order of vertex attributes
          * @param[out] drawcalls List of drawcalls
          */
 		void load(const asset::Scene& scene,
 				  const asset::VertexGroup& vertexGroup,
+				  const std::vector<asset::PrimitiveType>& types,
 				  std::vector<InstanceDrawcall>& drawcalls);
 	
 	public:
@@ -87,18 +91,21 @@ namespace vkcv::scene {
 
         /**
          * Copy-constructor of a mesh part.
+         *
          * @param[in] other Other mesh part
          */
 		MeshPart(const MeshPart& other);
 
         /**
          * Move-constructor of a mesh part.
+         *
          * @param[in,out] other Other mesh part
          */
         MeshPart(MeshPart&& other) noexcept;
 
         /**
          * Copy-operator of a mesh part.
+         *
          * @param[in] other Other mesh part
          * @return Reference to this mesh part
          */
@@ -106,6 +113,7 @@ namespace vkcv::scene {
 
         /**
          * Move-operator of a mesh part.
+         *
          * @param[in,out] other Other mesh part
          * @return Reference to this mesh part
          */
@@ -114,6 +122,7 @@ namespace vkcv::scene {
         /**
          * Get the material used by this specific part of
          * the mesh for rendering.
+         *
          * @return Material
          */
 		[[nodiscard]]
@@ -122,6 +131,7 @@ namespace vkcv::scene {
         /**
          * Return the axis aligned bounding box of this
          * specific part of the mesh.
+         *
          * @return Axis aligned bounding box of this mesh part
          */
 		[[nodiscard]]
@@ -130,6 +140,7 @@ namespace vkcv::scene {
         /**
          * Return the status if this part of the mesh is valid
          * as boolean value.
+         *
          * @return true if the mesh part is valid, otherwise false
          */
 		explicit operator bool() const;
@@ -137,6 +148,7 @@ namespace vkcv::scene {
         /**
          * Return the status if this part of the mesh is invalid
          * as boolean value.
+         *
          * @return true if the mesh part is invalid, otherwise false
          */
 		bool operator!() const;
diff --git a/modules/scene/include/vkcv/scene/Node.hpp b/modules/scene/include/vkcv/scene/Node.hpp
index 3cd5083c..484e1c12 100644
--- a/modules/scene/include/vkcv/scene/Node.hpp
+++ b/modules/scene/include/vkcv/scene/Node.hpp
@@ -45,25 +45,32 @@ namespace vkcv::scene {
 
         /**
          * Constructor of a new node with a given scene as parent.
+         *
          * @param[in,out] scene Scene
          */
 		explicit Node(Scene& scene);
 
         /**
          * Add a given mesh to this node for drawcall recording.
+         *
          * @param[in] mesh Mesh
          */
 		void addMesh(const Mesh& mesh);
 
         /**
          * Load and add a mesh from a scene preloaded with the asset loader.
+         *
          * @param[in] asset_scene Scene structure from asset loader
          * @param[in] asset_mesh Mesh structure from asset loader
+         * @param[in] types Primitive type order of vertex attributes
          */
-		void loadMesh(const asset::Scene& asset_scene, const asset::Mesh& asset_mesh);
+		void loadMesh(const asset::Scene& asset_scene,
+					  const asset::Mesh& asset_mesh,
+					  const std::vector<asset::PrimitiveType>& types);
 
         /**
          * Record drawcalls of all meshes of this node and its child nodes.
+         *
          * @param[in] viewProjection View-transformation and projection as 4x4 matrix
          * @param[out] pushConstants Structure to store push constants per drawcall
          * @param[out] drawcalls List of drawcall structures
@@ -78,12 +85,14 @@ namespace vkcv::scene {
          * Splits child nodes into tree based graphs of nodes
          * until all nodes contain an amount of meshes below
          * a given maximum.
+         *
          * @param[in] maxMeshesPerNode Maximum amount of meshes per node
          */
 		void splitMeshesToSubNodes(size_t maxMeshesPerNode);
 
         /**
          * Return the sum of drawcalls in the graph of this node.
+         *
          * @return Amount of drawcalls
          */
 		[[nodiscard]]
@@ -92,12 +101,14 @@ namespace vkcv::scene {
         /**
          * Add a new node as child to the scene graph with this node
          * as parent and return its index.
+         *
          * @return Index of the new node
          */
 		size_t addNode();
 
         /**
          * Get a reference to the child node with a given index.
+         *
          * @param[in] index Valid index of a child node
          * @return Matching child node
          */
@@ -105,6 +116,7 @@ namespace vkcv::scene {
 
         /**
          * Get a const reference to the child node with a given index.
+         *
          * @param[in] index Valid index of a child node
          * @return Matching child node
          */
@@ -119,18 +131,21 @@ namespace vkcv::scene {
 
         /**
          * Copy-constructor of a scene node.
+         *
          * @param[in] other Other scene node
          */
 		Node(const Node& other) = default;
 
         /**
          * Move-constructor of a scene node.
+         *
          * @param[in,out] other Other scene node
          */
 		Node(Node&& other) = default;
 
         /**
          * Copy-operator of a scene node.
+         *
          * @param[in] other Other scene node
          * @return Reference of this node
          */
@@ -138,6 +153,7 @@ namespace vkcv::scene {
 
         /**
          * Move-operator of a scene node.
+         *
          * @param[in,out] other Other scene node
          * @return Reference of this node
          */
@@ -146,6 +162,7 @@ namespace vkcv::scene {
         /**
          * Return the axis aligned bounding box of the
          * scene node.
+         *
          * @return Axis aligned bounding box of this node
          */
 		[[nodiscard]]
diff --git a/modules/scene/include/vkcv/scene/Scene.hpp b/modules/scene/include/vkcv/scene/Scene.hpp
index c482b464..74c5a50f 100644
--- a/modules/scene/include/vkcv/scene/Scene.hpp
+++ b/modules/scene/include/vkcv/scene/Scene.hpp
@@ -57,6 +57,7 @@ namespace vkcv::scene {
 
         /**
          * Constructor of a scene instance with a given Core instance.
+         *
          * @param[in,out] core Pointer to valid Core instance
          */
 		explicit Scene(Core* core);
@@ -64,12 +65,14 @@ namespace vkcv::scene {
         /**
          * Add a new node to the first level of the scene graph with
          * this scene as parent and return its index.
+         *
          * @return Index of the new node
          */
 		size_t addNode();
 
         /**
          * Get a reference to the first-level node with a given index.
+         *
          * @param[in] index Valid index of a first-level node
          * @return Matching first-level node
          */
@@ -77,6 +80,7 @@ namespace vkcv::scene {
 
         /**
         * Get a const reference to the first-level node with a given index.
+         *
         * @param[in] index Valid index of a first-level node
         * @return Matching first-level node
         */
@@ -85,12 +89,14 @@ namespace vkcv::scene {
 
         /**
          * Increase the amount of usages for a certain material via its index.
+         *
          * @param[in] index Index of a material
          */
 		void increaseMaterialUsage(size_t index);
 
         /**
          * Decrease the amount of usages for a certain material via its index.
+         *
          * @param[in] index Index of a material
          */
 		void decreaseMaterialUsage(size_t index);
@@ -98,6 +104,7 @@ namespace vkcv::scene {
         /**
          * Load a material from a scene preloaded with the asset loader via
          * its index.
+         *
          * @param[in] index Valid index of a material
          * @param[in] scene Scene structure from asset loader
          * @param[in] material Material structure from asset loader
@@ -113,18 +120,21 @@ namespace vkcv::scene {
 
         /**
          * Copy-constructor of a scene instance.
+         *
          * @param[in] other Other scene instance
          */
 		Scene(const Scene& other);
 
         /**
          * Move-constructor of a scene instance.
+         *
          * @param[in,out] other Other scene instance
          */
         Scene(Scene&& other) noexcept;
 
         /**
          * Copy-operator of a scene instance.
+         *
          * @param[in] other Other scene instance
          * @return Reference to this scene
          */
@@ -132,6 +142,7 @@ namespace vkcv::scene {
 
         /**
          * Move-operator of a scene instance.
+         *
          * @param[in,out] other Other scene instance
          * @return Reference to this scene
          */
@@ -139,6 +150,7 @@ namespace vkcv::scene {
 
         /**
          * Return the amount of materials managed by this scene.
+         *
          * @return Amount of materials
          */
         [[nodiscard]]
@@ -147,6 +159,7 @@ namespace vkcv::scene {
         /**
          * Get the material data by its certain index.
          * The material can still be invalid if it was not loaded properly.
+         *
          * @param[in] index Valid index of material
          * @return Material
          */
@@ -155,6 +168,7 @@ namespace vkcv::scene {
 
         /**
          * Record drawcalls of all meshes of this scene with CPU-side frustum culling.
+         *
          * @param cmdStream Command stream handle
          * @param camera Scene viewing camera
          * @param pass Render pass handle
@@ -175,7 +189,8 @@ namespace vkcv::scene {
 
         /**
          * Instantiation function to create a new scene instance.
-         * @param core Current Core instance
+         *
+         * @param[in,out] core Current Core instance
          * @return New scene instance
          */
 		static Scene create(Core& core);
@@ -183,11 +198,15 @@ namespace vkcv::scene {
         /**
          * Load function to create a new scene instance with materials and meshes
          * loaded from a file using the asset loader.
-         * @param core Current Core instance
-         * @param path Path of a valid file to load via asset loader
+         *
+         * @param[in,out] core Current Core instance
+         * @param[in] path Path of a valid file to load via asset loader
+         * @param[in] types Primitive type order of vertex attributes
          * @return New scene instance
          */
-		static Scene load(Core& core, const std::filesystem::path &path);
+		static Scene load(Core& core,
+						  const std::filesystem::path &path,
+						  const std::vector<asset::PrimitiveType>& types);
 		
 	};
 
diff --git a/modules/scene/src/vkcv/scene/Mesh.cpp b/modules/scene/src/vkcv/scene/Mesh.cpp
index 5729ed6c..3c6249fb 100644
--- a/modules/scene/src/vkcv/scene/Mesh.cpp
+++ b/modules/scene/src/vkcv/scene/Mesh.cpp
@@ -20,7 +20,9 @@ namespace vkcv::scene {
 		return matrix;
 	}
 	
-	void Mesh::load(const asset::Scene &scene, const asset::Mesh &mesh) {
+	void Mesh::load(const asset::Scene &scene,
+					const asset::Mesh &mesh,
+					const std::vector<asset::PrimitiveType>& types) {
 		m_parts.clear();
 		m_drawcalls.clear();
 		
@@ -32,7 +34,7 @@ namespace vkcv::scene {
 			}
 			
 			MeshPart part (m_scene);
-			part.load(scene, scene.vertexGroups[vertexGroupIndex], m_drawcalls);
+			part.load(scene, scene.vertexGroups[vertexGroupIndex], types, m_drawcalls);
 			
 			if (!part) {
 				continue;
diff --git a/modules/scene/src/vkcv/scene/MeshPart.cpp b/modules/scene/src/vkcv/scene/MeshPart.cpp
index a3fc117f..d7ceacff 100644
--- a/modules/scene/src/vkcv/scene/MeshPart.cpp
+++ b/modules/scene/src/vkcv/scene/MeshPart.cpp
@@ -17,6 +17,7 @@ namespace vkcv::scene {
 	
 	void MeshPart::load(const asset::Scene& scene,
 						const asset::VertexGroup &vertexGroup,
+						const std::vector<asset::PrimitiveType>& types,
 						std::vector<InstanceDrawcall>& drawcalls) {
 		Core& core = *(m_scene.m_core);
 		
@@ -27,15 +28,11 @@ namespace vkcv::scene {
 		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);
-		});
-		
-		for (const auto& attribute : attributes) {
-			m_vertexBindings.emplace_back(vertexBuffer.getHandle(), attribute.offset);
-		}
+		m_vertexBindings = asset::loadVertexBufferBindings(
+				vertexGroup.vertexBuffer.attributes,
+				vertexBuffer.getHandle(),
+				types
+		);
 		
 		auto indexBuffer = buffer<uint8_t>(
 				core, BufferType::INDEX, vertexGroup.indexBuffer.data.size()
diff --git a/modules/scene/src/vkcv/scene/Node.cpp b/modules/scene/src/vkcv/scene/Node.cpp
index 746fe180..f5234dd4 100644
--- a/modules/scene/src/vkcv/scene/Node.cpp
+++ b/modules/scene/src/vkcv/scene/Node.cpp
@@ -69,9 +69,11 @@ namespace vkcv::scene {
 		m_meshes.push_back(mesh);
 	}
 	
-	void Node::loadMesh(const asset::Scene &asset_scene, const asset::Mesh &asset_mesh) {
+	void Node::loadMesh(const asset::Scene &asset_scene,
+						const asset::Mesh &asset_mesh,
+						const std::vector<asset::PrimitiveType>& types) {
 		Mesh mesh (m_scene);
-		mesh.load(asset_scene, asset_mesh);
+		mesh.load(asset_scene, asset_mesh, types);
 		addMesh(mesh);
 	}
 	
diff --git a/modules/scene/src/vkcv/scene/Scene.cpp b/modules/scene/src/vkcv/scene/Scene.cpp
index e68de653..90d457c9 100644
--- a/modules/scene/src/vkcv/scene/Scene.cpp
+++ b/modules/scene/src/vkcv/scene/Scene.cpp
@@ -316,7 +316,9 @@ namespace vkcv::scene {
 		);
 	}
 	
-	Scene Scene::load(Core& core, const std::filesystem::path &path) {
+	Scene Scene::load(Core& core,
+					  const std::filesystem::path &path,
+					  const std::vector<asset::PrimitiveType>& types) {
 		asset::Scene asset_scene;
 		
 		if (!asset::loadScene(path.string(), asset_scene)) {
@@ -335,7 +337,7 @@ namespace vkcv::scene {
 		const size_t root = scene.addNode();
 		
 		for (const auto& mesh : asset_scene.meshes) {
-			scene.getNode(root).loadMesh(asset_scene, mesh);
+			scene.getNode(root).loadMesh(asset_scene, mesh, types);
 		}
 		
 		vkcv::SamplerHandle sampler = samplerLinear(core);
diff --git a/projects/bindless_textures/src/main.cpp b/projects/bindless_textures/src/main.cpp
index 0a34bcb7..e4dd4916 100644
--- a/projects/bindless_textures/src/main.cpp
+++ b/projects/bindless_textures/src/main.cpp
@@ -125,18 +125,20 @@ int main(int argc, const char** argv) {
 		{ vkcv::ShaderStage::VERTEX, "resources/shaders/shader.vert" },
 		{ vkcv::ShaderStage::FRAGMENT, "resources/shaders/shader.frag" }
 	}, nullptr);
- 
-	auto& attributes = mesh.vertexGroups[0].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);
-	});
-
-    const std::vector<vkcv::VertexAttachment> vertexAttachments = firstMeshProgram.getVertexAttachments();
-	std::vector<vkcv::VertexBinding> bindings;
-	for (size_t i = 0; i < vertexAttachments.size(); i++) {
-		bindings.push_back(vkcv::createVertexBinding(i, { vertexAttachments[i] }));
-	}
+	const auto vertexBufferBindings = vkcv::asset::loadVertexBufferBindings(
+			mesh.vertexGroups[0].vertexBuffer.attributes,
+			vertexBuffer.getHandle(),
+			{
+					vkcv::asset::PrimitiveType::POSITION,
+					vkcv::asset::PrimitiveType::NORMAL,
+					vkcv::asset::PrimitiveType::TEXCOORD_0
+			}
+	);
+	
+	std::vector<vkcv::VertexBinding> bindings = vkcv::createVertexBindings(
+			firstMeshProgram.getVertexAttachments()
+	);
 	
 	const vkcv::VertexLayout firstMeshLayout { bindings };
 	const std::unordered_map<uint32_t, vkcv::DescriptorBinding> &descriptorBindings = firstMeshProgram.getReflectedDescriptors().at(0);
@@ -182,13 +184,6 @@ int main(int argc, const char** argv) {
 	core.submitCommandStream(downsampleStream, false);
 
 	vkcv::SamplerHandle sampler = vkcv::samplerLinear(core);
-	
-	const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
-			vkcv::vertexBufferBinding(vertexBuffer.getHandle(), attributes[0].offset),
-			vkcv::vertexBufferBinding(vertexBuffer.getHandle(), attributes[1].offset),
-			vkcv::vertexBufferBinding(vertexBuffer.getHandle(), attributes[2].offset)
-	};
-
 	vkcv::DescriptorWrites setWrites;
 	
 	for(uint32_t i = 0; i < 6; i++)
diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp
index 2e28bd0f..88ab4b70 100644
--- a/projects/first_mesh/src/main.cpp
+++ b/projects/first_mesh/src/main.cpp
@@ -70,18 +70,20 @@ int main(int argc, const char** argv) {
 		{ vkcv::ShaderStage::VERTEX, "assets/shaders/shader.vert" },
 		{ vkcv::ShaderStage::FRAGMENT, "assets/shaders/shader.frag" }
 	}, nullptr);
- 
-	auto& attributes = mesh.vertexGroups[0].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);
-	});
+	const auto vertexBufferBindings = vkcv::asset::loadVertexBufferBindings(
+			mesh.vertexGroups[0].vertexBuffer.attributes,
+			vertexBuffer.getHandle(),
+			{
+					vkcv::asset::PrimitiveType::POSITION,
+					vkcv::asset::PrimitiveType::NORMAL,
+					vkcv::asset::PrimitiveType::TEXCOORD_0
+			}
+	);
 
-    const std::vector<vkcv::VertexAttachment> vertexAttachments = firstMeshProgram.getVertexAttachments();
-	std::vector<vkcv::VertexBinding> bindings;
-	for (size_t i = 0; i < vertexAttachments.size(); i++) {
-		bindings.push_back(vkcv::createVertexBinding(i, { vertexAttachments[i] }));
-	}
+	std::vector<vkcv::VertexBinding> bindings = vkcv::createVertexBindings(
+			firstMeshProgram.getVertexAttachments()
+	);
 	
 	const vkcv::VertexLayout firstMeshLayout { bindings };
 
@@ -126,12 +128,6 @@ int main(int argc, const char** argv) {
 
 	vkcv::SamplerHandle sampler = vkcv::samplerLinear(core);
 
-	const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
-			vkcv::vertexBufferBinding(vertexBuffer.getHandle(), attributes[0].offset),
-			vkcv::vertexBufferBinding(vertexBuffer.getHandle(), attributes[1].offset),
-			vkcv::vertexBufferBinding(vertexBuffer.getHandle(), attributes[2].offset)
-	};
-
 	vkcv::DescriptorWrites setWrites;
 	setWrites.writeSampledImage(0, texture.getHandle());
 	setWrites.writeSampler(1, sampler);
diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp
index 28a2f2d4..6b29b14b 100644
--- a/projects/first_scene/src/main.cpp
+++ b/projects/first_scene/src/main.cpp
@@ -45,9 +45,15 @@ int main(int argc, const char** argv) {
 	
 	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/Sponza/Sponza.gltf"
-	));
+	vkcv::scene::Scene scene = vkcv::scene::Scene::load(
+			core,
+			std::filesystem::path(argc > 1 ? argv[1] : "assets/Sponza/Sponza.gltf"),
+			{
+				vkcv::asset::PrimitiveType::POSITION,
+				vkcv::asset::PrimitiveType::NORMAL,
+				vkcv::asset::PrimitiveType::TEXCOORD_0
+			}
+	);
 	
 	vkcv::PassHandle scenePass = vkcv::passSwapchain(
 			core,
diff --git a/projects/head_demo/src/main.cpp b/projects/head_demo/src/main.cpp
index 2a0891f8..69daa8ba 100644
--- a/projects/head_demo/src/main.cpp
+++ b/projects/head_demo/src/main.cpp
@@ -38,9 +38,14 @@ int main(int argc, const char** argv) {
 	
 	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"
-	));
+	vkcv::scene::Scene scene = vkcv::scene::Scene::load(
+			core,
+			std::filesystem::path(argc > 1 ? argv[1] : "assets/skull_scaled/scene.gltf"),
+			{
+					vkcv::asset::PrimitiveType::POSITION,
+					vkcv::asset::PrimitiveType::NORMAL
+			}
+	);
 	
 	vk::Format colorFormat = vk::Format::eR16G16B16A16Sfloat;
 	
diff --git a/projects/indirect_dispatch/src/AppSetup.cpp b/projects/indirect_dispatch/src/AppSetup.cpp
index fb542509..26cfbbc3 100644
--- a/projects/indirect_dispatch/src/AppSetup.cpp
+++ b/projects/indirect_dispatch/src/AppSetup.cpp
@@ -43,19 +43,16 @@ bool loadMesh(vkcv::Core& core, const std::filesystem::path& path, MeshResources
 
 	outMesh->vertexBuffer = vertexBuffer.getHandle();
 	outMesh->indexBuffer  = indexBuffer.getHandle();
-
-	auto& attributes = vertexData.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);
-	});
-
-	const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
-		vkcv::vertexBufferBinding(vertexBuffer.getHandle(), attributes[0].offset),
-		vkcv::vertexBufferBinding(vertexBuffer.getHandle(), attributes[1].offset),
-		vkcv::vertexBufferBinding(vertexBuffer.getHandle(), attributes[2].offset)
-	};
+	
+	const auto vertexBufferBindings = vkcv::asset::loadVertexBufferBindings(
+			vertexData.attributes,
+			vertexBuffer.getHandle(),
+			{
+					vkcv::asset::PrimitiveType::POSITION,
+					vkcv::asset::PrimitiveType::NORMAL,
+					vkcv::asset::PrimitiveType::TEXCOORD_0
+			}
+	);
 
 	outMesh->mesh = vkcv::VertexData(vertexBufferBindings);
 	outMesh->mesh.setIndexBuffer(indexBuffer.getHandle());
diff --git a/projects/indirect_draw/src/main.cpp b/projects/indirect_draw/src/main.cpp
index 4e852dbb..39c5621e 100644
--- a/projects/indirect_draw/src/main.cpp
+++ b/projects/indirect_draw/src/main.cpp
@@ -90,28 +90,33 @@ struct CompiledMaterial
 
 void interleaveScene(vkcv::asset::Scene scene,
                      std::vector<std::vector<Vertex>> &interleavedVertexBuffers,
-                     std::vector<glm::vec4> &boundingBoxBuffers)
-{
-    for(const auto &mesh : scene.meshes)
-    {
-        for(auto vertexGroupIndex : mesh.vertexGroups)
-        {
-			// Sort attributes to fix it!
-			auto& attributes = scene.vertexGroups[vertexGroupIndex].vertexBuffer.attributes;
+                     std::vector<glm::vec4> &boundingBoxBuffers) {
 	
-			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);
-			});
-			
+    for(const auto &mesh : scene.meshes) {
+        for(auto vertexGroupIndex : mesh.vertexGroups) {
             const auto &vertexGroup = scene.vertexGroups[vertexGroupIndex];
 
-            const vkcv::asset::VertexAttribute positionAttribute = vertexGroup.vertexBuffer.attributes[0];
-            const vkcv::asset::VertexAttribute normalAttribute   = vertexGroup.vertexBuffer.attributes[1];
-            const vkcv::asset::VertexAttribute uvAttribute       = vertexGroup.vertexBuffer.attributes[2];
-
-            assert(positionAttribute.type   == vkcv::asset::PrimitiveType::POSITION);
-            assert(normalAttribute.type     == vkcv::asset::PrimitiveType::NORMAL);
-            assert(uvAttribute.type         == vkcv::asset::PrimitiveType::TEXCOORD_0);
+            const vkcv::asset::VertexAttribute* positionAttribute = nullptr;
+            const vkcv::asset::VertexAttribute* normalAttribute   = nullptr;
+            const vkcv::asset::VertexAttribute* uvAttribute       = nullptr;
+			
+			for (const auto& attribute : vertexGroup.vertexBuffer.attributes) {
+				switch (attribute.type) {
+					case vkcv::asset::PrimitiveType::POSITION:
+						positionAttribute = &attribute;
+						break;
+					case vkcv::asset::PrimitiveType::NORMAL:
+						normalAttribute = &attribute;
+						break;
+					case vkcv::asset::PrimitiveType::TEXCOORD_0:
+						uvAttribute = &attribute;
+						break;
+					default:
+						break;
+				}
+			}
+	
+			assert(positionAttribute && normalAttribute && uvAttribute);
 
             const uint64_t &verticesCount          = vertexGroup.numVertices;
             const std::vector<uint8_t> &vertexData = vertexGroup.vertexBuffer.data;
@@ -119,18 +124,18 @@ void interleaveScene(vkcv::asset::Scene scene,
             std::vector<Vertex> vertices;
             vertices.reserve(verticesCount);
 
-            const size_t positionStride = positionAttribute.stride == 0 ? sizeof(glm::vec3) : positionAttribute.stride;
-            const size_t normalStride   = normalAttribute.stride   == 0 ? sizeof(glm::vec3) : normalAttribute.stride;
-            const size_t uvStride       = uvAttribute.stride       == 0 ? sizeof(glm::vec2) : uvAttribute.stride;
+            const size_t positionStride = positionAttribute->stride == 0 ? sizeof(glm::vec3) : positionAttribute->stride;
+            const size_t normalStride   = normalAttribute->stride   == 0 ? sizeof(glm::vec3) : normalAttribute->stride;
+            const size_t uvStride       = uvAttribute->stride       == 0 ? sizeof(glm::vec2) : uvAttribute->stride;
 
             glm::vec3 max_pos(-std::numeric_limits<float>::max());
             glm::vec3 min_pos(std::numeric_limits<float>::max());
 
             for(size_t i = 0; i < verticesCount; i++)
             {
-                const size_t positionOffset = positionAttribute.offset + positionStride * i;
-                const size_t normalOffset   = normalAttribute.offset   + normalStride * i;
-                const size_t uvOffset       = uvAttribute.offset       + uvStride * i;
+                const size_t positionOffset = positionAttribute->offset + positionStride * i;
+                const size_t normalOffset   = normalAttribute->offset   + normalStride * i;
+                const size_t uvOffset       = uvAttribute->offset       + uvStride * i;
 
                 Vertex v;
 
diff --git a/projects/mesh_shader/src/main.cpp b/projects/mesh_shader/src/main.cpp
index 5fab2874..034ae3c0 100644
--- a/projects/mesh_shader/src/main.cpp
+++ b/projects/mesh_shader/src/main.cpp
@@ -120,22 +120,42 @@ int main(int argc, const char** argv) {
             vkcv::BufferMemoryType::DEVICE_LOCAL
     );
     indexBuffer.fill(mesh.vertexGroups[0].indexBuffer.data);
-
-	// format data for mesh shader
-	auto& attributes = mesh.vertexGroups[0].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);
-	});
-
-	const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
-			vkcv::vertexBufferBinding(vertexBuffer.getHandle(), attributes[0].offset),
-			vkcv::vertexBufferBinding(vertexBuffer.getHandle(), attributes[1].offset),
-			vkcv::vertexBufferBinding(vertexBuffer.getHandle(), attributes[2].offset)
-	};
+	
+	const auto vertexBufferBindings = vkcv::asset::loadVertexBufferBindings(
+			mesh.vertexGroups[0].vertexBuffer.attributes,
+			vertexBuffer.getHandle(),
+			{
+					vkcv::asset::PrimitiveType::POSITION,
+					vkcv::asset::PrimitiveType::NORMAL
+			}
+	);
+	
+	const vkcv::asset::VertexAttribute* positionAttribute = nullptr;
+	const vkcv::asset::VertexAttribute* normalAttribute   = nullptr;
+	
+	for (const auto& attribute : mesh.vertexGroups[0].vertexBuffer.attributes) {
+		switch (attribute.type) {
+			case vkcv::asset::PrimitiveType::POSITION:
+				positionAttribute = &attribute;
+				break;
+			case vkcv::asset::PrimitiveType::NORMAL:
+				normalAttribute = &attribute;
+				break;
+			default:
+				break;
+		}
+	}
+	
+	assert(positionAttribute && normalAttribute);
 
 	const auto& bunny = mesh.vertexGroups[0];
-	std::vector<vkcv::meshlet::Vertex> interleavedVertices = vkcv::meshlet::convertToVertices(bunny.vertexBuffer.data, bunny.numVertices, attributes[0], attributes[1]);
+	std::vector<vkcv::meshlet::Vertex> interleavedVertices = vkcv::meshlet::convertToVertices(
+			bunny.vertexBuffer.data,
+			bunny.numVertices,
+			*positionAttribute,
+			*normalAttribute
+	);
+	
 	// mesh shader buffers
 	const auto& assetLoaderIndexBuffer                    = mesh.vertexGroups[0].indexBuffer;
 	std::vector<uint32_t> indexBuffer32Bit                = vkcv::meshlet::assetLoaderIndicesTo32BitIndices(assetLoaderIndexBuffer.data, assetLoaderIndexBuffer.type);
diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp
index 740de623..da2be34b 100644
--- a/projects/voxelization/src/main.cpp
+++ b/projects/voxelization/src/main.cpp
@@ -137,20 +137,11 @@ int main(int argc, const char** argv) {
 	std::vector<std::vector<uint8_t>> vBuffers;
 	std::vector<std::vector<uint8_t>> iBuffers;
 
-	std::vector<vkcv::VertexBufferBinding> vBufferBindings;
 	std::vector<std::vector<vkcv::VertexBufferBinding>> vertexBufferBindings;
-	std::vector<vkcv::asset::VertexAttribute> vAttributes;
 
 	for (size_t i = 0; i < scene.vertexGroups.size(); i++) {
-
 		vBuffers.push_back(scene.vertexGroups[i].vertexBuffer.data);
 		iBuffers.push_back(scene.vertexGroups[i].indexBuffer.data);
-
-		auto& attributes = scene.vertexGroups[i].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<vkcv::Buffer<uint8_t>> vertexBuffers;
@@ -171,18 +162,17 @@ int main(int argc, const char** argv) {
 		indexBuffers.back().fill(dataBuffer);
 	}
 
-	int vertexBufferIndex = 0;
-	for (const auto& vertexGroup : scene.vertexGroups) {
-		for (const auto& attribute : vertexGroup.vertexBuffer.attributes) {
-			vAttributes.push_back(attribute);
-			vBufferBindings.push_back(vkcv::vertexBufferBinding(
-					vertexBuffers[vertexBufferIndex].getHandle(),
-					attribute.offset
-			));
-		}
-		vertexBufferBindings.push_back(vBufferBindings);
-		vBufferBindings.clear();
-		vertexBufferIndex++;
+	for (size_t i = 0; i < scene.vertexGroups.size(); i++) {
+		vertexBufferBindings.push_back(vkcv::asset::loadVertexBufferBindings(
+				scene.vertexGroups[i].vertexBuffer.attributes,
+				vertexBuffers[i].getHandle(),
+				{
+						vkcv::asset::PrimitiveType::POSITION,
+						vkcv::asset::PrimitiveType::NORMAL,
+						vkcv::asset::PrimitiveType::TEXCOORD_0,
+						vkcv::asset::PrimitiveType::TANGENT
+				}
+		));
 	}
 
 	const vk::Format colorBufferFormat = vk::Format::eB10G11R11UfloatPack32;
diff --git a/src/vkcv/VertexLayout.cpp b/src/vkcv/VertexLayout.cpp
index 73e3885b..1409fee0 100644
--- a/src/vkcv/VertexLayout.cpp
+++ b/src/vkcv/VertexLayout.cpp
@@ -43,5 +43,16 @@ namespace vkcv {
 		binding.stride = offset;
 		return binding;
 	}
-
+	
+	VertexBindings createVertexBindings(const VertexAttachments &attachments) {
+		VertexBindings bindings;
+		bindings.reserve(attachments.size());
+		
+		for (uint32_t i = 0; i < attachments.size(); i++) {
+			bindings.push_back(createVertexBinding(i, { attachments[i] }));
+		}
+		
+		return bindings;
+	}
+	
 }
\ No newline at end of file
-- 
GitLab