Quellcode durchsuchen

Merge pull request #29 from godlikepanos/new_vertex_packing

New vertex packing and a more flexible geometry format
Panagiotis Christopoulos Charitos vor 7 Jahren
Ursprung
Commit
8e7c35eed9
100 geänderte Dateien mit 833 neuen und 611 gelöschten Zeilen
  1. BIN
      engine_data/Plight.ankimesh
  2. BIN
      engine_data/Slight.ankimesh
  3. 23 7
      programs/SceneDebug.ankiprog
  4. 18 1
      samples/common/Framework.cpp
  5. BIN
      samples/simple_scene/assets/column.ankimesh
  6. BIN
      samples/simple_scene/assets/room.ankimesh
  7. BIN
      samples/simple_scene/assets/sector.ankimesh
  8. BIN
      samples/sponza/assets/Cube.ankimesh
  9. BIN
      samples/sponza/assets/arc_2.ankimesh
  10. BIN
      samples/sponza/assets/arch_a.ankimesh
  11. BIN
      samples/sponza/assets/arch_support_big.ankimesh
  12. BIN
      samples/sponza/assets/arch_support_med.ankimesh
  13. BIN
      samples/sponza/assets/arch_support_tiny.ankimesh
  14. BIN
      samples/sponza/assets/carpet.ankimesh
  15. BIN
      samples/sponza/assets/ceiling.ankimesh
  16. BIN
      samples/sponza/assets/column_a.001.ankimesh
  17. BIN
      samples/sponza/assets/column_a.ankimesh
  18. BIN
      samples/sponza/assets/column_b.ankimesh
  19. BIN
      samples/sponza/assets/column_b_top.ankimesh
  20. BIN
      samples/sponza/assets/column_c.ankimesh
  21. BIN
      samples/sponza/assets/column_c_small.ankimesh
  22. BIN
      samples/sponza/assets/column_c_small_top.ankimesh
  23. BIN
      samples/sponza/assets/column_c_square.ankimesh
  24. BIN
      samples/sponza/assets/door_b.ankimesh
  25. BIN
      samples/sponza/assets/fabric_a.ankimesh
  26. BIN
      samples/sponza/assets/fabric_b.ankimesh
  27. BIN
      samples/sponza/assets/flag_pole.ankimesh
  28. BIN
      samples/sponza/assets/hanging_vase.ankimesh
  29. BIN
      samples/sponza/assets/leaf.ankimesh
  30. BIN
      samples/sponza/assets/leaf_b.ankimesh
  31. BIN
      samples/sponza/assets/lion.ankimesh
  32. BIN
      samples/sponza/assets/lion_frame.ankimesh
  33. BIN
      samples/sponza/assets/list.ankimesh
  34. BIN
      samples/sponza/assets/list_b.ankimesh
  35. BIN
      samples/sponza/assets/marble_list.ankimesh
  36. BIN
      samples/sponza/assets/metal_rod.ankimesh
  37. BIN
      samples/sponza/assets/rod_end.ankimesh
  38. BIN
      samples/sponza/assets/round_window.ankimesh
  39. BIN
      samples/sponza/assets/small_window_inner.ankimesh
  40. BIN
      samples/sponza/assets/small_window_outter.ankimesh
  41. BIN
      samples/sponza/assets/sponza_00.ankimesh
  42. BIN
      samples/sponza/assets/sponza_05.ankimesh
  43. BIN
      samples/sponza/assets/sponza_06.ankimesh
  44. BIN
      samples/sponza/assets/sponza_117.ankimesh
  45. BIN
      samples/sponza/assets/sponza_122.ankimesh
  46. BIN
      samples/sponza/assets/sponza_17.ankimesh
  47. BIN
      samples/sponza/assets/sponza_18.ankimesh
  48. BIN
      samples/sponza/assets/sponza_257.ankimesh
  49. BIN
      samples/sponza/assets/sponza_258.ankimesh
  50. BIN
      samples/sponza/assets/sponza_277.ankimesh
  51. BIN
      samples/sponza/assets/sponza_278.ankimesh
  52. BIN
      samples/sponza/assets/sponza_279.ankimesh
  53. BIN
      samples/sponza/assets/sponza_280.ankimesh
  54. BIN
      samples/sponza/assets/sponza_281.ankimesh
  55. BIN
      samples/sponza/assets/sponza_34.ankimesh
  56. BIN
      samples/sponza/assets/sponza_35.ankimesh
  57. BIN
      samples/sponza/assets/sponza_36.ankimesh
  58. BIN
      samples/sponza/assets/sponza_369.002.ankimesh
  59. BIN
      samples/sponza/assets/sponza_379.ankimesh
  60. BIN
      samples/sponza/assets/sponza_380.ankimesh
  61. BIN
      samples/sponza/assets/sponza_381.ankimesh
  62. BIN
      samples/sponza/assets/sponza_382.ankimesh
  63. BIN
      samples/sponza/assets/sponza_66.ankimesh
  64. BIN
      samples/sponza/assets/sponza_68.ankimesh
  65. BIN
      samples/sponza/assets/sponza_69.ankimesh
  66. BIN
      samples/sponza/assets/square_door.ankimesh
  67. BIN
      samples/sponza/assets/vase.ankimesh
  68. BIN
      samples/sponza/assets/vase_chains.ankimesh
  69. BIN
      samples/sponza/assets/vase_flowers.ankimesh
  70. BIN
      samples/sponza/assets/vase_hanger.ankimesh
  71. BIN
      samples/sponza/assets/window.ankimesh
  72. 18 1
      sandbox/Main.cpp
  73. 7 4
      shaders/Common.glsl
  74. 1 0
      src/anki/collision/Obb.h
  75. 3 1
      src/anki/gr/Enums.h
  76. 10 0
      src/anki/gr/gl/Common.cpp
  77. 2 1
      src/anki/gr/vulkan/BufferImpl.cpp
  78. 6 5
      src/anki/gr/vulkan/BufferImpl.h
  79. 3 2
      src/anki/gr/vulkan/CommandBufferImpl.inl.h
  80. 27 3
      src/anki/math/Functions.h
  81. 2 2
      src/anki/physics/PhysicsCollisionShape.cpp
  82. 1 1
      src/anki/physics/PhysicsCollisionShape.h
  83. 19 45
      src/anki/renderer/Dbg.cpp
  84. 31 4
      src/anki/renderer/Dbg.h
  85. 33 71
      src/anki/renderer/Indirect.cpp
  86. 4 8
      src/anki/renderer/Indirect.h
  87. 9 0
      src/anki/renderer/RenderQueue.h
  88. 7 6
      src/anki/resource/CollisionResource.cpp
  89. 17 0
      src/anki/resource/Common.h
  90. 231 144
      src/anki/resource/MeshLoader.cpp
  91. 67 103
      src/anki/resource/MeshLoader.h
  92. 103 64
      src/anki/resource/MeshResource.cpp
  93. 79 47
      src/anki/resource/MeshResource.h
  94. 55 60
      src/anki/resource/ModelResource.cpp
  95. 12 5
      src/anki/resource/ModelResource.h
  96. 1 1
      src/anki/resource/ResourceFilesystem.h
  97. 31 13
      src/anki/scene/ModelNode.cpp
  98. 7 8
      src/anki/scene/OccluderNode.cpp
  99. 2 2
      src/anki/scene/ParticleEmitterNode.cpp
  100. 4 2
      src/anki/scene/ReflectionProxyNode.cpp

BIN
engine_data/Plight.ankimesh


BIN
engine_data/Slight.ankimesh


+ 23 - 7
programs/SceneDebug.ankiprog

@@ -7,6 +7,7 @@ http://www.anki3d.org/LICENSE
 <shaderProgram>
 	<mutators>
 		<mutator name="COLOR_TEXTURE" values="0 1"/>
+		<mutator name="DITHERED_DEPTH_TEST" values="0 1"/> 
 	</mutators>
 
 	<inputs>
@@ -24,12 +25,10 @@ layout(location = 1) in vec2 in_uv;
 layout(location = 0) out vec2 out_uv;
 #endif
 
-layout(ANKI_UBO_BINDING(0, 0), row_major) uniform u0_
+layout(ANKI_UBO_BINDING(1, 0), row_major) uniform u0_
 {
 	mat4 u_mvp[INSTANCE_COUNT];
-#if COLOR_TEXTURE == 0
 	vec4 u_color;
-#endif
 };
 
 out gl_PerVertex
@@ -53,21 +52,38 @@ void main()
 
 #if COLOR_TEXTURE == 1
 layout(location = 0) in vec2 in_uv;
-layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_tex;
-#else
-layout(ANKI_UBO_BINDING(0, 0), row_major) uniform u0_
+layout(ANKI_TEX_BINDING(1, 0)) uniform sampler2D u_tex;
+#endif
+
+layout(ANKI_UBO_BINDING(1, 0), row_major) uniform u0_
 {
 	mat4 u_mvp[INSTANCE_COUNT];
 	vec4 u_color;
 };
