Browse Source

Merge pull request #76 from godlikepanos/mesh_refactor

Scene ver 100. Yet another scenegraph refactoring
Panagiotis Christopoulos Charitos 4 years ago
parent
commit
c1817ad1d7
100 changed files with 2291 additions and 3648 deletions
  1. 2 2
      anki/Resource.h
  2. 3 2
      anki/Scene.h
  3. 3 3
      anki/collision/Aabb.cpp
  4. 8 0
      anki/collision/Sphere.h
  5. 2 1
      anki/core/App.cpp
  6. 1 1
      anki/gr/Buffer.h
  7. 3 1
      anki/gr/GrObject.h
  8. 4 1
      anki/gr/vulkan/CommandBufferFactory.cpp
  9. 19 4
      anki/gr/vulkan/CommandBufferFactory.h
  10. 19 2
      anki/gr/vulkan/CommandBufferImpl.cpp
  11. 59 80
      anki/importer/GltfImporter.cpp
  12. 1 2
      anki/importer/GltfImporter.h
  13. 68 108
      anki/importer/GltfImporterMesh.cpp
  14. 1 1
      anki/math/Functions.h
  15. 11 11
      anki/math/Transform.h
  16. 13 7
      anki/physics/PhysicsBody.cpp
  17. 5 3
      anki/physics/PhysicsBody.h
  18. 14 6
      anki/physics/PhysicsCollisionShape.h
  19. 1 3
      anki/physics/PhysicsDrawer.cpp
  20. 8 14
      anki/physics/PhysicsJoint.cpp
  21. 7 7
      anki/physics/PhysicsJoint.h
  22. 19 1
      anki/physics/PhysicsObject.cpp
  23. 58 17
      anki/physics/PhysicsObject.h
  24. 25 21
      anki/physics/PhysicsPlayerController.cpp
  25. 17 11
      anki/physics/PhysicsPlayerController.h
  26. 84 18
      anki/physics/PhysicsTrigger.cpp
  27. 26 5
      anki/physics/PhysicsTrigger.h
  28. 133 30
      anki/physics/PhysicsWorld.cpp
  29. 38 19
      anki/physics/PhysicsWorld.h
  30. 10 0
      anki/renderer/Dbg.cpp
  31. 4 0
      anki/renderer/GBuffer.cpp
  32. 1 1
      anki/renderer/TraditionalDeferredShading.cpp
  33. 0 70
      anki/resource/CollisionResource.cpp
  34. 0 50
      anki/resource/CollisionResource.h
  35. 0 1
      anki/resource/Common.h
  36. 46 0
      anki/resource/CpuMeshResource.cpp
  37. 53 0
      anki/resource/CpuMeshResource.h
  38. 2 2
      anki/resource/InstantiationMacros.h
  39. 9 0
      anki/resource/MaterialResource.h
  40. 6 2
      anki/resource/MeshBinary.h
  41. 4 2
      anki/resource/MeshBinary.xml
  42. 8 8
      anki/resource/MeshBinaryLoader.cpp
  43. 21 4
      anki/resource/MeshBinaryLoader.h
  44. 0 338
      anki/resource/MeshLoader.cpp
  45. 0 146
      anki/resource/MeshLoader.h
  46. 67 68
      anki/resource/MeshResource.cpp
  47. 33 48
      anki/resource/MeshResource.h
  48. 42 56
      anki/resource/ModelResource.cpp
  49. 42 65
      anki/resource/ModelResource.h
  50. 7 6
      anki/resource/ParticleEmitterResource.cpp
  51. 19 11
      anki/resource/ParticleEmitterResource.h
  52. 8 1
      anki/resource/ResourceManager.cpp
  53. 0 1
      anki/resource/ResourceManager.h
  54. 12 30
      anki/scene/BodyNode.cpp
  55. 0 6
      anki/scene/BodyNode.h
  56. 14 60
      anki/scene/CameraNode.cpp
  57. 1 15
      anki/scene/CameraNode.h
  58. 3 11
      anki/scene/Common.h
  59. 117 284
      anki/scene/DebugDrawer.cpp
  60. 49 99
      anki/scene/DebugDrawer.h
  61. 22 78
      anki/scene/DecalNode.cpp
  62. 2 14
      anki/scene/DecalNode.h
  63. 47 29
      anki/scene/FogDensityNode.cpp
  64. 5 10
      anki/scene/FogDensityNode.h
  65. 9 0
      anki/scene/Forward.h
  66. 36 96
      anki/scene/GlobalIlluminationProbeNode.cpp
  67. 2 15
      anki/scene/GlobalIlluminationProbeNode.h
  68. 52 181
      anki/scene/GpuParticleEmitterNode.cpp
  69. 3 39
      anki/scene/GpuParticleEmitterNode.h
  70. 97 159
      anki/scene/LightNode.cpp
  71. 11 31
      anki/scene/LightNode.h
  72. 150 140
      anki/scene/ModelNode.cpp
  73. 12 27
      anki/scene/ModelNode.h
  74. 0 85
      anki/scene/OccluderNode.cpp
  75. 0 40
      anki/scene/OccluderNode.h
  76. 40 422
      anki/scene/ParticleEmitterNode.cpp
  77. 4 47
      anki/scene/ParticleEmitterNode.h
  78. 21 50
      anki/scene/PhysicsDebugNode.cpp
  79. 6 8
      anki/scene/PhysicsDebugNode.h
  80. 18 28
      anki/scene/PlayerNode.cpp
  81. 0 5
      anki/scene/PlayerNode.h
  82. 79 102
      anki/scene/ReflectionProbeNode.cpp
  83. 5 23
      anki/scene/ReflectionProbeNode.h
  84. 19 3
      anki/scene/SceneGraph.cpp
  85. 10 6
      anki/scene/SceneGraph.h
  86. 59 18
      anki/scene/SceneNode.h
  87. 2 22
      anki/scene/StaticCollisionNode.cpp
  88. 0 9
      anki/scene/StaticCollisionNode.h
  89. 10 16
      anki/scene/TriggerNode.cpp
  90. 1 8
      anki/scene/TriggerNode.h
  91. 13 46
      anki/scene/Visibility.cpp
  92. 86 0
      anki/scene/components/BodyComponent.cpp
  93. 32 15
      anki/scene/components/BodyComponent.h
  94. 62 14
      anki/scene/components/DecalComponent.cpp
  95. 23 40
      anki/scene/components/DecalComponent.h
  96. 13 0
      anki/scene/components/FogDensityComponent.cpp
  97. 60 28
      anki/scene/components/FogDensityComponent.h
  98. 8 14
      anki/scene/components/FrustumComponent.cpp
  99. 29 14
      anki/scene/components/FrustumComponent.h
  100. 13 0
      anki/scene/components/GenericGpuComputeJobComponent.cpp

+ 2 - 2
anki/Resource.h

@@ -10,6 +10,7 @@
 #include <anki/resource/AnimationResource.h>
 #include <anki/resource/AnimationResource.h>
 #include <anki/resource/ScriptResource.h>
 #include <anki/resource/ScriptResource.h>
 #include <anki/resource/MeshResource.h>
 #include <anki/resource/MeshResource.h>
+#include <anki/resource/CpuMeshResource.h>
 #include <anki/resource/MaterialResource.h>
 #include <anki/resource/MaterialResource.h>
 #include <anki/resource/TextureAtlasResource.h>
 #include <anki/resource/TextureAtlasResource.h>
 #include <anki/resource/TextureResource.h>
 #include <anki/resource/TextureResource.h>
@@ -18,9 +19,8 @@
 #include <anki/resource/DummyResource.h>
 #include <anki/resource/DummyResource.h>
 #include <anki/resource/ModelResource.h>
 #include <anki/resource/ModelResource.h>
 #include <anki/resource/ShaderProgramResource.h>
 #include <anki/resource/ShaderProgramResource.h>
-#include <anki/resource/CollisionResource.h>
 
 
-#include <anki/resource/MeshLoader.h>
+#include <anki/resource/MeshBinaryLoader.h>
 
 
 /// @defgroup resource Collection of resources and management
 /// @defgroup resource Collection of resources and management
 
 

+ 3 - 2
anki/Scene.h

@@ -15,7 +15,6 @@
 #include <anki/scene/BodyNode.h>
 #include <anki/scene/BodyNode.h>
 #include <anki/scene/ReflectionProbeNode.h>
 #include <anki/scene/ReflectionProbeNode.h>
 #include <anki/scene/PlayerNode.h>
 #include <anki/scene/PlayerNode.h>
-#include <anki/scene/OccluderNode.h>
 #include <anki/scene/DecalNode.h>
 #include <anki/scene/DecalNode.h>
 #include <anki/scene/Octree.h>
 #include <anki/scene/Octree.h>
 #include <anki/scene/PhysicsDebugNode.h>
 #include <anki/scene/PhysicsDebugNode.h>
@@ -25,7 +24,6 @@
 
 
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/RenderComponent.h>
 #include <anki/scene/components/RenderComponent.h>
-#include <anki/scene/components/OccluderComponent.h>
 #include <anki/scene/components/LensFlareComponent.h>
 #include <anki/scene/components/LensFlareComponent.h>
 #include <anki/scene/components/PlayerControllerComponent.h>
 #include <anki/scene/components/PlayerControllerComponent.h>
 #include <anki/scene/components/SkinComponent.h>
 #include <anki/scene/components/SkinComponent.h>
@@ -41,6 +39,9 @@
 #include <anki/scene/components/FogDensityComponent.h>
 #include <anki/scene/components/FogDensityComponent.h>
 #include <anki/scene/components/GlobalIlluminationProbeComponent.h>
 #include <anki/scene/components/GlobalIlluminationProbeComponent.h>
 #include <anki/scene/components/GenericGpuComputeJobComponent.h>
 #include <anki/scene/components/GenericGpuComputeJobComponent.h>
+#include <anki/scene/components/ParticleEmitterComponent.h>
+#include <anki/scene/components/GpuParticleEmitterComponent.h>
+#include <anki/scene/components/ModelComponent.h>
 
 
 #include <anki/scene/events/EventManager.h>
 #include <anki/scene/events/EventManager.h>
 #include <anki/scene/events/Event.h>
 #include <anki/scene/events/Event.h>

+ 3 - 3
anki/collision/Aabb.cpp

@@ -16,11 +16,11 @@ Aabb Aabb::getTransformed(const Transform& trf) const
 		absM[i] = absolute(trf.getRotation()[i]);
 		absM[i] = absolute(trf.getRotation()[i]);
 	}
 	}
 
 
-	Vec4 center = (m_min + m_max) * 0.5;
-	Vec4 extend = (m_max - m_min) * 0.5;
+	Vec4 center = (m_min + m_max) * 0.5f;
+	Vec4 extend = (m_max - m_min) * 0.5f;
 
 
 	Vec4 newC = trf.transform(center);
 	Vec4 newC = trf.transform(center);
-	Vec4 newE = Vec4(absM * (extend * trf.getScale()), 0.0);
+	Vec4 newE = Vec4(absM * (extend * trf.getScale()), 0.0f);
 
 
 	return Aabb(newC - newE, newC + newE);
 	return Aabb(newC - newE, newC + newE);
 }
 }

+ 8 - 0
anki/collision/Sphere.h

@@ -38,6 +38,14 @@ public:
 		check();
 		check();
 	}
 	}
 
 
+	/// Constructor
+	Sphere(const Vec3& center, F32 radius)
+		: m_center(center.xyz0())
+		, m_radius(radius)
+	{
+		check();
+	}
+
 	/// Set from point cloud.
 	/// Set from point cloud.
 	Sphere(const Vec3* pointBuffer, U pointCount, PtrSize pointStride, PtrSize buffSize)
 	Sphere(const Vec3* pointBuffer, U pointCount, PtrSize pointStride, PtrSize buffSize)
 	{
 	{

+ 2 - 1
anki/core/App.cpp

@@ -11,6 +11,7 @@
 #include <anki/util/System.h>
 #include <anki/util/System.h>
 #include <anki/util/ThreadHive.h>
 #include <anki/util/ThreadHive.h>
 #include <anki/util/Tracer.h>
 #include <anki/util/Tracer.h>
+#include <anki/util/HighRezTimer.h>
 #include <anki/core/CoreTracer.h>
 #include <anki/core/CoreTracer.h>
 #include <anki/core/DeveloperConsole.h>
 #include <anki/core/DeveloperConsole.h>
 #include <anki/core/NativeWindow.h>
 #include <anki/core/NativeWindow.h>
@@ -408,7 +409,7 @@ Error App::initInternal(const ConfigSet& config_, AllocAlignedCallback allocCb,
 	//
 	//
 	m_physics = m_heapAlloc.newInstance<PhysicsWorld>();
 	m_physics = m_heapAlloc.newInstance<PhysicsWorld>();
 
 
-	ANKI_CHECK(m_physics->create(m_allocCb, m_allocCbData));
+	ANKI_CHECK(m_physics->init(m_allocCb, m_allocCbData));
 
 
 	//
 	//
 	// Resource FS
 	// Resource FS

+ 1 - 1
anki/gr/Buffer.h

@@ -48,7 +48,7 @@ class Buffer : public GrObject
 	ANKI_GR_OBJECT
 	ANKI_GR_OBJECT
 
 
 public:
 public:
-	static const GrObjectType CLASS_TYPE = GrObjectType::BUFFER;
+	static constexpr GrObjectType CLASS_TYPE = GrObjectType::BUFFER;
 
 
 	/// Return the size of the buffer.
 	/// Return the size of the buffer.
 	PtrSize getSize() const
 	PtrSize getSize() const

+ 3 - 1
anki/gr/GrObject.h

@@ -31,7 +31,9 @@ enum class GrObjectType : U8
 	FENCE,
 	FENCE,
 	RENDER_GRAPH,
 	RENDER_GRAPH,
 	ACCELERATION_STRUCTURE,
 	ACCELERATION_STRUCTURE,
-	COUNT
+
+	COUNT,
+	FIRST = 0
 };
 };
 
 
 /// Base of all graphics objects.
 /// Base of all graphics objects.

+ 4 - 1
anki/gr/vulkan/CommandBufferFactory.cpp

@@ -27,7 +27,10 @@ void MicroCommandBuffer::reset()
 	ANKI_ASSERT(m_refcount.load() == 0);
 	ANKI_ASSERT(m_refcount.load() == 0);
 	ANKI_ASSERT(!m_fence.isCreated() || m_fence->done());
 	ANKI_ASSERT(!m_fence.isCreated() || m_fence->done());
 
 
-	m_objectRefs.destroy(m_fastAlloc);
+	for(GrObjectType type : EnumIterable<GrObjectType>())
+	{
+		m_objectRefs[type].destroy(m_fastAlloc);
+	}
 
 
 	m_fastAlloc.getMemoryPool().reset();
 	m_fastAlloc.getMemoryPool().reset();
 
 

+ 19 - 4
anki/gr/vulkan/CommandBufferFactory.h

@@ -50,10 +50,9 @@ public:
 	}
 	}
 
 
 	template<typename T>
 	template<typename T>
-	void pushObjectRef(T& x)
+	void pushObjectRef(GrObjectPtrT<T>& x)
 	{
 	{
-		GrObject* grobj = x.get();
-		m_objectRefs.emplaceBack(m_fastAlloc, IntrusivePtr<GrObject>(grobj));
+		pushToArray(m_objectRefs[T::CLASS_TYPE], x.get());
 	}
 	}
 
 
 	void setFence(MicroFencePtr& fence)
 	void setFence(MicroFencePtr& fence)
@@ -68,7 +67,7 @@ private:
 	VkCommandBuffer m_handle = {};
 	VkCommandBuffer m_handle = {};
 
 
 	MicroFencePtr m_fence;
 	MicroFencePtr m_fence;
-	DynamicArray<IntrusivePtr<GrObject>> m_objectRefs;
+	Array<DynamicArray<GrObjectPtr>, U(GrObjectType::COUNT)> m_objectRefs;
 
 
 	// Cacheline boundary
 	// Cacheline boundary
 
 
@@ -78,8 +77,24 @@ private:
 
 
 	void destroy();
 	void destroy();
 	void reset();
 	void reset();
+
+	void pushToArray(DynamicArray<GrObjectPtr>& arr, GrObject* grobj)
+	{
+		ANKI_ASSERT(grobj);
+		// Check what if gobj is already there to avoid allocations and excessive dereferencing
+		if(arr.getSize() == 0 || arr.getBack().get() != grobj)
+		{
+			arr.emplaceBack(m_fastAlloc, GrObjectPtr(grobj));
+		}
+	}
 };
 };
 
 
+template<>
+inline void MicroCommandBuffer::pushObjectRef<GrObject>(GrObjectPtr& x)
+{
+	pushToArray(m_objectRefs[x->getType()], x.get());
+}
+
 /// Deleter.
 /// Deleter.
 class MicroCommandBufferPtrDeleter
 class MicroCommandBufferPtrDeleter
 {
 {

+ 19 - 2
anki/gr/vulkan/CommandBufferImpl.cpp

@@ -229,18 +229,35 @@ void CommandBufferImpl::endRecording()
 	m_finalized = true;
 	m_finalized = true;
 
 
 #if ANKI_EXTRA_CHECKS
 #if ANKI_EXTRA_CHECKS
+	static Atomic<U32> messagePrintCount(0);
+	constexpr U32 MAX_PRINT_COUNT = 10;
+
+	CString message;
 	if(!!(m_flags & CommandBufferFlag::SMALL_BATCH))
 	if(!!(m_flags & CommandBufferFlag::SMALL_BATCH))
 	{
 	{
 		if(m_commandCount > COMMAND_BUFFER_SMALL_BATCH_MAX_COMMANDS * 4)
 		if(m_commandCount > COMMAND_BUFFER_SMALL_BATCH_MAX_COMMANDS * 4)
 		{
 		{
-			ANKI_VK_LOGW("Command buffer has too many commands: %u", m_commandCount);
+			message = "Command buffer has too many commands%s: %u";
 		}
 		}
 	}
 	}
 	else
 	else
 	{
 	{
 		if(m_commandCount <= COMMAND_BUFFER_SMALL_BATCH_MAX_COMMANDS / 4)
 		if(m_commandCount <= COMMAND_BUFFER_SMALL_BATCH_MAX_COMMANDS / 4)
 		{
 		{
-			ANKI_VK_LOGW("Command buffer has too few commands: %u", m_commandCount);
+			message = "Command buffer has too few commands%s: %u";
+		}
+	}
+
+	if(!message.isEmpty())
+	{
+		const U32 count = messagePrintCount.fetchAdd(1) + 1;
+		if(count < MAX_PRINT_COUNT)
+		{
+			ANKI_VK_LOGW(message.cstr(), "", m_commandCount);
+		}
+		else if(count == MAX_PRINT_COUNT)
+		{
+			ANKI_VK_LOGW(message.cstr(), " (will ignore further warnings)", m_commandCount);
 		}
 		}
 	}
 	}
 #endif
 #endif

+ 59 - 80
anki/importer/GltfImporter.cpp

@@ -446,8 +446,12 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 				gpuParticles = true;
 				gpuParticles = true;
 			}
 			}
 
 
-			ANKI_CHECK(m_sceneFile.writeText("\nnode = scene:new%sParticleEmitterNode(\"%s\", \"%s\")\n",
-											 (gpuParticles) ? "Gpu" : "", getNodeName(node).cstr(), fname.cstr()));
+			ANKI_CHECK(m_sceneFile.writeText("\nnode = scene:new%sParticleEmitterNode(\"%s\")\n",
+											 (gpuParticles) ? "Gpu" : "", getNodeName(node).cstr()));
+
+			ANKI_CHECK(m_sceneFile.writeText("comp = node:getSceneNodeBase():get%sParticleEmitterComponent()\n",
+											 (gpuParticles) ? "Gpu" : ""));
+			ANKI_CHECK(m_sceneFile.writeText("comp:loadParticleEmitterResource(\"%s\")\n", fname.cstr()));
 
 
 			Transform localTrf;
 			Transform localTrf;
 			ANKI_CHECK(getNodeTransform(node, localTrf));
 			ANKI_CHECK(getNodeTransform(node, localTrf));
@@ -455,20 +459,12 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 		}
 		}
 		else if((it = extras.find("collision")) != extras.getEnd() && *it == "true")
 		else if((it = extras.find("collision")) != extras.getEnd() && *it == "true")
 		{
 		{
-			// Write colission mesh
-			{
-				StringAuto fname(m_alloc);
-				fname.sprintf("%s%s.ankicl", m_outDir.cstr(), node.mesh->name);
-				File file;
-				ANKI_CHECK(file.open(fname.toCString(), FileOpenFlag::WRITE));
-
-				ANKI_CHECK(file.writeText("<collisionShape>\n\t<type>staticMesh</type>\n\t<value>%s%s.ankimesh"
-										  "</value>\n</collisionShape>\n",
-										  m_rpath.cstr(), node.mesh->name));
-			}
+			ANKI_CHECK(
+				m_sceneFile.writeText("\nnode = scene:newStaticCollisionNode(\"%s\")\n", getNodeName(node).cstr()));
 
 
-			ANKI_CHECK(m_sceneFile.writeText("\nnode = scene:newStaticCollisionNode(\"%s\", \"%s%s.ankicl\")\n",
-											 getNodeName(node).cstr(), m_rpath.cstr(), node.mesh->name));
+			ANKI_CHECK(m_sceneFile.writeText("comp = scene:getSceneNodeBase():getBodyComponent()\n"));
+			ANKI_CHECK(
+				m_sceneFile.writeText("comp:loadMeshResource(\"%s%s.ankimesh\")\n", m_rpath.cstr(), node.mesh->name));
 
 
 			Transform localTrf;
 			Transform localTrf;
 			ANKI_CHECK(getNodeTransform(node, localTrf));
 			ANKI_CHECK(getNodeTransform(node, localTrf));
@@ -481,16 +477,16 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 			Vec3 scale;
 			Vec3 scale;
 			getNodeTransform(node, tsl, rot, scale);
 			getNodeTransform(node, tsl, rot, scale);
 
 
-			const Vec3 half = scale;
-			const Vec3 aabbMin = tsl - half - tsl;
-			const Vec3 aabbMax = tsl + half - tsl;
+			const Vec3 boxSize = scale * 2.0f;
 
 
-			ANKI_CHECK(m_sceneFile.writeText(
-				"\nnode = scene:newReflectionProbeNode(\"%s\", Vec4.new(%f, %f, %f, 0), Vec4.new(%f, %f, %f, 0))\n",
-				getNodeName(node).cstr(), aabbMin.x(), aabbMin.y(), aabbMin.z(), aabbMax.x(), aabbMax.y(),
-				aabbMax.z()));
+			ANKI_CHECK(
+				m_sceneFile.writeText("\nnode = scene:newReflectionProbeNode(\"%s\")\n", getNodeName(node).cstr()));
 
 
-			Transform localTrf = Transform(tsl.xyz0(), Mat3x4(Vec3(0.0f), rot), 1.0f);
+			ANKI_CHECK(m_sceneFile.writeText("comp = node:getSceneNodeBase():getReflectionProbeComponent()\n"));
+			ANKI_CHECK(m_sceneFile.writeText("comp:setBoxVolumeSize(Vec3.new(%f, %f, %f))\n", boxSize.x(), boxSize.y(),
+											 boxSize.z()));
+
+			const Transform localTrf = Transform(tsl.xyz0(), Mat3x4(Vec3(0.0f), rot), 1.0f);
 			ANKI_CHECK(writeTransform(parentTrf.combineTransformations(localTrf)));
 			ANKI_CHECK(writeTransform(parentTrf.combineTransformations(localTrf)));
 		}
 		}
 		else if((it = extras.find("gi_probe")) != extras.getEnd() && *it == "true")
 		else if((it = extras.find("gi_probe")) != extras.getEnd() && *it == "true")
@@ -500,9 +496,7 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 			Vec3 scale;
 			Vec3 scale;
 			getNodeTransform(node, tsl, rot, scale);
 			getNodeTransform(node, tsl, rot, scale);
 
 
-			const Vec3 half = scale;
-			const Vec3 aabbMin = tsl - half - tsl;
-			const Vec3 aabbMax = tsl + half - tsl;
+			const Vec3 boxSize = scale * 2.0f;
 
 
 			F32 fadeDistance = -1.0f;
 			F32 fadeDistance = -1.0f;
 			if((it = extras.find("gi_probe_fade_distance")) != extras.getEnd())
 			if((it = extras.find("gi_probe_fade_distance")) != extras.getEnd())
@@ -519,10 +513,8 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 			ANKI_CHECK(m_sceneFile.writeText("\nnode = scene:newGlobalIlluminationProbeNode(\"%s\")\n",
 			ANKI_CHECK(m_sceneFile.writeText("\nnode = scene:newGlobalIlluminationProbeNode(\"%s\")\n",
 											 getNodeName(node).cstr()));
 											 getNodeName(node).cstr()));
 			ANKI_CHECK(m_sceneFile.writeText("comp = node:getSceneNodeBase():getGlobalIlluminationProbeComponent()\n"));
 			ANKI_CHECK(m_sceneFile.writeText("comp = node:getSceneNodeBase():getGlobalIlluminationProbeComponent()\n"));
-
-			ANKI_CHECK(m_sceneFile.writeText("comp:setBoundingBox(Vec4.new(%f, %f, %f, 0), Vec4.new(%f, %f, %f, 0))\n",
-											 aabbMin.x(), aabbMin.y(), aabbMin.z(), aabbMax.x(), aabbMax.y(),
-											 aabbMax.z()));
+			ANKI_CHECK(m_sceneFile.writeText("comp:setBoxVolumeSize(Vec3.new(%f, %f, %f))\n", boxSize.x(), boxSize.y(),
+											 boxSize.z()));
 
 
 			if(fadeDistance > 0.0f)
 			if(fadeDistance > 0.0f)
 			{
 			{
@@ -594,7 +586,7 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 			Mat3 rot;
 			Mat3 rot;
 			Vec3 scale;
 			Vec3 scale;
 			getNodeTransform(node, tsl, rot, scale);
 			getNodeTransform(node, tsl, rot, scale);
-			Transform localTrf = Transform(tsl.xyz0(), Mat3x4(Vec3(0.0f), rot), 1.0f);
+			const Transform localTrf = Transform(tsl.xyz0(), Mat3x4(Vec3(0.0f), rot), 1.0f);
 			ANKI_CHECK(writeTransform(parentTrf.combineTransformations(localTrf)));
 			ANKI_CHECK(writeTransform(parentTrf.combineTransformations(localTrf)));
 		}
 		}
 		else
 		else
@@ -608,7 +600,6 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 				cgltf_mesh* m_mesh;
 				cgltf_mesh* m_mesh;
 				cgltf_material* m_mtl;
 				cgltf_material* m_mtl;
 				cgltf_skin* m_skin;
 				cgltf_skin* m_skin;
-				Bool m_selfCollision;
 				RayTypeBit m_rayTypes;
 				RayTypeBit m_rayTypes;
 			};
 			};
 			Ctx* ctx = m_alloc.newInstance<Ctx>();
 			Ctx* ctx = m_alloc.newInstance<Ctx>();
@@ -620,7 +611,16 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 
 
 			HashMapAuto<CString, StringAuto>::Iterator it2;
 			HashMapAuto<CString, StringAuto>::Iterator it2;
 			const Bool selfCollision = (it2 = extras.find("collision_mesh")) != extras.getEnd() && *it2 == "self";
 			const Bool selfCollision = (it2 = extras.find("collision_mesh")) != extras.getEnd() && *it2 == "self";
-			ctx->m_selfCollision = selfCollision;
+
+			U32 maxLod = 0;
+			if(m_lodCount > 1 && !skipMeshLod(*node.mesh, 1))
+			{
+				maxLod = 1;
+			}
+			if(m_lodCount > 2 && !skipMeshLod(*node.mesh, 2))
+			{
+				maxLod = 2;
+			}
 
 
 			// Thread task
 			// Thread task
 			auto callback = [](void* userData, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* signalSemaphore) {
 			auto callback = [](void* userData, U32 threadId, ThreadHive& hive, ThreadHiveSemaphore* signalSemaphore) {
@@ -628,7 +628,6 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 
 
 				// LOD 0
 				// LOD 0
 				Error err = self.m_importer->writeMesh(*self.m_mesh, CString(), self.m_importer->computeLodFactor(0));
 				Error err = self.m_importer->writeMesh(*self.m_mesh, CString(), self.m_importer->computeLodFactor(0));
-				U32 maxLod = 0;
 
 
 				// LOD 1
 				// LOD 1
 				if(!err && self.m_importer->m_lodCount > 1 && !self.m_importer->skipMeshLod(*self.m_mesh, 1))
 				if(!err && self.m_importer->m_lodCount > 1 && !self.m_importer->skipMeshLod(*self.m_mesh, 1))
@@ -636,7 +635,6 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 					StringAuto name(self.m_importer->m_alloc);
 					StringAuto name(self.m_importer->m_alloc);
 					name.sprintf("%s_lod1", self.m_mesh->name);
 					name.sprintf("%s_lod1", self.m_mesh->name);
 					err = self.m_importer->writeMesh(*self.m_mesh, name, self.m_importer->computeLodFactor(1));
 					err = self.m_importer->writeMesh(*self.m_mesh, name, self.m_importer->computeLodFactor(1));
-					maxLod = 1;
 				}
 				}
 
 
 				// LOD 2
 				// LOD 2
@@ -645,7 +643,6 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 					StringAuto name(self.m_importer->m_alloc);
 					StringAuto name(self.m_importer->m_alloc);
 					name.sprintf("%s_lod2", self.m_mesh->name);
 					name.sprintf("%s_lod2", self.m_mesh->name);
 					err = self.m_importer->writeMesh(*self.m_mesh, name, self.m_importer->computeLodFactor(2));
 					err = self.m_importer->writeMesh(*self.m_mesh, name, self.m_importer->computeLodFactor(2));
-					maxLod = 2;
 				}
 				}
 
 
 				if(!err)
 				if(!err)
@@ -655,7 +652,7 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 
 
 				if(!err)
 				if(!err)
 				{
 				{
-					err = self.m_importer->writeModel(*self.m_mesh, (self.m_skin) ? self.m_skin->name : CString());
+					err = self.m_importer->writeModel(*self.m_mesh);
 				}
 				}
 
 
 				if(!err && self.m_skin)
 				if(!err && self.m_skin)
@@ -663,11 +660,6 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 					err = self.m_importer->writeSkeleton(*self.m_skin);
 					err = self.m_importer->writeSkeleton(*self.m_skin);
 				}
 				}
 
 
-				if(!err && self.m_selfCollision)
-				{
-					err = self.m_importer->writeCollisionMesh(*self.m_mesh, maxLod);
-				}
-
 				if(err)
 				if(err)
 				{
 				{
 					self.m_importer->m_errorInThread.store(err._getCode());
 					self.m_importer->m_errorInThread.store(err._getCode());
@@ -693,9 +685,21 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 
 
 			if(selfCollision)
 			if(selfCollision)
 			{
 			{
-				ANKI_CHECK(
-					m_sceneFile.writeText("node2 = scene:newStaticCollisionNode(\"%s_cl\", \"%s%s.ankicl\", trf)\n",
-										  getNodeName(node).cstr(), m_rpath.cstr(), node.mesh->name));
+				ANKI_CHECK(m_sceneFile.writeText("node2 = scene:newStaticCollisionNode(\"%s_cl\")\n",
+												 getNodeName(node).cstr()));
+
+				ANKI_CHECK(m_sceneFile.writeText("comp = node2:getSceneNodeBase():getBodyComponent()\n"));
+				if(maxLod == 0)
+				{
+					ANKI_CHECK(m_sceneFile.writeText("comp:loadMeshResource(\"%s%s.ankimesh\")\n", m_rpath.cstr(),
+													 node.mesh->name));
+				}
+				else
+				{
+					ANKI_CHECK(m_sceneFile.writeText("comp:loadMeshResource(\"%s%s_lod%u.ankimesh\")\n", m_rpath.cstr(),
+													 node.mesh->name, maxLod));
+				}
+				ANKI_CHECK(m_sceneFile.writeText("comp:setWorldTransform(trf)\n"));
 			}
 			}
 		}
 		}
 	}
 	}
@@ -743,7 +747,7 @@ Error GltfImporter::writeTransform(const Transform& trf)
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
-Error GltfImporter::writeModel(const cgltf_mesh& mesh, CString skinName)
+Error GltfImporter::writeModel(const cgltf_mesh& mesh)
 {
 {
 	StringAuto modelFname(m_alloc);
 	StringAuto modelFname(m_alloc);
 	modelFname.sprintf("%s%s_%s.ankimdl", m_outDir.cstr(), mesh.name, mesh.primitives[0].material->name);
 	modelFname.sprintf("%s%s_%s.ankimdl", m_outDir.cstr(), mesh.name, mesh.primitives[0].material->name);
@@ -799,11 +803,6 @@ Error GltfImporter::writeModel(const cgltf_mesh& mesh, CString skinName)
 
 
 	ANKI_CHECK(file.writeText("\t</modelPatches>\n"));
 	ANKI_CHECK(file.writeText("\t</modelPatches>\n"));
 
 
-	if(skinName)
-	{
-		ANKI_CHECK(file.writeText("\t<skeleton>%s%s.ankiskel</skeleton>\n", m_rpath.cstr(), skinName.cstr()));
-	}
-
 	ANKI_CHECK(file.writeText("</model>\n"));
 	ANKI_CHECK(file.writeText("</model>\n"));
 
 
 	return Error::NONE;
 	return Error::NONE;
@@ -1166,34 +1165,6 @@ Error GltfImporter::writeSkeleton(const cgltf_skin& skin)
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
-Error GltfImporter::writeCollisionMesh(const cgltf_mesh& mesh, U32 maxLod)
-{
-	StringAuto fname(m_alloc);
-	fname.sprintf("%s%s.ankicl", m_outDir.cstr(), mesh.name);
-	ANKI_GLTF_LOGI("Importing collision mesh %s", fname.cstr());
-
-	// Write file
-	File file;
-	ANKI_CHECK(file.open(fname.toCString(), FileOpenFlag::WRITE));
-
-	ANKI_CHECK(file.writeText("%s\n", XML_HEADER));
-
-	if(maxLod == 0)
-	{
-		ANKI_CHECK(file.writeText("<collisionShape>\n\t<type>staticMesh</type>\n\t<value>"
-								  "%s%s.ankimesh</value>\n</collisionShape>\n",
-								  m_rpath.cstr(), mesh.name));
-	}
-	else
-	{
-		ANKI_CHECK(file.writeText("<collisionShape>\n\t<type>staticMesh</type>\n\t<value>"
-								  "%s%s_lod%u.ankimesh</value>\n</collisionShape>\n",
-								  m_rpath.cstr(), mesh.name, maxLod));
-	}
-
-	return Error::NONE;
-}
-
 Error GltfImporter::writeLight(const cgltf_node& node, const HashMapAuto<CString, StringAuto>& parentExtras)
 Error GltfImporter::writeLight(const cgltf_node& node, const HashMapAuto<CString, StringAuto>& parentExtras)
 {
 {
 	const cgltf_light& light = *node.light;
 	const cgltf_light& light = *node.light;
@@ -1369,9 +1340,17 @@ Error GltfImporter::writeModelNode(const cgltf_node& node, const HashMapAuto<CSt
 	StringAuto modelFname(m_alloc);
 	StringAuto modelFname(m_alloc);
 	modelFname.sprintf("%s%s_%s.ankimdl", m_rpath.cstr(), node.mesh->name, node.mesh->primitives[0].material->name);
 	modelFname.sprintf("%s%s_%s.ankimdl", m_rpath.cstr(), node.mesh->name, node.mesh->primitives[0].material->name);
 
 
-	ANKI_CHECK(m_sceneFile.writeText("\nnode = scene:newModelNode(\"%s\", \"%s\")\n", getNodeName(node).cstr(),
+	ANKI_CHECK(m_sceneFile.writeText("\nnode = scene:newModelNode(\"%s\")\n", getNodeName(node).cstr()));
+	ANKI_CHECK(m_sceneFile.writeText("node:getSceneNodeBase():getModelComponent():loadModelResource(\"%s\")\n",
 									 modelFname.cstr()));
 									 modelFname.cstr()));
 
 
+	if(node.skin)
+	{
+		ANKI_CHECK(m_sceneFile.writeText(
+			"node:getSceneNodeBase():getSkinComponent():loadSkeletonResource(\"%s%s.ankiskel\")\n", m_rpath.cstr(),
+			node.skin->name));
+	}
+
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 

+ 1 - 2
anki/importer/GltfImporter.h

@@ -132,10 +132,9 @@ private:
 	// Resources
 	// Resources
 	ANKI_USE_RESULT Error writeMesh(const cgltf_mesh& mesh, CString nameOverride, F32 decimateFactor);
 	ANKI_USE_RESULT Error writeMesh(const cgltf_mesh& mesh, CString nameOverride, F32 decimateFactor);
 	ANKI_USE_RESULT Error writeMaterial(const cgltf_material& mtl, RayTypeBit usedRayTypes);
 	ANKI_USE_RESULT Error writeMaterial(const cgltf_material& mtl, RayTypeBit usedRayTypes);
-	ANKI_USE_RESULT Error writeModel(const cgltf_mesh& mesh, CString skinName);
+	ANKI_USE_RESULT Error writeModel(const cgltf_mesh& mesh);
 	ANKI_USE_RESULT Error writeAnimation(const cgltf_animation& anim);
 	ANKI_USE_RESULT Error writeAnimation(const cgltf_animation& anim);
 	ANKI_USE_RESULT Error writeSkeleton(const cgltf_skin& skin);
 	ANKI_USE_RESULT Error writeSkeleton(const cgltf_skin& skin);
-	ANKI_USE_RESULT Error writeCollisionMesh(const cgltf_mesh& mesh, U32 maxLod);
 
 
 	// Scene
 	// Scene
 	ANKI_USE_RESULT Error writeTransform(const Transform& trf);
 	ANKI_USE_RESULT Error writeTransform(const Transform& trf);

+ 68 - 108
anki/importer/GltfImporterMesh.cpp

@@ -7,7 +7,8 @@
 #include <anki/util/StringList.h>
 #include <anki/util/StringList.h>
 #include <anki/collision/Plane.h>
 #include <anki/collision/Plane.h>
 #include <anki/collision/Functions.h>
 #include <anki/collision/Functions.h>
-#include <anki/resource/MeshLoader.h>
+#include <anki/resource/MeshBinary.h>
+#include <anki/shaders/include/ModelTypes.h>
 #include <meshoptimizer/meshoptimizer.h>
 #include <meshoptimizer/meshoptimizer.h>
 
 
 namespace anki
 namespace anki
@@ -65,7 +66,7 @@ static U calcImplicitStride(const cgltf_attribute& attrib)
 #endif
 #endif
 
 
 template<typename T>
 template<typename T>
-Error checkAttribute(const cgltf_attribute& attrib)
+static Error checkAttribute(const cgltf_attribute& attrib)
 {
 {
 	if(cgltfComponentCount(attrib.data->type) != T::COMPONENT_COUNT)
 	if(cgltfComponentCount(attrib.data->type) != T::COMPONENT_COUNT)
 	{
 	{
@@ -90,6 +91,21 @@ Error checkAttribute(const cgltf_attribute& attrib)
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
+/// Align after laying a buffer in a file.
+static Error alignBufferInFile(PtrSize bufferSize, File& file)
+{
+	const PtrSize alignedBufferSize = getAlignedRoundUp(MESH_BINARY_BUFFER_ALIGNMENT, bufferSize);
+	const PtrSize extraBytes = alignedBufferSize - bufferSize;
+
+	for(U32 i = 0; i < extraBytes; ++i)
+	{
+		U8 value = 0;
+		ANKI_CHECK(file.write(&value, sizeof(value)));
+	}
+
+	return Error::NONE;
+}
+
 class TempVertex
 class TempVertex
 {
 {
 public:
 public:
@@ -115,8 +131,8 @@ public:
 	DynamicArrayAuto<TempVertex> m_verts;
 	DynamicArrayAuto<TempVertex> m_verts;
 	DynamicArrayAuto<U32> m_indices;
 	DynamicArrayAuto<U32> m_indices;
 
 
-	Vec3 m_aabbMin{MAX_F32};
-	Vec3 m_aabbMax{MIN_F32};
+	Vec3 m_aabbMin = Vec3(MAX_F32);
+	Vec3 m_aabbMax = Vec3(MIN_F32);
 
 
 	U32 m_firstIdx = MAX_U32;
 	U32 m_firstIdx = MAX_U32;
 	U32 m_idxCount = MAX_U32;
 	U32 m_idxCount = MAX_U32;
@@ -128,12 +144,6 @@ public:
 	}
 	}
 };
 };
 
 
-struct WeightVertex
-{
-	U16Vec4 m_boneIndices{MAX_U16};
-	U8Vec4 m_weights{0_U8};
-};
-
 static void reindexSubmesh(SubMesh& submesh, GenericMemoryPoolAllocator<U8> alloc)
 static void reindexSubmesh(SubMesh& submesh, GenericMemoryPoolAllocator<U8> alloc)
 {
 {
 	const U32 vertSize = sizeof(submesh.m_verts[0]);
 	const U32 vertSize = sizeof(submesh.m_verts[0]);
@@ -610,58 +620,50 @@ Error GltfImporter::writeMesh(const cgltf_mesh& mesh, CString nameOverride, F32
 	}
 	}
 
 
 	// Chose the formats of the attributes
 	// Chose the formats of the attributes
-	MeshBinaryFile::Header header = {};
+	MeshBinaryHeader header;
+	memset(&header, 0, sizeof(header));
 	{
 	{
 		// Positions
 		// Positions
-		const Vec3 dist3d = aabbMin.abs().max(aabbMax.abs());
-		const F32 maxPositionDistance = max(max(dist3d.x(), dist3d.y()), dist3d.z());
-		MeshBinaryFile::VertexAttribute& posa = header.m_vertexAttributes[VertexAttributeLocation::POSITION];
+		MeshBinaryVertexAttribute& posa = header.m_vertexAttributes[VertexAttributeLocation::POSITION];
 		posa.m_bufferBinding = 0;
 		posa.m_bufferBinding = 0;
-		posa.m_format = (maxPositionDistance < 2.0) ? Format::R16G16B16A16_SFLOAT : Format::R32G32B32_SFLOAT;
+		posa.m_format = Format::R32G32B32_SFLOAT;
 		posa.m_relativeOffset = 0;
 		posa.m_relativeOffset = 0;
 		posa.m_scale = 1.0f;
 		posa.m_scale = 1.0f;
 
 
 		// Normals
 		// Normals
-		MeshBinaryFile::VertexAttribute& na = header.m_vertexAttributes[VertexAttributeLocation::NORMAL];
+		MeshBinaryVertexAttribute& na = header.m_vertexAttributes[VertexAttributeLocation::NORMAL];
 		na.m_bufferBinding = 1;
 		na.m_bufferBinding = 1;
 		na.m_format = Format::A2B10G10R10_SNORM_PACK32;
 		na.m_format = Format::A2B10G10R10_SNORM_PACK32;
 		na.m_relativeOffset = 0;
 		na.m_relativeOffset = 0;
 		na.m_scale = 1.0f;
 		na.m_scale = 1.0f;
 
 
 		// Tangents
 		// Tangents
-		MeshBinaryFile::VertexAttribute& ta = header.m_vertexAttributes[VertexAttributeLocation::TANGENT];
+		MeshBinaryVertexAttribute& ta = header.m_vertexAttributes[VertexAttributeLocation::TANGENT];
 		ta.m_bufferBinding = 1;
 		ta.m_bufferBinding = 1;
 		ta.m_format = Format::A2B10G10R10_SNORM_PACK32;
 		ta.m_format = Format::A2B10G10R10_SNORM_PACK32;
 		ta.m_relativeOffset = sizeof(U32);
 		ta.m_relativeOffset = sizeof(U32);
-		ta.m_scale = 1.0;
+		ta.m_scale = 1.0f;
 
 
 		// UVs
 		// UVs
-		MeshBinaryFile::VertexAttribute& uva = header.m_vertexAttributes[VertexAttributeLocation::UV];
+		MeshBinaryVertexAttribute& uva = header.m_vertexAttributes[VertexAttributeLocation::UV];
 		uva.m_bufferBinding = 1;
 		uva.m_bufferBinding = 1;
-		if(minUvDistance >= 0.0 && maxUvDistance <= 1.0)
-		{
-			uva.m_format = Format::R16G16_UNORM;
-		}
-		else
-		{
-			uva.m_format = Format::R16G16_SFLOAT;
-		}
+		uva.m_format = Format::R32G32_SFLOAT;
 		uva.m_relativeOffset = sizeof(U32) * 2;
 		uva.m_relativeOffset = sizeof(U32) * 2;
 		uva.m_scale = 1.0f;
 		uva.m_scale = 1.0f;
 
 
 		// Bone weight
 		// Bone weight
 		if(hasBoneWeights)
 		if(hasBoneWeights)
 		{
 		{
-			MeshBinaryFile::VertexAttribute& bidxa = header.m_vertexAttributes[VertexAttributeLocation::BONE_INDICES];
+			MeshBinaryVertexAttribute& bidxa = header.m_vertexAttributes[VertexAttributeLocation::BONE_INDICES];
 			bidxa.m_bufferBinding = 2;
 			bidxa.m_bufferBinding = 2;
-			bidxa.m_format = Format::R16G16B16A16_UINT;
+			bidxa.m_format = Format::R8G8B8A8_UINT;
 			bidxa.m_relativeOffset = 0;
 			bidxa.m_relativeOffset = 0;
 			bidxa.m_scale = 1.0f;
 			bidxa.m_scale = 1.0f;
 
 
-			MeshBinaryFile::VertexAttribute& wa = header.m_vertexAttributes[VertexAttributeLocation::BONE_WEIGHTS];
+			MeshBinaryVertexAttribute& wa = header.m_vertexAttributes[VertexAttributeLocation::BONE_WEIGHTS];
 			wa.m_bufferBinding = 2;
 			wa.m_bufferBinding = 2;
 			wa.m_format = Format::R8G8B8A8_UNORM;
 			wa.m_format = Format::R8G8B8A8_UNORM;
-			wa.m_relativeOffset = sizeof(U16Vec4);
+			wa.m_relativeOffset = sizeof(U8Vec4);
 			wa.m_scale = 1.0f;
 			wa.m_scale = 1.0f;
 		}
 		}
 	}
 	}
@@ -669,40 +671,28 @@ Error GltfImporter::writeMesh(const cgltf_mesh& mesh, CString nameOverride, F32
 	// Arange the attributes into vert buffers
 	// Arange the attributes into vert buffers
 	{
 	{
 		// First buff has positions
 		// First buff has positions
-		const MeshBinaryFile::VertexAttribute& posa = header.m_vertexAttributes[VertexAttributeLocation::POSITION];
-		if(posa.m_format == Format::R32G32B32_SFLOAT)
-		{
-			header.m_vertexBuffers[0].m_vertexStride = sizeof(F32) * 3;
-		}
-		else if(posa.m_format == Format::R16G16B16A16_SFLOAT)
-		{
-			header.m_vertexBuffers[0].m_vertexStride = sizeof(U16) * 4;
-		}
-		else
-		{
-			ANKI_ASSERT(0);
-		}
+		header.m_vertexBuffers[0].m_vertexStride = sizeof(Vec3);
 		++header.m_vertexBufferCount;
 		++header.m_vertexBufferCount;
 
 
 		// 2nd buff has normal + tangent + texcoords
 		// 2nd buff has normal + tangent + texcoords
-		header.m_vertexBuffers[1].m_vertexStride = sizeof(U32) * 2 + sizeof(U16) * 2;
+		header.m_vertexBuffers[1].m_vertexStride = sizeof(MainVertex);
 		++header.m_vertexBufferCount;
 		++header.m_vertexBufferCount;
 
 
 		// 3rd has bone weights
 		// 3rd has bone weights
 		if(hasBoneWeights)
 		if(hasBoneWeights)
 		{
 		{
-			header.m_vertexBuffers[2].m_vertexStride = sizeof(WeightVertex);
+			header.m_vertexBuffers[2].m_vertexStride = sizeof(BoneInfoVertex);
 			++header.m_vertexBufferCount;
 			++header.m_vertexBufferCount;
 		}
 		}
 	}
 	}
 
 
 	// Write some other header stuff
 	// Write some other header stuff
 	{
 	{
-		memcpy(&header.m_magic[0], MeshBinaryFile::MAGIC, 8);
-		header.m_flags = MeshBinaryFile::Flag::NONE;
+		memcpy(&header.m_magic[0], MESH_MAGIC, 8);
+		header.m_flags = MeshBinaryFlag::NONE;
 		if(convex)
 		if(convex)
 		{
 		{
-			header.m_flags |= MeshBinaryFile::Flag::CONVEX;
+			header.m_flags |= MeshBinaryFlag::CONVEX;
 		}
 		}
 		header.m_indexType = IndexType::U16;
 		header.m_indexType = IndexType::U16;
 		header.m_totalIndexCount = totalIndexCount;
 		header.m_totalIndexCount = totalIndexCount;
@@ -722,7 +712,7 @@ Error GltfImporter::writeMesh(const cgltf_mesh& mesh, CString nameOverride, F32
 	// Write sub meshes
 	// Write sub meshes
 	for(const SubMesh& in : submeshes)
 	for(const SubMesh& in : submeshes)
 	{
 	{
-		MeshBinaryFile::SubMesh out;
+		MeshBinarySubMesh out;
 		out.m_firstIndex = in.m_firstIdx;
 		out.m_firstIndex = in.m_firstIdx;
 		out.m_indexCount = in.m_idxCount;
 		out.m_indexCount = in.m_idxCount;
 		out.m_aabbMin = in.m_aabbMin;
 		out.m_aabbMin = in.m_aabbMin;
@@ -753,50 +743,26 @@ Error GltfImporter::writeMesh(const cgltf_mesh& mesh, CString nameOverride, F32
 		vertCount += submesh.m_verts.getSize();
 		vertCount += submesh.m_verts.getSize();
 	}
 	}
 
 
-	// Write first vert buffer
+	ANKI_CHECK(alignBufferInFile(header.m_totalIndexCount * sizeof(U16), file));
+
+	// Write position vert buffer
 	for(const SubMesh& submesh : submeshes)
 	for(const SubMesh& submesh : submeshes)
 	{
 	{
-		const MeshBinaryFile::VertexAttribute& posa = header.m_vertexAttributes[VertexAttributeLocation::POSITION];
-		if(posa.m_format == Format::R32G32B32_SFLOAT)
+		DynamicArrayAuto<Vec3> positions(m_alloc);
+		positions.create(submesh.m_verts.getSize());
+		for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
 		{
 		{
-			DynamicArrayAuto<Vec3> positions(m_alloc);
-			positions.create(submesh.m_verts.getSize());
-			for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
-			{
-				positions[v] = submesh.m_verts[v].m_position;
-			}
-			ANKI_CHECK(file.write(&positions[0], positions.getSizeInBytes()));
-		}
-		else if(posa.m_format == Format::R16G16B16A16_SFLOAT)
-		{
-			DynamicArrayAuto<HVec4> pos16(m_alloc);
-			pos16.create(submesh.m_verts.getSize());
-
-			for(U32 v = 0; v < submesh.m_verts.getSize(); ++v)
-			{
-				pos16[v] = HVec4(F16(submesh.m_verts[v].m_position.x()), F16(submesh.m_verts[v].m_position.y()),
-								 F16(submesh.m_verts[v].m_position.z()), F16(0.0f));
-			}
-
-			ANKI_CHECK(file.write(&pos16[0], pos16.getSizeInBytes()));
-		}
-		else
-		{
-			ANKI_ASSERT(0);
+			positions[v] = submesh.m_verts[v].m_position;
 		}
 		}
+		ANKI_CHECK(file.write(&positions[0], positions.getSizeInBytes()));
 	}
 	}
 
 
+	ANKI_CHECK(alignBufferInFile(header.m_totalVertexCount * sizeof(Vec3), file));
+
 	// Write the 2nd vert buffer
 	// Write the 2nd vert buffer
 	for(const SubMesh& submesh : submeshes)
 	for(const SubMesh& submesh : submeshes)
 	{
 	{
-		struct Vert
-		{
-			U32 m_n;
-			U32 m_t;
-			U16 m_uv[2];
-		};
-
-		DynamicArrayAuto<Vert> verts(m_alloc);
+		DynamicArrayAuto<MainVertex> verts(m_alloc);
 		verts.create(submesh.m_verts.getSize());
 		verts.create(submesh.m_verts.getSize());
 
 
 		for(U32 i = 0; i < verts.getSize(); ++i)
 		for(U32 i = 0; i < verts.getSize(); ++i)
@@ -805,46 +771,38 @@ Error GltfImporter::writeMesh(const cgltf_mesh& mesh, CString nameOverride, F32
 			const Vec4& tangent = submesh.m_verts[i].m_tangent;
 			const Vec4& tangent = submesh.m_verts[i].m_tangent;
 			const Vec2& uv = submesh.m_verts[i].m_uv;
 			const Vec2& uv = submesh.m_verts[i].m_uv;
 
 
-			verts[i].m_n = packColorToR10G10B10A2SNorm(normal.x(), normal.y(), normal.z(), 0.0f);
-			verts[i].m_t = packColorToR10G10B10A2SNorm(tangent.x(), tangent.y(), tangent.z(), tangent.w());
-
-			const Format uvfmt = header.m_vertexAttributes[VertexAttributeLocation::UV].m_format;
-			if(uvfmt == Format::R16G16_UNORM)
-			{
-				assert(uv[0] <= 1.0 && uv[0] >= 0.0 && uv[1] <= 1.0 && uv[1] >= 0.0);
-				verts[i].m_uv[0] = U16(uv[0] * 0xFFFF);
-				verts[i].m_uv[1] = U16(uv[1] * 0xFFFF);
-			}
-			else if(uvfmt == Format::R16G16_SFLOAT)
-			{
-				verts[i].m_uv[0] = F16(uv[0]).toU16();
-				verts[i].m_uv[1] = F16(uv[1]).toU16();
-			}
-			else
-			{
-				ANKI_ASSERT(0);
-			}
+			verts[i].m_normal = packColorToR10G10B10A2SNorm(normal.x(), normal.y(), normal.z(), 0.0f);
+			verts[i].m_tangent = packColorToR10G10B10A2SNorm(tangent.x(), tangent.y(), tangent.z(), tangent.w());
+			verts[i].m_uvs[UV_CHANNEL_0] = uv;
 		}
 		}
 
 
 		ANKI_CHECK(file.write(&verts[0], verts.getSizeInBytes()));
 		ANKI_CHECK(file.write(&verts[0], verts.getSizeInBytes()));
 	}
 	}
 
 
+	ANKI_CHECK(alignBufferInFile(header.m_totalVertexCount * sizeof(MainVertex), file));
+
 	// Write 3rd vert buffer
 	// Write 3rd vert buffer
 	if(hasBoneWeights)
 	if(hasBoneWeights)
 	{
 	{
 		for(const SubMesh& submesh : submeshes)
 		for(const SubMesh& submesh : submeshes)
 		{
 		{
-			DynamicArrayAuto<WeightVertex> verts(m_alloc);
+			DynamicArrayAuto<BoneInfoVertex> verts(m_alloc);
 			verts.create(submesh.m_verts.getSize());
 			verts.create(submesh.m_verts.getSize());
 
 
 			for(U32 i = 0; i < verts.getSize(); ++i)
 			for(U32 i = 0; i < verts.getSize(); ++i)
 			{
 			{
-				WeightVertex vert;
+				BoneInfoVertex vert;
 
 
 				for(U32 c = 0; c < 4; ++c)
 				for(U32 c = 0; c < 4; ++c)
 				{
 				{
-					vert.m_boneIndices[c] = submesh.m_verts[i].m_boneIds[c];
-					vert.m_weights[c] = U8(submesh.m_verts[i].m_boneWeights[c] * F32(MAX_U8));
+					if(submesh.m_verts[i].m_boneIds[c] > 0XFF)
+					{
+						ANKI_GLTF_LOGE("Only 256 bones are supported");
+						return Error::USER_DATA;
+					}
+
+					vert.m_boneIndices[c] = U8(submesh.m_verts[i].m_boneIds[c]);
+					vert.m_boneWeights[c] = U8(submesh.m_verts[i].m_boneWeights[c] * F32(MAX_U8));
 				}
 				}
 
 
 				verts[i] = vert;
 				verts[i] = vert;
@@ -852,6 +810,8 @@ Error GltfImporter::writeMesh(const cgltf_mesh& mesh, CString nameOverride, F32
 
 
 			ANKI_CHECK(file.write(&verts[0], verts.getSizeInBytes()));
 			ANKI_CHECK(file.write(&verts[0], verts.getSizeInBytes()));
 		}
 		}
+
+		ANKI_CHECK(alignBufferInFile(header.m_totalVertexCount * sizeof(BoneInfoVertex), file));
 	}
 	}
 
 
 	return Error::NONE;
 	return Error::NONE;

+ 1 - 1
anki/math/Functions.h

@@ -16,7 +16,7 @@ namespace anki
 /// @{
 /// @{
 
 
 /// Just PI.
 /// Just PI.
-const F32 PI = 3.14159265358979323846f;
+constexpr F32 PI = 3.14159265358979323846f;
 
 
 /// Floating point epsilon.
 /// Floating point epsilon.
 const F32 EPSILON = 1.0e-6f;
 const F32 EPSILON = 1.0e-6f;

+ 11 - 11
anki/math/Transform.h

@@ -44,7 +44,7 @@ public:
 		ANKI_ASSERT(isZero(scales.x() - scales.y(), E) && isZero(scales.y() - scales.z(), E)
 		ANKI_ASSERT(isZero(scales.x() - scales.y(), E) && isZero(scales.y() - scales.z(), E)
 					&& "Expecting uniform scale");
 					&& "Expecting uniform scale");
 
 
-		m_rotation.setColumns(s0 / scales.x(), s1 / scales.x(), s2 / scales.x(), TVec<T, 3>(0.0));
+		m_rotation.setColumns(s0 / scales.x(), s1 / scales.x(), s2 / scales.x(), TVec<T, 3>(T(0)));
 		m_origin = m4.getTranslationPart().xyz0();
 		m_origin = m4.getTranslationPart().xyz0();
 		m_scale = scales.x();
 		m_scale = scales.x();
 		checkW();
 		checkW();
@@ -60,23 +60,23 @@ public:
 
 
 	explicit TTransform(const TVec<T, 4>& origin)
 	explicit TTransform(const TVec<T, 4>& origin)
 		: m_origin(origin)
 		: m_origin(origin)
-		, m_rotation(Mat3x4::getIdentity())
-		, m_scale(1.0)
+		, m_rotation(TMat<T, 3, 4>::getIdentity())
+		, m_scale(T(1))
 	{
 	{
 		checkW();
 		checkW();
 	}
 	}
 
 
 	explicit TTransform(const TMat<T, 3, 4>& rotation)
 	explicit TTransform(const TMat<T, 3, 4>& rotation)
-		: m_origin(Vec4(0.0))
+		: m_origin(TVec<T, 4>(T(0)))
 		, m_rotation(rotation)
 		, m_rotation(rotation)
-		, m_scale(1.0)
+		, m_scale(T(1))
 	{
 	{
 		checkW();
 		checkW();
 	}
 	}
 
 
 	TTransform(const T scale)
 	TTransform(const T scale)
-		: m_origin(Vec4(0.0))
-		, m_rotation(Mat3x4::getIdentity())
+		: m_origin(TVec<T, 4>(T(0)))
+		, m_rotation(TMat<T, 3, 4>::getIdentity())
 		, m_scale(scale)
 		, m_scale(scale)
 	{
 	{
 		checkW();
 		checkW();
@@ -163,7 +163,7 @@ public:
 
 
 	static TTransform getIdentity()
 	static TTransform getIdentity()
 	{
 	{
-		return TTransform(TVec<T, 4>(0.0), TMat<T, 3, 4>::getIdentity(), 1.0);
+		return TTransform(TVec<T, 4>(T(0)), TMat<T, 3, 4>::getIdentity(), T(1));
 	}
 	}
 
 
 	/// @copybrief combineTTransformations
 	/// @copybrief combineTTransformations
@@ -173,7 +173,7 @@ public:
 		const TTransform& a = *this;
 		const TTransform& a = *this;
 		TTransform out;
 		TTransform out;
 
 
-		out.m_origin = TVec<T, 4>(a.m_rotation * (b.m_origin * a.m_scale), 0.0) + a.m_origin;
+		out.m_origin = TVec<T, 4>(a.m_rotation * (b.m_origin * a.m_scale), T(0)) + a.m_origin;
 
 
 		out.m_rotation = a.m_rotation.combineTransformations(b.m_rotation);
 		out.m_rotation = a.m_rotation.combineTransformations(b.m_rotation);
 		out.m_scale = a.m_scale * b.m_scale;
 		out.m_scale = a.m_scale * b.m_scale;
@@ -196,7 +196,7 @@ public:
 	void invert()
 	void invert()
 	{
 	{
 		m_rotation.transposeRotationPart();
 		m_rotation.transposeRotationPart();
-		m_scale = 1.0 / m_scale;
+		m_scale = T(1) / m_scale;
 		m_origin = -(m_rotation * (m_scale * m_origin));
 		m_origin = -(m_rotation * (m_scale * m_origin));
 	}
 	}
 
 
@@ -254,7 +254,7 @@ private:
 
 
 	void checkW() const
 	void checkW() const
 	{
 	{
-		ANKI_ASSERT(m_origin.w() == 0.0);
+		ANKI_ASSERT(m_origin.w() == T(0));
 	}
 	}
 };
 };
 
 

+ 13 - 7
anki/physics/PhysicsBody.cpp

@@ -13,6 +13,8 @@ namespace anki
 PhysicsBody::PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init)
 PhysicsBody::PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init)
 	: PhysicsFilteredObject(CLASS_TYPE, world)
 	: PhysicsFilteredObject(CLASS_TYPE, world)
 {
 {
+	ANKI_ASSERT(init.m_mass >= 0.0f);
+
 	const Bool dynamic = init.m_mass > 0.0f;
 	const Bool dynamic = init.m_mass > 0.0f;
 	m_shape = init.m_shape;
 	m_shape = init.m_shape;
 	m_mass = init.m_mass;
 	m_mass = init.m_mass;
@@ -45,18 +47,12 @@ PhysicsBody::PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init)
 		collidesWith &= ~PhysicsMaterialBit::STATIC_GEOMETRY;
 		collidesWith &= ~PhysicsMaterialBit::STATIC_GEOMETRY;
 	}
 	}
 	setMaterialMask(collidesWith);
 	setMaterialMask(collidesWith);
-
 	setTransform(init.m_transform);
 	setTransform(init.m_transform);
-
-	// Add to world
-	auto lock = getWorld().lockBtWorld();
-	getWorld().getBtWorld()->addRigidBody(m_body.get());
 }
 }
 
 
 PhysicsBody::~PhysicsBody()
 PhysicsBody::~PhysicsBody()
 {
 {
-	auto lock = getWorld().lockBtWorld();
-	getWorld().getBtWorld()->removeRigidBody(m_body.get());
+	m_body.destroy();
 }
 }
 
 
 void PhysicsBody::setMass(F32 mass)
 void PhysicsBody::setMass(F32 mass)
@@ -69,4 +65,14 @@ void PhysicsBody::setMass(F32 mass)
 	m_mass = mass;
 	m_mass = mass;
 }
 }
 
 
+void PhysicsBody::registerToWorld()
+{
+	getWorld().getBtWorld().addRigidBody(m_body.get());
+}
+
+void PhysicsBody::unregisterFromWorld()
+{
+	getWorld().getBtWorld().removeRigidBody(m_body.get());
+}
+
 } // end namespace anki
 } // end namespace anki

+ 5 - 3
anki/physics/PhysicsBody.h

@@ -27,11 +27,9 @@ public:
 /// Rigid body.
 /// Rigid body.
 class PhysicsBody : public PhysicsFilteredObject
 class PhysicsBody : public PhysicsFilteredObject
 {
 {
-	ANKI_PHYSICS_OBJECT
+	ANKI_PHYSICS_OBJECT(PhysicsObjectType::BODY)
 
 
 public:
 public:
-	static const PhysicsObjectType CLASS_TYPE = PhysicsObjectType::BODY;
-
 	const Transform& getTransform() const
 	const Transform& getTransform() const
 	{
 	{
 		return m_trf;
 		return m_trf;
@@ -129,6 +127,10 @@ private:
 	PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init);
 	PhysicsBody(PhysicsWorld* world, const PhysicsBodyInitInfo& init);
 
 
 	~PhysicsBody();
 	~PhysicsBody();
+
+	void registerToWorld() override;
+
+	void unregisterFromWorld() override;
 };
 };
 /// @}
 /// @}
 
 

+ 14 - 6
anki/physics/PhysicsCollisionShape.h

@@ -18,9 +18,9 @@ namespace anki
 /// The base of all collision shapes.
 /// The base of all collision shapes.
 class PhysicsCollisionShape : public PhysicsObject
 class PhysicsCollisionShape : public PhysicsObject
 {
 {
-public:
-	static const PhysicsObjectType CLASS_TYPE = PhysicsObjectType::COLLISION_SHAPE;
+	ANKI_PHYSICS_OBJECT(PhysicsObjectType::COLLISION_SHAPE)
 
 
+public:
 	ANKI_INTERNAL const btCollisionShape* getBtShape(Bool forDynamicBodies = false) const
 	ANKI_INTERNAL const btCollisionShape* getBtShape(Bool forDynamicBodies = false) const
 	{
 	{
 		return getBtShapeInternal(forDynamicBodies);
 		return getBtShapeInternal(forDynamicBodies);
@@ -88,12 +88,20 @@ protected:
 			return nullptr;
 			return nullptr;
 		}
 		}
 	}
 	}
+
+	void registerToWorld() override
+	{
+	}
+
+	void unregisterFromWorld() override
+	{
+	}
 };
 };
 
 
 /// Sphere collision shape.
 /// Sphere collision shape.
 class PhysicsSphere final : public PhysicsCollisionShape
 class PhysicsSphere final : public PhysicsCollisionShape
 {
 {
-	ANKI_PHYSICS_OBJECT
+	ANKI_PHYSICS_OBJECT(PhysicsObjectType::COLLISION_SHAPE)
 
 
 private:
 private:
 	PhysicsSphere(PhysicsWorld* world, F32 radius);
 	PhysicsSphere(PhysicsWorld* world, F32 radius);
@@ -104,7 +112,7 @@ private:
 /// Box collision shape.
 /// Box collision shape.
 class PhysicsBox final : public PhysicsCollisionShape
 class PhysicsBox final : public PhysicsCollisionShape
 {
 {
-	ANKI_PHYSICS_OBJECT
+	ANKI_PHYSICS_OBJECT(PhysicsObjectType::COLLISION_SHAPE)
 
 
 private:
 private:
 	PhysicsBox(PhysicsWorld* world, const Vec3& extend);
 	PhysicsBox(PhysicsWorld* world, const Vec3& extend);
@@ -115,7 +123,7 @@ private:
 /// Convex hull collision shape.
 /// Convex hull collision shape.
 class PhysicsConvexHull final : public PhysicsCollisionShape
 class PhysicsConvexHull final : public PhysicsCollisionShape
 {
 {
-	ANKI_PHYSICS_OBJECT
+	ANKI_PHYSICS_OBJECT(PhysicsObjectType::COLLISION_SHAPE)
 
 
 private:
 private:
 	PhysicsConvexHull(PhysicsWorld* world, const Vec3* positions, U32 positionsCount, U32 positionsStride);
 	PhysicsConvexHull(PhysicsWorld* world, const Vec3* positions, U32 positionsCount, U32 positionsStride);
@@ -126,7 +134,7 @@ private:
 /// Static triangle mesh shape.
 /// Static triangle mesh shape.
 class PhysicsTriangleSoup final : public PhysicsCollisionShape
 class PhysicsTriangleSoup final : public PhysicsCollisionShape
 {
 {
-	ANKI_PHYSICS_OBJECT
+	ANKI_PHYSICS_OBJECT(PhysicsObjectType::COLLISION_SHAPE)
 
 
 private:
 private:
 	ClassWrapper<btTriangleMesh> m_mesh;
 	ClassWrapper<btTriangleMesh> m_mesh;

+ 1 - 3
anki/physics/PhysicsDrawer.cpp

@@ -11,9 +11,7 @@ namespace anki
 
 
 void PhysicsDrawer::drawWorld(const PhysicsWorld& world)
 void PhysicsDrawer::drawWorld(const PhysicsWorld& world)
 {
 {
-	auto lock = world.lockBtWorld();
-
-	btDynamicsWorld& btWorld = *const_cast<btDynamicsWorld*>(world.getBtWorld());
+	btDynamicsWorld& btWorld = const_cast<btDynamicsWorld&>(world.getBtWorld());
 
 
 	btWorld.setDebugDrawer(&m_debugDraw);
 	btWorld.setDebugDrawer(&m_debugDraw);
 	btWorld.debugDrawWorld();
 	btWorld.debugDrawWorld();

+ 8 - 14
anki/physics/PhysicsJoint.cpp

@@ -16,18 +16,14 @@ PhysicsJoint::PhysicsJoint(PhysicsWorld* world, JointType type)
 {
 {
 }
 }
 
 
-void PhysicsJoint::addToWorld()
+void PhysicsJoint::registerToWorld()
 {
 {
-	getJoint()->setUserConstraintPtr(static_cast<PhysicsObject*>(this));
-
-	auto lock = getWorld().lockBtWorld();
-	getWorld().getBtWorld()->addConstraint(getJoint());
+	getWorld().getBtWorld().addConstraint(getJoint());
 }
 }
 
 
-void PhysicsJoint::removeFromWorld()
+void PhysicsJoint::unregisterFromWorld()
 {
 {
-	auto lock = getWorld().lockBtWorld();
-	getWorld().getBtWorld()->removeConstraint(getJoint());
+	getWorld().getBtWorld().removeConstraint(getJoint());
 }
 }
 
 
 PhysicsPoint2PointJoint::PhysicsPoint2PointJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPos)
 PhysicsPoint2PointJoint::PhysicsPoint2PointJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPos)
@@ -35,7 +31,7 @@ PhysicsPoint2PointJoint::PhysicsPoint2PointJoint(PhysicsWorld* world, PhysicsBod
 {
 {
 	m_bodyA = bodyA;
 	m_bodyA = bodyA;
 	m_p2p.init(*m_bodyA->getBtBody(), toBt(relPos));
 	m_p2p.init(*m_bodyA->getBtBody(), toBt(relPos));
-	addToWorld();
+	getJoint()->setUserConstraintPtr(static_cast<PhysicsObject*>(this));
 }
 }
 
 
 PhysicsPoint2PointJoint::PhysicsPoint2PointJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPosA,
 PhysicsPoint2PointJoint::PhysicsPoint2PointJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPosA,
@@ -47,12 +43,11 @@ PhysicsPoint2PointJoint::PhysicsPoint2PointJoint(PhysicsWorld* world, PhysicsBod
 	m_bodyB = bodyB;
 	m_bodyB = bodyB;
 
 
 	m_p2p.init(*m_bodyA->getBtBody(), *m_bodyB->getBtBody(), toBt(relPosA), toBt(relPosB));
 	m_p2p.init(*m_bodyA->getBtBody(), *m_bodyB->getBtBody(), toBt(relPosA), toBt(relPosB));
-	addToWorld();
+	getJoint()->setUserConstraintPtr(static_cast<PhysicsObject*>(this));
 }
 }
 
 
 PhysicsPoint2PointJoint::~PhysicsPoint2PointJoint()
 PhysicsPoint2PointJoint::~PhysicsPoint2PointJoint()
 {
 {
-	removeFromWorld();
 	m_p2p.destroy();
 	m_p2p.destroy();
 }
 }
 
 
@@ -61,13 +56,12 @@ PhysicsHingeJoint::PhysicsHingeJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA,
 {
 {
 	m_bodyA = bodyA;
 	m_bodyA = bodyA;
 	m_hinge.init(*m_bodyA->getBtBody(), toBt(relPos), toBt(axis));
 	m_hinge.init(*m_bodyA->getBtBody(), toBt(relPos), toBt(axis));
-	addToWorld();
+	getJoint()->setUserConstraintPtr(static_cast<PhysicsObject*>(this));
 }
 }
 
 
 PhysicsHingeJoint::~PhysicsHingeJoint()
 PhysicsHingeJoint::~PhysicsHingeJoint()
 {
 {
-	removeFromWorld();
 	m_hinge.destroy();
 	m_hinge.destroy();
 }
 }
 
 
-} // end namespace anki
+} // end namespace anki

+ 7 - 7
anki/physics/PhysicsJoint.h

@@ -17,9 +17,9 @@ namespace anki
 /// Joint base class. Joints connect two (or a single one) rigid bodies together.
 /// Joint base class. Joints connect two (or a single one) rigid bodies together.
 class PhysicsJoint : public PhysicsObject
 class PhysicsJoint : public PhysicsObject
 {
 {
-public:
-	static const PhysicsObjectType CLASS_TYPE = PhysicsObjectType::JOINT;
+	ANKI_PHYSICS_OBJECT(PhysicsObjectType::JOINT)
 
 
+public:
 	/// Set the breaking impulse.
 	/// Set the breaking impulse.
 	void setBreakingImpulseThreshold(F32 impulse)
 	void setBreakingImpulseThreshold(F32 impulse)
 	{
 	{
@@ -58,8 +58,8 @@ protected:
 
 
 	PhysicsJoint(PhysicsWorld* world, JointType type);
 	PhysicsJoint(PhysicsWorld* world, JointType type);
 
 
-	void addToWorld();
-	void removeFromWorld();
+	void registerToWorld() override;
+	void unregisterFromWorld() override;
 
 
 	const btTypedConstraint* getJoint() const
 	const btTypedConstraint* getJoint() const
 	{
 	{
@@ -89,7 +89,7 @@ protected:
 /// Point to point joint.
 /// Point to point joint.
 class PhysicsPoint2PointJoint : public PhysicsJoint
 class PhysicsPoint2PointJoint : public PhysicsJoint
 {
 {
-	ANKI_PHYSICS_OBJECT
+	ANKI_PHYSICS_OBJECT(PhysicsObjectType::JOINT)
 
 
 private:
 private:
 	PhysicsPoint2PointJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPos);
 	PhysicsPoint2PointJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPos);
@@ -103,7 +103,7 @@ private:
 /// Hinge joint.
 /// Hinge joint.
 class PhysicsHingeJoint : public PhysicsJoint
 class PhysicsHingeJoint : public PhysicsJoint
 {
 {
-	ANKI_PHYSICS_OBJECT
+	ANKI_PHYSICS_OBJECT(PhysicsObjectType::JOINT)
 
 
 private:
 private:
 	PhysicsHingeJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPos, const Vec3& axis);
 	PhysicsHingeJoint(PhysicsWorld* world, PhysicsBodyPtr bodyA, const Vec3& relPos, const Vec3& axis);
@@ -115,4 +115,4 @@ private:
 };
 };
 /// @}
 /// @}
 
 
-} // end namespace anki
+} // end namespace anki

+ 19 - 1
anki/physics/PhysicsObject.cpp

@@ -9,9 +9,27 @@
 namespace anki
 namespace anki
 {
 {
 
 
+PhysicsFilteredObject::~PhysicsFilteredObject()
+{
+	for(PhysicsTriggerFilteredPair* pair : m_triggerFilteredPairs)
+	{
+		if(pair == nullptr)
+		{
+			continue;
+		}
+
+		pair->m_filteredObject = nullptr;
+
+		if(pair->shouldDelete())
+		{
+			getAllocator().deleteInstance(pair);
+		}
+	}
+}
+
 HeapAllocator<U8> PhysicsObject::getAllocator() const
 HeapAllocator<U8> PhysicsObject::getAllocator() const
 {
 {
 	return m_world->getAllocator();
 	return m_world->getAllocator();
 }
 }
 
 
-} // end namespace anki
+} // end namespace anki

+ 58 - 17
anki/physics/PhysicsObject.h

@@ -31,9 +31,24 @@ enum class PhysicsObjectType : U8
 };
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(PhysicsObjectType)
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(PhysicsObjectType)
 
 
+#define ANKI_PHYSICS_OBJECT_FRIENDS \
+	friend class PhysicsWorld; \
+	friend class PhysicsPtrDeleter; \
+	template<typename T, typename TDeleter> \
+	friend class IntrusivePtr;
+
+#define ANKI_PHYSICS_OBJECT(type) \
+	ANKI_PHYSICS_OBJECT_FRIENDS \
+public: \
+	static constexpr PhysicsObjectType CLASS_TYPE = type; \
+\
+private:
+
 /// Base of all physics objects.
 /// Base of all physics objects.
 class PhysicsObject : public IntrusiveListEnabled<PhysicsObject>
 class PhysicsObject : public IntrusiveListEnabled<PhysicsObject>
 {
 {
+	ANKI_PHYSICS_OBJECT_FRIENDS
+
 public:
 public:
 	PhysicsObject(PhysicsObjectType type, PhysicsWorld* world)
 	PhysicsObject(PhysicsObjectType type, PhysicsWorld* world)
 		: m_world(world)
 		: m_world(world)
@@ -44,6 +59,7 @@ public:
 
 
 	virtual ~PhysicsObject()
 	virtual ~PhysicsObject()
 	{
 	{
+		ANKI_ASSERT(!m_registered);
 	}
 	}
 
 
 	PhysicsObjectType getType() const
 	PhysicsObjectType getType() const
@@ -51,6 +67,19 @@ public:
 		return m_type;
 		return m_type;
 	}
 	}
 
 
+	void setUserData(void* ud)
+	{
+		m_userData = ud;
+	}
+
+	void* getUserData() const
+	{
+		return m_userData;
+	}
+
+protected:
+	PhysicsWorld* m_world = nullptr;
+
 	PhysicsWorld& getWorld()
 	PhysicsWorld& getWorld()
 	{
 	{
 		return *m_world;
 		return *m_world;
@@ -68,18 +97,12 @@ public:
 
 
 	HeapAllocator<U8> getAllocator() const;
 	HeapAllocator<U8> getAllocator() const;
 
 
-	void setUserData(void* ud)
-	{
-		m_userData = ud;
-	}
+private:
+	Bool m_registered = false;
 
 
-	void* getUserData() const
-	{
-		return m_userData;
-	}
+	virtual void registerToWorld() = 0;
 
 
-protected:
-	PhysicsWorld* m_world = nullptr;
+	virtual void unregisterFromWorld() = 0;
 
 
 private:
 private:
 	Atomic<I32> m_refcount = {0};
 	Atomic<I32> m_refcount = {0};
@@ -87,10 +110,6 @@ private:
 	void* m_userData = nullptr;
 	void* m_userData = nullptr;
 };
 };
 
 
-#define ANKI_PHYSICS_OBJECT \
-	friend class PhysicsWorld; \
-	friend class PhysicsPtrDeleter;
-
 /// This is a factor that will decide if two filtered objects will be checked for collision.
 /// This is a factor that will decide if two filtered objects will be checked for collision.
 /// @memberof PhysicsFilteredObject
 /// @memberof PhysicsFilteredObject
 class PhysicsBroadPhaseFilterCallback
 class PhysicsBroadPhaseFilterCallback
@@ -99,18 +118,37 @@ public:
 	virtual Bool needsCollision(const PhysicsFilteredObject& a, const PhysicsFilteredObject& b) = 0;
 	virtual Bool needsCollision(const PhysicsFilteredObject& a, const PhysicsFilteredObject& b) = 0;
 };
 };
 
 
+/// A pair of a trigger and a filtered object.
+class PhysicsTriggerFilteredPair
+{
+public:
+	PhysicsFilteredObject* m_filteredObject = nullptr;
+	PhysicsTrigger* m_trigger = nullptr;
+	U64 m_frame = 0;
+
+	Bool isAlive() const
+	{
+		return m_filteredObject && m_trigger;
+	}
+
+	Bool shouldDelete() const
+	{
+		return m_filteredObject == nullptr && m_trigger == nullptr;
+	}
+};
+
 /// A PhysicsObject that takes part into collision detection. Has functionality to filter the broad phase detection.
 /// A PhysicsObject that takes part into collision detection. Has functionality to filter the broad phase detection.
 class PhysicsFilteredObject : public PhysicsObject
 class PhysicsFilteredObject : public PhysicsObject
 {
 {
 public:
 public:
+	ANKI_PHYSICS_OBJECT_FRIENDS
+
 	PhysicsFilteredObject(PhysicsObjectType type, PhysicsWorld* world)
 	PhysicsFilteredObject(PhysicsObjectType type, PhysicsWorld* world)
 		: PhysicsObject(type, world)
 		: PhysicsObject(type, world)
 	{
 	{
 	}
 	}
 
 
-	~PhysicsFilteredObject()
-	{
-	}
+	~PhysicsFilteredObject();
 
 
 	static Bool classof(const PhysicsObject* obj)
 	static Bool classof(const PhysicsObject* obj)
 	{
 	{
@@ -159,6 +197,9 @@ private:
 	PhysicsMaterialBit m_materialMask = PhysicsMaterialBit::ALL;
 	PhysicsMaterialBit m_materialMask = PhysicsMaterialBit::ALL;
 
 
 	PhysicsBroadPhaseFilterCallback* m_filter = nullptr;
 	PhysicsBroadPhaseFilterCallback* m_filter = nullptr;
+
+	static constexpr U32 MAX_TRIGGER_FILTERED_PAIRS = 4;
+	Array<PhysicsTriggerFilteredPair*, MAX_TRIGGER_FILTERED_PAIRS> m_triggerFilteredPairs = {};
 };
 };
 /// @}
 /// @}
 
 

+ 25 - 21
anki/physics/PhysicsPlayerController.cpp

@@ -25,41 +25,45 @@ PhysicsPlayerController::PhysicsPlayerController(PhysicsWorld* world, const Phys
 
 
 	m_controller.init(m_ghostObject.get(), m_convexShape.get(), init.m_stepHeight, btVector3(0, 1, 0));
 	m_controller.init(m_ghostObject.get(), m_convexShape.get(), init.m_stepHeight, btVector3(0, 1, 0));
 
 
-	{
-		auto lock = getWorld().lockBtWorld();
-		btDynamicsWorld* btworld = getWorld().getBtWorld();
-
-		btworld->addCollisionObject(m_ghostObject.get(), btBroadphaseProxy::CharacterFilter,
-									btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter);
-		btworld->addAction(m_controller.get());
-	}
-
 	// Need to call this else the player is upside down
 	// Need to call this else the player is upside down
 	moveToPosition(init.m_position);
 	moveToPosition(init.m_position);
 }
 }
 
 
 PhysicsPlayerController::~PhysicsPlayerController()
 PhysicsPlayerController::~PhysicsPlayerController()
 {
 {
-	{
-		auto lock = getWorld().lockBtWorld();
-		getWorld().getBtWorld()->removeAction(m_controller.get());
-		getWorld().getBtWorld()->removeCollisionObject(m_ghostObject.get());
-	}
-
 	m_controller.destroy();
 	m_controller.destroy();
 	m_ghostObject.destroy();
 	m_ghostObject.destroy();
 	m_convexShape.destroy();
 	m_convexShape.destroy();
 }
 }
 
 
-void PhysicsPlayerController::moveToPosition(const Vec4& position)
+void PhysicsPlayerController::registerToWorld()
+{
+	btDynamicsWorld& btworld = getWorld().getBtWorld();
+	btworld.addCollisionObject(m_ghostObject.get(), btBroadphaseProxy::CharacterFilter,
+							   btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter);
+	btworld.addAction(m_controller.get());
+}
+
+void PhysicsPlayerController::unregisterFromWorld()
+{
+	getWorld().getBtWorld().removeAction(m_controller.get());
+	getWorld().getBtWorld().removeCollisionObject(m_ghostObject.get());
+}
+
+void PhysicsPlayerController::moveToPositionForReal()
 {
 {
-	auto lock = getWorld().lockBtWorld();
+	if(m_moveToPosition.x() == MAX_F32)
+	{
+		return;
+	}
+
+	getWorld().getBtWorld().getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(
+		m_ghostObject->getBroadphaseHandle(), getWorld().getBtWorld().getDispatcher());
 
 
-	getWorld().getBtWorld()->getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(
-		m_ghostObject->getBroadphaseHandle(), getWorld().getBtWorld()->getDispatcher());
+	m_controller->reset(&getWorld().getBtWorld());
+	m_controller->warp(toBt(m_moveToPosition));
 
 
-	m_controller->reset(getWorld().getBtWorld());
-	m_controller->warp(toBt(position.xyz()));
+	m_moveToPosition.x() = MAX_F32;
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 17 - 11
anki/physics/PhysicsPlayerController.h

@@ -23,42 +23,48 @@ public:
 	F32 m_outerRadius = 0.50f;
 	F32 m_outerRadius = 0.50f;
 	F32 m_height = 1.9f;
 	F32 m_height = 1.9f;
 	F32 m_stepHeight = 1.9f * 0.33f;
 	F32 m_stepHeight = 1.9f * 0.33f;
-	Vec4 m_position = Vec4(0.0f);
+	Vec3 m_position = Vec3(0.0f);
 };
 };
 
 
 /// A player controller that walks the world.
 /// A player controller that walks the world.
 class PhysicsPlayerController final : public PhysicsFilteredObject
 class PhysicsPlayerController final : public PhysicsFilteredObject
 {
 {
-	ANKI_PHYSICS_OBJECT
+	ANKI_PHYSICS_OBJECT(PhysicsObjectType::PLAYER_CONTROLLER)
 
 
 public:
 public:
-	static const PhysicsObjectType CLASS_TYPE = PhysicsObjectType::PLAYER_CONTROLLER;
-
 	// Update the state machine
 	// Update the state machine
 	void setVelocity(F32 forwardSpeed, F32 strafeSpeed, F32 jumpSpeed, const Vec4& forwardDir)
 	void setVelocity(F32 forwardSpeed, F32 strafeSpeed, F32 jumpSpeed, const Vec4& forwardDir)
 	{
 	{
 		m_controller->setWalkDirection(toBt((forwardDir * forwardSpeed).xyz()));
 		m_controller->setWalkDirection(toBt((forwardDir * forwardSpeed).xyz()));
 	}
 	}
 
 
-	void moveToPosition(const Vec4& position);
+	/// This is a deferred operation, will happen on the next PhysicsWorld::update.
+	void moveToPosition(const Vec3& position)
+	{
+		m_moveToPosition = position;
+	}
 
 
-	Transform getTransform(Bool& updated)
+	Transform getTransform()
 	{
 	{
-		Transform out = toAnki(m_ghostObject->getWorldTransform());
-		updated = m_prevTrf != out;
-		return out;
+		return toAnki(m_ghostObject->getWorldTransform());
 	}
 	}
 
 
 private:
 private:
 	ClassWrapper<btPairCachingGhostObject> m_ghostObject;
 	ClassWrapper<btPairCachingGhostObject> m_ghostObject;
 	ClassWrapper<btCapsuleShape> m_convexShape;
 	ClassWrapper<btCapsuleShape> m_convexShape;
 	ClassWrapper<btKinematicCharacterController> m_controller;
 	ClassWrapper<btKinematicCharacterController> m_controller;
-
-	Transform m_prevTrf = Transform::getIdentity();
+	Vec3 m_moveToPosition = Vec3(MAX_F32);
 
 
 	PhysicsPlayerController(PhysicsWorld* world, const PhysicsPlayerControllerInitInfo& init);
 	PhysicsPlayerController(PhysicsWorld* world, const PhysicsPlayerControllerInitInfo& init);
 
 
 	~PhysicsPlayerController();
 	~PhysicsPlayerController();
+
+	void registerToWorld() override;
+
+	void unregisterFromWorld() override;
+
+	/// Called in PhysicsWorld::update.
+	void moveToPositionForReal();
 };
 };
 /// @}
 /// @}
 
 

+ 84 - 18
anki/physics/PhysicsTrigger.cpp

@@ -20,49 +20,115 @@ PhysicsTrigger::PhysicsTrigger(PhysicsWorld* world, PhysicsCollisionShapePtr sha
 	m_ghostShape->setWorldTransform(btTransform::getIdentity());
 	m_ghostShape->setWorldTransform(btTransform::getIdentity());
 	m_ghostShape->setCollisionShape(shape->getBtShape(true));
 	m_ghostShape->setCollisionShape(shape->getBtShape(true));
 
 
+	// If you don't have that bodies will bounce on the trigger
+	m_ghostShape->setCollisionFlags(btCollisionObject::CF_NO_CONTACT_RESPONSE);
+
 	m_ghostShape->setUserPointer(static_cast<PhysicsObject*>(this));
 	m_ghostShape->setUserPointer(static_cast<PhysicsObject*>(this));
 
 
 	setMaterialGroup(PhysicsMaterialBit::TRIGGER);
 	setMaterialGroup(PhysicsMaterialBit::TRIGGER);
-	setMaterialMask(PhysicsMaterialBit::ALL);
-
-	auto lock = getWorld().lockBtWorld();
-	getWorld().getBtWorld()->addCollisionObject(m_ghostShape.get());
+	setMaterialMask(PhysicsMaterialBit::ALL ^ PhysicsMaterialBit::STATIC_GEOMETRY);
 }
 }
 
 
 PhysicsTrigger::~PhysicsTrigger()
 PhysicsTrigger::~PhysicsTrigger()
 {
 {
+	for(PhysicsTriggerFilteredPair* pair : m_pairs)
 	{
 	{
-		auto lock = getWorld().lockBtWorld();
-		getWorld().getBtWorld()->removeCollisionObject(m_ghostShape.get());
+		ANKI_ASSERT(pair);
+
+		pair->m_trigger = nullptr;
+
+		if(pair->shouldDelete())
+		{
+			getAllocator().deleteInstance(pair);
+		}
 	}
 	}
 
 
+	m_pairs.destroy(getAllocator());
+
 	m_ghostShape.destroy();
 	m_ghostShape.destroy();
 }
 }
 
 
+void PhysicsTrigger::registerToWorld()
+{
+	getWorld().getBtWorld().addCollisionObject(m_ghostShape.get());
+}
+
+void PhysicsTrigger::unregisterFromWorld()
+{
+	getWorld().getBtWorld().removeCollisionObject(m_ghostShape.get());
+}
+
 void PhysicsTrigger::processContacts()
 void PhysicsTrigger::processContacts()
 {
 {
+	++m_processContactsFrame;
+
 	if(m_contactCallback == nullptr)
 	if(m_contactCallback == nullptr)
 	{
 	{
+		m_pairs.destroy(getAllocator());
 		return;
 		return;
 	}
 	}
 
 
-	if(m_ghostShape->getOverlappingPairs().size() < 0)
+	// Gather the new pairs
+	DynamicArrayAuto<PhysicsTriggerFilteredPair*> newPairs(getWorld().getTempAllocator());
+	newPairs.resizeStorage(m_ghostShape->getOverlappingPairs().size());
+	for(U32 i = 0; i < U32(m_ghostShape->getOverlappingPairs().size()); ++i)
 	{
 	{
-		return;
+		btCollisionObject* bobj = m_ghostShape->getOverlappingPairs()[i];
+		ANKI_ASSERT(bobj);
+		PhysicsObject* aobj = static_cast<PhysicsObject*>(bobj->getUserPointer());
+		ANKI_ASSERT(aobj);
+		PhysicsFilteredObject* obj = dcast<PhysicsFilteredObject*>(aobj);
+
+		Bool isNew;
+		PhysicsTriggerFilteredPair* pair = getWorld().getOrCreatePhysicsTriggerFilteredPair(this, obj, isNew);
+		if(pair)
+		{
+			ANKI_ASSERT(pair->isAlive());
+			newPairs.emplaceBack(pair);
+
+			if(isNew)
+			{
+				m_contactCallback->onTriggerEnter(*this, *obj);
+			}
+			else
+			{
+				m_contactCallback->onTriggerInside(*this, *obj);
+			}
+
+			pair->m_frame = m_processContactsFrame;
+		}
 	}
 	}
 
 
-	// Process contacts
-	for(U32 i = 0; i < U32(m_ghostShape->getOverlappingPairs().size()); ++i)
+	// Remove stale pairs
+	for(U32 i = 0; i < m_pairs.getSize(); ++i)
 	{
 	{
-		btCollisionObject* obj = m_ghostShape->getOverlappingPairs()[i];
-
-		ANKI_ASSERT(obj);
-
-		PhysicsObject* aobj = static_cast<PhysicsObject*>(obj->getUserPointer());
-		ANKI_ASSERT(aobj);
+		PhysicsTriggerFilteredPair* pair = m_pairs[i];
+		ANKI_ASSERT(pair->m_trigger == this);
+
+		if(pair->m_filteredObject == nullptr)
+		{
+			// Filtered object died while inside the tigger, destroy the pair
+			getAllocator().deleteInstance(pair);
+		}
+		else if(pair->m_frame == m_processContactsFrame)
+		{
+			// Was updated this frame so don't touch it
+		}
+		else
+		{
+			// Was updated in some previous frame, notify and brake the link to the pair
+			ANKI_ASSERT(pair->isAlive());
+			ANKI_ASSERT(pair->m_frame < m_processContactsFrame);
+			m_contactCallback->onTriggerExit(*this, *pair->m_filteredObject);
+			pair->m_trigger = nullptr;
+		}
+	}
 
 
-		PhysicsFilteredObject* fobj = dcast<PhysicsFilteredObject*>(aobj);
-		m_contactCallback->processContact(*this, *fobj);
+	// Store the new contacts
+	m_pairs.resize(getAllocator(), newPairs.getSize());
+	if(m_pairs.getSize())
+	{
+		memcpy(&m_pairs[0], &newPairs[0], m_pairs.getSizeInBytes());
 	}
 	}
 }
 }
 
 

+ 26 - 5
anki/physics/PhysicsTrigger.h

@@ -8,6 +8,7 @@
 #include <anki/physics/PhysicsObject.h>
 #include <anki/physics/PhysicsObject.h>
 #include <anki/util/WeakArray.h>
 #include <anki/util/WeakArray.h>
 #include <anki/util/ClassWrapper.h>
 #include <anki/util/ClassWrapper.h>
+#include <anki/util/HashMap.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -20,20 +21,32 @@ namespace anki
 class PhysicsTriggerProcessContactCallback
 class PhysicsTriggerProcessContactCallback
 {
 {
 public:
 public:
-	virtual ~PhysicsTriggerProcessContactCallback()
+	/// Will be called whenever a contact first touches a trigger.
+	virtual void onTriggerEnter(PhysicsTrigger& trigger, PhysicsFilteredObject& obj)
 	{
 	{
 	}
 	}
 
 
-	virtual void processContact(PhysicsTrigger& trigger, PhysicsFilteredObject& obj) = 0;
+	/// Will be called whenever a contact touches a trigger.
+	virtual void onTriggerInside(PhysicsTrigger& trigger, PhysicsFilteredObject& obj)
+	{
+	}
+
+	/// Will be called whenever a contact stops touching a trigger.
+	virtual void onTriggerExit(PhysicsTrigger& trigger, PhysicsFilteredObject& obj)
+	{
+	}
 };
 };
 
 
 /// A trigger that uses a PhysicsShape and its purpose is to collect collision events.
 /// A trigger that uses a PhysicsShape and its purpose is to collect collision events.
 class PhysicsTrigger : public PhysicsFilteredObject
 class PhysicsTrigger : public PhysicsFilteredObject
 {
 {
-	ANKI_PHYSICS_OBJECT
+	ANKI_PHYSICS_OBJECT(PhysicsObjectType::TRIGGER)
 
 
 public:
 public:
-	static const PhysicsObjectType CLASS_TYPE = PhysicsObjectType::TRIGGER;
+	Transform getTransform() const
+	{
+		return toAnki(m_ghostShape->getWorldTransform());
+	}
 
 
 	void setTransform(const Transform& trf)
 	void setTransform(const Transform& trf)
 	{
 	{
@@ -49,14 +62,22 @@ private:
 	PhysicsCollisionShapePtr m_shape;
 	PhysicsCollisionShapePtr m_shape;
 	ClassWrapper<btGhostObject> m_ghostShape;
 	ClassWrapper<btGhostObject> m_ghostShape;
 
 
+	DynamicArray<PhysicsTriggerFilteredPair*> m_pairs;
+
 	PhysicsTriggerProcessContactCallback* m_contactCallback = nullptr;
 	PhysicsTriggerProcessContactCallback* m_contactCallback = nullptr;
 
 
+	U64 m_processContactsFrame = 0;
+
 	PhysicsTrigger(PhysicsWorld* world, PhysicsCollisionShapePtr shape);
 	PhysicsTrigger(PhysicsWorld* world, PhysicsCollisionShapePtr shape);
 
 
 	~PhysicsTrigger();
 	~PhysicsTrigger();
 
 
+	void registerToWorld() override;
+
+	void unregisterFromWorld() override;
+
 	void processContacts();
 	void processContacts();
 };
 };
 /// @}
 /// @}
 
 
-} // end namespace anki
+} // end namespace anki

+ 133 - 30
anki/physics/PhysicsWorld.cpp

@@ -7,6 +7,7 @@
 #include <anki/physics/PhysicsCollisionShape.h>
 #include <anki/physics/PhysicsCollisionShape.h>
 #include <anki/physics/PhysicsBody.h>
 #include <anki/physics/PhysicsBody.h>
 #include <anki/physics/PhysicsTrigger.h>
 #include <anki/physics/PhysicsTrigger.h>
+#include <anki/physics/PhysicsPlayerController.h>
 #include <anki/util/Rtti.h>
 #include <anki/util/Rtti.h>
 #include <BulletCollision/Gimpact/btGImpactCollisionAlgorithm.h>
 #include <BulletCollision/Gimpact/btGImpactCollisionAlgorithm.h>
 
 
@@ -14,18 +15,18 @@ namespace anki
 {
 {
 
 
 // Ugly but there is no other way
 // Ugly but there is no other way
-static HeapAllocator<U8>* gAlloc = nullptr;
+static HeapAllocator<U8>* g_alloc = nullptr;
 
 
 static void* btAlloc(size_t size)
 static void* btAlloc(size_t size)
 {
 {
-	ANKI_ASSERT(gAlloc);
-	return gAlloc->getMemoryPool().allocate(size, 16);
+	ANKI_ASSERT(g_alloc);
+	return g_alloc->getMemoryPool().allocate(size, 16);
 }
 }
 
 
 static void btFree(void* ptr)
 static void btFree(void* ptr)
 {
 {
-	ANKI_ASSERT(gAlloc);
-	gAlloc->getMemoryPool().free(ptr);
+	ANKI_ASSERT(g_alloc);
+	g_alloc->getMemoryPool().free(ptr);
 }
 }
 
 
 /// Broad phase collision callback.
 /// Broad phase collision callback.
@@ -145,12 +146,9 @@ PhysicsWorld::PhysicsWorld()
 
 
 PhysicsWorld::~PhysicsWorld()
 PhysicsWorld::~PhysicsWorld()
 {
 {
-#if ANKI_ENABLE_ASSERTS
-	for(PhysicsObjectType type = PhysicsObjectType::FIRST; type < PhysicsObjectType::COUNT; ++type)
-	{
-		ANKI_ASSERT(m_objectLists[type].isEmpty() && "Someone is holding refs to some physics objects");
-	}
-#endif
+	destroyMarkedForDeletion();
+
+	ANKI_ASSERT(m_objectsCreatedCount.load() == 0 && "Forgot to delete some objects");
 
 
 	m_world.destroy();
 	m_world.destroy();
 	m_solver.destroy();
 	m_solver.destroy();
@@ -160,16 +158,16 @@ PhysicsWorld::~PhysicsWorld()
 	m_gpc.destroy();
 	m_gpc.destroy();
 	m_alloc.deleteInstance(m_filterCallback);
 	m_alloc.deleteInstance(m_filterCallback);
 
 
-	gAlloc = nullptr;
+	g_alloc = nullptr;
 }
 }
 
 
-Error PhysicsWorld::create(AllocAlignedCallback allocCb, void* allocCbData)
+Error PhysicsWorld::init(AllocAlignedCallback allocCb, void* allocCbData)
 {
 {
 	m_alloc = HeapAllocator<U8>(allocCb, allocCbData);
 	m_alloc = HeapAllocator<U8>(allocCb, allocCbData);
 	m_tmpAlloc = StackAllocator<U8>(allocCb, allocCbData, 1_KB, 2.0f);
 	m_tmpAlloc = StackAllocator<U8>(allocCb, allocCbData, 1_KB, 2.0f);
 
 
 	// Set allocators
 	// Set allocators
-	gAlloc = &m_alloc;
+	g_alloc = &m_alloc;
 	btAlignedAllocSetCustom(btAlloc, btFree);
 	btAlignedAllocSetCustom(btAlloc, btFree);
 
 
 	// Create objects
 	// Create objects
@@ -192,24 +190,75 @@ Error PhysicsWorld::create(AllocAlignedCallback allocCb, void* allocCbData)
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
-Error PhysicsWorld::update(Second dt)
+void PhysicsWorld::destroyMarkedForDeletion()
 {
 {
-	// Update world
+	while(true)
 	{
 	{
-		auto lock = lockBtWorld();
-		m_world->stepSimulation(F32(dt), 1, 1.0f / 60.0f);
+		PhysicsObject* obj = nullptr;
+
+		// Don't delete the instance (call the destructor) while holding the lock to avoid deadlocks
+		{
+			LockGuard<Mutex> lock(m_markedMtx);
+			if(!m_markedForDeletion.isEmpty())
+			{
+				obj = m_markedForDeletion.popFront();
+				if(obj->m_registered)
+				{
+					obj->unregisterFromWorld();
+					obj->m_registered = false;
+				}
+			}
+		}
+
+		if(obj == nullptr)
+		{
+			break;
+		}
+
+		m_alloc.deleteInstance(obj);
+#if ANKI_ENABLE_ASSERTS
+		const I32 count = m_objectsCreatedCount.fetchSub(1) - 1;
+		ANKI_ASSERT(count >= 0);
+#endif
 	}
 	}
+}
 
 
-	// Process trigger contacts
+Error PhysicsWorld::update(Second dt)
+{
+	// First destroy
+	destroyMarkedForDeletion();
+
+	// Create new objects
 	{
 	{
-		LockGuard<Mutex> lock(m_objectListsMtx);
+		LockGuard<Mutex> lock(m_markedMtx);
 
 
-		for(PhysicsObject& trigger : m_objectLists[PhysicsObjectType::TRIGGER])
+		// Create
+		while(!m_markedForCreation.isEmpty())
 		{
 		{
-			static_cast<PhysicsTrigger&>(trigger).processContacts();
+			PhysicsObject* obj = m_markedForCreation.popFront();
+			ANKI_ASSERT(!obj->m_registered);
+			obj->registerToWorld();
+			obj->m_registered = true;
+			m_objectLists[obj->getType()].pushBack(obj);
 		}
 		}
 	}
 	}
 
 
+	// Update the player controllers
+	for(PhysicsObject& obj : m_objectLists[PhysicsObjectType::PLAYER_CONTROLLER])
+	{
+		PhysicsPlayerController& playerController = static_cast<PhysicsPlayerController&>(obj);
+		playerController.moveToPositionForReal();
+	}
+
+	// Update world
+	m_world->stepSimulation(F32(dt), 1, 1.0f / 60.0f);
+
+	// Process trigger contacts
+	for(PhysicsObject& trigger : m_objectLists[PhysicsObjectType::TRIGGER])
+	{
+		static_cast<PhysicsTrigger&>(trigger).processContacts();
+	}
+
 	// Reset the pool
 	// Reset the pool
 	m_tmpAlloc.getMemoryPool().reset();
 	m_tmpAlloc.getMemoryPool().reset();
 
 
@@ -219,20 +268,20 @@ Error PhysicsWorld::update(Second dt)
 void PhysicsWorld::destroyObject(PhysicsObject* obj)
 void PhysicsWorld::destroyObject(PhysicsObject* obj)
 {
 {
 	ANKI_ASSERT(obj);
 	ANKI_ASSERT(obj);
-
+	LockGuard<Mutex> lock(m_markedMtx);
+	if(obj->m_registered)
 	{
 	{
-		LockGuard<Mutex> lock(m_objectListsMtx);
 		m_objectLists[obj->getType()].erase(obj);
 		m_objectLists[obj->getType()].erase(obj);
 	}
 	}
-
-	obj->~PhysicsObject();
-	m_alloc.getMemoryPool().free(obj);
+	else
+	{
+		m_markedForCreation.erase(obj);
+	}
+	m_markedForDeletion.pushBack(obj);
 }
 }
 
 
-void PhysicsWorld::rayCast(WeakArray<PhysicsWorldRayCastCallback*> rayCasts)
+void PhysicsWorld::rayCast(WeakArray<PhysicsWorldRayCastCallback*> rayCasts) const
 {
 {
-	auto lock = lockBtWorld();
-
 	MyRaycastCallback callback;
 	MyRaycastCallback callback;
 	for(PhysicsWorldRayCastCallback* cb : rayCasts)
 	for(PhysicsWorldRayCastCallback* cb : rayCasts)
 	{
 	{
@@ -241,4 +290,58 @@ void PhysicsWorld::rayCast(WeakArray<PhysicsWorldRayCastCallback*> rayCasts)
 	}
 	}
 }
 }
 
 
+PhysicsTriggerFilteredPair* PhysicsWorld::getOrCreatePhysicsTriggerFilteredPair(PhysicsTrigger* trigger,
+																				PhysicsFilteredObject* filtered,
+																				Bool& isNew)
+{
+	ANKI_ASSERT(trigger && filtered);
+
+	U32 emptySlot = MAX_U32;
+	for(U32 i = 0; i < filtered->m_triggerFilteredPairs.getSize(); ++i)
+	{
+		PhysicsTriggerFilteredPair* pair = filtered->m_triggerFilteredPairs[i];
+
+		if(pair && pair->m_trigger == trigger)
+		{
+			// Found it
+			ANKI_ASSERT(pair->m_filteredObject == filtered);
+			isNew = false;
+			return pair;
+		}
+		else if(pair == nullptr)
+		{
+			// Empty slot, save it for later
+			emptySlot = i;
+		}
+		else if(pair && pair->m_trigger == nullptr)
+		{
+			// Pair exists but it's invalid, repurpose it
+			ANKI_ASSERT(pair->m_filteredObject == filtered);
+			emptySlot = i;
+		}
+	}
+
+	if(emptySlot == MAX_U32)
+	{
+		ANKI_PHYS_LOGW("Contact ignored. Too many active contacts for the filtered object");
+		return nullptr;
+	}
+
+	// Not found, create a new one
+	isNew = true;
+
+	PhysicsTriggerFilteredPair* newPair;
+	if(filtered->m_triggerFilteredPairs[emptySlot] == nullptr)
+	{
+		filtered->m_triggerFilteredPairs[emptySlot] = m_alloc.newInstance<PhysicsTriggerFilteredPair>();
+	}
+	newPair = filtered->m_triggerFilteredPairs[emptySlot];
+
+	newPair->m_filteredObject = filtered;
+	newPair->m_trigger = trigger;
+	newPair->m_frame = 0;
+
+	return newPair;
+}
+
 } // end namespace anki
 } // end namespace anki

+ 38 - 19
anki/physics/PhysicsWorld.h

@@ -44,17 +44,21 @@ public:
 	PhysicsWorld();
 	PhysicsWorld();
 	~PhysicsWorld();
 	~PhysicsWorld();
 
 
-	ANKI_USE_RESULT Error create(AllocAlignedCallback allocCb, void* allocCbData);
+	ANKI_USE_RESULT Error init(AllocAlignedCallback allocCb, void* allocCbData);
 
 
 	template<typename T, typename... TArgs>
 	template<typename T, typename... TArgs>
 	PhysicsPtr<T> newInstance(TArgs&&... args)
 	PhysicsPtr<T> newInstance(TArgs&&... args)
 	{
 	{
 		T* obj = static_cast<T*>(m_alloc.getMemoryPool().allocate(sizeof(T), alignof(T)));
 		T* obj = static_cast<T*>(m_alloc.getMemoryPool().allocate(sizeof(T), alignof(T)));
 		::new(obj) T(this, std::forward<TArgs>(args)...);
 		::new(obj) T(this, std::forward<TArgs>(args)...);
-
-		LockGuard<Mutex> lock(m_objectListsMtx);
-		m_objectLists[obj->getType()].pushBack(obj);
-
+		{
+			LockGuard<Mutex> lock(m_markedMtx);
+			m_markedForCreation.pushBack(obj);
+		}
+#if ANKI_ENABLE_ASSERTS
+		const U32 count = m_objectsCreatedCount.fetchAdd(1) + 1;
+		ANKI_ASSERT(count > 0);
+#endif
 		return PhysicsPtr<T>(obj);
 		return PhysicsPtr<T>(obj);
 	}
 	}
 
 
@@ -71,37 +75,45 @@ public:
 		return m_alloc;
 		return m_alloc;
 	}
 	}
 
 
-	void rayCast(WeakArray<PhysicsWorldRayCastCallback*> rayCasts);
+	StackAllocator<U8> getTempAllocator() const
+	{
+		return m_tmpAlloc;
+	}
+
+	StackAllocator<U8>& getTempAllocator()
+	{
+		return m_tmpAlloc;
+	}
+
+	void rayCast(WeakArray<PhysicsWorldRayCastCallback*> rayCasts) const;
 
 
-	void rayCast(PhysicsWorldRayCastCallback& raycast)
+	void rayCast(PhysicsWorldRayCastCallback& raycast) const
 	{
 	{
 		PhysicsWorldRayCastCallback* ptr = &raycast;
 		PhysicsWorldRayCastCallback* ptr = &raycast;
 		WeakArray<PhysicsWorldRayCastCallback*> arr(&ptr, 1);
 		WeakArray<PhysicsWorldRayCastCallback*> arr(&ptr, 1);
 		rayCast(arr);
 		rayCast(arr);
 	}
 	}
 
 
-	ANKI_INTERNAL btDynamicsWorld* getBtWorld()
+	ANKI_INTERNAL btDynamicsWorld& getBtWorld()
 	{
 	{
-		return m_world.get();
+		return *m_world;
 	}
 	}
 
 
-	ANKI_INTERNAL const btDynamicsWorld* getBtWorld() const
+	ANKI_INTERNAL const btDynamicsWorld& getBtWorld() const
 	{
 	{
-		return m_world.get();
+		return *m_world;
 	}
 	}
 
 
-	ANKI_INTERNAL F32 getCollisionMargin() const
+	ANKI_INTERNAL constexpr F32 getCollisionMargin() const
 	{
 	{
 		return 0.04f;
 		return 0.04f;
 	}
 	}
 
 
-	ANKI_INTERNAL ANKI_USE_RESULT LockGuard<Mutex> lockBtWorld() const
-	{
-		return LockGuard<Mutex>(m_btWorldMtx);
-	}
-
 	ANKI_INTERNAL void destroyObject(PhysicsObject* obj);
 	ANKI_INTERNAL void destroyObject(PhysicsObject* obj);
 
 
+	ANKI_INTERNAL PhysicsTriggerFilteredPair*
+	getOrCreatePhysicsTriggerFilteredPair(PhysicsTrigger* trigger, PhysicsFilteredObject* filtered, Bool& isNew);
+
 private:
 private:
 	class MyOverlapFilterCallback;
 	class MyOverlapFilterCallback;
 	class MyRaycastCallback;
 	class MyRaycastCallback;
@@ -117,10 +129,17 @@ private:
 	ClassWrapper<btCollisionDispatcher> m_dispatcher;
 	ClassWrapper<btCollisionDispatcher> m_dispatcher;
 	ClassWrapper<btSequentialImpulseConstraintSolver> m_solver;
 	ClassWrapper<btSequentialImpulseConstraintSolver> m_solver;
 	ClassWrapper<btDiscreteDynamicsWorld> m_world;
 	ClassWrapper<btDiscreteDynamicsWorld> m_world;
-	mutable Mutex m_btWorldMtx;
 
 
 	Array<IntrusiveList<PhysicsObject>, U(PhysicsObjectType::COUNT)> m_objectLists;
 	Array<IntrusiveList<PhysicsObject>, U(PhysicsObjectType::COUNT)> m_objectLists;
-	mutable Mutex m_objectListsMtx;
+	IntrusiveList<PhysicsObject> m_markedForCreation;
+	IntrusiveList<PhysicsObject> m_markedForDeletion;
+	Mutex m_markedMtx; ///< Locks the above
+
+#if ANKI_ENABLE_ASSERTS
+	Atomic<I32> m_objectsCreatedCount = {0};
+#endif
+
+	void destroyMarkedForDeletion();
 };
 };
 /// @}
 /// @}
 
 

+ 10 - 0
anki/renderer/Dbg.cpp

@@ -97,6 +97,16 @@ void Dbg::run(RenderPassWorkContext& rgraphCtx, const RenderingContext& ctx)
 		el.m_callback(dctx, a);
 		el.m_callback(dctx, a);
 	}
 	}
 
 
+	// Draw forward shaded
+	if(threadId == 0)
+	{
+		for(const RenderableQueueElement& el : ctx.m_renderQueue->m_forwardShadingRenderables)
+		{
+			Array<void*, 1> a = {const_cast<void*>(el.m_userData)};
+			el.m_callback(dctx, a);
+		}
+	}
+
 	// Draw probes
 	// Draw probes
 	if(threadId == 0)
 	if(threadId == 0)
 	{
 	{

+ 4 - 0
anki/renderer/GBuffer.cpp

@@ -67,6 +67,10 @@ Error GBuffer::initInternal(const ConfigSet& initializer)
 		m_fbDescr.m_colorAttachments[i].m_loadOperation = loadop;
 		m_fbDescr.m_colorAttachments[i].m_loadOperation = loadop;
 		m_fbDescr.m_colorAttachments[i].m_clearValue.m_colorf = {1.0f, 0.0f, 1.0f, 0.0f};
 		m_fbDescr.m_colorAttachments[i].m_clearValue.m_colorf = {1.0f, 0.0f, 1.0f, 0.0f};
 	}
 	}
+
+	m_fbDescr.m_colorAttachments[3].m_loadOperation = AttachmentLoadOperation::CLEAR;
+	m_fbDescr.m_colorAttachments[3].m_clearValue.m_colorf = {-1.0f, -1.0f, 0.0f, 0.0f}; // TODO This clear value is slow
+
 	m_fbDescr.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::CLEAR;
 	m_fbDescr.m_depthStencilAttachment.m_loadOperation = AttachmentLoadOperation::CLEAR;
 	m_fbDescr.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth = 1.0f;
 	m_fbDescr.m_depthStencilAttachment.m_clearValue.m_depthStencil.m_depth = 1.0f;
 	m_fbDescr.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;
 	m_fbDescr.m_depthStencilAttachment.m_aspect = DepthStencilAspectBit::DEPTH;

+ 1 - 1
anki/renderer/TraditionalDeferredShading.cpp

@@ -60,7 +60,7 @@ void TraditionalDeferredLightShading::bindVertexIndexBuffers(MeshResourcePtr& me
 	// Attrib
 	// Attrib
 	U32 bufferBinding;
 	U32 bufferBinding;
 	Format fmt;
 	Format fmt;
-	PtrSize relativeOffset;
+	U32 relativeOffset;
 	mesh->getVertexAttributeInfo(VertexAttributeLocation::POSITION, bufferBinding, fmt, relativeOffset);
 	mesh->getVertexAttributeInfo(VertexAttributeLocation::POSITION, bufferBinding, fmt, relativeOffset);
 
 
 	cmdb->setVertexAttribute(0, 0, fmt, relativeOffset);
 	cmdb->setVertexAttribute(0, 0, fmt, relativeOffset);

+ 0 - 70
anki/resource/CollisionResource.cpp

@@ -1,70 +0,0 @@
-// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <anki/resource/CollisionResource.h>
-#include <anki/resource/ResourceManager.h>
-#include <anki/resource/MeshLoader.h>
-#include <anki/physics/PhysicsWorld.h>
-#include <anki/util/Xml.h>
-
-namespace anki
-{
-
-Error CollisionResource::load(const ResourceFilename& filename, Bool async)
-{
-	XmlElement el;
-	XmlDocument doc;
-	ANKI_CHECK(openFileParseXml(filename, doc));
-
-	XmlElement collEl;
-	ANKI_CHECK(doc.getChildElement("collisionShape", collEl));
-
-	ANKI_CHECK(collEl.getChildElement("type", el));
-	CString type;
-	ANKI_CHECK(el.getText(type));
-
-	XmlElement valEl;
-	ANKI_CHECK(collEl.getChildElement("value", valEl));
-
-	PhysicsWorld& physics = getManager().getPhysicsWorld();
-
-	if(type == "sphere")
-	{
-		F32 tmp;
-		ANKI_CHECK(valEl.getNumber(tmp));
-		m_physicsShape = physics.newInstance<PhysicsSphere>(tmp);
-	}
-	else if(type == "box")
-	{
-		Vec3 extend;
-		ANKI_CHECK(valEl.getNumbers(extend));
-		m_physicsShape = physics.newInstance<PhysicsBox>(extend);
-	}
-	else if(type == "staticMesh")
-	{
-		CString meshfname;
-		ANKI_CHECK(valEl.getText(meshfname));
-
-		MeshLoader loader(&getManager(), getTempAllocator());
-		ANKI_CHECK(loader.load(meshfname));
-
-		DynamicArrayAuto<U32> indices(getTempAllocator());
-		DynamicArrayAuto<Vec3> positions(getTempAllocator());
-		ANKI_CHECK(loader.storeIndicesAndPosition(indices, positions));
-
-		const Bool convex = !!(loader.getHeader().m_flags & MeshBinaryFile::Flag::CONVEX);
-
-		m_physicsShape = physics.newInstance<PhysicsTriangleSoup>(positions, indices, convex);
-	}
-	else
-	{
-		ANKI_RESOURCE_LOGE("Incorrect collision type");
-		return Error::USER_DATA;
-	}
-
-	return Error::NONE;
-}
-
-} // end namespace anki

+ 0 - 50
anki/resource/CollisionResource.h

@@ -1,50 +0,0 @@
-// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <anki/resource/ResourceObject.h>
-#include <anki/physics/PhysicsCollisionShape.h>
-
-namespace anki
-{
-
-/// @addtogroup resource
-/// @{
-
-/// Load a collision shape.
-///
-/// XML file format:
-/// @code
-/// <collisionShape>
-/// 	<type>sphere | box | mesh</type>
-/// 	<value>radius | extend | path/to/mesh</value>
-/// </collisionShape>
-/// @endcode
-class CollisionResource : public ResourceObject
-{
-public:
-	CollisionResource(ResourceManager* manager)
-		: ResourceObject(manager)
-	{
-	}
-
-	~CollisionResource()
-	{
-	}
-
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
-
-	PhysicsCollisionShapePtr getShape() const
-	{
-		return m_physicsShape;
-	}
-
-private:
-	PhysicsCollisionShapePtr m_physicsShape;
-};
-/// @}
-
-} // end namespace anki

+ 0 - 1
anki/resource/Common.h

@@ -34,7 +34,6 @@ class TransferGpuAllocatorHandle;
 /// @{
 /// @{
 constexpr U32 MAX_LOD_COUNT = 3;
 constexpr U32 MAX_LOD_COUNT = 3;
 constexpr U32 MAX_INSTANCES = 64; ///< @warning Change MAX_INSTANCE_GROUPS as well
 constexpr U32 MAX_INSTANCES = 64; ///< @warning Change MAX_INSTANCE_GROUPS as well
-constexpr U32 MAX_SUB_DRAWCALLS = 64;
 constexpr U32 MAX_INSTANCE_GROUPS = 7; ///< It's log2(MAX_INSTANCES) + 1
 constexpr U32 MAX_INSTANCE_GROUPS = 7; ///< It's log2(MAX_INSTANCES) + 1
 
 
 /// Standard attribute locations. Should be the same as in Common.glsl.
 /// Standard attribute locations. Should be the same as in Common.glsl.

+ 46 - 0
anki/resource/CpuMeshResource.cpp

@@ -0,0 +1,46 @@
+// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/resource/CpuMeshResource.h>
+#include <anki/resource/MeshBinaryLoader.h>
+#include <anki/resource/ResourceManager.h>
+#include <anki/physics/PhysicsWorld.h>
+
+namespace anki
+{
+
+CpuMeshResource::CpuMeshResource(ResourceManager* manager)
+	: ResourceObject(manager)
+{
+}
+
+CpuMeshResource::~CpuMeshResource()
+{
+	m_indices.destroy(getAllocator());
+	m_positions.destroy(getAllocator());
+}
+
+Error CpuMeshResource::load(const ResourceFilename& filename, Bool async)
+{
+	MeshBinaryLoader loader(&getManager());
+
+	ANKI_CHECK(loader.load(filename));
+
+	DynamicArrayAuto<Vec3> tempPositions(getAllocator());
+	DynamicArrayAuto<U32> tempIndices(getAllocator());
+
+	ANKI_CHECK(loader.storeIndicesAndPosition(tempIndices, tempPositions));
+
+	m_indices = std::move(tempIndices);
+	m_positions = std::move(tempPositions);
+
+	// Create the collision shape
+	const Bool convex = !!(loader.getHeader().m_flags & MeshBinaryFlag::CONVEX);
+	m_physicsShape = getManager().getPhysicsWorld().newInstance<PhysicsTriangleSoup>(m_positions, m_indices, convex);
+
+	return Error::NONE;
+}
+
+} // end namespace anki

+ 53 - 0
anki/resource/CpuMeshResource.h

@@ -0,0 +1,53 @@
+// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/resource/ResourceObject.h>
+#include <anki/Math.h>
+#include <anki/util/WeakArray.h>
+#include <anki/physics/PhysicsCollisionShape.h>
+
+namespace anki
+{
+
+/// @addtogroup resource
+/// @{
+
+/// CPU Mesh Resource. It contains the geometry packed in CPU buffers.
+class CpuMeshResource : public ResourceObject
+{
+public:
+	/// Default constructor
+	CpuMeshResource(ResourceManager* manager);
+
+	~CpuMeshResource();
+
+	/// Load from a mesh file
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
+
+	ConstWeakArray<Vec3> getPositions() const
+	{
+		return m_positions;
+	}
+
+	ConstWeakArray<U32> getIndices() const
+	{
+		return m_indices;
+	}
+
+	const PhysicsCollisionShapePtr& getCollisionShape() const
+	{
+		return m_physicsShape;
+	}
+
+private:
+	DynamicArray<Vec3> m_positions;
+	DynamicArray<U32> m_indices;
+	PhysicsCollisionShapePtr m_physicsShape;
+};
+/// @}
+
+} // namespace anki

+ 2 - 2
anki/resource/InstantiationMacros.h

@@ -11,6 +11,8 @@ ANKI_INSTANTIATE_RESOURCE(TextureResource, TextureResourcePtr)
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANTIATE_RESOURCE(MeshResource, MeshResourcePtr)
 ANKI_INSTANTIATE_RESOURCE(MeshResource, MeshResourcePtr)
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+ANKI_INSTANTIATE_RESOURCE(CpuMeshResource, CpuMeshResourcePtr)
+ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANTIATE_RESOURCE(SkeletonResource, SkeletonResourcePtr)
 ANKI_INSTANTIATE_RESOURCE(SkeletonResource, SkeletonResourcePtr)
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANTIATE_RESOURCE(ParticleEmitterResource, ParticleEmitterResourcePtr)
 ANKI_INSTANTIATE_RESOURCE(ParticleEmitterResource, ParticleEmitterResourcePtr)
@@ -21,8 +23,6 @@ ANKI_INSTANTIATE_RESOURCE(ScriptResource, ScriptResourcePtr)
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANTIATE_RESOURCE(DummyResource, DummyResourcePtr)
 ANKI_INSTANTIATE_RESOURCE(DummyResource, DummyResourcePtr)
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-ANKI_INSTANTIATE_RESOURCE(CollisionResource, CollisionResourcePtr)
-ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANTIATE_RESOURCE(GenericResource, GenericResourcePtr)
 ANKI_INSTANTIATE_RESOURCE(GenericResource, GenericResourcePtr)
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANSIATE_RESOURCE_DELIMITER()
 ANKI_INSTANTIATE_RESOURCE(TextureAtlasResource, TextureAtlasResourcePtr)
 ANKI_INSTANTIATE_RESOURCE(TextureAtlasResource, TextureAtlasResourcePtr)

+ 9 - 0
anki/resource/MaterialResource.h

@@ -326,14 +326,23 @@ public:
 
 
 	U32 getBoneTransformsBinding() const
 	U32 getBoneTransformsBinding() const
 	{
 	{
+		ANKI_ASSERT(supportsSkinning());
 		return m_boneTrfsBinding;
 		return m_boneTrfsBinding;
 	}
 	}
 
 
 	U32 getPrevFrameBoneTransformsBinding() const
 	U32 getPrevFrameBoneTransformsBinding() const
 	{
 	{
+		ANKI_ASSERT(supportsSkinning());
 		return m_prevFrameBoneTrfsBinding;
 		return m_prevFrameBoneTrfsBinding;
 	}
 	}
 
 
+	Bool supportsSkinning() const
+	{
+		ANKI_ASSERT((m_boneTrfsBinding == MAX_U32 && m_prevFrameBoneTrfsBinding == MAX_U32)
+					|| (m_boneTrfsBinding != MAX_U32 && m_prevFrameBoneTrfsBinding != MAX_U32));
+		return m_boneTrfsBinding != MAX_U32;
+	}
+
 	U32 getUniformsBinding() const
 	U32 getUniformsBinding() const
 	{
 	{
 		ANKI_ASSERT(m_uboBinding != MAX_U32);
 		ANKI_ASSERT(m_uboBinding != MAX_U32);

+ 6 - 2
anki/resource/MeshBinary.h

@@ -18,6 +18,8 @@ namespace anki
 
 
 static constexpr const char* MESH_MAGIC = "ANKIMES5";
 static constexpr const char* MESH_MAGIC = "ANKIMES5";
 
 
+constexpr U32 MESH_BINARY_BUFFER_ALIGNMENT = 16;
+
 enum class MeshBinaryFlag : U32
 enum class MeshBinaryFlag : U32
 {
 {
 	NONE = 0,
 	NONE = 0,
@@ -28,7 +30,8 @@ enum class MeshBinaryFlag : U32
 };
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(MeshBinaryFlag)
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(MeshBinaryFlag)
 
 
-/// Vertex buffer info. The size of the buffer is m_vertexStride*MeshBinaryHeader::m_totalVertexCount aligned to 16.
+/// Vertex buffer info. The size of the buffer is m_vertexStride*MeshBinaryHeader::m_totalVertexCount aligned to
+/// MESH_BINARY_BUFFER_ALIGNMENT.
 class MeshBinaryVertexBuffer
 class MeshBinaryVertexBuffer
 {
 {
 public:
 public:
@@ -115,7 +118,8 @@ public:
 	}
 	}
 };
 };
 
 
-/// The 1st things that appears in a mesh binary. @note The index and vertex buffers are aligned to 16 bytes.
+/// The 1st things that appears in a mesh binary. @note The index and vertex buffers are aligned to
+/// MESH_BINARY_BUFFER_ALIGNMENT bytes.
 class MeshBinaryHeader
 class MeshBinaryHeader
 {
 {
 public:
 public:

+ 4 - 2
anki/resource/MeshBinary.xml

@@ -9,6 +9,8 @@
 	<prefix_code><![CDATA[
 	<prefix_code><![CDATA[
 static constexpr const char* MESH_MAGIC = "ANKIMES5";
 static constexpr const char* MESH_MAGIC = "ANKIMES5";
 
 
+constexpr U32 MESH_BINARY_BUFFER_ALIGNMENT = 16;
+
 enum class MeshBinaryFlag : U32
 enum class MeshBinaryFlag : U32
 {
 {
 	NONE = 0,
 	NONE = 0,
@@ -21,7 +23,7 @@ ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(MeshBinaryFlag)
 ]]></prefix_code>
 ]]></prefix_code>
 
 
 	<classes>
 	<classes>
-		<class name="MeshBinaryVertexBuffer" comment="Vertex buffer info. The size of the buffer is m_vertexStride*MeshBinaryHeader::m_totalVertexCount aligned to 16">
+		<class name="MeshBinaryVertexBuffer" comment="Vertex buffer info. The size of the buffer is m_vertexStride*MeshBinaryHeader::m_totalVertexCount aligned to MESH_BINARY_BUFFER_ALIGNMENT">
 			<members>
 			<members>
 				<member name="m_vertexStride" type="U32" comment="The size of the vertex"/>
 				<member name="m_vertexStride" type="U32" comment="The size of the vertex"/>
 			</members>
 			</members>
@@ -45,7 +47,7 @@ ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(MeshBinaryFlag)
 			</members>
 			</members>
 		</class>
 		</class>
 
 
-		<class name="MeshBinaryHeader" comment="The 1st things that appears in a mesh binary. @note The index and vertex buffers are aligned to 16 bytes">
+		<class name="MeshBinaryHeader" comment="The 1st things that appears in a mesh binary. @note The index and vertex buffers are aligned to MESH_BINARY_BUFFER_ALIGNMENT bytes">
 			<members>
 			<members>
 				<member name="m_magic" type="U8" array_size="8"/>
 				<member name="m_magic" type="U8" array_size="8"/>
 				<member name="m_flags" type="MeshBinaryFlag"/>
 				<member name="m_flags" type="MeshBinaryFlag"/>

+ 8 - 8
anki/resource/MeshBinaryLoader.cpp

@@ -146,19 +146,19 @@ Error MeshBinaryLoader::checkHeader() const
 	ANKI_CHECK(checkFormat(VertexAttributeLocation::UV, Array<Format, 1>{{Format::R32G32_SFLOAT}}, 1, 8));
 	ANKI_CHECK(checkFormat(VertexAttributeLocation::UV, Array<Format, 1>{{Format::R32G32_SFLOAT}}, 1, 8));
 	ANKI_CHECK(checkFormat(VertexAttributeLocation::UV2, Array<Format, 1>{{Format::NONE}}, 1, 0));
 	ANKI_CHECK(checkFormat(VertexAttributeLocation::UV2, Array<Format, 1>{{Format::NONE}}, 1, 0));
 	ANKI_CHECK(checkFormat(VertexAttributeLocation::BONE_INDICES,
 	ANKI_CHECK(checkFormat(VertexAttributeLocation::BONE_INDICES,
-						   Array<Format, 2>{{Format::NONE, Format::R16G16B16A16_UINT}}, 2, 0));
+						   Array<Format, 2>{{Format::NONE, Format::R8G8B8A8_UINT}}, 2, 0));
 	ANKI_CHECK(checkFormat(VertexAttributeLocation::BONE_WEIGHTS,
 	ANKI_CHECK(checkFormat(VertexAttributeLocation::BONE_WEIGHTS,
-						   Array<Format, 2>{{Format::NONE, Format::R8G8B8A8_UNORM}}, 2, 8));
+						   Array<Format, 2>{{Format::NONE, Format::R8G8B8A8_UNORM}}, 2, 4));
 
 
 	// Vertex buffers
 	// Vertex buffers
-	if(m_header.m_vertexBufferCount != 2 + hasBoneInfo())
+	if(m_header.m_vertexBufferCount != 2 + U32(hasBoneInfo()))
 	{
 	{
 		ANKI_RESOURCE_LOGE("Wrong number of vertex buffers");
 		ANKI_RESOURCE_LOGE("Wrong number of vertex buffers");
 		return Error::USER_DATA;
 		return Error::USER_DATA;
 	}
 	}
 
 
 	if(m_header.m_vertexBuffers[0].m_vertexStride != sizeof(Vec3) || m_header.m_vertexBuffers[1].m_vertexStride != 16
 	if(m_header.m_vertexBuffers[0].m_vertexStride != sizeof(Vec3) || m_header.m_vertexBuffers[1].m_vertexStride != 16
-	   || (hasBoneInfo() && m_header.m_vertexBuffers[2].m_vertexStride != 12))
+	   || (hasBoneInfo() && m_header.m_vertexBuffers[2].m_vertexStride != 8))
 	{
 	{
 		ANKI_RESOURCE_LOGE("Some of the vertex buffers have incorrect vertex stride");
 		ANKI_RESOURCE_LOGE("Some of the vertex buffers have incorrect vertex stride");
 		return Error::USER_DATA;
 		return Error::USER_DATA;
@@ -207,11 +207,11 @@ Error MeshBinaryLoader::checkHeader() const
 	PtrSize totalSize = sizeof(m_header);
 	PtrSize totalSize = sizeof(m_header);
 
 
 	totalSize += sizeof(MeshBinarySubMesh) * m_header.m_subMeshCount;
 	totalSize += sizeof(MeshBinarySubMesh) * m_header.m_subMeshCount;
-	totalSize += getIndexBufferSize();
+	totalSize += getAlignedIndexBufferSize();
 
 
 	for(U32 i = 0; i < m_header.m_vertexBufferCount; ++i)
 	for(U32 i = 0; i < m_header.m_vertexBufferCount; ++i)
 	{
 	{
-		totalSize += getVertexBufferSize(i);
+		totalSize += getAlignedVertexBufferSize(i);
 	}
 	}
 
 
 	if(totalSize != m_file->getSize())
 	if(totalSize != m_file->getSize())
@@ -243,10 +243,10 @@ Error MeshBinaryLoader::storeVertexBuffer(U32 bufferIdx, void* ptr, PtrSize size
 	ANKI_ASSERT(bufferIdx < m_header.m_vertexBufferCount);
 	ANKI_ASSERT(bufferIdx < m_header.m_vertexBufferCount);
 	ANKI_ASSERT(size == getVertexBufferSize(bufferIdx));
 	ANKI_ASSERT(size == getVertexBufferSize(bufferIdx));
 
 
-	PtrSize seek = sizeof(m_header) + m_subMeshes.getSizeInBytes() + getIndexBufferSize();
+	PtrSize seek = sizeof(m_header) + m_subMeshes.getSizeInBytes() + getAlignedIndexBufferSize();
 	for(U32 i = 0; i < bufferIdx; ++i)
 	for(U32 i = 0; i < bufferIdx; ++i)
 	{
 	{
-		seek += getVertexBufferSize(i);
+		seek += getAlignedVertexBufferSize(i);
 	}
 	}
 
 
 	ANKI_CHECK(m_file->seek(seek, FileSeekOrigin::BEGINNING));
 	ANKI_CHECK(m_file->seek(seek, FileSeekOrigin::BEGINNING));

+ 21 - 4
anki/resource/MeshBinaryLoader.h

@@ -7,6 +7,7 @@
 
 
 #include <anki/resource/MeshBinary.h>
 #include <anki/resource/MeshBinary.h>
 #include <anki/resource/ResourceFilesystem.h>
 #include <anki/resource/ResourceFilesystem.h>
+#include <anki/resource/MeshBinary.h>
 #include <anki/util/WeakArray.h>
 #include <anki/util/WeakArray.h>
 
 
 namespace anki
 namespace anki
@@ -50,6 +51,11 @@ public:
 		return m_header.m_vertexAttributes[VertexAttributeLocation::BONE_INDICES].m_format != Format::NONE;
 		return m_header.m_vertexAttributes[VertexAttributeLocation::BONE_INDICES].m_format != Format::NONE;
 	}
 	}
 
 
+	ConstWeakArray<MeshBinarySubMesh> getSubMeshes() const
+	{
+		return ConstWeakArray<MeshBinarySubMesh>(m_subMeshes);
+	}
+
 private:
 private:
 	ResourceManager* m_manager;
 	ResourceManager* m_manager;
 	GenericMemoryPoolAllocator<U8> m_alloc;
 	GenericMemoryPoolAllocator<U8> m_alloc;
@@ -67,16 +73,27 @@ private:
 	PtrSize getIndexBufferSize() const
 	PtrSize getIndexBufferSize() const
 	{
 	{
 		ANKI_ASSERT(isLoaded());
 		ANKI_ASSERT(isLoaded());
-		return getAlignedRoundUp(16, PtrSize(m_header.m_totalIndexCount)
-										 * ((m_header.m_indexType == IndexType::U16) ? 2 : 4));
+		return PtrSize(m_header.m_totalIndexCount) * ((m_header.m_indexType == IndexType::U16) ? 2 : 4);
+	}
+
+	PtrSize getAlignedIndexBufferSize() const
+	{
+		ANKI_ASSERT(isLoaded());
+		return getAlignedRoundUp(MESH_BINARY_BUFFER_ALIGNMENT, getIndexBufferSize());
 	}
 	}
 
 
 	PtrSize getVertexBufferSize(U32 bufferIdx) const
 	PtrSize getVertexBufferSize(U32 bufferIdx) const
 	{
 	{
 		ANKI_ASSERT(isLoaded());
 		ANKI_ASSERT(isLoaded());
 		ANKI_ASSERT(bufferIdx < m_header.m_vertexBufferCount);
 		ANKI_ASSERT(bufferIdx < m_header.m_vertexBufferCount);
-		return getAlignedRoundUp(16, PtrSize(m_header.m_totalVertexCount)
-										 * PtrSize(m_header.m_vertexBuffers[bufferIdx].m_vertexStride));
+		return PtrSize(m_header.m_totalVertexCount) * PtrSize(m_header.m_vertexBuffers[bufferIdx].m_vertexStride);
+	}
+
+	PtrSize getAlignedVertexBufferSize(U32 bufferIdx) const
+	{
+		ANKI_ASSERT(isLoaded());
+		ANKI_ASSERT(bufferIdx < m_header.m_vertexBufferCount);
+		return getAlignedRoundUp(MESH_BINARY_BUFFER_ALIGNMENT, getVertexBufferSize(bufferIdx));
 	}
 	}
 
 
 	ANKI_USE_RESULT Error checkHeader() const;
 	ANKI_USE_RESULT Error checkHeader() const;

+ 0 - 338
anki/resource/MeshLoader.cpp

@@ -1,338 +0,0 @@
-// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <anki/resource/MeshLoader.h>
-#include <anki/resource/ResourceManager.h>
-#include <anki/resource/ResourceFilesystem.h>
-
-namespace anki
-{
-
-MeshLoader::MeshLoader(ResourceManager* manager)
-	: MeshLoader(manager, manager->getTempAllocator())
-{
-}
-
-MeshLoader::~MeshLoader()
-{
-	m_subMeshes.destroy(m_alloc);
-}
-
-Error MeshLoader::load(const ResourceFilename& filename)
-{
-	auto& alloc = m_alloc;
-
-	// Load header
-	ANKI_CHECK(m_manager->getFilesystem().openFile(filename, m_file));
-	ANKI_CHECK(m_file->read(&m_header, sizeof(m_header)));
-	ANKI_CHECK(checkHeader());
-
-	// Read submesh info
-	{
-		m_subMeshes.create(alloc, m_header.m_subMeshCount);
-		ANKI_CHECK(m_file->read(&m_subMeshes[0], m_subMeshes.getSizeInBytes()));
-
-		// 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;
-		}
-
-		if(idxSum != m_header.m_totalIndexCount)
-		{
-			ANKI_RESOURCE_LOGE("Incorrect sub mesh info");
-			return Error::USER_DATA;
-		}
-	}
-
-	// Read vert buffer info
-	{
-		U32 vertBufferMask = 0;
-		U32 vertBufferCount = 0;
-		for(const MeshBinaryFile::VertexAttribute& attrib : m_header.m_vertexAttributes)
-		{
-			if(attrib.m_format == Format::NONE)
-			{
-				continue;
-			}
-
-			vertBufferCount = max(attrib.m_bufferBinding + 1, vertBufferCount);
-			vertBufferMask |= 1 << attrib.m_bufferBinding;
-		}
-
-		if(U(__builtin_popcount(vertBufferMask)) != vertBufferCount)
-		{
-			ANKI_RESOURCE_LOGE("Problem in vertex buffers");
-			return Error::USER_DATA;
-		}
-
-		if(vertBufferCount != m_header.m_vertexBufferCount)
-		{
-			ANKI_RESOURCE_LOGE("Wrong vertex buffer count in the header");
-			return Error::USER_DATA;
-		}
-	}
-
-	// Count and check the file size
-	{
-		U32 totalSize = sizeof(m_header);
-
-		totalSize += sizeof(MeshBinaryFile::SubMesh) * m_header.m_subMeshCount;
-		totalSize += U32(getIndexBufferSize());
-
-		for(U i = 0; i < m_header.m_vertexBufferCount; ++i)
-		{
-			totalSize += m_header.m_vertexBuffers[i].m_vertexStride * m_header.m_totalVertexCount;
-		}
-
-		if(totalSize != m_file->getSize())
-		{
-			ANKI_RESOURCE_LOGE("Unexpected file size");
-			return Error::USER_DATA;
-		}
-	}
-
-	return Error::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)
-		{
-			found = true;
-			break;
-		}
-	}
-
-	if(!found)
-	{
-		ANKI_RESOURCE_LOGE("Vertex attribute %u has unsupported format %u", U32(type),
-						   U32(m_header.m_vertexAttributes[type].m_format));
-		return Error::USER_DATA;
-	}
-
-	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("Vertex attribute %u should have 1.0 scale", U32(type));
-		return Error::USER_DATA;
-	}
-
-	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("Wrong magic word");
-		return Error::USER_DATA;
-	}
-
-	// Flags
-	if((h.m_flags & ~MeshBinaryFile::Flag::ALL) != MeshBinaryFile::Flag::NONE)
-	{
-		ANKI_RESOURCE_LOGE("Wrong header flags");
-		return Error::USER_DATA;
-	}
-
-	// Attributes
-	ANKI_CHECK(checkFormat(VertexAttributeLocation::POSITION,
-						   Array<Format, 2>{{Format::R16G16B16A16_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;
-	}
-
-	// m_totalIndexCount
-	const U indicesPerFace = !!(h.m_flags & MeshBinaryFile::Flag::QUAD) ? 4 : 3;
-	if(h.m_totalIndexCount == 0 || (h.m_totalIndexCount % indicesPerFace) != 0)
-	{
-		ANKI_RESOURCE_LOGE("Wrong index count");
-		return Error::USER_DATA;
-	}
-
-	// m_totalVertexCount
-	if(h.m_totalVertexCount == 0)
-	{
-		ANKI_RESOURCE_LOGE("Wrong vertex count");
-		return Error::USER_DATA;
-	}
-
-	// m_subMeshCount
-	if(h.m_subMeshCount == 0)
-	{
-		ANKI_RESOURCE_LOGE("Wrong submesh count");
-		return Error::USER_DATA;
-	}
-
-	// 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;
-}
-
-Error MeshLoader::storeIndexBuffer(void* ptr, PtrSize size)
-{
-	ANKI_ASSERT(isLoaded());
-	ANKI_ASSERT(size == getIndexBufferSize());
-	ANKI_ASSERT(m_loadedChunk == 0);
-
-	if(ptr)
-	{
-		ANKI_CHECK(m_file->read(ptr, size));
-	}
-	else
-	{
-		ANKI_CHECK(m_file->seek(size, FileSeekOrigin::CURRENT));
-	}
-
-	++m_loadedChunk;
-	return Error::NONE;
-}
-
-Error MeshLoader::storeVertexBuffer(U32 bufferIdx, void* ptr, PtrSize size)
-{
-	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_CHECK(m_file->read(ptr, size));
-	}
-	else
-	{
-		ANKI_CHECK(m_file->seek(size, FileSeekOrigin::CURRENT));
-	}
-
-	++m_loadedChunk;
-	return Error::NONE;
-}
-
-Error MeshLoader::storeIndicesAndPosition(DynamicArrayAuto<U32>& indices, DynamicArrayAuto<Vec3>& positions)
-{
-	// Store indices
-	{
-		indices.resize(m_header.m_totalIndexCount);
-
-		// Create staging buff
-		const PtrSize idxBufferSize = getIndexBufferSize();
-		DynamicArrayAuto<U8, PtrSize> staging(m_alloc);
-		staging.create(idxBufferSize);
-
-		// Store to staging buff
-		ANKI_CHECK(storeIndexBuffer(&staging[0], staging.getSizeInBytes()));
-
-		// Copy
-		for(U32 i = 0; i < m_header.m_totalIndexCount; ++i)
-		{
-			if(m_header.m_indexType == IndexType::U32)
-			{
-				indices[i] = *reinterpret_cast<U32*>(&staging[i * 4]);
-			}
-			else
-			{
-				indices[i] = *reinterpret_cast<U16*>(&staging[i * 2]);
-			}
-		}
-	}
-
-	// 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, PtrSize> staging(m_alloc);
-		staging.create(vertBuffSize);
-
-		// Store to staging buff
-		ANKI_CHECK(storeVertexBuffer(attrib.m_bufferBinding, &staging[0], staging.getSizeInBytes()));
-
-		// Copy
-		for(U32 i = 0; i < m_header.m_totalVertexCount; ++i)
-		{
-			Vec3 vert(0.0f);
-			if(attrib.m_format == Format::R32G32B32_SFLOAT)
-			{
-				vert = *reinterpret_cast<Vec3*>(&staging[i * buffInfo.m_vertexStride + attrib.m_relativeOffset]);
-			}
-			else if(attrib.m_format == Format::R16G16B16A16_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();
-			}
-			else
-			{
-				ANKI_ASSERT(0);
-			}
-
-			positions[i] = vert;
-		}
-	}
-
-	return Error::NONE;
-}
-
-} // end namespace anki

+ 0 - 146
anki/resource/MeshLoader.h

@@ -1,146 +0,0 @@
-// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#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
-{
-
-/// @addtogroup resource
-/// @{
-
-/// Information to decode mesh binary files.
-class MeshBinaryFile
-{
-public:
-	static constexpr const char* MAGIC = "ANKIMES4";
-
-	enum class Flag : U32
-	{
-		NONE = 0,
-		QUAD = 1 << 0,
-		CONVEX = 1 << 1,
-
-		ALL = QUAD | CONVEX,
-	};
-	ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS_FRIEND(Flag)
-
-	struct VertexBuffer
-	{
-		U32 m_vertexStride;
-	};
-
-	struct VertexAttribute
-	{
-		U32 m_bufferBinding;
-		Format m_format;
-		U32 m_relativeOffset;
-		F32 m_scale;
-	};
-
-	struct SubMesh
-	{
-		U32 m_firstIndex;
-		U32 m_indexCount;
-		Vec3 m_aabbMin; ///< Bounding box min.
-		Vec3 m_aabbMax; ///< Bounding box max.
-	};
-
-	struct Header
-	{
-		char m_magic[8]; ///< Magic word.
-		Flag m_flags;
-
-		Array<VertexBuffer, U32(VertexAttributeLocation::COUNT)> m_vertexBuffers;
-		U32 m_vertexBufferCount;
-
-		Array<VertexAttribute, U32(VertexAttributeLocation::COUNT)> m_vertexAttributes;
-
-		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)
-		: m_manager(manager)
-		, m_alloc(alloc)
-	{
-	}
-
-	~MeshLoader();
-
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
-
-	ANKI_USE_RESULT Error storeIndexBuffer(void* ptr, PtrSize size);
-
-	ANKI_USE_RESULT Error storeVertexBuffer(U32 bufferIdx, void* ptr, PtrSize size);
-
-	/// 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 MeshBinaryFile::Header& getHeader() const
-	{
-		ANKI_ASSERT(isLoaded());
-		return m_header;
-	}
-
-	Bool hasBoneInfo() const
-	{
-		ANKI_ASSERT(isLoaded());
-		return m_header.m_vertexAttributes[VertexAttributeLocation::BONE_INDICES].m_format != Format::NONE;
-	}
-
-	ConstWeakArray<MeshBinaryFile::SubMesh> getSubMeshes() const
-	{
-		return ConstWeakArray<MeshBinaryFile::SubMesh>(m_subMeshes);
-	}
-
-private:
-	ResourceManager* m_manager;
-	GenericMemoryPoolAllocator<U8> m_alloc;
-
-	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_file.get() != nullptr;
-	}
-
-	PtrSize getIndexBufferSize() const
-	{
-		return PtrSize(m_header.m_totalIndexCount) * ((m_header.m_indexType == IndexType::U16) ? 2 : 4);
-	}
-
-	ANKI_USE_RESULT Error checkHeader() const;
-	ANKI_USE_RESULT Error checkFormat(VertexAttributeLocation type, ConstWeakArray<Format> supportedFormats) const;
-};
-/// @}
-
-} // end namespace anki

+ 67 - 68
anki/resource/MeshResource.cpp

@@ -5,11 +5,10 @@
 
 
 #include <anki/resource/MeshResource.h>
 #include <anki/resource/MeshResource.h>
 #include <anki/resource/ResourceManager.h>
 #include <anki/resource/ResourceManager.h>
-#include <anki/resource/MeshLoader.h>
+#include <anki/resource/MeshBinaryLoader.h>
 #include <anki/resource/AsyncLoader.h>
 #include <anki/resource/AsyncLoader.h>
 #include <anki/util/Functions.h>
 #include <anki/util/Functions.h>
 #include <anki/util/Filesystem.h>
 #include <anki/util/Filesystem.h>
-#include <anki/util/Xml.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -17,10 +16,10 @@ namespace anki
 class MeshResource::LoadContext
 class MeshResource::LoadContext
 {
 {
 public:
 public:
-	MeshResource* m_mesh;
-	MeshLoader m_loader;
+	MeshResourcePtr m_mesh;
+	MeshBinaryLoader m_loader;
 
 
-	LoadContext(MeshResource* mesh, GenericMemoryPoolAllocator<U8> alloc)
+	LoadContext(const MeshResourcePtr& mesh, GenericMemoryPoolAllocator<U8> alloc)
 		: m_mesh(mesh)
 		: m_mesh(mesh)
 		, m_loader(&mesh->getManager(), alloc)
 		, m_loader(&mesh->getManager(), alloc)
 	{
 	{
@@ -33,7 +32,7 @@ class MeshResource::LoadTask : public AsyncLoaderTask
 public:
 public:
 	MeshResource::LoadContext m_ctx;
 	MeshResource::LoadContext m_ctx;
 
 
-	LoadTask(MeshResource* mesh)
+	LoadTask(const MeshResourcePtr& mesh)
 		: m_ctx(mesh, mesh->getManager().getAsyncLoader().getAllocator())
 		: m_ctx(mesh, mesh->getManager().getAsyncLoader().getAllocator())
 	{
 	{
 	}
 	}
@@ -42,6 +41,11 @@ public:
 	{
 	{
 		return m_ctx.m_mesh->loadAsync(m_ctx.m_loader);
 		return m_ctx.m_mesh->loadAsync(m_ctx.m_loader);
 	}
 	}
+
+	GenericMemoryPoolAllocator<U8> getAllocator() const
+	{
+		return m_ctx.m_mesh->getManager().getAsyncLoader().getAllocator();
+	}
 };
 };
 
 
 MeshResource::MeshResource(ResourceManager* manager)
 MeshResource::MeshResource(ResourceManager* manager)
@@ -53,45 +57,40 @@ MeshResource::MeshResource(ResourceManager* manager)
 MeshResource::~MeshResource()
 MeshResource::~MeshResource()
 {
 {
 	m_subMeshes.destroy(getAllocator());
 	m_subMeshes.destroy(getAllocator());
-	m_vertBufferInfos.destroy(getAllocator());
+	m_vertexBufferInfos.destroy(getAllocator());
 }
 }
 
 
 Bool MeshResource::isCompatible(const MeshResource& other) const
 Bool MeshResource::isCompatible(const MeshResource& other) const
 {
 {
-	return hasBoneWeights() == other.hasBoneWeights() && getSubMeshCount() == other.getSubMeshCount()
-		   && m_texChannelCount == other.m_texChannelCount;
+	return hasBoneWeights() == other.hasBoneWeights() && getSubMeshCount() == other.getSubMeshCount();
 }
 }
 
 
 Error MeshResource::load(const ResourceFilename& filename, Bool async)
 Error MeshResource::load(const ResourceFilename& filename, Bool async)
 {
 {
-	LoadTask* task;
+	UniquePtr<LoadTask> task;
 	LoadContext* ctx;
 	LoadContext* ctx;
-	LoadContext localCtx(this, getTempAllocator());
+	LoadContext localCtx(MeshResourcePtr(this), getTempAllocator());
 
 
 	StringAuto basename(getTempAllocator());
 	StringAuto basename(getTempAllocator());
 	getFilepathFilename(filename, basename);
 	getFilepathFilename(filename, basename);
 
 
 	const Bool rayTracingEnabled = getManager().getGrManager().getDeviceCapabilities().m_rayTracingEnabled;
 	const Bool rayTracingEnabled = getManager().getGrManager().getDeviceCapabilities().m_rayTracingEnabled;
-	if(rayTracingEnabled)
-	{
-		async = false; // TODO Find a better way
-	}
 
 
 	if(async)
 	if(async)
 	{
 	{
-		task = getManager().getAsyncLoader().newTask<LoadTask>(this);
+		task.reset(getManager().getAsyncLoader().newTask<LoadTask>(MeshResourcePtr(this)));
 		ctx = &task->m_ctx;
 		ctx = &task->m_ctx;
 	}
 	}
 	else
 	else
 	{
 	{
-		task = nullptr;
+		task.reset(nullptr);
 		ctx = &localCtx;
 		ctx = &localCtx;
 	}
 	}
 
 
 	// Open file
 	// Open file
-	MeshLoader& loader = ctx->m_loader;
+	MeshBinaryLoader& loader = ctx->m_loader;
 	ANKI_CHECK(loader.load(filename));
 	ANKI_CHECK(loader.load(filename));
-	const MeshBinaryFile::Header& header = loader.getHeader();
+	const MeshBinaryHeader& header = loader.getHeader();
 
 
 	// Get submeshes
 	// Get submeshes
 	m_subMeshes.create(getAllocator(), header.m_subMeshCount);
 	m_subMeshes.create(getAllocator(), header.m_subMeshCount);
@@ -99,10 +98,8 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 	{
 	{
 		m_subMeshes[i].m_firstIndex = loader.getSubMeshes()[i].m_firstIndex;
 		m_subMeshes[i].m_firstIndex = loader.getSubMeshes()[i].m_firstIndex;
 		m_subMeshes[i].m_indexCount = loader.getSubMeshes()[i].m_indexCount;
 		m_subMeshes[i].m_indexCount = loader.getSubMeshes()[i].m_indexCount;
-
-		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());
+		m_subMeshes[i].m_aabb.setMin(loader.getSubMeshes()[i].m_aabbMin);
+		m_subMeshes[i].m_aabb.setMax(loader.getSubMeshes()[i].m_aabbMax);
 	}
 	}
 
 
 	// Index stuff
 	// Index stuff
@@ -117,23 +114,23 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 	{
 	{
 		indexBufferUsage |= BufferUsageBit::ACCELERATION_STRUCTURE_BUILD;
 		indexBufferUsage |= BufferUsageBit::ACCELERATION_STRUCTURE_BUILD;
 	}
 	}
-	m_indexBuff = getManager().getGrManager().newBuffer(
+	m_indexBuffer = getManager().getGrManager().newBuffer(
 		BufferInitInfo(indexBuffSize, indexBufferUsage, BufferMapAccessBit::NONE,
 		BufferInitInfo(indexBuffSize, indexBufferUsage, BufferMapAccessBit::NONE,
 					   StringAuto(getTempAllocator()).sprintf("%s_%s", "Idx", basename.cstr())));
 					   StringAuto(getTempAllocator()).sprintf("%s_%s", "Idx", basename.cstr())));
 
 
 	// Vertex stuff
 	// Vertex stuff
-	m_vertCount = header.m_totalVertexCount;
-	m_vertBufferInfos.create(getAllocator(), header.m_vertexBufferCount);
+	m_vertexCount = header.m_totalVertexCount;
+	m_vertexBufferInfos.create(getAllocator(), header.m_vertexBufferCount);
 
 
 	U32 totalVertexBuffSize = 0;
 	U32 totalVertexBuffSize = 0;
 	for(U32 i = 0; i < header.m_vertexBufferCount; ++i)
 	for(U32 i = 0; i < header.m_vertexBufferCount; ++i)
 	{
 	{
-		alignRoundUp(VERTEX_BUFFER_ALIGNMENT, totalVertexBuffSize);
+		alignRoundUp(MESH_BINARY_BUFFER_ALIGNMENT, totalVertexBuffSize);
 
 
-		m_vertBufferInfos[i].m_offset = totalVertexBuffSize;
-		m_vertBufferInfos[i].m_stride = header.m_vertexBuffers[i].m_vertexStride;
+		m_vertexBufferInfos[i].m_offset = totalVertexBuffSize;
+		m_vertexBufferInfos[i].m_stride = header.m_vertexBuffers[i].m_vertexStride;
 
 
-		totalVertexBuffSize += m_vertCount * m_vertBufferInfos[i].m_stride;
+		totalVertexBuffSize += m_vertexCount * m_vertexBufferInfos[i].m_stride;
 	}
 	}
 
 
 	BufferUsageBit vertexBufferUsage = BufferUsageBit::VERTEX | BufferUsageBit::TRANSFER_DESTINATION;
 	BufferUsageBit vertexBufferUsage = BufferUsageBit::VERTEX | BufferUsageBit::TRANSFER_DESTINATION;
@@ -141,21 +138,19 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 	{
 	{
 		vertexBufferUsage |= BufferUsageBit::ACCELERATION_STRUCTURE_BUILD;
 		vertexBufferUsage |= BufferUsageBit::ACCELERATION_STRUCTURE_BUILD;
 	}
 	}
-	m_vertBuff = getManager().getGrManager().newBuffer(
+	m_vertexBuffer = getManager().getGrManager().newBuffer(
 		BufferInitInfo(totalVertexBuffSize, vertexBufferUsage, BufferMapAccessBit::NONE,
 		BufferInitInfo(totalVertexBuffSize, vertexBufferUsage, BufferMapAccessBit::NONE,
 					   StringAuto(getTempAllocator()).sprintf("%s_%s", "Vert", basename.cstr())));
 					   StringAuto(getTempAllocator()).sprintf("%s_%s", "Vert", basename.cstr())));
 
 
-	m_texChannelCount = !!header.m_vertexAttributes[VertexAttributeLocation::UV2].m_format ? 2 : 1;
-
 	for(VertexAttributeLocation attrib = VertexAttributeLocation::FIRST; attrib < VertexAttributeLocation::COUNT;
 	for(VertexAttributeLocation attrib = VertexAttributeLocation::FIRST; attrib < VertexAttributeLocation::COUNT;
 		++attrib)
 		++attrib)
 	{
 	{
-		AttribInfo& out = m_attribs[attrib];
-		const MeshBinaryFile::VertexAttribute& in = header.m_vertexAttributes[attrib];
+		AttribInfo& out = m_attributes[attrib];
+		const MeshBinaryVertexAttribute& in = header.m_vertexAttributes[attrib];
 
 
 		if(!!in.m_format)
 		if(!!in.m_format)
 		{
 		{
-			out.m_fmt = in.m_format;
+			out.m_format = in.m_format;
 			out.m_relativeOffset = in.m_relativeOffset;
 			out.m_relativeOffset = in.m_relativeOffset;
 			out.m_buffIdx = U8(in.m_bufferBinding);
 			out.m_buffIdx = U8(in.m_bufferBinding);
 			ANKI_ASSERT(in.m_scale == 1.0f && "Not supported ATM");
 			ANKI_ASSERT(in.m_scale == 1.0f && "Not supported ATM");
@@ -163,23 +158,22 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 	}
 	}
 
 
 	// Other
 	// 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());
+	m_aabb.setMin(header.m_aabbMin);
+	m_aabb.setMax(header.m_aabbMax);
 
 
 	// Clear the buffers
 	// Clear the buffers
-	if(!async)
+	if(async)
 	{
 	{
 		CommandBufferInitInfo cmdbinit;
 		CommandBufferInitInfo cmdbinit;
 		cmdbinit.m_flags = CommandBufferFlag::SMALL_BATCH;
 		cmdbinit.m_flags = CommandBufferFlag::SMALL_BATCH;
 		CommandBufferPtr cmdb = getManager().getGrManager().newCommandBuffer(cmdbinit);
 		CommandBufferPtr cmdb = getManager().getGrManager().newCommandBuffer(cmdbinit);
 
 
-		cmdb->fillBuffer(m_vertBuff, 0, MAX_PTR_SIZE, 0);
-		cmdb->fillBuffer(m_indexBuff, 0, MAX_PTR_SIZE, 0);
+		cmdb->fillBuffer(m_vertexBuffer, 0, MAX_PTR_SIZE, 0);
+		cmdb->fillBuffer(m_indexBuffer, 0, MAX_PTR_SIZE, 0);
 
 
-		cmdb->setBufferBarrier(m_vertBuff, BufferUsageBit::TRANSFER_DESTINATION, BufferUsageBit::VERTEX, 0,
+		cmdb->setBufferBarrier(m_vertexBuffer, BufferUsageBit::TRANSFER_DESTINATION, BufferUsageBit::VERTEX, 0,
 							   MAX_PTR_SIZE);
 							   MAX_PTR_SIZE);
-		cmdb->setBufferBarrier(m_indexBuff, BufferUsageBit::TRANSFER_DESTINATION, BufferUsageBit::INDEX, 0,
+		cmdb->setBufferBarrier(m_indexBuffer, BufferUsageBit::TRANSFER_DESTINATION, BufferUsageBit::INDEX, 0,
 							   MAX_PTR_SIZE);
 							   MAX_PTR_SIZE);
 
 
 		cmdb->flush();
 		cmdb->flush();
@@ -191,14 +185,14 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 		AccelerationStructureInitInfo inf(StringAuto(getTempAllocator()).sprintf("%s_%s", "Blas", basename.cstr()));
 		AccelerationStructureInitInfo inf(StringAuto(getTempAllocator()).sprintf("%s_%s", "Blas", basename.cstr()));
 		inf.m_type = AccelerationStructureType::BOTTOM_LEVEL;
 		inf.m_type = AccelerationStructureType::BOTTOM_LEVEL;
 
 
-		inf.m_bottomLevel.m_indexBuffer = m_indexBuff;
+		inf.m_bottomLevel.m_indexBuffer = m_indexBuffer;
 		inf.m_bottomLevel.m_indexBufferOffset = 0;
 		inf.m_bottomLevel.m_indexBufferOffset = 0;
 		inf.m_bottomLevel.m_indexCount = m_indexCount;
 		inf.m_bottomLevel.m_indexCount = m_indexCount;
 		inf.m_bottomLevel.m_indexType = m_indexType;
 		inf.m_bottomLevel.m_indexType = m_indexType;
 
 
 		U32 bufferIdx;
 		U32 bufferIdx;
 		Format format;
 		Format format;
-		PtrSize relativeOffset;
+		U32 relativeOffset;
 		getVertexAttributeInfo(VertexAttributeLocation::POSITION, bufferIdx, format, relativeOffset);
 		getVertexAttributeInfo(VertexAttributeLocation::POSITION, bufferIdx, format, relativeOffset);
 
 
 		BufferPtr buffer;
 		BufferPtr buffer;
@@ -210,7 +204,7 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 		inf.m_bottomLevel.m_positionBufferOffset = offset;
 		inf.m_bottomLevel.m_positionBufferOffset = offset;
 		inf.m_bottomLevel.m_positionStride = U32(stride);
 		inf.m_bottomLevel.m_positionStride = U32(stride);
 		inf.m_bottomLevel.m_positionsFormat = format;
 		inf.m_bottomLevel.m_positionsFormat = format;
-		inf.m_bottomLevel.m_positionCount = m_vertCount;
+		inf.m_bottomLevel.m_positionCount = m_vertexCount;
 
 
 		m_blas = getManager().getGrManager().newAccelerationStructure(inf);
 		m_blas = getManager().getGrManager().newAccelerationStructure(inf);
 	}
 	}
@@ -220,13 +214,13 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 	{
 	{
 		U32 bufferIdx;
 		U32 bufferIdx;
 		Format format;
 		Format format;
-		PtrSize relativeOffset;
+		U32 relativeOffset;
 		getVertexAttributeInfo(VertexAttributeLocation::POSITION, bufferIdx, format, relativeOffset);
 		getVertexAttributeInfo(VertexAttributeLocation::POSITION, bufferIdx, format, relativeOffset);
 		BufferPtr buffer;
 		BufferPtr buffer;
 		PtrSize offset;
 		PtrSize offset;
 		PtrSize stride;
 		PtrSize stride;
 		getVertexBufferInfo(bufferIdx, buffer, offset, stride);
 		getVertexBufferInfo(bufferIdx, buffer, offset, stride);
-		m_meshGpuDescriptor.m_indexBufferPtr = m_indexBuff->getGpuAddress();
+		m_meshGpuDescriptor.m_indexBufferPtr = m_indexBuffer->getGpuAddress();
 		m_meshGpuDescriptor.m_positionBufferPtr = buffer->getGpuAddress();
 		m_meshGpuDescriptor.m_positionBufferPtr = buffer->getGpuAddress();
 
 
 		getVertexAttributeInfo(VertexAttributeLocation::NORMAL, bufferIdx, format, relativeOffset);
 		getVertexAttributeInfo(VertexAttributeLocation::NORMAL, bufferIdx, format, relativeOffset);
@@ -241,7 +235,7 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 		}
 		}
 
 
 		m_meshGpuDescriptor.m_indexCount = m_indexCount;
 		m_meshGpuDescriptor.m_indexCount = m_indexCount;
-		m_meshGpuDescriptor.m_vertexCount = m_vertCount;
+		m_meshGpuDescriptor.m_vertexCount = m_vertexCount;
 		m_meshGpuDescriptor.m_aabbMin = header.m_aabbMin;
 		m_meshGpuDescriptor.m_aabbMin = header.m_aabbMin;
 		m_meshGpuDescriptor.m_aabbMax = header.m_aabbMax;
 		m_meshGpuDescriptor.m_aabbMax = header.m_aabbMax;
 	}
 	}
@@ -249,7 +243,9 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 	// Submit the loading task
 	// Submit the loading task
 	if(async)
 	if(async)
 	{
 	{
-		getManager().getAsyncLoader().submitTask(task);
+		getManager().getAsyncLoader().submitTask(task.get());
+		LoadTask* pTask;
+		task.moveAndReset(pTask);
 	}
 	}
 	else
 	else
 	{
 	{
@@ -259,7 +255,7 @@ Error MeshResource::load(const ResourceFilename& filename, Bool async)
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
-Error MeshResource::loadAsync(MeshLoader& loader) const
+Error MeshResource::loadAsync(MeshBinaryLoader& loader) const
 {
 {
 	GrManager& gr = getManager().getGrManager();
 	GrManager& gr = getManager().getGrManager();
 	TransferGpuAllocator& transferAlloc = getManager().getTransferGpuAllocator();
 	TransferGpuAllocator& transferAlloc = getManager().getTransferGpuAllocator();
@@ -270,49 +266,52 @@ Error MeshResource::loadAsync(MeshLoader& loader) const
 	CommandBufferPtr cmdb = gr.newCommandBuffer(cmdbinit);
 	CommandBufferPtr cmdb = gr.newCommandBuffer(cmdbinit);
 
 
 	// Set barriers
 	// Set barriers
-	cmdb->setBufferBarrier(m_vertBuff, BufferUsageBit::VERTEX, BufferUsageBit::TRANSFER_DESTINATION, 0, MAX_PTR_SIZE);
-	cmdb->setBufferBarrier(m_indexBuff, BufferUsageBit::INDEX, BufferUsageBit::TRANSFER_DESTINATION, 0, MAX_PTR_SIZE);
+	cmdb->setBufferBarrier(m_vertexBuffer, BufferUsageBit::VERTEX, BufferUsageBit::TRANSFER_DESTINATION, 0,
+						   MAX_PTR_SIZE);
+	cmdb->setBufferBarrier(m_indexBuffer, BufferUsageBit::INDEX, BufferUsageBit::TRANSFER_DESTINATION, 0, MAX_PTR_SIZE);
 
 
 	// Write index buffer
 	// Write index buffer
 	{
 	{
-		ANKI_CHECK(transferAlloc.allocate(m_indexBuff->getSize(), handles[1]));
+		ANKI_CHECK(transferAlloc.allocate(m_indexBuffer->getSize(), handles[1]));
 		void* data = handles[1].getMappedMemory();
 		void* data = handles[1].getMappedMemory();
 		ANKI_ASSERT(data);
 		ANKI_ASSERT(data);
 
 
-		ANKI_CHECK(loader.storeIndexBuffer(data, m_indexBuff->getSize()));
+		ANKI_CHECK(loader.storeIndexBuffer(data, m_indexBuffer->getSize()));
 
 
-		cmdb->copyBufferToBuffer(handles[1].getBuffer(), handles[1].getOffset(), m_indexBuff, 0, handles[1].getRange());
+		cmdb->copyBufferToBuffer(handles[1].getBuffer(), handles[1].getOffset(), m_indexBuffer, 0,
+								 handles[1].getRange());
 	}
 	}
 
 
 	// Write vert buff
 	// Write vert buff
 	{
 	{
-		ANKI_CHECK(transferAlloc.allocate(m_vertBuff->getSize(), handles[0]));
+		ANKI_CHECK(transferAlloc.allocate(m_vertexBuffer->getSize(), handles[0]));
 		U8* data = static_cast<U8*>(handles[0].getMappedMemory());
 		U8* data = static_cast<U8*>(handles[0].getMappedMemory());
 		ANKI_ASSERT(data);
 		ANKI_ASSERT(data);
 
 
 		// Load to staging
 		// Load to staging
 		PtrSize offset = 0;
 		PtrSize offset = 0;
-		for(U32 i = 0; i < m_vertBufferInfos.getSize(); ++i)
+		for(U32 i = 0; i < m_vertexBufferInfos.getSize(); ++i)
 		{
 		{
-			alignRoundUp(VERTEX_BUFFER_ALIGNMENT, offset);
+			alignRoundUp(MESH_BINARY_BUFFER_ALIGNMENT, offset);
 			ANKI_CHECK(
 			ANKI_CHECK(
-				loader.storeVertexBuffer(i, data + offset, PtrSize(m_vertBufferInfos[i].m_stride) * m_vertCount));
+				loader.storeVertexBuffer(i, data + offset, PtrSize(m_vertexBufferInfos[i].m_stride) * m_vertexCount));
 
 
-			offset += PtrSize(m_vertBufferInfos[i].m_stride) * m_vertCount;
+			offset += PtrSize(m_vertexBufferInfos[i].m_stride) * m_vertexCount;
 		}
 		}
 
 
-		ANKI_ASSERT(offset == m_vertBuff->getSize());
+		ANKI_ASSERT(offset == m_vertexBuffer->getSize());
 
 
 		// Copy
 		// Copy
-		cmdb->copyBufferToBuffer(handles[0].getBuffer(), handles[0].getOffset(), m_vertBuff, 0, handles[0].getRange());
+		cmdb->copyBufferToBuffer(handles[0].getBuffer(), handles[0].getOffset(), m_vertexBuffer, 0,
+								 handles[0].getRange());
 	}
 	}
 
 
 	// Build the BLAS
 	// Build the BLAS
 	if(gr.getDeviceCapabilities().m_rayTracingEnabled)
 	if(gr.getDeviceCapabilities().m_rayTracingEnabled)
 	{
 	{
-		cmdb->setBufferBarrier(m_vertBuff, BufferUsageBit::TRANSFER_DESTINATION,
+		cmdb->setBufferBarrier(m_vertexBuffer, BufferUsageBit::TRANSFER_DESTINATION,
 							   BufferUsageBit::ACCELERATION_STRUCTURE_BUILD | BufferUsageBit::VERTEX, 0, MAX_PTR_SIZE);
 							   BufferUsageBit::ACCELERATION_STRUCTURE_BUILD | BufferUsageBit::VERTEX, 0, MAX_PTR_SIZE);
-		cmdb->setBufferBarrier(m_indexBuff, BufferUsageBit::TRANSFER_DESTINATION,
+		cmdb->setBufferBarrier(m_indexBuffer, BufferUsageBit::TRANSFER_DESTINATION,
 							   BufferUsageBit::ACCELERATION_STRUCTURE_BUILD | BufferUsageBit::INDEX, 0, MAX_PTR_SIZE);
 							   BufferUsageBit::ACCELERATION_STRUCTURE_BUILD | BufferUsageBit::INDEX, 0, MAX_PTR_SIZE);
 
 
 		cmdb->setAccelerationStructureBarrier(m_blas, AccelerationStructureUsageBit::NONE,
 		cmdb->setAccelerationStructureBarrier(m_blas, AccelerationStructureUsageBit::NONE,
@@ -325,9 +324,9 @@ Error MeshResource::loadAsync(MeshLoader& loader) const
 	}
 	}
 	else
 	else
 	{
 	{
-		cmdb->setBufferBarrier(m_vertBuff, BufferUsageBit::TRANSFER_DESTINATION, BufferUsageBit::VERTEX, 0,
+		cmdb->setBufferBarrier(m_vertexBuffer, BufferUsageBit::TRANSFER_DESTINATION, BufferUsageBit::VERTEX, 0,
 							   MAX_PTR_SIZE);
 							   MAX_PTR_SIZE);
-		cmdb->setBufferBarrier(m_indexBuff, BufferUsageBit::TRANSFER_DESTINATION, BufferUsageBit::INDEX, 0,
+		cmdb->setBufferBarrier(m_indexBuffer, BufferUsageBit::TRANSFER_DESTINATION, BufferUsageBit::INDEX, 0,
 							   MAX_PTR_SIZE);
 							   MAX_PTR_SIZE);
 	}
 	}
 
 

+ 33 - 48
anki/resource/MeshResource.h

@@ -8,14 +8,14 @@
 #include <anki/resource/ResourceObject.h>
 #include <anki/resource/ResourceObject.h>
 #include <anki/Math.h>
 #include <anki/Math.h>
 #include <anki/Gr.h>
 #include <anki/Gr.h>
-#include <anki/collision/Obb.h>
+#include <anki/collision/Aabb.h>
 #include <anki/shaders/include/ModelTypes.h>
 #include <anki/shaders/include/ModelTypes.h>
 
 
 namespace anki
 namespace anki
 {
 {
 
 
 // Forward
 // Forward
-class MeshLoader;
+class MeshBinaryLoader;
 
 
 /// @addtogroup resource
 /// @addtogroup resource
 /// @{
 /// @{
@@ -36,18 +36,18 @@ public:
 	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
 
 	/// Get the complete bounding box.
 	/// Get the complete bounding box.
-	const Obb& getBoundingShape() const
+	const Aabb& getBoundingShape() const
 	{
 	{
-		return m_obb;
+		return m_aabb;
 	}
 	}
 
 
 	/// Get submesh info.
 	/// Get submesh info.
-	void getSubMeshInfo(U32 subMeshId, U32& firstIndex, U32& indexCount, const Obb*& obb) const
+	void getSubMeshInfo(U32 subMeshId, U32& firstIndex, U32& indexCount, Aabb& aabb) const
 	{
 	{
 		const SubMesh& sm = m_subMeshes[subMeshId];
 		const SubMesh& sm = m_subMeshes[subMeshId];
 		firstIndex = sm.m_firstIndex;
 		firstIndex = sm.m_firstIndex;
 		indexCount = sm.m_indexCount;
 		indexCount = sm.m_indexCount;
-		obb = &sm.m_obb;
+		aabb = sm.m_aabb;
 	}
 	}
 
 
 	U32 getSubMeshCount() const
 	U32 getSubMeshCount() const
@@ -58,7 +58,7 @@ public:
 	/// Get all info around vertex indices.
 	/// Get all info around vertex indices.
 	void getIndexBufferInfo(BufferPtr& buff, PtrSize& buffOffset, U32& indexCount, IndexType& indexType) const
 	void getIndexBufferInfo(BufferPtr& buff, PtrSize& buffOffset, U32& indexCount, IndexType& indexType) const
 	{
 	{
-		buff = m_indexBuff;
+		buff = m_indexBuffer;
 		buffOffset = 0;
 		buffOffset = 0;
 		indexCount = m_indexCount;
 		indexCount = m_indexCount;
 		indexType = m_indexType;
 		indexType = m_indexType;
@@ -67,37 +67,31 @@ public:
 	/// Get the number of logical vertex buffers.
 	/// Get the number of logical vertex buffers.
 	U32 getVertexBufferCount() const
 	U32 getVertexBufferCount() const
 	{
 	{
-		return m_vertBufferInfos.getSize();
+		return m_vertexBufferInfos.getSize();
 	}
 	}
 
 
 	/// Get vertex buffer info.
 	/// Get vertex buffer info.
 	void getVertexBufferInfo(const U32 buffIdx, BufferPtr& buff, PtrSize& offset, PtrSize& stride) const
 	void getVertexBufferInfo(const U32 buffIdx, BufferPtr& buff, PtrSize& offset, PtrSize& stride) const
 	{
 	{
-		buff = m_vertBuff;
-		offset = m_vertBufferInfos[buffIdx].m_offset;
-		stride = m_vertBufferInfos[buffIdx].m_stride;
+		buff = m_vertexBuffer;
+		offset = m_vertexBufferInfos[buffIdx].m_offset;
+		stride = m_vertexBufferInfos[buffIdx].m_stride;
 	}
 	}
 
 
 	/// Get attribute info. You need to check if the attribute is preset first (isVertexAttributePresent)
 	/// Get attribute info. You need to check if the attribute is preset first (isVertexAttributePresent)
 	void getVertexAttributeInfo(const VertexAttributeLocation attrib, U32& bufferIdx, Format& format,
 	void getVertexAttributeInfo(const VertexAttributeLocation attrib, U32& bufferIdx, Format& format,
-								PtrSize& relativeOffset) const
+								U32& relativeOffset) const
 	{
 	{
-		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;
+		ANKI_ASSERT(isVertexAttributePresent(attrib));
+		bufferIdx = m_attributes[attrib].m_buffIdx;
+		format = m_attributes[attrib].m_format;
+		relativeOffset = m_attributes[attrib].m_relativeOffset;
 	}
 	}
 
 
 	/// Check if a vertex attribute is present.
 	/// Check if a vertex attribute is present.
 	Bool isVertexAttributePresent(const VertexAttributeLocation attrib) const
 	Bool isVertexAttributePresent(const VertexAttributeLocation attrib) const
 	{
 	{
-		return !!m_attribs[attrib].m_fmt;
-	}
-
-	/// Get the number of texture coordinates channels.
-	U32 getTextureChannelCount() const
-	{
-		return m_texChannelCount;
+		return !!m_attributes[attrib].m_format;
 	}
 	}
 
 
 	/// Return true if it has bone weights.
 	/// Return true if it has bone weights.
@@ -120,67 +114,58 @@ public:
 	/// Get the buffer that contains all the indices of all submesses.
 	/// Get the buffer that contains all the indices of all submesses.
 	BufferPtr getIndexBuffer() const
 	BufferPtr getIndexBuffer() const
 	{
 	{
-		return m_indexBuff;
+		return m_indexBuffer;
 	}
 	}
 
 
 	/// Get the buffer that contains all the vertices of all submesses.
 	/// Get the buffer that contains all the vertices of all submesses.
 	BufferPtr getVertexBuffer() const
 	BufferPtr getVertexBuffer() const
 	{
 	{
-		return m_vertBuff;
+		return m_vertexBuffer;
 	}
 	}
 
 
 private:
 private:
 	class LoadTask;
 	class LoadTask;
 	class LoadContext;
 	class LoadContext;
 
 
-	static constexpr U VERTEX_BUFFER_ALIGNMENT = 64;
-
-	/// Sub-mesh data
 	class SubMesh
 	class SubMesh
 	{
 	{
 	public:
 	public:
 		U32 m_firstIndex;
 		U32 m_firstIndex;
 		U32 m_indexCount;
 		U32 m_indexCount;
-		Obb m_obb;
+		Aabb m_aabb;
 	};
 	};
-	DynamicArray<SubMesh> m_subMeshes;
-
-	// Index stuff
-	U32 m_indexCount = 0;
-	BufferPtr m_indexBuff;
-	IndexType m_indexType = IndexType::COUNT;
-
-	// Vertex stuff
-	U32 m_vertCount = 0;
 
 
 	class VertBuffInfo
 	class VertBuffInfo
 	{
 	{
 	public:
 	public:
-		U32 m_offset; ///< Offset from the base of m_vertBuff.
+		PtrSize m_offset; ///< Offset from the base of m_vertBuff.
 		U32 m_stride;
 		U32 m_stride;
 	};
 	};
-	DynamicArray<VertBuffInfo> m_vertBufferInfos;
 
 
 	class AttribInfo
 	class AttribInfo
 	{
 	{
 	public:
 	public:
-		Format m_fmt = Format::NONE;
+		Format m_format = Format::NONE;
 		U32 m_relativeOffset = 0;
 		U32 m_relativeOffset = 0;
-		U8 m_buffIdx = 0;
+		U32 m_buffIdx = 0;
 	};
 	};
-	Array<AttribInfo, U(VertexAttributeLocation::COUNT)> m_attribs;
 
 
-	BufferPtr m_vertBuff;
-	U8 m_texChannelCount = 0;
+	DynamicArray<SubMesh> m_subMeshes;
+	DynamicArray<VertBuffInfo> m_vertexBufferInfos;
+	Array<AttribInfo, U(VertexAttributeLocation::COUNT)> m_attributes;
 
 
-	// Other
-	Obb m_obb;
+	BufferPtr m_indexBuffer;
+	BufferPtr m_vertexBuffer;
+	U32 m_indexCount = 0;
+	U32 m_vertexCount = 0;
+	Aabb m_aabb;
+	IndexType m_indexType;
 
 
 	// RT
 	// RT
 	AccelerationStructurePtr m_blas;
 	AccelerationStructurePtr m_blas;
 	MeshGpuDescriptor m_meshGpuDescriptor;
 	MeshGpuDescriptor m_meshGpuDescriptor;
 
 
-	ANKI_USE_RESULT Error loadAsync(MeshLoader& loader) const;
+	ANKI_USE_RESULT Error loadAsync(MeshBinaryLoader& loader) const;
 };
 };
 /// @}
 /// @}
 
 

+ 42 - 56
anki/resource/ModelResource.cpp

@@ -6,7 +6,6 @@
 #include <anki/resource/ModelResource.h>
 #include <anki/resource/ModelResource.h>
 #include <anki/resource/ResourceManager.h>
 #include <anki/resource/ResourceManager.h>
 #include <anki/resource/MeshResource.h>
 #include <anki/resource/MeshResource.h>
-#include <anki/resource/MeshLoader.h>
 #include <anki/util/Xml.h>
 #include <anki/util/Xml.h>
 #include <anki/util/Logger.h>
 #include <anki/util/Logger.h>
 
 
@@ -30,29 +29,12 @@ static Bool attributeIsRequired(VertexAttributeLocation loc, Pass pass, Bool has
 	}
 	}
 }
 }
 
 
-void ModelPatch::getRenderingInfo(const RenderingKey& key, WeakArray<U8> subMeshIndicesArray,
-								  ModelRenderingInfo& inf) const
+void ModelPatch::getRenderingInfo(const RenderingKey& key, ModelRenderingInfo& inf) const
 {
 {
-	const Bool hasSkin = m_model->getSkeleton().isCreated();
+	ANKI_ASSERT(!(!m_model->supportsSkinning() && key.isSkinned()));
 
 
 	// Get the resources
 	// Get the resources
-	RenderingKey meshKey = key;
-	meshKey.setLod(min<U32>(key.getLod(), m_meshCount - 1));
-	const MeshResource& mesh = getMesh(meshKey);
-
-	// Get program
-	{
-		RenderingKey mtlKey = key;
-		mtlKey.setLod(min(key.getLod(), m_mtl->getLodCount() - 1));
-		mtlKey.setSkinned(hasSkin);
-
-		const MaterialVariant& variant = m_mtl->getOrCreateVariant(mtlKey);
-
-		inf.m_program = variant.getShaderProgram();
-
-		inf.m_boneTransformsBinding = m_mtl->getBoneTransformsBinding();
-		inf.m_prevFrameBoneTransformsBinding = m_mtl->getPrevFrameBoneTransformsBinding();
-	}
+	const MeshResource& mesh = *getMesh(min<U32>(key.getLod(), m_meshLodCount - 1));
 
 
 	// Vertex attributes & bindings
 	// Vertex attributes & bindings
 	{
 	{
@@ -64,13 +46,13 @@ void ModelPatch::getRenderingInfo(const RenderingKey& key, WeakArray<U8> subMesh
 
 
 		for(VertexAttributeLocation loc = VertexAttributeLocation::FIRST; loc < VertexAttributeLocation::COUNT; ++loc)
 		for(VertexAttributeLocation loc = VertexAttributeLocation::FIRST; loc < VertexAttributeLocation::COUNT; ++loc)
 		{
 		{
-			if(!mesh.isVertexAttributePresent(loc) || !attributeIsRequired(loc, key.getPass(), hasSkin))
+			if(!mesh.isVertexAttributePresent(loc) || !attributeIsRequired(loc, key.getPass(), key.isSkinned()))
 			{
 			{
 				continue;
 				continue;
 			}
 			}
 
 
 			// Attribute
 			// Attribute
-			VertexAttributeInfo& attrib = inf.m_vertexAttributes[inf.m_vertexAttributeCount++];
+			ModelVertexAttribute& attrib = inf.m_vertexAttributes[inf.m_vertexAttributeCount++];
 			attrib.m_location = loc;
 			attrib.m_location = loc;
 			mesh.getVertexAttributeInfo(loc, attrib.m_bufferBinding, attrib.m_format, attrib.m_relativeOffset);
 			mesh.getVertexAttributeInfo(loc, attrib.m_bufferBinding, attrib.m_format, attrib.m_relativeOffset);
 
 
@@ -79,7 +61,7 @@ void ModelPatch::getRenderingInfo(const RenderingKey& key, WeakArray<U8> subMesh
 			{
 			{
 				bufferBindingVisitedMask |= 1 << attrib.m_bufferBinding;
 				bufferBindingVisitedMask |= 1 << attrib.m_bufferBinding;
 
 
-				VertexBufferBinding& binding = inf.m_vertexBufferBindings[inf.m_vertexBufferBindingCount];
+				ModelVertexBufferBinding& binding = inf.m_vertexBufferBindings[inf.m_vertexBufferBindingCount];
 				mesh.getVertexBufferInfo(attrib.m_bufferBinding, binding.m_buffer, binding.m_offset, binding.m_stride);
 				mesh.getVertexBufferInfo(attrib.m_bufferBinding, binding.m_buffer, binding.m_offset, binding.m_stride);
 
 
 				realBufferBindingToVirtual[attrib.m_bufferBinding] = inf.m_vertexBufferBindingCount;
 				realBufferBindingToVirtual[attrib.m_bufferBinding] = inf.m_vertexBufferBindingCount;
@@ -94,14 +76,27 @@ void ModelPatch::getRenderingInfo(const RenderingKey& key, WeakArray<U8> subMesh
 	}
 	}
 
 
 	// Index buff
 	// Index buff
-	U32 indexCount;
-	mesh.getIndexBufferInfo(inf.m_indexBuffer, inf.m_indexBufferOffset, indexCount, inf.m_indexType);
-
-	// Other
-	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;
+	mesh.getIndexBufferInfo(inf.m_indexBuffer, inf.m_indexBufferOffset, inf.m_indexCount, inf.m_indexType);
+
+	// Get program
+	{
+		RenderingKey mtlKey = key;
+		mtlKey.setLod(min(key.getLod(), m_mtl->getLodCount() - 1));
+
+		const MaterialVariant& variant = m_mtl->getOrCreateVariant(mtlKey);
+
+		inf.m_program = variant.getShaderProgram();
+
+		if(m_mtl->supportsSkinning())
+		{
+			inf.m_boneTransformsBinding = m_mtl->getBoneTransformsBinding();
+			inf.m_prevFrameBoneTransformsBinding = m_mtl->getPrevFrameBoneTransformsBinding();
+		}
+		else
+		{
+			inf.m_boneTransformsBinding = inf.m_prevFrameBoneTransformsBinding = MAX_U32;
+		}
+	}
 }
 }
 
 
 void ModelPatch::getRayTracingInfo(U32 lod, ModelRayTracingInfo& info) const
 void ModelPatch::getRayTracingInfo(U32 lod, ModelRayTracingInfo& info) const
@@ -112,7 +107,7 @@ void ModelPatch::getRayTracingInfo(U32 lod, ModelRayTracingInfo& info) const
 	memset(&info.m_descriptor, 0, sizeof(info.m_descriptor));
 	memset(&info.m_descriptor, 0, sizeof(info.m_descriptor));
 
 
 	// Mesh
 	// Mesh
-	const MeshResourcePtr& mesh = m_meshes[min(U32(m_meshCount - 1), lod)];
+	const MeshResourcePtr& mesh = m_meshes[min(U32(m_meshLodCount - 1), lod)];
 	info.m_bottomLevelAccelerationStructure = mesh->getBottomLevelAccelerationStructure();
 	info.m_bottomLevelAccelerationStructure = mesh->getBottomLevelAccelerationStructure();
 	info.m_descriptor.m_mesh = mesh->getMeshGpuDescriptor();
 	info.m_descriptor.m_mesh = mesh->getMeshGpuDescriptor();
 	info.m_grObjectReferences[info.m_grObjectReferenceCount++] = mesh->getIndexBuffer();
 	info.m_grObjectReferences[info.m_grObjectReferenceCount++] = mesh->getIndexBuffer();
@@ -139,11 +134,6 @@ void ModelPatch::getRayTracingInfo(U32 lod, ModelRayTracingInfo& info) const
 	}
 	}
 }
 }
 
 
-U32 ModelPatch::getLodCount() const
-{
-	return max<U32>(m_meshCount, getMaterial()->getLodCount());
-}
-
 Error ModelPatch::init(ModelResource* model, ConstWeakArray<CString> meshFNames, const CString& mtlFName, Bool async,
 Error ModelPatch::init(ModelResource* model, ConstWeakArray<CString> meshFNames, const CString& mtlFName, Bool async,
 					   ResourceManager* manager)
 					   ResourceManager* manager)
 {
 {
@@ -154,7 +144,7 @@ Error ModelPatch::init(ModelResource* model, ConstWeakArray<CString> meshFNames,
 	ANKI_CHECK(manager->loadResource(mtlFName, m_mtl, async));
 	ANKI_CHECK(manager->loadResource(mtlFName, m_mtl, async));
 
 
 	// Load meshes
 	// Load meshes
-	m_meshCount = 0;
+	m_meshLodCount = 0;
 	for(U32 i = 0; i < meshFNames.getSize(); i++)
 	for(U32 i = 0; i < meshFNames.getSize(); i++)
 	{
 	{
 		ANKI_CHECK(manager->loadResource(meshFNames[i], m_meshes[i], async));
 		ANKI_CHECK(manager->loadResource(meshFNames[i], m_meshes[i], async));
@@ -166,7 +156,7 @@ Error ModelPatch::init(ModelResource* model, ConstWeakArray<CString> meshFNames,
 			return Error::USER_DATA;
 			return Error::USER_DATA;
 		}
 		}
 
 
-		++m_meshCount;
+		++m_meshLodCount;
 	}
 	}
 
 
 	return Error::NONE;
 	return Error::NONE;
@@ -231,7 +221,6 @@ Error ModelResource::load(const ResourceFilename& filename, Bool async)
 		Array<CString, 3> meshesFnames;
 		Array<CString, 3> meshesFnames;
 		U32 meshesCount = 1;
 		U32 meshesCount = 1;
 
 
-		// Get mesh
 		XmlElement meshEl;
 		XmlElement meshEl;
 		ANKI_CHECK(modelPatchEl.getChildElement("mesh", meshEl));
 		ANKI_CHECK(modelPatchEl.getChildElement("mesh", meshEl));
 
 
@@ -261,28 +250,25 @@ Error ModelResource::load(const ResourceFilename& filename, Bool async)
 		ANKI_CHECK(m_modelPatches[count].init(this, ConstWeakArray<CString>(&meshesFnames[0], meshesCount), cstr, async,
 		ANKI_CHECK(m_modelPatches[count].init(this, ConstWeakArray<CString>(&meshesFnames[0], meshesCount), cstr, async,
 											  &getManager()));
 											  &getManager()));
 
 
+		if(count > 0 && m_modelPatches[count].supportsSkinning() != m_modelPatches[count - 1].supportsSkinning())
+		{
+			ANKI_RESOURCE_LOGE("All model patches should support skinning or all shouldn't support skinning");
+			return Error::USER_DATA;
+		}
+
+		m_skinning = m_modelPatches[count].supportsSkinning();
+
 		// Move to next
 		// Move to next
 		ANKI_CHECK(modelPatchEl.getNextSiblingElement("modelPatch", modelPatchEl));
 		ANKI_CHECK(modelPatchEl.getNextSiblingElement("modelPatch", modelPatchEl));
+		++count;
 	} while(modelPatchEl);
 	} while(modelPatchEl);
-
-	// <skeleton>
-	XmlElement skeletonEl;
-	ANKI_CHECK(rootEl.getChildElementOptional("skeleton", skeletonEl));
-	if(skeletonEl)
-	{
-		CString fname;
-		ANKI_CHECK(skeletonEl.getText(fname));
-		ANKI_CHECK(getManager().loadResource(fname, m_skeleton));
-	}
+	ANKI_ASSERT(count == m_modelPatches.getSize());
 
 
 	// Calculate compound bounding volume
 	// Calculate compound bounding volume
-	RenderingKey key;
-	key.setLod(0);
-	m_visibilityShape = m_modelPatches[0].getMesh(key).getBoundingShape();
-
+	m_boundingVolume = m_modelPatches[0].m_meshes[0]->getBoundingShape();
 	for(auto it = m_modelPatches.getBegin() + 1; it != m_modelPatches.getEnd(); ++it)
 	for(auto it = m_modelPatches.getBegin() + 1; it != m_modelPatches.getEnd(); ++it)
 	{
 	{
-		m_visibilityShape = m_visibilityShape.getCompoundShape((*it).getMesh(key).getBoundingShape());
+		m_boundingVolume = m_boundingVolume.getCompoundShape((*it).m_meshes[0]->getBoundingShape());
 	}
 	}
 
 
 	return Error::NONE;
 	return Error::NONE;

+ 42 - 65
anki/resource/ModelResource.h

@@ -7,12 +7,10 @@
 
 
 #include <anki/resource/ResourceObject.h>
 #include <anki/resource/ResourceObject.h>
 #include <anki/Gr.h>
 #include <anki/Gr.h>
-#include <anki/collision/Obb.h>
+#include <anki/collision/Aabb.h>
 #include <anki/resource/RenderingKey.h>
 #include <anki/resource/RenderingKey.h>
 #include <anki/resource/MeshResource.h>
 #include <anki/resource/MeshResource.h>
 #include <anki/resource/MaterialResource.h>
 #include <anki/resource/MaterialResource.h>
-#include <anki/resource/SkeletonResource.h>
-#include <anki/resource/AnimationResource.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -20,67 +18,69 @@ namespace anki
 /// @addtogroup resource
 /// @addtogroup resource
 /// @{
 /// @{
 
 
-class VertexBufferBinding
+/// @memberof ModelResource
+class ModelVertexBufferBinding
 {
 {
 public:
 public:
 	BufferPtr m_buffer;
 	BufferPtr m_buffer;
 	PtrSize m_offset;
 	PtrSize m_offset;
 	PtrSize m_stride;
 	PtrSize m_stride;
 
 
-	Bool operator==(const VertexBufferBinding& b) const
+	Bool operator==(const ModelVertexBufferBinding& b) const
 	{
 	{
 		return m_buffer == b.m_buffer && m_offset == b.m_offset && m_stride == b.m_stride;
 		return m_buffer == b.m_buffer && m_offset == b.m_offset && m_stride == b.m_stride;
 	}
 	}
 
 
-	Bool operator!=(const VertexBufferBinding& b) const
+	Bool operator!=(const ModelVertexBufferBinding& b) const
 	{
 	{
 		return !(*this == b);
 		return !(*this == b);
 	}
 	}
 };
 };
 
 
-class VertexAttributeInfo
+/// @memberof ModelResource
+class ModelVertexAttribute
 {
 {
 public:
 public:
-	U32 m_bufferBinding;
 	VertexAttributeLocation m_location;
 	VertexAttributeLocation m_location;
 	Format m_format;
 	Format m_format;
-	PtrSize m_relativeOffset;
+	U32 m_bufferBinding;
+	U32 m_relativeOffset;
 
 
-	Bool operator==(const VertexAttributeInfo& b) const
+	Bool operator==(const ModelVertexAttribute& 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;
 			   && m_location == b.m_location;
 	}
 	}
 
 
-	Bool operator!=(const VertexAttributeInfo& b) const
+	Bool operator!=(const ModelVertexAttribute& b) const
 	{
 	{
 		return !(*this == b);
 		return !(*this == b);
 	}
 	}
 };
 };
 
 
+/// @memberof ModelResource
+/// Part of the information required render the model.
 class ModelRenderingInfo
 class ModelRenderingInfo
 {
 {
 public:
 public:
-	Array<U32, MAX_SUB_DRAWCALLS> m_indicesCountArray;
-	Array<PtrSize, MAX_SUB_DRAWCALLS> m_indicesOffsetArray;
-	U32 m_drawcallCount;
-
 	ShaderProgramPtr m_program;
 	ShaderProgramPtr m_program;
 
 
-	Array<VertexBufferBinding, MAX_VERTEX_ATTRIBUTES> m_vertexBufferBindings;
+	Array<ModelVertexBufferBinding, MAX_VERTEX_ATTRIBUTES> m_vertexBufferBindings;
 	U32 m_vertexBufferBindingCount;
 	U32 m_vertexBufferBindingCount;
-	Array<VertexAttributeInfo, MAX_VERTEX_ATTRIBUTES> m_vertexAttributes;
+	Array<ModelVertexAttribute, MAX_VERTEX_ATTRIBUTES> m_vertexAttributes;
 	U32 m_vertexAttributeCount;
 	U32 m_vertexAttributeCount;
 
 
 	BufferPtr m_indexBuffer;
 	BufferPtr m_indexBuffer;
 	PtrSize m_indexBufferOffset;
 	PtrSize m_indexBufferOffset;
 	IndexType m_indexType;
 	IndexType m_indexType;
+	U32 m_indexCount;
 
 
 	U32 m_boneTransformsBinding;
 	U32 m_boneTransformsBinding;
 	U32 m_prevFrameBoneTransformsBinding;
 	U32 m_prevFrameBoneTransformsBinding;
 };
 };
 
 
 /// Part of the information required to create a TLAS and a SBT.
 /// Part of the information required to create a TLAS and a SBT.
+/// @memberof ModelResource
 class ModelRayTracingInfo
 class ModelRayTracingInfo
 {
 {
 public:
 public:
@@ -104,38 +104,18 @@ public:
 		return m_mtl;
 		return m_mtl;
 	}
 	}
 
 
-	const MeshResource& getMesh(const RenderingKey& key) const
+	const MeshResourcePtr& getMesh(U32 lod) const
 	{
 	{
-		return *m_meshes[key.getLod()];
+		return m_meshes[lod];
 	}
 	}
 
 
-	const ModelResource& getModel() const
-	{
-		ANKI_ASSERT(m_model);
-		return *m_model;
-	}
-
-	const Obb& getBoundingShape() const
+	const Aabb& getBoundingShape() const
 	{
 	{
 		return m_meshes[0]->getBoundingShape();
 		return m_meshes[0]->getBoundingShape();
 	}
 	}
 
 
-	const Obb& getBoundingShapeSub(U32 subMeshId) const
-	{
-		U32 firstIdx, idxCount;
-		const Obb* obb;
-		m_meshes[0]->getSubMeshInfo(subMeshId, firstIdx, idxCount, obb);
-		return *obb;
-	}
-
-	U32 getSubMeshCount() const
-	{
-		return m_meshes[0]->getSubMeshCount();
-	}
-
-	/// Get information for multiDraw rendering. Given an array of submeshes that are visible return the correct indices
-	/// offsets and counts.
-	void getRenderingInfo(const RenderingKey& key, WeakArray<U8> subMeshIndicesArray, ModelRenderingInfo& inf) const;
+	/// Get information for rendering.
+	void getRenderingInfo(const RenderingKey& key, ModelRenderingInfo& inf) const;
 
 
 	/// Get the ray tracing info.
 	/// Get the ray tracing info.
 	void getRayTracingInfo(U32 lod, ModelRayTracingInfo& info) const;
 	void getRayTracingInfo(U32 lod, ModelRayTracingInfo& info) const;
@@ -148,15 +128,18 @@ public:
 private:
 private:
 	ModelResource* m_model ANKI_DEBUG_CODE(= nullptr);
 	ModelResource* m_model ANKI_DEBUG_CODE(= nullptr);
 
 
-	Array<MeshResourcePtr, MAX_LOD_COUNT> m_meshes; ///< One for each LOD
-	U8 m_meshCount = 0;
 	MaterialResourcePtr m_mtl;
 	MaterialResourcePtr m_mtl;
 
 
-	/// Return the maximum number of LODs
-	U32 getLodCount() const;
+	Array<MeshResourcePtr, MAX_LOD_COUNT> m_meshes; ///< One for each LOD
+	U8 m_meshLodCount = 0;
 
 
 	ANKI_USE_RESULT Error init(ModelResource* model, ConstWeakArray<CString> meshFNames, const CString& mtlFName,
 	ANKI_USE_RESULT Error init(ModelResource* model, ConstWeakArray<CString> meshFNames, const CString& mtlFName,
 							   Bool async, ResourceManager* resources);
 							   Bool async, ResourceManager* resources);
+
+	ANKI_USE_RESULT Bool supportsSkinning() const
+	{
+		return m_meshes[0]->hasBoneWeights() && m_mtl->supportsSkinning();
+	}
 };
 };
 
 
 /// Model is an entity that acts as a container for other resources. Models are all the non static objects in a map.
 /// Model is an entity that acts as a container for other resources. Models are all the non static objects in a map.
@@ -165,27 +148,21 @@ private:
 /// @code
 /// @code
 /// <model>
 /// <model>
 /// 	<modelPatches>
 /// 	<modelPatches>
-/// 		<modelPatch>
+/// 		<modelPatch [subMeshIndex=int]>
 /// 			<mesh>path/to/mesh.mesh</mesh>
 /// 			<mesh>path/to/mesh.mesh</mesh>
-///				[<mesh1>path/to/mesh_lod_1.mesh</mesh1>]
-///				[<mesh2>path/to/mesh_lod_2.mesh</mesh2>]
-/// 			<material>path/to/material.mtl</material>
+///				[<mesh1>path/to/mesh_lod_1.ankimesh</mesh1>]
+///				[<mesh2>path/to/mesh_lod_2.ankimesh</mesh2>]
+/// 			<material>path/to/material.ankimtl</material>
 /// 		</modelPatch>
 /// 		</modelPatch>
 /// 		...
 /// 		...
 /// 		<modelPatch>...</modelPatch>
 /// 		<modelPatch>...</modelPatch>
 /// 	</modelPatches>
 /// 	</modelPatches>
-/// 	[<skeleton>path/to/skeleton.skel</skeleton>]
-/// 	[<skeletonAnimations>
-/// 		<animation>path/to/animation.anim</animation>
-/// 		...
-/// 	</skeletonAnimations>]
 /// </model>
 /// </model>
 /// @endcode
 /// @endcode
 ///
 ///
 /// Requirements:
 /// Requirements:
 /// - If the materials need texture coords then mesh should have them
 /// - If the materials need texture coords then mesh should have them
-/// - The skeleton and skelAnims are optional
-/// - Its an error to have skelAnims without skeleton
+/// - If the subMeshIndex is not present then assume the whole mesh
 class ModelResource : public ResourceObject
 class ModelResource : public ResourceObject
 {
 {
 public:
 public:
@@ -193,28 +170,28 @@ public:
 
 
 	~ModelResource();
 	~ModelResource();
 
 
-	const DynamicArray<ModelPatch>& getModelPatches() const
+	ConstWeakArray<ModelPatch> getModelPatches() const
 	{
 	{
 		return m_modelPatches;
 		return m_modelPatches;
 	}
 	}
 
 
-	const Obb& getVisibilityShape() const
+	/// The volume that includes all the geometry of all model patches.
+	const Aabb& getBoundingVolume() const
 	{
 	{
-		return m_visibilityShape;
+		return m_boundingVolume;
 	}
 	}
 
 
-	SkeletonResourcePtr getSkeleton() const
+	Bool supportsSkinning() const
 	{
 	{
-		return m_skeleton;
+		return m_skinning;
 	}
 	}
 
 
 	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
 
 private:
 private:
 	DynamicArray<ModelPatch> m_modelPatches;
 	DynamicArray<ModelPatch> m_modelPatches;
-	Obb m_visibilityShape;
-	SkeletonResourcePtr m_skeleton;
-	DynamicArray<AnimationResourcePtr> m_animations;
+	Aabb m_boundingVolume;
+	Bool m_skinning = false;
 };
 };
 /// @}
 /// @}
 
 

+ 7 - 6
anki/resource/ParticleEmitterResource.cpp

@@ -25,12 +25,6 @@ ANKI_USE_RESULT Error getXmlVal(const XmlElement& el, const CString& tag, Vec3&
 	return el.getAttributeNumbersOptional(tag, out, found);
 	return el.getAttributeNumbersOptional(tag, out, found);
 }
 }
 
 
-ParticleEmitterProperties& ParticleEmitterProperties::operator=(const ParticleEmitterProperties& b)
-{
-	memcpy(this, &b, sizeof(ParticleEmitterProperties));
-	return *this;
-}
-
 ParticleEmitterResource::ParticleEmitterResource(ResourceManager* manager)
 ParticleEmitterResource::ParticleEmitterResource(ResourceManager* manager)
 	: ResourceObject(manager)
 	: ResourceObject(manager)
 {
 {
@@ -80,6 +74,13 @@ Error ParticleEmitterResource::load(const ResourceFilename& filename, Bool async
 		ANKI_CHECK(el.getAttributeNumber("value", m_usePhysicsEngine));
 		ANKI_CHECK(el.getAttributeNumber("value", m_usePhysicsEngine));
 	}
 	}
 
 
+	ANKI_CHECK(rootEl.getChildElementOptional("emitterBoundingVolume", el));
+	if(el)
+	{
+		ANKI_CHECK(el.getAttributeNumbers("min", m_emitterBoundingVolumeMin));
+		ANKI_CHECK(el.getAttributeNumbers("max", m_emitterBoundingVolumeMax));
+	}
+
 	CString cstr;
 	CString cstr;
 	ANKI_CHECK(rootEl.getChildElement("material", el));
 	ANKI_CHECK(rootEl.getChildElement("material", el));
 	ANKI_CHECK(el.getAttributeText("value", cstr));
 	ANKI_CHECK(el.getAttributeText("value", cstr));

+ 19 - 11
anki/resource/ParticleEmitterResource.h

@@ -23,17 +23,6 @@ class XmlElement;
 class ParticleEmitterProperties
 class ParticleEmitterProperties
 {
 {
 public:
 public:
-	ParticleEmitterProperties()
-	{
-	}
-
-	ParticleEmitterProperties(const ParticleEmitterProperties& b)
-	{
-		*this = b;
-	}
-
-	ParticleEmitterProperties& operator=(const ParticleEmitterProperties& b);
-
 	/// @name Particle specific properties
 	/// @name Particle specific properties
 	/// @{
 	/// @{
 	class
 	class
@@ -79,8 +68,27 @@ public:
 	U32 m_particlesPerEmission = 1; ///< How many particles are emitted every emission. Required
 	U32 m_particlesPerEmission = 1; ///< How many particles are emitted every emission. Required
 
 
 	Bool m_usePhysicsEngine = false; ///< Use bullet for the simulation
 	Bool m_usePhysicsEngine = false; ///< Use bullet for the simulation
+
+	Vec3 m_emitterBoundingVolumeMin = Vec3(0.0f); ///< Limit the size of the emitter. Mainly for visibility tests.
+
+	Vec3 m_emitterBoundingVolumeMax = Vec3(0.0f); ///< Limit the size of the emitter. Mainly for visibility tests.
 	/// @}
 	/// @}
 
 
+	ParticleEmitterProperties()
+	{
+	}
+
+	ParticleEmitterProperties(const ParticleEmitterProperties& b)
+	{
+		*this = b;
+	}
+
+	ParticleEmitterProperties& operator=(const ParticleEmitterProperties& b)
+	{
+		memcpy(this, &b, sizeof(*this));
+		return *this;
+	}
+
 	Bool forceEnabled() const
 	Bool forceEnabled() const
 	{
 	{
 		return m_particle.m_maxForceMagnitude > 0.0f;
 		return m_particle.m_maxForceMagnitude > 0.0f;

+ 8 - 1
anki/resource/ResourceManager.cpp

@@ -12,6 +12,7 @@
 
 
 #include <anki/resource/MaterialResource.h>
 #include <anki/resource/MaterialResource.h>
 #include <anki/resource/MeshResource.h>
 #include <anki/resource/MeshResource.h>
+#include <anki/resource/CpuMeshResource.h>
 #include <anki/resource/ModelResource.h>
 #include <anki/resource/ModelResource.h>
 #include <anki/resource/ScriptResource.h>
 #include <anki/resource/ScriptResource.h>
 #include <anki/resource/DummyResource.h>
 #include <anki/resource/DummyResource.h>
@@ -20,7 +21,7 @@
 #include <anki/resource/GenericResource.h>
 #include <anki/resource/GenericResource.h>
 #include <anki/resource/TextureAtlasResource.h>
 #include <anki/resource/TextureAtlasResource.h>
 #include <anki/resource/ShaderProgramResource.h>
 #include <anki/resource/ShaderProgramResource.h>
-#include <anki/resource/CollisionResource.h>
+#include <anki/resource/SkeletonResource.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -99,6 +100,9 @@ Error ResourceManager::loadResource(const CString& filename, ResourcePtr<T>& out
 		T* ptr = m_alloc.newInstance<T>(this);
 		T* ptr = m_alloc.newInstance<T>(this);
 		ANKI_ASSERT(ptr->getRefcount().load() == 0);
 		ANKI_ASSERT(ptr->getRefcount().load() == 0);
 
 
+		// Increment the refcount in that case where async jobs increment it and decrement it in the scope of a load()
+		ptr->getRefcount().fetchAdd(1);
+
 		// Populate the ptr. Use a block to cleanup temp_pool allocations
 		// Populate the ptr. Use a block to cleanup temp_pool allocations
 		auto& pool = m_tmpAlloc.getMemoryPool();
 		auto& pool = m_tmpAlloc.getMemoryPool();
 
 
@@ -130,6 +134,9 @@ Error ResourceManager::loadResource(const CString& filename, ResourcePtr<T>& out
 		// Register resource
 		// Register resource
 		registerResource(ptr);
 		registerResource(ptr);
 		out.reset(ptr);
 		out.reset(ptr);
+
+		// Decrement because of the increment happened a few lines above
+		ptr->getRefcount().fetchSub(1);
 	}
 	}
 
 
 	return err;
 	return err;

+ 0 - 1
anki/resource/ResourceManager.h

@@ -49,7 +49,6 @@ protected:
 
 
 	void registerResource(Type* ptr)
 	void registerResource(Type* ptr)
 	{
 	{
-		ANKI_ASSERT(ptr->getRefcount().load() == 0);
 		ANKI_ASSERT(find(ptr->getFilename()) == m_ptrs.getEnd());
 		ANKI_ASSERT(find(ptr->getFilename()) == m_ptrs.getEnd());
 		m_ptrs.pushBack(m_alloc, ptr);
 		m_ptrs.pushBack(m_alloc, ptr);
 	}
 	}

+ 12 - 30
anki/scene/BodyNode.cpp

@@ -17,9 +17,11 @@ namespace anki
 /// Body feedback component.
 /// Body feedback component.
 class BodyNode::FeedbackComponent : public SceneComponent
 class BodyNode::FeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(BodyNode::FeedbackComponent)
+
 public:
 public:
-	FeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	FeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
 	}
 	}
 
 
@@ -32,47 +34,27 @@ public:
 		if(bodyc.getTimestamp() == node.getGlobalTimestamp())
 		if(bodyc.getTimestamp() == node.getGlobalTimestamp())
 		{
 		{
 			MoveComponent& move = node.getFirstComponentOfType<MoveComponent>();
 			MoveComponent& move = node.getFirstComponentOfType<MoveComponent>();
-			move.setLocalTransform(bodyc.getTransform());
+			move.setLocalTransform(bodyc.getWorldTransform());
 		}
 		}
 
 
 		return Error::NONE;
 		return Error::NONE;
 	}
 	}
 };
 };
 
 
+ANKI_SCENE_COMPONENT_STATICS(BodyNode::FeedbackComponent)
+
 BodyNode::BodyNode(SceneGraph* scene, CString name)
 BodyNode::BodyNode(SceneGraph* scene, CString name)
 	: SceneNode(scene, name)
 	: SceneNode(scene, name)
 {
 {
+	newComponent<JointComponent>();
+	newComponent<BodyComponent>();
+	newComponent<FeedbackComponent>();
+	MoveComponent* movec = newComponent<MoveComponent>();
+	movec->setIgnoreParentTransform(true);
 }
 }
 
 
 BodyNode::~BodyNode()
 BodyNode::~BodyNode()
 {
 {
 }
 }
 
 
-Error BodyNode::init(const CString& resourceFname)
-{
-	// Load resource
-	ANKI_CHECK(getResourceManager().loadResource(resourceFname, m_rsrc));
-
-	// Create body
-	PhysicsBodyInitInfo init;
-	init.m_mass = 1.0f;
-	init.m_shape = m_rsrc->getShape();
-	m_body = getSceneGraph().getPhysicsWorld().newInstance<PhysicsBody>(init);
-	m_body->setUserData(this);
-
-	// Joint component
-	newComponent<JointComponent>(this);
-
-	// Body component
-	newComponent<BodyComponent>(m_body);
-
-	// Feedback component
-	newComponent<FeedbackComponent>();
-
-	// Move component
-	newComponent<MoveComponent>(MoveComponentFlag::IGNORE_PARENT_TRANSFORM);
-
-	return Error::NONE;
-}
-
 } // end namespace anki
 } // end namespace anki

+ 0 - 6
anki/scene/BodyNode.h

@@ -6,7 +6,6 @@
 #pragma once
 #pragma once
 
 
 #include <anki/scene/SceneNode.h>
 #include <anki/scene/SceneNode.h>
-#include <anki/resource/CollisionResource.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -22,13 +21,8 @@ public:
 
 
 	~BodyNode();
 	~BodyNode();
 
 
-	ANKI_USE_RESULT Error init(const CString& resourceFname);
-
 private:
 private:
 	class FeedbackComponent;
 	class FeedbackComponent;
-
-	CollisionResourcePtr m_rsrc;
-	PhysicsBodyPtr m_body;
 };
 };
 /// @}
 /// @}
 
 

+ 14 - 60
anki/scene/CameraNode.cpp

@@ -15,9 +15,11 @@ namespace anki
 /// Feedback component.
 /// Feedback component.
 class CameraNode::MoveFeedbackComponent : public SceneComponent
 class CameraNode::MoveFeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(CameraNode::MoveFeedbackComponent)
+
 public:
 public:
-	MoveFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	MoveFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
 	}
 	}
 
 
@@ -36,29 +38,7 @@ public:
 	}
 	}
 };
 };
 
 
-/// Feedback component.
-class CameraNode::FrustumFeedbackComponent : public SceneComponent
-{
-public:
-	FrustumFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
-	{
-	}
-
-	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
-	{
-		updated = false;
-
-		FrustumComponent& fr = node.getFirstComponentOfType<FrustumComponent>();
-		if(fr.getTimestamp() == node.getGlobalTimestamp())
-		{
-			CameraNode& cam = static_cast<CameraNode&>(node);
-			cam.onFrustumComponentUpdate(fr);
-		}
-
-		return Error::NONE;
-	}
-};
+ANKI_SCENE_COMPONENT_STATICS(CameraNode::MoveFeedbackComponent)
 
 
 CameraNode::CameraNode(SceneGraph* scene, CString name)
 CameraNode::CameraNode(SceneGraph* scene, CString name)
 	: SceneNode(scene, name)
 	: SceneNode(scene, name)
@@ -69,7 +49,7 @@ CameraNode::~CameraNode()
 {
 {
 }
 }
 
 
-Error CameraNode::init(FrustumType frustumType)
+void CameraNode::initCommon(FrustumType frustumType)
 {
 {
 	// Move component
 	// Move component
 	newComponent<MoveComponent>();
 	newComponent<MoveComponent>();
@@ -78,7 +58,8 @@ Error CameraNode::init(FrustumType frustumType)
 	newComponent<MoveFeedbackComponent>();
 	newComponent<MoveFeedbackComponent>();
 
 
 	// Frustum component
 	// Frustum component
-	FrustumComponent* frc = newComponent<FrustumComponent>(this, frustumType);
+	FrustumComponent* frc = newComponent<FrustumComponent>();
+	frc->setFrustumType(frustumType);
 	const FrustumComponentVisibilityTestFlag visibilityFlags =
 	const FrustumComponentVisibilityTestFlag visibilityFlags =
 		FrustumComponentVisibilityTestFlag::RENDER_COMPONENTS | FrustumComponentVisibilityTestFlag::LIGHT_COMPONENTS
 		FrustumComponentVisibilityTestFlag::RENDER_COMPONENTS | FrustumComponentVisibilityTestFlag::LIGHT_COMPONENTS
 		| FrustumComponentVisibilityTestFlag::LENS_FLARE_COMPONENTS
 		| FrustumComponentVisibilityTestFlag::LENS_FLARE_COMPONENTS
@@ -96,7 +77,8 @@ Error CameraNode::init(FrustumType frustumType)
 	// Extended frustum for RT
 	// Extended frustum for RT
 	if(getSceneGraph().getConfig().m_rayTracedShadows)
 	if(getSceneGraph().getConfig().m_rayTracedShadows)
 	{
 	{
-		FrustumComponent* rtFrustumComponent = newComponent<FrustumComponent>(this, FrustumType::ORTHOGRAPHIC);
+		FrustumComponent* rtFrustumComponent = newComponent<FrustumComponent>();
+		rtFrustumComponent->setFrustumType(FrustumType::ORTHOGRAPHIC);
 		rtFrustumComponent->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::RAY_TRACING_SHADOWS);
 		rtFrustumComponent->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::RAY_TRACING_SHADOWS);
 
 
 		const F32 dist = getSceneGraph().getConfig().m_rayTracingExtendedFrustumDistance;
 		const F32 dist = getSceneGraph().getConfig().m_rayTracingExtendedFrustumDistance;
@@ -106,31 +88,6 @@ Error CameraNode::init(FrustumType frustumType)
 		rtFrustumComponent->setLodDistance(1, getSceneGraph().getConfig().m_maxLodDistances[1]);
 		rtFrustumComponent->setLodDistance(1, getSceneGraph().getConfig().m_maxLodDistances[1]);
 		rtFrustumComponent->setLodDistance(2, getSceneGraph().getConfig().m_maxLodDistances[2]);
 		rtFrustumComponent->setLodDistance(2, getSceneGraph().getConfig().m_maxLodDistances[2]);
 	}
 	}
-
-	// Feedback component #2
-	newComponent<FrustumFeedbackComponent>();
-
-	// Spatial component
-	SpatialComponent* spatialc;
-	if(frustumType == FrustumType::PERSPECTIVE)
-	{
-		spatialc = newComponent<SpatialComponent>(this, &frc->getPerspectiveBoundingShape());
-	}
-	else
-	{
-		spatialc = newComponent<SpatialComponent>(this, &frc->getOrthographicBoundingShape());
-	}
-
-	spatialc->setUpdateOctreeBounds(false);
-
-	return Error::NONE;
-}
-
-void CameraNode::onFrustumComponentUpdate(FrustumComponent& fr)
-{
-	// Spatial
-	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
-	sp.markForUpdate();
 }
 }
 
 
 void CameraNode::onMoveComponentUpdate(MoveComponent& move)
 void CameraNode::onMoveComponentUpdate(MoveComponent& move)
@@ -142,7 +99,7 @@ void CameraNode::onMoveComponentUpdate(MoveComponent& move)
 	const Error err = iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& fc) {
 	const Error err = iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& fc) {
 		if(count == 0)
 		if(count == 0)
 		{
 		{
-			fc.setTransform(worldTransform);
+			fc.setWorldTransform(worldTransform);
 		}
 		}
 		else
 		else
 		{
 		{
@@ -153,23 +110,19 @@ void CameraNode::onMoveComponentUpdate(MoveComponent& move)
 			Vec3 newOrigin = worldTransform.getOrigin().xyz();
 			Vec3 newOrigin = worldTransform.getOrigin().xyz();
 			newOrigin.z() += far / 2.0f;
 			newOrigin.z() += far / 2.0f;
 			extendedFrustumTransform.setOrigin(newOrigin.xyz0());
 			extendedFrustumTransform.setOrigin(newOrigin.xyz0());
-			fc.setTransform(extendedFrustumTransform);
+			fc.setWorldTransform(extendedFrustumTransform);
 		}
 		}
 
 
 		++count;
 		++count;
 		return Error::NONE;
 		return Error::NONE;
 	});
 	});
 	(void)err;
 	(void)err;
-
-	// Spatial
-	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
-	sp.setSpatialOrigin(move.getWorldTransform().getOrigin());
-	sp.markForUpdate();
 }
 }
 
 
 PerspectiveCameraNode::PerspectiveCameraNode(SceneGraph* scene, CString name)
 PerspectiveCameraNode::PerspectiveCameraNode(SceneGraph* scene, CString name)
 	: CameraNode(scene, name)
 	: CameraNode(scene, name)
 {
 {
+	initCommon(FrustumType::PERSPECTIVE);
 }
 }
 
 
 PerspectiveCameraNode::~PerspectiveCameraNode()
 PerspectiveCameraNode::~PerspectiveCameraNode()
@@ -179,6 +132,7 @@ PerspectiveCameraNode::~PerspectiveCameraNode()
 OrthographicCameraNode::OrthographicCameraNode(SceneGraph* scene, CString name)
 OrthographicCameraNode::OrthographicCameraNode(SceneGraph* scene, CString name)
 	: CameraNode(scene, name)
 	: CameraNode(scene, name)
 {
 {
+	initCommon(FrustumType::ORTHOGRAPHIC);
 }
 }
 
 
 OrthographicCameraNode::~OrthographicCameraNode()
 OrthographicCameraNode::~OrthographicCameraNode()

+ 1 - 15
anki/scene/CameraNode.h

@@ -23,17 +23,13 @@ public:
 	~CameraNode();
 	~CameraNode();
 
 
 protected:
 protected:
-	ANKI_USE_RESULT Error init(FrustumType frustumType);
+	void initCommon(FrustumType frustumType);
 
 
 private:
 private:
 	class MoveFeedbackComponent;
 	class MoveFeedbackComponent;
-	class FrustumFeedbackComponent;
 
 
 	/// Called when moved.
 	/// Called when moved.
 	void onMoveComponentUpdate(MoveComponent& move);
 	void onMoveComponentUpdate(MoveComponent& move);
-
-	/// Called when something changed in the frustum.
-	void onFrustumComponentUpdate(FrustumComponent& fr);
 };
 };
 
 
 /// Perspective camera
 /// Perspective camera
@@ -43,11 +39,6 @@ public:
 	PerspectiveCameraNode(SceneGraph* scene, CString name);
 	PerspectiveCameraNode(SceneGraph* scene, CString name);
 
 
 	~PerspectiveCameraNode();
 	~PerspectiveCameraNode();
-
-	ANKI_USE_RESULT Error init()
-	{
-		return CameraNode::init(FrustumType::PERSPECTIVE);
-	}
 };
 };
 
 
 /// Orthographic camera
 /// Orthographic camera
@@ -57,11 +48,6 @@ public:
 	OrthographicCameraNode(SceneGraph* scene, CString name);
 	OrthographicCameraNode(SceneGraph* scene, CString name);
 
 
 	~OrthographicCameraNode();
 	~OrthographicCameraNode();
-
-	ANKI_USE_RESULT Error init()
-	{
-		return CameraNode::init(FrustumType::ORTHOGRAPHIC);
-	}
 };
 };
 /// @}
 /// @}
 
 

+ 3 - 11
anki/scene/Common.h

@@ -12,22 +12,14 @@
 namespace anki
 namespace anki
 {
 {
 
 
+/// @addtogroup scene
+/// @{
+
 #define ANKI_SCENE_LOGI(...) ANKI_LOG("SCEN", NORMAL, __VA_ARGS__)
 #define ANKI_SCENE_LOGI(...) ANKI_LOG("SCEN", NORMAL, __VA_ARGS__)
 #define ANKI_SCENE_LOGE(...) ANKI_LOG("SCEN", ERROR, __VA_ARGS__)
 #define ANKI_SCENE_LOGE(...) ANKI_LOG("SCEN", ERROR, __VA_ARGS__)
 #define ANKI_SCENE_LOGW(...) ANKI_LOG("SCEN", WARNING, __VA_ARGS__)
 #define ANKI_SCENE_LOGW(...) ANKI_LOG("SCEN", WARNING, __VA_ARGS__)
 #define ANKI_SCENE_LOGF(...) ANKI_LOG("SCEN", FATAL, __VA_ARGS__)
 #define ANKI_SCENE_LOGF(...) ANKI_LOG("SCEN", FATAL, __VA_ARGS__)
 
 
-// Forward
-class SceneGraph;
-class SceneNode;
-class MoveComponent;
-class DecalComponent;
-class EventManager;
-class Event;
-
-/// @addtogroup Scene
-/// @{
-
 /// The type of the scene's allocator
 /// The type of the scene's allocator
 template<typename T>
 template<typename T>
 using SceneAllocator = HeapAllocator<T>;
 using SceneAllocator = HeapAllocator<T>;

+ 117 - 284
anki/scene/DebugDrawer.cpp

@@ -8,236 +8,12 @@
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/core/StagingGpuMemoryManager.h>
 #include <anki/core/StagingGpuMemoryManager.h>
 #include <anki/physics/PhysicsWorld.h>
 #include <anki/physics/PhysicsWorld.h>
+#include <anki/gr/Buffer.h>
 #include <anki/Collision.h>
 #include <anki/Collision.h>
 
 
 namespace anki
 namespace anki
 {
 {
 
 
-Error DebugDrawer::init(ResourceManager* rsrcManager)
-{
-	ANKI_ASSERT(rsrcManager);
-
-	// Create the prog and shaders
-	ANKI_CHECK(rsrcManager->loadResource("shaders/SceneDebug.ankiprog", m_prog));
-
-	return Error::NONE;
-}
-
-void DebugDrawer::prepareFrame(RenderQueueDrawContext* ctx)
-{
-	m_ctx = ctx;
-}
-
-void DebugDrawer::flush()
-{
-	if(m_cachedPositionCount == 0)
-	{
-		return;
-	}
-
-	CommandBufferPtr& cmdb = m_ctx->m_commandBuffer;
-
-	// Bind program
-	{
-		ShaderProgramResourceVariantInitInfo variantInitInfo(m_prog);
-		variantInitInfo.addMutation("COLOR_TEXTURE", 0);
-		variantInitInfo.addMutation("DITHERED_DEPTH_TEST",
-									m_ctx->m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON));
-		variantInitInfo.addConstant("INSTANCE_COUNT", 1u);
-		const ShaderProgramResourceVariant* variant;
-		m_prog->getOrCreateVariant(variantInitInfo, variant);
-		cmdb->bindShaderProgram(variant->getProgram());
-	}
-
-	// Set vertex state
-	{
-		const U32 size = m_cachedPositionCount * sizeof(Vec3);
-
-		StagingGpuMemoryToken token;
-		void* mem = m_ctx->m_stagingGpuAllocator->allocateFrame(size, StagingGpuMemoryType::VERTEX, token);
-		memcpy(mem, &m_cachedPositions[0], size);
-
-		cmdb->bindVertexBuffer(0, token.m_buffer, token.m_offset, sizeof(Vec3));
-		cmdb->setVertexAttribute(0, 0, Format::R32G32B32_SFLOAT, 0);
-	}
-
-	// Set uniform state
-	{
-		struct Uniforms
-		{
-			Mat4 m_mvp;
-			Vec4 m_color;
-		};
-
-		StagingGpuMemoryToken token;
-		Uniforms* uniforms = static_cast<Uniforms*>(
-			m_ctx->m_stagingGpuAllocator->allocateFrame(sizeof(Uniforms), StagingGpuMemoryType::UNIFORM, token));
-		uniforms->m_mvp = m_mvpMat;
-		uniforms->m_color = m_crntCol;
-
-		cmdb->bindUniformBuffer(1, 0, token.m_buffer, token.m_offset, token.m_range);
-	}
-
-	const Bool enableDepthTest = m_ctx->m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
-	if(enableDepthTest)
-	{
-		cmdb->setDepthCompareOperation(CompareOperation::LESS);
-	}
-	else
-	{
-		cmdb->setDepthCompareOperation(CompareOperation::ALWAYS);
-	}
-
-	// Draw
-	cmdb->setLineWidth(1.0f);
-	cmdb->drawArrays(m_topology, m_cachedPositionCount);
-
-	// Restore state
-	if(!enableDepthTest)
-	{
-		cmdb->setDepthCompareOperation(CompareOperation::LESS);
-	}
-
-	// Other
-	m_cachedPositionCount = 0;
-}
-
-void DebugDrawer::drawLine(const Vec3& from, const Vec3& to, const Vec4& color)
-{
-	setColor(color);
-	setTopology(PrimitiveTopology::LINES);
-	pushBackVertex(from);
-	pushBackVertex(to);
-}
-
-void DebugDrawer::drawGrid()
-{
-	Vec4 col0(0.5, 0.5, 0.5, 1.0);
-	Vec4 col1(0.0, 0.0, 1.0, 1.0);
-	Vec4 col2(1.0, 0.0, 0.0, 1.0);
-
-	const F32 SPACE = 1.0f; // space between lines
-	const F32 NUM = 57.0f; // lines number. must be odd
-
-	const F32 GRID_HALF_SIZE = ((NUM - 1.0f) * SPACE / 2.0f);
-
-	setColor(col0);
-	setTopology(PrimitiveTopology::LINES);
-
-	for(F32 x = -NUM / 2.0f * SPACE; x < NUM / 2 * SPACE; x += SPACE)
-	{
-		setColor(col0);
-
-		// if the middle line then change color
-		if(x == 0)
-		{
-			setColor(col0 * 0.5 + col1 * 0.5);
-			pushBackVertex(Vec3(x, 0.0, -GRID_HALF_SIZE));
-			pushBackVertex(Vec3(x, 0.0, 0.0));
-
-			setColor(col1);
-			pushBackVertex(Vec3(x, 0.0, 0.0));
-			pushBackVertex(Vec3(x, 0.0, GRID_HALF_SIZE));
-		}
-		else
-		{
-			// line in z
-			pushBackVertex(Vec3(x, 0.0, -GRID_HALF_SIZE));
-			pushBackVertex(Vec3(x, 0.0, GRID_HALF_SIZE));
-		}
-
-		// if middle line change col so you can highlight the x-axis
-		if(x == 0)
-		{
-			setColor(col0 * 0.5 + col2 * 0.5);
-			pushBackVertex(Vec3(-GRID_HALF_SIZE, 0.0, x));
-			pushBackVertex(Vec3(0.0, 0.0, x));
-
-			setColor(col2);
-			pushBackVertex(Vec3(0.0, 0.0, x));
-			pushBackVertex(Vec3(GRID_HALF_SIZE, 0.0, x));
-		}
-		else
-		{
-			// line in the x
-			pushBackVertex(Vec3(-GRID_HALF_SIZE, 0.0, x));
-			pushBackVertex(Vec3(GRID_HALF_SIZE, 0.0, x));
-		}
-	}
-}
-
-void DebugDrawer::drawSphere(F32 radius, I complexity)
-{
-	Mat4 oldMMat = m_mMat;
-
-	setModelMatrix(m_mMat * Mat4(Vec4(0.0, 0.0, 0.0, 1.0), Mat3::getIdentity(), radius));
-	setTopology(PrimitiveTopology::LINES);
-
-	// Pre-calculate the sphere points5
-	F32 fi = PI / F32(complexity);
-
-	Vec3 prev(1.0, 0.0, 0.0);
-	for(F32 th = fi; th < PI * 2.0 + fi; th += fi)
-	{
-		Vec3 p = Mat3(Euler(0.0, th, 0.0)) * Vec3(1.0, 0.0, 0.0);
-
-		for(F32 th2 = 0.0; th2 < PI; th2 += fi)
-		{
-			Mat3 rot(Euler(th2, 0.0, 0.0));
-
-			Vec3 rotPrev = rot * prev;
-			Vec3 rotP = rot * p;
-
-			pushBackVertex(rotPrev);
-			pushBackVertex(rotP);
-
-			Mat3 rot2(Euler(0.0, 0.0, PI / 2));
-
-			pushBackVertex(rot2 * rotPrev);
-			pushBackVertex(rot2 * rotP);
-		}
-
-		prev = p;
-	}
-
-	setModelMatrix(oldMMat);
-}
-
-void DebugDrawer::drawCube(F32 size)
-{
-	const Vec3 maxPos = Vec3(0.5f * size);
-	const Vec3 minPos = Vec3(-0.5f * size);
-
-	Array<Vec3, 8> points = {
-		Vec3(maxPos.x(), maxPos.y(), maxPos.z()), // right top front
-		Vec3(minPos.x(), maxPos.y(), maxPos.z()), // left top front
-		Vec3(minPos.x(), minPos.y(), maxPos.z()), // left bottom front
-		Vec3(maxPos.x(), minPos.y(), maxPos.z()), // right bottom front
-		Vec3(maxPos.x(), maxPos.y(), minPos.z()), // right top back
-		Vec3(minPos.x(), maxPos.y(), minPos.z()), // left top back
-		Vec3(minPos.x(), minPos.y(), minPos.z()), // left bottom back
-		Vec3(maxPos.x(), minPos.y(), minPos.z()) // right bottom back
-	};
-
-	static const Array<U32, 24> indeces = {0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7};
-
-	setTopology(PrimitiveTopology::LINES);
-	for(U32 id : indeces)
-	{
-		pushBackVertex(points[id]);
-	}
-}
-
-void PhysicsDebugDrawer::drawLines(const Vec3* lines, const U32 vertCount, const Vec4& color)
-{
-	m_dbg->setTopology(PrimitiveTopology::LINES);
-	m_dbg->setColor(color);
-	for(U i = 0; i < vertCount; ++i)
-	{
-		m_dbg->pushBackVertex(lines[i]);
-	}
-}
-
 void allocateAndPopulateDebugBox(StagingGpuMemoryManager& stagingGpuAllocator, StagingGpuMemoryToken& vertsToken,
 void allocateAndPopulateDebugBox(StagingGpuMemoryManager& stagingGpuAllocator, StagingGpuMemoryToken& vertsToken,
 								 StagingGpuMemoryToken& indicesToken, U32& indexCount)
 								 StagingGpuMemoryToken& indicesToken, U32& indexCount)
 {
 {
@@ -292,67 +68,92 @@ void allocateAndPopulateDebugBox(StagingGpuMemoryManager& stagingGpuAllocator, S
 
 
 Error DebugDrawer2::init(ResourceManager* rsrcManager)
 Error DebugDrawer2::init(ResourceManager* rsrcManager)
 {
 {
-	return rsrcManager->loadResource("shaders/SceneDebug.ankiprog", m_prog);
+	ANKI_CHECK(rsrcManager->loadResource("shaders/SceneDebug.ankiprog", m_prog));
+
+	{
+		BufferInitInfo bufferInit("DebugCube");
+		bufferInit.m_usage = BufferUsageBit::VERTEX;
+		bufferInit.m_size = sizeof(Vec3) * 8;
+		bufferInit.m_mapAccess = BufferMapAccessBit::WRITE;
+		m_cubePositionsBuffer = rsrcManager->getGrManager().newBuffer(bufferInit);
+
+		Vec3* verts = static_cast<Vec3*>(m_cubePositionsBuffer->map(0, MAX_PTR_SIZE, BufferMapAccessBit::WRITE));
+
+		const F32 size = 1.0f;
+		verts[0] = Vec3(size, size, size); // front top right
+		verts[1] = Vec3(-size, size, size); // front top left
+		verts[2] = Vec3(-size, -size, size); // front bottom left
+		verts[3] = Vec3(size, -size, size); // front bottom right
+		verts[4] = Vec3(size, size, -size); // back top right
+		verts[5] = Vec3(-size, size, -size); // back top left
+		verts[6] = Vec3(-size, -size, -size); // back bottom left
+		verts[7] = Vec3(size, -size, -size); // back bottom right
+
+		m_cubePositionsBuffer->unmap();
+	}
+
+	{
+		constexpr U INDEX_COUNT = 12 * 2;
+
+		BufferInitInfo bufferInit("DebugCube");
+		bufferInit.m_usage = BufferUsageBit::VERTEX;
+		bufferInit.m_size = sizeof(U16) * INDEX_COUNT;
+		bufferInit.m_mapAccess = BufferMapAccessBit::WRITE;
+		m_cubeIndicesBuffer = rsrcManager->getGrManager().newBuffer(bufferInit);
+
+		U16* indices = static_cast<U16*>(m_cubeIndicesBuffer->map(0, MAX_PTR_SIZE, BufferMapAccessBit::WRITE));
+
+		U32 indexCount = 0;
+		indices[indexCount++] = 0;
+		indices[indexCount++] = 1;
+		indices[indexCount++] = 1;
+		indices[indexCount++] = 2;
+		indices[indexCount++] = 2;
+		indices[indexCount++] = 3;
+		indices[indexCount++] = 3;
+		indices[indexCount++] = 0;
+
+		indices[indexCount++] = 4;
+		indices[indexCount++] = 5;
+		indices[indexCount++] = 5;
+		indices[indexCount++] = 6;
+		indices[indexCount++] = 6;
+		indices[indexCount++] = 7;
+		indices[indexCount++] = 7;
+		indices[indexCount++] = 4;
+
+		indices[indexCount++] = 0;
+		indices[indexCount++] = 4;
+		indices[indexCount++] = 1;
+		indices[indexCount++] = 5;
+		indices[indexCount++] = 2;
+		indices[indexCount++] = 6;
+		indices[indexCount++] = 3;
+		indices[indexCount++] = 7;
+
+		m_cubeIndicesBuffer->unmap();
+	}
+
+	return Error::NONE;
 }
 }
 
 
 void DebugDrawer2::drawCubes(ConstWeakArray<Mat4> mvps, const Vec4& color, F32 lineSize, Bool ditherFailedDepth,
 void DebugDrawer2::drawCubes(ConstWeakArray<Mat4> mvps, const Vec4& color, F32 lineSize, Bool ditherFailedDepth,
 							 F32 cubeSideSize, StagingGpuMemoryManager& stagingGpuAllocator,
 							 F32 cubeSideSize, StagingGpuMemoryManager& stagingGpuAllocator,
 							 CommandBufferPtr& cmdb) const
 							 CommandBufferPtr& cmdb) const
 {
 {
-	StagingGpuMemoryToken vertsToken;
-	StagingGpuMemoryToken indicesToken;
-
-	Vec3* verts = static_cast<Vec3*>(
-		stagingGpuAllocator.allocateFrame(sizeof(Vec3) * 8, StagingGpuMemoryType::VERTEX, vertsToken));
-
-	const F32 size = cubeSideSize / 2.0f;
-	verts[0] = Vec3(size, size, size); // front top right
-	verts[1] = Vec3(-size, size, size); // front top left
-	verts[2] = Vec3(-size, -size, size); // front bottom left
-	verts[3] = Vec3(size, -size, size); // front bottom right
-	verts[4] = Vec3(size, size, -size); // back top right
-	verts[5] = Vec3(-size, size, -size); // back top left
-	verts[6] = Vec3(-size, -size, -size); // back bottom left
-	verts[7] = Vec3(size, -size, -size); // back bottom right
-
-	const U INDEX_COUNT = 12 * 2;
-	U16* indices = static_cast<U16*>(
-		stagingGpuAllocator.allocateFrame(sizeof(U16) * INDEX_COUNT, StagingGpuMemoryType::VERTEX, indicesToken));
-
-	U32 indexCount = 0;
-	indices[indexCount++] = 0;
-	indices[indexCount++] = 1;
-	indices[indexCount++] = 1;
-	indices[indexCount++] = 2;
-	indices[indexCount++] = 2;
-	indices[indexCount++] = 3;
-	indices[indexCount++] = 3;
-	indices[indexCount++] = 0;
-
-	indices[indexCount++] = 4;
-	indices[indexCount++] = 5;
-	indices[indexCount++] = 5;
-	indices[indexCount++] = 6;
-	indices[indexCount++] = 6;
-	indices[indexCount++] = 7;
-	indices[indexCount++] = 7;
-	indices[indexCount++] = 4;
-
-	indices[indexCount++] = 0;
-	indices[indexCount++] = 4;
-	indices[indexCount++] = 1;
-	indices[indexCount++] = 5;
-	indices[indexCount++] = 2;
-	indices[indexCount++] = 6;
-	indices[indexCount++] = 3;
-	indices[indexCount++] = 7;
-
 	// Set the uniforms
 	// Set the uniforms
 	StagingGpuMemoryToken unisToken;
 	StagingGpuMemoryToken unisToken;
 	Mat4* pmvps = static_cast<Mat4*>(stagingGpuAllocator.allocateFrame(sizeof(Mat4) * mvps.getSize() + sizeof(Vec4),
 	Mat4* pmvps = static_cast<Mat4*>(stagingGpuAllocator.allocateFrame(sizeof(Mat4) * mvps.getSize() + sizeof(Vec4),
 																	   StagingGpuMemoryType::UNIFORM, unisToken));
 																	   StagingGpuMemoryType::UNIFORM, unisToken));
 
 
-	memcpy(pmvps, &mvps[0], mvps.getSizeInBytes());
+	if(cubeSideSize == 2.0f)
+	{
+		memcpy(pmvps, &mvps[0], mvps.getSizeInBytes());
+	}
+	else
+	{
+		ANKI_ASSERT(!"TODO");
+	}
 
 
 	Vec4* pcolor = reinterpret_cast<Vec4*>(pmvps + mvps.getSize());
 	Vec4* pcolor = reinterpret_cast<Vec4*>(pmvps + mvps.getSize());
 	*pcolor = color;
 	*pcolor = color;
@@ -367,27 +168,28 @@ void DebugDrawer2::drawCubes(ConstWeakArray<Mat4> mvps, const Vec4& color, F32 l
 	cmdb->bindShaderProgram(variant->getProgram());
 	cmdb->bindShaderProgram(variant->getProgram());
 
 
 	cmdb->setVertexAttribute(0, 0, Format::R32G32B32_SFLOAT, 0);
 	cmdb->setVertexAttribute(0, 0, Format::R32G32B32_SFLOAT, 0);
-	cmdb->bindVertexBuffer(0, vertsToken.m_buffer, vertsToken.m_offset, sizeof(Vec3));
-	cmdb->bindIndexBuffer(indicesToken.m_buffer, indicesToken.m_offset, IndexType::U16);
+	cmdb->bindVertexBuffer(0, m_cubePositionsBuffer, 0, sizeof(Vec3));
+	cmdb->bindIndexBuffer(m_cubeIndicesBuffer, 0, IndexType::U16);
 
 
 	cmdb->bindUniformBuffer(1, 0, unisToken.m_buffer, unisToken.m_offset, unisToken.m_range);
 	cmdb->bindUniformBuffer(1, 0, unisToken.m_buffer, unisToken.m_offset, unisToken.m_range);
 
 
 	cmdb->setLineWidth(lineSize);
 	cmdb->setLineWidth(lineSize);
-	cmdb->drawElements(PrimitiveTopology::LINES, indexCount, mvps.getSize());
+	constexpr U INDEX_COUNT = 12 * 2;
+	cmdb->drawElements(PrimitiveTopology::LINES, INDEX_COUNT, mvps.getSize());
 }
 }
 
 
 void DebugDrawer2::drawLines(ConstWeakArray<Mat4> mvps, const Vec4& color, F32 lineSize, Bool ditherFailedDepth,
 void DebugDrawer2::drawLines(ConstWeakArray<Mat4> mvps, const Vec4& color, F32 lineSize, Bool ditherFailedDepth,
-							 ConstWeakArray<Vec3> lines, StagingGpuMemoryManager& stagingGpuAllocator,
+							 ConstWeakArray<Vec3> linePositions, StagingGpuMemoryManager& stagingGpuAllocator,
 							 CommandBufferPtr& cmdb) const
 							 CommandBufferPtr& cmdb) const
 {
 {
 	ANKI_ASSERT(mvps.getSize() > 0);
 	ANKI_ASSERT(mvps.getSize() > 0);
-	ANKI_ASSERT(lines.getSize() > 0 && (lines.getSize() % 2) == 0);
+	ANKI_ASSERT(linePositions.getSize() > 0 && (linePositions.getSize() % 2) == 0);
 
 
 	// Verts
 	// Verts
 	StagingGpuMemoryToken vertsToken;
 	StagingGpuMemoryToken vertsToken;
-	Vec3* verts = static_cast<Vec3*>(
-		stagingGpuAllocator.allocateFrame(sizeof(Vec3) * lines.getSize(), StagingGpuMemoryType::VERTEX, vertsToken));
-	memcpy(verts, lines.getBegin(), lines.getSizeInBytes());
+	Vec3* verts = static_cast<Vec3*>(stagingGpuAllocator.allocateFrame(sizeof(Vec3) * linePositions.getSize(),
+																	   StagingGpuMemoryType::VERTEX, vertsToken));
+	memcpy(verts, linePositions.getBegin(), linePositions.getSizeInBytes());
 
 
 	// Set the uniforms
 	// Set the uniforms
 	StagingGpuMemoryToken unisToken;
 	StagingGpuMemoryToken unisToken;
@@ -413,7 +215,7 @@ void DebugDrawer2::drawLines(ConstWeakArray<Mat4> mvps, const Vec4& color, F32 l
 	cmdb->bindUniformBuffer(1, 0, unisToken.m_buffer, unisToken.m_offset, unisToken.m_range);
 	cmdb->bindUniformBuffer(1, 0, unisToken.m_buffer, unisToken.m_offset, unisToken.m_range);
 
 
 	cmdb->setLineWidth(lineSize);
 	cmdb->setLineWidth(lineSize);
-	cmdb->drawArrays(PrimitiveTopology::LINES, lines.getSize(), mvps.getSize());
+	cmdb->drawArrays(PrimitiveTopology::LINES, linePositions.getSize(), mvps.getSize());
 }
 }
 
 
 void DebugDrawer2::drawBillboardTextures(const Mat4& projMat, const Mat4& viewMat, ConstWeakArray<Vec3> positions,
 void DebugDrawer2::drawBillboardTextures(const Mat4& projMat, const Mat4& viewMat, ConstWeakArray<Vec3> positions,
@@ -486,4 +288,35 @@ void DebugDrawer2::drawBillboardTextures(const Mat4& projMat, const Mat4& viewMa
 	cmdb->drawArrays(PrimitiveTopology::TRIANGLE_STRIP, 4, positions.getSize());
 	cmdb->drawArrays(PrimitiveTopology::TRIANGLE_STRIP, 4, positions.getSize());
 }
 }
 
 
+void PhysicsDebugDrawer::drawLines(const Vec3* lines, const U32 vertCount, const Vec4& color)
+{
+	if(color != m_currentColor)
+	{
+		// Color have changed, flush and change the color
+		flush();
+		m_currentColor = color;
+	}
+
+	for(U32 i = 0; i < vertCount; ++i)
+	{
+		if(m_vertCount == m_vertCache.getSize())
+		{
+			flush();
+		}
+
+		m_vertCache[m_vertCount++] = lines[i];
+	}
+}
+
+void PhysicsDebugDrawer::flush()
+{
+	if(m_vertCount > 0)
+	{
+		m_dbg->drawLines(ConstWeakArray<Mat4>(&m_mvp, 1), m_currentColor, 2.0f, false,
+						 ConstWeakArray<Vec3>(&m_vertCache[0], m_vertCount), *m_stagingGpuAllocator, m_cmdb);
+
+		m_vertCount = 0;
+	}
+}
+
 } // end namespace anki
 } // end namespace anki

+ 49 - 99
anki/scene/DebugDrawer.h

@@ -23,104 +23,6 @@ class StagingGpuMemoryToken;
 /// @addtogroup renderer
 /// @addtogroup renderer
 /// @{
 /// @{
 
 
-/// Draws simple primitives
-class DebugDrawer
-{
-public:
-	ANKI_USE_RESULT Error init(ResourceManager* rsrcManager);
-
-	void prepareFrame(RenderQueueDrawContext* ctx);
-
-	void finishFrame()
-	{
-		flush();
-		m_ctx = nullptr;
-	}
-
-	void drawGrid();
-	void drawSphere(F32 radius, I complexity = 8);
-	void drawCube(F32 size = 1.0);
-	void drawLine(const Vec3& from, const Vec3& to, const Vec4& color);
-
-	void setTopology(PrimitiveTopology topology)
-	{
-		if(topology != m_topology)
-		{
-			flush();
-		}
-		m_topology = topology;
-	}
-
-	void pushBackVertex(const Vec3& pos)
-	{
-		if((m_cachedPositionCount + 3) >= m_cachedPositions.getSize())
-		{
-			flush();
-			ANKI_ASSERT(m_cachedPositionCount == 0);
-		}
-		m_cachedPositions[m_cachedPositionCount++] = pos;
-	}
-
-	/// Something like glColor
-	void setColor(const Vec4& col)
-	{
-		if(m_crntCol != col)
-		{
-			flush();
-		}
-		m_crntCol = col;
-	}
-
-	void setModelMatrix(const Mat4& m)
-	{
-		flush();
-		m_mMat = m;
-		m_mvpMat = m_vpMat * m_mMat;
-	}
-
-	void setViewProjectionMatrix(const Mat4& m)
-	{
-		flush();
-		m_vpMat = m;
-		m_mvpMat = m_vpMat * m_mMat;
-	}
-
-private:
-	ShaderProgramResourcePtr m_prog;
-
-	RenderQueueDrawContext* m_ctx = nullptr;
-
-	// State
-	Mat4 m_mMat = Mat4::getIdentity();
-	Mat4 m_vpMat = Mat4::getIdentity();
-	Mat4 m_mvpMat = Mat4::getIdentity(); ///< Optimization.
-	Vec4 m_crntCol = Vec4(1.0f, 0.0f, 0.0f, 1.0f);
-	PrimitiveTopology m_topology = PrimitiveTopology::LINES;
-
-	static const U MAX_VERTS_BEFORE_FLUSH = 128;
-	Array<Vec3, MAX_VERTS_BEFORE_FLUSH> m_cachedPositions;
-	U32 m_cachedPositionCount = 0;
-
-	DynamicArray<Vec3> m_sphereVerts;
-
-	void flush();
-};
-
-/// Implement physics debug drawer.
-class PhysicsDebugDrawer : public PhysicsDrawer
-{
-public:
-	PhysicsDebugDrawer(DebugDrawer* dbg)
-		: m_dbg(dbg)
-	{
-	}
-
-	void drawLines(const Vec3* lines, const U32 vertCount, const Vec4& color) final;
-
-private:
-	DebugDrawer* m_dbg; ///< The debug drawer
-};
-
 /// Allocate memory for a line cube and populate it.
 /// Allocate memory for a line cube and populate it.
 void allocateAndPopulateDebugBox(StagingGpuMemoryManager& stagingGpuAllocator, StagingGpuMemoryToken& vertsToken,
 void allocateAndPopulateDebugBox(StagingGpuMemoryManager& stagingGpuAllocator, StagingGpuMemoryToken& vertsToken,
 								 StagingGpuMemoryToken& indicesToken, U32& indexCount);
 								 StagingGpuMemoryToken& indicesToken, U32& indexCount);
@@ -131,6 +33,11 @@ class DebugDrawer2
 public:
 public:
 	ANKI_USE_RESULT Error init(ResourceManager* rsrcManager);
 	ANKI_USE_RESULT Error init(ResourceManager* rsrcManager);
 
 
+	Bool isInitialized() const
+	{
+		return m_prog.isCreated();
+	}
+
 	void drawCubes(ConstWeakArray<Mat4> mvps, const Vec4& color, F32 lineSize, Bool ditherFailedDepth, F32 cubeSideSize,
 	void drawCubes(ConstWeakArray<Mat4> mvps, const Vec4& color, F32 lineSize, Bool ditherFailedDepth, F32 cubeSideSize,
 				   StagingGpuMemoryManager& stagingGpuAllocator, CommandBufferPtr& cmdb) const;
 				   StagingGpuMemoryManager& stagingGpuAllocator, CommandBufferPtr& cmdb) const;
 
 
@@ -142,7 +49,7 @@ public:
 	}
 	}
 
 
 	void drawLines(ConstWeakArray<Mat4> mvps, const Vec4& color, F32 lineSize, Bool ditherFailedDepth,
 	void drawLines(ConstWeakArray<Mat4> mvps, const Vec4& color, F32 lineSize, Bool ditherFailedDepth,
-				   ConstWeakArray<Vec3> lines, StagingGpuMemoryManager& stagingGpuAllocator,
+				   ConstWeakArray<Vec3> linePositions, StagingGpuMemoryManager& stagingGpuAllocator,
 				   CommandBufferPtr& cmdb) const;
 				   CommandBufferPtr& cmdb) const;
 
 
 	void drawLine(const Mat4& mvp, const Vec4& color, F32 lineSize, Bool ditherFailedDepth, const Vec3& a,
 	void drawLine(const Mat4& mvp, const Vec4& color, F32 lineSize, Bool ditherFailedDepth, const Vec3& a,
@@ -167,6 +74,49 @@ public:
 
 
 private:
 private:
 	ShaderProgramResourcePtr m_prog;
 	ShaderProgramResourcePtr m_prog;
+	BufferPtr m_cubePositionsBuffer;
+	BufferPtr m_cubeIndicesBuffer;
+};
+
+/// Implement physics debug drawer.
+class PhysicsDebugDrawer : public PhysicsDrawer
+{
+public:
+	PhysicsDebugDrawer(const DebugDrawer2* dbg)
+		: m_dbg(dbg)
+	{
+	}
+
+	void start(const Mat4& mvp, CommandBufferPtr& cmdb, StagingGpuMemoryManager* stagingGpuAllocator)
+	{
+		ANKI_ASSERT(stagingGpuAllocator);
+		ANKI_ASSERT(m_vertCount == 0);
+		m_mvp = mvp;
+		m_cmdb = cmdb;
+		m_stagingGpuAllocator = stagingGpuAllocator;
+	}
+
+	void drawLines(const Vec3* lines, const U32 vertCount, const Vec4& color) final;
+
+	void end()
+	{
+		flush();
+		m_cmdb.reset(nullptr); // This is essential!!!
+		m_stagingGpuAllocator = nullptr;
+	}
+
+private:
+	const DebugDrawer2* m_dbg; ///< The debug drawer
+	Mat4 m_mvp = Mat4::getIdentity();
+	CommandBufferPtr m_cmdb;
+	StagingGpuMemoryManager* m_stagingGpuAllocator = nullptr;
+
+	// Use a vertex cache because drawLines() is practically called for every line
+	Array<Vec3, 32> m_vertCache;
+	U32 m_vertCount = 0;
+	Vec4 m_currentColor = Vec4(-1.0f);
+
+	void flush();
 };
 };
 /// @}
 /// @}
 
 

+ 22 - 78
anki/scene/DecalNode.cpp

@@ -7,7 +7,6 @@
 #include <anki/scene/components/DecalComponent.h>
 #include <anki/scene/components/DecalComponent.h>
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
-#include <anki/resource/ResourceManager.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -15,9 +14,11 @@ namespace anki
 /// Decal feedback component.
 /// Decal feedback component.
 class DecalNode::MoveFeedbackComponent : public SceneComponent
 class DecalNode::MoveFeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(DecalNode::MoveFeedbackComponent)
+
 public:
 public:
-	MoveFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	MoveFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
 	}
 	}
 
 
@@ -26,7 +27,6 @@ public:
 		updated = false;
 		updated = false;
 
 
 		MoveComponent& movec = node.getFirstComponentOfType<MoveComponent>();
 		MoveComponent& movec = node.getFirstComponentOfType<MoveComponent>();
-
 		if(movec.getTimestamp() == node.getGlobalTimestamp())
 		if(movec.getTimestamp() == node.getGlobalTimestamp())
 		{
 		{
 			static_cast<DecalNode&>(node).onMove(movec);
 			static_cast<DecalNode&>(node).onMove(movec);
@@ -36,12 +36,16 @@ public:
 	}
 	}
 };
 };
 
 
+ANKI_SCENE_COMPONENT_STATICS(DecalNode::MoveFeedbackComponent)
+
 /// Decal feedback component.
 /// Decal feedback component.
 class DecalNode::ShapeFeedbackComponent : public SceneComponent
 class DecalNode::ShapeFeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(DecalNode::ShapeFeedbackComponent)
+
 public:
 public:
-	ShapeFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	ShapeFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
 	}
 	}
 
 
@@ -53,98 +57,38 @@ public:
 
 
 		if(decalc.getTimestamp() == node.getGlobalTimestamp())
 		if(decalc.getTimestamp() == node.getGlobalTimestamp())
 		{
 		{
-			static_cast<DecalNode&>(node).onDecalUpdated();
+			static_cast<DecalNode&>(node).onDecalUpdated(decalc);
 		}
 		}
 
 
 		return Error::NONE;
 		return Error::NONE;
 	}
 	}
 };
 };
 
 
-DecalNode::~DecalNode()
-{
-}
+ANKI_SCENE_COMPONENT_STATICS(DecalNode::ShapeFeedbackComponent)
 
 
-Error DecalNode::init()
+DecalNode::DecalNode(SceneGraph* scene, CString name)
+	: SceneNode(scene, name)
 {
 {
 	newComponent<MoveComponent>();
 	newComponent<MoveComponent>();
 	newComponent<MoveFeedbackComponent>();
 	newComponent<MoveFeedbackComponent>();
-	DecalComponent* decalc = newComponent<DecalComponent>(this);
-	decalc->setDrawCallback(drawCallback, this);
+	newComponent<DecalComponent>();
 	newComponent<ShapeFeedbackComponent>();
 	newComponent<ShapeFeedbackComponent>();
-	newComponent<SpatialComponent>(this, &decalc->getBoundingVolume());
-
-	ANKI_CHECK(m_dbgDrawer.init(&getResourceManager()));
-	ANKI_CHECK(getResourceManager().loadResource("engine_data/GreenDecal.ankitex", m_dbgTex));
-
-	return Error::NONE;
+	newComponent<SpatialComponent>();
 }
 }
 
 
-void DecalNode::onMove(MoveComponent& movec)
+DecalNode::~DecalNode()
 {
 {
-	SpatialComponent& sc = getFirstComponentOfType<SpatialComponent>();
-	sc.setSpatialOrigin(movec.getWorldTransform().getOrigin());
-	sc.markForUpdate();
-
-	DecalComponent& decalc = getFirstComponentOfType<DecalComponent>();
-	decalc.updateTransform(movec.getWorldTransform());
 }
 }
 
 
-void DecalNode::onDecalUpdated()
+void DecalNode::onMove(MoveComponent& movec)
 {
 {
-	SpatialComponent& sc = getFirstComponentOfType<SpatialComponent>();
-	sc.markForUpdate();
+	getFirstComponentOfType<SpatialComponent>().setSpatialOrigin(movec.getWorldTransform().getOrigin().xyz());
+	getFirstComponentOfType<DecalComponent>().setWorldTransform(movec.getWorldTransform());
 }
 }
 
 
-void DecalNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData)
+void DecalNode::onDecalUpdated(DecalComponent& decalc)
 {
 {
-	Mat4* const mvps = ctx.m_frameAllocator.newArray<Mat4>(userData.getSize());
-	Vec3* const positions = ctx.m_frameAllocator.newArray<Vec3>(userData.getSize());
-	for(U32 i = 0; i < userData.getSize(); ++i)
-	{
-		const DecalNode& self = *static_cast<const DecalNode*>(userData[i]);
-		const DecalComponent& decalComp = self.getFirstComponentOfType<DecalComponent>();
-
-		const Mat3 rot = decalComp.getBoundingVolume().getRotation().getRotationPart();
-		const Vec4 tsl = decalComp.getBoundingVolume().getCenter().xyz1();
-		const Vec3 scale = decalComp.getBoundingVolume().getExtend().xyz();
-
-		Mat3 nonUniScale = Mat3::getZero();
-		nonUniScale(0, 0) = scale.x();
-		nonUniScale(1, 1) = scale.y();
-		nonUniScale(2, 2) = scale.z();
-
-		mvps[i] = ctx.m_viewProjectionMatrix * Mat4(tsl, rot * nonUniScale, 1.0f);
-		positions[i] = tsl.xyz();
-	}
-
-	const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
-	if(enableDepthTest)
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::LESS);
-	}
-	else
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::ALWAYS);
-	}
-
-	const DecalNode& self = *static_cast<const DecalNode*>(userData[0]);
-	self.m_dbgDrawer.drawCubes(ConstWeakArray<Mat4>(mvps, userData.getSize()), Vec4(0.0f, 1.0f, 0.0f, 1.0f), 1.0f,
-							   ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), 2.0f,
-							   *ctx.m_stagingGpuAllocator, ctx.m_commandBuffer);
-
-	self.m_dbgDrawer.drawBillboardTextures(
-		ctx.m_projectionMatrix, ctx.m_viewMatrix, ConstWeakArray<Vec3>(positions, userData.getSize()), Vec4(1.0f),
-		ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), self.m_dbgTex->getGrTextureView(),
-		ctx.m_sampler, Vec2(0.75f), *ctx.m_stagingGpuAllocator, ctx.m_commandBuffer);
-
-	ctx.m_frameAllocator.deleteArray(positions, userData.getSize());
-	ctx.m_frameAllocator.deleteArray(mvps, userData.getSize());
-
-	// Restore state
-	if(!enableDepthTest)
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::LESS);
-	}
+	getFirstComponentOfType<SpatialComponent>().setObbWorldSpace(decalc.getBoundingVolumeWorldSpace());
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 2 - 14
anki/scene/DecalNode.h

@@ -6,8 +6,6 @@
 #pragma once
 #pragma once
 
 
 #include <anki/scene/SceneNode.h>
 #include <anki/scene/SceneNode.h>
-#include <anki/scene/DebugDrawer.h>
-#include <anki/collision/Obb.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -19,26 +17,16 @@ namespace anki
 class DecalNode : public SceneNode
 class DecalNode : public SceneNode
 {
 {
 public:
 public:
-	DecalNode(SceneGraph* scene, CString name)
-		: SceneNode(scene, name)
-	{
-	}
+	DecalNode(SceneGraph* scene, CString name);
 
 
 	~DecalNode();
 	~DecalNode();
 
 
-	ANKI_USE_RESULT Error init();
-
 private:
 private:
 	class MoveFeedbackComponent;
 	class MoveFeedbackComponent;
 	class ShapeFeedbackComponent;
 	class ShapeFeedbackComponent;
 
 
-	DebugDrawer2 m_dbgDrawer;
-	TextureResourcePtr m_dbgTex;
-
 	void onMove(MoveComponent& movec);
 	void onMove(MoveComponent& movec);
-	void onDecalUpdated();
-
-	static void drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData);
+	void onDecalUpdated(DecalComponent& decalc);
 };
 };
 /// @}
 /// @}
 
 

+ 47 - 29
anki/scene/FogDensityNode.cpp

@@ -11,11 +11,13 @@
 namespace anki
 namespace anki
 {
 {
 
 
-class FogDensityNode::FeedbackComponent : public SceneComponent
+class FogDensityNode::MoveFeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(FogDensityNode::MoveFeedbackComponent)
+
 public:
 public:
-	FeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	MoveFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
 	}
 	}
 
 
@@ -26,58 +28,74 @@ public:
 		const MoveComponent& movec = node.getFirstComponentOfType<MoveComponent>();
 		const MoveComponent& movec = node.getFirstComponentOfType<MoveComponent>();
 		if(movec.getTimestamp() == node.getGlobalTimestamp())
 		if(movec.getTimestamp() == node.getGlobalTimestamp())
 		{
 		{
-			static_cast<FogDensityNode&>(node).moveUpdated(movec);
+			static_cast<FogDensityNode&>(node).onMoveUpdated(movec);
+		}
+
+		return Error::NONE;
+	}
+};
+
+ANKI_SCENE_COMPONENT_STATICS(FogDensityNode::MoveFeedbackComponent)
+
+class FogDensityNode::DensityShapeFeedbackComponent : public SceneComponent
+{
+	ANKI_SCENE_COMPONENT(FogDensityNode::DensityShapeFeedbackComponent)
+
+public:
+	DensityShapeFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
+	{
+	}
+
+	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
+	{
+		updated = false;
+
+		const FogDensityComponent& fogc = node.getFirstComponentOfType<FogDensityComponent>();
+		if(fogc.getTimestamp() == node.getGlobalTimestamp())
+		{
+			static_cast<FogDensityNode&>(node).onDensityShapeUpdated(fogc);
 		}
 		}
 
 
 		return Error::NONE;
 		return Error::NONE;
 	}
 	}
 };
 };
 
 
+ANKI_SCENE_COMPONENT_STATICS(FogDensityNode::DensityShapeFeedbackComponent)
+
 FogDensityNode::FogDensityNode(SceneGraph* scene, CString name)
 FogDensityNode::FogDensityNode(SceneGraph* scene, CString name)
 	: SceneNode(scene, name)
 	: SceneNode(scene, name)
 {
 {
-	// Create components
-	newComponent<MoveComponent>(MoveComponentFlag::NONE);
-	newComponent<FeedbackComponent>();
+	newComponent<MoveComponent>();
+	newComponent<MoveFeedbackComponent>();
 	newComponent<FogDensityComponent>();
 	newComponent<FogDensityComponent>();
-	newComponent<SpatialComponent>(this, &m_spatialBox);
+	newComponent<DensityShapeFeedbackComponent>();
+	newComponent<SpatialComponent>();
 }
 }
 
 
 FogDensityNode::~FogDensityNode()
 FogDensityNode::~FogDensityNode()
 {
 {
 }
 }
 
 
-void FogDensityNode::moveUpdated(const MoveComponent& movec)
+void FogDensityNode::onMoveUpdated(const MoveComponent& movec)
 {
 {
-	// Update the fog component
-	FogDensityComponent& fogc = getFirstComponentOfType<FogDensityComponent>();
-	fogc.updatePosition(movec.getWorldTransform().getOrigin());
+	getFirstComponentOfType<FogDensityComponent>().setWorldPosition(movec.getWorldTransform().getOrigin().xyz());
+	getFirstComponentOfType<SpatialComponent>().setSpatialOrigin(movec.getWorldTransform().getOrigin().xyz());
+}
 
 
-	// Update the spatial component
+void FogDensityNode::onDensityShapeUpdated(const FogDensityComponent& fogc)
+{
 	SpatialComponent& spatialc = getFirstComponentOfType<SpatialComponent>();
 	SpatialComponent& spatialc = getFirstComponentOfType<SpatialComponent>();
 
 
-	Vec4 min, max;
 	if(fogc.isAabb())
 	if(fogc.isAabb())
 	{
 	{
-		fogc.getAabb(min, max);
+		spatialc.setAabbWorldSpace(fogc.getAabbWorldSpace());
 	}
 	}
 	else
 	else
 	{
 	{
-		F32 radius;
-		fogc.getSphere(radius);
-
-		min = Vec4(-radius, -radius, -radius, 0.0f);
-		max = Vec4(radius, radius, radius, 0.0f);
+		ANKI_ASSERT(fogc.isSphere());
+		spatialc.setSphereWorldSpace(fogc.getSphereWorldSpace());
 	}
 	}
-
-	min += movec.getWorldTransform().getOrigin();
-	max += movec.getWorldTransform().getOrigin();
-
-	m_spatialBox.setMin(min);
-	m_spatialBox.setMax(max);
-
-	spatialc.setSpatialOrigin(movec.getWorldTransform().getOrigin());
-	spatialc.markForUpdate();
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 5 - 10
anki/scene/FogDensityNode.h

@@ -22,18 +22,13 @@ public:
 
 
 	~FogDensityNode();
 	~FogDensityNode();
 
 
-	ANKI_USE_RESULT Error init()
-	{
-		return Error::NONE;
-	}
-
 private:
 private:
-	class FeedbackComponent;
-
-	Aabb m_spatialBox;
+	class MoveFeedbackComponent;
+	class DensityShapeFeedbackComponent;
 
 
-	void moveUpdated(const MoveComponent& movec);
+	void onMoveUpdated(const MoveComponent& movec);
+	void onDensityShapeUpdated(const FogDensityComponent& fogc);
 };
 };
 /// @}
 /// @}
 
 
-} // end namespace anki
+} // end namespace anki

+ 9 - 0
anki/scene/Forward.h

@@ -18,6 +18,11 @@ class SpatialComponent;
 class DecalComponent;
 class DecalComponent;
 class ReflectionProxyComponent;
 class ReflectionProxyComponent;
 class ReflectionProbeComponent;
 class ReflectionProbeComponent;
+class FogDensityComponent;
+class SkinComponent;
+class ParticleEmitterComponent;
+class GpuParticleEmitterComponent;
+class ModelComponent;
 
 
 // Nodes
 // Nodes
 class SceneNode;
 class SceneNode;
@@ -26,6 +31,10 @@ class PointLightNode;
 class SpotLightNode;
 class SpotLightNode;
 class CameraNode;
 class CameraNode;
 
 
+// Events
+class EventManager;
+class Event;
+
 // Other
 // Other
 class SceneGraph;
 class SceneGraph;
 
 

+ 36 - 96
anki/scene/GlobalIlluminationProbeNode.cpp

@@ -9,22 +9,22 @@
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
 #include <anki/scene/components/GlobalIlluminationProbeComponent.h>
 #include <anki/scene/components/GlobalIlluminationProbeComponent.h>
-#include <anki/resource/ResourceManager.h>
-#include <anki/resource/TextureResource.h>
 
 
 namespace anki
 namespace anki
 {
 {
 
 
-const FrustumComponentVisibilityTestFlag FRUSTUM_TEST_FLAGS =
+constexpr FrustumComponentVisibilityTestFlag FRUSTUM_TEST_FLAGS =
 	FrustumComponentVisibilityTestFlag::RENDER_COMPONENTS | FrustumComponentVisibilityTestFlag::LIGHT_COMPONENTS
 	FrustumComponentVisibilityTestFlag::RENDER_COMPONENTS | FrustumComponentVisibilityTestFlag::LIGHT_COMPONENTS
 	| FrustumComponentVisibilityTestFlag::DIRECTIONAL_LIGHT_SHADOWS_1_CASCADE;
 	| FrustumComponentVisibilityTestFlag::DIRECTIONAL_LIGHT_SHADOWS_1_CASCADE;
 
 
 /// Feedback component
 /// Feedback component
 class GlobalIlluminationProbeNode::MoveFeedbackComponent : public SceneComponent
 class GlobalIlluminationProbeNode::MoveFeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(GlobalIlluminationProbeNode::MoveFeedbackComponent)
+
 public:
 public:
-	MoveFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	MoveFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
 	}
 	}
 
 
@@ -44,12 +44,18 @@ public:
 	}
 	}
 };
 };
 
 
+ANKI_SCENE_COMPONENT_STATICS(GlobalIlluminationProbeNode::MoveFeedbackComponent)
+
 /// Feedback component
 /// Feedback component
 class GlobalIlluminationProbeNode::ShapeFeedbackComponent : public SceneComponent
 class GlobalIlluminationProbeNode::ShapeFeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(GlobalIlluminationProbeNode::ShapeFeedbackComponent)
+
 public:
 public:
-	ShapeFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	ShapeFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(),
+						 false // Not feedback component. Can't be skipped because of getMarkedForRendering()
+		)
 	{
 	{
 	}
 	}
 
 
@@ -69,11 +75,10 @@ public:
 	}
 	}
 };
 };
 
 
-GlobalIlluminationProbeNode::~GlobalIlluminationProbeNode()
-{
-}
+ANKI_SCENE_COMPONENT_STATICS(GlobalIlluminationProbeNode::ShapeFeedbackComponent)
 
 
-Error GlobalIlluminationProbeNode::init()
+GlobalIlluminationProbeNode::GlobalIlluminationProbeNode(SceneGraph* scene, CString name)
+	: SceneNode(scene, name)
 {
 {
 	// Move component first
 	// Move component first
 	newComponent<MoveComponent>();
 	newComponent<MoveComponent>();
@@ -82,17 +87,13 @@ Error GlobalIlluminationProbeNode::init()
 	newComponent<MoveFeedbackComponent>();
 	newComponent<MoveFeedbackComponent>();
 
 
 	// GI probe comp
 	// GI probe comp
-	GlobalIlluminationProbeComponent* giprobec =
-		newComponent<GlobalIlluminationProbeComponent>(getSceneGraph().getNewUuid());
-	ANKI_CHECK(giprobec->init());
-	giprobec->setBoundingBox(m_spatialAabb.getMin(), m_spatialAabb.getMax());
-	giprobec->setDrawCallback(debugDrawCallback, this);
+	newComponent<GlobalIlluminationProbeComponent>();
 
 
 	// Second feedback component
 	// Second feedback component
 	newComponent<ShapeFeedbackComponent>();
 	newComponent<ShapeFeedbackComponent>();
 
 
 	// The frustum components
 	// The frustum components
-	const F32 ang = toRad(90.0f);
+	constexpr F32 ang = toRad(90.0f);
 	const F32 zNear = LIGHT_FRUSTUM_NEAR_PLANE;
 	const F32 zNear = LIGHT_FRUSTUM_NEAR_PLANE;
 
 
 	Mat3 rot;
 	Mat3 rot;
@@ -114,33 +115,31 @@ Error GlobalIlluminationProbeNode::init()
 		m_cubeFaceTransforms[i].setOrigin(Vec4(0.0f));
 		m_cubeFaceTransforms[i].setOrigin(Vec4(0.0f));
 		m_cubeFaceTransforms[i].setScale(1.0f);
 		m_cubeFaceTransforms[i].setScale(1.0f);
 
 
-		FrustumComponent* frc = newComponent<FrustumComponent>(this, FrustumType::PERSPECTIVE);
+		FrustumComponent* frc = newComponent<FrustumComponent>();
+		frc->setFrustumType(FrustumType::PERSPECTIVE);
 		const F32 tempEffectiveDistance = 1.0f;
 		const F32 tempEffectiveDistance = 1.0f;
 		frc->setPerspective(zNear, tempEffectiveDistance, ang, ang);
 		frc->setPerspective(zNear, tempEffectiveDistance, ang, ang);
-		frc->setTransform(m_cubeFaceTransforms[i]);
+		frc->setWorldTransform(m_cubeFaceTransforms[i]);
 		frc->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE);
 		frc->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE);
 		frc->setEffectiveShadowDistance(getSceneGraph().getConfig().m_reflectionProbeShadowEffectiveDistance);
 		frc->setEffectiveShadowDistance(getSceneGraph().getConfig().m_reflectionProbeShadowEffectiveDistance);
 	}
 	}
 
 
 	// Spatial component
 	// Spatial component
-	SpatialComponent* spatialc = newComponent<SpatialComponent>(this, &m_spatialAabb);
+	SpatialComponent* spatialc = newComponent<SpatialComponent>();
 	spatialc->setUpdateOctreeBounds(false);
 	spatialc->setUpdateOctreeBounds(false);
+}
 
 
-	// Misc
-	ANKI_CHECK(m_dbgDrawer.init(&getResourceManager()));
-	ANKI_CHECK(getResourceManager().loadResource("engine_data/GiProbe.ankitex", m_dbgTex));
-
-	return Error::NONE;
+GlobalIlluminationProbeNode::~GlobalIlluminationProbeNode()
+{
 }
 }
 
 
 void GlobalIlluminationProbeNode::onMoveUpdate(MoveComponent& move)
 void GlobalIlluminationProbeNode::onMoveUpdate(MoveComponent& move)
 {
 {
-	// Update the probe comp
-	const Vec4 displacement = move.getWorldTransform().getOrigin() - m_previousPosition;
-	m_previousPosition = move.getWorldTransform().getOrigin();
-
 	GlobalIlluminationProbeComponent& gic = getFirstComponentOfType<GlobalIlluminationProbeComponent>();
 	GlobalIlluminationProbeComponent& gic = getFirstComponentOfType<GlobalIlluminationProbeComponent>();
-	gic.setBoundingBox(gic.getAlignedBoundingBoxMin() + displacement, gic.getAlignedBoundingBoxMax() + displacement);
+	gic.setWorldPosition(move.getWorldTransform().getOrigin().xyz());
+
+	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
+	sp.setSpatialOrigin(move.getWorldTransform().getOrigin().xyz());
 }
 }
 
 
 void GlobalIlluminationProbeNode::onShapeUpdateOrProbeNeedsRendering()
 void GlobalIlluminationProbeNode::onShapeUpdateOrProbeNeedsRendering()
@@ -151,20 +150,17 @@ void GlobalIlluminationProbeNode::onShapeUpdateOrProbeNeedsRendering()
 	if(gic.getMarkedForRendering())
 	if(gic.getMarkedForRendering())
 	{
 	{
 		// Compute effective distance
 		// Compute effective distance
-		F32 effectiveDistance = gic.getAlignedBoundingBoxMax().x() - gic.getAlignedBoundingBoxMin().x();
-		effectiveDistance =
-			max(effectiveDistance, gic.getAlignedBoundingBoxMax().y() - gic.getAlignedBoundingBoxMin().y());
-		effectiveDistance =
-			max(effectiveDistance, gic.getAlignedBoundingBoxMax().z() - gic.getAlignedBoundingBoxMin().z());
+		F32 effectiveDistance = max(gic.getBoxVolumeSize().x(), gic.getBoxVolumeSize().y());
+		effectiveDistance = max(effectiveDistance, gic.getBoxVolumeSize().z());
 		effectiveDistance = max(effectiveDistance, getSceneGraph().getConfig().m_reflectionProbeEffectiveDistance);
 		effectiveDistance = max(effectiveDistance, getSceneGraph().getConfig().m_reflectionProbeEffectiveDistance);
 
 
 		// Update frustum components
 		// Update frustum components
 		U count = 0;
 		U count = 0;
-		Error err = iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) -> Error {
+		const Error err = iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) -> Error {
 			Transform trf = m_cubeFaceTransforms[count];
 			Transform trf = m_cubeFaceTransforms[count];
-			trf.setOrigin(gic.getRenderPosition());
+			trf.setOrigin(gic.getRenderPosition().xyz0());
 
 
-			frc.setTransform(trf);
+			frc.setWorldTransform(trf);
 			frc.setFar(effectiveDistance);
 			frc.setFar(effectiveDistance);
 			++count;
 			++count;
 
 
@@ -182,10 +178,7 @@ void GlobalIlluminationProbeNode::onShapeUpdateOrProbeNeedsRendering()
 		// Update only when the shape was actually update
 		// Update only when the shape was actually update
 
 
 		SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
 		SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
-		sp.markForUpdate();
-		sp.setSpatialOrigin((m_spatialAabb.getMax() + m_spatialAabb.getMin()) / 2.0f);
-		m_spatialAabb.setMin(gic.getAlignedBoundingBoxMin());
-		m_spatialAabb.setMax(gic.getAlignedBoundingBoxMax());
+		sp.setAabbWorldSpace(gic.getAabbWorldSpace());
 	}
 	}
 }
 }
 
 
@@ -195,7 +188,7 @@ Error GlobalIlluminationProbeNode::frameUpdate(Second prevUpdateTime, Second crn
 	const GlobalIlluminationProbeComponent& gic = getFirstComponentOfType<GlobalIlluminationProbeComponent>();
 	const GlobalIlluminationProbeComponent& gic = getFirstComponentOfType<GlobalIlluminationProbeComponent>();
 
 
 	const FrustumComponentVisibilityTestFlag testFlags =
 	const FrustumComponentVisibilityTestFlag testFlags =
-		gic.getMarkedForRendering() ? FRUSTUM_TEST_FLAGS : FrustumComponentVisibilityTestFlag::NONE;
+		(gic.getMarkedForRendering()) ? FRUSTUM_TEST_FLAGS : FrustumComponentVisibilityTestFlag::NONE;
 
 
 	const Error err = iterateComponentsOfType<FrustumComponent>([testFlags](FrustumComponent& frc) -> Error {
 	const Error err = iterateComponentsOfType<FrustumComponent>([testFlags](FrustumComponent& frc) -> Error {
 		frc.setEnabledVisibilityTests(testFlags);
 		frc.setEnabledVisibilityTests(testFlags);
@@ -206,57 +199,4 @@ Error GlobalIlluminationProbeNode::frameUpdate(Second prevUpdateTime, Second crn
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
-void GlobalIlluminationProbeNode::debugDrawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData)
-{
-	Mat4* const mvps = ctx.m_frameAllocator.newArray<Mat4>(userData.getSize());
-	Vec3* const positions = ctx.m_frameAllocator.newArray<Vec3>(userData.getSize());
-	for(U32 i = 0; i < userData.getSize(); ++i)
-	{
-		const GlobalIlluminationProbeNode& self = *static_cast<const GlobalIlluminationProbeNode*>(userData[i]);
-		const GlobalIlluminationProbeComponent& giComp =
-			self.getFirstComponentOfType<GlobalIlluminationProbeComponent>();
-
-		const Vec3 tsl = (giComp.getAlignedBoundingBoxMin().xyz() + giComp.getAlignedBoundingBoxMax().xyz()) / 2.0f;
-		const Vec3 scale = (tsl - giComp.getAlignedBoundingBoxMin().xyz());
-
-		// Set non uniform scale.
-		Mat3 rot = Mat3::getIdentity();
-		rot(0, 0) *= scale.x();
-		rot(1, 1) *= scale.y();
-		rot(2, 2) *= scale.z();
-
-		mvps[i] = ctx.m_viewProjectionMatrix * Mat4(tsl.xyz1(), rot, 1.0f);
-		positions[i] = tsl.xyz();
-	}
-
-	const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
-	if(enableDepthTest)
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::LESS);
-	}
-	else
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::ALWAYS);
-	}
-
-	const GlobalIlluminationProbeNode& self = *static_cast<const GlobalIlluminationProbeNode*>(userData[0]);
-	self.m_dbgDrawer.drawCubes(ConstWeakArray<Mat4>(mvps, userData.getSize()), Vec4(0.729f, 0.635f, 0.196f, 1.0f), 1.0f,
-							   ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), 2.0f,
-							   *ctx.m_stagingGpuAllocator, ctx.m_commandBuffer);
-
-	self.m_dbgDrawer.drawBillboardTextures(
-		ctx.m_projectionMatrix, ctx.m_viewMatrix, ConstWeakArray<Vec3>(positions, userData.getSize()), Vec4(1.0f),
-		ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), self.m_dbgTex->getGrTextureView(),
-		ctx.m_sampler, Vec2(0.75f), *ctx.m_stagingGpuAllocator, ctx.m_commandBuffer);
-
-	ctx.m_frameAllocator.deleteArray(positions, userData.getSize());
-	ctx.m_frameAllocator.deleteArray(mvps, userData.getSize());
-
-	// Restore state
-	if(!enableDepthTest)
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::LESS);
-	}
-}
-
 } // end namespace anki
 } // end namespace anki

+ 2 - 15
anki/scene/GlobalIlluminationProbeNode.h

@@ -6,8 +6,7 @@
 #pragma once
 #pragma once
 
 
 #include <anki/scene/SceneNode.h>
 #include <anki/scene/SceneNode.h>
-#include <anki/scene/DebugDrawer.h>
-#include <anki/collision/Aabb.h>
+#include <anki/Math.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -19,15 +18,10 @@ namespace anki
 class GlobalIlluminationProbeNode : public SceneNode
 class GlobalIlluminationProbeNode : public SceneNode
 {
 {
 public:
 public:
-	GlobalIlluminationProbeNode(SceneGraph* scene, CString name)
-		: SceneNode(scene, name)
-	{
-	}
+	GlobalIlluminationProbeNode(SceneGraph* scene, CString name);
 
 
 	~GlobalIlluminationProbeNode();
 	~GlobalIlluminationProbeNode();
 
 
-	ANKI_USE_RESULT Error init();
-
 	ANKI_USE_RESULT Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
 	ANKI_USE_RESULT Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
 
 
 private:
 private:
@@ -35,16 +29,9 @@ private:
 	class ShapeFeedbackComponent;
 	class ShapeFeedbackComponent;
 
 
 	Array<Transform, 6> m_cubeFaceTransforms;
 	Array<Transform, 6> m_cubeFaceTransforms;
-	Aabb m_spatialAabb = Aabb(Vec3(-1.0f), Vec3(1.0f));
-	Vec4 m_previousPosition = Vec4(0.0f);
-
-	DebugDrawer2 m_dbgDrawer;
-	TextureResourcePtr m_dbgTex;
 
 
 	void onMoveUpdate(MoveComponent& move);
 	void onMoveUpdate(MoveComponent& move);
 	void onShapeUpdateOrProbeNeedsRendering();
 	void onShapeUpdateOrProbeNeedsRendering();
-
-	static void debugDrawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData);
 };
 };
 /// @}
 /// @}
 
 

+ 52 - 181
anki/scene/GpuParticleEmitterNode.cpp

@@ -4,22 +4,23 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <anki/scene/GpuParticleEmitterNode.h>
 #include <anki/scene/GpuParticleEmitterNode.h>
-#include <anki/scene/SceneGraph.h>
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
 #include <anki/scene/components/GenericGpuComputeJobComponent.h>
 #include <anki/scene/components/GenericGpuComputeJobComponent.h>
 #include <anki/scene/components/RenderComponent.h>
 #include <anki/scene/components/RenderComponent.h>
-#include <anki/resource/ResourceManager.h>
-#include <anki/shaders/include/ParticleTypes.h>
+#include <anki/scene/components/GpuParticleEmitterComponent.h>
 
 
 namespace anki
 namespace anki
 {
 {
 
 
+/// Feedback component
 class GpuParticleEmitterNode::MoveFeedbackComponent : public SceneComponent
 class GpuParticleEmitterNode::MoveFeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(GpuParticleEmitterNode::MoveFeedbackComponent)
+
 public:
 public:
-	MoveFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	MoveFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
 	}
 	}
 
 
@@ -38,210 +39,80 @@ public:
 	}
 	}
 };
 };
 
 
-GpuParticleEmitterNode::GpuParticleEmitterNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-}
+ANKI_SCENE_COMPONENT_STATICS(GpuParticleEmitterNode::MoveFeedbackComponent)
 
 
-GpuParticleEmitterNode::~GpuParticleEmitterNode()
+/// Feedback component
+class GpuParticleEmitterNode::ShapeFeedbackComponent : public SceneComponent
 {
 {
-}
+	ANKI_SCENE_COMPONENT(GpuParticleEmitterNode::ShapeFeedbackComponent)
 
 
-Error GpuParticleEmitterNode::init(const CString& filename)
-{
-	// Create program
-	ANKI_CHECK(getResourceManager().loadResource("shaders/GpuParticlesSimulation.ankiprog", m_prog));
-	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(variant);
-	m_grProg = variant->getProgram();
-	m_workgroupSizeX = variant->getWorkgroupSizes()[0];
-
-	// Load particle props
-	ANKI_CHECK(getResourceManager().loadResource(filename, m_emitterRsrc));
-
-	// Create a UBO with the props
-	BufferInitInfo buffInit;
-	buffInit.m_mapAccess = BufferMapAccessBit::WRITE;
-	buffInit.m_usage = BufferUsageBit::UNIFORM_COMPUTE;
-	buffInit.m_size = sizeof(GpuParticleEmitterProperties);
-	m_propsBuff = getSceneGraph().getGrManager().newBuffer(buffInit);
-	GpuParticleEmitterProperties* props =
-		static_cast<GpuParticleEmitterProperties*>(m_propsBuff->map(0, MAX_PTR_SIZE, BufferMapAccessBit::WRITE));
-
-	const ParticleEmitterProperties& inProps = m_emitterRsrc->getProperties();
-	props->m_minGravity = inProps.m_particle.m_minGravity;
-	props->m_minMass = inProps.m_particle.m_minMass;
-	props->m_maxGravity = inProps.m_particle.m_maxGravity;
-	props->m_maxMass = inProps.m_particle.m_maxMass;
-	props->m_minForce = inProps.m_particle.m_minForceDirection * inProps.m_particle.m_minForceMagnitude;
-	props->m_minLife = F32(inProps.m_particle.m_minLife);
-	props->m_maxForce = inProps.m_particle.m_maxForceDirection * inProps.m_particle.m_maxForceMagnitude;
-	props->m_maxLife = F32(inProps.m_particle.m_maxLife);
-	props->m_minStartingPosition = inProps.m_particle.m_minStartingPosition;
-	props->m_maxStartingPosition = inProps.m_particle.m_maxStartingPosition;
-	props->m_particleCount = inProps.m_maxNumOfParticles;
-
-	m_propsBuff->unmap();
-
-	m_particleCount = inProps.m_maxNumOfParticles;
-
-	// Create the particle buffer
-	buffInit.m_mapAccess = BufferMapAccessBit::WRITE;
-	buffInit.m_usage = BufferUsageBit::ALL_STORAGE;
-	buffInit.m_size = sizeof(GpuParticle) * inProps.m_maxNumOfParticles;
-	m_particlesBuff = getSceneGraph().getGrManager().newBuffer(buffInit);
-
-	GpuParticle* particle = static_cast<GpuParticle*>(m_particlesBuff->map(0, MAX_PTR_SIZE, BufferMapAccessBit::WRITE));
-	const GpuParticle* end = particle + inProps.m_maxNumOfParticles;
-	for(; particle < end; ++particle)
+public:
+	ShapeFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
-		particle->m_life = -1.0f; // Force GPU to init the particle
 	}
 	}
 
 
-	m_particlesBuff->unmap();
-
-	// Create the rand buffer
-	buffInit.m_size = sizeof(U32) + MAX_RAND_FACTORS * sizeof(F32);
-	m_randFactorsBuff = getSceneGraph().getGrManager().newBuffer(buffInit);
-
-	F32* randFactors = static_cast<F32*>(m_randFactorsBuff->map(0, MAX_PTR_SIZE, BufferMapAccessBit::WRITE));
-
-	*reinterpret_cast<U32*>(randFactors) = MAX_RAND_FACTORS;
-	++randFactors;
-
-	const F32* randFactorsEnd = randFactors + MAX_RAND_FACTORS;
-	for(; randFactors < randFactorsEnd; ++randFactors)
+	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
 	{
 	{
-		*randFactors = getRandomRange(0.0f, 1.0f);
-	}
-
-	m_randFactorsBuff->unmap();
+		updated = false;
 
 
-	// Create the sampler
-	{
-		SamplerInitInfo sinit;
-		sinit.m_addressing = SamplingAddressing::CLAMP;
+		const GpuParticleEmitterComponent& pec = node.getFirstComponentOfType<GpuParticleEmitterComponent>();
+		if(pec.getTimestamp() == node.getGlobalTimestamp())
+		{
+			GpuParticleEmitterNode& mnode = static_cast<GpuParticleEmitterNode&>(node);
+			mnode.onShapeUpdate(pec);
+		}
 
 
-		m_nearestAnyClampSampler = getSceneGraph().getGrManager().newSampler(sinit);
+		return Error::NONE;
 	}
 	}
+};
 
 
-	// Find the extend of the particles
-	{
-		const Vec3 maxForce = inProps.m_particle.m_maxForceDirection * inProps.m_particle.m_maxForceMagnitude;
-		const Vec3& maxGravity = inProps.m_particle.m_maxGravity;
-		const F32 maxMass = inProps.m_particle.m_maxMass;
-		const F32 maxTime = F32(inProps.m_particle.m_maxLife);
-
-		const Vec3 totalForce = maxGravity * maxMass + maxForce;
-		const Vec3 accelleration = totalForce / maxMass;
-		const Vec3 velocity = accelleration * maxTime;
-		m_maxDistanceAParticleCanGo = (velocity * maxTime).getLength();
-	}
+ANKI_SCENE_COMPONENT_STATICS(GpuParticleEmitterNode::ShapeFeedbackComponent)
 
 
+GpuParticleEmitterNode::GpuParticleEmitterNode(SceneGraph* scene, CString name)
+	: SceneNode(scene, name)
+{
 	// Create the components
 	// Create the components
 	newComponent<MoveComponent>();
 	newComponent<MoveComponent>();
 	newComponent<MoveFeedbackComponent>();
 	newComponent<MoveFeedbackComponent>();
-	newComponent<SpatialComponent>(this, &m_spatialVolume);
-	GenericGpuComputeJobComponent* gpuComp = newComponent<GenericGpuComputeJobComponent>();
-	gpuComp->setCallback(
-		[](GenericGpuComputeJobQueueElementContext& ctx, const void* userData) {
-			static_cast<const GpuParticleEmitterNode*>(userData)->simulate(ctx);
-		},
-		this);
-	RenderComponent* rcomp = newComponent<RenderComponent>();
-	rcomp->initRaster(
-		[](RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) {
-			ANKI_ASSERT(userData.getSize() == 1);
-			static_cast<const GpuParticleEmitterNode*>(userData[0])->draw(ctx);
-		},
-		this,
-		0 // No merging
-	);
-	rcomp->setFlagsFromMaterial(m_emitterRsrc->getMaterial());
+	GpuParticleEmitterComponent* pec = newComponent<GpuParticleEmitterComponent>();
+	newComponent<ShapeFeedbackComponent>();
+	newComponent<SpatialComponent>();
 
 
-	return Error::NONE;
-}
+	GenericGpuComputeJobComponent* gpuComp = newComponent<GenericGpuComputeJobComponent>();
+	gpuComp->setCallback(GpuParticleEmitterComponent::simulateCallback, pec);
 
 
-void GpuParticleEmitterNode::onMoveComponentUpdate(const MoveComponent& movec)
-{
-	const Vec4& pos = movec.getWorldTransform().getOrigin();
-
-	// Update the AABB
-	m_spatialVolume.setMin((pos - m_maxDistanceAParticleCanGo).xyz());
-	m_spatialVolume.setMax((pos + m_maxDistanceAParticleCanGo).xyz());
-	SpatialComponent& spatialc = getFirstComponentOfType<SpatialComponent>();
-	spatialc.markForUpdate();
-	spatialc.setSpatialOrigin(pos);
-
-	// Stash the position
-	m_worldPosition = pos.xyz();
-	m_worldRotation = movec.getWorldTransform().getRotation();
+	RenderComponent* rcomp = newComponent<RenderComponent>();
+	const U64 noMerging = 0;
+	rcomp->initRaster(GpuParticleEmitterComponent::drawCallback, pec, noMerging);
 }
 }
 
 
-void GpuParticleEmitterNode::simulate(GenericGpuComputeJobQueueElementContext& ctx) const
+GpuParticleEmitterNode::~GpuParticleEmitterNode()
 {
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	cmdb->bindShaderProgram(m_grProg);
-
-	// Bind resources
-	cmdb->bindStorageBuffer(1, 0, m_particlesBuff, 0, MAX_PTR_SIZE);
-	cmdb->bindUniformBuffer(1, 1, m_propsBuff, 0, MAX_PTR_SIZE);
-	cmdb->bindStorageBuffer(1, 2, m_randFactorsBuff, 0, MAX_PTR_SIZE);
-	cmdb->bindSampler(1, 3, m_nearestAnyClampSampler);
-
-	StagingGpuMemoryToken token;
-	GpuParticleSimulationState* unis =
-		static_cast<GpuParticleSimulationState*>(ctx.m_stagingGpuAllocator->allocateFrame(
-			sizeof(GpuParticleSimulationState), StagingGpuMemoryType::UNIFORM, token));
-
-	unis->m_viewProjMat = ctx.m_viewProjectionMatrix;
-	unis->m_unprojectionParams = ctx.m_projectionMatrix.extractPerspectiveUnprojectionParams();
-	unis->m_randomIndex = rand();
-	unis->m_dt = F32(m_dt);
-	unis->m_emitterPosition = m_worldPosition;
-	unis->m_emitterRotation = m_worldRotation;
-	unis->m_invViewRotation = Mat3x4(Vec3(0.0f), ctx.m_cameraTransform.getRotationPart());
-	cmdb->bindUniformBuffer(1, 4, token.m_buffer, token.m_offset, token.m_range);
-
-	// Dispatch
-	const U32 workgroupCount = (m_particleCount + m_workgroupSizeX - 1) / m_workgroupSizeX;
-	cmdb->dispatchCompute(workgroupCount, 1, 1);
 }
 }
 
 
-void GpuParticleEmitterNode::draw(RenderQueueDrawContext& ctx) const
+Error GpuParticleEmitterNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 {
 {
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
+	const GpuParticleEmitterComponent& pec = getFirstComponentOfType<GpuParticleEmitterComponent>();
 
 
-	if(!ctx.m_debugDraw)
+	if(pec.isEnabled())
 	{
 	{
-		// Program
-		ShaderProgramPtr prog;
-		m_emitterRsrc->getRenderingInfo(ctx.m_key, prog);
-		cmdb->bindShaderProgram(prog);
-
-		// Resources
-		static const Mat4 identity = Mat4::getIdentity();
-
-		RenderComponent::allocateAndSetupUniforms(m_emitterRsrc->getMaterial(), ctx, ConstWeakArray<Mat4>(&identity, 1),
-												  ConstWeakArray<Mat4>(&identity, 1), *ctx.m_stagingGpuAllocator);
+		RenderComponent& rc = getFirstComponentOfType<RenderComponent>();
+		rc.setFlagsFromMaterial(pec.getParticleEmitterResource()->getMaterial());
+	}
 
 
-		cmdb->bindStorageBuffer(0, 1, m_particlesBuff, 0, MAX_PTR_SIZE);
+	return Error::NONE;
+}
 
 
-		StagingGpuMemoryToken token;
-		Vec4* extraUniforms = static_cast<Vec4*>(
-			ctx.m_stagingGpuAllocator->allocateFrame(sizeof(Vec4), StagingGpuMemoryType::UNIFORM, token));
-		*extraUniforms = ctx.m_cameraTransform.getColumn(2);
-		cmdb->bindUniformBuffer(0, 2, token.m_buffer, token.m_offset, token.m_range);
+void GpuParticleEmitterNode::onMoveComponentUpdate(const MoveComponent& movec)
+{
+	getFirstComponentOfType<SpatialComponent>().setSpatialOrigin(movec.getWorldTransform().getOrigin().xyz());
+	getFirstComponentOfType<GpuParticleEmitterComponent>().setWorldTransform(movec.getWorldTransform());
+}
 
 
-		// Draw
-		cmdb->setLineWidth(8.0f);
-		cmdb->drawArrays(PrimitiveTopology::LINES, m_particleCount * 2);
-	}
-	else
-	{
-		// TODO
-	}
+void GpuParticleEmitterNode::onShapeUpdate(const GpuParticleEmitterComponent& pec)
+{
+	getFirstComponentOfType<SpatialComponent>().setAabbWorldSpace(pec.getBoundingVolumeWorldSpace());
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 3 - 39
anki/scene/GpuParticleEmitterNode.h

@@ -6,17 +6,10 @@
 #pragma once
 #pragma once
 
 
 #include <anki/scene/SceneNode.h>
 #include <anki/scene/SceneNode.h>
-#include <anki/resource/ParticleEmitterResource.h>
-#include <anki/collision/Aabb.h>
 
 
 namespace anki
 namespace anki
 {
 {
 
 
-// Forward
-class GenericGpuComputeJobQueueElementContext;
-class RenderableQueueElement;
-class RenderQueueDrawContext;
-
 /// @addtogroup scene
 /// @addtogroup scene
 /// @{
 /// @{
 
 
@@ -28,43 +21,14 @@ public:
 
 
 	~GpuParticleEmitterNode();
 	~GpuParticleEmitterNode();
 
 
-	ANKI_USE_RESULT Error init(const CString& filename);
-
-	ANKI_USE_RESULT Error frameUpdate(Second prevUpdateTime, Second crntTime) override
-	{
-		m_dt = crntTime - prevUpdateTime;
-		return Error::NONE;
-	}
+	Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
 
 
 private:
 private:
-	static constexpr U32 MAX_RAND_FACTORS = 32;
-
 	class MoveFeedbackComponent;
 	class MoveFeedbackComponent;
-
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-	U32 m_workgroupSizeX = 0;
-
-	ParticleEmitterResourcePtr m_emitterRsrc;
-
-	BufferPtr m_propsBuff; ///< Constant buffer with particle properties.
-	BufferPtr m_particlesBuff; ///< Particles buffer.
-	BufferPtr m_randFactorsBuff; ///< Contains flots with random values. Values in range [0.0, 1.0].
-
-	SamplerPtr m_nearestAnyClampSampler;
-
-	Aabb m_spatialVolume = Aabb(Vec3(-1.0f), Vec3(1.0f));
-	F32 m_maxDistanceAParticleCanGo = -1.0f;
-	U32 m_particleCount = 0;
-	Second m_dt = 0.0;
-	Vec3 m_worldPosition = Vec3(0.0f); //< Cache it.
-	Mat3x4 m_worldRotation = Mat3x4::getIdentity();
+	class ShapeFeedbackComponent;
 
 
 	void onMoveComponentUpdate(const MoveComponent& movec);
 	void onMoveComponentUpdate(const MoveComponent& movec);
-
-	void simulate(GenericGpuComputeJobQueueElementContext& ctx) const;
-
-	void draw(RenderQueueDrawContext& ctx) const;
+	void onShapeUpdate(const GpuParticleEmitterComponent& pec);
 };
 };
 /// @}
 /// @}
 
 

+ 97 - 159
anki/scene/LightNode.cpp

@@ -9,18 +9,19 @@
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
 #include <anki/scene/components/FrustumComponent.h>
 #include <anki/scene/components/FrustumComponent.h>
-#include <anki/resource/ResourceManager.h>
 #include <anki/shaders/include/ClusteredShadingTypes.h>
 #include <anki/shaders/include/ClusteredShadingTypes.h>
 
 
 namespace anki
 namespace anki
 {
 {
 
 
 /// Feedback component.
 /// Feedback component.
-class LightNode::MovedFeedbackComponent : public SceneComponent
+class LightNode::OnMovedFeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(LightNode::OnMovedFeedbackComponent)
+
 public:
 public:
-	MovedFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	OnMovedFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
 	}
 	}
 
 
@@ -32,19 +33,23 @@ public:
 		if(move.getTimestamp() == node.getGlobalTimestamp())
 		if(move.getTimestamp() == node.getGlobalTimestamp())
 		{
 		{
 			// Move updated
 			// Move updated
-			static_cast<LightNode&>(node).onMoveUpdate(move);
+			static_cast<LightNode&>(node).onMoved(move);
 		}
 		}
 
 
 		return Error::NONE;
 		return Error::NONE;
 	}
 	}
 };
 };
 
 
+ANKI_SCENE_COMPONENT_STATICS(LightNode::OnMovedFeedbackComponent)
+
 /// Feedback component.
 /// Feedback component.
-class LightNode::LightChangedFeedbackComponent : public SceneComponent
+class LightNode::OnLightShapeUpdatedFeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(LightNode::OnLightShapeUpdatedFeedbackComponent)
+
 public:
 public:
-	LightChangedFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	OnLightShapeUpdatedFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
 	}
 	}
 
 
@@ -56,13 +61,15 @@ public:
 		if(light.getTimestamp() == node.getGlobalTimestamp())
 		if(light.getTimestamp() == node.getGlobalTimestamp())
 		{
 		{
 			// Shape updated
 			// Shape updated
-			static_cast<LightNode&>(node).onShapeUpdate(light);
+			static_cast<LightNode&>(node).onLightShapeUpdated(light);
 		}
 		}
 
 
 		return Error::NONE;
 		return Error::NONE;
 	}
 	}
 };
 };
 
 
+ANKI_SCENE_COMPONENT_STATICS(LightNode::OnLightShapeUpdatedFeedbackComponent)
+
 LightNode::LightNode(SceneGraph* scene, CString name)
 LightNode::LightNode(SceneGraph* scene, CString name)
 	: SceneNode(scene, name)
 	: SceneNode(scene, name)
 {
 {
@@ -72,25 +79,6 @@ LightNode::~LightNode()
 {
 {
 }
 }
 
 
-Error LightNode::initCommon(LightComponentType lightType)
-{
-	CString texFname;
-	switch(lightType)
-	{
-	case LightComponentType::POINT:
-		texFname = "engine_data/LightBulb.ankitex";
-		break;
-	case LightComponentType::SPOT:
-		texFname = "engine_data/SpotLight.ankitex";
-		break;
-	default:
-		ANKI_ASSERT(0);
-	}
-	ANKI_CHECK(getResourceManager().loadResource(texFname, m_dbgTex));
-
-	return m_dbgDrawer.init(&getResourceManager());
-}
-
 void LightNode::frameUpdateCommon()
 void LightNode::frameUpdateCommon()
 {
 {
 	// Update frustum comps shadow info
 	// Update frustum comps shadow info
@@ -116,73 +104,30 @@ void LightNode::onMoveUpdateCommon(const MoveComponent& move)
 {
 {
 	// Update the spatial
 	// Update the spatial
 	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
 	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
-	sp.markForUpdate();
-	sp.setSpatialOrigin(move.getWorldTransform().getOrigin());
+	sp.setSpatialOrigin(move.getWorldTransform().getOrigin().xyz());
 
 
 	// Update the lens flare
 	// Update the lens flare
 	LensFlareComponent* lf = tryGetFirstComponentOfType<LensFlareComponent>();
 	LensFlareComponent* lf = tryGetFirstComponentOfType<LensFlareComponent>();
 	if(lf)
 	if(lf)
 	{
 	{
-		lf->setWorldPosition(move.getWorldTransform().getOrigin());
+		lf->setWorldPosition(move.getWorldTransform().getOrigin().xyz());
 	}
 	}
 
 
 	// Update light component
 	// Update light component
-	getFirstComponentOfType<LightComponent>().updateWorldTransform(move.getWorldTransform());
-}
-
-Error LightNode::loadLensFlare(const CString& filename)
-{
-	ANKI_ASSERT(tryGetFirstComponentOfType<LensFlareComponent>() == nullptr);
-
-	LensFlareComponent* flareComp = newComponent<LensFlareComponent>(this);
-
-	const Error err = flareComp->init(filename);
-	if(err)
-	{
-		ANKI_ASSERT(!"TODO: Remove component");
-		return err;
-	}
-
-	return Error::NONE;
-}
-
-void LightNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData)
-{
-	for(const void* plight : userData)
-	{
-		const LightNode& self = *static_cast<const LightNode*>(plight);
-		const LightComponent& lcomp = self.getFirstComponentOfType<LightComponent>();
-
-		const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
-		if(enableDepthTest)
-		{
-			ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::LESS);
-		}
-		else
-		{
-			ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::ALWAYS);
-		}
-
-		Vec3 color = lcomp.getDiffuseColor().xyz();
-		color /= max(max(color.x(), color.y()), color.z());
-
-		self.m_dbgDrawer.drawBillboardTexture(
-			ctx.m_projectionMatrix, ctx.m_viewMatrix, lcomp.getTransform().getOrigin().xyz(), color.xyz1(),
-			ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON),
-			self.m_dbgTex->getGrTextureView(), ctx.m_sampler, Vec2(0.75f), *ctx.m_stagingGpuAllocator,
-			ctx.m_commandBuffer);
-
-		// Restore state
-		if(!enableDepthTest)
-		{
-			ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::LESS);
-		}
-	}
+	getFirstComponentOfType<LightComponent>().setWorldTransform(move.getWorldTransform());
 }
 }
 
 
 PointLightNode::PointLightNode(SceneGraph* scene, CString name)
 PointLightNode::PointLightNode(SceneGraph* scene, CString name)
 	: LightNode(scene, name)
 	: LightNode(scene, name)
 {
 {
+	newComponent<MoveComponent>();
+	newComponent<OnMovedFeedbackComponent>();
+
+	LightComponent* lc = newComponent<LightComponent>();
+	lc->setLightComponentType(LightComponentType::POINT);
+
+	newComponent<OnLightShapeUpdatedFeedbackComponent>();
+	newComponent<SpatialComponent>();
 }
 }
 
 
 PointLightNode::~PointLightNode()
 PointLightNode::~PointLightNode()
@@ -190,30 +135,7 @@ PointLightNode::~PointLightNode()
 	m_shadowData.destroy(getAllocator());
 	m_shadowData.destroy(getAllocator());
 }
 }
 
 
-Error PointLightNode::init()
-{
-	ANKI_CHECK(initCommon(LightComponentType::POINT));
-
-	// Move component
-	newComponent<MoveComponent>();
-
-	// Feedback component
-	newComponent<MovedFeedbackComponent>();
-
-	// Light component
-	LightComponent* lc = newComponent<LightComponent>(LightComponentType::POINT, getSceneGraph().getNewUuid());
-	lc->setDrawCallback(drawCallback, static_cast<LightNode*>(this));
-
-	// Feedback component
-	newComponent<LightChangedFeedbackComponent>();
-
-	// Spatial component
-	newComponent<SpatialComponent>(this, &m_sphereW);
-
-	return Error::NONE;
-}
-
-void PointLightNode::onMoveUpdate(const MoveComponent& move)
+void PointLightNode::onMoved(const MoveComponent& move)
 {
 {
 	onMoveUpdateCommon(move);
 	onMoveUpdateCommon(move);
 
 
@@ -223,36 +145,36 @@ void PointLightNode::onMoveUpdate(const MoveComponent& move)
 		Transform trf = m_shadowData[count].m_localTrf;
 		Transform trf = m_shadowData[count].m_localTrf;
 		trf.setOrigin(move.getWorldTransform().getOrigin());
 		trf.setOrigin(move.getWorldTransform().getOrigin());
 
 
-		fr.setTransform(trf);
+		fr.setWorldTransform(trf);
 		++count;
 		++count;
 
 
 		return Error::NONE;
 		return Error::NONE;
 	});
 	});
-
 	(void)err;
 	(void)err;
-
-	m_sphereW.setCenter(move.getWorldTransform().getOrigin());
 }
 }
 
 
-void PointLightNode::onShapeUpdate(LightComponent& light)
+void PointLightNode::onLightShapeUpdated(LightComponent& light)
 {
 {
-	Error err = iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& fr) -> Error {
+	const Error err = iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& fr) -> Error {
 		fr.setFar(light.getRadius());
 		fr.setFar(light.getRadius());
 		return Error::NONE;
 		return Error::NONE;
 	});
 	});
 	(void)err;
 	(void)err;
 
 
-	m_sphereW.setRadius(light.getRadius());
+	SpatialComponent& spatialc = getFirstComponentOfType<SpatialComponent>();
+	spatialc.setSphereWorldSpace(Sphere(light.getWorldTransform().getOrigin(), light.getRadius()));
 }
 }
 
 
 Error PointLightNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 Error PointLightNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 {
 {
-	if(getFirstComponentOfType<LightComponent>().getShadowEnabled() && m_shadowData.isEmpty())
+	// Lazily init
+	const LightComponent& lightc = getFirstComponentOfType<LightComponent>();
+	if(lightc.getShadowEnabled() && m_shadowData.isEmpty())
 	{
 	{
 		m_shadowData.create(getAllocator(), 6);
 		m_shadowData.create(getAllocator(), 6);
 
 
 		const F32 ang = toRad(90.0f);
 		const F32 ang = toRad(90.0f);
-		const F32 dist = m_sphereW.getRadius();
+		const F32 dist = lightc.getRadius();
 		const F32 zNear = LIGHT_FRUSTUM_NEAR_PLANE;
 		const F32 zNear = LIGHT_FRUSTUM_NEAR_PLANE;
 
 
 		Mat3 rot;
 		Mat3 rot;
@@ -276,9 +198,10 @@ Error PointLightNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 			Transform trf = m_shadowData[i].m_localTrf;
 			Transform trf = m_shadowData[i].m_localTrf;
 			trf.setOrigin(origin);
 			trf.setOrigin(origin);
 
 
-			FrustumComponent* frc = newComponent<FrustumComponent>(this, FrustumType::PERSPECTIVE);
+			FrustumComponent* frc = newComponent<FrustumComponent>();
+			frc->setFrustumType(FrustumType::PERSPECTIVE);
 			frc->setPerspective(zNear, dist, ang, ang);
 			frc->setPerspective(zNear, dist, ang, ang);
-			frc->setTransform(trf);
+			frc->setWorldTransform(trf);
 		}
 		}
 	}
 	}
 
 
@@ -287,43 +210,58 @@ Error PointLightNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
-SpotLightNode::SpotLightNode(SceneGraph* scene, CString name)
-	: LightNode(scene, name)
+class SpotLightNode::OnFrustumUpdatedFeedbackComponent : public SceneComponent
 {
 {
-}
+	ANKI_SCENE_COMPONENT(SpotLightNode::OnFrustumUpdatedFeedbackComponent)
 
 
-Error SpotLightNode::init()
-{
-	ANKI_CHECK(initCommon(LightComponentType::SPOT));
+public:
+	OnFrustumUpdatedFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
+	{
+	}
 
 
-	// Move component
-	newComponent<MoveComponent>();
+	Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
+	{
+		updated = false;
 
 
-	// Feedback component
-	newComponent<MovedFeedbackComponent>();
+		FrustumComponent& frc = node.getFirstComponentOfType<FrustumComponent>();
+		if(frc.getTimestamp() == node.getGlobalTimestamp())
+		{
+			// Shape updated
+			static_cast<SpotLightNode&>(node).onFrustumUpdated(frc);
+		}
+
+		return Error::NONE;
+	}
+};
+
+ANKI_SCENE_COMPONENT_STATICS(SpotLightNode::OnFrustumUpdatedFeedbackComponent)
+
+SpotLightNode::SpotLightNode(SceneGraph* scene, CString name)
+	: LightNode(scene, name)
+{
+	newComponent<MoveComponent>();
+	newComponent<OnMovedFeedbackComponent>();
 
 
-	// Light component
-	LightComponent* lc = newComponent<LightComponent>(LightComponentType::SPOT, getSceneGraph().getNewUuid());
-	lc->setDrawCallback(drawCallback, static_cast<LightNode*>(this));
+	LightComponent* lc = newComponent<LightComponent>();
+	lc->setLightComponentType(LightComponentType::SPOT);
 
 
-	// Feedback component
-	newComponent<LightChangedFeedbackComponent>();
+	newComponent<OnLightShapeUpdatedFeedbackComponent>();
 
 
-	// Frustum component
-	FrustumComponent* fr = newComponent<FrustumComponent>(this, FrustumType::PERSPECTIVE);
+	FrustumComponent* fr = newComponent<FrustumComponent>();
+	fr->setFrustumType(FrustumType::PERSPECTIVE);
 	fr->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE);
 	fr->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE);
 
 
-	// Spatial component
-	newComponent<SpatialComponent>(this, &fr->getPerspectiveBoundingShape());
+	newComponent<OnFrustumUpdatedFeedbackComponent>();
 
 
-	return Error::NONE;
+	newComponent<SpatialComponent>();
 }
 }
 
 
-void SpotLightNode::onMoveUpdate(const MoveComponent& move)
+void SpotLightNode::onMoved(const MoveComponent& move)
 {
 {
 	// Update the frustums
 	// Update the frustums
 	Error err = iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& fr) -> Error {
 	Error err = iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& fr) -> Error {
-		fr.setTransform(move.getWorldTransform());
+		fr.setWorldTransform(move.getWorldTransform());
 		return Error::NONE;
 		return Error::NONE;
 	});
 	});
 
 
@@ -332,15 +270,16 @@ void SpotLightNode::onMoveUpdate(const MoveComponent& move)
 	onMoveUpdateCommon(move);
 	onMoveUpdateCommon(move);
 }
 }
 
 
-void SpotLightNode::onShapeUpdate(LightComponent& light)
+void SpotLightNode::onLightShapeUpdated(LightComponent& light)
 {
 {
-	// Update the frustum first
 	FrustumComponent& frc = getFirstComponentOfType<FrustumComponent>();
 	FrustumComponent& frc = getFirstComponentOfType<FrustumComponent>();
 	frc.setPerspective(LIGHT_FRUSTUM_NEAR_PLANE, light.getDistance(), light.getOuterAngle(), light.getOuterAngle());
 	frc.setPerspective(LIGHT_FRUSTUM_NEAR_PLANE, light.getDistance(), light.getOuterAngle(), light.getOuterAngle());
+}
 
 
-	// Mark the spatial for update
+void SpotLightNode::onFrustumUpdated(FrustumComponent& frc)
+{
 	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
 	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
-	sp.markForUpdate();
+	sp.setConvexHullWorldSpace(frc.getPerspectiveBoundingShapeWorldSpace());
 }
 }
 
 
 Error SpotLightNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 Error SpotLightNode::frameUpdate(Second prevUpdateTime, Second crntTime)
@@ -351,9 +290,11 @@ Error SpotLightNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 
 
 class DirectionalLightNode::FeedbackComponent : public SceneComponent
 class DirectionalLightNode::FeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(DirectionalLightNode::FeedbackComponent)
+
 public:
 public:
-	FeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	FeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
 	}
 	}
 
 
@@ -364,38 +305,35 @@ public:
 		{
 		{
 			// Move updated
 			// Move updated
 			LightComponent& lightc = node.getFirstComponentOfType<LightComponent>();
 			LightComponent& lightc = node.getFirstComponentOfType<LightComponent>();
-			lightc.updateWorldTransform(move.getWorldTransform());
+			lightc.setWorldTransform(move.getWorldTransform());
 
 
 			SpatialComponent& spatialc = node.getFirstComponentOfType<SpatialComponent>();
 			SpatialComponent& spatialc = node.getFirstComponentOfType<SpatialComponent>();
-			spatialc.setSpatialOrigin(move.getWorldTransform().getOrigin());
-			spatialc.markForUpdate();
+			spatialc.setSpatialOrigin(move.getWorldTransform().getOrigin().xyz());
 		}
 		}
 
 
 		return Error::NONE;
 		return Error::NONE;
 	}
 	}
 };
 };
 
 
+ANKI_SCENE_COMPONENT_STATICS(DirectionalLightNode::FeedbackComponent)
+
 DirectionalLightNode::DirectionalLightNode(SceneGraph* scene, CString name)
 DirectionalLightNode::DirectionalLightNode(SceneGraph* scene, CString name)
 	: SceneNode(scene, name)
 	: SceneNode(scene, name)
-{
-}
-
-Error DirectionalLightNode::init()
 {
 {
 	newComponent<MoveComponent>();
 	newComponent<MoveComponent>();
 	newComponent<FeedbackComponent>();
 	newComponent<FeedbackComponent>();
 
 
-	LightComponent* lc = newComponent<LightComponent>(LightComponentType::DIRECTIONAL, getSceneGraph().getNewUuid());
-	lc->setDrawCallback(drawCallback, this);
+	LightComponent* lc = newComponent<LightComponent>();
+	lc->setLightComponentType(LightComponentType::DIRECTIONAL);
 
 
-	SpatialComponent* spatialc = newComponent<SpatialComponent>(this, &m_boundingBox);
+	SpatialComponent* spatialc = newComponent<SpatialComponent>();
 
 
 	// Make the bounding box large enough so it will always be visible. Because of that don't update the octree bounds
 	// Make the bounding box large enough so it will always be visible. Because of that don't update the octree bounds
-	m_boundingBox.setMin(getSceneGraph().getSceneMin());
-	m_boundingBox.setMax(getSceneGraph().getSceneMax());
+	Aabb boundingBox;
+	boundingBox.setMin(getSceneGraph().getSceneMin());
+	boundingBox.setMax(getSceneGraph().getSceneMax());
+	spatialc->setAabbWorldSpace(boundingBox);
 	spatialc->setUpdateOctreeBounds(false);
 	spatialc->setUpdateOctreeBounds(false);
-
-	return Error::NONE;
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 11 - 31
anki/scene/LightNode.h

@@ -6,11 +6,7 @@
 #pragma once
 #pragma once
 
 
 #include <anki/scene/SceneNode.h>
 #include <anki/scene/SceneNode.h>
-#include <anki/scene/Forward.h>
-#include <anki/scene/DebugDrawer.h>
 #include <anki/scene/components/LightComponent.h>
 #include <anki/scene/components/LightComponent.h>
-#include <anki/resource/TextureResource.h>
-#include <anki/Collision.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -26,28 +22,18 @@ public:
 
 
 	~LightNode();
 	~LightNode();
 
 
-	ANKI_USE_RESULT Error loadLensFlare(const CString& filename);
-
 protected:
 protected:
-	class MovedFeedbackComponent;
-	class LightChangedFeedbackComponent;
-
-	ANKI_USE_RESULT Error initCommon(LightComponentType lightType);
+	class OnMovedFeedbackComponent;
+	class OnLightShapeUpdatedFeedbackComponent;
 
 
 	/// Called when moved
 	/// Called when moved
 	void onMoveUpdateCommon(const MoveComponent& move);
 	void onMoveUpdateCommon(const MoveComponent& move);
 
 
 	void frameUpdateCommon();
 	void frameUpdateCommon();
 
 
-	virtual void onMoveUpdate(const MoveComponent& move) = 0;
-
-	virtual void onShapeUpdate(LightComponent& light) = 0;
+	virtual void onMoved(const MoveComponent& move) = 0;
 
 
-	static void drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData);
-
-private:
-	DebugDrawer2 m_dbgDrawer;
-	TextureResourcePtr m_dbgTex;
+	virtual void onLightShapeUpdated(LightComponent& light) = 0;
 };
 };
 
 
 /// Point light
 /// Point light
@@ -57,8 +43,6 @@ public:
 	PointLightNode(SceneGraph* scene, CString name);
 	PointLightNode(SceneGraph* scene, CString name);
 	~PointLightNode();
 	~PointLightNode();
 
 
-	ANKI_USE_RESULT Error init();
-
 	ANKI_USE_RESULT Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
 	ANKI_USE_RESULT Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
 
 
 private:
 private:
@@ -68,11 +52,10 @@ private:
 		Transform m_localTrf = Transform::getIdentity();
 		Transform m_localTrf = Transform::getIdentity();
 	};
 	};
 
 
-	Sphere m_sphereW = Sphere(Vec4(0.0f), 1.0f);
 	DynamicArray<ShadowCombo> m_shadowData;
 	DynamicArray<ShadowCombo> m_shadowData;
 
 
-	void onMoveUpdate(const MoveComponent& move) override;
-	void onShapeUpdate(LightComponent& light) override;
+	void onMoved(const MoveComponent& move) override;
+	void onLightShapeUpdated(LightComponent& light) override;
 };
 };
 
 
 /// Spot light
 /// Spot light
@@ -81,13 +64,14 @@ class SpotLightNode : public LightNode
 public:
 public:
 	SpotLightNode(SceneGraph* scene, CString name);
 	SpotLightNode(SceneGraph* scene, CString name);
 
 
-	ANKI_USE_RESULT Error init();
-
 	ANKI_USE_RESULT Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
 	ANKI_USE_RESULT Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
 
 
 private:
 private:
-	void onMoveUpdate(const MoveComponent& move) override;
-	void onShapeUpdate(LightComponent& light) override;
+	class OnFrustumUpdatedFeedbackComponent;
+
+	void onMoved(const MoveComponent& move) override;
+	void onLightShapeUpdated(LightComponent& light) override;
+	void onFrustumUpdated(FrustumComponent& frc);
 };
 };
 
 
 /// Directional light (the sun).
 /// Directional light (the sun).
@@ -96,13 +80,9 @@ class DirectionalLightNode : public SceneNode
 public:
 public:
 	DirectionalLightNode(SceneGraph* scene, CString name);
 	DirectionalLightNode(SceneGraph* scene, CString name);
 
 
-	ANKI_USE_RESULT Error init();
-
 private:
 private:
 	class FeedbackComponent;
 	class FeedbackComponent;
 
 
-	Aabb m_boundingBox;
-
 	static void drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData)
 	static void drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData)
 	{
 	{
 		// TODO
 		// TODO

+ 150 - 140
anki/scene/ModelNode.cpp

@@ -6,9 +6,11 @@
 #include <anki/scene/ModelNode.h>
 #include <anki/scene/ModelNode.h>
 #include <anki/scene/SceneGraph.h>
 #include <anki/scene/SceneGraph.h>
 #include <anki/scene/DebugDrawer.h>
 #include <anki/scene/DebugDrawer.h>
-#include <anki/scene/components/BodyComponent.h>
+#include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/SkinComponent.h>
 #include <anki/scene/components/SkinComponent.h>
+#include <anki/scene/components/SpatialComponent.h>
 #include <anki/scene/components/RenderComponent.h>
 #include <anki/scene/components/RenderComponent.h>
+#include <anki/scene/components/ModelComponent.h>
 #include <anki/resource/ModelResource.h>
 #include <anki/resource/ModelResource.h>
 #include <anki/resource/ResourceManager.h>
 #include <anki/resource/ResourceManager.h>
 #include <anki/resource/SkeletonResource.h>
 #include <anki/resource/SkeletonResource.h>
@@ -18,153 +20,154 @@ namespace anki
 {
 {
 
 
 /// Feedback component.
 /// Feedback component.
-class ModelNode::MoveFeedbackComponent : public SceneComponent
+class ModelNode::FeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(ModelNode::FeedbackComponent)
+
 public:
 public:
-	MoveFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	FeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
 	}
 	}
 
 
 	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
 	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
 	{
 	{
 		updated = false;
 		updated = false;
-
-		const MoveComponent& move = node.getFirstComponentOfType<MoveComponent>();
-		const SkinComponent* skin = node.tryGetFirstComponentOfType<SkinComponent>();
-		if(move.getTimestamp() == node.getGlobalTimestamp()
-		   || (skin && skin->getTimestamp() == node.getGlobalTimestamp()))
-		{
-			ModelNode& mnode = static_cast<ModelNode&>(node);
-			mnode.updateSpatialComponent(move);
-		}
-
+		static_cast<ModelNode&>(node).feedbackUpdate();
 		return Error::NONE;
 		return Error::NONE;
 	}
 	}
 };
 };
 
 
-/// Feedback component.
-class ModelNode::SkinFeedbackComponent : public SceneComponent
+ANKI_SCENE_COMPONENT_STATICS(ModelNode::FeedbackComponent)
+
+class ModelNode::RenderProxy
 {
 {
 public:
 public:
-	SkinFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
-	{
-	}
-
-	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
-	{
-		updated = false;
-
-		const SkinComponent& skin = node.getFirstComponentOfType<SkinComponent>();
-		if(skin.getTimestamp() == node.getGlobalTimestamp())
-		{
-			ModelNode& mnode = static_cast<ModelNode&>(node);
-
-			const Aabb& box = skin.getBoneBoundingVolume();
-			mnode.m_obbLocal.setCenter((box.getMin() + box.getMax()) / 2.0f);
-			mnode.m_obbLocal.setExtend(box.getMax() - mnode.m_obbLocal.getCenter());
-			mnode.m_obbLocal.setRotation(Mat3x4::getIdentity());
-		}
-
-		return Error::NONE;
-	}
+	ModelNode* m_node = nullptr;
 };
 };
 
 
 ModelNode::ModelNode(SceneGraph* scene, CString name)
 ModelNode::ModelNode(SceneGraph* scene, CString name)
 	: SceneNode(scene, name)
 	: SceneNode(scene, name)
 {
 {
+	newComponent<ModelComponent>();
+	newComponent<SkinComponent>();
+	newComponent<MoveComponent>();
+	newComponent<FeedbackComponent>();
+	newComponent<SpatialComponent>();
+	newComponent<RenderComponent>(); // One of many
+	m_renderProxies.create(getAllocator(), 1);
 }
 }
 
 
 ModelNode::~ModelNode()
 ModelNode::~ModelNode()
 {
 {
+	m_renderProxies.destroy(getAllocator());
 }
 }
 
 
-Error ModelNode::init(ModelResourcePtr resource, U32 modelPatchIdx)
+void ModelNode::feedbackUpdate()
 {
 {
-	ANKI_ASSERT(modelPatchIdx < resource->getModelPatches().getSize());
+	const ModelComponent& modelc = getFirstComponentOfType<ModelComponent>();
+	const SkinComponent& skinc = getFirstComponentOfType<SkinComponent>();
+	const MoveComponent& movec = getFirstComponentOfType<MoveComponent>();
 
 
-	ANKI_CHECK(m_dbgDrawer.init(&getResourceManager()));
-	m_model = resource;
-	m_modelPatchIdx = modelPatchIdx;
+	if(!modelc.isEnabled())
+	{
+		// Disable everything
+		ANKI_ASSERT(!"TODO");
+		return;
+	}
 
 
-	// Merge key
-	Array<U64, 2> toHash;
-	toHash[0] = modelPatchIdx;
-	toHash[1] = resource->getUuid();
-	m_mergeKey = computeHash(&toHash[0], sizeof(toHash));
+	const U64 globTimestamp = getGlobalTimestamp();
+	Bool updateSpatial = false;
 
 
-	// Components
-	if(m_model->getSkeleton().isCreated())
+	// Model update
+	if(modelc.getTimestamp() == globTimestamp)
 	{
 	{
-		newComponent<SkinComponent>(this, m_model->getSkeleton());
-		newComponent<SkinFeedbackComponent>();
+		m_aabbLocal = modelc.getModelResource()->getBoundingVolume();
+		updateSpatial = true;
+
+		if(modelc.getModelResource()->getModelPatches().getSize() == countComponentsOfType<RenderComponent>())
+		{
+			// Easy, just re-init the render components
+			initRenderComponents();
+		}
+		else
+		{
+			// Need to create more render components, deffer it
+			ANKI_ASSERT(!"TODO");
+		}
 	}
 	}
-	newComponent<MoveComponent>();
-	newComponent<MoveFeedbackComponent>();
-	newComponent<SpatialComponent>(this, &m_obbWorld);
-	RenderComponent* rcomp = newComponent<RenderComponent>();
-	rcomp->initRaster(
-		[](RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) {
-			const ModelNode& self = *static_cast<const ModelNode*>(userData[0]);
-			self.draw(ctx, userData);
-		},
-		this, m_mergeKey);
-	rcomp->setFlagsFromMaterial(m_model->getModelPatches()[m_modelPatchIdx].getMaterial());
-
-	if(m_model->getModelPatches()[m_modelPatchIdx].getSupportedRayTracingTypes() != RayTypeBit::NONE)
+
+	// Skin update
+	if(skinc.isEnabled() && skinc.getTimestamp() == globTimestamp)
 	{
 	{
-		rcomp->initRayTracing(setupRayTracingInstanceQueueElement, this);
+		m_aabbLocal = skinc.getBoneBoundingVolumeLocalSpace();
+		updateSpatial = true;
 	}
 	}
 
 
-	m_obbLocal = m_model->getModelPatches()[m_modelPatchIdx].getBoundingShape();
+	// Move update
+	if(movec.getTimestamp() == globTimestamp)
+	{
+		getFirstComponentOfType<SpatialComponent>().setSpatialOrigin(movec.getWorldTransform().getOrigin().xyz());
+		updateSpatial = true;
+	}
 
 
-	return Error::NONE;
+	// Spatial update
+	if(updateSpatial)
+	{
+		const Aabb aabbWorld = m_aabbLocal.getTransformed(movec.getWorldTransform());
+		getFirstComponentOfType<SpatialComponent>().setAabbWorldSpace(aabbWorld);
+	}
 }
 }
 
 
-Error ModelNode::init(const CString& modelFname)
+void ModelNode::initRenderComponents()
 {
 {
-	ModelResourcePtr model;
-	ANKI_CHECK(getResourceManager().loadResource(modelFname, model));
+	const ModelComponent& modelc = getFirstComponentOfType<ModelComponent>();
+	const ModelResourcePtr& model = modelc.getModelResource();
 
 
-	// Init this
-	ANKI_CHECK(init(model, 0));
+	ANKI_ASSERT(modelc.getModelResource()->getModelPatches().getSize() == countComponentsOfType<RenderComponent>());
+	ANKI_ASSERT(modelc.getModelResource()->getModelPatches().getSize() == m_renderProxies.getSize());
 
 
-	// Create separate nodes for the model patches and make the children
-	for(U32 i = 1; i < model->getModelPatches().getSize(); ++i)
+	for(U32 patchIdx = 0; patchIdx < model->getModelPatches().getSize(); ++patchIdx)
 	{
 	{
-		ModelNode* other;
-		ANKI_CHECK(getSceneGraph().newSceneNode(CString(), other, model, i));
+		RenderComponent& rc = getNthComponentOfType<RenderComponent>(patchIdx);
+		rc.initRaster(
+			[](RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) {
+				const RenderProxy& proxy = *static_cast<const RenderProxy*>(userData[0]);
+				const U32 modelPatchIdx = U32(&proxy - &proxy.m_node->m_renderProxies[0]);
+				proxy.m_node->draw(ctx, userData, modelPatchIdx);
+			},
+			&m_renderProxies[patchIdx], modelc.getRenderMergeKeys()[patchIdx]);
+
+		rc.setFlagsFromMaterial(model->getModelPatches()[patchIdx].getMaterial());
+
+		if(model->getModelPatches()[patchIdx].getSupportedRayTracingTypes() != RayTypeBit::NONE)
+		{
+			rc.initRayTracing(
+				[](U32 lod, const void* userData, RayTracingInstanceQueueElement& el) {
+					const RenderProxy& proxy = *static_cast<const RenderProxy*>(userData);
+					const U32 modelPatchIdx = U32(&proxy - &proxy.m_node->m_renderProxies[0]);
+					proxy.m_node->setupRayTracingInstanceQueueElement(lod, modelPatchIdx, el);
+				},
+				&m_renderProxies[patchIdx]);
+		}
 
 
-		addChild(other);
+		m_renderProxies[patchIdx].m_node = this;
 	}
 	}
-
-	return Error::NONE;
-}
-
-void ModelNode::updateSpatialComponent(const MoveComponent& move)
-{
-	m_obbWorld = m_obbLocal.getTransformed(move.getWorldTransform());
-
-	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
-	sp.markForUpdate();
-	sp.setSpatialOrigin(move.getWorldTransform().getOrigin());
 }
 }
 
 
-void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) const
+void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData, U32 modelPatchIdx) const
 {
 {
-	ANKI_ASSERT(userData.getSize() > 0 && userData.getSize() <= MAX_INSTANCES);
-	ANKI_ASSERT(ctx.m_key.getInstanceCount() == userData.getSize());
+	const U32 instanceCount = userData.getSize();
+	ANKI_ASSERT(instanceCount > 0 && instanceCount <= MAX_INSTANCES);
+	ANKI_ASSERT(ctx.m_key.getInstanceCount() == instanceCount);
 
 
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
 
 
 	if(ANKI_LIKELY(!ctx.m_debugDraw))
 	if(ANKI_LIKELY(!ctx.m_debugDraw))
 	{
 	{
-		const ModelPatch& patch = m_model->getModelPatches()[m_modelPatchIdx];
-
-		// 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.getSubMeshCount() == 1);
+		const ModelComponent& modelc = getFirstComponentOfType<ModelComponent>();
+		const ModelPatch& patch = modelc.getModelResource()->getModelPatches()[modelPatchIdx];
+		const SkinComponent& skinc = getFirstComponentOfType<SkinComponent>();
 
 
 		// Transforms
 		// Transforms
 		Array<Mat4, MAX_INSTANCES> trfs;
 		Array<Mat4, MAX_INSTANCES> trfs;
@@ -173,24 +176,30 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 		trfs[0] = Mat4(movec.getWorldTransform());
 		trfs[0] = Mat4(movec.getWorldTransform());
 		prevTrfs[0] = Mat4(movec.getPreviousWorldTransform());
 		prevTrfs[0] = Mat4(movec.getPreviousWorldTransform());
 		Bool moved = trfs[0] != prevTrfs[0];
 		Bool moved = trfs[0] != prevTrfs[0];
-		for(U32 i = 1; i < userData.getSize(); ++i)
+		for(U32 i = 1; i < instanceCount; ++i)
 		{
 		{
-			const ModelNode& self2 = *static_cast<const ModelNode*>(userData[i]);
-			const MoveComponent& movec = self2.getFirstComponentOfType<MoveComponent>();
-			trfs[i] = Mat4(movec.getWorldTransform());
-			prevTrfs[i] = Mat4(movec.getPreviousWorldTransform());
+			const ModelNode& otherNode = *static_cast<const RenderProxy*>(userData[i])->m_node;
+
+			const U32 otherNodeModelPatchIdx =
+				U32(static_cast<const RenderProxy*>(userData[i]) - &otherNode.m_renderProxies[0]);
+			(void)otherNodeModelPatchIdx;
+			ANKI_ASSERT(otherNodeModelPatchIdx == modelPatchIdx);
+
+			const MoveComponent& otherNodeMovec = otherNode.getFirstComponentOfType<MoveComponent>();
+			trfs[i] = Mat4(otherNodeMovec.getWorldTransform());
+			prevTrfs[i] = Mat4(otherNodeMovec.getPreviousWorldTransform());
 
 
 			moved = moved || (trfs[i] != prevTrfs[i]);
 			moved = moved || (trfs[i] != prevTrfs[i]);
 		}
 		}
 
 
 		ctx.m_key.setVelocity(moved && ctx.m_key.getPass() == Pass::GB);
 		ctx.m_key.setVelocity(moved && ctx.m_key.getPass() == Pass::GB);
+		ctx.m_key.setSkinned(skinc.isEnabled());
 		ModelRenderingInfo modelInf;
 		ModelRenderingInfo modelInf;
-		patch.getRenderingInfo(ctx.m_key, WeakArray<U8>(), modelInf);
+		patch.getRenderingInfo(ctx.m_key, modelInf);
 
 
 		// Bones storage
 		// Bones storage
-		if(m_model->getSkeleton())
+		if(skinc.isEnabled())
 		{
 		{
-			const SkinComponent& skinc = getComponentAt<SkinComponent>(0);
 			const U32 boneCount = skinc.getBoneTransforms().getSize();
 			const U32 boneCount = skinc.getBoneTransforms().getSize();
 			StagingGpuMemoryToken token, tokenPrev;
 			StagingGpuMemoryToken token, tokenPrev;
 			void* trfs = ctx.m_stagingGpuAllocator->allocateFrame(boneCount * sizeof(Mat4),
 			void* trfs = ctx.m_stagingGpuAllocator->allocateFrame(boneCount * sizeof(Mat4),
@@ -215,15 +224,15 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 		cmdb->bindShaderProgram(modelInf.m_program);
 		cmdb->bindShaderProgram(modelInf.m_program);
 
 
 		// Uniforms
 		// Uniforms
-		RenderComponent::allocateAndSetupUniforms(m_model->getModelPatches()[m_modelPatchIdx].getMaterial(), ctx,
-												  ConstWeakArray<Mat4>(&trfs[0], userData.getSize()),
-												  ConstWeakArray<Mat4>(&prevTrfs[0], userData.getSize()),
-												  *ctx.m_stagingGpuAllocator);
+		RenderComponent::allocateAndSetupUniforms(
+			modelc.getModelResource()->getModelPatches()[modelPatchIdx].getMaterial(), ctx,
+			ConstWeakArray<Mat4>(&trfs[0], instanceCount), ConstWeakArray<Mat4>(&prevTrfs[0], instanceCount),
+			*ctx.m_stagingGpuAllocator);
 
 
 		// Set attributes
 		// Set attributes
 		for(U i = 0; i < modelInf.m_vertexAttributeCount; ++i)
 		for(U i = 0; i < modelInf.m_vertexAttributeCount; ++i)
 		{
 		{
-			const VertexAttributeInfo& attrib = modelInf.m_vertexAttributes[i];
+			const ModelVertexAttribute& attrib = modelInf.m_vertexAttributes[i];
 			ANKI_ASSERT(attrib.m_format != Format::NONE);
 			ANKI_ASSERT(attrib.m_format != Format::NONE);
 			cmdb->setVertexAttribute(U32(attrib.m_location), attrib.m_bufferBinding, attrib.m_format,
 			cmdb->setVertexAttribute(U32(attrib.m_location), attrib.m_bufferBinding, attrib.m_format,
 									 attrib.m_relativeOffset);
 									 attrib.m_relativeOffset);
@@ -232,29 +241,27 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 		// Set vertex buffers
 		// Set vertex buffers
 		for(U32 i = 0; i < modelInf.m_vertexBufferBindingCount; ++i)
 		for(U32 i = 0; i < modelInf.m_vertexBufferBindingCount; ++i)
 		{
 		{
-			const VertexBufferBinding& binding = modelInf.m_vertexBufferBindings[i];
+			const ModelVertexBufferBinding& binding = modelInf.m_vertexBufferBindings[i];
 			cmdb->bindVertexBuffer(i, binding.m_buffer, binding.m_offset, binding.m_stride, VertexStepRate::VERTEX);
 			cmdb->bindVertexBuffer(i, binding.m_buffer, binding.m_offset, binding.m_stride, VertexStepRate::VERTEX);
 		}
 		}
 
 
 		// Index buffer
 		// Index buffer
-		cmdb->bindIndexBuffer(modelInf.m_indexBuffer, 0, IndexType::U16);
+		cmdb->bindIndexBuffer(modelInf.m_indexBuffer, modelInf.m_indexBufferOffset, IndexType::U16);
 
 
 		// Draw
 		// Draw
-		cmdb->drawElements(PrimitiveTopology::TRIANGLES, modelInf.m_indicesCountArray[0], userData.getSize(),
-						   U32(modelInf.m_indicesOffsetArray[0] / sizeof(U16)), 0, 0);
+		cmdb->drawElements(PrimitiveTopology::TRIANGLES, modelInf.m_indexCount, instanceCount, 0, 0, 0);
 	}
 	}
 	else
 	else
 	{
 	{
 		// Draw the bounding volumes
 		// Draw the bounding volumes
 
 
-		Mat4* const mvps = ctx.m_frameAllocator.newArray<Mat4>(userData.getSize());
-		for(U32 i = 0; i < userData.getSize(); ++i)
+		Mat4* const mvps = ctx.m_frameAllocator.newArray<Mat4>(instanceCount);
+		for(U32 i = 0; i < instanceCount; ++i)
 		{
 		{
-			const ModelNode& self2 = *static_cast<const ModelNode*>(userData[i]);
-
-			const Mat3 rot = self2.m_obbWorld.getRotation().getRotationPart();
-			const Vec4 tsl = self2.m_obbWorld.getCenter().xyz1();
-			const Vec3 scale = self2.m_obbWorld.getExtend().xyz();
+			const ModelNode& otherNode = *static_cast<const RenderProxy*>(userData[i])->m_node;
+			const Aabb& box = otherNode.getFirstComponentOfType<SpatialComponent>().getAabbWorldSpace();
+			const Vec4 tsl = (box.getMin() + box.getMax()) / 2.0f;
+			const Vec3 scale = (box.getMax().xyz() - box.getMin().xyz()) / 2.0f;
 
 
 			// Set non uniform scale. Add a margin to avoid flickering
 			// Set non uniform scale. Add a margin to avoid flickering
 			Mat3 nonUniScale = Mat3::getZero();
 			Mat3 nonUniScale = Mat3::getZero();
@@ -263,7 +270,7 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 			nonUniScale(1, 1) = scale.y() * MARGIN;
 			nonUniScale(1, 1) = scale.y() * MARGIN;
 			nonUniScale(2, 2) = scale.z() * MARGIN;
 			nonUniScale(2, 2) = scale.z() * MARGIN;
 
 
-			mvps[i] = ctx.m_viewProjectionMatrix * Mat4(tsl, rot * nonUniScale, 1.0f);
+			mvps[i] = ctx.m_viewProjectionMatrix * Mat4(tsl.xyz1(), Mat3::getIdentity() * nonUniScale, 1.0f);
 		}
 		}
 
 
 		const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
 		const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
@@ -276,14 +283,16 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 			cmdb->setDepthCompareOperation(CompareOperation::ALWAYS);
 			cmdb->setDepthCompareOperation(CompareOperation::ALWAYS);
 		}
 		}
 
 
-		m_dbgDrawer.drawCubes(ConstWeakArray<Mat4>(mvps, userData.getSize()), Vec4(1.0f, 0.0f, 1.0f, 1.0f), 2.0f,
-							  ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), 2.0f,
-							  *ctx.m_stagingGpuAllocator, cmdb);
+		getSceneGraph().getDebugDrawer().drawCubes(
+			ConstWeakArray<Mat4>(mvps, instanceCount), Vec4(1.0f, 0.0f, 1.0f, 1.0f), 2.0f,
+			ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), 2.0f,
+			*ctx.m_stagingGpuAllocator, cmdb);
 
 
-		ctx.m_frameAllocator.deleteArray(mvps, userData.getSize());
+		ctx.m_frameAllocator.deleteArray(mvps, instanceCount);
 
 
 		// Bones
 		// Bones
-		if(m_model->getSkeleton())
+		const SkinComponent& skinc = getFirstComponentOfType<SkinComponent>();
+		if(skinc.isEnabled())
 		{
 		{
 			const SkinComponent& skinc = getComponentAt<SkinComponent>(0);
 			const SkinComponent& skinc = getComponentAt<SkinComponent>(0);
 			SkeletonResourcePtr skeleton = skinc.getSkeleronResource();
 			SkeletonResourcePtr skeleton = skinc.getSkeleronResource();
@@ -321,13 +330,15 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 
 
 			const Mat4 mvp =
 			const Mat4 mvp =
 				ctx.m_viewProjectionMatrix * Mat4(getFirstComponentOfType<MoveComponent>().getWorldTransform());
 				ctx.m_viewProjectionMatrix * Mat4(getFirstComponentOfType<MoveComponent>().getWorldTransform());
-			m_dbgDrawer.drawLines(ConstWeakArray<Mat4>(&mvp, 1), Vec4(1.0f), 20.0f,
-								  ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), lines,
-								  *ctx.m_stagingGpuAllocator, cmdb);
-
-			m_dbgDrawer.drawLines(ConstWeakArray<Mat4>(&mvp, 1), Vec4(0.7f, 0.7f, 0.7f, 1.0f), 5.0f,
-								  ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON),
-								  chidlessLines, *ctx.m_stagingGpuAllocator, cmdb);
+			getSceneGraph().getDebugDrawer().drawLines(
+				ConstWeakArray<Mat4>(&mvp, 1), Vec4(1.0f), 20.0f,
+				ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), lines,
+				*ctx.m_stagingGpuAllocator, cmdb);
+
+			getSceneGraph().getDebugDrawer().drawLines(
+				ConstWeakArray<Mat4>(&mvp, 1), Vec4(0.7f, 0.7f, 0.7f, 1.0f), 5.0f,
+				ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), chidlessLines,
+				*ctx.m_stagingGpuAllocator, cmdb);
 		}
 		}
 
 
 		// Restore state
 		// Restore state
@@ -338,12 +349,11 @@ void ModelNode::draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData
 	}
 	}
 }
 }
 
 
-void ModelNode::setupRayTracingInstanceQueueElement(U32 lod, const void* userData, RayTracingInstanceQueueElement& el)
+void ModelNode::setupRayTracingInstanceQueueElement(U32 lod, U32 modelPatchIdx,
+													RayTracingInstanceQueueElement& el) const
 {
 {
-	const ModelNode& self = *static_cast<const ModelNode*>(userData);
-	const ModelPatch& patch = self.m_model->getModelPatches()[self.m_modelPatchIdx];
-
-	// printf("%s\n", patch.getMaterial()->getFilename().cstr());
+	const ModelComponent& modelc = getFirstComponentOfType<ModelComponent>();
+	const ModelPatch& patch = modelc.getModelResource()->getModelPatches()[modelPatchIdx];
 
 
 	ModelRayTracingInfo info;
 	ModelRayTracingInfo info;
 	patch.getRayTracingInfo(lod, info);
 	patch.getRayTracingInfo(lod, info);
@@ -355,7 +365,7 @@ void ModelNode::setupRayTracingInstanceQueueElement(U32 lod, const void* userDat
 
 
 	// Set the descriptor
 	// Set the descriptor
 	el.m_modelDescriptor = info.m_descriptor;
 	el.m_modelDescriptor = info.m_descriptor;
-	const MoveComponent& movec = self.getFirstComponentOfType<MoveComponent>();
+	const MoveComponent& movec = getFirstComponentOfType<MoveComponent>();
 	const Mat3x4 worldTrf(movec.getWorldTransform());
 	const Mat3x4 worldTrf(movec.getWorldTransform());
 	memcpy(&el.m_modelDescriptor.m_worldTransform, &worldTrf, sizeof(worldTrf));
 	memcpy(&el.m_modelDescriptor.m_worldTransform, &worldTrf, sizeof(worldTrf));
 	el.m_modelDescriptor.m_worldRotation = movec.getWorldTransform().getRotation().getRotationPart();
 	el.m_modelDescriptor.m_worldRotation = movec.getWorldTransform().getRotation().getRotationPart();

+ 12 - 27
anki/scene/ModelNode.h

@@ -6,20 +6,15 @@
 #pragma once
 #pragma once
 
 
 #include <anki/scene/SceneNode.h>
 #include <anki/scene/SceneNode.h>
-#include <anki/scene/components/MoveComponent.h>
-#include <anki/scene/components/SpatialComponent.h>
-#include <anki/scene/DebugDrawer.h>
-#include <anki/resource/ModelResource.h>
-#include <anki/collision/Obb.h>
-#include <anki/renderer/RenderQueue.h>
+#include <anki/collision/Aabb.h>
+#include <anki/util/WeakArray.h>
 
 
 namespace anki
 namespace anki
 {
 {
 
 
 // Forward
 // Forward
-class ObbSpatialComponent;
-class BodyComponent;
-class ModelNode;
+class RenderQueueDrawContext;
+class RayTracingInstanceQueueElement;
 
 
 /// @addtogroup scene
 /// @addtogroup scene
 /// @{
 /// @{
@@ -27,35 +22,25 @@ class ModelNode;
 /// The model scene node.
 /// The model scene node.
 class ModelNode : public SceneNode
 class ModelNode : public SceneNode
 {
 {
-	friend class ModelPatchNode;
-
 public:
 public:
 	ModelNode(SceneGraph* scene, CString name);
 	ModelNode(SceneGraph* scene, CString name);
 
 
 	~ModelNode();
 	~ModelNode();
 
 
-	ANKI_USE_RESULT Error init(const CString& modelFname);
-
-	ANKI_USE_RESULT Error init(ModelResourcePtr resource, U32 modelPatchIdx);
-
 private:
 private:
-	class MoveFeedbackComponent;
-	class SkinFeedbackComponent;
-
-	ModelResourcePtr m_model; ///< The resource
+	class FeedbackComponent;
+	class RenderProxy;
 
 
-	Obb m_obbLocal;
-	Obb m_obbWorld;
-	U64 m_mergeKey = 0;
-	U32 m_modelPatchIdx = 0;
+	Aabb m_aabbLocal;
+	DynamicArray<RenderProxy> m_renderProxies; ///< The size matches the number of render components.
 
 
-	DebugDrawer2 m_dbgDrawer;
+	void feedbackUpdate();
 
 
-	void updateSpatialComponent(const MoveComponent& move);
+	void draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData, U32 modelPatchIdx) const;
 
 
-	void draw(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) const;
+	void setupRayTracingInstanceQueueElement(U32 lod, U32 modelPatchIdx, RayTracingInstanceQueueElement& el) const;
 
 
-	static void setupRayTracingInstanceQueueElement(U32 lod, const void* userData, RayTracingInstanceQueueElement& el);
+	void initRenderComponents();
 };
 };
 /// @}
 /// @}
 
 

+ 0 - 85
anki/scene/OccluderNode.cpp

@@ -1,85 +0,0 @@
-// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <anki/scene/OccluderNode.h>
-#include <anki/scene/SceneGraph.h>
-#include <anki/scene/components/MoveComponent.h>
-#include <anki/scene/components/OccluderComponent.h>
-#include <anki/resource/MeshLoader.h>
-
-namespace anki
-{
-
-/// Feedback component.
-class OccluderNode::MoveFeedbackComponent : public SceneComponent
-{
-public:
-	MoveFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
-	{
-	}
-
-	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
-	{
-		updated = false;
-
-		MoveComponent& move = node.getFirstComponentOfType<MoveComponent>();
-		if(move.getTimestamp() == node.getGlobalTimestamp())
-		{
-			OccluderNode& mnode = static_cast<OccluderNode&>(node);
-			mnode.onMoveComponentUpdate(move);
-		}
-
-		return Error::NONE;
-	}
-};
-
-OccluderNode::~OccluderNode()
-{
-	m_vertsL.destroy(getAllocator());
-	m_vertsW.destroy(getAllocator());
-}
-
-Error OccluderNode::init(const CString& meshFname)
-{
-	// Load mesh
-	MeshLoader loader(&getSceneGraph().getResourceManager());
-	ANKI_CHECK(loader.load(meshFname));
-
-	const U32 indexCount = loader.getHeader().m_totalIndexCount;
-	m_vertsL.create(getAllocator(), indexCount);
-	m_vertsW.create(getAllocator(), indexCount);
-
-	DynamicArrayAuto<Vec3> positions(getAllocator());
-	DynamicArrayAuto<U32> indices(getAllocator());
-	ANKI_CHECK(loader.storeIndicesAndPosition(indices, positions));
-
-	for(U32 i = 0; i < indices.getSize(); ++i)
-	{
-		m_vertsL[i] = positions[indices[i]];
-	}
-
-	// Create the components
-	newComponent<MoveComponent>();
-	newComponent<MoveFeedbackComponent>();
-	newComponent<OccluderComponent>();
-
-	return Error::NONE;
-}
-
-void OccluderNode::onMoveComponentUpdate(MoveComponent& movec)
-{
-	const Transform& trf(movec.getWorldTransform());
-	U32 count = m_vertsL.getSize();
-	while(count--)
-	{
-
-		m_vertsW[count] = trf.transform(m_vertsL[count]);
-	}
-
-	getFirstComponentOfType<OccluderComponent>().setVertices(&m_vertsW[0], m_vertsW.getSize(), sizeof(m_vertsW[0]));
-}
-
-} // end namespace anki

+ 0 - 40
anki/scene/OccluderNode.h

@@ -1,40 +0,0 @@
-// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <anki/scene/SceneNode.h>
-#include <anki/Math.h>
-
-namespace anki
-{
-
-/// @addtogroup scene
-/// @{
-
-/// Occluder scene node.
-class OccluderNode : public SceneNode
-{
-public:
-	OccluderNode(SceneGraph* scene, CString name)
-		: SceneNode(scene, name)
-	{
-	}
-
-	~OccluderNode();
-
-	ANKI_USE_RESULT Error init(const CString& meshFname);
-
-private:
-	class MoveFeedbackComponent;
-
-	DynamicArray<Vec3> m_vertsL; ///< Verts in local space.
-	DynamicArray<Vec3> m_vertsW; ///< Verts in world space.
-
-	void onMoveComponentUpdate(MoveComponent& movec);
-};
-/// @}
-
-} // end namespace anki

+ 40 - 422
anki/scene/ParticleEmitterNode.cpp

@@ -4,485 +4,103 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <anki/scene/ParticleEmitterNode.h>
 #include <anki/scene/ParticleEmitterNode.h>
-#include <anki/scene/SceneGraph.h>
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
+#include <anki/scene/components/ParticleEmitterComponent.h>
 #include <anki/scene/components/RenderComponent.h>
 #include <anki/scene/components/RenderComponent.h>
-#include <anki/resource/ModelResource.h>
-#include <anki/resource/ResourceManager.h>
-#include <anki/util/Functions.h>
-#include <anki/physics/PhysicsBody.h>
-#include <anki/physics/PhysicsWorld.h>
-#include <anki/physics/PhysicsCollisionShape.h>
-#include <anki/Gr.h>
 
 
 namespace anki
 namespace anki
 {
 {
 
 
-static Vec3 getRandom(const Vec3& min, const Vec3& max)
-{
-	Vec3 out;
-	out.x() = mix(min.x(), max.x(), getRandomRange(0.0f, 1.0f));
-	out.y() = mix(min.y(), max.y(), getRandomRange(0.0f, 1.0f));
-	out.z() = mix(min.z(), max.z(), getRandomRange(0.0f, 1.0f));
-	return out;
-}
-
-/// Particle base
-class ParticleEmitterNode::ParticleBase
-{
-public:
-	Second m_timeOfBirth; ///< Keep the time of birth for nice effects
-	Second m_timeOfDeath = -1.0; ///< Time of death. If < 0.0 then dead
-
-	F32 m_initialSize;
-	F32 m_finalSize;
-	F32 m_crntSize;
-
-	F32 m_initialAlpha;
-	F32 m_finalAlpha;
-	F32 m_crntAlpha;
-
-	Vec4 m_crntPosition;
-
-	virtual ~ParticleBase()
-	{
-	}
-
-	Bool isDead() const
-	{
-		return m_timeOfDeath < 0.0;
-	}
-
-	/// Kill the particle
-	virtual void kill()
-	{
-		ANKI_ASSERT(m_timeOfDeath > 0.0);
-		m_timeOfDeath = -1.0;
-	}
-
-	/// Revive the particle
-	virtual void revive(const ParticleEmitterProperties& props, const Transform& trf, Second prevUpdateTime,
-						Second crntTime)
-	{
-		ANKI_ASSERT(isDead());
-
-		// life
-		m_timeOfDeath = crntTime + getRandomRange(props.m_particle.m_minLife, props.m_particle.m_maxLife);
-		m_timeOfBirth = crntTime;
-
-		// Size
-		m_initialSize = getRandomRange(props.m_particle.m_minInitialSize, props.m_particle.m_maxInitialSize);
-		m_finalSize = getRandomRange(props.m_particle.m_minFinalSize, props.m_particle.m_maxFinalSize);
-
-		// Alpha
-		m_initialAlpha = getRandomRange(props.m_particle.m_minInitialAlpha, props.m_particle.m_maxInitialAlpha);
-		m_finalAlpha = getRandomRange(props.m_particle.m_minFinalAlpha, props.m_particle.m_maxFinalAlpha);
-	}
-
-	/// Common sumulation code
-	virtual void simulate(Second prevUpdateTime, Second crntTime)
-	{
-		const F32 lifeFactor = F32((crntTime - m_timeOfBirth) / (m_timeOfDeath - m_timeOfBirth));
-
-		m_crntSize = mix(m_initialSize, m_finalSize, lifeFactor);
-		m_crntAlpha = mix(m_initialAlpha, m_finalAlpha, lifeFactor);
-	}
-};
-
-/// Simple particle for simple simulation
-class ParticleEmitterNode::ParticleSimple : public ParticleEmitterNode::ParticleBase
+/// Feedback component
+class ParticleEmitterNode::MoveFeedbackComponent : public SceneComponent
 {
 {
-public:
-	Vec4 m_velocity = Vec4(0.0);
-	Vec4 m_acceleration = Vec4(0.0);
-
-	void revive(const ParticleEmitterProperties& props, const Transform& trf, Second prevUpdateTime,
-				Second crntTime) override
-	{
-		ParticleBase::revive(props, trf, prevUpdateTime, crntTime);
-		m_velocity = Vec4(0.0);
-
-		m_acceleration = getRandom(props.m_particle.m_minGravity, props.m_particle.m_maxGravity).xyz0();
-
-		// Set the initial position
-		m_crntPosition =
-			getRandom(props.m_particle.m_minStartingPosition, props.m_particle.m_maxStartingPosition).xyz0();
-
-		m_crntPosition += trf.getOrigin();
-	}
-
-	void simulate(Second prevUpdateTime, Second crntTime) override
-	{
-		ParticleBase::simulate(prevUpdateTime, crntTime);
-
-		F32 dt = F32(crntTime - prevUpdateTime);
-
-		Vec4 xp = m_crntPosition;
-		Vec4 xc = m_acceleration * (dt * dt) + m_velocity * dt + xp;
+	ANKI_SCENE_COMPONENT(ParticleEmitterNode::MoveFeedbackComponent)
 
 
-		m_crntPosition = xc;
-
-		m_velocity += m_acceleration * dt;
-	}
-};
-
-/// Particle for bullet simulations
-class ParticleEmitterNode::PhysParticle : public ParticleEmitterNode::ParticleBase
-{
 public:
 public:
-	PhysicsBodyPtr m_body;
-
-	PhysParticle(const PhysicsBodyInitInfo& init, SceneNode* node)
+	MoveFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
-		m_body = node->getSceneGraph().getPhysicsWorld().newInstance<PhysicsBody>(init);
-		m_body->setUserData(node);
-		m_body->activate(false);
-		m_body->setMaterialGroup(PhysicsMaterialBit::PARTICLE);
-		m_body->setMaterialMask(PhysicsMaterialBit::STATIC_GEOMETRY);
-		m_body->setAngularFactor(Vec3(0.0f, 0.0f, 0.0f));
 	}
 	}
 
 
-	void kill() override
-	{
-		ParticleBase::kill();
-		m_body->activate(false);
-	}
-
-	void revive(const ParticleEmitterProperties& props, const Transform& trf, Second prevUpdateTime,
-				Second crntTime) override
+	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
 	{
 	{
-		ParticleBase::revive(props, trf, prevUpdateTime, crntTime);
-
-		// pre calculate
-		const Bool forceFlag = props.forceEnabled();
-		const Bool worldGravFlag = props.wordGravityEnabled();
-
-		// Activate it
-		m_body->activate(true);
-		m_body->setLinearVelocity(Vec3(0.0f));
-		m_body->setAngularVelocity(Vec3(0.0f));
-		m_body->clearForces();
-
-		// force
-		if(forceFlag)
-		{
-			Vec3 forceDir = getRandom(props.m_particle.m_minForceDirection, props.m_particle.m_maxForceDirection);
-			forceDir.normalize();
-
-			// the forceDir depends on the particle emitter rotation
-			forceDir = trf.getRotation().getRotationPart() * forceDir;
-
-			const F32 forceMag =
-				getRandomRange(props.m_particle.m_minForceMagnitude, props.m_particle.m_maxForceMagnitude);
-			m_body->applyForce(forceDir * forceMag, Vec3(0.0f));
-		}
+		updated = false; // Don't care about updates for this component
 
 
-		// gravity
-		if(!worldGravFlag)
+		MoveComponent& move = node.getFirstComponentOfType<MoveComponent>();
+		if(move.getTimestamp() == node.getGlobalTimestamp())
 		{
 		{
-			m_body->setGravity(getRandom(props.m_particle.m_minGravity, props.m_particle.m_maxGravity));
+			static_cast<ParticleEmitterNode&>(node).onMoveComponentUpdate(move);
 		}
 		}
 
 
-		// Starting pos. In local space
-		Vec3 pos = getRandom(props.m_particle.m_minStartingPosition, props.m_particle.m_maxStartingPosition);
-		pos = trf.transform(pos);
-
-		m_body->setTransform(Transform(pos.xyz0(), trf.getRotation(), 1.0f));
-		m_crntPosition = pos.xyz0();
-	}
-
-	void simulate(Second prevUpdateTime, Second crntTime) override
-	{
-		ParticleBase::simulate(prevUpdateTime, crntTime);
-		m_crntPosition = m_body->getTransform().getOrigin();
+		return Error::NONE;
 	}
 	}
 };
 };
 
 
+ANKI_SCENE_COMPONENT_STATICS(ParticleEmitterNode::MoveFeedbackComponent)
+
 /// Feedback component
 /// Feedback component
-class ParticleEmitterNode::MoveFeedbackComponent : public SceneComponent
+class ParticleEmitterNode::ShapeFeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(ParticleEmitterNode::ShapeFeedbackComponent)
+
 public:
 public:
-	MoveFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	ShapeFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), false // Not feedback because it's always being called
+		)
 	{
 	{
 	}
 	}
 
 
 	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
 	ANKI_USE_RESULT Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
 	{
 	{
-		updated = false; // Don't care about updates for this component
-
-		MoveComponent& move = node.getFirstComponentOfType<MoveComponent>();
-		if(move.getTimestamp() == node.getGlobalTimestamp())
-		{
-			static_cast<ParticleEmitterNode&>(node).onMoveComponentUpdate(move);
-		}
-
+		updated = false;
+		static_cast<ParticleEmitterNode&>(node).onShapeUpdate();
 		return Error::NONE;
 		return Error::NONE;
 	}
 	}
 };
 };
 
 
+ANKI_SCENE_COMPONENT_STATICS(ParticleEmitterNode::ShapeFeedbackComponent)
+
 ParticleEmitterNode::ParticleEmitterNode(SceneGraph* scene, CString name)
 ParticleEmitterNode::ParticleEmitterNode(SceneGraph* scene, CString name)
 	: SceneNode(scene, name)
 	: SceneNode(scene, name)
 {
 {
-}
-
-ParticleEmitterNode::~ParticleEmitterNode()
-{
-	// Delete simple particles
-	for(ParticleBase* part : m_particles)
-	{
-		getAllocator().deleteInstance(part);
-	}
-
-	m_particles.destroy(getAllocator());
-}
-
-Error ParticleEmitterNode::init(const CString& filename)
-{
-	// Load resource
-	ANKI_CHECK(getResourceManager().loadResource(filename, m_particleEmitterResource));
-
-	// Move component
+	// Components
 	newComponent<MoveComponent>();
 	newComponent<MoveComponent>();
 
 
-	// Move component feedback
 	newComponent<MoveFeedbackComponent>();
 	newComponent<MoveFeedbackComponent>();
 
 
-	// Spatial component
-	newComponent<SpatialComponent>(this, &m_obb);
-
-	// Render component
-	RenderComponent* rcomp = newComponent<RenderComponent>();
-	rcomp->initRaster(drawCallback, this, 0); // No merging
-	rcomp->setFlagsFromMaterial(m_particleEmitterResource->getMaterial());
+	ParticleEmitterComponent* particleEmitterc = newComponent<ParticleEmitterComponent>();
 
 
-	// Other
-	m_obb.setCenter(Vec4(0.0));
-	m_obb.setExtend(Vec4(1.0, 1.0, 1.0, 0.0));
-	m_obb.setRotation(Mat3x4::getIdentity());
+	newComponent<ShapeFeedbackComponent>();
+	newComponent<SpatialComponent>();
 
 
-	// copy the resource to me
-	ParticleEmitterProperties& me = *this;
-	const ParticleEmitterProperties& other = m_particleEmitterResource->getProperties();
-	me = other;
-
-	if(m_usePhysicsEngine)
-	{
-		createParticlesPhysicsSimulation(&getSceneGraph());
-		m_simulationType = SimulationType::PHYSICS_ENGINE;
-	}
-	else
-	{
-		createParticlesSimpleSimulation();
-		m_simulationType = SimulationType::SIMPLE;
-	}
-
-	// Create the vertex buffer and object
-	m_vertBuffSize = m_maxNumOfParticles * VERTEX_SIZE;
-
-	return Error::NONE;
+	RenderComponent* rcomp = newComponent<RenderComponent>();
+	rcomp->initRaster(ParticleEmitterComponent::drawCallback, particleEmitterc, 0); // No merging
 }
 }
 
 
-void ParticleEmitterNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData)
+ParticleEmitterNode::~ParticleEmitterNode()
 {
 {
-	ANKI_ASSERT(userData.getSize() == 1);
-
-	const ParticleEmitterNode& self = *static_cast<const ParticleEmitterNode*>(userData[0]);
-
-	// Early exit
-	if(ANKI_UNLIKELY(self.m_aliveParticlesCount == 0))
-	{
-		return;
-	}
-
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	if(!ctx.m_debugDraw)
-	{
-		// Load verts
-		StagingGpuMemoryToken token;
-		void* gpuStorage = ctx.m_stagingGpuAllocator->allocateFrame(self.m_aliveParticlesCount * VERTEX_SIZE,
-																	StagingGpuMemoryType::VERTEX, token);
-		memcpy(gpuStorage, self.m_verts, self.m_aliveParticlesCount * VERTEX_SIZE);
-
-		// Program
-		ShaderProgramPtr prog;
-		self.m_particleEmitterResource->getRenderingInfo(ctx.m_key, prog);
-		cmdb->bindShaderProgram(prog);
-
-		// Vertex attribs
-		cmdb->setVertexAttribute(0, 0, Format::R32G32B32_SFLOAT, 0);
-		cmdb->setVertexAttribute(1, 0, Format::R32_SFLOAT, sizeof(Vec3));
-		cmdb->setVertexAttribute(2, 0, Format::R32_SFLOAT, sizeof(Vec3) + sizeof(F32));
-
-		// Vertex buff
-		cmdb->bindVertexBuffer(0, token.m_buffer, token.m_offset, VERTEX_SIZE, VertexStepRate::INSTANCE);
-
-		// Uniforms
-		Array<Mat4, 1> trf = {Mat4::getIdentity()};
-		RenderComponent::allocateAndSetupUniforms(self.m_particleEmitterResource->getMaterial(), ctx, trf, trf,
-												  *ctx.m_stagingGpuAllocator);
-
-		// Draw
-		cmdb->drawArrays(PrimitiveTopology::TRIANGLE_STRIP, 4, self.m_aliveParticlesCount, 0, 0);
-	}
-	else
-	{
-		// TODO
-	}
 }
 }
 
 
 void ParticleEmitterNode::onMoveComponentUpdate(MoveComponent& move)
 void ParticleEmitterNode::onMoveComponentUpdate(MoveComponent& move)
 {
 {
-	m_identityRotation = move.getWorldTransform().getRotation() == Mat3x4::getIdentity();
-
-	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
-	sp.setSpatialOrigin(move.getWorldTransform().getOrigin());
-	sp.markForUpdate();
-}
-
-void ParticleEmitterNode::createParticlesPhysicsSimulation(SceneGraph* scene)
-{
-	PhysicsCollisionShapePtr collisionShape =
-		getSceneGraph().getPhysicsWorld().newInstance<PhysicsSphere>(m_particle.m_minInitialSize / 2.0f);
-
-	PhysicsBodyInitInfo binit;
-	binit.m_shape = collisionShape;
-
-	m_particles.create(getAllocator(), m_maxNumOfParticles);
-
-	for(U32 i = 0; i < m_maxNumOfParticles; i++)
-	{
-		binit.m_mass = getRandomRange(m_particle.m_minMass, m_particle.m_maxMass);
-
-		PhysParticle* part = getAllocator().newInstance<PhysParticle>(binit, this);
-
-		m_particles[i] = part;
-	}
+	getFirstComponentOfType<ParticleEmitterComponent>().setWorldTransform(move.getWorldTransform());
+	getFirstComponentOfType<SpatialComponent>().setSpatialOrigin(move.getWorldTransform().getOrigin().xyz());
 }
 }
 
 
-void ParticleEmitterNode::createParticlesSimpleSimulation()
+void ParticleEmitterNode::onShapeUpdate()
 {
 {
-	m_particles.create(getAllocator(), m_maxNumOfParticles);
-
-	for(U32 i = 0; i < m_maxNumOfParticles; i++)
-	{
-		ParticleSimple* part = getAllocator().newInstance<ParticleSimple>();
-
-		m_particles[i] = part;
-	}
+	const ParticleEmitterComponent& pec = getFirstComponentOfType<ParticleEmitterComponent>();
+	getFirstComponentOfType<SpatialComponent>().setAabbWorldSpace(pec.getAabbWorldSpace());
 }
 }
 
 
 Error ParticleEmitterNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 Error ParticleEmitterNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 {
 {
-	// - Deactivate the dead particles
-	// - Calc the AABB
-	// - Calc the instancing stuff
-	//
-	Vec4 aabbmin(MAX_F32, MAX_F32, MAX_F32, 0.0f);
-	Vec4 aabbmax(MIN_F32, MIN_F32, MIN_F32, 0.0f);
-	m_aliveParticlesCount = 0;
-
-	F32* verts = reinterpret_cast<F32*>(getFrameAllocator().allocate(m_vertBuffSize));
-	m_verts = verts;
-
-	const F32* verts_base = verts;
-	(void)verts_base;
-
-	F32 maxParticleSize = -1.0f;
-
-	for(ParticleBase* p : m_particles)
-	{
-		if(p->isDead())
-		{
-			// if its already dead so dont deactivate it again
-			continue;
-		}
-
-		if(p->m_timeOfDeath < crntTime)
-		{
-			// Just died
-			p->kill();
-		}
-		else
-		{
-			// It's alive
-
-			// Do checks
-			ANKI_ASSERT((PtrSize(verts) + VERTEX_SIZE - PtrSize(verts_base)) <= m_vertBuffSize);
-
-			// This will calculate a new world transformation
-			p->simulate(prevUpdateTime, crntTime);
-
-			const Vec4& origin = p->m_crntPosition;
-
-			aabbmin = aabbmin.min(origin);
-			aabbmax = aabbmax.max(origin);
-
-			verts[0] = origin.x();
-			verts[1] = origin.y();
-			verts[2] = origin.z();
-
-			verts[3] = p->m_crntSize;
-			maxParticleSize = max(maxParticleSize, p->m_crntSize);
-
-			verts[4] = clamp(p->m_crntAlpha, 0.0f, 1.0f);
-
-			++m_aliveParticlesCount;
-			verts += 5;
-		}
-	}
-
-	if(m_aliveParticlesCount != 0)
-	{
-		ANKI_ASSERT(maxParticleSize > 0.0f);
-		Vec4 min = aabbmin - maxParticleSize;
-		Vec4 max = aabbmax + maxParticleSize;
-		Vec4 center = (min + max) / 2.0;
-
-		m_obb = Obb(center.xyz0(), Mat3x4::getIdentity(), (max - center).xyz0());
-	}
-	else
-	{
-		m_obb = Obb(Vec4(0.0), Mat3x4::getIdentity(), Vec4(Vec3(0.001f), 0.0f));
-		m_verts = nullptr;
-	}
-
-	getFirstComponentOfType<SpatialComponent>().markForUpdate();
-
-	//
-	// Emit new particles
-	//
-	if(m_timeLeftForNextEmission <= 0.0)
-	{
-		MoveComponent& move = getFirstComponentOfType<MoveComponent>();
-
-		U particlesCount = 0; // How many particles I am allowed to emmit
-		for(ParticleBase* pp : m_particles)
-		{
-			ParticleBase& p = *pp;
-			if(!p.isDead())
-			{
-				// its alive so skip it
-				continue;
-			}
-
-			p.revive(*this, move.getWorldTransform(), prevUpdateTime, crntTime);
-
-			// do the rest
-			++particlesCount;
-			if(particlesCount >= m_particlesPerEmission)
-			{
-				break;
-			}
-		} // end for all particles
-
-		m_timeLeftForNextEmission = m_emissionPeriod;
-	} // end if can emit
-	else
+	const ParticleEmitterComponent& pec = getFirstComponentOfType<ParticleEmitterComponent>();
+	if(pec.isEnabled())
 	{
 	{
-		m_timeLeftForNextEmission -= crntTime - prevUpdateTime;
+		RenderComponent& rc = getFirstComponentOfType<RenderComponent>();
+		rc.setFlagsFromMaterial(pec.getParticleEmitterResource()->getMaterial());
 	}
 	}
 
 
 	return Error::NONE;
 	return Error::NONE;

+ 4 - 47
anki/scene/ParticleEmitterNode.h

@@ -6,10 +6,6 @@
 #pragma once
 #pragma once
 
 
 #include <anki/scene/SceneNode.h>
 #include <anki/scene/SceneNode.h>
-#include <anki/resource/ParticleEmitterResource.h>
-#include <anki/renderer/RenderQueue.h>
-#include <anki/collision/Obb.h>
-#include <anki/physics/Forward.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -18,60 +14,21 @@ namespace anki
 /// @{
 /// @{
 
 
 /// The particle emitter scene node. This scene node emitts
 /// The particle emitter scene node. This scene node emitts
-class ParticleEmitterNode : public SceneNode, private ParticleEmitterProperties
+class ParticleEmitterNode : public SceneNode
 {
 {
 public:
 public:
 	ParticleEmitterNode(SceneGraph* scene, CString name);
 	ParticleEmitterNode(SceneGraph* scene, CString name);
 
 
 	~ParticleEmitterNode();
 	~ParticleEmitterNode();
 
 
-	ANKI_USE_RESULT Error init(const CString& filename);
-
-	/// @name SceneNode virtuals
-	/// @{
-	ANKI_USE_RESULT Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
-	/// @}
+	Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
 
 
 private:
 private:
 	class MoveFeedbackComponent;
 	class MoveFeedbackComponent;
-	class ParticleBase;
-	class ParticleSimple;
-	class PhysParticle;
-
-	enum class SimulationType : U8
-	{
-		UNDEFINED,
-		SIMPLE,
-		PHYSICS_ENGINE
-	};
-
-	/// Size of a single vertex.
-	static const U32 VERTEX_SIZE = 5 * sizeof(F32);
-
-	ParticleEmitterResourcePtr m_particleEmitterResource;
-	DynamicArray<ParticleBase*> m_particles;
-	Second m_timeLeftForNextEmission = 0.0;
-	Obb m_obb;
-
-	// Opt: We dont have to make extra calculations if the ParticleEmitterNode's rotation is the identity
-	Bool m_identityRotation = true;
-
-	U32 m_aliveParticlesCount = 0;
-
-	/// @name Graphics
-	/// @{
-	U32 m_vertBuffSize = 0;
-	void* m_verts = nullptr;
-	/// @}
-
-	SimulationType m_simulationType = SimulationType::UNDEFINED;
-
-	void createParticlesPhysicsSimulation(SceneGraph* scene);
-	void createParticlesSimpleSimulation();
+	class ShapeFeedbackComponent;
 
 
 	void onMoveComponentUpdate(MoveComponent& move);
 	void onMoveComponentUpdate(MoveComponent& move);
-
-	static void drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData);
+	void onShapeUpdate();
 };
 };
 /// @}
 /// @}
 
 

+ 21 - 50
anki/scene/PhysicsDebugNode.cpp

@@ -4,71 +4,42 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <anki/scene/PhysicsDebugNode.h>
 #include <anki/scene/PhysicsDebugNode.h>
-#include <anki/resource/ResourceManager.h>
 #include <anki/scene/components/RenderComponent.h>
 #include <anki/scene/components/RenderComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
 #include <anki/scene/SceneGraph.h>
 #include <anki/scene/SceneGraph.h>
-#include <anki/scene/DebugDrawer.h>
+#include <anki/resource/ResourceManager.h>
 
 
 namespace anki
 namespace anki
 {
 {
 
 
-/// Render component implementation.
-class PhysicsDebugNode::MyRenderComponent : public RenderComponent
+PhysicsDebugNode::PhysicsDebugNode(SceneGraph* scene, CString name)
+	: SceneNode(scene, name)
+	, m_physDbgDrawer(&scene->getDebugDrawer())
 {
 {
-public:
-	SceneNode* m_node;
-	DebugDrawer m_dbgDrawer;
-	PhysicsDebugDrawer m_physDbgDrawer;
-
-	MyRenderComponent(SceneNode* node)
-		: m_node(node)
-		, m_physDbgDrawer(&m_dbgDrawer)
-	{
-		ANKI_ASSERT(node);
-		setFlags(RenderComponentFlag::NONE);
-		initRaster([](RenderQueueDrawContext& ctx,
-					  ConstWeakArray<void*> userData) { static_cast<MyRenderComponent*>(userData[0])->draw(ctx); },
-				   this, 0);
-	}
+	RenderComponent* rcomp = newComponent<RenderComponent>();
+	rcomp->setFlags(RenderComponentFlag::NONE);
+	rcomp->initRaster([](RenderQueueDrawContext& ctx,
+						 ConstWeakArray<void*> userData) { static_cast<PhysicsDebugNode*>(userData[0])->draw(ctx); },
+					  this, 0);
 
 
-	ANKI_USE_RESULT Error init()
-	{
-		return m_dbgDrawer.init(&m_node->getSceneGraph().getResourceManager());
-	}
-
-	/// Draw the world.
-	void draw(RenderQueueDrawContext& ctx)
-	{
-		if(ctx.m_debugDraw)
-		{
-			m_dbgDrawer.prepareFrame(&ctx);
-			m_dbgDrawer.setViewProjectionMatrix(ctx.m_viewProjectionMatrix);
-			m_dbgDrawer.setModelMatrix(Mat4::getIdentity());
-			m_physDbgDrawer.drawWorld(m_node->getSceneGraph().getPhysicsWorld());
-			m_dbgDrawer.finishFrame();
-		}
-	}
-};
+	SpatialComponent* scomp = newComponent<SpatialComponent>();
+	scomp->setUpdateOctreeBounds(false); // Don't mess with the bounds
+	scomp->setAabbWorldSpace(Aabb(getSceneGraph().getSceneMin(), getSceneGraph().getSceneMax()));
+	scomp->setSpatialOrigin(Vec3(0.0f));
+}
 
 
 PhysicsDebugNode::~PhysicsDebugNode()
 PhysicsDebugNode::~PhysicsDebugNode()
 {
 {
 }
 }
 
 
-Error PhysicsDebugNode::init()
+void PhysicsDebugNode::draw(RenderQueueDrawContext& ctx)
 {
 {
-	MyRenderComponent* rcomp = newComponent<MyRenderComponent>(this);
-	ANKI_CHECK(rcomp->init());
-
-	ObbSpatialComponent* scomp = newComponent<ObbSpatialComponent>(this);
-	const Vec3 center = (getSceneGraph().getSceneMax() + getSceneGraph().getSceneMin()) / 2.0f;
-	scomp->m_obb.setCenter(center.xyz0());
-	scomp->m_obb.setExtend((getSceneGraph().getSceneMax() - center).xyz0());
-	scomp->m_obb.setRotation(Mat3x4::getIdentity());
-	scomp->setSpatialOrigin(Vec4(0.0f));
-	scomp->setUpdateOctreeBounds(false); // Don't mess with the bounds
-
-	return Error::NONE;
+	if(ctx.m_debugDraw)
+	{
+		m_physDbgDrawer.start(ctx.m_viewProjectionMatrix, ctx.m_commandBuffer, ctx.m_stagingGpuAllocator);
+		m_physDbgDrawer.drawWorld(getSceneGraph().getPhysicsWorld());
+		m_physDbgDrawer.end();
+	}
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 6 - 8
anki/scene/PhysicsDebugNode.h

@@ -6,6 +6,7 @@
 #pragma once
 #pragma once
 
 
 #include <anki/scene/SceneNode.h>
 #include <anki/scene/SceneNode.h>
+#include <anki/scene/DebugDrawer.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -14,17 +15,14 @@ namespace anki
 class PhysicsDebugNode : public SceneNode
 class PhysicsDebugNode : public SceneNode
 {
 {
 public:
 public:
-	PhysicsDebugNode(SceneGraph* scene, CString name)
-		: SceneNode(scene, name)
-	{
-	}
+	PhysicsDebugNode(SceneGraph* scene, CString name);
 
 
 	~PhysicsDebugNode();
 	~PhysicsDebugNode();
 
 
-	ANKI_USE_RESULT Error init();
-
 private:
 private:
-	class MyRenderComponent;
+	PhysicsDebugDrawer m_physDbgDrawer;
+
+	void draw(RenderQueueDrawContext& ctx);
 };
 };
 
 
-} // end namespace anki
+} // end namespace anki

+ 18 - 28
anki/scene/PlayerNode.cpp

@@ -17,9 +17,11 @@ namespace anki
 /// Feedback component.
 /// Feedback component.
 class PlayerNode::FeedbackComponent final : public SceneComponent
 class PlayerNode::FeedbackComponent final : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(PlayerNode::FeedbackComponent)
+
 public:
 public:
-	FeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	FeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), false)
 	{
 	{
 	}
 	}
 
 
@@ -38,7 +40,7 @@ public:
 			MoveComponent& move = node.getFirstComponentOfType<MoveComponent>();
 			MoveComponent& move = node.getFirstComponentOfType<MoveComponent>();
 
 
 			// Set origin
 			// Set origin
-			Vec4 origin = playerc.getTransform().getOrigin();
+			Vec4 origin = playerc.getWorldTransform().getOrigin();
 			origin.y() += 1.9f;
 			origin.y() += 1.9f;
 
 
 			// Set rotation
 			// Set rotation
@@ -60,12 +62,17 @@ public:
 	}
 	}
 };
 };
 
 
+ANKI_SCENE_COMPONENT_STATICS(PlayerNode::FeedbackComponent)
+
 /// Feedback component.
 /// Feedback component.
 class PlayerNode::FeedbackComponent2 final : public SceneComponent
 class PlayerNode::FeedbackComponent2 final : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(PlayerNode::FeedbackComponent2)
+
 public:
 public:
-	FeedbackComponent2()
-		: SceneComponent(SceneComponentType::NONE)
+	FeedbackComponent2(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), false // This shouldn't behave as a feedback component
+		)
 	{
 	{
 	}
 	}
 
 
@@ -110,36 +117,19 @@ public:
 	}
 	}
 };
 };
 
 
+ANKI_SCENE_COMPONENT_STATICS(PlayerNode::FeedbackComponent2)
+
 PlayerNode::PlayerNode(SceneGraph* scene, CString name)
 PlayerNode::PlayerNode(SceneGraph* scene, CString name)
 	: SceneNode(scene, name)
 	: SceneNode(scene, name)
 {
 {
-}
-
-PlayerNode::~PlayerNode()
-{
-}
-
-Error PlayerNode::init(const Vec4& position)
-{
-	// Create physics object
-	PhysicsPlayerControllerInitInfo init;
-	init.m_position = position;
-	m_player = getSceneGraph().getPhysicsWorld().newInstance<PhysicsPlayerController>(init);
-	m_player->setUserData(this);
-
-	// Player controller component
-	newComponent<PlayerControllerComponent>(m_player);
-
-	// Feedback component
+	newComponent<PlayerControllerComponent>();
 	newComponent<FeedbackComponent>();
 	newComponent<FeedbackComponent>();
-
-	// Move component
 	newComponent<MoveComponent>();
 	newComponent<MoveComponent>();
-
-	// Feedback component #2
 	newComponent<FeedbackComponent2>();
 	newComponent<FeedbackComponent2>();
+}
 
 
-	return Error::NONE;
+PlayerNode::~PlayerNode()
+{
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 0 - 5
anki/scene/PlayerNode.h

@@ -7,7 +7,6 @@
 
 
 #include <anki/scene/SceneNode.h>
 #include <anki/scene/SceneNode.h>
 #include <anki/Math.h>
 #include <anki/Math.h>
-#include <anki/physics/PhysicsPlayerController.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -23,13 +22,9 @@ public:
 
 
 	~PlayerNode();
 	~PlayerNode();
 
 
-	ANKI_USE_RESULT Error init(const Vec4& position);
-
 private:
 private:
 	class FeedbackComponent;
 	class FeedbackComponent;
 	class FeedbackComponent2;
 	class FeedbackComponent2;
-
-	PhysicsPlayerControllerPtr m_player;
 };
 };
 /// @}
 /// @}
 
 

+ 79 - 102
anki/scene/ReflectionProbeNode.cpp

@@ -9,7 +9,6 @@
 #include <anki/scene/components/FrustumComponent.h>
 #include <anki/scene/components/FrustumComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
 #include <anki/scene/components/SpatialComponent.h>
 #include <anki/scene/SceneGraph.h>
 #include <anki/scene/SceneGraph.h>
-#include <anki/scene/SceneGraph.h>
 #include <anki/renderer/LightShading.h>
 #include <anki/renderer/LightShading.h>
 #include <anki/shaders/include/ClusteredShadingTypes.h>
 #include <anki/shaders/include/ClusteredShadingTypes.h>
 
 
@@ -23,9 +22,11 @@ const FrustumComponentVisibilityTestFlag FRUSTUM_TEST_FLAGS =
 /// Feedback component
 /// Feedback component
 class ReflectionProbeNode::MoveFeedbackComponent : public SceneComponent
 class ReflectionProbeNode::MoveFeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(ReflectionProbeNode::MoveFeedbackComponent)
+
 public:
 public:
-	MoveFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	MoveFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
 	}
 	}
 
 
@@ -45,18 +46,39 @@ public:
 	}
 	}
 };
 };
 
 
-ReflectionProbeNode::~ReflectionProbeNode()
-{
-}
+ANKI_SCENE_COMPONENT_STATICS(ReflectionProbeNode::MoveFeedbackComponent)
 
 
-Error ReflectionProbeNode::init(const Vec4& aabbMinLSpace, const Vec4& aabbMaxLSpace)
+/// Feedback component
+class ReflectionProbeNode::ShapeFeedbackComponent : public SceneComponent
 {
 {
-	// Compute effective distance
-	F32 effectiveDistance = aabbMaxLSpace.x() - aabbMinLSpace.x();
-	effectiveDistance = max(effectiveDistance, aabbMaxLSpace.y() - aabbMinLSpace.y());
-	effectiveDistance = max(effectiveDistance, aabbMaxLSpace.z() - aabbMinLSpace.z());
-	effectiveDistance = max(effectiveDistance, getSceneGraph().getConfig().m_reflectionProbeEffectiveDistance);
+	ANKI_SCENE_COMPONENT(ReflectionProbeNode::ShapeFeedbackComponent)
+
+public:
+	ShapeFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
+	{
+	}
+
+	Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
+	{
+		updated = false;
+
+		ReflectionProbeComponent& reflc = node.getFirstComponentOfType<ReflectionProbeComponent>();
+		if(reflc.getTimestamp() == node.getGlobalTimestamp())
+		{
+			ReflectionProbeNode& dnode = static_cast<ReflectionProbeNode&>(node);
+			dnode.onShapeUpdate(reflc);
+		}
 
 
+		return Error::NONE;
+	}
+};
+
+ANKI_SCENE_COMPONENT_STATICS(ReflectionProbeNode::ShapeFeedbackComponent)
+
+ReflectionProbeNode::ReflectionProbeNode(SceneGraph* scene, CString name)
+	: SceneNode(scene, name)
+{
 	// Move component first
 	// Move component first
 	newComponent<MoveComponent>();
 	newComponent<MoveComponent>();
 
 
@@ -65,54 +87,48 @@ Error ReflectionProbeNode::init(const Vec4& aabbMinLSpace, const Vec4& aabbMaxLS
 
 
 	// The frustum components
 	// The frustum components
 	const F32 ang = toRad(90.0f);
 	const F32 ang = toRad(90.0f);
-	const F32 zNear = LIGHT_FRUSTUM_NEAR_PLANE;
 
 
 	Mat3 rot;
 	Mat3 rot;
 
 
 	rot = Mat3(Euler(0.0f, -PI / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, PI));
 	rot = Mat3(Euler(0.0f, -PI / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, PI));
-	m_cubeSides[0].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot));
+	m_frustumTransforms[0].setRotation(Mat3x4(Vec3(0.0f), rot));
 	rot = Mat3(Euler(0.0f, PI / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, PI));
 	rot = Mat3(Euler(0.0f, PI / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, PI));
-	m_cubeSides[1].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot));
+	m_frustumTransforms[1].setRotation(Mat3x4(Vec3(0.0f), rot));
 	rot = Mat3(Euler(PI / 2.0f, 0.0f, 0.0f));
 	rot = Mat3(Euler(PI / 2.0f, 0.0f, 0.0f));
-	m_cubeSides[2].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot));
+	m_frustumTransforms[2].setRotation(Mat3x4(Vec3(0.0f), rot));
 	rot = Mat3(Euler(-PI / 2.0f, 0.0f, 0.0f));
 	rot = Mat3(Euler(-PI / 2.0f, 0.0f, 0.0f));
-	m_cubeSides[3].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot));
+	m_frustumTransforms[3].setRotation(Mat3x4(Vec3(0.0f), rot));
 	rot = Mat3(Euler(0.0f, PI, 0.0f)) * Mat3(Euler(0.0f, 0.0f, PI));
 	rot = Mat3(Euler(0.0f, PI, 0.0f)) * Mat3(Euler(0.0f, 0.0f, PI));
-	m_cubeSides[4].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot));
+	m_frustumTransforms[4].setRotation(Mat3x4(Vec3(0.0f), rot));
 	rot = Mat3(Euler(0.0f, 0.0f, PI));
 	rot = Mat3(Euler(0.0f, 0.0f, PI));
-	m_cubeSides[5].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot));
+	m_frustumTransforms[5].setRotation(Mat3x4(Vec3(0.0f), rot));
 
 
 	for(U i = 0; i < 6; ++i)
 	for(U i = 0; i < 6; ++i)
 	{
 	{
-		m_cubeSides[i].m_localTrf.setOrigin(Vec4(0.0f));
-		m_cubeSides[i].m_localTrf.setScale(1.0f);
+		m_frustumTransforms[i].setOrigin(Vec4(0.0f));
+		m_frustumTransforms[i].setScale(1.0f);
 
 
-		FrustumComponent* frc = newComponent<FrustumComponent>(this, FrustumType::PERSPECTIVE);
-		frc->setPerspective(zNear, effectiveDistance, ang, ang);
-		frc->setTransform(m_cubeSides[i].m_localTrf);
+		FrustumComponent* frc = newComponent<FrustumComponent>();
+		frc->setFrustumType(FrustumType::PERSPECTIVE);
+		frc->setPerspective(LIGHT_FRUSTUM_NEAR_PLANE, 10.0f, ang, ang);
+		frc->setWorldTransform(m_frustumTransforms[i]);
 		frc->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE);
 		frc->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::NONE);
 		frc->setEffectiveShadowDistance(getSceneGraph().getConfig().m_reflectionProbeShadowEffectiveDistance);
 		frc->setEffectiveShadowDistance(getSceneGraph().getConfig().m_reflectionProbeShadowEffectiveDistance);
 	}
 	}
 
 
-	// Spatial component
-	m_aabbMinLSpace = aabbMinLSpace.xyz();
-	m_aabbMaxLSpace = aabbMaxLSpace.xyz();
-	m_spatialAabb.setMin(aabbMinLSpace);
-	m_spatialAabb.setMax(aabbMaxLSpace);
-	SpatialComponent* spatialc = newComponent<SpatialComponent>(this, &m_spatialAabb);
-	spatialc->setUpdateOctreeBounds(false);
-
 	// Reflection probe comp
 	// Reflection probe comp
-	ReflectionProbeComponent* reflc = newComponent<ReflectionProbeComponent>(getSceneGraph().getNewUuid());
-	reflc->setPosition(Vec4(0.0f));
-	reflc->setBoundingBox(aabbMinLSpace, aabbMaxLSpace);
-	reflc->setDrawCallback(drawCallback, this);
+	newComponent<ReflectionProbeComponent>();
 
 
-	// Misc
-	ANKI_CHECK(m_dbgDrawer.init(&getResourceManager()));
-	ANKI_CHECK(getResourceManager().loadResource("engine_data/Mirror.ankitex", m_dbgTex));
+	// Feedback
+	newComponent<ShapeFeedbackComponent>();
 
 
-	return Error::NONE;
+	// Spatial component
+	SpatialComponent* spatialc = newComponent<SpatialComponent>();
+	spatialc->setUpdateOctreeBounds(false);
+}
+
+ReflectionProbeNode::~ReflectionProbeNode()
+{
 }
 }
 
 
 void ReflectionProbeNode::onMoveUpdate(MoveComponent& move)
 void ReflectionProbeNode::onMoveUpdate(MoveComponent& move)
@@ -120,10 +136,10 @@ void ReflectionProbeNode::onMoveUpdate(MoveComponent& move)
 	// Update frustum components
 	// Update frustum components
 	U count = 0;
 	U count = 0;
 	Error err = iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) -> Error {
 	Error err = iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) -> Error {
-		Transform trf = m_cubeSides[count].m_localTrf;
+		Transform trf = m_frustumTransforms[count];
 		trf.setOrigin(move.getWorldTransform().getOrigin());
 		trf.setOrigin(move.getWorldTransform().getOrigin());
 
 
-		frc.setTransform(trf);
+		frc.setWorldTransform(trf);
 		++count;
 		++count;
 
 
 		return Error::NONE;
 		return Error::NONE;
@@ -134,17 +150,30 @@ void ReflectionProbeNode::onMoveUpdate(MoveComponent& move)
 
 
 	// Update the spatial comp
 	// Update the spatial comp
 	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
 	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
-	sp.markForUpdate();
-	sp.setSpatialOrigin(move.getWorldTransform().getOrigin());
-	const Vec3 aabbMinWSpace = m_aabbMinLSpace + move.getWorldTransform().getOrigin().xyz();
-	const Vec3 aabbMaxWSpace = m_aabbMaxLSpace + move.getWorldTransform().getOrigin().xyz();
-	m_spatialAabb.setMin(aabbMinWSpace);
-	m_spatialAabb.setMax(aabbMaxWSpace);
+	sp.setSpatialOrigin(move.getWorldTransform().getOrigin().xyz());
 
 
 	// Update the refl comp
 	// Update the refl comp
 	ReflectionProbeComponent& reflc = getFirstComponentOfType<ReflectionProbeComponent>();
 	ReflectionProbeComponent& reflc = getFirstComponentOfType<ReflectionProbeComponent>();
-	reflc.setPosition(move.getWorldTransform().getOrigin());
-	reflc.setBoundingBox(aabbMinWSpace.xyz0(), aabbMaxWSpace.xyz0());
+	reflc.setWorldPosition(move.getWorldTransform().getOrigin().xyz());
+}
+
+void ReflectionProbeNode::onShapeUpdate(ReflectionProbeComponent& reflc)
+{
+	const Vec3 halfProbeSize = reflc.getBoxVolumeSize() / 2.0f;
+	F32 effectiveDistance = max(halfProbeSize.x(), halfProbeSize.y());
+	effectiveDistance = max(effectiveDistance, halfProbeSize.z());
+	effectiveDistance = max(effectiveDistance, getSceneGraph().getConfig().m_reflectionProbeEffectiveDistance);
+
+	// Update frustum components
+	Error err = iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) -> Error {
+		frc.setFar(effectiveDistance);
+		return Error::NONE;
+	});
+	(void)err;
+
+	// Update the spatial comp
+	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
+	sp.setAabbWorldSpace(reflc.getAabbWorldSpace());
 }
 }
 
 
 Error ReflectionProbeNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 Error ReflectionProbeNode::frameUpdate(Second prevUpdateTime, Second crntTime)
@@ -164,56 +193,4 @@ Error ReflectionProbeNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
-void ReflectionProbeNode::drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData)
-{
-	Mat4* const mvps = ctx.m_frameAllocator.newArray<Mat4>(userData.getSize());
-	Vec3* const positions = ctx.m_frameAllocator.newArray<Vec3>(userData.getSize());
-	for(U32 i = 0; i < userData.getSize(); ++i)
-	{
-		const ReflectionProbeNode& self = *static_cast<const ReflectionProbeNode*>(userData[i]);
-		const ReflectionProbeComponent& rComp = self.getFirstComponentOfType<ReflectionProbeComponent>();
-
-		const Vec3 tsl = (rComp.getBoundingBoxMin().xyz() + rComp.getBoundingBoxMax().xyz()) / 2.0f;
-		const Vec3 scale = (tsl - rComp.getBoundingBoxMin().xyz());
-
-		// Set non uniform scale.
-		Mat3 rot = Mat3::getIdentity();
-		rot(0, 0) *= scale.x();
-		rot(1, 1) *= scale.y();
-		rot(2, 2) *= scale.z();
-
-		mvps[i] = ctx.m_viewProjectionMatrix * Mat4(tsl.xyz1(), rot, 1.0f);
-		positions[i] = tsl.xyz();
-	}
-
-	const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
-	if(enableDepthTest)
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::LESS);
-	}
-	else
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::ALWAYS);
-	}
-
-	const ReflectionProbeNode& self = *static_cast<const ReflectionProbeNode*>(userData[0]);
-	self.m_dbgDrawer.drawCubes(ConstWeakArray<Mat4>(mvps, userData.getSize()), Vec4(0.0f, 0.0f, 1.0f, 1.0f), 1.0f,
-							   ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), 2.0f,
-							   *ctx.m_stagingGpuAllocator, ctx.m_commandBuffer);
-
-	self.m_dbgDrawer.drawBillboardTextures(
-		ctx.m_projectionMatrix, ctx.m_viewMatrix, ConstWeakArray<Vec3>(positions, userData.getSize()), Vec4(1.0f),
-		ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), self.m_dbgTex->getGrTextureView(),
-		ctx.m_sampler, Vec2(0.75f), *ctx.m_stagingGpuAllocator, ctx.m_commandBuffer);
-
-	ctx.m_frameAllocator.deleteArray(positions, userData.getSize());
-	ctx.m_frameAllocator.deleteArray(mvps, userData.getSize());
-
-	// Restore state
-	if(!enableDepthTest)
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::LESS);
-	}
-}
-
 } // end namespace anki
 } // end namespace anki

+ 5 - 23
anki/scene/ReflectionProbeNode.h

@@ -6,8 +6,7 @@
 #pragma once
 #pragma once
 
 
 #include <anki/scene/SceneNode.h>
 #include <anki/scene/SceneNode.h>
-#include <anki/collision/Aabb.h>
-#include <anki/scene/DebugDrawer.h>
+#include <anki/Math.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -19,37 +18,20 @@ namespace anki
 class ReflectionProbeNode : public SceneNode
 class ReflectionProbeNode : public SceneNode
 {
 {
 public:
 public:
-	ReflectionProbeNode(SceneGraph* scene, CString name)
-		: SceneNode(scene, name)
-	{
-	}
+	ReflectionProbeNode(SceneGraph* scene, CString name);
 
 
 	~ReflectionProbeNode();
 	~ReflectionProbeNode();
 
 
-	ANKI_USE_RESULT Error init(const Vec4& aabbMinLSpace, const Vec4& aabbMaxLSpace);
-
 	ANKI_USE_RESULT Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
 	ANKI_USE_RESULT Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
 
 
 private:
 private:
 	class MoveFeedbackComponent;
 	class MoveFeedbackComponent;
+	class ShapeFeedbackComponent;
 
 
-	class CubeSide
-	{
-	public:
-		Transform m_localTrf;
-	};
-
-	Array<CubeSide, 6> m_cubeSides;
-	Vec3 m_aabbMinLSpace = Vec3(+1.0f);
-	Vec3 m_aabbMaxLSpace = Vec3(-1.0f);
-	Aabb m_spatialAabb;
-
-	DebugDrawer2 m_dbgDrawer;
-	TextureResourcePtr m_dbgTex;
+	Array<Transform, 6> m_frustumTransforms;
 
 
 	void onMoveUpdate(MoveComponent& move);
 	void onMoveUpdate(MoveComponent& move);
-
-	static void drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData);
+	void onShapeUpdate(ReflectionProbeComponent& reflc);
 };
 };
 /// @}
 /// @}
 
 

+ 19 - 3
anki/scene/SceneGraph.cpp

@@ -15,6 +15,7 @@
 #include <anki/core/ConfigSet.h>
 #include <anki/core/ConfigSet.h>
 #include <anki/util/ThreadHive.h>
 #include <anki/util/ThreadHive.h>
 #include <anki/util/Tracer.h>
 #include <anki/util/Tracer.h>
+#include <anki/util/HighRezTimer.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -95,6 +96,9 @@ Error SceneGraph::init(AllocAlignedCallback allocCb, void* allocCbData, ThreadHi
 	PhysicsDebugNode* pnode;
 	PhysicsDebugNode* pnode;
 	ANKI_CHECK(newSceneNode<PhysicsDebugNode>("_physicsDebugNode", pnode));
 	ANKI_CHECK(newSceneNode<PhysicsDebugNode>("_physicsDebugNode", pnode));
 
 
+	// Other
+	ANKI_CHECK(m_debugDrawer.init(&getResourceManager()));
+
 	return Error::NONE;
 	return Error::NONE;
 }
 }
 
 
@@ -199,7 +203,8 @@ Error SceneGraph::update(Second prevUpdateTime, Second crntTime)
 	// Delete stuff
 	// Delete stuff
 	{
 	{
 		ANKI_TRACE_SCOPED_EVENT(SCENE_MARKED_FOR_DELETION);
 		ANKI_TRACE_SCOPED_EVENT(SCENE_MARKED_FOR_DELETION);
-		m_events.deleteEventsMarkedForDeletion();
+		const Bool fullCleanup = m_objectsMarkedForDeletionCount.load() != 0;
+		m_events.deleteEventsMarkedForDeletion(fullCleanup);
 		deleteNodesMarkedForDeletion();
 		deleteNodesMarkedForDeletion();
 	}
 	}
 
 
@@ -258,15 +263,26 @@ Error SceneGraph::updateNode(Second prevTime, Second crntTime, SceneNode& node)
 
 
 	// Components update
 	// Components update
 	Timestamp componentTimestamp = 0;
 	Timestamp componentTimestamp = 0;
-	err = node.iterateComponents([&](SceneComponent& comp) -> Error {
+	Bool atLeastOneComponentUpdated = false;
+	err = node.iterateComponents([&](SceneComponent& comp, Bool isFeedbackComponent) -> Error {
 		Bool updated = false;
 		Bool updated = false;
-		Error e = comp.update(node, prevTime, crntTime, updated);
+		Error e = Error::NONE;
+		if(!atLeastOneComponentUpdated && isFeedbackComponent)
+		{
+			// Skip feedback component if prior components didn't got updated
+		}
+		else
+		{
+			e = comp.update(node, prevTime, crntTime, updated);
+		}
 
 
 		if(updated)
 		if(updated)
 		{
 		{
+			ANKI_TRACE_INC_COUNTER(SCENE_COMPONENTS_UPDATED, 1);
 			comp.setTimestamp(node.getSceneGraph().m_timestamp);
 			comp.setTimestamp(node.getSceneGraph().m_timestamp);
 			componentTimestamp = max(componentTimestamp, node.getSceneGraph().m_timestamp);
 			componentTimestamp = max(componentTimestamp, node.getSceneGraph().m_timestamp);
 			ANKI_ASSERT(componentTimestamp > 0);
 			ANKI_ASSERT(componentTimestamp > 0);
+			atLeastOneComponentUpdated = true;
 		}
 		}
 
 
 		return e;
 		return e;

+ 10 - 6
anki/scene/SceneGraph.h

@@ -7,9 +7,8 @@
 
 
 #include <anki/scene/Common.h>
 #include <anki/scene/Common.h>
 #include <anki/scene/SceneNode.h>
 #include <anki/scene/SceneNode.h>
+#include <anki/scene/DebugDrawer.h>
 #include <anki/Math.h>
 #include <anki/Math.h>
-#include <anki/util/Singleton.h>
-#include <anki/util/HighRezTimer.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/HashMap.h>
 #include <anki/core/App.h>
 #include <anki/core/App.h>
 #include <anki/scene/events/EventManager.h>
 #include <anki/scene/events/EventManager.h>
@@ -19,13 +18,11 @@ namespace anki
 {
 {
 
 
 // Forward
 // Forward
-class MainRenderer;
 class ResourceManager;
 class ResourceManager;
 class CameraNode;
 class CameraNode;
 class Input;
 class Input;
 class ConfigSet;
 class ConfigSet;
 class PerspectiveCameraNode;
 class PerspectiveCameraNode;
-class UpdateSceneNodesCtx;
 class Octree;
 class Octree;
 
 
 /// @addtogroup scene
 /// @addtogroup scene
@@ -227,6 +224,11 @@ public:
 		return *m_octree;
 		return *m_octree;
 	}
 	}
 
 
+	const DebugDrawer2& getDebugDrawer() const
+	{
+		return m_debugDrawer;
+	}
+
 private:
 private:
 	class UpdateSceneNodesCtx;
 	class UpdateSceneNodesCtx;
 
 
@@ -256,8 +258,8 @@ private:
 
 
 	Octree* m_octree = nullptr;
 	Octree* m_octree = nullptr;
 
 
-	Vec3 m_sceneMin = {-1000.0f, -200.0f, -1000.0f};
-	Vec3 m_sceneMax = {1000.0f, 200.0f, 1000.0f};
+	Vec3 m_sceneMin = Vec3(-1000.0f, -200.0f, -1000.0f);
+	Vec3 m_sceneMax = Vec3(1000.0f, 200.0f, 1000.0f);
 
 
 	Atomic<U32> m_objectsMarkedForDeletionCount = {0};
 	Atomic<U32> m_objectsMarkedForDeletionCount = {0};
 
 
@@ -266,6 +268,8 @@ private:
 	SceneGraphConfig m_config;
 	SceneGraphConfig m_config;
 	SceneGraphStats m_stats;
 	SceneGraphStats m_stats;
 
 
+	DebugDrawer2 m_debugDrawer;
+
 	/// Put a node in the appropriate containers
 	/// Put a node in the appropriate containers
 	ANKI_USE_RESULT Error registerNode(SceneNode* node);
 	ANKI_USE_RESULT Error registerNode(SceneNode* node);
 	void unregisterNode(SceneNode* node);
 	void unregisterNode(SceneNode* node);

+ 59 - 18
anki/scene/SceneNode.h

@@ -5,13 +5,12 @@
 
 
 #pragma once
 #pragma once
 
 
-#include <anki/scene/Common.h>
+#include <anki/scene/components/SceneComponent.h>
 #include <anki/util/Hierarchy.h>
 #include <anki/util/Hierarchy.h>
 #include <anki/util/BitMask.h>
 #include <anki/util/BitMask.h>
 #include <anki/util/BitSet.h>
 #include <anki/util/BitSet.h>
 #include <anki/util/List.h>
 #include <anki/util/List.h>
 #include <anki/util/Enum.h>
 #include <anki/util/Enum.h>
-#include <anki/scene/components/SceneComponent.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -36,6 +35,12 @@ public:
 	/// Unregister node
 	/// Unregister node
 	virtual ~SceneNode();
 	virtual ~SceneNode();
 
 
+	/// A dummy init for those scene nodes that don't need it.
+	ANKI_USE_RESULT Error init()
+	{
+		return Error::NONE;
+	}
+
 	SceneGraph& getSceneGraph()
 	SceneGraph& getSceneGraph()
 	{
 	{
 		return *m_scene;
 		return *m_scene;
@@ -106,7 +111,7 @@ public:
 		for(; !err && it != end; ++it)
 		for(; !err && it != end; ++it)
 		{
 		{
 			const SceneComponent* c = *it;
 			const SceneComponent* c = *it;
-			err = func(*c);
+			err = func(*c, it->isFeedbackComponent());
 		}
 		}
 
 
 		return err;
 		return err;
@@ -122,7 +127,7 @@ public:
 		for(; !err && it != end; ++it)
 		for(; !err && it != end; ++it)
 		{
 		{
 			SceneComponent* c = *it;
 			SceneComponent* c = *it;
-			err = func(*c);
+			err = func(*c, it->isFeedbackComponent());
 		}
 		}
 
 
 		return err;
 		return err;
@@ -137,7 +142,7 @@ public:
 		auto end = m_components.getEnd();
 		auto end = m_components.getEnd();
 		for(; !err && it != end; ++it)
 		for(; !err && it != end; ++it)
 		{
 		{
-			if(it->getComponentType() == TComponent::CLASS_TYPE)
+			if(it->getComponentClassId() == TComponent::getStaticClassId())
 			{
 			{
 				const SceneComponent* comp = *it;
 				const SceneComponent* comp = *it;
 				err = func(static_cast<const TComponent&>(*comp));
 				err = func(static_cast<const TComponent&>(*comp));
@@ -156,7 +161,7 @@ public:
 		auto end = m_components.getEnd();
 		auto end = m_components.getEnd();
 		for(; !err && it != end; ++it)
 		for(; !err && it != end; ++it)
 		{
 		{
-			if(it->getComponentType() == TComponent::CLASS_TYPE)
+			if(it->getComponentClassId() == TComponent::getStaticClassId())
 			{
 			{
 				SceneComponent* comp = *it;
 				SceneComponent* comp = *it;
 				err = func(static_cast<TComponent&>(*comp));
 				err = func(static_cast<TComponent&>(*comp));
@@ -172,7 +177,7 @@ public:
 	{
 	{
 		for(const ComponentsArrayElement& el : m_components)
 		for(const ComponentsArrayElement& el : m_components)
 		{
 		{
-			if(el.getComponentType() == TComponent::CLASS_TYPE)
+			if(el.getComponentClassId() == TComponent::getStaticClassId())
 			{
 			{
 				const SceneComponent* comp = el;
 				const SceneComponent* comp = el;
 				return static_cast<const TComponent*>(comp);
 				return static_cast<const TComponent*>(comp);
@@ -213,7 +218,7 @@ public:
 		I32 inth = I32(nth);
 		I32 inth = I32(nth);
 		for(const ComponentsArrayElement& el : m_components)
 		for(const ComponentsArrayElement& el : m_components)
 		{
 		{
-			if(el.getComponentType() == TComponent::CLASS_TYPE && inth-- == 0)
+			if(el.getComponentClassId() == TComponent::getStaticClassId() && inth-- == 0)
 			{
 			{
 				const SceneComponent* comp = el;
 				const SceneComponent* comp = el;
 				return static_cast<const TComponent*>(comp);
 				return static_cast<const TComponent*>(comp);
@@ -230,11 +235,27 @@ public:
 		return const_cast<TComponent*>(c);
 		return const_cast<TComponent*>(c);
 	}
 	}
 
 
+	template<typename TComponent>
+	const TComponent& getNthComponentOfType(U32 nth) const
+	{
+		const TComponent* out = tryGetNthComponentOfType<TComponent>(nth);
+		ANKI_ASSERT(out);
+		return *out;
+	}
+
+	template<typename TComponent>
+	TComponent& getNthComponentOfType(U32 nth)
+	{
+		TComponent* out = tryGetNthComponentOfType<TComponent>(nth);
+		ANKI_ASSERT(out);
+		return *out;
+	}
+
 	/// Get the nth component.
 	/// Get the nth component.
 	template<typename TComponent>
 	template<typename TComponent>
 	TComponent& getComponentAt(U32 idx)
 	TComponent& getComponentAt(U32 idx)
 	{
 	{
-		ANKI_ASSERT(m_components[idx].getComponentType() == TComponent::CLASS_TYPE);
+		ANKI_ASSERT(m_components[idx].getComponentClassId() == TComponent::getStaticClassId());
 		SceneComponent* c = m_components[idx];
 		SceneComponent* c = m_components[idx];
 		return *static_cast<TComponent*>(c);
 		return *static_cast<TComponent*>(c);
 	}
 	}
@@ -243,7 +264,7 @@ public:
 	template<typename TComponent>
 	template<typename TComponent>
 	const TComponent& getComponentAt(U32 idx) const
 	const TComponent& getComponentAt(U32 idx) const
 	{
 	{
-		ANKI_ASSERT(m_components[idx].getComponentType() == TComponent::CLASS_TYPE);
+		ANKI_ASSERT(m_components[idx].getComponentClassId() == TComponent::getStaticClassId());
 		const SceneComponent* c = m_components[idx];
 		const SceneComponent* c = m_components[idx];
 		return *static_cast<const TComponent*>(c);
 		return *static_cast<const TComponent*>(c);
 	}
 	}
@@ -253,12 +274,24 @@ public:
 		return m_components.getSize();
 		return m_components.getSize();
 	}
 	}
 
 
+	template<typename TComponent>
+	U32 countComponentsOfType() const
+	{
+		U32 count = 0;
+		const Error err = iterateComponentsOfType<TComponent>([&](const TComponent& c) -> Error {
+			++count;
+			return Error::NONE;
+		});
+		(void)err;
+		return count;
+	}
+
 protected:
 protected:
 	/// Create and append a component to the components container. The SceneNode has the ownership.
 	/// Create and append a component to the components container. The SceneNode has the ownership.
-	template<typename TComponent, typename... TArgs>
-	TComponent* newComponent(TArgs&&... args)
+	template<typename TComponent>
+	TComponent* newComponent()
 	{
 	{
-		TComponent* comp = getAllocator().newInstance<TComponent>(std::forward<TArgs>(args)...);
+		TComponent* comp = getAllocator().newInstance<TComponent>(this);
 		m_components.emplaceBack(getAllocator(), comp);
 		m_components.emplaceBack(getAllocator(), comp);
 		return comp;
 		return comp;
 	}
 	}
@@ -271,7 +304,8 @@ private:
 	class ComponentsArrayElement
 	class ComponentsArrayElement
 	{
 	{
 	public:
 	public:
-		PtrSize m_combo; ///< Encodes the SceneComponentType in the high 8bits and the SceneComponent* to the remaining.
+		/// Encodes the SceneComponent's class ID, the SceneComponent* and if it's feedback component or not.
+		PtrSize m_combo;
 
 
 		ComponentsArrayElement(SceneComponent* comp)
 		ComponentsArrayElement(SceneComponent* comp)
 		{
 		{
@@ -309,17 +343,25 @@ private:
 			return getPtr();
 			return getPtr();
 		}
 		}
 
 
-		SceneComponentType getComponentType() const
+		U8 getComponentClassId() const
+		{
+			return m_combo & 0xFF;
+		}
+
+		Bool isFeedbackComponent() const
 		{
 		{
-			return SceneComponentType(m_combo & 0xFF);
+			return m_combo & PtrSize(1 << 7);
 		}
 		}
 
 
 	private:
 	private:
 		void set(SceneComponent* comp)
 		void set(SceneComponent* comp)
 		{
 		{
 			m_combo = ptrToNumber(comp) << 8;
 			m_combo = ptrToNumber(comp) << 8;
-			m_combo |= PtrSize(comp->getType());
+			m_combo |= PtrSize(comp->isFeedbackComponent()) << 7;
+			m_combo |= PtrSize(comp->getClassId()) & 0x7F;
 			ANKI_ASSERT(getPtr() == comp);
 			ANKI_ASSERT(getPtr() == comp);
+			ANKI_ASSERT(getComponentClassId() == comp->getClassId());
+			ANKI_ASSERT(isFeedbackComponent() == comp->isFeedbackComponent());
 		}
 		}
 
 
 		SceneComponent* getPtr() const
 		SceneComponent* getPtr() const
@@ -328,7 +370,6 @@ private:
 		}
 		}
 	};
 	};
 
 
-	static_assert(sizeof(SceneComponentType) == 1, "Wrong size");
 	static_assert(sizeof(ComponentsArrayElement) == sizeof(void*), "Wrong size");
 	static_assert(sizeof(ComponentsArrayElement) == sizeof(void*), "Wrong size");
 
 
 	SceneGraph* m_scene = nullptr;
 	SceneGraph* m_scene = nullptr;

+ 2 - 22
anki/scene/StaticCollisionNode.cpp

@@ -4,11 +4,7 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <anki/scene/StaticCollisionNode.h>
 #include <anki/scene/StaticCollisionNode.h>
-#include <anki/scene/SceneGraph.h>
-#include <anki/resource/CollisionResource.h>
-#include <anki/resource/ResourceManager.h>
-#include <anki/physics/PhysicsBody.h>
-#include <anki/physics/PhysicsWorld.h>
+#include <anki/scene/components/BodyComponent.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -16,27 +12,11 @@ namespace anki
 StaticCollisionNode::StaticCollisionNode(SceneGraph* scene, CString name)
 StaticCollisionNode::StaticCollisionNode(SceneGraph* scene, CString name)
 	: SceneNode(scene, name)
 	: SceneNode(scene, name)
 {
 {
+	newComponent<BodyComponent>();
 }
 }
 
 
 StaticCollisionNode::~StaticCollisionNode()
 StaticCollisionNode::~StaticCollisionNode()
 {
 {
 }
 }
 
 
-Error StaticCollisionNode::init(const CString& resourceFname, const Transform& transform)
-{
-	// Load resource
-	ANKI_CHECK(getResourceManager().loadResource(resourceFname, m_rsrc));
-
-	// Create body
-	PhysicsBodyInitInfo init;
-	init.m_shape = m_rsrc->getShape();
-	init.m_mass = 0.0f;
-	init.m_transform = transform;
-
-	m_body = getSceneGraph().getPhysicsWorld().newInstance<PhysicsBody>(init);
-	m_body->setUserData(this);
-
-	return Error::NONE;
-}
-
 } // namespace anki
 } // namespace anki

+ 0 - 9
anki/scene/StaticCollisionNode.h

@@ -22,15 +22,6 @@ public:
 	StaticCollisionNode(SceneGraph* scene, CString name);
 	StaticCollisionNode(SceneGraph* scene, CString name);
 
 
 	~StaticCollisionNode();
 	~StaticCollisionNode();
-
-	/// Initialize the node.
-	/// @param[in] resourceFname The file to load. It points to a .ankicl file.
-	/// @param[in] transform The transformation. That cannot change.
-	ANKI_USE_RESULT Error init(const CString& resourceFname, const Transform& transform);
-
-private:
-	CollisionResourcePtr m_rsrc;
-	PhysicsBodyPtr m_body;
 };
 };
 /// @}
 /// @}
 
 

+ 10 - 16
anki/scene/TriggerNode.cpp

@@ -16,9 +16,11 @@ namespace anki
 
 
 class TriggerNode::MoveFeedbackComponent : public SceneComponent
 class TriggerNode::MoveFeedbackComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(TriggerNode::MoveFeedbackComponent)
+
 public:
 public:
-	MoveFeedbackComponent()
-		: SceneComponent(SceneComponentType::NONE)
+	MoveFeedbackComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId(), true)
 	{
 	{
 	}
 	}
 
 
@@ -29,33 +31,25 @@ public:
 		const MoveComponent& move = node.getFirstComponentOfType<MoveComponent>();
 		const MoveComponent& move = node.getFirstComponentOfType<MoveComponent>();
 		if(move.getTimestamp() == node.getGlobalTimestamp())
 		if(move.getTimestamp() == node.getGlobalTimestamp())
 		{
 		{
-			static_cast<TriggerNode&>(node).m_trigger->setTransform(move.getWorldTransform());
+			node.getFirstComponentOfType<TriggerComponent>().setWorldTransform(move.getWorldTransform());
 		}
 		}
 
 
 		return Error::NONE;
 		return Error::NONE;
 	}
 	}
 };
 };
 
 
+ANKI_SCENE_COMPONENT_STATICS(TriggerNode::MoveFeedbackComponent)
+
 TriggerNode::TriggerNode(SceneGraph* scene, CString name)
 TriggerNode::TriggerNode(SceneGraph* scene, CString name)
 	: SceneNode(scene, name)
 	: SceneNode(scene, name)
 {
 {
+	newComponent<MoveComponent>();
+	newComponent<MoveFeedbackComponent>();
+	newComponent<TriggerComponent>();
 }
 }
 
 
 TriggerNode::~TriggerNode()
 TriggerNode::~TriggerNode()
 {
 {
 }
 }
 
 
-Error TriggerNode::init(F32 sphereRadius)
-{
-	m_shape = getSceneGraph().getPhysicsWorld().newInstance<PhysicsSphere>(sphereRadius);
-	m_trigger = getSceneGraph().getPhysicsWorld().newInstance<PhysicsTrigger>(m_shape);
-	m_trigger->setUserData(this);
-
-	newComponent<MoveComponent>();
-	newComponent<MoveFeedbackComponent>();
-	newComponent<TriggerComponent>(this, m_trigger);
-
-	return Error::NONE;
-}
-
 } // end namespace anki
 } // end namespace anki

+ 1 - 8
anki/scene/TriggerNode.h

@@ -22,16 +22,9 @@ public:
 
 
 	~TriggerNode();
 	~TriggerNode();
 
 
-	ANKI_USE_RESULT Error init(const CString& collisionShapeFilename);
-
-	ANKI_USE_RESULT Error init(F32 sphereRadius);
-
 private:
 private:
 	class MoveFeedbackComponent;
 	class MoveFeedbackComponent;
-
-	PhysicsCollisionShapePtr m_shape;
-	PhysicsTriggerPtr m_trigger;
 };
 };
 /// @}
 /// @}
 
 
-} // end namespace anki
+} // end namespace anki

+ 13 - 46
anki/scene/Visibility.cpp

@@ -9,7 +9,6 @@
 #include <anki/scene/components/LensFlareComponent.h>
 #include <anki/scene/components/LensFlareComponent.h>
 #include <anki/scene/components/RenderComponent.h>
 #include <anki/scene/components/RenderComponent.h>
 #include <anki/scene/components/ReflectionProbeComponent.h>
 #include <anki/scene/components/ReflectionProbeComponent.h>
-#include <anki/scene/components/OccluderComponent.h>
 #include <anki/scene/components/DecalComponent.h>
 #include <anki/scene/components/DecalComponent.h>
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/MoveComponent.h>
 #include <anki/scene/components/FogDensityComponent.h>
 #include <anki/scene/components/FogDensityComponent.h>
@@ -57,7 +56,7 @@ void VisibilityContext::submitNewWork(const FrustumComponent& frc, const Frustum
 		return;
 		return;
 	}
 	}
 
 
-	rqueue.m_cameraTransform = Mat4(frc.getTransform());
+	rqueue.m_cameraTransform = Mat4(frc.getWorldTransform());
 	rqueue.m_viewMatrix = frc.getViewMatrix();
 	rqueue.m_viewMatrix = frc.getViewMatrix();
 	rqueue.m_projectionMatrix = frc.getProjectionMatrix();
 	rqueue.m_projectionMatrix = frc.getProjectionMatrix();
 	rqueue.m_viewProjectionMatrix = frc.getViewProjectionMatrix();
 	rqueue.m_viewProjectionMatrix = frc.getViewProjectionMatrix();
@@ -305,50 +304,17 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 			continue;
 			continue;
 		}
 		}
 
 
-		// Test all spatial components of that node
-		struct SpatialTemp
+		const SpatialComponent* spatialc = node.tryGetFirstComponentOfType<SpatialComponent>();
+		if(ANKI_UNLIKELY(spatialc == nullptr))
 		{
 		{
-			SpatialComponent* m_sp;
-			U8 m_idx;
-			Vec4 m_origin;
-		};
-		Array<SpatialTemp, MAX_SUB_DRAWCALLS> sps;
-
-		U32 spIdx = 0;
-		U32 count = 0;
-		Error err = node.iterateComponentsOfType<SpatialComponent>([&](SpatialComponent& sp) {
-			if(spatialInsideFrustum(testedFrc, sp) && testAgainstRasterizer(sp.getAabb()))
-			{
-				// Inside
-				ANKI_ASSERT(spIdx < MAX_U8);
-				sps[count++] = SpatialTemp{&sp, static_cast<U8>(spIdx), sp.getSpatialOrigin()};
-			}
-
-			++spIdx;
-
-			return Error::NONE;
-		});
-		(void)err;
+			continue;
+		}
 
 
-		if(ANKI_UNLIKELY(count == 0))
+		if(!spatialInsideFrustum(testedFrc, *spatialc) || !testAgainstRasterizer(spatialc->getAabbWorldSpace()))
 		{
 		{
 			continue;
 			continue;
 		}
 		}
 
 
-		ANKI_ASSERT(count == 1 && "TODO: Support sub-spatials");
-
-		// Sort sub-spatials
-		const Vec4 origin = testedFrc.getTransform().getOrigin();
-		std::sort(sps.begin(), sps.begin() + count, [origin](const SpatialTemp& a, const SpatialTemp& b) -> Bool {
-			const Vec4& spa = a.m_origin;
-			const Vec4& spb = b.m_origin;
-
-			F32 dist0 = origin.getDistanceSquared(spa);
-			F32 dist1 = origin.getDistanceSquared(spb);
-
-			return dist0 < dist1;
-		});
-
 		WeakArray<RenderQueue> nextQueues;
 		WeakArray<RenderQueue> nextQueues;
 		WeakArray<FrustumComponent> nextQueueFrustumComponents; // Optional
 		WeakArray<FrustumComponent> nextQueueFrustumComponents; // Optional
 
 
@@ -370,7 +336,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 			const Plane& nearPlane = testedFrc.getViewPlanes()[FrustumPlaneType::NEAR];
 			const Plane& nearPlane = testedFrc.getViewPlanes()[FrustumPlaneType::NEAR];
 			el->m_distanceFromCamera = !!(rc->getFlags() & RenderComponentFlag::SORT_LAST)
 			el->m_distanceFromCamera = !!(rc->getFlags() & RenderComponentFlag::SORT_LAST)
 										   ? testedFrc.getFar()
 										   ? testedFrc.getFar()
-										   : max(0.0f, testPlane(nearPlane, sps[0].m_sp->getAabb()));
+										   : max(0.0f, testPlane(nearPlane, spatialc->getAabbWorldSpace()));
 
 
 			if(wantsEarlyZ && el->m_distanceFromCamera < m_frcCtx->m_visCtx->m_earlyZDist
 			if(wantsEarlyZ && el->m_distanceFromCamera < m_frcCtx->m_visCtx->m_earlyZDist
 			   && !(rc->getFlags() & RenderComponentFlag::FORWARD_SHADING))
 			   && !(rc->getFlags() & RenderComponentFlag::FORWARD_SHADING))
@@ -388,7 +354,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 			ANKI_ASSERT(m_frcCtx->m_primaryFrustum);
 			ANKI_ASSERT(m_frcCtx->m_primaryFrustum);
 			const FrustumComponent& primartFrc = *m_frcCtx->m_primaryFrustum;
 			const FrustumComponent& primartFrc = *m_frcCtx->m_primaryFrustum;
 			const Plane& nearPlane = primartFrc.getViewPlanes()[FrustumPlaneType::NEAR];
 			const Plane& nearPlane = primartFrc.getViewPlanes()[FrustumPlaneType::NEAR];
-			const F32 dist = testPlane(nearPlane, sps[0].m_sp->getAabb());
+			const F32 dist = testPlane(nearPlane, spatialc->getAabbWorldSpace());
 
 
 			static_assert(MAX_LOD_COUNT == 3, "Following code was designed around that");
 			static_assert(MAX_LOD_COUNT == 3, "Following code was designed around that");
 			U32 lod;
 			U32 lod;
@@ -422,7 +388,7 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 
 
 				// Compute distance from the frustum
 				// Compute distance from the frustum
 				const Plane& nearPlane = testedFrc.getViewPlanes()[FrustumPlaneType::NEAR];
 				const Plane& nearPlane = testedFrc.getViewPlanes()[FrustumPlaneType::NEAR];
-				const F32 distFromFrustum = max(0.0f, testPlane(nearPlane, sps[0].m_sp->getAabb()));
+				const F32 distFromFrustum = max(0.0f, testPlane(nearPlane, spatialc->getAabbWorldSpace()));
 
 
 				castsShadow = distFromFrustum < testedFrc.getEffectiveShadowDistance();
 				castsShadow = distFromFrustum < testedFrc.getEffectiveShadowDistance();
 			}
 			}
@@ -503,7 +469,8 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 					cascadeCount);
 					cascadeCount);
 				for(U32 i = 0; i < cascadeCount; ++i)
 				for(U32 i = 0; i < cascadeCount; ++i)
 				{
 				{
-					::new(&cascadeFrustumComponents[i]) FrustumComponent(&node, FrustumType::ORTHOGRAPHIC);
+					::new(&cascadeFrustumComponents[i]) FrustumComponent(&node);
+					cascadeFrustumComponents[i].setFrustumType(FrustumType::ORTHOGRAPHIC);
 				}
 				}
 
 
 				lc->setupDirectionalLightQueueElement(testedFrc, result.m_directionalLight, cascadeFrustumComponents);
 				lc->setupDirectionalLightQueueElement(testedFrc, result.m_directionalLight, cascadeFrustumComponents);
@@ -614,11 +581,11 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 		// Add more frustums to the list
 		// Add more frustums to the list
 		if(nextQueues.getSize() > 0)
 		if(nextQueues.getSize() > 0)
 		{
 		{
-			count = 0;
+			U32 count = 0;
 
 
 			if(ANKI_LIKELY(nextQueueFrustumComponents.getSize() == 0))
 			if(ANKI_LIKELY(nextQueueFrustumComponents.getSize() == 0))
 			{
 			{
-				err = node.iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) {
+				const Error err = node.iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) {
 					m_frcCtx->m_visCtx->submitNewWork(frc, nullptr, nextQueues[count++], hive);
 					m_frcCtx->m_visCtx->submitNewWork(frc, nullptr, nextQueues[count++], hive);
 					return Error::NONE;
 					return Error::NONE;
 				});
 				});

+ 86 - 0
anki/scene/components/BodyComponent.cpp

@@ -4,12 +4,98 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <anki/scene/components/BodyComponent.h>
 #include <anki/scene/components/BodyComponent.h>
+#include <anki/scene/SceneNode.h>
+#include <anki/scene/SceneGraph.h>
+#include <anki/resource/CpuMeshResource.h>
+#include <anki/resource/ResourceManager.h>
+#include <anki/physics/PhysicsWorld.h>
 
 
 namespace anki
 namespace anki
 {
 {
 
 
+ANKI_SCENE_COMPONENT_STATICS(BodyComponent)
+
+BodyComponent::BodyComponent(SceneNode* node)
+	: SceneComponent(node, getStaticClassId())
+	, m_node(node)
+{
+}
+
 BodyComponent::~BodyComponent()
 BodyComponent::~BodyComponent()
 {
 {
 }
 }
 
 
+Error BodyComponent::loadMeshResource(CString meshFilename)
+{
+	m_body.reset(nullptr);
+	ANKI_CHECK(m_node->getSceneGraph().getResourceManager().loadResource(meshFilename, m_mesh));
+
+	const Transform prevTransform = (m_body) ? m_body->getTransform() : m_trf;
+	const F32 prevMass = (m_body) ? m_body->getMass() : 0.0f;
+
+	PhysicsBodyInitInfo init;
+	init.m_mass = prevMass;
+	init.m_transform = prevTransform;
+	init.m_shape = m_mesh->getCollisionShape();
+	m_body = m_node->getSceneGraph().getPhysicsWorld().newInstance<PhysicsBody>(init);
+	m_body->setUserData(this);
+
+	m_markedForUpdate = true;
+	return Error::NONE;
+}
+
+CString BodyComponent::getMeshResourceFilename() const
+{
+	return (m_mesh.isCreated()) ? m_mesh->getFilename() : CString();
+}
+
+void BodyComponent::setMass(F32 mass)
+{
+	if(mass < 0.0f)
+	{
+		ANKI_SCENE_LOGW("Attempting to set a negative mass");
+		mass = 0.0f;
+	}
+
+	if(m_body.isCreated())
+	{
+		if((m_body->getMass() == 0.0f && mass != 0.0f) || (m_body->getMass() != 0.0f && mass == 0.0f))
+		{
+			// Will become from static to dynamic or the opposite, re-create the body
+
+			const Transform prevTransform = getWorldTransform();
+			PhysicsBodyInitInfo init;
+			init.m_transform = prevTransform;
+			init.m_mass = mass;
+			init.m_shape = m_mesh->getCollisionShape();
+			m_body = m_node->getSceneGraph().getPhysicsWorld().newInstance<PhysicsBody>(init);
+			m_body->setUserData(this);
+
+			m_markedForUpdate = true;
+		}
+		else
+		{
+			m_body->setMass(mass);
+		}
+	}
+	else
+	{
+		ANKI_SCENE_LOGW("BodyComponent is not initialized. Ignoring setting of mass");
+	}
+}
+
+Error BodyComponent::update(SceneNode& node, Second, Second, Bool& updated)
+{
+	updated = m_markedForUpdate;
+	m_markedForUpdate = false;
+
+	if(m_body && m_body->getTransform() != m_trf)
+	{
+		updated = true;
+		m_trf = m_body->getTransform();
+	}
+
+	return Error::NONE;
+}
+
 } // end namespace anki
 } // end namespace anki

+ 32 - 15
anki/scene/components/BodyComponent.h

@@ -7,6 +7,7 @@
 
 
 #include <anki/scene/components/SceneComponent.h>
 #include <anki/scene/components/SceneComponent.h>
 #include <anki/physics/PhysicsBody.h>
 #include <anki/physics/PhysicsBody.h>
+#include <anki/resource/Forward.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -17,25 +18,39 @@ namespace anki
 /// Rigid body component.
 /// Rigid body component.
 class BodyComponent : public SceneComponent
 class BodyComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(BodyComponent)
+
 public:
 public:
-	static const SceneComponentType CLASS_TYPE = SceneComponentType::BODY;
+	BodyComponent(SceneNode* node);
+
+	~BodyComponent();
+
+	ANKI_USE_RESULT Error loadMeshResource(CString meshFilename);
+
+	CString getMeshResourceFilename() const;
 
 
-	BodyComponent(PhysicsBodyPtr body)
-		: SceneComponent(CLASS_TYPE)
-		, m_body(body)
+	void setMass(F32 mass);
+
+	F32 getMass() const
 	{
 	{
+		return (m_body) ? m_body->getMass() : 0.0f;
 	}
 	}
 
 
-	~BodyComponent();
-
-	const Transform& getTransform() const
+	void setWorldTransform(const Transform& trf)
 	{
 	{
-		return m_trf;
+		if(m_body)
+		{
+			m_body->setTransform(trf);
+		}
+		else
+		{
+			m_trf = trf;
+		}
 	}
 	}
 
 
-	void setTransform(const Transform& trf)
+	Transform getWorldTransform() const
 	{
 	{
-		m_body->setTransform(trf);
+		return (m_body) ? m_body->getTransform() : m_trf;
 	}
 	}
 
 
 	PhysicsBodyPtr getPhysicsBody() const
 	PhysicsBodyPtr getPhysicsBody() const
@@ -43,17 +58,19 @@ public:
 		return m_body;
 		return m_body;
 	}
 	}
 
 
-	ANKI_USE_RESULT Error update(SceneNode& node, Second, Second, Bool& updated) override
+	ANKI_USE_RESULT Error update(SceneNode& node, Second, Second, Bool& updated) override;
+
+	Bool isEnabled() const
 	{
 	{
-		Transform newTrf = m_body->getTransform();
-		updated = newTrf != m_trf;
-		m_trf = newTrf;
-		return Error::NONE;
+		return m_mesh.isCreated();
 	}
 	}
 
 
 private:
 private:
+	SceneNode* m_node = nullptr;
+	CpuMeshResourcePtr m_mesh;
 	PhysicsBodyPtr m_body;
 	PhysicsBodyPtr m_body;
 	Transform m_trf = Transform::getIdentity();
 	Transform m_trf = Transform::getIdentity();
+	Bool m_markedForUpdate = true;
 };
 };
 /// @}
 /// @}
 
 

+ 62 - 14
anki/scene/components/DecalComponent.cpp

@@ -11,11 +11,17 @@
 namespace anki
 namespace anki
 {
 {
 
 
+ANKI_SCENE_COMPONENT_STATICS(DecalComponent)
+
 DecalComponent::DecalComponent(SceneNode* node)
 DecalComponent::DecalComponent(SceneNode* node)
-	: SceneComponent(CLASS_TYPE)
+	: SceneComponent(node, getStaticClassId())
 	, m_node(node)
 	, m_node(node)
 {
 {
 	ANKI_ASSERT(node);
 	ANKI_ASSERT(node);
+	if(node->getSceneGraph().getResourceManager().loadResource("engine_data/GreenDecal.ankitex", m_debugTex))
+	{
+		ANKI_SCENE_LOGF("Failed to load resources");
+	}
 }
 }
 
 
 DecalComponent::~DecalComponent()
 DecalComponent::~DecalComponent()
@@ -37,9 +43,10 @@ Error DecalComponent::setLayer(CString texAtlasFname, CString texAtlasSubtexName
 		return Error::USER_DATA;
 		return Error::USER_DATA;
 	}
 	}
 
 
-	Vec2 marginf = F32(ATLAS_SUB_TEXTURE_MARGIN / 2) / Vec2(F32(l.m_atlas->getWidth()), F32(l.m_atlas->getHeight()));
-	Vec2 minUv = l.m_uv.xy() - marginf;
-	Vec2 sizeUv = (l.m_uv.zw() - l.m_uv.xy()) + 2.0f * marginf;
+	const Vec2 marginf =
+		F32(ATLAS_SUB_TEXTURE_MARGIN / 2) / Vec2(F32(l.m_atlas->getWidth()), F32(l.m_atlas->getHeight()));
+	const Vec2 minUv = l.m_uv.xy() - marginf;
+	const Vec2 sizeUv = (l.m_uv.zw() - l.m_uv.xy()) + 2.0f * marginf;
 	l.m_uv = Vec4(minUv.x(), minUv.y(), minUv.x() + sizeUv.x(), minUv.y() + sizeUv.y());
 	l.m_uv = Vec4(minUv.x(), minUv.y(), minUv.x() + sizeUv.x(), minUv.y() + sizeUv.y());
 
 
 	l.m_blendFactor = blendFactor;
 	l.m_blendFactor = blendFactor;
@@ -48,26 +55,67 @@ Error DecalComponent::setLayer(CString texAtlasFname, CString texAtlasSubtexName
 
 
 void DecalComponent::updateInternal()
 void DecalComponent::updateInternal()
 {
 {
+	const Vec3 halfBoxSize = m_boxSize / 2.0f;
+
 	// Calculate the texture matrix
 	// Calculate the texture matrix
-	Mat4 worldTransform(m_trf);
+	const Mat4 worldTransform(m_trf);
 
 
-	Mat4 viewMat = worldTransform.getInverse();
+	const Mat4 viewMat = worldTransform.getInverse();
 
 
-	Mat4 projMat =
-		Mat4::calculateOrthographicProjectionMatrix(m_sizes.x() / 2.0f, -m_sizes.x() / 2.0f, m_sizes.y() / 2.0f,
-													-m_sizes.y() / 2.0f, LIGHT_FRUSTUM_NEAR_PLANE, m_sizes.z());
+	const Mat4 projMat = Mat4::calculateOrthographicProjectionMatrix(
+		halfBoxSize.x(), -halfBoxSize.x(), halfBoxSize.y(), -halfBoxSize.y(), LIGHT_FRUSTUM_NEAR_PLANE, m_boxSize.z());
 
 
-	static const Mat4 biasMat4(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0);
+	static const Mat4 biasMat4(0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+							   1.0f);
 
 
 	m_biasProjViewMat = biasMat4 * projMat * viewMat;
 	m_biasProjViewMat = biasMat4 * projMat * viewMat;
 
 
 	// Calculate the OBB
 	// Calculate the OBB
-	Vec4 center(0.0f, 0.0f, -m_sizes.z() / 2.0f, 0.0f);
-	Vec4 extend(m_sizes.x() / 2.0f, m_sizes.y() / 2.0f, m_sizes.z() / 2.0f, 0.0f);
+	const Vec4 center(0.0f, 0.0f, -halfBoxSize.z(), 0.0f);
+	const Vec4 extend(halfBoxSize.x(), halfBoxSize.y(), halfBoxSize.z(), 0.0f);
+	const Obb obbL(center, Mat3x4::getIdentity(), extend);
+	m_obb = obbL.getTransformed(m_trf);
+}
 
 
-	Obb obbL(center, Mat3x4::getIdentity(), extend);
+void DecalComponent::draw(RenderQueueDrawContext& ctx) const
+{
+	const Mat3 rot = m_obb.getRotation().getRotationPart();
+	const Vec4 tsl = m_obb.getCenter().xyz1();
+	const Vec3 scale = m_obb.getExtend().xyz();
 
 
-	m_obb = obbL.getTransformed(m_trf);
+	Mat3 nonUniScale = Mat3::getZero();
+	nonUniScale(0, 0) = scale.x();
+	nonUniScale(1, 1) = scale.y();
+	nonUniScale(2, 2) = scale.z();
+
+	const Mat4 mvp = ctx.m_viewProjectionMatrix * Mat4(tsl, rot * nonUniScale, 1.0f);
+
+	const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DEPTH_TEST_ON);
+	if(enableDepthTest)
+	{
+		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::LESS);
+	}
+	else
+	{
+		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::ALWAYS);
+	}
+
+	m_node->getSceneGraph().getDebugDrawer().drawCubes(
+		ConstWeakArray<Mat4>(&mvp, 1), Vec4(0.0f, 1.0f, 0.0f, 1.0f), 1.0f,
+		ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), 2.0f, *ctx.m_stagingGpuAllocator,
+		ctx.m_commandBuffer);
+
+	const Vec3 pos = m_obb.getCenter().xyz();
+	m_node->getSceneGraph().getDebugDrawer().drawBillboardTextures(
+		ctx.m_projectionMatrix, ctx.m_viewMatrix, ConstWeakArray<Vec3>(&pos, 1), Vec4(1.0f),
+		ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::DITHERED_DEPTH_TEST_ON), m_debugTex->getGrTextureView(),
+		ctx.m_sampler, Vec2(0.75f), *ctx.m_stagingGpuAllocator, ctx.m_commandBuffer);
+
+	// Restore state
+	if(!enableDepthTest)
+	{
+		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::LESS);
+	}
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 23 - 40
anki/scene/components/DecalComponent.h

@@ -19,8 +19,9 @@ namespace anki
 /// Decal component. Contains all the relevant info for a deferred decal.
 /// Decal component. Contains all the relevant info for a deferred decal.
 class DecalComponent : public SceneComponent
 class DecalComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(DecalComponent)
+
 public:
 public:
-	static const SceneComponentType CLASS_TYPE = SceneComponentType::DECAL;
 	static constexpr U32 ATLAS_SUB_TEXTURE_MARGIN = 16;
 	static constexpr U32 ATLAS_SUB_TEXTURE_MARGIN = 16;
 
 
 	DecalComponent(SceneNode* node);
 	DecalComponent(SceneNode* node);
@@ -38,53 +39,36 @@ public:
 	}
 	}
 
 
 	/// Update the internal structures.
 	/// Update the internal structures.
-	void updateShape(F32 width, F32 height, F32 depth)
+	void setBoxVolumeSize(const Vec3& sizeXYZ)
 	{
 	{
-		m_sizes = Vec3(width, height, depth);
+		m_boxSize = sizeXYZ;
 		m_markedForUpdate = true;
 		m_markedForUpdate = true;
 	}
 	}
 
 
-	F32 getWidth() const
-	{
-		return m_sizes.x();
-	}
-
-	F32 getHeight() const
+	const Vec3& getBoxVolumeSize() const
 	{
 	{
-		return m_sizes.y();
+		return m_boxSize;
 	}
 	}
 
 
-	F32 getDepth() const
-	{
-		return m_sizes.z();
-	}
-
-	const Obb& getBoundingVolume() const
+	const Obb& getBoundingVolumeWorldSpace() const
 	{
 	{
 		return m_obb;
 		return m_obb;
 	}
 	}
 
 
-	void updateTransform(const Transform& trf)
+	void setWorldTransform(const Transform& trf)
 	{
 	{
 		ANKI_ASSERT(trf.getScale() == 1.0f);
 		ANKI_ASSERT(trf.getScale() == 1.0f);
 		m_trf = trf;
 		m_trf = trf;
 		m_markedForUpdate = true;
 		m_markedForUpdate = true;
 	}
 	}
 
 
-	void setDrawCallback(RenderQueueDrawCallback callback, const void* userData)
-	{
-		m_drawCallback = callback;
-		m_drawCallbackUserData = userData;
-	}
-
 	/// Implements SceneComponent::update.
 	/// Implements SceneComponent::update.
 	ANKI_USE_RESULT Error update(SceneNode& node, Second, Second, Bool& updated) override
 	ANKI_USE_RESULT Error update(SceneNode& node, Second, Second, Bool& updated) override
 	{
 	{
 		updated = m_markedForUpdate;
 		updated = m_markedForUpdate;
-
-		if(m_markedForUpdate)
+		m_markedForUpdate = false;
+		if(updated)
 		{
 		{
-			m_markedForUpdate = false;
 			updateInternal();
 			updateInternal();
 		}
 		}
 
 
@@ -117,11 +101,6 @@ public:
 		blendFactor = m_layers[LayerType::SPECULAR_ROUGHNESS].m_blendFactor;
 		blendFactor = m_layers[LayerType::SPECULAR_ROUGHNESS].m_blendFactor;
 	}
 	}
 
 
-	const Vec3& getVolumeSize() const
-	{
-		return m_sizes;
-	}
-
 	void setupDecalQueueElement(DecalQueueElement& el)
 	void setupDecalQueueElement(DecalQueueElement& el)
 	{
 	{
 		el.m_diffuseAtlas = (m_layers[LayerType::DIFFUSE].m_atlas)
 		el.m_diffuseAtlas = (m_layers[LayerType::DIFFUSE].m_atlas)
@@ -138,12 +117,15 @@ public:
 		el.m_obbCenter = m_obb.getCenter().xyz();
 		el.m_obbCenter = m_obb.getCenter().xyz();
 		el.m_obbExtend = m_obb.getExtend().xyz();
 		el.m_obbExtend = m_obb.getExtend().xyz();
 		el.m_obbRotation = m_obb.getRotation().getRotationPart();
 		el.m_obbRotation = m_obb.getRotation().getRotationPart();
-		el.m_debugDrawCallback = m_drawCallback;
-		el.m_debugDrawCallbackUserData = m_drawCallbackUserData;
+		el.m_debugDrawCallback = [](RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) {
+			ANKI_ASSERT(userData.getSize() == 1);
+			static_cast<const DecalComponent*>(userData[0])->draw(ctx);
+		};
+		el.m_debugDrawCallbackUserData = this;
 	}
 	}
 
 
 private:
 private:
-	enum class LayerType
+	enum class LayerType : U8
 	{
 	{
 		DIFFUSE,
 		DIFFUSE,
 		SPECULAR_ROUGHNESS,
 		SPECULAR_ROUGHNESS,
@@ -158,19 +140,20 @@ private:
 		F32 m_blendFactor = 0.0f;
 		F32 m_blendFactor = 0.0f;
 	};
 	};
 
 
-	SceneNode* m_node;
-	RenderQueueDrawCallback m_drawCallback = nullptr;
-	const void* m_drawCallbackUserData = nullptr;
+	SceneNode* m_node = nullptr;
 	Array<Layer, U(LayerType::COUNT)> m_layers;
 	Array<Layer, U(LayerType::COUNT)> m_layers;
-	Mat4 m_biasProjViewMat;
-	Vec3 m_sizes = Vec3(1.0f);
+	Mat4 m_biasProjViewMat = Mat4::getIdentity();
+	Vec3 m_boxSize = Vec3(1.0f);
 	Transform m_trf = Transform::getIdentity();
 	Transform m_trf = Transform::getIdentity();
-	Obb m_obb = Obb(Vec4(0.0f), Mat3x4::getIdentity(), Vec4(1.0f, 1.0f, 1.0f, 0.0f));
+	Obb m_obb = Obb(Vec4(0.0f), Mat3x4::getIdentity(), Vec4(0.5f, 0.5f, 0.5f, 0.0f));
+	TextureResourcePtr m_debugTex;
 	Bool m_markedForUpdate = true;
 	Bool m_markedForUpdate = true;
 
 
 	ANKI_USE_RESULT Error setLayer(CString texAtlasFname, CString texAtlasSubtexName, F32 blendFactor, LayerType type);
 	ANKI_USE_RESULT Error setLayer(CString texAtlasFname, CString texAtlasSubtexName, F32 blendFactor, LayerType type);
 
 
 	void updateInternal();
 	void updateInternal();
+
+	void draw(RenderQueueDrawContext& ctx) const;
 };
 };
 /// @}
 /// @}
 
 

+ 13 - 0
anki/scene/components/FogDensityComponent.cpp

@@ -0,0 +1,13 @@
+// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/scene/components/FogDensityComponent.h>
+
+namespace anki
+{
+
+ANKI_SCENE_COMPONENT_STATICS(FogDensityComponent)
+
+} // end namespace anki

+ 60 - 28
anki/scene/components/FogDensityComponent.h

@@ -19,43 +19,66 @@ namespace anki
 /// Fog density component. Controls the fog density.
 /// Fog density component. Controls the fog density.
 class FogDensityComponent : public SceneComponent
 class FogDensityComponent : public SceneComponent
 {
 {
+	ANKI_SCENE_COMPONENT(FogDensityComponent)
+
 public:
 public:
-	static const SceneComponentType CLASS_TYPE = SceneComponentType::FOG_DENSITY;
+	static constexpr F32 MIN_SHAPE_SIZE = 1.0_cm;
 
 
-	FogDensityComponent()
-		: SceneComponent(CLASS_TYPE)
+	FogDensityComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId())
+		, m_isBox(true)
+		, m_markedForUpdate(true)
 	{
 	{
 	}
 	}
 
 
-	void setAabb(const Vec4& aabbMin, const Vec4& aabbMax)
+	void setBoxVolumeSize(Vec3 sizeXYZ)
 	{
 	{
-		m_aabbMin = aabbMin;
-		m_aabbMax = aabbMax;
-		m_box = true;
+		sizeXYZ = sizeXYZ.max(Vec3(MIN_SHAPE_SIZE));
+		m_aabbMin = -sizeXYZ / 2.0f;
+		m_aabbMax = sizeXYZ / 2.0f;
+		m_isBox = true;
+		m_markedForUpdate = true;
 	}
 	}
 
 
-	void setSphere(F32 radius)
+	Vec3 getBoxVolumeSize() const
 	{
 	{
-		m_sphereRadius = radius;
-		m_box = false;
+		ANKI_ASSERT(isAabb());
+		return m_aabbMax.xyz() - m_aabbMin.xyz();
 	}
 	}
 
 
-	Bool isAabb() const
+	Aabb getAabbWorldSpace() const
 	{
 	{
-		return m_box == true;
+		ANKI_ASSERT(isAabb());
+		return Aabb(m_aabbMin + m_worldPos, m_aabbMax + m_worldPos);
 	}
 	}
 
 
-	void getAabb(Vec4& aabbMin, Vec4& aabbMax) const
+	void setSphereVolumeRadius(F32 radius)
 	{
 	{
-		ANKI_ASSERT(isAabb());
-		aabbMin = m_aabbMin;
-		aabbMax = m_aabbMax;
+		m_sphereRadius = max(MIN_SHAPE_SIZE, radius);
+		m_isBox = false;
+		m_markedForUpdate = true;
+	}
+
+	F32 getSphereVolumeRadius() const
+	{
+		ANKI_ASSERT(isSphere());
+		return m_sphereRadius;
+	}
+
+	Sphere getSphereWorldSpace() const
+	{
+		ANKI_ASSERT(isSphere());
+		return Sphere(m_worldPos, m_sphereRadius);
+	}
+
+	Bool isAabb() const
+	{
+		return m_isBox == true;
 	}
 	}
 
 
-	void getSphere(F32& radius) const
+	Bool isSphere() const
 	{
 	{
-		ANKI_ASSERT(!isAabb());
-		radius = m_sphereRadius;
+		return !m_isBox;
 	}
 	}
 
 
 	void setDensity(F32 d)
 	void setDensity(F32 d)
@@ -69,16 +92,17 @@ public:
 		return m_density;
 		return m_density;
 	}
 	}
 
 
-	void updatePosition(const Vec4& pos)
+	void setWorldPosition(const Vec3& pos)
 	{
 	{
 		m_worldPos = pos;
 		m_worldPos = pos;
+		m_markedForUpdate = true;
 	}
 	}
 
 
 	void setupFogDensityQueueElement(FogDensityQueueElement& el) const
 	void setupFogDensityQueueElement(FogDensityQueueElement& el) const
 	{
 	{
 		el.m_density = m_density;
 		el.m_density = m_density;
-		el.m_isBox = m_box;
-		if(m_box)
+		el.m_isBox = m_isBox;
+		if(m_isBox)
 		{
 		{
 			el.m_aabbMin = (m_aabbMin + m_worldPos).xyz();
 			el.m_aabbMin = (m_aabbMin + m_worldPos).xyz();
 			el.m_aabbMax = (m_aabbMax + m_worldPos).xyz();
 			el.m_aabbMax = (m_aabbMax + m_worldPos).xyz();
@@ -90,19 +114,27 @@ public:
 		}
 		}
 	}
 	}
 
 
+	Error update(SceneNode& node, Second prevTime, Second crntTime, Bool& updated) override
+	{
+		updated = m_markedForUpdate;
+		m_markedForUpdate = false;
+		return Error::NONE;
+	}
+
 private:
 private:
-	Vec4 m_aabbMin{0.0f};
+	Vec3 m_aabbMin = Vec3(0.0f);
 
 
 	union
 	union
 	{
 	{
-		Vec4 m_aabbMax{1.0f};
+		Vec3 m_aabbMax = Vec3(1.0f);
 		F32 m_sphereRadius;
 		F32 m_sphereRadius;
 	};
 	};
 
 
-	Vec4 m_worldPos{0.0f};
-
+	Vec3 m_worldPos = Vec3(0.0f);
 	F32 m_density = 1.0f;
 	F32 m_density = 1.0f;
-	Bool m_box = false;
+
+	Bool m_isBox : 1;
+	Bool m_markedForUpdate : 1;
 };
 };
 
 
-} // end namespace anki
+} // end namespace anki

+ 8 - 14
anki/scene/components/FrustumComponent.cpp

@@ -5,30 +5,24 @@
 
 
 #include <anki/scene/components/FrustumComponent.h>
 #include <anki/scene/components/FrustumComponent.h>
 #include <anki/scene/SceneNode.h>
 #include <anki/scene/SceneNode.h>
-#include <anki/scene/components/SpatialComponent.h>
+#include <anki/collision/Functions.h>
 
 
 namespace anki
 namespace anki
 {
 {
 
 
-FrustumComponent::FrustumComponent(SceneNode* node, FrustumType frustumType)
-	: SceneComponent(CLASS_TYPE)
+ANKI_SCENE_COMPONENT_STATICS(FrustumComponent)
+
+FrustumComponent::FrustumComponent(SceneNode* node)
+	: SceneComponent(node, getStaticClassId())
 	, m_node(node)
 	, m_node(node)
-	, m_frustumType(frustumType)
+	, m_shapeMarkedForUpdate(true)
+	, m_trfMarkedForUpdate(true)
 {
 {
 	ANKI_ASSERT(&m_perspective.m_far == &m_ortho.m_far);
 	ANKI_ASSERT(&m_perspective.m_far == &m_ortho.m_far);
 	ANKI_ASSERT(node);
 	ANKI_ASSERT(node);
-	ANKI_ASSERT(frustumType < FrustumType::COUNT);
 
 
 	// Set some default values
 	// Set some default values
-	if(frustumType == FrustumType::PERSPECTIVE)
-	{
-		setPerspective(0.1f, 100.0f, toRad(45.0f), toRad(45.0f));
-	}
-	else
-	{
-		setOrthographic(0.1f, 100.0f, 5.0f, -5.0f, 5.0f, -5.0f);
-	}
-
+	setFrustumType(FrustumType::PERSPECTIVE);
 	updateInternal();
 	updateInternal();
 }
 }
 
 

+ 29 - 14
anki/scene/components/FrustumComponent.h

@@ -61,10 +61,10 @@ ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(FrustumComponentVisibilityTestFlag)
 /// Frustum component. Useful for nodes that take part in visibility tests like cameras and lights.
 /// Frustum component. Useful for nodes that take part in visibility tests like cameras and lights.
 class FrustumComponent : public SceneComponent
 class FrustumComponent : public SceneComponent
 {
 {
-public:
-	static const SceneComponentType CLASS_TYPE = SceneComponentType::FRUSTUM;
+	ANKI_SCENE_COMPONENT(FrustumComponent)
 
 
-	FrustumComponent(SceneNode* node, FrustumType frustumType);
+public:
+	FrustumComponent(SceneNode* node);
 
 
 	~FrustumComponent();
 	~FrustumComponent();
 
 
@@ -73,8 +73,28 @@ public:
 		return *m_node;
 		return *m_node;
 	}
 	}
 
 
+	const SceneNode& getSceneNode() const
+	{
+		return *m_node;
+	}
+
+	void setFrustumType(FrustumType type)
+	{
+		ANKI_ASSERT(type >= FrustumType::FIRST && type < FrustumType::COUNT);
+		m_frustumType = type;
+		if(m_frustumType == FrustumType::PERSPECTIVE)
+		{
+			setPerspective(0.1f, 100.0f, toRad(45.0f), toRad(45.0f));
+		}
+		else
+		{
+			setOrthographic(0.1f, 100.0f, 5.0f, -5.0f, 5.0f, -5.0f);
+		}
+	}
+
 	FrustumType getFrustumType() const
 	FrustumType getFrustumType() const
 	{
 	{
+		ANKI_ASSERT(m_frustumType != FrustumType::COUNT);
 		return m_frustumType;
 		return m_frustumType;
 	}
 	}
 
 
@@ -200,17 +220,12 @@ public:
 		m_ortho.m_bottom = value;
 		m_ortho.m_bottom = value;
 	}
 	}
 
 
-	const SceneNode& getSceneNode() const
-	{
-		return *m_node;
-	}
-
-	const Transform& getTransform() const
+	const Transform& getWorldTransform() const
 	{
 	{
 		return m_trf;
 		return m_trf;
 	}
 	}
 
 
-	void setTransform(const Transform& trf)
+	void setWorldTransform(const Transform& trf)
 	{
 	{
 		m_trf = trf;
 		m_trf = trf;
 		m_trfMarkedForUpdate = true;
 		m_trfMarkedForUpdate = true;
@@ -321,13 +336,13 @@ public:
 												  getEffectiveShadowDistance(), getCascadeCount());
 												  getEffectiveShadowDistance(), getCascadeCount());
 	}
 	}
 
 
-	const ConvexHullShape& getPerspectiveBoundingShape() const
+	const ConvexHullShape& getPerspectiveBoundingShapeWorldSpace() const
 	{
 	{
 		ANKI_ASSERT(m_frustumType == FrustumType::PERSPECTIVE);
 		ANKI_ASSERT(m_frustumType == FrustumType::PERSPECTIVE);
 		return m_perspective.m_hull;
 		return m_perspective.m_hull;
 	}
 	}
 
 
-	const Obb& getOrthographicBoundingShape() const
+	const Obb& getOrthographicBoundingShapeWorldSpace() const
 	{
 	{
 		ANKI_ASSERT(m_frustumType == FrustumType::ORTHOGRAPHIC);
 		ANKI_ASSERT(m_frustumType == FrustumType::ORTHOGRAPHIC);
 		return m_ortho.m_obbW;
 		return m_ortho.m_obbW;
@@ -418,8 +433,8 @@ private:
 	} m_coverageBuff; ///< Coverage buffer for extra visibility tests.
 	} m_coverageBuff; ///< Coverage buffer for extra visibility tests.
 
 
 	FrustumComponentVisibilityTestFlag m_flags = FrustumComponentVisibilityTestFlag::NONE;
 	FrustumComponentVisibilityTestFlag m_flags = FrustumComponentVisibilityTestFlag::NONE;
-	Bool m_shapeMarkedForUpdate = true;
-	Bool m_trfMarkedForUpdate = true;
+	Bool m_shapeMarkedForUpdate : 1;
+	Bool m_trfMarkedForUpdate : 1;
 
 
 	Bool updateInternal();
 	Bool updateInternal();
 
 

+ 13 - 0
anki/scene/components/GenericGpuComputeJobComponent.cpp

@@ -0,0 +1,13 @@
+// Copyright (C) 2009-2020, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/scene/components/GenericGpuComputeJobComponent.h>
+
+namespace anki
+{
+
+ANKI_SCENE_COMPONENT_STATICS(GenericGpuComputeJobComponent)
+
+} // end namespace anki

Some files were not shown because too many files changed in this diff