+
+#if DITHERED_DEPTH_TEST == 1
+layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_depthRt;
 #endif
 
 layout(location = 0) out vec4 out_color;
 
 void main()
 {
+	// Check if we should skip the frag
+#if DITHERED_DEPTH_TEST == 1
+	vec2 uv = gl_FragCoord.xy / vec2(textureSize(u_depthRt, 0));
+	float depthRef = textureLod(u_depthRt, uv, 0.0).r;
+	bool depthTestFailed = gl_FragCoord.z >= depthRef;
+	ivec2 fragCoordi = ivec2(gl_FragCoord.xy);
+	if(depthTestFailed && ((fragCoordi.x + fragCoordi.y) % 8) != 0)
+	{
+		discard;
+	}
+#endif
+
+	// Write the color
 #if COLOR_TEXTURE == 1
-	out_color = texture(u_tex, in_uv);
+	out_color = texture(u_tex, in_uv) * u_color;
 #else
 	out_color = u_color;
 #endif

+ 18 - 1
samples/common/Framework.cpp

@@ -66,7 +66,24 @@ Error SampleApp::userMainLoop(Bool& quit)
 
 	if(in.getKey(KeyCode::F1) == 1)
 	{
-		renderer.getDbg().setEnabled(!renderer.getDbg().getEnabled());
+		static U mode = 0;
+		mode = (mode + 1) % 3;
+		if(mode == 0)
+		{
+			renderer.getDbg().setEnabled(false);
+		}
+		else if(mode == 1)
+		{
+			renderer.getDbg().setEnabled(true);
+			renderer.getDbg().setDepthTestEnabled(true);
+			renderer.getDbg().setDitheredDepthTestEnabled(false);
+		}
+		else
+		{
+			renderer.getDbg().setEnabled(true);
+			renderer.getDbg().setDepthTestEnabled(false);
+			renderer.getDbg().setDitheredDepthTestEnabled(true);
+		}
 	}
 	if(in.getKey(KeyCode::F2) == 1)
 	{

BIN
samples/simple_scene/assets/column.ankimesh


BIN
samples/simple_scene/assets/room.ankimesh


BIN
samples/simple_scene/assets/sector.ankimesh


BIN
samples/sponza/assets/Cube.ankimesh


BIN
samples/sponza/assets/arc_2.ankimesh


BIN
samples/sponza/assets/arch_a.ankimesh


BIN
samples/sponza/assets/arch_support_big.ankimesh


BIN
samples/sponza/assets/arch_support_med.ankimesh


BIN
samples/sponza/assets/arch_support_tiny.ankimesh


BIN
samples/sponza/assets/carpet.ankimesh


BIN
samples/sponza/assets/ceiling.ankimesh


BIN
samples/sponza/assets/column_a.001.ankimesh


BIN
samples/sponza/assets/column_a.ankimesh


BIN
samples/sponza/assets/column_b.ankimesh


BIN
samples/sponza/assets/column_b_top.ankimesh


BIN
samples/sponza/assets/column_c.ankimesh


BIN
samples/sponza/assets/column_c_small.ankimesh


BIN
samples/sponza/assets/column_c_small_top.ankimesh


BIN
samples/sponza/assets/column_c_square.ankimesh


BIN
samples/sponza/assets/door_b.ankimesh


BIN
samples/sponza/assets/fabric_a.ankimesh


BIN
samples/sponza/assets/fabric_b.ankimesh


BIN
samples/sponza/assets/flag_pole.ankimesh


BIN
samples/sponza/assets/hanging_vase.ankimesh


BIN
samples/sponza/assets/leaf.ankimesh


BIN
samples/sponza/assets/leaf_b.ankimesh


BIN
samples/sponza/assets/lion.ankimesh


BIN
samples/sponza/assets/lion_frame.ankimesh


BIN
samples/sponza/assets/list.ankimesh


BIN
samples/sponza/assets/list_b.ankimesh


BIN
samples/sponza/assets/marble_list.ankimesh


BIN
samples/sponza/assets/metal_rod.ankimesh


BIN
samples/sponza/assets/rod_end.ankimesh


BIN
samples/sponza/assets/round_window.ankimesh


BIN
samples/sponza/assets/small_window_inner.ankimesh


BIN
samples/sponza/assets/small_window_outter.ankimesh


BIN
samples/sponza/assets/sponza_00.ankimesh


BIN
samples/sponza/assets/sponza_05.ankimesh


BIN
samples/sponza/assets/sponza_06.ankimesh


BIN
samples/sponza/assets/sponza_117.ankimesh


BIN
samples/sponza/assets/sponza_122.ankimesh


BIN
samples/sponza/assets/sponza_17.ankimesh


BIN
samples/sponza/assets/sponza_18.ankimesh


BIN
samples/sponza/assets/sponza_257.ankimesh


BIN
samples/sponza/assets/sponza_258.ankimesh


BIN
samples/sponza/assets/sponza_277.ankimesh


BIN
samples/sponza/assets/sponza_278.ankimesh


BIN
samples/sponza/assets/sponza_279.ankimesh


BIN
samples/sponza/assets/sponza_280.ankimesh


BIN
samples/sponza/assets/sponza_281.ankimesh


BIN
samples/sponza/assets/sponza_34.ankimesh


BIN
samples/sponza/assets/sponza_35.ankimesh


BIN
samples/sponza/assets/sponza_36.ankimesh


BIN
samples/sponza/assets/sponza_369.002.ankimesh


BIN
samples/sponza/assets/sponza_379.ankimesh


BIN
samples/sponza/assets/sponza_380.ankimesh


BIN
samples/sponza/assets/sponza_381.ankimesh


BIN
samples/sponza/assets/sponza_382.ankimesh


BIN
samples/sponza/assets/sponza_66.ankimesh


BIN
samples/sponza/assets/sponza_68.ankimesh


BIN
samples/sponza/assets/sponza_69.ankimesh


BIN
samples/sponza/assets/square_door.ankimesh


BIN
samples/sponza/assets/vase.ankimesh


BIN
samples/sponza/assets/vase_chains.ankimesh


BIN
samples/sponza/assets/vase_flowers.ankimesh


BIN
samples/sponza/assets/vase_hanger.ankimesh


BIN
samples/sponza/assets/window.ankimesh


+ 18 - 1
sandbox/Main.cpp

@@ -125,7 +125,24 @@ Error MyApp::userMainLoop(Bool& quit)
 
 	if(in.getKey(KeyCode::F1) == 1)
 	{
-		renderer.getDbg().setEnabled(!renderer.getDbg().getEnabled());
+		static U mode = 0;
+		mode = (mode + 1) % 3;
+		if(mode == 0)
+		{
+			renderer.getDbg().setEnabled(false);
+		}
+		else if(mode == 1)
+		{
+			renderer.getDbg().setEnabled(true);
+			renderer.getDbg().setDepthTestEnabled(true);
+			renderer.getDbg().setDitheredDepthTestEnabled(false);
+		}
+		else
+		{
+			renderer.getDbg().setEnabled(true);
+			renderer.getDbg().setDepthTestEnabled(false);
+			renderer.getDbg().setDitheredDepthTestEnabled(true);
+		}
 	}
 	if(in.getKey(KeyCode::F2) == 1)
 	{

+ 7 - 4
shaders/Common.glsl

@@ -44,10 +44,13 @@ const uint UBO_MAX_SIZE = 16384u;
 // Common locations
 #define POSITION_LOCATION 0
 #define TEXTURE_COORDINATE_LOCATION 1
-#define NORMAL_LOCATION 2
-#define TANGENT_LOCATION 3
-#define BONE_WEIGHTS_LOCATION 4
-#define BONE_INDICES_LOCATION 5
+#define TEXTURE_COORDINATE_LOCATION_2 2
+#define NORMAL_LOCATION 3
+#define TANGENT_LOCATION 4
+#define COLOR_LOCATION 5
+#define BONE_WEIGHTS_LOCATION 6
+#define BONE_INDICES_LOCATION 7
+
 #define SCALE_LOCATION 1
 #define ALPHA_LOCATION 2
 

+ 1 - 0
src/anki/collision/Obb.h

@@ -40,6 +40,7 @@ public:
 		, m_transposedRotation(rotation)
 		, m_extend(extend)
 	{
+		ANKI_ASSERT(m_center.w() == 0.0f && m_extend.w() == 0.0f);
 		m_transposedRotation.transposeRotationPart();
 	}
 

+ 3 - 1
src/anki/gr/Enums.h

@@ -327,6 +327,7 @@ enum class Format : U32
 	PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006,
 	PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007,
 };
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(Format, inline)
 
 inline Bool formatIsDepthStencil(const Format fmt)
 {
@@ -598,7 +599,8 @@ ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(BufferMapAccessBit, inline)
 enum class IndexType : U8
 {
 	U16,
-	U32
+	U32,
+	COUNT
 };
 /// @}
 

+ 10 - 0
src/anki/gr/gl/Common.cpp

@@ -160,6 +160,16 @@ void convertVertexFormat(Format fmt, U& compCount, GLenum& type, Bool& normalize
 		type = GL_UNSIGNED_SHORT;
 		normalized = true;
 		break;
+	case Format::R16G16B16_SFLOAT:
+		compCount = 3;
+		type = GL_HALF_FLOAT;
+		normalized = false;
+		break;
+	case Format::R16G16B16_UNORM:
+		compCount = 3;
+		type = GL_UNSIGNED_SHORT;
+		normalized = true;
+		break;
 	case Format::A2B10G10R10_SNORM_PACK32:
 		compCount = 4;
 		type = GL_INT_2_10_10_10_REV;

+ 2 - 1
src/anki/gr/vulkan/BufferImpl.cpp

@@ -138,7 +138,8 @@ Error BufferImpl::init(const BufferInitInfo& inf)
 	ANKI_TRACE_STOP_EVENT(VK_BIND_OBJECT);
 
 	m_access = access;
-	m_size = size;
+	m_size = inf.m_size;
+	m_actualSize = size;
 	m_usage = usage;
 	return Error::NONE;
 }

+ 6 - 5
src/anki/gr/vulkan/BufferImpl.h

@@ -47,15 +47,15 @@ public:
 		return m_handle;
 	}
 
-	PtrSize getSize() const
+	Bool usageValid(BufferUsageBit usage) const
 	{
-		ANKI_ASSERT(m_size);
-		return m_size;
+		return (m_usage & usage) == usage;
 	}
 
-	Bool usageValid(BufferUsageBit usage) const
+	PtrSize getActualSize() const
 	{
-		return (m_usage & usage) == usage;
+		ANKI_ASSERT(m_actualSize > 0);
+		return m_actualSize;
 	}
 
 	void computeBarrierInfo(BufferUsageBit before,
@@ -69,6 +69,7 @@ private:
 	VkBuffer m_handle = VK_NULL_HANDLE;
 	GpuMemoryHandle m_memHandle;
 	VkMemoryPropertyFlags m_memoryFlags = 0;
+	PtrSize m_actualSize = 0;
 
 #if ANKI_EXTRA_CHECKS
 	Bool8 m_mapped = false;

+ 3 - 2
src/anki/gr/vulkan/CommandBufferImpl.inl.h

@@ -620,8 +620,9 @@ inline void CommandBufferImpl::fillBuffer(BufferPtr buff, PtrSize offset, PtrSiz
 	ANKI_ASSERT(offset < impl.getSize());
 	ANKI_ASSERT((offset % 4) == 0 && "Should be multiple of 4");
 
-	size = (size == MAX_PTR_SIZE) ? (impl.getSize() - offset) : size;
-	ANKI_ASSERT(offset + size <= impl.getSize());
+	size = (size == MAX_PTR_SIZE) ? (impl.getActualSize() - offset) : size;
+	alignRoundUp(4, size); // Needs to be multiple of 4
+	ANKI_ASSERT(offset + size <= impl.getActualSize());
 	ANKI_ASSERT((size % 4) == 0 && "Should be multiple of 4");
 
 	ANKI_CMD(vkCmdFillBuffer(m_handle, impl.getHandle(), offset, size, value), ANY_OTHER_COMMAND);

+ 27 - 3
src/anki/math/Functions.h

@@ -146,7 +146,7 @@ inline T toDegrees(const T rad)
 /// @param[in] to Ending value
 /// @param[in] u The percentage from the from "from" value. Values from [0.0, 1.0]
 template<typename Type>
-static Type linearInterpolate(const Type& from, const Type& to, F32 u)
+inline Type linearInterpolate(const Type& from, const Type& to, F32 u)
 {
 	return from * (1.0f - u) + to * u;
 }
@@ -156,7 +156,7 @@ static Type linearInterpolate(const Type& from, const Type& to, F32 u)
 /// @param[in] to Ending value
 /// @param[in] u The percentage from the from "from" value. Values from [0.0, 1.0]
 template<typename Type>
-static Type cosInterpolate(const Type& from, const Type& to, F32 u)
+inline Type cosInterpolate(const Type& from, const Type& to, F32 u)
 {
 	F32 u2 = (1.0f - cos<Type>(u * PI)) / 2.0f;
 	return from * (1.0f - u2) + to * u2;
@@ -169,7 +169,7 @@ static Type cosInterpolate(const Type& from, const Type& to, F32 u)
 /// @param[in] d Point d
 /// @param[in] u The percentage from the from b point to d point. Value from [0.0, 1.0]
 template<typename Type>
-static Type cubicInterpolate(const Type& a, const Type& b, const Type& c, const Type& d, F32 u)
+inline Type cubicInterpolate(const Type& a, const Type& b, const Type& c, const Type& d, F32 u)
 {
 	F32 u2 = u * u;
 	Type a0 = d - c - a + b;
@@ -179,6 +179,30 @@ static Type cubicInterpolate(const Type& a, const Type& b, const Type& c, const
 
 	return (a0 * u * u2 + a1 * u2 + a2 * u + a3);
 }
+
+/// Pack 4 color components to R10G10B10A2 SNORM format.
+inline U32 packColorToR10G10B10A2SNorm(F32 r, F32 g, F32 b, F32 a)
+{
+	union SignedR10G10B10A10
+	{
+		struct
+		{
+			I m_x : 10;
+			I m_y : 10;
+			I m_z : 10;
+			I m_w : 2;
+		} m_unpacked;
+		U32 m_packed;
+	};
+
+	SignedR10G10B10A10 out;
+	out.m_unpacked.m_x = I(round(r * 511.0f));
+	out.m_unpacked.m_y = I(round(g * 511.0f));
+	out.m_unpacked.m_z = I(round(b * 511.0f));
+	out.m_unpacked.m_w = I(round(a * 1.0f));
+
+	return out.m_packed;
+}
 /// @}
 
 } // end namespace anki

+ 2 - 2
src/anki/physics/PhysicsCollisionShape.cpp

@@ -50,7 +50,7 @@ Error PhysicsBox::create(PhysicsCollisionShapeInitInfo& init, const Vec3& extend
 Error PhysicsTriangleSoup::create(PhysicsCollisionShapeInitInfo& init,
 	const Vec3* positions,
 	U32 positionsStride,
-	const U16* indices,
+	const U32* indices,
 	U32 indicesCount)
 {
 	m_shape = NewtonCreateTreeCollision(m_world->getNewtonWorld(), 0);
@@ -63,7 +63,7 @@ Error PhysicsTriangleSoup::create(PhysicsCollisionShapeInitInfo& init,
 	NewtonTreeCollisionBeginBuild(m_shape);
 
 	// Iterate index array
-	const U16* indicesEnd = indices + indicesCount;
+	const U32* indicesEnd = indices + indicesCount;
 	for(; indices != indicesEnd; indices += 3)
 	{
 		Array<Vec3, 3> facePos;

+ 1 - 1
src/anki/physics/PhysicsCollisionShape.h

@@ -117,7 +117,7 @@ public:
 	ANKI_USE_RESULT Error create(PhysicsCollisionShapeInitInfo& init,
 		const Vec3* positions,
 		U32 positionsStride,
-		const U16* indices,
+		const U32* indices,
 		U32 indicesCount);
 };
 /// @}

+ 19 - 45
src/anki/renderer/Dbg.cpp

@@ -26,10 +26,6 @@ Dbg::Dbg(Renderer* r)
 
 Dbg::~Dbg()
 {
-	if(m_drawer != nullptr)
-	{
-		getAllocator().deleteInstance(m_drawer);
-	}
 }
 
 Error Dbg::init(const ConfigSet& initializer)
@@ -57,52 +53,26 @@ Error Dbg::lazyInit()
 	m_fbDescr.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
 	m_fbDescr.bake();
 
-	m_drawer = getAllocator().newInstance<DebugDrawer>();
-	ANKI_CHECK(m_drawer->init(m_r));
-
 	return Error::NONE;
 }
 
-Bool Dbg::getDepthTestEnabled() const
-{
-	return m_drawer->getDepthTestEnabled();
-}
-
-void Dbg::setDepthTestEnabled(Bool enable)
-{
-	m_drawer->setDepthTestEnabled(enable);
-}
-
-void Dbg::switchDepthTestEnabled()
-{
-	Bool enabled = m_drawer->getDepthTestEnabled();
-	m_drawer->setDepthTestEnabled(!enabled);
-}
-
 void Dbg::run(RenderPassWorkContext& rgraphCtx, const RenderingContext& ctx)
 {
 	ANKI_ASSERT(m_enabled);
 
 	CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
 
-	if(!m_initialized)
-	{
-		if(lazyInit())
-		{
-			return;
-		}
-		m_initialized = true;
-	}
-
+	// Set common state
 	cmdb->setViewport(0, 0, m_r->getWidth(), m_r->getHeight());
+	cmdb->setDepthWrite(false);
 
-	m_drawer->prepareFrame(cmdb);
-	m_drawer->setViewProjectionMatrix(ctx.m_renderQueue->m_viewProjectionMatrix);
-	m_drawer->setModelMatrix(Mat4::getIdentity());
-	// m_drawer->drawGrid();
-
-	SceneDebugDrawer sceneDrawer(m_drawer);
+	rgraphCtx.bindTextureAndSampler(0,
+		0,
+		m_r->getGBuffer().getDepthRt(),
+		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
+		m_r->getNearestSampler());
 
+	// Set the context
 	RenderQueueDrawContext dctx;
 	dctx.m_viewMatrix = ctx.m_renderQueue->m_viewMatrix;
 	dctx.m_viewProjectionMatrix = ctx.m_renderQueue->m_viewProjectionMatrix;
@@ -112,23 +82,27 @@ void Dbg::run(RenderPassWorkContext& rgraphCtx, const RenderingContext& ctx)
 	dctx.m_commandBuffer = cmdb;
 	dctx.m_key = RenderingKey(Pass::GB_FS, 0, 1);
 	dctx.m_debugDraw = true;
+	dctx.m_debugDrawFlags = m_debugDrawFlags;
 
+	// Draw
 	for(const RenderableQueueElement& el : ctx.m_renderQueue->m_renderables)
 	{
 		Array<void*, 1> a = {{const_cast<void*>(el.m_userData)}};
 		el.m_callback(dctx, {&a[0], 1});
 	}
-
-	for(const PointLightQueueElement& plight : ctx.m_renderQueue->m_pointLights)
-	{
-		sceneDrawer.draw(plight);
-	}
-
-	m_drawer->finishFrame();
 }
 
 void Dbg::populateRenderGraph(RenderingContext& ctx)
 {
+	if(!m_initialized)
+	{
+		if(lazyInit())
+		{
+			return;
+		}
+		m_initialized = true;
+	}
+
 	m_runCtx.m_ctx = &ctx;
 	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
 

+ 31 - 4
src/anki/renderer/Dbg.h

@@ -8,6 +8,7 @@
 #include <anki/renderer/RendererObject.h>
 #include <anki/Gr.h>
 #include <anki/util/Enum.h>
+#include <anki/renderer/RenderQueue.h>
 
 namespace anki
 {
@@ -29,9 +30,35 @@ public:
 		m_enabled = e;
 	}
 
-	Bool getDepthTestEnabled() const;
-	void setDepthTestEnabled(Bool enable);
-	void switchDepthTestEnabled();
+	Bool getDepthTestEnabled() const
+	{
+		return m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
+	}
+
+	void setDepthTestEnabled(Bool enable)
+	{
+		m_debugDrawFlags.set(RenderQueueDebugDrawFlag::DEPTH_TEST_ON, enable);
+	}
+
+	void switchDepthTestEnabled()
+	{
+		m_debugDrawFlags.flip(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
+	}
+
+	Bool getDitheredDepthTestEnabled() const
+	{
+		return m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON);
+	}
+
+	void setDitheredDepthTestEnabled(Bool enable)
+	{
+		m_debugDrawFlags.set(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON, enable);
+	}
+
+	void switchDitheredDepthTestEnabled()
+	{
+		m_debugDrawFlags.flip(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON);
+	}
 
 anki_internal:
 	Dbg(Renderer* r);
@@ -53,7 +80,7 @@ private:
 	Bool8 m_initialized = false; ///< Lazily initialize.
 	RenderTargetDescription m_rtDescr;
 	FramebufferDescription m_fbDescr;
-	DebugDrawer* m_drawer = nullptr;
+	BitSet<U(RenderQueueDebugDrawFlag::COUNT), U32> m_debugDrawFlags = {false};
 
 	class
 	{

+ 33 - 71
src/anki/renderer/Indirect.cpp

@@ -10,7 +10,7 @@
 #include <anki/renderer/RenderQueue.h>
 #include <anki/core/Config.h>
 #include <anki/core/Trace.h>
-#include <anki/resource/MeshLoader.h>
+#include <anki/resource/MeshResource.h>
 
 namespace anki
 {
@@ -88,59 +88,6 @@ Error Indirect::initInternal(const ConfigSet& config)
 	return Error::NONE;
 }
 
-Error Indirect::loadMesh(CString fname, BufferPtr& vert, BufferPtr& idx, U32& idxCount)
-{
-	MeshLoader loader(&getResourceManager());
-	ANKI_CHECK(loader.load(fname));
-
-	PtrSize vertBuffSize = loader.getHeader().m_totalVerticesCount * sizeof(Vec3);
-	vert = getGrManager().newBuffer(BufferInitInfo(vertBuffSize,
-		BufferUsageBit::VERTEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION,
-		BufferMapAccessBit::NONE,
-		"IndirectMesh"));
-
-	idx = getGrManager().newBuffer(BufferInitInfo(loader.getIndexDataSize(),
-		BufferUsageBit::INDEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION,
-		BufferMapAccessBit::NONE,
-		"IndirectMesh"));
-
-	// Upload data
-	CommandBufferInitInfo init;
-	init.m_flags = CommandBufferFlag::SMALL_BATCH;
-	CommandBufferPtr cmdb = getGrManager().newCommandBuffer(init);
-
-	TransferGpuAllocatorHandle handle;
-	ANKI_CHECK(m_r->getResourceManager().getTransferGpuAllocator().allocate(vertBuffSize, handle));
-
-	Vec3* verts = static_cast<Vec3*>(handle.getMappedMemory());
-
-	const U8* ptr = loader.getVertexData();
-	for(U i = 0; i < loader.getHeader().m_totalVerticesCount; ++i)
-	{
-		*verts = *reinterpret_cast<const Vec3*>(ptr);
-		++verts;
-		ptr += loader.getVertexSize();
-	}
-
-	cmdb->copyBufferToBuffer(handle.getBuffer(), handle.getOffset(), vert, 0, handle.getRange());
-
-	TransferGpuAllocatorHandle handle2;
-	ANKI_CHECK(m_r->getResourceManager().getTransferGpuAllocator().allocate(loader.getIndexDataSize(), handle2));
-	void* cpuIds = handle2.getMappedMemory();
-
-	memcpy(cpuIds, loader.getIndexData(), loader.getIndexDataSize());
-
-	cmdb->copyBufferToBuffer(handle2.getBuffer(), handle2.getOffset(), idx, 0, handle2.getRange());
-	idxCount = loader.getHeader().m_totalIndicesCount;
-
-	FencePtr fence;
-	cmdb->flush(&fence);
-	m_r->getResourceManager().getTransferGpuAllocator().release(handle, fence);
-	m_r->getResourceManager().getTransferGpuAllocator().release(handle2, fence);
-
-	return Error::NONE;
-}
-
 Error Indirect::initGBuffer(const ConfigSet& config)
 {
 	m_gbuffer.m_tileSize = config.getNumber("r.indirect.reflectionResolution");
@@ -229,16 +176,9 @@ Error Indirect::initLightShading(const ConfigSet& config)
 		m_lightShading.m_slightGrProg = variant->getProgram();
 	}
 
-	// Init vert/idx buffers
-	ANKI_CHECK(loadMesh("engine_data/Plight.ankimesh",
-		m_lightShading.m_plightPositions,
-		m_lightShading.m_plightIndices,
-		m_lightShading.m_plightIdxCount));
-
-	ANKI_CHECK(loadMesh("engine_data/Slight.ankimesh",
-		m_lightShading.m_slightPositions,
-		m_lightShading.m_slightIndices,
-		m_lightShading.m_slightIdxCount));
+	// Init meshes
+	ANKI_CHECK(getResourceManager().loadResource("engine_data/Plight.ankimesh", m_lightShading.m_plightMesh, false));
+	ANKI_CHECK(getResourceManager().loadResource("engine_data/Slight.ankimesh", m_lightShading.m_slightMesh, false));
 
 	return Error::NONE;
 }
@@ -440,6 +380,30 @@ void Indirect::runGBuffer(CommandBufferPtr& cmdb)
 	cmdb->setScissor(0, 0, MAX_U32, MAX_U32);
 }
 
+void Indirect::bindVertexIndexBuffers(MeshResourcePtr& mesh, CommandBufferPtr& cmdb, U32& indexCount)
+{
+	// Attrib
+	U32 bufferBinding;
+	Format fmt;
+	PtrSize relativeOffset;
+	mesh->getVertexAttributeInfo(VertexAttributeLocation::POSITION, bufferBinding, fmt, relativeOffset);
+
+	cmdb->setVertexAttribute(0, 0, fmt, relativeOffset);
+
+	// Vert buff
+	BufferPtr buff;
+	PtrSize offset, stride;
+	mesh->getVertexBufferInfo(bufferBinding, buff, offset, stride);
+
+	cmdb->bindVertexBuffer(0, buff, offset, stride);
+
+	// Idx buff
+	IndexType idxType;
+	mesh->getIndexBufferInfo(buff, offset, indexCount, idxType);
+
+	cmdb->bindIndexBuffer(buff, offset, idxType);
+}
+
 void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 {
 	ANKI_ASSERT(faceIdx <= 6);
@@ -461,7 +425,6 @@ void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 		m_ctx.m_gbufferDepthRt,
 		TextureSubresourceInfo(DepthStencilAspectBit::DEPTH),
 		m_r->getNearestSampler());
-	cmdb->setVertexAttribute(0, 0, Format::R32G32B32_SFLOAT, 0);
 	cmdb->setViewport(0, 0, m_lightShading.m_tileSize, m_lightShading.m_tileSize);
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::ONE);
 	cmdb->setCullMode(FaceSelectionBit::FRONT);
@@ -475,9 +438,9 @@ void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 		const Mat4 invViewProjMat = rqueue.m_viewProjectionMatrix.getInverse();
 
 		// Do point lights
+		U32 indexCount;
+		bindVertexIndexBuffers(m_lightShading.m_plightMesh, cmdb, indexCount);
 		cmdb->bindShaderProgram(m_lightShading.m_plightGrProg);
-		cmdb->bindVertexBuffer(0, m_lightShading.m_plightPositions, 0, sizeof(F32) * 3);
-		cmdb->bindIndexBuffer(m_lightShading.m_plightIndices, 0, IndexType::U16);
 
 		const PointLightQueueElement* plightEl = rqueue.m_pointLights.getBegin();
 		while(plightEl != rqueue.m_pointLights.getEnd())
@@ -502,15 +465,14 @@ void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 			light->m_specularColorPad1 = plightEl->m_specularColor.xyz0();
 
 			// Draw
-			cmdb->drawElements(PrimitiveTopology::TRIANGLES, m_lightShading.m_plightIdxCount);
+			cmdb->drawElements(PrimitiveTopology::TRIANGLES, indexCount);
 
 			++plightEl;
 		}
 
 		// Do spot lights
+		bindVertexIndexBuffers(m_lightShading.m_slightMesh, cmdb, indexCount);
 		cmdb->bindShaderProgram(m_lightShading.m_slightGrProg);
-		cmdb->bindVertexBuffer(0, m_lightShading.m_slightPositions, 0, sizeof(F32) * 3);
-		cmdb->bindIndexBuffer(m_lightShading.m_slightIndices, 0, IndexType::U16);
 
 		const SpotLightQueueElement* splightEl = rqueue.m_spotLights.getBegin();
 		while(splightEl != rqueue.m_spotLights.getEnd())
@@ -553,7 +515,7 @@ void Indirect::runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx)
 			light->m_lightDirPad1 = lightDir.xyz0();
 
 			// Draw
-			cmdb->drawElements(PrimitiveTopology::TRIANGLES, m_lightShading.m_slightIdxCount);
+			cmdb->drawElements(PrimitiveTopology::TRIANGLES, indexCount);
 
 			++splightEl;
 		}

+ 4 - 8
src/anki/renderer/Indirect.h

@@ -81,14 +81,10 @@ private:
 		ShaderProgramPtr m_plightGrProg;
 		ShaderProgramPtr m_slightGrProg;
 
-		/// @name Vertex & index buffer of light volumes.
+		/// @name Meshes of light volumes.
 		/// @{
-		BufferPtr m_plightPositions;
-		BufferPtr m_plightIndices;
-		U32 m_plightIdxCount;
-		BufferPtr m_slightPositions;
-		BufferPtr m_slightIndices;
-		U32 m_slightIdxCount;
+		MeshResourcePtr m_plightMesh;
+		MeshResourcePtr m_slightMesh;
 		/// @}
 	} m_lightShading; ///< Light shading.
 
@@ -136,7 +132,6 @@ private:
 	ANKI_USE_RESULT Error initGBuffer(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initLightShading(const ConfigSet& cfg);
 	ANKI_USE_RESULT Error initIrradiance(const ConfigSet& cfg);
-	ANKI_USE_RESULT Error loadMesh(CString fname, BufferPtr& vert, BufferPtr& idx, U32& idxCount);
 
 	/// Lazily init the cache entry
 	void initCacheEntry(U32 cacheEntryIdx);
@@ -151,6 +146,7 @@ private:
 	void runLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
 	void runMipmappingOfLightShading(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
 	void runIrradiance(U32 faceIdx, RenderPassWorkContext& rgraphCtx);
+	static void bindVertexIndexBuffers(MeshResourcePtr& mesh, CommandBufferPtr& cmdb, U32& indexCount);
 
 	// A RenderPassWorkCallback for G-buffer pass
 	static void runGBufferCallback(RenderPassWorkContext& rgraphCtx)

+ 9 - 0
src/anki/renderer/RenderQueue.h

@@ -24,6 +24,14 @@ public:
 	Mat4 m_viewProjectionMatrix;
 };
 
+/// Some options that can be used as hints in debug drawcalls.
+enum class RenderQueueDebugDrawFlag : U32
+{
+	DEPTH_TEST_ON,
+	DITHERED_DEPTH_TEST_ON,
+	COUNT
+};
+
 /// Context that contains variables for drawing and will be passed to RenderQueueDrawCallback.
 class RenderQueueDrawContext final : public RenderingMatrices
 {
@@ -32,6 +40,7 @@ public:
 	CommandBufferPtr m_commandBuffer;
 	StagingGpuMemoryManager* m_stagingGpuAllocator ANKI_DBG_NULLIFY;
 	Bool m_debugDraw; ///< If true the drawcall should be drawing some kind of debug mesh.
+	BitSet<U(RenderQueueDebugDrawFlag::COUNT), U32> m_debugDrawFlags = {false};
 };
 
 /// Draw callback for drawing.

+ 7 - 6
src/anki/resource/CollisionResource.cpp

@@ -48,14 +48,15 @@ Error CollisionResource::load(const ResourceFilename& filename, Bool async)
 		CString meshfname;
 		ANKI_CHECK(valEl.getText(meshfname));
 
-		MeshLoader loader(&getManager());
+		MeshLoader loader(&getManager(), getTempAllocator());
 		ANKI_CHECK(loader.load(meshfname));
 
-		m_physicsShape = physics.newInstance<PhysicsTriangleSoup>(csInit,
-			reinterpret_cast<const Vec3*>(loader.getVertexData()),
-			loader.getVertexSize(),
-			reinterpret_cast<const U16*>(loader.getIndexData()),
-			loader.getHeader().m_totalIndicesCount);
+		DynamicArrayAuto<U32> indices(getTempAllocator());
+		DynamicArrayAuto<Vec3> positions(getTempAllocator());
+		ANKI_CHECK(loader.storeIndicesAndPosition(indices, positions));
+
+		m_physicsShape = physics.newInstance<PhysicsTriangleSoup>(
+			csInit, &positions[0], sizeof(Vec3), &indices[0], indices.getSize());
 	}
 	else
 	{

+ 17 - 0
src/anki/resource/Common.h

@@ -36,6 +36,23 @@ const U MAX_LOD_COUNT = 3;
 const U MAX_INSTANCES = 64;
 const U MAX_SUB_DRAWCALLS = 64; ///< @warning If changed don't forget to change MAX_INSTANCE_GROUPS
 const U MAX_INSTANCE_GROUPS = 7; ///< It's log2(MAX_INSTANCES) + 1
+
+/// Standard attribute locations. Should be the same as in Common.glsl.
+enum class VertexAttributeLocation : U8
+{
+	POSITION,
+	UV,
+	UV2,
+	NORMAL,
+	TANGENT,
+	COLOR,
+	BONE_WEIGHTS,
+	BONE_INDICES,
+
+	COUNT,
+	FIRST = POSITION,
+};
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(VertexAttributeLocation, inline)
 /// @}
 
 /// Deleter for ResourcePtr.

+ 231 - 144
src/anki/resource/MeshLoader.cpp

@@ -17,9 +17,6 @@ MeshLoader::MeshLoader(ResourceManager* manager)
 
 MeshLoader::~MeshLoader()
 {
-	// WARNING: Watch the order of deallocation. Reverse of the deallocation to have successful cleanups
-	m_verts.destroy(m_alloc);
-	m_indices.destroy(m_alloc);
 	m_subMeshes.destroy(m_alloc);
 }
 
@@ -28,217 +25,307 @@ Error MeshLoader::load(const ResourceFilename& filename)
 	auto& alloc = m_alloc;
 
 	// Load header
-	ResourceFilePtr file;
-	ANKI_CHECK(m_manager->getFilesystem().openFile(filename, file));
-	ANKI_CHECK(file->read(&m_header, sizeof(m_header)));
+	ANKI_CHECK(m_manager->getFilesystem().openFile(filename, m_file));
+	ANKI_CHECK(m_file->read(&m_header, sizeof(m_header)));
+	ANKI_CHECK(checkHeader());
 
-	//
-	// Check header
-	//
-	if(memcmp(&m_header.m_magic[0], "ANKIMES3", 8) != 0)
+	// Read submesh info
 	{
-		ANKI_RESOURCE_LOGE("Wrong magic word");
-		return Error::USER_DATA;
-	}
+		m_subMeshes.create(alloc, m_header.m_subMeshCount);
+		ANKI_CHECK(m_file->read(&m_subMeshes[0], m_subMeshes.getSizeInBytes()));
 
-	if(checkFormat(m_header.m_positionsFormat, "positions", true)
-		|| checkFormat(m_header.m_normalsFormat, "normals", true)
-		|| checkFormat(m_header.m_tangentsFormat, "tangents", true)
-		|| checkFormat(m_header.m_colorsFormat, "colors", false) || checkFormat(m_header.m_uvsFormat, "UVs", true)
-		|| checkFormat(m_header.m_boneWeightsFormat, "bone weights", false)
-		|| checkFormat(m_header.m_boneIndicesFormat, "bone ids", false)
-		|| checkFormat(m_header.m_indicesFormat, "indices format", true))
-	{
-		return Error::USER_DATA;
-	}
+		// Checks
+		const U32 indicesPerFace = !!(m_header.m_flags & MeshBinaryFile::Flag::QUAD) ? 4 : 3;
+		U idxSum = 0;
+		for(U i = 0; i < m_subMeshes.getSize(); i++)
+		{
+			const MeshBinaryFile::SubMesh& sm = m_subMeshes[0];
+			if(sm.m_firstIndex != idxSum || (sm.m_indexCount % indicesPerFace) != 0)
+			{
+				ANKI_RESOURCE_LOGE("Incorrect sub mesh info");
+				return Error::USER_DATA;
+			}
+
+			for(U d = 0; d < 3; ++d)
+			{
+				if(sm.m_aabbMin[i] >= sm.m_aabbMax[i])
+				{
+					ANKI_RESOURCE_LOGE("Wrong bounding box");
+					return Error::USER_DATA;
+				}
+			}
+
+			idxSum += sm.m_indexCount;
+		}
 
-	// Check positions
-	if(m_header.m_positionsFormat.m_components != ComponentFormat::R32G32B32
-		|| m_header.m_positionsFormat.m_transform != FormatTransform::FLOAT)
-	{
-		ANKI_RESOURCE_LOGE("Incorrect/unsupported positions format");
-		return Error::USER_DATA;
+		if(idxSum != m_header.m_totalIndexCount)
+		{
+			ANKI_RESOURCE_LOGE("Incorrect sub mesh info");
+			return Error::USER_DATA;
+		}
 	}
 
-	// Check normals
-	if(m_header.m_normalsFormat.m_components != ComponentFormat::R10G10B10A2
-		|| m_header.m_normalsFormat.m_transform != FormatTransform::SNORM)
+	// Read vert buffer info
 	{
-		ANKI_RESOURCE_LOGE("Incorrect/unsupported normals format");
-		return Error::USER_DATA;
-	}
+		U32 vertBufferMask = 0;
+		U32 vertBufferCount = 0;
+		for(const MeshBinaryFile::VertexAttribute& attrib : m_header.m_vertexAttributes)
+		{
+			if(attrib.m_format == Format::NONE)
+			{
+				continue;
+			}
 
-	// Check tangents
-	if(m_header.m_tangentsFormat.m_components != ComponentFormat::R10G10B10A2
-		|| m_header.m_tangentsFormat.m_transform != FormatTransform::SNORM)
-	{
-		ANKI_RESOURCE_LOGE("Incorrect/unsupported tangents format");
-		return Error::USER_DATA;
-	}
+			vertBufferCount = max(attrib.m_bufferBinding + 1, vertBufferCount);
+			vertBufferMask |= 1 << attrib.m_bufferBinding;
+		}
 
-	// Check colors
-	if(m_header.m_colorsFormat.m_components != ComponentFormat::NONE
-		|| m_header.m_colorsFormat.m_transform != FormatTransform::NONE)
-	{
-		ANKI_RESOURCE_LOGE("Incorrect/unsupported color format");
-		return Error::USER_DATA;
-	}
+		if(U(__builtin_popcount(vertBufferMask)) != vertBufferCount)
+		{
+			ANKI_RESOURCE_LOGE("Problem in vertex buffers");
+			return Error::USER_DATA;
+		}
 
-	// Check UVs
-	if(m_header.m_uvsFormat.m_components != ComponentFormat::R16G16
-		|| m_header.m_uvsFormat.m_transform != FormatTransform::FLOAT)
-	{
-		ANKI_RESOURCE_LOGE("Incorrect/unsupported UVs format");
-		return Error::USER_DATA;
+		if(vertBufferCount != m_header.m_vertexBufferCount)
+		{
+			ANKI_RESOURCE_LOGE("Wrong vertex buffer count in the header");
+			return Error::USER_DATA;
+		}
 	}
 
-	Bool hasBoneInfo = false;
-	if(m_header.m_boneWeightsFormat.m_components != ComponentFormat::NONE)
+	// Count and check the file size
 	{
-		// Has bone info
+		U32 totalSize = sizeof(m_header);
 
-		hasBoneInfo = true;
+		totalSize += sizeof(MeshBinaryFile::SubMesh) * m_header.m_subMeshCount;
+		totalSize += getIndexBufferSize();
 
-		// Bone weights
-		if(m_header.m_boneWeightsFormat.m_components != ComponentFormat::R8G8B8A8
-			|| m_header.m_boneWeightsFormat.m_transform != FormatTransform::UNORM)
+		for(U i = 0; i < m_header.m_vertexBufferCount; ++i)
 		{
-			ANKI_RESOURCE_LOGE("Incorrect/unsupported UVs format");
-			return Error::USER_DATA;
+			totalSize += m_header.m_vertexBuffers[i].m_vertexStride * m_header.m_totalVertexCount;
 		}
 
-		// Bone indices
-		if(m_header.m_boneIndicesFormat.m_components != ComponentFormat::R16G16B16A16
-			|| m_header.m_boneIndicesFormat.m_transform != FormatTransform::UINT)
+		if(totalSize != m_file->getSize())
 		{
-			ANKI_RESOURCE_LOGE("Incorrect/unsupported UVs format");
+			ANKI_RESOURCE_LOGE("Unexpected file size");
 			return Error::USER_DATA;
 		}
 	}
-	else
-	{
-		// No bone info
 
-		// Bone weights
-		if(m_header.m_boneWeightsFormat.m_components != ComponentFormat::NONE
-			|| m_header.m_boneWeightsFormat.m_transform != FormatTransform::NONE)
-		{
-			ANKI_RESOURCE_LOGE("Incorrect/unsupported UVs format");
-			return Error::USER_DATA;
-		}
+	return Error::NONE;
+}
 
-		// Bone indices
-		if(m_header.m_boneIndicesFormat.m_components != ComponentFormat::NONE
-			|| m_header.m_boneIndicesFormat.m_transform != FormatTransform::NONE)
+Error MeshLoader::checkFormat(VertexAttributeLocation type, ConstWeakArray<Format> supportedFormats) const
+{
+	const MeshBinaryFile::VertexAttribute& attrib = m_header.m_vertexAttributes[type];
+
+	// Check format
+	Bool found = false;
+	for(Format fmt : supportedFormats)
+	{
+		if(fmt == attrib.m_format)
 		{
-			ANKI_RESOURCE_LOGE("Incorrect/unsupported UVs format");
-			return Error::USER_DATA;
+			found = true;
+			break;
 		}
 	}
 
-	// Check indices
-	U indicesPerFace = ((m_header.m_flags & Flag::QUADS) == Flag::QUADS) ? 4 : 3;
-	if(m_header.m_totalIndicesCount < indicesPerFace || (m_header.m_totalIndicesCount % indicesPerFace) != 0
-		|| m_header.m_totalIndicesCount > MAX_U16 || m_header.m_indicesFormat.m_components != ComponentFormat::R16
-		|| m_header.m_indicesFormat.m_transform != FormatTransform::UINT)
+	if(!found)
 	{
-		// Only 16bit indices are supported for now
-		ANKI_RESOURCE_LOGE("Incorrect/unsuported index info");
+		ANKI_RESOURCE_LOGE(
+			"Vertex attribute %u has unsupported format %u", U(type), U(m_header.m_vertexAttributes[type].m_format));
 		return Error::USER_DATA;
 	}
 
-	// Check other
-	if(m_header.m_totalVerticesCount == 0)
+	if(!attrib.m_format)
+	{
+		// Attrib is not in use, no more checks
+		return Error::NONE;
+	}
+
+	// Scale should be 1.0 for now
+	if(attrib.m_scale != 1.0f)
 	{
-		ANKI_RESOURCE_LOGE("Incorrect/unsuported vertex count");
+		ANKI_RESOURCE_LOGE("Vertex attribute %u should have 1.0 scale", U(type));
 		return Error::USER_DATA;
 	}
 
-	if(m_header.m_uvsChannelCount != 1)
+	return Error::NONE;
+}
+
+Error MeshLoader::checkHeader() const
+{
+	const MeshBinaryFile::Header& h = m_header;
+
+	// Header
+	if(memcmp(&h.m_magic[0], MeshBinaryFile::MAGIC, 8) != 0)
 	{
-		ANKI_RESOURCE_LOGE("Incorrect/unsuported UVs channel count");
+		ANKI_RESOURCE_LOGE("Wrong magic word");
 		return Error::USER_DATA;
 	}
 
-	if(m_header.m_subMeshCount == 0)
+	// Flags
+	if((h.m_flags & ~MeshBinaryFile::Flag::ALL) != MeshBinaryFile::Flag::NONE)
 	{
-		ANKI_RESOURCE_LOGE("Incorrect/unsuported submesh count");
+		ANKI_RESOURCE_LOGE("Wrong header flags");
 		return Error::USER_DATA;
 	}
 
-	//
-	// Read submesh info
-	//
-	m_subMeshes.create(alloc, m_header.m_subMeshCount);
-	ANKI_CHECK(file->read(&m_subMeshes[0], m_subMeshes.getSizeInBytes()));
+	// Attributes
+	ANKI_CHECK(checkFormat(
+		VertexAttributeLocation::POSITION, Array<Format, 2>{{Format::R16G16B16_SFLOAT, Format::R32G32B32_SFLOAT}}));
+	ANKI_CHECK(checkFormat(VertexAttributeLocation::NORMAL, Array<Format, 1>{{Format::A2B10G10R10_SNORM_PACK32}}));
+	ANKI_CHECK(checkFormat(VertexAttributeLocation::TANGENT, Array<Format, 1>{{Format::A2B10G10R10_SNORM_PACK32}}));
+	ANKI_CHECK(
+		checkFormat(VertexAttributeLocation::UV, Array<Format, 2>{{Format::R16G16_UNORM, Format::R16G16_SFLOAT}}));
+	ANKI_CHECK(checkFormat(
+		VertexAttributeLocation::BONE_INDICES, Array<Format, 2>{{Format::NONE, Format::R16G16B16A16_UINT}}));
+	ANKI_CHECK(
+		checkFormat(VertexAttributeLocation::BONE_WEIGHTS, Array<Format, 2>{{Format::NONE, Format::R8G8B8A8_UNORM}}));
+
+	// Indices format
+	if(h.m_indexType != IndexType::U16 && h.m_indexType != IndexType::U32)
+	{
+		ANKI_RESOURCE_LOGE("Wrong format for indices");
+		return Error::USER_DATA;
+	}
 
-	// Checks
-	U idxSum = 0;
-	for(U i = 0; i < m_subMeshes.getSize(); i++)
+	// m_totalIndexCount
+	const U indicesPerFace = !!(h.m_flags & MeshBinaryFile::Flag::QUAD) ? 4 : 3;
+	if(h.m_totalIndexCount == 0 || (h.m_totalIndexCount % indicesPerFace) != 0)
 	{
-		const SubMesh& sm = m_subMeshes[0];
-		if(sm.m_firstIndex != idxSum || sm.m_indicesCount < 3)
-		{
-			ANKI_RESOURCE_LOGE("Incorrect sub mesh info");
-			return Error::USER_DATA;
-		}
+		ANKI_RESOURCE_LOGE("Wrong index count");
+		return Error::USER_DATA;
+	}
 
-		idxSum += sm.m_indicesCount;
+	// m_totalVertexCount
+	if(h.m_totalVertexCount == 0)
+	{
+		ANKI_RESOURCE_LOGE("Wrong vertex count");
+		return Error::USER_DATA;
 	}
 
-	if(idxSum != m_header.m_totalIndicesCount)
+	// m_subMeshCount
+	if(h.m_subMeshCount == 0)
 	{
-		ANKI_RESOURCE_LOGE("Incorrect sub mesh info");
+		ANKI_RESOURCE_LOGE("Wrong submesh count");
 		return Error::USER_DATA;
 	}
 
-	//
-	// Read indices
-	//
-	m_indices.create(alloc, m_header.m_totalIndicesCount * sizeof(U16));
-	ANKI_CHECK(file->read(&m_indices[0], m_indices.getSizeInBytes()));
+	// AABB
+	for(U d = 0; d < 3; ++d)
+	{
+		if(h.m_aabbMin[d] >= h.m_aabbMax[d])
+		{
+			ANKI_RESOURCE_LOGE("Wrong bounding box");
+			return Error::USER_DATA;
+		}
+	}
+
+	return Error::NONE;
+}
 
-	//
-	// Read vertices
-	//
-	m_vertSize = 3 * sizeof(F32) // pos
-				 + 1 * sizeof(U32) // norm
-				 + 1 * sizeof(U32) // tang
-				 + 2 * sizeof(U16) // uvs
-				 + ((hasBoneInfo) ? (4 * sizeof(U8) + 4 * sizeof(U16)) : 0);
+Error MeshLoader::storeIndexBuffer(void* ptr, PtrSize size)
+{
+	ANKI_ASSERT(isLoaded());
+	ANKI_ASSERT(size == getIndexBufferSize());
+	ANKI_ASSERT(m_loadedChunk == 0);
 
-	m_verts.create(alloc, m_header.m_totalVerticesCount * m_vertSize);
-	ANKI_CHECK(file->read(&m_verts[0], m_verts.getSizeInBytes()));
+	if(ptr)
+	{
+		ANKI_CHECK(m_file->read(ptr, size));
+	}
+	else
+	{
+		ANKI_CHECK(m_file->seek(size, ResourceFile::SeekOrigin::CURRENT));
+	}
 
+	++m_loadedChunk;
 	return Error::NONE;
 }
 
-Error MeshLoader::checkFormat(const Format& fmt, const CString& attrib, Bool cannotBeEmpty)
+Error MeshLoader::storeVertexBuffer(U32 bufferIdx, void* ptr, PtrSize size)
 {
-	if(fmt.m_components >= ComponentFormat::COUNT)
+	ANKI_ASSERT(isLoaded());
+	ANKI_ASSERT(bufferIdx < m_header.m_vertexBufferCount);
+	ANKI_ASSERT(size == m_header.m_vertexBuffers[bufferIdx].m_vertexStride * m_header.m_totalVertexCount);
+	ANKI_ASSERT(m_loadedChunk == bufferIdx + 1);
+
+	if(ptr)
 	{
-		ANKI_RESOURCE_LOGE("Incorrect component format for %s", &attrib[0]);
-		return Error::USER_DATA;
+		ANKI_CHECK(m_file->read(ptr, size));
 	}
-
-	if(fmt.m_transform >= FormatTransform::COUNT)
+	else
 	{
-		ANKI_RESOURCE_LOGE("Incorrect format transform for %s", &attrib[0]);
-		return Error::USER_DATA;
+		ANKI_CHECK(m_file->seek(size, ResourceFile::SeekOrigin::CURRENT));
 	}
 
-	if(cannotBeEmpty)
+	++m_loadedChunk;
+	return Error::NONE;
+}
+
+Error MeshLoader::storeIndicesAndPosition(DynamicArrayAuto<U32>& indices, DynamicArrayAuto<Vec3>& positions)
+{
+	// Store indices
 	{
-		if(fmt.m_components == ComponentFormat::NONE)
+		indices.resize(m_header.m_totalIndexCount);
+
+		// Create staging buff
+		const PtrSize idxBufferSize = getIndexBufferSize();
+		DynamicArrayAuto<U8> staging(m_alloc);
+		staging.create(idxBufferSize);
+
+		// Store to staging buff
+		ANKI_CHECK(storeIndexBuffer(&staging[0], staging.getSizeInBytes()));
+
+		// Copy
+		for(U i = 0; i < m_header.m_totalIndexCount; ++i)
 		{
-			ANKI_RESOURCE_LOGE("Format cannot be zero for %s", &attrib[0]);
-			return Error::USER_DATA;
+			if(m_header.m_indexType == IndexType::U32)
+			{
+				indices[i] = *reinterpret_cast<U32*>(&staging[i * 4]);
+			}
+			else
+			{
+				indices[i] = *reinterpret_cast<U16*>(&staging[i * 2]);
+			}
 		}
+	}
 
-		if(fmt.m_transform == FormatTransform::NONE)
+	// Store positions
+	{
+		positions.resize(m_header.m_totalVertexCount);
+
+		const MeshBinaryFile::VertexAttribute& attrib = m_header.m_vertexAttributes[VertexAttributeLocation::POSITION];
+		const MeshBinaryFile::VertexBuffer& buffInfo = m_header.m_vertexBuffers[attrib.m_bufferBinding];
+
+		// Create staging buff
+		const PtrSize vertBuffSize = m_header.m_totalVertexCount * buffInfo.m_vertexStride;
+		DynamicArrayAuto<U8> staging(m_alloc);
+		staging.create(vertBuffSize);
+
+		// Store to staging buff
+		ANKI_CHECK(storeVertexBuffer(attrib.m_bufferBinding, &staging[0], staging.getSizeInBytes()));
+
+		// Copy
+		for(U i = 0; i < m_header.m_totalVertexCount; ++i)
 		{
-			ANKI_RESOURCE_LOGE("Transform cannot be zero for %s", &attrib[0]);
-			return Error::USER_DATA;
+			Vec3 vert;
+			if(attrib.m_format == Format::R32G32B32_SFLOAT)
+			{
+				vert = *reinterpret_cast<Vec3*>(&staging[i * buffInfo.m_vertexStride + attrib.m_relativeOffset]);
+			}
+			else
+			{
+				ANKI_ASSERT(attrib.m_format == Format::R16G16B16_SFLOAT);
+				F16* f16 = reinterpret_cast<F16*>(&staging[i * buffInfo.m_vertexStride + attrib.m_relativeOffset]);
+
+				vert[0] = f16[0].toF32();
+				vert[1] = f16[1].toF32();
+				vert[2] = f16[2].toF32();
+			}
+
+			positions[i] = vert;
 		}
 	}
 

+ 67 - 103
src/anki/resource/MeshLoader.h

@@ -6,8 +6,10 @@
 #pragma once
 
 #include <anki/resource/Common.h>
+#include <anki/resource/ResourceFilesystem.h>
 #include <anki/Math.h>
 #include <anki/util/Enum.h>
+#include <anki/util/WeakArray.h>
 
 namespace anki
 {
@@ -15,93 +17,68 @@ namespace anki
 /// @addtogroup resource
 /// @{
 
-/// Mesh data. This class loads the mesh file and the Mesh class loads it to the CPU.
-class MeshLoader
+/// Information to decode mesh binary files.
+class MeshBinaryFile
 {
 public:
-	/// Type of the components.
-	enum class ComponentFormat : U32
-	{
-		NONE,
-
-		R8,
-		R8G8,
-		R8G8B8,
-		R8G8B8A8,
+	static constexpr const char* MAGIC = "ANKIMES4";
 
-		R16,
-		R16G16,
-		R16G16B16,
-		R16G16B16A16,
-
-		R32,
-		R32G32,
-		R32G32B32,
-		R32G32B32A32,
-
-		R10G10B10A2,
+	enum class Flag : U32
+	{
+		NONE = 0,
+		QUAD = 1 << 0,
 
-		COUNT
+		ALL = QUAD,
 	};
+	ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(Flag, friend)
 
-	enum class FormatTransform : U32
+	struct VertexBuffer
 	{
-		NONE,
-
-		UNORM,
-		SNORM,
-		UINT,
-		SINT,
-		FLOAT,
-
-		COUNT
+		U32 m_vertexStride;
 	};
 
-	struct Format
+	struct VertexAttribute
 	{
-		ComponentFormat m_components = ComponentFormat::NONE;
-		FormatTransform m_transform = FormatTransform::NONE;
+		U32 m_bufferBinding;
+		Format m_format;
+		U32 m_relativeOffset;
+		F32 m_scale;
 	};
 
-	enum class Flag : U32
+	struct SubMesh
 	{
-		NONE = 0,
-		QUADS = 1 << 0
+		U32 m_firstIndex;
+		U32 m_indexCount;
+		Vec3 m_aabbMin; ///< Bounding box min.
+		Vec3 m_aabbMax; ///< Bounding box max.
 	};
-	ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(Flag, friend);
 
 	struct Header
 	{
-		Array<U8, 8> m_magic; ///< Magic word.
-		U32 m_flags;
-		U32 m_flags2;
-		Format m_positionsFormat;
-		Format m_normalsFormat;
-		Format m_tangentsFormat;
-		Format m_colorsFormat; ///< Vertex color.
-		Format m_uvsFormat;
-		Format m_boneWeightsFormat;
-		Format m_boneIndicesFormat;
-		Format m_indicesFormat; ///< Vertex indices.
-
-		U32 m_totalIndicesCount;
-		U32 m_totalVerticesCount;
-		/// Number of UV sets. Eg one for normal diffuse and another for lightmaps.
-		U32 m_uvsChannelCount;
-		U32 m_subMeshCount;
+		char m_magic[8]; ///< Magic word.
+		Flag m_flags;
 
-		U8 m_padding[32];
-	};
+		Array<VertexBuffer, U32(VertexAttributeLocation::COUNT)> m_vertexBuffers;
+		U32 m_vertexBufferCount;
 
-	static_assert(sizeof(Header) == 128, "Check size of struct");
+		Array<VertexAttribute, U32(VertexAttributeLocation::COUNT)> m_vertexAttributes;
 
-	class SubMesh
-	{
-	public:
-		U32 m_firstIndex = 0;
-		U32 m_indicesCount = 0;
+		IndexType m_indexType;
+		U8 _padding[3];
+
+		U32 m_totalIndexCount;
+		U32 m_totalVertexCount;
+		U32 m_subMeshCount;
+
+		Vec3 m_aabbMin; ///< Bounding box min.
+		Vec3 m_aabbMax; ///< Bounding box max.
 	};
+};
 
+/// Mesh data. This class loads the mesh file and the Mesh class loads it to the CPU.
+class MeshLoader
+{
+public:
 	MeshLoader(ResourceManager* manager);
 
 	MeshLoader(ResourceManager* manager, GenericMemoryPoolAllocator<U8> alloc)
@@ -114,67 +91,54 @@ public:
 
 	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
 
-	const Header& getHeader() const
-	{
-		ANKI_ASSERT(isLoaded());
-		return m_header;
-	}
-
-	const U8* getVertexData() const
-	{
-		ANKI_ASSERT(isLoaded());
-		return &m_verts[0];
-	}
+	ANKI_USE_RESULT Error storeIndexBuffer(void* ptr, PtrSize size);
 
-	PtrSize getVertexDataSize() const
-	{
-		ANKI_ASSERT(isLoaded());
-		return m_verts.getSizeInBytes();
-	}
+	ANKI_USE_RESULT Error storeVertexBuffer(U32 bufferIdx, void* ptr, PtrSize size);
 
-	PtrSize getVertexSize() const
-	{
-		ANKI_ASSERT(isLoaded());
-		return m_vertSize;
-	}
+	/// Instead of calling storeIndexBuffer and storeVertexBuffer use this method to get those buffers into the CPU.
+	ANKI_USE_RESULT Error storeIndicesAndPosition(DynamicArrayAuto<U32>& indices, DynamicArrayAuto<Vec3>& positions);
 
-	const U8* getIndexData() const
+	const MeshBinaryFile::Header& getHeader() const
 	{
 		ANKI_ASSERT(isLoaded());
-		return &m_indices[0];
+		return m_header;
 	}
 
-	PtrSize getIndexDataSize() const
+	Bool hasBoneInfo() const
 	{
 		ANKI_ASSERT(isLoaded());
-		return m_indices.getSizeInBytes();
+		return m_header.m_vertexAttributes[VertexAttributeLocation::BONE_INDICES].m_format != Format::NONE;
 	}
 
-	Bool hasBoneInfo() const
+	ConstWeakArray<MeshBinaryFile::SubMesh> getSubMeshes() const
 	{
-		ANKI_ASSERT(isLoaded());
-		return m_header.m_boneWeightsFormat.m_components != ComponentFormat::NONE;
+		return ConstWeakArray<MeshBinaryFile::SubMesh>(m_subMeshes);
 	}
 
 private:
-	template<typename T>
-	using MDynamicArray = DynamicArray<T>;
-
 	ResourceManager* m_manager;
 	GenericMemoryPoolAllocator<U8> m_alloc;
-	Header m_header;
 
-	MDynamicArray<U8> m_verts;
-	MDynamicArray<U8> m_indices;
-	MDynamicArray<SubMesh> m_subMeshes;
-	U8 m_vertSize = 0;
+	ResourceFilePtr m_file;
+
+	MeshBinaryFile::Header m_header;
+
+	DynamicArray<MeshBinaryFile::SubMesh> m_subMeshes;
+
+	U32 m_loadedChunk = 0; ///< Because the store methods need to be called in sequence.
 
 	Bool isLoaded() const
 	{
-		return m_verts.getSize() > 0;
+		return m_file.get() != nullptr;
+	}
+
+	PtrSize getIndexBufferSize() const
+	{
+		return m_header.m_totalIndexCount * ((m_header.m_indexType == IndexType::U16) ? 2 : 4);
 	}
 
-	static ANKI_USE_RESULT Error checkFormat(const Format& fmt, const CString& attrib, Bool cannotBeEmpty);
+	ANKI_USE_RESULT Error checkHeader() const;
+	ANKI_USE_RESULT Error checkFormat(VertexAttributeLocation type, ConstWeakArray<Format> supportedFormats) const;
 };
 /// @}
 

+ 103 - 64
src/anki/resource/MeshResource.cpp

@@ -16,14 +16,12 @@ namespace anki
 class MeshResource::LoadContext
 {
 public:
-	ResourceManager* m_manager ANKI_DBG_NULLIFY;
+	MeshResource* m_mesh;
 	MeshLoader m_loader;
-	BufferPtr m_vertBuff;
-	BufferPtr m_indicesBuff;
 
-	LoadContext(ResourceManager* manager, GenericMemoryPoolAllocator<U8> alloc)
-		: m_manager(manager)
-		, m_loader(manager, alloc)
+	LoadContext(MeshResource* mesh, GenericMemoryPoolAllocator<U8> alloc)
+		: m_mesh(mesh)
+		, m_loader(&mesh->getManager(), alloc)
 	{
 	}
 };
@@ -34,14 +32,14 @@ class MeshResource::LoadTask : public AsyncLoaderTask
 public:
 	MeshResource::LoadContext m_ctx;
 
-	LoadTask(ResourceManager* manager)
-		: m_ctx(manager, manager->getAsyncLoader().getAllocator())
+	LoadTask(MeshResource* mesh)
+		: m_ctx(mesh, mesh->getManager().getAsyncLoader().getAllocator())
 	{
 	}
 
 	Error operator()(AsyncLoaderTaskContext& ctx) final
 	{
-		return MeshResource::load(m_ctx);
+		return m_ctx.m_mesh->loadAsync(m_ctx.m_loader);
 	}
 };
 
@@ -53,23 +51,24 @@ MeshResource::MeshResource(ResourceManager* manager)
 MeshResource::~MeshResource()
 {
 	m_subMeshes.destroy(getAllocator());
+	m_vertBufferInfos.destroy(getAllocator());
 }
 
 Bool MeshResource::isCompatible(const MeshResource& other) const
 {
-	return hasBoneWeights() == other.hasBoneWeights() && getSubMeshesCount() == other.getSubMeshesCount()
-		   && m_texChannelsCount == other.m_texChannelsCount;
+	return hasBoneWeights() == other.hasBoneWeights() && getSubMeshCount() == other.getSubMeshCount()
+		   && m_texChannelCount == other.m_texChannelCount;
 }
 
 Error MeshResource::load(const ResourceFilename& filename, Bool async)
 {
 	LoadTask* task;
 	LoadContext* ctx;
-	LoadContext localCtx(&getManager(), getTempAllocator());
+	LoadContext localCtx(this, getTempAllocator());
 
 	if(async)
 	{
-		task = getManager().getAsyncLoader().newTask<LoadTask>(&getManager());
+		task = getManager().getAsyncLoader().newTask<LoadTask>(this);
 		ctx = &task->m_ctx;
 	}
 	else
@@ -78,74 +77,107 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 		ctx = &localCtx;
 	}
 
+	// Open file
 	MeshLoader& loader = ctx->m_loader;
 	ANKI_CHECK(loader.load(filename));
+	const MeshBinaryFile::Header& header = loader.getHeader();
 
-	const MeshLoader::Header& header = loader.getHeader();
-	m_indicesCount = header.m_totalIndicesCount;
+	// Get submeshes
+	m_subMeshes.create(getAllocator(), header.m_subMeshCount);
+	for(U i = 0; i < m_subMeshes.getSize(); ++i)
+	{
+		m_subMeshes[i].m_firstIndex = loader.getSubMeshes()[i].m_firstIndex;
+		m_subMeshes[i].m_indexCount = loader.getSubMeshes()[i].m_indexCount;
 
-	m_vertSize = loader.getVertexSize();
+		const Vec3 obbCenter = (loader.getSubMeshes()[i].m_aabbMax + loader.getSubMeshes()[i].m_aabbMin) / 2.0f;
+		const Vec3 obbExtend = loader.getSubMeshes()[i].m_aabbMax - obbCenter;
+		m_subMeshes[i].m_obb = Obb(obbCenter.xyz0(), Mat3x4::getIdentity(), obbExtend.xyz0());
+	}
 
-	PtrSize vertexSize = loader.getVertexSize();
-	m_obb.setFromPointCloud(
-		loader.getVertexData(), header.m_totalVerticesCount, vertexSize, loader.getVertexDataSize());
-	ANKI_ASSERT(m_indicesCount > 0);
-	ANKI_ASSERT(m_indicesCount % 3 == 0 && "Expecting triangles");
+	// Index stuff
+	m_indexCount = header.m_totalIndexCount;
+	ANKI_ASSERT((m_indexCount % 3) == 0 && "Expecting triangles");
+	m_indexType = header.m_indexType;
 
-	// Set the non-VBO members
-	m_vertsCount = header.m_totalVerticesCount;
-	ANKI_ASSERT(m_vertsCount > 0);
+	const PtrSize indexBuffSize = m_indexCount * ((m_indexType == IndexType::U32) ? 4 : 2);
 
-	m_texChannelsCount = header.m_uvsChannelCount;
-	m_weights = loader.hasBoneInfo();
+	m_indexBuff = getManager().getGrManager().newBuffer(BufferInitInfo(indexBuffSize,
+		BufferUsageBit::INDEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION | BufferUsageBit::FILL,
+		BufferMapAccessBit::NONE,
+		"MeshIdx"));
 
-	// Allocate the buffers
-	GrManager& gr = getManager().getGrManager();
+	// Vertex stuff
+	m_vertCount = header.m_totalVertexCount;
+	m_vertBufferInfos.create(getAllocator(), header.m_vertexBufferCount);
+
+	PtrSize totalVertexBuffSize = 0;
+	for(U i = 0; i < header.m_vertexBufferCount; ++i)
+	{
+		alignRoundUp(VERTEX_BUFFER_ALIGNMENT, totalVertexBuffSize);
+
+		m_vertBufferInfos[i].m_offset = totalVertexBuffSize;
+		m_vertBufferInfos[i].m_stride = header.m_vertexBuffers[i].m_vertexStride;
+
+		totalVertexBuffSize += m_vertCount * m_vertBufferInfos[i].m_stride;
+	}
 
-	m_vertBuff = gr.newBuffer(BufferInitInfo(loader.getVertexDataSize(),
+	m_vertBuff = getManager().getGrManager().newBuffer(BufferInitInfo(totalVertexBuffSize,
 		BufferUsageBit::VERTEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION | BufferUsageBit::FILL,
 		BufferMapAccessBit::NONE,
 		"MeshVert"));
 
-	m_indicesBuff = gr.newBuffer(BufferInitInfo(loader.getIndexDataSize(),
-		BufferUsageBit::INDEX | BufferUsageBit::BUFFER_UPLOAD_DESTINATION | BufferUsageBit::FILL,
-		BufferMapAccessBit::NONE,
-		"MeshIdx"));
+	m_texChannelCount = !!header.m_vertexAttributes[VertexAttributeLocation::UV2].m_format ? 2 : 1;
 
-	// Clear them
+	for(VertexAttributeLocation attrib = VertexAttributeLocation::FIRST; attrib < VertexAttributeLocation::COUNT;
+		++attrib)
+	{
+		AttribInfo& out = m_attribs[attrib];
+		const MeshBinaryFile::VertexAttribute& in = header.m_vertexAttributes[attrib];
+
+		if(!!in.m_format)
+		{
+			out.m_fmt = in.m_format;
+			out.m_relativeOffset = in.m_relativeOffset;
+			out.m_buffIdx = in.m_bufferBinding;
+			ANKI_ASSERT(in.m_scale == 1.0f && "Not supported ATM");
+		}
+	}
+
+	// Other
+	const Vec3 obbCenter = (header.m_aabbMax + header.m_aabbMin) / 2.0f;
+	const Vec3 obbExtend = header.m_aabbMax - obbCenter;
+	m_obb = Obb(obbCenter.xyz0(), Mat3x4::getIdentity(), obbExtend.xyz0());
+
+	// Clear the buffers
 	CommandBufferInitInfo cmdbinit;
 	cmdbinit.m_flags = CommandBufferFlag::SMALL_BATCH;
-	CommandBufferPtr cmdb = gr.newCommandBuffer(cmdbinit);
+	CommandBufferPtr cmdb = getManager().getGrManager().newCommandBuffer(cmdbinit);
 
 	cmdb->fillBuffer(m_vertBuff, 0, MAX_PTR_SIZE, 0);
-	cmdb->fillBuffer(m_indicesBuff, 0, MAX_PTR_SIZE, 0);
+	cmdb->fillBuffer(m_indexBuff, 0, MAX_PTR_SIZE, 0);
 
 	cmdb->setBufferBarrier(m_vertBuff, BufferUsageBit::FILL, BufferUsageBit::VERTEX, 0, MAX_PTR_SIZE);
-	cmdb->setBufferBarrier(m_indicesBuff, BufferUsageBit::FILL, BufferUsageBit::INDEX, 0, MAX_PTR_SIZE);
+	cmdb->setBufferBarrier(m_indexBuff, BufferUsageBit::FILL, BufferUsageBit::INDEX, 0, MAX_PTR_SIZE);
 
 	cmdb->flush();
 
 	// Submit the loading task
-	ctx->m_vertBuff = m_vertBuff;
-	ctx->m_indicesBuff = m_indicesBuff;
-
 	if(async)
 	{
 		getManager().getAsyncLoader().submitTask(task);
 	}
 	else
 	{
-		ANKI_CHECK(load(*ctx));
+		ANKI_CHECK(loadAsync(loader));
 	}
 
 	return Error::NONE;
 }
 
-Error MeshResource::load(LoadContext& ctx)
+Error MeshResource::loadAsync(MeshLoader& loader) const
 {
-	GrManager& gr = ctx.m_manager->getGrManager();
-	TransferGpuAllocator& transferAlloc = ctx.m_manager->getTransferGpuAllocator();
-	const MeshLoader& loader = ctx.m_loader;
+	GrManager& gr = getManager().getGrManager();
+	TransferGpuAllocator& transferAlloc = getManager().getTransferGpuAllocator();
 	Array<TransferGpuAllocatorHandle, 2> handles;
 
 	CommandBufferInitInfo cmdbinit;
@@ -154,43 +186,50 @@ Error MeshResource::load(LoadContext& ctx)
 
 	// Set barriers
 	cmdb->setBufferBarrier(
-		ctx.m_vertBuff, BufferUsageBit::VERTEX, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, 0, MAX_PTR_SIZE);
+		m_vertBuff, BufferUsageBit::VERTEX, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, 0, MAX_PTR_SIZE);
 	cmdb->setBufferBarrier(
-		ctx.m_indicesBuff, BufferUsageBit::INDEX, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, 0, MAX_PTR_SIZE);
+		m_indexBuff, BufferUsageBit::INDEX, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, 0, MAX_PTR_SIZE);
 
-	// Write vert buff
+	// Write index buffer
 	{
-		ANKI_CHECK(transferAlloc.allocate(loader.getVertexDataSize(), handles[0]));
-		void* data = handles[0].getMappedMemory();
+		ANKI_CHECK(transferAlloc.allocate(m_indexBuff->getSize(), handles[1]));
+		void* data = handles[1].getMappedMemory();
 		ANKI_ASSERT(data);
 
-		memcpy(data, loader.getVertexData(), loader.getVertexDataSize());
+		ANKI_CHECK(loader.storeIndexBuffer(data, m_indexBuff->getSize()));
 
-		cmdb->copyBufferToBuffer(
-			handles[0].getBuffer(), handles[0].getOffset(), ctx.m_vertBuff, 0, handles[0].getRange());
+		cmdb->copyBufferToBuffer(handles[1].getBuffer(), handles[1].getOffset(), m_indexBuff, 0, handles[1].getRange());
 	}
 
-	// Create index buffer
+	// Write vert buff
 	{
-		ANKI_CHECK(transferAlloc.allocate(loader.getIndexDataSize(), handles[1]));
-		void* data = handles[1].getMappedMemory();
+		ANKI_CHECK(transferAlloc.allocate(m_vertBuff->getSize(), handles[0]));
+		U8* data = static_cast<U8*>(handles[0].getMappedMemory());
 		ANKI_ASSERT(data);
 
-		memcpy(data, loader.getIndexData(), loader.getIndexDataSize());
+		// Load to staging
+		PtrSize offset = 0;
+		for(U i = 0; i < m_vertBufferInfos.getSize(); ++i)
+		{
+			alignRoundUp(VERTEX_BUFFER_ALIGNMENT, offset);
+			ANKI_CHECK(loader.storeVertexBuffer(i, data + offset, m_vertBufferInfos[i].m_stride * m_vertCount));
 
-		cmdb->copyBufferToBuffer(
-			handles[1].getBuffer(), handles[1].getOffset(), ctx.m_indicesBuff, 0, handles[1].getRange());
+			offset += m_vertBufferInfos[i].m_stride * m_vertCount;
+		}
+
+		ANKI_ASSERT(offset == m_vertBuff->getSize());
+
+		// Copy
+		cmdb->copyBufferToBuffer(handles[0].getBuffer(), handles[0].getOffset(), m_vertBuff, 0, handles[0].getRange());
 	}
 
 	// Set barriers
 	cmdb->setBufferBarrier(
-		ctx.m_vertBuff, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, BufferUsageBit::VERTEX, 0, MAX_PTR_SIZE);
+		m_vertBuff, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, BufferUsageBit::VERTEX, 0, MAX_PTR_SIZE);
 	cmdb->setBufferBarrier(
-		ctx.m_indicesBuff, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, BufferUsageBit::INDEX, 0, MAX_PTR_SIZE);
-
-	ctx.m_vertBuff.reset(nullptr);
-	ctx.m_indicesBuff.reset(nullptr);
+		m_indexBuff, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, BufferUsageBit::INDEX, 0, MAX_PTR_SIZE);
 
+	// Finalize
 	FencePtr fence;
 	cmdb->flush(&fence);
 

+ 79 - 47
src/anki/resource/MeshResource.h

@@ -28,96 +28,128 @@ public:
 
 	~MeshResource();
 
-	U32 getTextureChannelsCount() const
-	{
-		return m_texChannelsCount;
-	}
+	/// Helper function for correct loading
+	Bool isCompatible(const MeshResource& other) const;
 
-	Bool hasBoneWeights() const
+	/// Load from a mesh file
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
+
+	/// Get the complete bounding box.
+	const Obb& getBoundingShape() const
 	{
-		return m_weights;
+		return m_obb;
 	}
 
-	/// Used only to clone the VBO
-	U32 getVerticesCount() const
+	/// Get submesh info.
+	void getSubMeshInfo(U subMeshId, U32& firstIndex, U32& indexCount, const Obb*& obb) const
 	{
-		return m_vertsCount;
+		const SubMesh& sm = m_subMeshes[subMeshId];
+		firstIndex = sm.m_firstIndex;
+		indexCount = sm.m_indexCount;
+		obb = &sm.m_obb;
 	}
 
-	U32 getIndicesCount() const
+	U32 getSubMeshCount() const
 	{
-		return m_indicesCount;
+		return m_subMeshes.getSize();
 	}
 
-	const Obb& getBoundingShape() const
+	/// Get all info around vertex indices.
+	void getIndexBufferInfo(BufferPtr& buff, PtrSize& buffOffset, U32& indexCount, IndexType& indexType) const
 	{
-		return m_obb;
+		buff = m_indexBuff;
+		buffOffset = 0;
+		indexCount = m_indexCount;
+		indexType = m_indexType;
 	}
 
-	/// Get indices count and offset of submesh
-	U32 getIndicesCountSub(U subMeshId, U32& offset) const
+	/// Get the number of logical vertex buffers.
+	U32 getVertexBufferCount() const
 	{
-		const SubMesh& sm = m_subMeshes[subMeshId];
-		offset = sm.m_indicesOffset;
-		return sm.m_indicesCount;
+		return m_vertBufferInfos.getSize();
 	}
 
-	const Obb& getBoundingShapeSub(U subMeshId) const
+	/// Get vertex buffer info.
+	void getVertexBufferInfo(const U32 buffIdx, BufferPtr& buff, PtrSize& offset, PtrSize& stride) const
 	{
-		return m_subMeshes[subMeshId].m_obb;
+		buff = m_vertBuff;
+		offset = m_vertBufferInfos[buffIdx].m_offset;
+		stride = m_vertBufferInfos[buffIdx].m_stride;
 	}
 
-	/// If returns zero then the mesh is a single uniform mesh
-	U32 getSubMeshesCount() const
+	/// Get attribute info. You need to check if the attribute is preset first (isVertexAttributePresent)
+	void getVertexAttributeInfo(
+		const VertexAttributeLocation attrib, U32& bufferIdx, Format& format, PtrSize& relativeOffset) const
 	{
-		return m_subMeshes.getSize();
+		ANKI_ASSERT(!!m_attribs[attrib].m_fmt);
+		bufferIdx = m_attribs[attrib].m_buffIdx;
+		format = m_attribs[attrib].m_fmt;
+		relativeOffset = m_attribs[attrib].m_relativeOffset;
 	}
 
-	BufferPtr getVertexBuffer() const
+	/// Check if a vertex attribute is present.
+	Bool isVertexAttributePresent(const VertexAttributeLocation attrib) const
 	{
-		return m_vertBuff;
+		return !!m_attribs[attrib].m_fmt;
 	}
 
-	BufferPtr getIndexBuffer() const
+	/// Get the number of texture coordinates channels.
+	U32 getTextureChannelCount() const
 	{
-		return m_indicesBuff;
+		return m_texChannelCount;
 	}
 
-	U32 getVertexSize() const
+	/// Return true if it has bone weights.
+	Bool hasBoneWeights() const
 	{
-		return m_vertSize;
+		return isVertexAttributePresent(VertexAttributeLocation::BONE_WEIGHTS);
 	}
 
-	/// Helper function for correct loading
-	Bool isCompatible(const MeshResource& other) const;
-
-	/// Load from a mesh file
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
-
 protected:
 	class LoadTask;
 	class LoadContext;
 
-	/// Per sub mesh data
+	static constexpr U VERTEX_BUFFER_ALIGNMENT = 64;
+
+	/// Sub-mesh data
 	struct SubMesh
 	{
-		U32 m_indicesCount;
-		U32 m_indicesOffset;
+		U32 m_firstIndex;
+		U32 m_indexCount;
 		Obb m_obb;
 	};
-
 	DynamicArray<SubMesh> m_subMeshes;
-	U32 m_indicesCount;
-	U32 m_vertsCount;
-	U32 m_vertSize;
-	Obb m_obb;
-	U8 m_texChannelsCount;
-	Bool8 m_weights;
+
+	// Index stuff
+	U32 m_indexCount = 0;
+	BufferPtr m_indexBuff;
+	IndexType m_indexType = IndexType::COUNT;
+
+	// Vertex stuff
+	U32 m_vertCount = 0;
+
+	struct VertBuffInfo
+	{
+		U32 m_offset; ///< Offset from the base of m_vertBuff.
+		U32 m_stride;
+	};
+	DynamicArray<VertBuffInfo> m_vertBufferInfos;
+
+	struct AttribInfo
+	{
+		Format m_fmt = Format::NONE;
+		U32 m_relativeOffset = 0;
+		U8 m_buffIdx = 0;
+	};
+	Array<AttribInfo, U(VertexAttributeLocation::COUNT)> m_attribs;
 
 	BufferPtr m_vertBuff;
-	BufferPtr m_indicesBuff;
+	U8 m_texChannelCount = 0;
+
+	// Other
+	Obb m_obb;
 
-	static ANKI_USE_RESULT Error load(LoadContext& ctx);
+	ANKI_USE_RESULT Error loadAsync(MeshLoader& loader) const;
 };
 /// @}
 

+ 55 - 60
src/anki/resource/ModelResource.cpp

@@ -41,81 +41,80 @@ void ModelPatch::getRenderingDataSub(
 		inf.m_program = variant.getShaderProgram();
 	}
 
-	// Vertex info
+	// Vertex attributes
+	U32 positionBinding = MAX_U32;
 	{
-		inf.m_vertexBufferBindingCount = 1;
-		VertexBufferBinding& vertBuffBinding = inf.m_vertexBufferBindings[0];
-		vertBuffBinding.m_buffer = mesh.getVertexBuffer();
-		vertBuffBinding.m_offset = 0;
-		vertBuffBinding.m_stride = mesh.getVertexSize();
+		if(key.m_pass == Pass::GB_FS)
+		{
+			// All attributes
+
+			inf.m_vertexAttributeCount = 0;
 
-		auto& attribs = inf.m_vertexAttributes;
+			for(VertexAttributeLocation loc = VertexAttributeLocation::FIRST; loc < VertexAttributeLocation::COUNT;
+				++loc)
+			{
+				if(!mesh.isVertexAttributePresent(loc))
+				{
+					continue;
+				}
 
-		U relativeOffset = 0;
+				VertexAttributeInfo& out = inf.m_vertexAttributes[inf.m_vertexAttributeCount++];
 
-		attribs[0].m_bufferBinding = 0;
-		attribs[0].m_format = Format::R32G32B32_SFLOAT;
-		attribs[0].m_relativeOffset = relativeOffset;
-		relativeOffset += sizeof(Vec3);
+				out.m_location = loc;
+				mesh.getVertexAttributeInfo(loc, out.m_bufferBinding, out.m_format, out.m_relativeOffset);
+			}
+		}
+		else
+		{
+			// Only position
 
-		attribs[1].m_bufferBinding = 0;
-		attribs[1].m_format = Format::R16G16_SFLOAT;
-		attribs[1].m_relativeOffset = relativeOffset;
-		relativeOffset += sizeof(U16) * 2;
+			inf.m_vertexAttributeCount = 1;
 
-		inf.m_vertexAttributeCount = 2;
+			VertexAttributeInfo& out = inf.m_vertexAttributes[0];
+			out.m_location = VertexAttributeLocation::POSITION;
 
+			mesh.getVertexAttributeInfo(out.m_location, out.m_bufferBinding, out.m_format, out.m_relativeOffset);
+
+			// Rewrite the binding just so we can keep it in a low binding location
+			positionBinding = out.m_bufferBinding;
+			out.m_bufferBinding = 0;
+		}
+	}
+
+	// Vertex buffers
+	{
 		if(key.m_pass == Pass::GB_FS)
 		{
-			attribs[2].m_bufferBinding = 0;
-			attribs[2].m_format = Format::A2B10G10R10_SNORM_PACK32;
-			attribs[2].m_relativeOffset = relativeOffset;
-			relativeOffset += sizeof(U32);
+			// All attributes
 
-			attribs[3].m_bufferBinding = 0;
-			attribs[3].m_format = Format::A2B10G10R10_SNORM_PACK32;
-			attribs[3].m_relativeOffset = relativeOffset;
-			relativeOffset += sizeof(U32);
+			inf.m_vertexBufferBindingCount = mesh.getVertexBufferCount();
 
-			inf.m_vertexAttributeCount = 4;
+			for(U i = 0; i < inf.m_vertexBufferBindingCount; ++i)
+			{
+				VertexBufferBinding& out = inf.m_vertexBufferBindings[i];
+				mesh.getVertexBufferInfo(i, out.m_buffer, out.m_offset, out.m_stride);
+			}
 		}
 		else
 		{
-			relativeOffset += sizeof(U32) * 2;
-		}
-
-		if(mesh.hasBoneWeights())
-		{
-			attribs[4].m_bufferBinding = 0;
-			attribs[4].m_format = Format::R8G8B8A8_UNORM;
-			attribs[4].m_relativeOffset = relativeOffset;
-			relativeOffset += sizeof(U8) * 4;
+			// Only position
 
-			attribs[5].m_bufferBinding = 0;
-			attribs[5].m_format = Format::R16G16B16A16_UINT;
-			attribs[5].m_relativeOffset = relativeOffset;
-			relativeOffset += sizeof(U16) * 4;
+			inf.m_vertexBufferBindingCount = 1;
 
-			inf.m_vertexAttributeCount = 6;
+			VertexBufferBinding& out = inf.m_vertexBufferBindings[0];
+			mesh.getVertexBufferInfo(positionBinding, out.m_buffer, out.m_offset, out.m_stride);
 		}
-
-		ANKI_ASSERT(relativeOffset == mesh.getVertexSize());
 	}
 
 	// Index buff
-	inf.m_indexBuffer = mesh.getIndexBuffer();
+	U32 indexCount;
+	mesh.getIndexBufferInfo(inf.m_indexBuffer, inf.m_indexBufferOffset, indexCount, inf.m_indexType);
 
 	// Other
-	if(subMeshIndicesArray.getSize() == 0 || mesh.getSubMeshesCount() == 0)
-	{
-		inf.m_drawcallCount = 1;
-		inf.m_indicesOffsetArray[0] = 0;
-		inf.m_indicesCountArray[0] = mesh.getIndicesCount();
-	}
-	else
-	{
-		ANKI_ASSERT(!"TODO");
-	}
+	ANKI_ASSERT(subMeshIndicesArray.getSize() == 0 && mesh.getSubMeshCount() == 1 && "Not supported ATM");
+	inf.m_drawcallCount = 1;
+	inf.m_indicesOffsetArray[0] = 0;
+	inf.m_indicesCountArray[0] = indexCount;
 }
 
 U ModelPatch::getLodCount() const
@@ -123,7 +122,8 @@ U ModelPatch::getLodCount() const
 	return max<U>(m_meshCount, getMaterial()->getLodCount());
 }
 
-Error ModelPatch::create(WeakArray<CString> meshFNames, const CString& mtlFName, Bool async, ResourceManager* manager)
+Error ModelPatch::create(
+	ConstWeakArray<CString> meshFNames, const CString& mtlFName, Bool async, ResourceManager* manager)
 {
 	ANKI_ASSERT(meshFNames.getSize() > 0);
 
@@ -242,12 +242,7 @@ Error ModelResource::load(const ResourceFilename& filename, Bool async)
 		ANKI_CHECK(materialEl.getText(cstr));
 		ModelPatch* mpatch = alloc.newInstance<ModelPatch>(this);
 
-		if(mpatch == nullptr)
-		{
-			return Error::OUT_OF_MEMORY;
-		}
-
-		ANKI_CHECK(mpatch->create(WeakArray<CString>(&meshesFnames[0], meshesCount), cstr, async, &getManager()));
+		ANKI_CHECK(mpatch->create(ConstWeakArray<CString>(&meshesFnames[0], meshesCount), cstr, async, &getManager()));
 
 		m_modelPatches[count++] = mpatch;
 

+ 12 - 5
src/anki/resource/ModelResource.h

@@ -45,12 +45,14 @@ class VertexAttributeInfo
 {
 public:
 	U32 m_bufferBinding;
+	VertexAttributeLocation m_location;
 	Format m_format;
 	PtrSize m_relativeOffset;
 
 	Bool operator==(const VertexAttributeInfo& b) const
 	{
-		return m_bufferBinding == b.m_bufferBinding && m_format == b.m_format && m_relativeOffset == b.m_relativeOffset;
+		return m_bufferBinding == b.m_bufferBinding && m_format == b.m_format && m_relativeOffset == b.m_relativeOffset
+			   && m_location == b.m_location;
 	}
 
 	Bool operator!=(const VertexAttributeInfo& b) const
@@ -74,6 +76,8 @@ public:
 	U32 m_vertexAttributeCount;
 
 	BufferPtr m_indexBuffer;
+	PtrSize m_indexBufferOffset;
+	IndexType m_indexType;
 };
 
 /// Model patch interface class. Its very important class and it binds the material with the mesh
@@ -107,16 +111,19 @@ public:
 
 	const Obb& getBoundingShapeSub(U32 subMeshId) const
 	{
-		return m_meshes[0]->getBoundingShapeSub(subMeshId);
+		U32 firstIdx, idxCount;
+		const Obb* obb;
+		m_meshes[0]->getSubMeshInfo(subMeshId, firstIdx, idxCount, obb);
+		return *obb;
 	}
 
-	U32 getSubMeshesCount() const
+	U32 getSubMeshCount() const
 	{
-		return m_meshes[0]->getSubMeshesCount();
+		return m_meshes[0]->getSubMeshCount();
 	}
 
 	ANKI_USE_RESULT Error create(
-		WeakArray<CString> meshFNames, const CString& mtlFName, Bool async, ResourceManager* resources);
+		ConstWeakArray<CString> meshFNames, const CString& mtlFName, Bool async, ResourceManager* resources);
 
 	/// Get information for multiDraw rendering. Given an array of submeshes that are visible return the correct indices
 	/// offsets and counts.

+ 1 - 1
src/anki/resource/ResourceFilesystem.h

@@ -86,7 +86,7 @@ public:
 
 	ANKI_USE_RESULT Error init(const ConfigSet& config, const CString& cacheDir);
 
-	/// Search the path list to find the file. Then open the file for reading.
+	/// Search the path list to find the file. Then open the file for reading. It's thread-safe.
 	ANKI_USE_RESULT Error openFile(const ResourceFilename& filename, ResourceFilePtr& file);
 
 #if !ANKI_TESTS

+ 31 - 13
src/anki/scene/ModelNode.cpp

@@ -76,7 +76,7 @@ void ModelPatchNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<vo
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
 	// That will not work on multi-draw and instanced at the same time. Make sure that there is no multi-draw anywhere
-	ANKI_ASSERT(self.m_modelPatch->getSubMeshesCount() == 0);
+	ANKI_ASSERT(self.m_modelPatch->getSubMeshCount() == 1);
 
 	ModelRenderingInfo modelInf;
 	self.m_modelPatch->getRenderingDataSub(ctx.m_key, WeakArray<U8>(), modelInf);
@@ -88,7 +88,8 @@ void ModelPatchNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<vo
 	for(U i = 0; i < modelInf.m_vertexAttributeCount; ++i)
 	{
 		const VertexAttributeInfo& attrib = modelInf.m_vertexAttributes[i];
-		cmdb->setVertexAttribute(i, attrib.m_bufferBinding, attrib.m_format, attrib.m_relativeOffset);
+		cmdb->setVertexAttribute(
+			U32(attrib.m_location), attrib.m_bufferBinding, attrib.m_format, attrib.m_relativeOffset);
 	}
 
 	// Set vertex buffers
@@ -263,7 +264,7 @@ void ModelNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*>
 
 		// That will not work on multi-draw and instanced at the same time. Make sure that there is no multi-draw
 		// anywhere
-		ANKI_ASSERT(patch->getSubMeshesCount() == 0);
+		ANKI_ASSERT(patch->getSubMeshCount() == 1);
 
 		ModelRenderingInfo modelInf;
 		patch->getRenderingDataSub(ctx.m_key, WeakArray<U8>(), modelInf);
@@ -275,10 +276,9 @@ void ModelNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*>
 		for(U i = 0; i < modelInf.m_vertexAttributeCount; ++i)
 		{
 			const VertexAttributeInfo& attrib = modelInf.m_vertexAttributes[i];
-			if(attrib.m_format != Format::NONE)
-			{
-				cmdb->setVertexAttribute(i, attrib.m_bufferBinding, attrib.m_format, attrib.m_relativeOffset);
-			}
+			ANKI_ASSERT(attrib.m_format != Format::NONE);
+			cmdb->setVertexAttribute(
+				U32(attrib.m_location), attrib.m_bufferBinding, attrib.m_format, attrib.m_relativeOffset);
 		}
 
 		// Set vertex buffers
@@ -392,10 +392,11 @@ void ModelNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*>
 			const Vec4 tsl = self2.m_obb.getCenter().xyz1();
 			const Vec3 scale = self2.m_obb.getExtend().xyz();
 
-			// Set non uniform scale
-			rot(0, 0) *= scale.x();
-			rot(0, 1) *= scale.y();
-			rot(0, 2) *= scale.z();
+			// Set non uniform scale. Add a margin to avoid flickering
+			const F32 MARGIN = 1.02;
+			rot(0, 0) *= scale.x() * MARGIN;
+			rot(1, 1) *= scale.y() * MARGIN;
+			rot(2, 2) *= scale.z() * MARGIN;
 
 			*mvps = ctx.m_viewProjectionMatrix * Mat4(tsl, rot, 1.0f);
 			++mvps;
@@ -405,21 +406,38 @@ void ModelNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*>
 		*color = Vec4(1.0f, 0.0f, 1.0f, 1.0f);
 
 		// Setup state
-		ShaderProgramResourceMutationInitList<1> mutators(self.m_dbgProg);
+		ShaderProgramResourceMutationInitList<2> mutators(self.m_dbgProg);
 		mutators.add("COLOR_TEXTURE", 0);
+		mutators.add("DITHERED_DEPTH_TEST", ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON));
 		ShaderProgramResourceConstantValueInitList<1> consts(self.m_dbgProg);
 		consts.add("INSTANCE_COUNT", U32(userData.getSize()));
 		const ShaderProgramResourceVariant* variant;
 		self.m_dbgProg->getOrCreateVariant(mutators.get(), consts.get(), variant);
 		cmdb->bindShaderProgram(variant->getProgram());
 
+		const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
+		if(enableDepthTest)
+		{
+			cmdb->setDepthCompareOperation(CompareOperation::LESS);
+		}
+		else
+		{
+			cmdb->setDepthCompareOperation(CompareOperation::ALWAYS);
+		}
+
 		cmdb->setVertexAttribute(0, 0, Format::R32G32B32_SFLOAT, 0);
 		cmdb->bindVertexBuffer(0, vertToken.m_buffer, vertToken.m_offset, sizeof(Vec3));
 		cmdb->bindIndexBuffer(indicesToken.m_buffer, indicesToken.m_offset, IndexType::U16);
 
-		cmdb->bindUniformBuffer(0, 0, unisToken.m_buffer, unisToken.m_offset, unisToken.m_range);
+		cmdb->bindUniformBuffer(1, 0, unisToken.m_buffer, unisToken.m_offset, unisToken.m_range);
 
 		cmdb->drawElements(PrimitiveTopology::LINES, INDEX_COUNT, userData.getSize());
+
+		// Restore state
+		if(!enableDepthTest)
+		{
+			cmdb->setDepthCompareOperation(CompareOperation::LESS);
+		}
 	}
 }
 

+ 7 - 8
src/anki/scene/OccluderNode.cpp

@@ -48,18 +48,17 @@ Error OccluderNode::init(const CString& meshFname)
 	MeshLoader loader(&getSceneGraph().getResourceManager());
 	ANKI_CHECK(loader.load(meshFname));
 
-	const U16* indices = reinterpret_cast<const U16*>(loader.getIndexData());
-	U indexCount = loader.getIndexDataSize() / sizeof(U16);
-	U vertSize = loader.getVertexSize();
-
+	const U indexCount = loader.getHeader().m_totalIndexCount;
 	m_vertsL.create(getSceneAllocator(), indexCount);
 	m_vertsW.create(getSceneAllocator(), indexCount);
 
-	for(U i = 0; i < indexCount; ++i)
+	DynamicArrayAuto<Vec3> positions(getSceneAllocator());
+	DynamicArrayAuto<U32> indices(getSceneAllocator());
+	ANKI_CHECK(loader.storeIndicesAndPosition(indices, positions));
+
+	for(U i = 0; i < indices.getSize(); ++i)
 	{
-		U idx = indices[i];
-		const Vec3* vert = reinterpret_cast<const Vec3*>(loader.getVertexData() + idx * vertSize);
-		m_vertsL[i] = *vert;
+		m_vertsL[i] = positions[indices[i]];
 	}
 
 	// Create the components

+ 2 - 2
src/anki/scene/ParticleEmitterNode.cpp

@@ -446,11 +446,11 @@ Error ParticleEmitterNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 		Vec4 max = aabbmax + m_particle.m_size;
 		Vec4 center = (min + max) / 2.0;
 
-		m_obb = Obb(center, Mat3x4::getIdentity(), max - center);
+		m_obb = Obb(center.xyz0(), Mat3x4::getIdentity(), (max - center).xyz0());
 	}
 	else
 	{
-		m_obb = Obb(Vec4(0.0), Mat3x4::getIdentity(), Vec4(0.001));
+		m_obb = Obb(Vec4(0.0), Mat3x4::getIdentity(), Vec4(Vec3(0.001f), 0.0f));
 		m_verts = nullptr;
 	}
 

+ 4 - 2
src/anki/scene/ReflectionProxyNode.cpp

@@ -40,6 +40,7 @@ public:
 
 Error ReflectionProxyNode::init(const CString& proxyMesh)
 {
+#if 0
 	// Move component first
 	newComponent<MoveComponent>(this);
 
@@ -50,13 +51,13 @@ Error ReflectionProxyNode::init(const CString& proxyMesh)
 	MeshLoader loader(&getResourceManager());
 	ANKI_CHECK(loader.load(proxyMesh));
 
-	if((loader.getHeader().m_flags & MeshLoader::Flag::QUADS) == MeshLoader::Flag::NONE)
+	if(!(loader.getHeader().m_flags & MeshBinaryFile::Flag::QUAD))
 	{
 		ANKI_SCENE_LOGE("Expecting quad mesh");
 		return Error::USER_DATA;
 	}
 
-	const U indexCount = loader.getHeader().m_totalIndicesCount;
+	const U indexCount = loader.getHeader().m_totalIndexCount;
 	const U quadCount = indexCount / 4;
 	m_quadsLSpace.create(getSceneAllocator(), quadCount);
 
@@ -91,6 +92,7 @@ Error ReflectionProxyNode::init(const CString& proxyMesh)
 	m_boxWSpace = m_boxLSpace;
 
 	newComponent<SpatialComponent>(this, &m_boxWSpace);
+#endif
 
 	return Error::NONE;
 }

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.