Browse Source

Refactor the CPU scene

Panagiotis Christopoulos Charitos 2 years ago
parent
commit
a54e7bf93d
100 changed files with 1620 additions and 5203 deletions
  1. 5 5
      AnKi/Math/Euler.h
  2. 3 3
      AnKi/Physics/PhysicsObject.h
  3. 4 24
      AnKi/Renderer/ClusterBinning.cpp
  4. 0 3
      AnKi/Renderer/Common.h
  5. 2 7
      AnKi/Renderer/GBufferPost.cpp
  6. 4 8
      AnKi/Renderer/RenderQueue.h
  7. 13 32
      AnKi/Scene.h
  8. 0 59
      AnKi/Scene/BodyNode.cpp
  9. 0 28
      AnKi/Scene/BodyNode.h
  10. 0 139
      AnKi/Scene/CameraNode.cpp
  11. 0 53
      AnKi/Scene/CameraNode.h
  12. 2 2
      AnKi/Scene/Common.h
  13. 23 15
      AnKi/Scene/Components/BodyComponent.cpp
  14. 7 20
      AnKi/Scene/Components/BodyComponent.h
  15. 55 25
      AnKi/Scene/Components/CameraComponent.cpp
  16. 47 5
      AnKi/Scene/Components/CameraComponent.h
  17. 34 70
      AnKi/Scene/Components/DecalComponent.cpp
  18. 22 80
      AnKi/Scene/Components/DecalComponent.h
  19. 34 0
      AnKi/Scene/Components/FogDensityComponent.cpp
  20. 12 31
      AnKi/Scene/Components/FogDensityComponent.h
  21. 0 226
      AnKi/Scene/Components/FrustumComponent.cpp
  22. 0 479
      AnKi/Scene/Components/FrustumComponent.h
  23. 0 10
      AnKi/Scene/Components/GenericGpuComputeJobComponent.cpp
  24. 0 51
      AnKi/Scene/Components/GenericGpuComputeJobComponent.h
  25. 49 38
      AnKi/Scene/Components/GlobalIlluminationProbeComponent.cpp
  26. 27 38
      AnKi/Scene/Components/GlobalIlluminationProbeComponent.h
  27. 0 290
      AnKi/Scene/Components/GpuParticleEmitterComponent.cpp
  28. 0 94
      AnKi/Scene/Components/GpuParticleEmitterComponent.h
  29. 36 12
      AnKi/Scene/Components/JointComponent.cpp
  30. 9 6
      AnKi/Scene/Components/JointComponent.h
  31. 35 3
      AnKi/Scene/Components/LensFlareComponent.cpp
  32. 13 24
      AnKi/Scene/Components/LensFlareComponent.h
  33. 149 65
      AnKi/Scene/Components/LightComponent.cpp
  34. 31 43
      AnKi/Scene/Components/LightComponent.h
  35. 75 31
      AnKi/Scene/Components/ModelComponent.cpp
  36. 10 4
      AnKi/Scene/Components/ModelComponent.h
  37. 1 89
      AnKi/Scene/Components/MoveComponent.cpp
  38. 4 153
      AnKi/Scene/Components/MoveComponent.h
  39. 28 31
      AnKi/Scene/Components/ParticleEmitterComponent.cpp
  40. 7 14
      AnKi/Scene/Components/ParticleEmitterComponent.h
  41. 17 1
      AnKi/Scene/Components/PlayerControllerComponent.cpp
  42. 1 22
      AnKi/Scene/Components/PlayerControllerComponent.h
  43. 58 35
      AnKi/Scene/Components/ReflectionProbeComponent.cpp
  44. 17 33
      AnKi/Scene/Components/ReflectionProbeComponent.h
  45. 2 3
      AnKi/Scene/Components/SceneComponent.cpp
  46. 8 9
      AnKi/Scene/Components/SceneComponent.h
  47. 0 9
      AnKi/Scene/Components/SceneComponentClasses.defs.h
  48. 0 4
      AnKi/Scene/Components/SceneComponentStatics.cpp
  49. 23 8
      AnKi/Scene/Components/ScriptComponent.cpp
  50. 1 1
      AnKi/Scene/Components/ScriptComponent.h
  51. 14 5
      AnKi/Scene/Components/SkinComponent.cpp
  52. 3 1
      AnKi/Scene/Components/SkinComponent.h
  53. 16 6
      AnKi/Scene/Components/SkyboxComponent.cpp
  54. 5 1
      AnKi/Scene/Components/SkyboxComponent.h
  55. 0 113
      AnKi/Scene/Components/SpatialComponent.cpp
  56. 0 151
      AnKi/Scene/Components/SpatialComponent.h
  57. 20 6
      AnKi/Scene/Components/TriggerComponent.cpp
  58. 0 13
      AnKi/Scene/Components/TriggerComponent.h
  59. 8 0
      AnKi/Scene/Components/UiComponent.cpp
  60. 7 0
      AnKi/Scene/Components/UiComponent.h
  61. 3 3
      AnKi/Scene/ConfigVars.defs.h
  62. 0 93
      AnKi/Scene/DecalNode.cpp
  63. 0 32
      AnKi/Scene/DecalNode.h
  64. 2 4
      AnKi/Scene/Events/AnimationEvent.cpp
  65. 3 9
      AnKi/Scene/Events/JitterMoveEvent.cpp
  66. 0 100
      AnKi/Scene/FogDensityNode.cpp
  67. 0 33
      AnKi/Scene/FogDensityNode.h
  68. 2 12
      AnKi/Scene/Forward.h
  69. 25 15
      AnKi/Scene/Frustum.cpp
  70. 50 15
      AnKi/Scene/Frustum.h
  71. 0 205
      AnKi/Scene/GlobalIlluminationProbeNode.cpp
  72. 0 37
      AnKi/Scene/GlobalIlluminationProbeNode.h
  73. 0 99
      AnKi/Scene/GpuParticleEmitterNode.cpp
  74. 0 32
      AnKi/Scene/GpuParticleEmitterNode.h
  75. 0 325
      AnKi/Scene/LightNode.cpp
  76. 0 93
      AnKi/Scene/LightNode.h
  77. 0 110
      AnKi/Scene/ModelNode.cpp
  78. 0 39
      AnKi/Scene/ModelNode.h
  79. 0 1
      AnKi/Scene/Octree.cpp
  80. 0 91
      AnKi/Scene/ParticleEmitterNode.cpp
  81. 0 32
      AnKi/Scene/ParticleEmitterNode.h
  82. 0 50
      AnKi/Scene/PhysicsDebugNode.cpp
  83. 0 27
      AnKi/Scene/PhysicsDebugNode.h
  84. 0 134
      AnKi/Scene/PlayerNode.cpp
  85. 0 30
      AnKi/Scene/PlayerNode.h
  86. 0 196
      AnKi/Scene/ReflectionProbeNode.cpp
  87. 0 37
      AnKi/Scene/ReflectionProbeNode.h
  88. 9 26
      AnKi/Scene/SceneGraph.cpp
  89. 3 3
      AnKi/Scene/SceneGraph.h
  90. 74 1
      AnKi/Scene/SceneNode.cpp
  91. 161 76
      AnKi/Scene/SceneNode.h
  92. 0 30
      AnKi/Scene/SkyboxNode.cpp
  93. 0 25
      AnKi/Scene/SkyboxNode.h
  94. 46 23
      AnKi/Scene/Spatial.h
  95. 0 21
      AnKi/Scene/StaticCollisionNode.cpp
  96. 0 27
      AnKi/Scene/StaticCollisionNode.h
  97. 0 54
      AnKi/Scene/TriggerNode.cpp
  98. 0 29
      AnKi/Scene/TriggerNode.h
  99. 267 293
      AnKi/Scene/Visibility.cpp
  100. 34 10
      AnKi/Scene/VisibilityInternal.h

+ 5 - 5
AnKi/Math/Euler.h

@@ -24,14 +24,14 @@ public:
 	{
 	{
 	}
 	}
 
 
-	TEuler(const T x_, const T y_, const T z_)
+	constexpr TEuler(const T x_, const T y_, const T z_)
 	{
 	{
-		x() = x_;
-		y() = y_;
-		z() = z_;
+		m_vec.m_x = x_;
+		m_vec.m_y = y_;
+		m_vec.m_z = z_;
 	}
 	}
 
 
-	TEuler(const TEuler& b)
+	constexpr TEuler(const TEuler& b)
 	{
 	{
 		x() = b.x();
 		x() = b.x();
 		y() = b.y();
 		y() = b.y();

+ 3 - 3
AnKi/Physics/PhysicsObject.h

@@ -77,9 +77,6 @@ public:
 		return m_userData;
 		return m_userData;
 	}
 	}
 
 
-protected:
-	PhysicsWorld* m_world = nullptr;
-
 	PhysicsWorld& getWorld()
 	PhysicsWorld& getWorld()
 	{
 	{
 		return *m_world;
 		return *m_world;
@@ -90,6 +87,9 @@ protected:
 		return *m_world;
 		return *m_world;
 	}
 	}
 
 
+protected:
+	PhysicsWorld* m_world = nullptr;
+
 	void retain() const
 	void retain() const
 	{
 	{
 		m_refcount.fetchAdd(1);
 		m_refcount.fetchAdd(1);

+ 4 - 24
AnKi/Renderer/ClusterBinning.cpp

@@ -322,31 +322,15 @@ void ClusterBinning::writeClustererBuffersTask()
 	{
 	{
 		Decal* decals = static_cast<Decal*>(cs.m_decalsAddress);
 		Decal* decals = static_cast<Decal*>(cs.m_decalsAddress);
 
 
-		TextureView* diffuseAtlas = nullptr;
-		TextureView* specularRoughnessAtlas = nullptr;
 		for(U32 i = 0; i < rqueue.m_decals.getSize(); ++i)
 		for(U32 i = 0; i < rqueue.m_decals.getSize(); ++i)
 		{
 		{
 			const DecalQueueElement& in = rqueue.m_decals[i];
 			const DecalQueueElement& in = rqueue.m_decals[i];
 			Decal& out = decals[i];
 			Decal& out = decals[i];
 
 
-			if((diffuseAtlas != nullptr && diffuseAtlas != in.m_diffuseAtlas)
-			   || (specularRoughnessAtlas != nullptr && specularRoughnessAtlas != in.m_specularRoughnessAtlas))
-			{
-				ANKI_R_LOGF("All decals should have the same tex atlas");
-			}
-
-			diffuseAtlas = in.m_diffuseAtlas;
-			specularRoughnessAtlas = in.m_specularRoughnessAtlas;
-
-			// Diff
-			Vec4 uv = in.m_diffuseAtlasUv;
-			out.m_diffuseUv = Vec4(uv.x(), uv.y(), uv.z() - uv.x(), uv.w() - uv.y());
-			out.m_blendFactors[0] = in.m_diffuseAtlasBlendFactor;
-
-			// Other
-			uv = in.m_specularRoughnessAtlasUv;
-			out.m_normRoughnessUv = Vec4(uv.x(), uv.y(), uv.z() - uv.x(), uv.w() - uv.y());
-			out.m_blendFactors[1] = in.m_specularRoughnessAtlasBlendFactor;
+			out.m_diffuseTexture = in.m_diffuseBindlessTextureIndex;
+			out.m_roughnessMetalnessTexture = in.m_roughnessMetalnessBindlessTextureIndex;
+			out.m_diffuseBlendFactor = in.m_diffuseBlendFactor;
+			out.m_roughnessMetalnessFactor = in.m_roughnessMetalnessBlendFactor;
 
 
 			// bias * proj_l * view
 			// bias * proj_l * view
 			out.m_textureMatrix = in.m_textureMatrix;
 			out.m_textureMatrix = in.m_textureMatrix;
@@ -356,10 +340,6 @@ void ClusterBinning::writeClustererBuffersTask()
 			out.m_invertedTransform = trf.getInverse();
 			out.m_invertedTransform = trf.getInverse();
 			out.m_obbExtend = in.m_obbExtend;
 			out.m_obbExtend = in.m_obbExtend;
 		}
 		}
-
-		ANKI_ASSERT(diffuseAtlas || specularRoughnessAtlas);
-		ctx.m_clusteredShading.m_diffuseDecalTextureView.reset(diffuseAtlas);
-		ctx.m_clusteredShading.m_specularRoughnessDecalTextureView.reset(specularRoughnessAtlas);
 	}
 	}
 
 
 	// Fog volumes
 	// Fog volumes

+ 0 - 3
AnKi/Renderer/Common.h

@@ -109,9 +109,6 @@ public:
 	void* m_clustersAddress = nullptr;
 	void* m_clustersAddress = nullptr;
 
 
 	BufferHandle m_clustersBufferHandle; ///< To track dependencies. Don't track all tokens, not worth it.
 	BufferHandle m_clustersBufferHandle; ///< To track dependencies. Don't track all tokens, not worth it.
-
-	TextureViewPtr m_diffuseDecalTextureView;
-	TextureViewPtr m_specularRoughnessDecalTextureView;
 };
 };
 
 
 /// Rendering context.
 /// Rendering context.

+ 2 - 7
AnKi/Renderer/GBufferPost.cpp

@@ -96,14 +96,9 @@ void GBufferPost::run(const RenderingContext& ctx, RenderPassWorkContext& rgraph
 
 
 	bindUniforms(cmdb, 0, 3, rsrc.m_clusteredShadingUniformsToken);
 	bindUniforms(cmdb, 0, 3, rsrc.m_clusteredShadingUniformsToken);
 	bindUniforms(cmdb, 0, 4, rsrc.m_decalsToken);
 	bindUniforms(cmdb, 0, 4, rsrc.m_decalsToken);
+	bindStorage(cmdb, 0, 5, rsrc.m_clustersToken);
 
 
-	cmdb->bindTexture(0, 5,
-					  (rsrc.m_diffuseDecalTextureView) ? rsrc.m_diffuseDecalTextureView : m_r->getDummyTextureView2d());
-	cmdb->bindTexture(0, 6,
-					  (rsrc.m_specularRoughnessDecalTextureView) ? rsrc.m_specularRoughnessDecalTextureView
-																 : m_r->getDummyTextureView2d());
-
-	bindStorage(cmdb, 0, 7, rsrc.m_clustersToken);
+	cmdb->bindAllBindless(1);
 
 
 	// Draw
 	// Draw
 	cmdb->drawArrays(PrimitiveTopology::kTriangles, 3);
 	cmdb->drawArrays(PrimitiveTopology::kTriangles, 3);

+ 4 - 8
AnKi/Renderer/RenderQueue.h

@@ -303,14 +303,10 @@ class DecalQueueElement final
 public:
 public:
 	RenderQueueDrawCallback m_debugDrawCallback;
 	RenderQueueDrawCallback m_debugDrawCallback;
 	const void* m_debugDrawCallbackUserData;
 	const void* m_debugDrawCallbackUserData;
-	/// Totaly unsafe but we can't have a smart ptr in here since there will be no deletion.
-	TextureView* m_diffuseAtlas;
-	/// Totaly unsafe but we can't have a smart ptr in here since there will be no deletion.
-	TextureView* m_specularRoughnessAtlas;
-	Vec4 m_diffuseAtlasUv;
-	Vec4 m_specularRoughnessAtlasUv;
-	F32 m_diffuseAtlasBlendFactor;
-	F32 m_specularRoughnessAtlasBlendFactor;
+	U32 m_diffuseBindlessTextureIndex;
+	U32 m_roughnessMetalnessBindlessTextureIndex;
+	F32 m_diffuseBlendFactor;
+	F32 m_roughnessMetalnessBlendFactor;
 	Mat4 m_textureMatrix;
 	Mat4 m_textureMatrix;
 	Vec3 m_obbCenter;
 	Vec3 m_obbCenter;
 	Vec3 m_obbExtend;
 	Vec3 m_obbExtend;

+ 13 - 32
AnKi/Scene.h

@@ -6,47 +6,28 @@
 #pragma once
 #pragma once
 
 
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Scene/SceneGraph.h>
-#include <AnKi/Scene/ModelNode.h>
-#include <AnKi/Scene/ParticleEmitterNode.h>
-#include <AnKi/Scene/GpuParticleEmitterNode.h>
-#include <AnKi/Scene/CameraNode.h>
-#include <AnKi/Scene/LightNode.h>
-#include <AnKi/Scene/StaticCollisionNode.h>
-#include <AnKi/Scene/BodyNode.h>
-#include <AnKi/Scene/ReflectionProbeNode.h>
-#include <AnKi/Scene/PlayerNode.h>
-#include <AnKi/Scene/DecalNode.h>
 #include <AnKi/Scene/Octree.h>
 #include <AnKi/Scene/Octree.h>
-#include <AnKi/Scene/PhysicsDebugNode.h>
-#include <AnKi/Scene/TriggerNode.h>
-#include <AnKi/Scene/FogDensityNode.h>
-#include <AnKi/Scene/GlobalIlluminationProbeNode.h>
-#include <AnKi/Scene/SkyboxNode.h>
 
 
-#include <AnKi/Scene/Components/MoveComponent.h>
-#include <AnKi/Scene/Components/LensFlareComponent.h>
-#include <AnKi/Scene/Components/PlayerControllerComponent.h>
-#include <AnKi/Scene/Components/SkinComponent.h>
-#include <AnKi/Scene/Components/ReflectionProbeComponent.h>
-#include <AnKi/Scene/Components/DecalComponent.h>
-#include <AnKi/Scene/Components/SceneComponent.h>
-#include <AnKi/Scene/Components/LightComponent.h>
 #include <AnKi/Scene/Components/BodyComponent.h>
 #include <AnKi/Scene/Components/BodyComponent.h>
-#include <AnKi/Scene/Components/SpatialComponent.h>
-#include <AnKi/Scene/Components/FrustumComponent.h>
-#include <AnKi/Scene/Components/JointComponent.h>
-#include <AnKi/Scene/Components/TriggerComponent.h>
+#include <AnKi/Scene/Components/CameraComponent.h>
+#include <AnKi/Scene/Components/DecalComponent.h>
 #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/ParticleEmitterComponent.h>
-#include <AnKi/Scene/Components/GpuParticleEmitterComponent.h>
+#include <AnKi/Scene/Components/JointComponent.h>
+#include <AnKi/Scene/Components/LensFlareComponent.h>
+#include <AnKi/Scene/Components/LightComponent.h>
 #include <AnKi/Scene/Components/ModelComponent.h>
 #include <AnKi/Scene/Components/ModelComponent.h>
-#include <AnKi/Scene/Components/UiComponent.h>
+#include <AnKi/Scene/Components/MoveComponent.h>
+#include <AnKi/Scene/Components/ParticleEmitterComponent.h>
+#include <AnKi/Scene/Components/PlayerControllerComponent.h>
+#include <AnKi/Scene/Components/ReflectionProbeComponent.h>
+#include <AnKi/Scene/Components/ScriptComponent.h>
+#include <AnKi/Scene/Components/SkinComponent.h>
 #include <AnKi/Scene/Components/SkyboxComponent.h>
 #include <AnKi/Scene/Components/SkyboxComponent.h>
+#include <AnKi/Scene/Components/TriggerComponent.h>
+#include <AnKi/Scene/Components/UiComponent.h>
 
 
 #include <AnKi/Scene/Events/EventManager.h>
 #include <AnKi/Scene/Events/EventManager.h>
-#include <AnKi/Scene/Events/Event.h>
 #include <AnKi/Scene/Events/AnimationEvent.h>
 #include <AnKi/Scene/Events/AnimationEvent.h>
 #include <AnKi/Scene/Events/JitterMoveEvent.h>
 #include <AnKi/Scene/Events/JitterMoveEvent.h>
 #include <AnKi/Scene/Events/LightEvent.h>
 #include <AnKi/Scene/Events/LightEvent.h>

+ 0 - 59
AnKi/Scene/BodyNode.cpp

@@ -1,59 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/BodyNode.h>
-#include <AnKi/Scene/Components/BodyComponent.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
-#include <AnKi/Scene/Components/JointComponent.h>
-#include <AnKi/Scene/SceneGraph.h>
-#include <AnKi/Physics/PhysicsWorld.h>
-#include <AnKi/Resource/ResourceManager.h>
-
-namespace anki {
-
-/// Body feedback component.
-class BodyNode::FeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(BodyNode::FeedbackComponent)
-
-public:
-	FeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		BodyComponent& bodyc = info.m_node->getFirstComponentOfType<BodyComponent>();
-
-		if(bodyc.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			MoveComponent& move = info.m_node->getFirstComponentOfType<MoveComponent>();
-			move.setLocalTransform(bodyc.getWorldTransform());
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(BodyNode::FeedbackComponent, -1.0f)
-
-BodyNode::BodyNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-	newComponent<JointComponent>();
-	newComponent<BodyComponent>();
-	newComponent<FeedbackComponent>();
-	MoveComponent* movec = newComponent<MoveComponent>();
-	movec->setIgnoreParentTransform(true);
-}
-
-BodyNode::~BodyNode()
-{
-}
-
-} // end namespace anki

+ 0 - 28
AnKi/Scene/BodyNode.h

@@ -1,28 +0,0 @@
-// Copyright (C) 2009-2022, 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>
-
-namespace anki {
-
-/// @addtogroup scene
-/// @{
-
-/// Node that gets affected by physics.
-class BodyNode : public SceneNode
-{
-public:
-	BodyNode(SceneGraph* scene, CString name);
-
-	~BodyNode();
-
-private:
-	class FeedbackComponent;
-};
-/// @}
-
-} // end namespace anki

+ 0 - 139
AnKi/Scene/CameraNode.cpp

@@ -1,139 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/CameraNode.h>
-#include <AnKi/Scene/Components/FrustumComponent.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
-#include <AnKi/Scene/Components/SpatialComponent.h>
-#include <AnKi/Scene/SceneGraph.h>
-#include <AnKi/Core/ConfigSet.h>
-
-namespace anki {
-
-/// Feedback component.
-class CameraNode::MoveFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(CameraNode::MoveFeedbackComponent)
-
-public:
-	MoveFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		MoveComponent& move = info.m_node->getFirstComponentOfType<MoveComponent>();
-		if(move.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			CameraNode& cam = static_cast<CameraNode&>(*info.m_node);
-			cam.onMoveComponentUpdate(move);
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(CameraNode::MoveFeedbackComponent, -1.0f)
-
-CameraNode::CameraNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-}
-
-CameraNode::~CameraNode()
-{
-}
-
-void CameraNode::initCommon(FrustumType frustumType)
-{
-	// Move component
-	newComponent<MoveComponent>();
-
-	// Feedback component
-	newComponent<MoveFeedbackComponent>();
-
-	// Frustum component
-	FrustumComponent* frc = newComponent<FrustumComponent>();
-	frc->setFrustumType(frustumType);
-	frc->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::kAll
-								   ^ (FrustumComponentVisibilityTestFlag::kAllRayTracing
-									  | FrustumComponentVisibilityTestFlag::kShadowCasterRenderComponents));
-	frc->setLodDistance(0, getExternalSubsystems().m_config->getLod0MaxDistance());
-	frc->setLodDistance(1, getExternalSubsystems().m_config->getLod1MaxDistance());
-	frc->setShadowCascadeCount(getExternalSubsystems().m_config->getSceneShadowCascadeCount());
-
-	static_assert(kMaxShadowCascades == 4);
-	frc->setShadowCascadeDistance(0, getExternalSubsystems().m_config->getSceneShadowCascade0Distance());
-	frc->setShadowCascadeDistance(1, getExternalSubsystems().m_config->getSceneShadowCascade1Distance());
-	frc->setShadowCascadeDistance(2, getExternalSubsystems().m_config->getSceneShadowCascade2Distance());
-	frc->setShadowCascadeDistance(3, getExternalSubsystems().m_config->getSceneShadowCascade3Distance());
-
-	// Extended frustum for RT
-	if(getExternalSubsystems().m_grManager->getDeviceCapabilities().m_rayTracingEnabled
-	   && getExternalSubsystems().m_config->getSceneRayTracedShadows())
-	{
-		FrustumComponent* rtFrustumComponent = newComponent<FrustumComponent>();
-		rtFrustumComponent->setFrustumType(FrustumType::kOrthographic);
-		rtFrustumComponent->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::kRayTracingShadows);
-
-		const F32 dist = getExternalSubsystems().m_config->getSceneRayTracingExtendedFrustumDistance();
-
-		rtFrustumComponent->setOrthographic(0.1f, dist * 2.0f, dist, -dist, dist, -dist);
-		rtFrustumComponent->setLodDistance(0, getExternalSubsystems().m_config->getLod0MaxDistance());
-		rtFrustumComponent->setLodDistance(1, getExternalSubsystems().m_config->getLod1MaxDistance());
-	}
-}
-
-void CameraNode::onMoveComponentUpdate(MoveComponent& move)
-{
-	const Transform& worldTransform = move.getWorldTransform();
-
-	// Frustum
-	U count = 0;
-	iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& fc) {
-		if(count == 0)
-		{
-			fc.setWorldTransform(worldTransform);
-		}
-		else
-		{
-			// Extended RT frustum, re-align it so the frustum is positioned at the center of the camera eye
-			ANKI_ASSERT(fc.getFrustumType() == FrustumType::kOrthographic);
-			const F32 far = fc.getFar();
-			Transform extendedFrustumTransform = Transform::getIdentity();
-			Vec3 newOrigin = worldTransform.getOrigin().xyz();
-			newOrigin.z() += far / 2.0f;
-			extendedFrustumTransform.setOrigin(newOrigin.xyz0());
-			fc.setWorldTransform(extendedFrustumTransform);
-		}
-
-		++count;
-	});
-}
-
-PerspectiveCameraNode::PerspectiveCameraNode(SceneGraph* scene, CString name)
-	: CameraNode(scene, name)
-{
-	initCommon(FrustumType::kPerspective);
-}
-
-PerspectiveCameraNode::~PerspectiveCameraNode()
-{
-}
-
-OrthographicCameraNode::OrthographicCameraNode(SceneGraph* scene, CString name)
-	: CameraNode(scene, name)
-{
-	initCommon(FrustumType::kOrthographic);
-}
-
-OrthographicCameraNode::~OrthographicCameraNode()
-{
-}
-
-} // end namespace anki

+ 0 - 53
AnKi/Scene/CameraNode.h

@@ -1,53 +0,0 @@
-// Copyright (C) 2009-2022, 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/Collision/Common.h>
-
-namespace anki {
-
-/// @addtogroup Scene
-/// @{
-
-/// Camera SceneNode interface class
-class CameraNode : public SceneNode
-{
-public:
-	CameraNode(SceneGraph* scene, CString name);
-
-	~CameraNode();
-
-protected:
-	void initCommon(FrustumType frustumType);
-
-private:
-	class MoveFeedbackComponent;
-
-	/// Called when moved.
-	void onMoveComponentUpdate(MoveComponent& move);
-};
-
-/// Perspective camera
-class PerspectiveCameraNode : public CameraNode
-{
-public:
-	PerspectiveCameraNode(SceneGraph* scene, CString name);
-
-	~PerspectiveCameraNode();
-};
-
-/// Orthographic camera
-class OrthographicCameraNode : public CameraNode
-{
-public:
-	OrthographicCameraNode(SceneGraph* scene, CString name);
-
-	~OrthographicCameraNode();
-};
-/// @}
-
-} // end namespace anki

+ 2 - 2
AnKi/Scene/Common.h

@@ -34,14 +34,14 @@ class PhysicsWorld;
 #define ANKI_SCENE_LOGF(...) ANKI_LOG("SCEN", kFatal, __VA_ARGS__)
 #define ANKI_SCENE_LOGF(...) ANKI_LOG("SCEN", kFatal, __VA_ARGS__)
 
 
 #define ANKI_SCENE_ASSERT(expression) \
 #define ANKI_SCENE_ASSERT(expression) \
-	ANKI_LIKELY(std::invoke([&]() -> Bool { \
+	std::invoke([&]() -> Bool { \
 		const Bool ok = (expression); \
 		const Bool ok = (expression); \
 		if(!ok) [[unlikely]] \
 		if(!ok) [[unlikely]] \
 		{ \
 		{ \
 			ANKI_SCENE_LOGE("Expression failed: " #expression); \
 			ANKI_SCENE_LOGE("Expression failed: " #expression); \
 		} \
 		} \
 		return ok; \
 		return ok; \
-	}))
+	})
 
 
 class SceneGraphExternalSubsystems
 class SceneGraphExternalSubsystems
 {
 {

+ 23 - 15
AnKi/Scene/Components/BodyComponent.cpp

@@ -16,18 +16,26 @@ BodyComponent::BodyComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
 	: SceneComponent(node, getStaticClassId())
 	, m_node(node)
 	, m_node(node)
 {
 {
+	node->setIgnoreParentTransform(true);
 }
 }
 
 
 BodyComponent::~BodyComponent()
 BodyComponent::~BodyComponent()
 {
 {
 }
 }
 
 
-Error BodyComponent::loadMeshResource(CString meshFilename)
+void BodyComponent::loadMeshResource(CString meshFilename)
 {
 {
-	m_body.reset(nullptr);
-	ANKI_CHECK(getExternalSubsystems(*m_node).m_resourceManager->loadResource(meshFilename, m_mesh));
+	CpuMeshResourcePtr rsrc;
+	const Error err = getExternalSubsystems(*m_node).m_resourceManager->loadResource(meshFilename, rsrc);
+	if(err)
+	{
+		ANKI_SCENE_LOGE("Failed to load mesh");
+		return;
+	}
 
 
-	const Transform prevTransform = (m_body) ? m_body->getTransform() : m_trf;
+	m_mesh = std::move(rsrc);
+
+	const Transform prevTransform = m_node->getWorldTransform();
 	const F32 prevMass = (m_body) ? m_body->getMass() : 0.0f;
 	const F32 prevMass = (m_body) ? m_body->getMass() : 0.0f;
 
 
 	PhysicsBodyInitInfo init;
 	PhysicsBodyInitInfo init;
@@ -36,9 +44,9 @@ Error BodyComponent::loadMeshResource(CString meshFilename)
 	init.m_shape = m_mesh->getCollisionShape();
 	init.m_shape = m_mesh->getCollisionShape();
 	m_body = getExternalSubsystems(*m_node).m_physicsWorld->newInstance<PhysicsBody>(init);
 	m_body = getExternalSubsystems(*m_node).m_physicsWorld->newInstance<PhysicsBody>(init);
 	m_body->setUserData(this);
 	m_body->setUserData(this);
+	m_body->setTransform(m_node->getWorldTransform());
 
 
-	m_markedForUpdate = true;
-	return Error::kNone;
+	m_dirty = true;
 }
 }
 
 
 CString BodyComponent::getMeshResourceFilename() const
 CString BodyComponent::getMeshResourceFilename() const
@@ -48,9 +56,8 @@ CString BodyComponent::getMeshResourceFilename() const
 
 
 void BodyComponent::setMass(F32 mass)
 void BodyComponent::setMass(F32 mass)
 {
 {
-	if(mass < 0.0f)
+	if(!ANKI_SCENE_ASSERT(mass >= 0.0f))
 	{
 	{
-		ANKI_SCENE_LOGW("Attempting to set a negative mass");
 		mass = 0.0f;
 		mass = 0.0f;
 	}
 	}
 
 
@@ -60,15 +67,16 @@ void BodyComponent::setMass(F32 mass)
 		{
 		{
 			// Will become from static to dynamic or the opposite, re-create the body
 			// Will become from static to dynamic or the opposite, re-create the body
 
 
-			const Transform prevTransform = getWorldTransform();
+			const Transform& prevTransform = m_body->getTransform();
 			PhysicsBodyInitInfo init;
 			PhysicsBodyInitInfo init;
 			init.m_transform = prevTransform;
 			init.m_transform = prevTransform;
 			init.m_mass = mass;
 			init.m_mass = mass;
 			init.m_shape = m_mesh->getCollisionShape();
 			init.m_shape = m_mesh->getCollisionShape();
 			m_body = getExternalSubsystems(*m_node).m_physicsWorld->newInstance<PhysicsBody>(init);
 			m_body = getExternalSubsystems(*m_node).m_physicsWorld->newInstance<PhysicsBody>(init);
 			m_body->setUserData(this);
 			m_body->setUserData(this);
+			m_body->setTransform(m_node->getWorldTransform());
 
 
-			m_markedForUpdate = true;
+			m_dirty = true;
 		}
 		}
 		else
 		else
 		{
 		{
@@ -81,15 +89,15 @@ void BodyComponent::setMass(F32 mass)
 	}
 	}
 }
 }
 
 
-Error BodyComponent::update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
+Error BodyComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 {
-	updated = m_markedForUpdate;
-	m_markedForUpdate = false;
+	updated = m_dirty;
+	m_dirty = false;
 
 
-	if(m_body && m_body->getTransform() != m_trf)
+	if(m_body && m_body->getTransform() != info.m_node->getWorldTransform())
 	{
 	{
 		updated = true;
 		updated = true;
-		m_trf = m_body->getTransform();
+		info.m_node->setLocalTransform(m_body->getTransform());
 	}
 	}
 
 
 	return Error::kNone;
 	return Error::kNone;

+ 7 - 20
AnKi/Scene/Components/BodyComponent.h

@@ -24,7 +24,7 @@ public:
 
 
 	~BodyComponent();
 	~BodyComponent();
 
 
-	Error loadMeshResource(CString meshFilename);
+	void loadMeshResource(CString meshFilename);
 
 
 	CString getMeshResourceFilename() const;
 	CString getMeshResourceFilename() const;
 
 
@@ -35,23 +35,6 @@ public:
 		return (m_body) ? m_body->getMass() : 0.0f;
 		return (m_body) ? m_body->getMass() : 0.0f;
 	}
 	}
 
 
-	void setWorldTransform(const Transform& trf)
-	{
-		if(m_body)
-		{
-			m_body->setTransform(trf);
-		}
-		else
-		{
-			m_trf = trf;
-		}
-	}
-
-	Transform getWorldTransform() const
-	{
-		return (m_body) ? m_body->getTransform() : m_trf;
-	}
-
 	PhysicsBodyPtr getPhysicsBody() const
 	PhysicsBodyPtr getPhysicsBody() const
 	{
 	{
 		return m_body;
 		return m_body;
@@ -62,12 +45,16 @@ public:
 		return m_mesh.isCreated();
 		return m_mesh.isCreated();
 	}
 	}
 
 
+	void teleportTo(const Transform& trf)
+	{
+		m_body->setTransform(trf);
+	}
+
 private:
 private:
 	SceneNode* m_node = nullptr;
 	SceneNode* m_node = nullptr;
 	CpuMeshResourcePtr m_mesh;
 	CpuMeshResourcePtr m_mesh;
 	PhysicsBodyPtr m_body;
 	PhysicsBodyPtr m_body;
-	Transform m_trf = Transform::getIdentity();
-	Bool m_markedForUpdate = true;
+	Bool m_dirty = true;
 
 
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 };
 };

+ 55 - 25
AnKi/Scene/Components/CameraComponent.cpp

@@ -6,13 +6,50 @@
 #include <AnKi/Scene/Components/CameraComponent.h>
 #include <AnKi/Scene/Components/CameraComponent.h>
 #include <AnKi/Scene/Components/MoveComponent.h>
 #include <AnKi/Scene/Components/MoveComponent.h>
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneNode.h>
+#include <AnKi/Gr/GrManager.h>
+#include <AnKi/Core/ConfigSet.h>
 
 
 namespace anki {
 namespace anki {
 
 
 CameraComponent::CameraComponent(SceneNode* node)
 CameraComponent::CameraComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
 	: SceneComponent(node, getStaticClassId())
 {
 {
+	const ConfigSet& config = *getExternalSubsystems(*node).m_config;
+
+	// Init main frustum
 	m_frustum.init(FrustumType::kPerspective, &node->getMemoryPool());
 	m_frustum.init(FrustumType::kPerspective, &node->getMemoryPool());
+
+	m_frustum.setLodDistance(0, config.getLod0MaxDistance());
+	m_frustum.setLodDistance(1, config.getLod1MaxDistance());
+	m_frustum.setShadowCascadeCount(config.getSceneShadowCascadeCount());
+
+	static_assert(kMaxShadowCascades == 4);
+	m_frustum.setShadowCascadeDistance(0, config.getSceneShadowCascade0Distance());
+	m_frustum.setShadowCascadeDistance(1, config.getSceneShadowCascade1Distance());
+	m_frustum.setShadowCascadeDistance(2, config.getSceneShadowCascade2Distance());
+	m_frustum.setShadowCascadeDistance(3, config.getSceneShadowCascade3Distance());
+
+	m_frustum.setWorldTransform(node->getWorldTransform());
+
+	m_frustum.setEarlyZDistance(config.getSceneEarlyZDistance());
+
+	m_frustum.update();
+
+	// Init extended frustum
+	m_usesExtendedFrustum = getExternalSubsystems(*node).m_grManager->getDeviceCapabilities().m_rayTracingEnabled
+							&& config.getSceneRayTracedShadows();
+
+	if(m_usesExtendedFrustum)
+	{
+		m_extendedFrustum.init(FrustumType::kOrthographic, &node->getMemoryPool());
+
+		const F32 dist = config.getSceneRayTracingExtendedFrustumDistance();
+
+		m_extendedFrustum.setOrthographic(0.1f, dist * 2.0f, dist, -dist, dist, -dist);
+		m_extendedFrustum.setWorldTransform(computeExtendedFrustumTransform(node->getWorldTransform()));
+
+		m_extendedFrustum.update();
+	}
 }
 }
 
 
 CameraComponent::~CameraComponent()
 CameraComponent::~CameraComponent()
@@ -21,42 +58,35 @@ CameraComponent::~CameraComponent()
 
 
 Error CameraComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 Error CameraComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 {
-	if(m_moveComponent) [[likely]]
-	{
-		updated = m_frustum.update(m_moveComponent->wasDirtyThisFrame(), m_moveComponent->getWorldTransform());
-	}
-	else
+	if(info.m_node->movedThisFrame())
 	{
 	{
-		updated = m_frustum.update(true, Transform::getIdentity());
+		m_frustum.setWorldTransform(info.m_node->getWorldTransform());
+
+		if(m_usesExtendedFrustum)
+		{
+			m_extendedFrustum.setWorldTransform(computeExtendedFrustumTransform(info.m_node->getWorldTransform()));
+		}
 	}
 	}
 
 
-	return Error::kNone;
-}
+	updated = m_frustum.update();
 
 
-void CameraComponent::onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added)
-{
-	if(other->getClassId() != MoveComponent::getStaticClassId())
+	if(m_usesExtendedFrustum)
 	{
 	{
-		return;
+		updated = updated || m_extendedFrustum.update();
 	}
 	}
 
 
-	MoveComponent* movec = static_cast<MoveComponent*>(other);
-	if(added)
-	{
-		m_moveComponent = movec;
-	}
-	else if(m_moveComponent == movec)
-	{
-		m_moveComponent = nullptr;
-	}
+	return Error::kNone;
 }
 }
 
 
-void CameraComponent::fillCoverage(void* userData, F32* depthValues, U32 width, U32 height)
+Transform CameraComponent::computeExtendedFrustumTransform(const Transform& cameraTransform) const
 {
 {
-	ANKI_ASSERT(userData && depthValues && width > 0 && height > 0);
+	const F32 far = m_extendedFrustum.getFar();
+	Transform extendedFrustumTransform = Transform::getIdentity();
+	Vec3 newOrigin = cameraTransform.getOrigin().xyz();
+	newOrigin.z() += far / 2.0f;
+	extendedFrustumTransform.setOrigin(newOrigin.xyz0());
 
 
-	CameraComponent& self = *static_cast<CameraComponent*>(userData);
-	self.m_frustum.setCoverageBuffer(depthValues, width, height);
+	return extendedFrustumTransform;
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 47 - 5
AnKi/Scene/Components/CameraComponent.h

@@ -75,25 +75,67 @@ public:
 		return m_frustum.getFovY();
 		return m_frustum.getFovY();
 	}
 	}
 
 
-	const Frustum& getFrustum() const
+	void setPerspective(F32 near, F32 far, F32 fovx, F32 fovy)
+	{
+		m_frustum.setPerspective(near, far, fovx, fovy);
+	}
+
+	void setShadowCascadeDistance(U32 cascade, F32 distance)
+	{
+		if(ANKI_SCENE_ASSERT(cascade < m_frustum.getShadowCascadeCount()))
+		{
+			m_frustum.setShadowCascadeDistance(cascade, distance);
+		}
+	}
+
+	F32 getShadowCascadeDistance(U32 cascade) const
+	{
+		if(ANKI_SCENE_ASSERT(cascade < m_frustum.getShadowCascadeCount()))
+		{
+			return m_frustum.getShadowCascadeDistance(cascade);
+		}
+		else
+		{
+			return 0.0f;
+		}
+	}
+
+	ANKI_INTERNAL const Frustum& getFrustum() const
 	{
 	{
 		return m_frustum;
 		return m_frustum;
 	}
 	}
 
 
-	Frustum& getFrustum()
+	ANKI_INTERNAL Frustum& getFrustum()
 	{
 	{
 		return m_frustum;
 		return m_frustum;
 	}
 	}
 
 
-	static void fillCoverage(void* userData, F32* depthValues, U32 width, U32 height);
+	ANKI_INTERNAL Bool getHasExtendedFrustum() const
+	{
+		return m_usesExtendedFrustum;
+	}
+
+	ANKI_INTERNAL const Frustum& getExtendedFrustum() const
+	{
+		ANKI_ASSERT(m_usesExtendedFrustum);
+		return m_extendedFrustum;
+	}
+
+	ANKI_INTERNAL Frustum& getExtendedFrustum()
+	{
+		ANKI_ASSERT(m_usesExtendedFrustum);
+		return m_extendedFrustum;
+	}
 
 
 private:
 private:
 	Frustum m_frustum;
 	Frustum m_frustum;
-	MoveComponent* m_moveComponent = nullptr;
+	Frustum m_extendedFrustum; ///< For ray tracing.
+
+	Bool m_usesExtendedFrustum : 1 = false;
 
 
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 
 
-	void onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added);
+	Transform computeExtendedFrustumTransform(const Transform& cameraTransform) const;
 };
 };
 /// @}
 /// @}
 
 

+ 34 - 70
AnKi/Scene/Components/DecalComponent.cpp

@@ -12,107 +12,71 @@ namespace anki {
 
 
 DecalComponent::DecalComponent(SceneNode* node)
 DecalComponent::DecalComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
 	: SceneComponent(node, getStaticClassId())
+	, m_spatial(this)
 	, m_node(node)
 	, m_node(node)
 {
 {
-	ANKI_ASSERT(node);
-	if(getExternalSubsystems(*m_node).m_resourceManager->loadResource("EngineAssets/GreenDecal.ankitex", m_debugImage))
-	{
-		ANKI_SCENE_LOGF("Failed to load resources");
-	}
 }
 }
 
 
 DecalComponent::~DecalComponent()
 DecalComponent::~DecalComponent()
 {
 {
+	m_spatial.removeFromOctree(m_node->getSceneGraph().getOctree());
 }
 }
 
 
-Error DecalComponent::setLayer(CString texAtlasFname, CString texAtlasSubtexName, F32 blendFactor, LayerType type)
+void DecalComponent::setLayer(CString fname, F32 blendFactor, LayerType type)
 {
 {
 	Layer& l = m_layers[type];
 	Layer& l = m_layers[type];
 
 
-	ANKI_CHECK(getExternalSubsystems(*m_node).m_resourceManager->loadResource(texAtlasFname, l.m_atlas));
+	ImageResourcePtr rsrc;
 
 
-	ANKI_CHECK(l.m_atlas->getSubImageInfo(texAtlasSubtexName, &l.m_uv[0]));
-
-	// Add a border to the UVs to avoid complex shader logic
-	if(l.m_atlas->getSubImageMargin() < kAtlasSubImageMargin)
+	const Error err = getExternalSubsystems(*m_node).m_resourceManager->loadResource(fname, rsrc);
+	if(err)
 	{
 	{
-		ANKI_SCENE_LOGE("Need image atlas with margin at least %u", kAtlasSubImageMargin);
-		return Error::kUserData;
+		ANKI_SCENE_LOGE("Failed to load image");
+		return;
 	}
 	}
 
 
-	const Vec2 marginf = F32(kAtlasSubImageMargin / 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());
+	m_dirty = true;
 
 
+	l.m_image = std::move(rsrc);
+	l.m_bindlessTextureIndex = l.m_image->getTextureView()->getOrCreateBindlessTextureIndex();
 	l.m_blendFactor = blendFactor;
 	l.m_blendFactor = blendFactor;
-	return Error::kNone;
 }
 }
 
 
-void DecalComponent::updateInternal()
+Error DecalComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 {
-	const Vec3 halfBoxSize = m_boxSize / 2.0f;
+	updated = m_dirty || info.m_node->movedThisFrame();
 
 
-	// Calculate the texture matrix
-	const Mat4 worldTransform(m_trf);
-
-	const Mat4 viewMat = worldTransform.getInverse();
+	if(updated)
+	{
+		m_dirty = false;
 
 
-	const Mat4 projMat =
-		Mat4::calculateOrthographicProjectionMatrix(halfBoxSize.x(), -halfBoxSize.x(), halfBoxSize.y(),
-													-halfBoxSize.y(), kClusterObjectFrustumNearPlane, m_boxSize.z());
+		const Vec3 halfBoxSize = m_boxSize / 2.0f;
 
 
-	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);
+		// Calculate the texture matrix
+		const Mat4 worldTransform(info.m_node->getWorldTransform());
 
 
-	m_biasProjViewMat = biasMat4 * projMat * viewMat;
+		const Mat4 viewMat = worldTransform.getInverse();
 
 
-	// Calculate the OBB
-	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);
-}
+		const Mat4 projMat = Mat4::calculateOrthographicProjectionMatrix(halfBoxSize.x(), -halfBoxSize.x(),
+																		 halfBoxSize.y(), -halfBoxSize.y(),
+																		 kClusterObjectFrustumNearPlane, m_boxSize.z());
 
 
-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();
+		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);
 
 
-	Mat3 nonUniScale = Mat3::getZero();
-	nonUniScale(0, 0) = scale.x();
-	nonUniScale(1, 1) = scale.y();
-	nonUniScale(2, 2) = scale.z();
+		m_biasProjViewMat = biasMat4 * projMat * viewMat;
 
 
-	const Mat4 mvp = ctx.m_viewProjectionMatrix * Mat4(tsl, rot * nonUniScale, 1.0f);
+		// Update the spatial
+		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(info.m_node->getWorldTransform());
 
 
-	const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::kDepthTestOn);
-	if(enableDepthTest)
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::kLess);
-	}
-	else
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::kAlways);
+		m_spatial.setBoundingShape(m_obb);
+		m_spatial.update(info.m_node->getSceneGraph().getOctree());
 	}
 	}
 
 
-	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::kDitheredDepthTestOn), 2.0f, *ctx.m_rebarStagingPool,
-		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::kDitheredDepthTestOn), m_debugImage->getTextureView(),
-		ctx.m_sampler, Vec2(0.75f), *ctx.m_rebarStagingPool, ctx.m_commandBuffer);
-
-	// Restore state
-	if(!enableDepthTest)
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::kLess);
-	}
+	return Error::kNone;
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 22 - 80
AnKi/Scene/Components/DecalComponent.h

@@ -6,6 +6,7 @@
 #pragma once
 #pragma once
 
 
 #include <AnKi/Scene/Components/SceneComponent.h>
 #include <AnKi/Scene/Components/SceneComponent.h>
+#include <AnKi/Scene/Spatial.h>
 #include <AnKi/Resource/ImageAtlasResource.h>
 #include <AnKi/Resource/ImageAtlasResource.h>
 #include <AnKi/Collision/Obb.h>
 #include <AnKi/Collision/Obb.h>
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Renderer/RenderQueue.h>
@@ -27,21 +28,21 @@ public:
 
 
 	~DecalComponent();
 	~DecalComponent();
 
 
-	Error setDiffuseDecal(CString texAtlasFname, CString texAtlasSubtexName, F32 blendFactor)
+	void loadDiffuseImageResource(CString fname, F32 blendFactor)
 	{
 	{
-		return setLayer(texAtlasFname, texAtlasSubtexName, blendFactor, LayerType::kDiffuse);
+		setLayer(fname, blendFactor, LayerType::kDiffuse);
 	}
 	}
 
 
-	Error setSpecularRoughnessDecal(CString texAtlasFname, CString texAtlasSubtexName, F32 blendFactor)
+	void loadRoughnessMetalnessImageResource(CString fname, F32 blendFactor)
 	{
 	{
-		return setLayer(texAtlasFname, texAtlasSubtexName, blendFactor, LayerType::kSpecularRoughness);
+		setLayer(fname, blendFactor, LayerType::kRoughnessMetalness);
 	}
 	}
 
 
 	/// Update the internal structures.
 	/// Update the internal structures.
 	void setBoxVolumeSize(const Vec3& sizeXYZ)
 	void setBoxVolumeSize(const Vec3& sizeXYZ)
 	{
 	{
 		m_boxSize = sizeXYZ;
 		m_boxSize = sizeXYZ;
-		m_markedForUpdate = true;
+		m_dirty = true;
 	}
 	}
 
 
 	const Vec3& getBoxVolumeSize() const
 	const Vec3& getBoxVolumeSize() const
@@ -49,64 +50,18 @@ public:
 		return m_boxSize;
 		return m_boxSize;
 	}
 	}
 
 
-	const Obb& getBoundingVolumeWorldSpace() const
+	void setupDecalQueueElement(DecalQueueElement& el) const
 	{
 	{
-		return m_obb;
-	}
-
-	void setWorldTransform(const Transform& trf)
-	{
-		ANKI_ASSERT(trf.getScale() == 1.0f);
-		m_trf = trf;
-		m_markedForUpdate = true;
-	}
-
-	const Mat4& getBiasProjectionViewMatrix() const
-	{
-		return m_biasProjViewMat;
-	}
-
-	void getDiffuseAtlasInfo(Vec4& uv, TexturePtr& tex, F32& blendFactor) const
-	{
-		uv = m_layers[LayerType::kDiffuse].m_uv;
-		tex = m_layers[LayerType::kDiffuse].m_atlas->getTexture();
-		blendFactor = m_layers[LayerType::kDiffuse].m_blendFactor;
-	}
-
-	void getSpecularRoughnessAtlasInfo(Vec4& uv, TexturePtr& tex, F32& blendFactor) const
-	{
-		uv = m_layers[LayerType::kSpecularRoughness].m_uv;
-		if(m_layers[LayerType::kSpecularRoughness].m_atlas)
-		{
-			tex = m_layers[LayerType::kSpecularRoughness].m_atlas->getTexture();
-		}
-		else
-		{
-			tex.reset(nullptr);
-		}
-		blendFactor = m_layers[LayerType::kSpecularRoughness].m_blendFactor;
-	}
-
-	void setupDecalQueueElement(DecalQueueElement& el)
-	{
-		el.m_diffuseAtlas = (m_layers[LayerType::kDiffuse].m_atlas)
-								? m_layers[LayerType::kDiffuse].m_atlas->getTextureView().get()
-								: nullptr;
-		el.m_specularRoughnessAtlas = (m_layers[LayerType::kSpecularRoughness].m_atlas)
-										  ? m_layers[LayerType::kSpecularRoughness].m_atlas->getTextureView().get()
-										  : nullptr;
-		el.m_diffuseAtlasUv = m_layers[LayerType::kDiffuse].m_uv;
-		el.m_specularRoughnessAtlasUv = m_layers[LayerType::kSpecularRoughness].m_uv;
-		el.m_diffuseAtlasBlendFactor = m_layers[LayerType::kDiffuse].m_blendFactor;
-		el.m_specularRoughnessAtlasBlendFactor = m_layers[LayerType::kSpecularRoughness].m_blendFactor;
+		ANKI_ASSERT(isEnabled());
+		el.m_diffuseBindlessTextureIndex = m_layers[LayerType::kDiffuse].m_bindlessTextureIndex;
+		el.m_roughnessMetalnessBindlessTextureIndex = m_layers[LayerType::kRoughnessMetalness].m_bindlessTextureIndex;
+		el.m_diffuseBlendFactor = m_layers[LayerType::kDiffuse].m_blendFactor;
+		el.m_roughnessMetalnessBlendFactor = m_layers[LayerType::kRoughnessMetalness].m_blendFactor;
 		el.m_textureMatrix = m_biasProjViewMat;
 		el.m_textureMatrix = m_biasProjViewMat;
 		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 = [](RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) {
-			ANKI_ASSERT(userData.getSize() == 1);
-			static_cast<const DecalComponent*>(userData[0])->draw(ctx);
-		};
+		el.m_debugDrawCallback = nullptr;
 		el.m_debugDrawCallbackUserData = this;
 		el.m_debugDrawCallbackUserData = this;
 	}
 	}
 
 
@@ -114,44 +69,31 @@ private:
 	enum class LayerType : U8
 	enum class LayerType : U8
 	{
 	{
 		kDiffuse,
 		kDiffuse,
-		kSpecularRoughness,
+		kRoughnessMetalness,
 		kCount
 		kCount
 	};
 	};
 
 
 	class Layer
 	class Layer
 	{
 	{
 	public:
 	public:
-		ImageAtlasResourcePtr m_atlas;
-		Vec4 m_uv = Vec4(0.0f);
+		ImageResourcePtr m_image;
 		F32 m_blendFactor = 0.0f;
 		F32 m_blendFactor = 0.0f;
+		U32 m_bindlessTextureIndex = kMaxU32;
 	};
 	};
 
 
-	SceneNode* m_node = nullptr;
+	SceneNode* m_node;
+	Spatial m_spatial;
+
 	Array<Layer, U(LayerType::kCount)> m_layers;
 	Array<Layer, U(LayerType::kCount)> m_layers;
 	Mat4 m_biasProjViewMat = Mat4::getIdentity();
 	Mat4 m_biasProjViewMat = Mat4::getIdentity();
 	Vec3 m_boxSize = Vec3(1.0f);
 	Vec3 m_boxSize = Vec3(1.0f);
-	Transform m_trf = Transform::getIdentity();
 	Obb m_obb = Obb(Vec4(0.0f), Mat3x4::getIdentity(), Vec4(0.5f, 0.5f, 0.5f, 0.0f));
 	Obb m_obb = Obb(Vec4(0.0f), Mat3x4::getIdentity(), Vec4(0.5f, 0.5f, 0.5f, 0.0f));
-	ImageResourcePtr m_debugImage;
-	Bool m_markedForUpdate = true;
-
-	Error setLayer(CString texAtlasFname, CString texAtlasSubtexName, F32 blendFactor, LayerType type);
 
 
-	void updateInternal();
+	Bool m_dirty = true;
 
 
-	Error update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = m_markedForUpdate;
-		m_markedForUpdate = false;
-		if(updated)
-		{
-			updateInternal();
-		}
-
-		return Error::kNone;
-	}
+	void setLayer(CString fname, F32 blendFactor, LayerType type);
 
 
-	void draw(RenderQueueDrawContext& ctx) const;
+	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 };
 };
 /// @}
 /// @}
 
 

+ 34 - 0
AnKi/Scene/Components/FogDensityComponent.cpp

@@ -4,7 +4,41 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <AnKi/Scene/Components/FogDensityComponent.h>
 #include <AnKi/Scene/Components/FogDensityComponent.h>
+#include <AnKi/Scene/SceneNode.h>
+#include <AnKi/Scene/SceneGraph.h>
 
 
 namespace anki {
 namespace anki {
 
 
+void FogDensityComponent::onDestroy(SceneNode& node)
+{
+	m_spatial.removeFromOctree(node.getSceneGraph().getOctree());
+}
+
+Error FogDensityComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+{
+	updated = m_dirty || info.m_node->movedThisFrame();
+
+	if(updated)
+	{
+		m_dirty = false;
+
+		m_worldPos = info.m_node->getWorldTransform().getOrigin().xyz();
+
+		if(m_isBox)
+		{
+			const Aabb aabb(m_aabbMin + m_worldPos, m_aabbMax + m_worldPos);
+			m_spatial.setBoundingShape(aabb);
+		}
+		else
+		{
+			const Sphere sphere(m_worldPos, m_sphereRadius);
+			m_spatial.setBoundingShape(sphere);
+		}
+
+		m_spatial.update(info.m_node->getSceneGraph().getOctree());
+	}
+
+	return Error::kNone;
+}
+
 } // end namespace anki
 } // end namespace anki

+ 12 - 31
AnKi/Scene/Components/FogDensityComponent.h

@@ -6,6 +6,7 @@
 #pragma once
 #pragma once
 
 
 #include <AnKi/Scene/Components/SceneComponent.h>
 #include <AnKi/Scene/Components/SceneComponent.h>
+#include <AnKi/Scene/Spatial.h>
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Collision/Aabb.h>
 #include <AnKi/Collision/Aabb.h>
 #include <AnKi/Collision/Sphere.h>
 #include <AnKi/Collision/Sphere.h>
@@ -25,8 +26,7 @@ public:
 
 
 	FogDensityComponent(SceneNode* node)
 	FogDensityComponent(SceneNode* node)
 		: SceneComponent(node, getStaticClassId())
 		: SceneComponent(node, getStaticClassId())
-		, m_isBox(true)
-		, m_markedForUpdate(true)
+		, m_spatial(this)
 	{
 	{
 	}
 	}
 
 
@@ -36,7 +36,7 @@ public:
 		m_aabbMin = -sizeXYZ / 2.0f;
 		m_aabbMin = -sizeXYZ / 2.0f;
 		m_aabbMax = sizeXYZ / 2.0f;
 		m_aabbMax = sizeXYZ / 2.0f;
 		m_isBox = true;
 		m_isBox = true;
-		m_markedForUpdate = true;
+		m_dirty = true;
 	}
 	}
 
 
 	Vec3 getBoxVolumeSize() const
 	Vec3 getBoxVolumeSize() const
@@ -45,17 +45,11 @@ public:
 		return m_aabbMax.xyz() - m_aabbMin.xyz();
 		return m_aabbMax.xyz() - m_aabbMin.xyz();
 	}
 	}
 
 
-	Aabb getAabbWorldSpace() const
-	{
-		ANKI_ASSERT(isAabb());
-		return Aabb(m_aabbMin + m_worldPos, m_aabbMax + m_worldPos);
-	}
-
 	void setSphereVolumeRadius(F32 radius)
 	void setSphereVolumeRadius(F32 radius)
 	{
 	{
 		m_sphereRadius = max(kMinShapeSize, radius);
 		m_sphereRadius = max(kMinShapeSize, radius);
 		m_isBox = false;
 		m_isBox = false;
-		m_markedForUpdate = true;
+		m_dirty = true;
 	}
 	}
 
 
 	F32 getSphereVolumeRadius() const
 	F32 getSphereVolumeRadius() const
@@ -64,12 +58,6 @@ public:
 		return m_sphereRadius;
 		return m_sphereRadius;
 	}
 	}
 
 
-	Sphere getSphereWorldSpace() const
-	{
-		ANKI_ASSERT(isSphere());
-		return Sphere(m_worldPos, m_sphereRadius);
-	}
-
 	Bool isAabb() const
 	Bool isAabb() const
 	{
 	{
 		return m_isBox == true;
 		return m_isBox == true;
@@ -91,12 +79,6 @@ public:
 		return m_density;
 		return m_density;
 	}
 	}
 
 
-	void setWorldPosition(const Vec3& 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;
@@ -114,7 +96,7 @@ public:
 	}
 	}
 
 
 private:
 private:
-	Vec3 m_aabbMin = Vec3(0.0f);
+	Vec3 m_aabbMin = Vec3(0.0f); ///< In local space.
 
 
 	union
 	union
 	{
 	{
@@ -122,18 +104,17 @@ private:
 		F32 m_sphereRadius;
 		F32 m_sphereRadius;
 	};
 	};
 
 
+	Spatial m_spatial;
+
 	Vec3 m_worldPos = Vec3(0.0f);
 	Vec3 m_worldPos = Vec3(0.0f);
 	F32 m_density = 1.0f;
 	F32 m_density = 1.0f;
 
 
-	Bool m_isBox : 1;
-	Bool m_markedForUpdate : 1;
+	Bool m_isBox : 1 = true;
+	Bool m_dirty : 1 = true;
 
 
-	Error update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = m_markedForUpdate;
-		m_markedForUpdate = false;
-		return Error::kNone;
-	}
+	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+
+	void onDestroy(SceneNode& node);
 };
 };
 
 
 } // end namespace anki
 } // end namespace anki

+ 0 - 226
AnKi/Scene/Components/FrustumComponent.cpp

@@ -1,226 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/Components/FrustumComponent.h>
-#include <AnKi/Scene/SceneNode.h>
-#include <AnKi/Collision/Functions.h>
-
-namespace anki {
-
-FrustumComponent::FrustumComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
-	, m_node(node)
-	, m_shapeMarkedForUpdate(true)
-	, m_trfMarkedForUpdate(true)
-	, m_miscMarkedForUpdate(true)
-{
-	ANKI_ASSERT(&m_perspective.m_far == &m_ortho.m_far);
-	ANKI_ASSERT(node);
-
-	// Set some default values
-	setFrustumType(FrustumType::kPerspective);
-	for(U i = 0; i < m_misc.m_maxLodDistances.getSize(); ++i)
-	{
-		const F32 dist = (m_common.m_far - m_common.m_near) / F32(kMaxLodCount + 1);
-		m_misc.m_maxLodDistances[i] = m_common.m_near + dist * F32(i + 1);
-	}
-
-	updateInternal();
-}
-
-FrustumComponent::~FrustumComponent()
-{
-	m_coverageBuff.m_depthMap.destroy(m_node->getMemoryPool());
-}
-
-Bool FrustumComponent::updateInternal()
-{
-	ANKI_ASSERT(m_frustumType != FrustumType::kCount);
-
-	Bool updated = false;
-
-	for(U32 i = kPrevMatrixHistory - 1; i != 0; --i)
-	{
-		m_prevViewProjMats[i] = m_prevViewProjMats[i - 1];
-		m_prevViewMats[i] = m_prevViewMats[i - 1];
-		m_prevProjMats[i] = m_prevProjMats[i - 1];
-	}
-
-	m_prevViewProjMats[0] = m_viewProjMat;
-	m_prevViewMats[0] = m_viewMat;
-	m_prevProjMats[0] = m_projMat;
-
-	// Update the shape
-	if(m_shapeMarkedForUpdate)
-	{
-		updated = true;
-
-		// Fix user data
-		if(!ANKI_SCENE_ASSERT(m_common.m_far - m_common.m_near > 1.0_cm))
-		{
-			setFrustumType(m_frustumType);
-		}
-
-		if(m_frustumType == FrustumType::kPerspective)
-		{
-			m_projMat = Mat4::calculatePerspectiveProjectionMatrix(m_perspective.m_fovX, m_perspective.m_fovY,
-																   m_perspective.m_near, m_perspective.m_far);
-
-			computeEdgesOfFrustum(m_perspective.m_far, m_perspective.m_fovX, m_perspective.m_fovY,
-								  &m_perspective.m_edgesL[0]);
-
-			// Planes
-			F32 c, s; // cos & sine
-
-			sinCos(kPi + m_perspective.m_fovX / 2.0f, s, c);
-			// right
-			m_viewPlanesL[FrustumPlaneType::kRight] = Plane(Vec4(c, 0.0f, s, 0.0f), 0.0f);
-			// left
-			m_viewPlanesL[FrustumPlaneType::kLeft] = Plane(Vec4(-c, 0.0f, s, 0.0f), 0.0f);
-
-			sinCos((kPi + m_perspective.m_fovY) * 0.5f, s, c);
-			// bottom
-			m_viewPlanesL[FrustumPlaneType::kBottom] = Plane(Vec4(0.0f, s, c, 0.0f), 0.0f);
-			// top
-			m_viewPlanesL[FrustumPlaneType::kTop] = Plane(Vec4(0.0f, -s, c, 0.0f), 0.0f);
-
-			// near
-			m_viewPlanesL[FrustumPlaneType::kNear] = Plane(Vec4(0.0f, 0.0f, -1.0, 0.0f), m_perspective.m_near);
-			// far
-			m_viewPlanesL[FrustumPlaneType::kFar] = Plane(Vec4(0.0f, 0.0f, 1.0, 0.0f), -m_perspective.m_far);
-		}
-		else
-		{
-			m_projMat = Mat4::calculateOrthographicProjectionMatrix(m_ortho.m_right, m_ortho.m_left, m_ortho.m_top,
-																	m_ortho.m_bottom, m_ortho.m_near, m_ortho.m_far);
-
-			// OBB
-			const Vec4 c((m_ortho.m_right + m_ortho.m_left) * 0.5f, (m_ortho.m_top + m_ortho.m_bottom) * 0.5f,
-						 -(m_ortho.m_far + m_ortho.m_near) * 0.5f, 0.0f);
-			const Vec4 e = Vec4(m_ortho.m_right, m_ortho.m_top, -m_ortho.m_far, 0.0f) - c;
-
-			m_ortho.m_obbL = Obb(c, Mat3x4::getIdentity(), e);
-
-			// Planes
-			m_viewPlanesL[FrustumPlaneType::kLeft] = Plane(Vec4(1.0f, 0.0f, 0.0f, 0.0f), m_ortho.m_left);
-			m_viewPlanesL[FrustumPlaneType::kRight] = Plane(Vec4(-1.0f, 0.0f, 0.0f, 0.0f), -m_ortho.m_right);
-			m_viewPlanesL[FrustumPlaneType::kNear] = Plane(Vec4(0.0f, 0.0f, -1.0f, 0.0f), m_ortho.m_near);
-			m_viewPlanesL[FrustumPlaneType::kFar] = Plane(Vec4(0.0f, 0.0f, 1.0f, 0.0f), -m_ortho.m_far);
-			m_viewPlanesL[FrustumPlaneType::kTop] = Plane(Vec4(0.0f, -1.0f, 0.0f, 0.0f), -m_ortho.m_top);
-			m_viewPlanesL[FrustumPlaneType::kBottom] = Plane(Vec4(0.0f, 1.0f, 0.0f, 0.0f), m_ortho.m_bottom);
-		}
-	}
-
-	// Update transform related things
-	if(m_trfMarkedForUpdate)
-	{
-		updated = true;
-		m_viewMat = Mat3x4(m_trf.getInverse());
-	}
-
-	// Fixup the misc data
-	if(m_miscMarkedForUpdate)
-	{
-		updated = true;
-		const F32 frustumFraction = (m_common.m_far - m_common.m_near) / 100.0f;
-
-		for(U32 i = 0; i < m_misc.m_shadowCascadeCount; ++i)
-		{
-			if(!ANKI_SCENE_ASSERT(m_misc.m_shadowCascadeDistances[i] > m_common.m_near
-								  && m_misc.m_shadowCascadeDistances[i] <= m_common.m_far))
-			{
-				m_misc.m_shadowCascadeDistances[i] =
-					clamp(m_misc.m_shadowCascadeDistances[i], m_common.m_near + kEpsilonf, m_common.m_far);
-			}
-
-			if(i != 0
-			   && !ANKI_SCENE_ASSERT(m_misc.m_shadowCascadeDistances[i - 1] < m_misc.m_shadowCascadeDistances[i]))
-			{
-				m_misc.m_shadowCascadeDistances[i] = m_misc.m_shadowCascadeDistances[i - 1] + frustumFraction;
-			}
-		}
-
-		for(U32 i = 0; i < m_misc.m_maxLodDistances.getSize(); ++i)
-		{
-			if(!ANKI_SCENE_ASSERT(m_misc.m_maxLodDistances[i] > m_common.m_near
-								  && m_misc.m_maxLodDistances[i] <= m_common.m_far))
-			{
-				m_misc.m_maxLodDistances[i] =
-					clamp(m_misc.m_maxLodDistances[i], m_common.m_near + kEpsilonf, m_common.m_far);
-			}
-
-			if(i != 0 && !ANKI_SCENE_ASSERT(m_misc.m_maxLodDistances[i - 1] < m_misc.m_maxLodDistances[i]))
-			{
-				m_misc.m_maxLodDistances[i] = m_misc.m_maxLodDistances[i - 1] + frustumFraction;
-			}
-		}
-	}
-
-	// Updates that are affected by transform & shape updates
-	if(updated)
-	{
-		m_viewProjMat = m_projMat * Mat4(m_viewMat, Vec4(0.0f, 0.0f, 0.0f, 1.0f));
-		m_shapeMarkedForUpdate = false;
-		m_trfMarkedForUpdate = false;
-		m_miscMarkedForUpdate = false;
-
-		if(m_frustumType == FrustumType::kPerspective)
-		{
-			m_perspective.m_edgesW[0] = m_trf.getOrigin();
-			m_perspective.m_edgesW[1] = m_trf.transform(m_perspective.m_edgesL[0]);
-			m_perspective.m_edgesW[2] = m_trf.transform(m_perspective.m_edgesL[1]);
-			m_perspective.m_edgesW[3] = m_trf.transform(m_perspective.m_edgesL[2]);
-			m_perspective.m_edgesW[4] = m_trf.transform(m_perspective.m_edgesL[3]);
-
-			m_perspective.m_hull = ConvexHullShape(&m_perspective.m_edgesW[0], m_perspective.m_edgesW.getSize());
-		}
-		else
-		{
-			m_ortho.m_obbW = m_ortho.m_obbL.getTransformed(m_trf);
-		}
-
-		for(FrustumPlaneType planeId : EnumIterable<FrustumPlaneType>())
-		{
-			m_viewPlanesW[planeId] = m_viewPlanesL[planeId].getTransformed(m_trf);
-		}
-	}
-
-	return updated;
-}
-
-void FrustumComponent::fillCoverageBufferCallback(void* userData, F32* depthValues, U32 width, U32 height)
-{
-	ANKI_ASSERT(userData && depthValues && width > 0 && height > 0);
-	FrustumComponent& self = *static_cast<FrustumComponent*>(userData);
-
-	self.m_coverageBuff.m_depthMap.destroy(self.m_node->getMemoryPool());
-	self.m_coverageBuff.m_depthMap.create(self.m_node->getMemoryPool(), width * height);
-	memcpy(&self.m_coverageBuff.m_depthMap[0], depthValues, self.m_coverageBuff.m_depthMap.getSizeInBytes());
-
-	self.m_coverageBuff.m_depthMapWidth = width;
-	self.m_coverageBuff.m_depthMapHeight = height;
-}
-
-void FrustumComponent::setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag bits)
-{
-	m_flags = FrustumComponentVisibilityTestFlag::kNone;
-	m_flags |= bits;
-
-#if ANKI_ENABLE_ASSERTIONS
-	if(!!(m_flags & FrustumComponentVisibilityTestFlag::kRenderComponents)
-	   || !!(m_flags & FrustumComponentVisibilityTestFlag::kShadowCasterRenderComponents))
-	{
-		if((m_flags & FrustumComponentVisibilityTestFlag::kRenderComponents)
-		   == (m_flags & FrustumComponentVisibilityTestFlag::kShadowCasterRenderComponents))
-		{
-			ANKI_ASSERT(0 && "Cannot have them both");
-		}
-	}
-
-	// TODO
-#endif
-}
-
-} // end namespace anki

+ 0 - 479
AnKi/Scene/Components/FrustumComponent.h

@@ -1,479 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <AnKi/Scene/Components/SceneComponent.h>
-#include <AnKi/Util/BitMask.h>
-#include <AnKi/Util/WeakArray.h>
-#include <AnKi/Collision/Obb.h>
-#include <AnKi/Collision/ConvexHullShape.h>
-#include <AnKi/Collision/Plane.h>
-#include <AnKi/Resource/Common.h>
-#include <AnKi/Shaders/Include/Common.h>
-
-namespace anki {
-
-/// @addtogroup scene
-/// @{
-
-/// Flags that affect visibility tests.
-enum class FrustumComponentVisibilityTestFlag : U32
-{
-	kNone = 0,
-	kRenderComponents = 1 << 0,
-	kShadowCasterRenderComponents = 1 << 1, ///< Render components that cast shadow
-	kLights = 1 << 2,
-	kLensFlares = 1 << 3,
-	kReflectionProbes = 1 << 4,
-	kOccluders = 1 << 5,
-	kDecals = 1 << 6,
-	kFogDensityVolumes = 1 << 7,
-	kGlobalIlluminationProbes = 1 << 8,
-	kGenericComputeJobs = 1 << 9,
-	kRayTracingShadows = 1 << 10,
-	kRayTracingGi = 1 << 11,
-	kRayTracingReflections = 1 << 12,
-	kRayTracingPathTracing = 1 << 13,
-	kUi = 1 << 14,
-	kSkybox = 1 << 15,
-	kEarlyZ = 1 << 16,
-	kPointLightShadowsEnabled = 1 << 17,
-	kSpotLightShadowsEnabled = 1 << 18,
-
-	kAll = kRenderComponents | kShadowCasterRenderComponents | kLights | kLensFlares | kReflectionProbes | kOccluders
-		   | kDecals | kFogDensityVolumes | kGlobalIlluminationProbes | kGenericComputeJobs | kRayTracingShadows
-		   | kRayTracingGi | kRayTracingReflections | kRayTracingPathTracing | kUi | kSkybox | kEarlyZ
-		   | kPointLightShadowsEnabled | kSpotLightShadowsEnabled,
-
-	kAllRayTracing = kRayTracingShadows | kRayTracingGi | kRayTracingReflections | kRayTracingPathTracing,
-};
-ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(FrustumComponentVisibilityTestFlag)
-
-/// Frustum component. Useful for nodes that take part in visibility tests like cameras and lights.
-class FrustumComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(FrustumComponent)
-
-public:
-	FrustumComponent(SceneNode* node);
-
-	~FrustumComponent();
-
-	SceneNode& getSceneNode()
-	{
-		return *m_node;
-	}
-
-	const SceneNode& getSceneNode() const
-	{
-		return *m_node;
-	}
-
-	void setFrustumType(FrustumType type)
-	{
-		ANKI_ASSERT(type >= FrustumType::kFirst && type < FrustumType::kCount);
-		m_frustumType = type;
-		setNear(kDefaultNear);
-		setFar(kDefaultFar);
-		if(m_frustumType == FrustumType::kPerspective)
-		{
-			setFovX(kDefaultFovAngle);
-			setFovY(kDefaultFovAngle);
-		}
-		else
-		{
-			setLeft(-5.0f);
-			setRight(5.0f);
-			setBottom(-1.0f);
-			setTop(1.0f);
-		}
-	}
-
-	void setPerspective(F32 near, F32 far, F32 fovX, F32 fovY)
-	{
-		ANKI_ASSERT(m_frustumType == FrustumType::kPerspective);
-		setNear(near);
-		setFar(far);
-		setFovX(fovX);
-		setFovY(fovY);
-	}
-
-	void setOrthographic(F32 near, F32 far, F32 right, F32 left, F32 top, F32 bottom)
-	{
-		ANKI_ASSERT(m_frustumType == FrustumType::kOrthographic);
-		setNear(near);
-		setFar(far);
-		setLeft(left);
-		setRight(right);
-		setTop(top);
-		setBottom(bottom);
-	}
-
-	FrustumType getFrustumType() const
-	{
-		ANKI_ASSERT(m_frustumType != FrustumType::kCount);
-		return m_frustumType;
-	}
-
-	void setNear(F32 near)
-	{
-		if(ANKI_SCENE_ASSERT(near > 0.0f))
-		{
-			m_common.m_near = near;
-			m_shapeMarkedForUpdate = true;
-		}
-	}
-
-	F32 getNear() const
-	{
-		return m_common.m_near;
-	}
-
-	void setFar(F32 far)
-	{
-		if(ANKI_SCENE_ASSERT(far > 0.0f))
-		{
-			m_common.m_far = far;
-			m_shapeMarkedForUpdate = true;
-		}
-	}
-
-	F32 getFar() const
-	{
-		return m_common.m_far;
-	}
-
-	void setFovX(F32 fovx)
-	{
-		if(ANKI_SCENE_ASSERT(m_frustumType == FrustumType::kPerspective)
-		   && ANKI_SCENE_ASSERT(fovx > 0.0f && fovx < kPi))
-		{
-			m_shapeMarkedForUpdate = true;
-			m_perspective.m_fovX = fovx;
-		}
-	}
-
-	F32 getFovX() const
-	{
-		return (ANKI_SCENE_ASSERT(m_frustumType == FrustumType::kPerspective)) ? m_perspective.m_fovX : 0.0f;
-	}
-
-	void setFovY(F32 fovy)
-	{
-		if(ANKI_SCENE_ASSERT(m_frustumType == FrustumType::kPerspective)
-		   && ANKI_SCENE_ASSERT(fovy > 0.0f && fovy < kPi))
-		{
-			m_shapeMarkedForUpdate = true;
-			m_perspective.m_fovY = fovy;
-		}
-	}
-
-	F32 getFovY() const
-	{
-		return (ANKI_SCENE_ASSERT(m_frustumType == FrustumType::kPerspective)) ? m_perspective.m_fovY : 0.0f;
-	}
-
-	F32 getLeft() const
-	{
-		return (ANKI_SCENE_ASSERT(m_frustumType == FrustumType::kOrthographic)) ? m_ortho.m_left : 0.0f;
-	}
-
-	void setLeft(F32 value)
-	{
-		if(ANKI_SCENE_ASSERT(m_frustumType == FrustumType::kOrthographic))
-		{
-			m_shapeMarkedForUpdate = true;
-			m_ortho.m_left = value;
-		}
-	}
-
-	F32 getRight() const
-	{
-		return (ANKI_SCENE_ASSERT(m_frustumType == FrustumType::kOrthographic)) ? m_ortho.m_right : 0.0f;
-	}
-
-	void setRight(F32 value)
-	{
-		if(ANKI_SCENE_ASSERT(m_frustumType == FrustumType::kOrthographic))
-		{
-			m_shapeMarkedForUpdate = true;
-			m_ortho.m_right = value;
-		}
-	}
-
-	F32 getTop() const
-	{
-		return (ANKI_SCENE_ASSERT(m_frustumType == FrustumType::kOrthographic)) ? m_ortho.m_top : 0.0f;
-	}
-
-	void setTop(F32 value)
-	{
-		if(ANKI_SCENE_ASSERT(m_frustumType == FrustumType::kOrthographic))
-		{
-			m_shapeMarkedForUpdate = true;
-			m_ortho.m_top = value;
-		}
-	}
-
-	F32 getBottom() const
-	{
-		return (ANKI_SCENE_ASSERT(m_frustumType == FrustumType::kOrthographic)) ? m_ortho.m_bottom : 0.0f;
-	}
-
-	void setBottom(F32 value)
-	{
-		if(ANKI_SCENE_ASSERT(m_frustumType == FrustumType::kOrthographic))
-		{
-			m_shapeMarkedForUpdate = true;
-			m_ortho.m_bottom = value;
-		}
-	}
-
-	const Transform& getWorldTransform() const
-	{
-		return m_trf;
-	}
-
-	void setWorldTransform(const Transform& trf)
-	{
-		m_trf = trf;
-		m_trfMarkedForUpdate = true;
-	}
-
-	const Mat4& getProjectionMatrix() const
-	{
-		return m_projMat;
-	}
-
-	const Mat3x4& getViewMatrix() const
-	{
-		return m_viewMat;
-	}
-
-	const Mat4& getViewProjectionMatrix() const
-	{
-		return m_viewProjMat;
-	}
-
-	/// @param nMinusOneFrame The number of the previous frame. If 0 means the previous frame, 1 means the
-	///                       previous-previous frame.
-	const Mat4& getPreviousViewProjectionMatrix(U32 nMinusOneFrame = 0) const
-	{
-		return m_prevViewProjMats[nMinusOneFrame];
-	}
-
-	/// @see getPreviousViewProjectionMatrix.
-	const Mat3x4& getPreviousViewMatrix(U32 nMinusOneFrame = 0) const
-	{
-		return m_prevViewMats[nMinusOneFrame];
-	}
-
-	/// @see getPreviousViewProjectionMatrix.
-	const Mat4& getPreviousProjectionMatrix(U32 nMinusOneFrame = 0) const
-	{
-		return m_prevProjMats[nMinusOneFrame];
-	}
-
-	/// Check if a shape is inside the frustum.
-	template<typename T>
-	Bool insideFrustum(const T& t) const
-	{
-		for(const Plane& plane : m_viewPlanesW)
-		{
-			if(testPlane(plane, t) < 0.0f)
-			{
-				return false;
-			}
-		}
-
-		return true;
-	}
-
-	void setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag bits);
-
-	FrustumComponentVisibilityTestFlag getEnabledVisibilityTests() const
-	{
-		return m_flags;
-	}
-
-	/// The type is FillCoverageBufferCallback.
-	static void fillCoverageBufferCallback(void* userData, F32* depthValues, U32 width, U32 height);
-
-	Bool hasCoverageBuffer() const
-	{
-		return m_coverageBuff.m_depthMap.getSize() > 0;
-	}
-
-	void getCoverageBufferInfo(ConstWeakArray<F32>& depthBuff, U32& width, U32& height) const
-	{
-		if(m_coverageBuff.m_depthMap.getSize() > 0)
-		{
-			depthBuff = ConstWeakArray<F32>(&m_coverageBuff.m_depthMap[0], m_coverageBuff.m_depthMap.getSize());
-			width = m_coverageBuff.m_depthMapWidth;
-			height = m_coverageBuff.m_depthMapHeight;
-		}
-		else
-		{
-			depthBuff = ConstWeakArray<F32>();
-			width = height = 0;
-		}
-	}
-
-	void setShadowCascadeDistance(U32 cascade, F32 distance)
-	{
-		m_misc.m_shadowCascadeDistances[cascade] = distance;
-		m_miscMarkedForUpdate = true;
-	}
-
-	F32 getShadowCascadeDistance(U32 cascade) const
-	{
-		return m_misc.m_shadowCascadeDistances[cascade];
-	}
-
-	[[nodiscard]] U32 getShadowCascadeCount() const
-	{
-		return m_misc.m_shadowCascadeCount;
-	}
-
-	void setShadowCascadeCount(U32 count)
-	{
-		m_misc.m_shadowCascadeCount = U8(count);
-		m_miscMarkedForUpdate = true;
-	}
-
-	const ConvexHullShape& getPerspectiveBoundingShapeWorldSpace() const
-	{
-		ANKI_ASSERT(m_frustumType == FrustumType::kPerspective);
-		return m_perspective.m_hull;
-	}
-
-	const Obb& getOrthographicBoundingShapeWorldSpace() const
-	{
-		ANKI_ASSERT(m_frustumType == FrustumType::kOrthographic);
-		return m_ortho.m_obbW;
-	}
-
-	const Array<Plane, U32(FrustumPlaneType::kCount)>& getViewPlanes() const
-	{
-		return m_viewPlanesW;
-	}
-
-	/// Set the lod distance. It doesn't interact with the far plane.
-	void setLodDistance(U32 lod, F32 maxDistance)
-	{
-		m_misc.m_maxLodDistances[lod] = maxDistance;
-		m_miscMarkedForUpdate = true;
-	}
-
-	void setLodDistances(const Array<F32, kMaxLodCount>& distances)
-	{
-		for(U i = 0; i < m_misc.m_maxLodDistances.getSize(); ++i)
-		{
-			ANKI_ASSERT(distances[i] > m_common.m_near && distances[i] <= m_common.m_far);
-			m_misc.m_maxLodDistances[i] = distances[i];
-		}
-	}
-
-	/// See setLodDistance.
-	F32 getLodDistance(U32 lod) const
-	{
-		ANKI_ASSERT(m_misc.m_maxLodDistances[lod] > 0.0f);
-		return m_misc.m_maxLodDistances[lod];
-	}
-
-private:
-	class Common
-	{
-	public:
-		F32 m_near;
-		F32 m_far;
-	};
-
-	class Perspective : public Common
-	{
-	public:
-		F32 m_fovX;
-		F32 m_fovY;
-		Array<Vec4, 5> m_edgesW;
-		Array<Vec4, 4> m_edgesL; ///< Don't need the eye point.
-		ConvexHullShape m_hull;
-	};
-
-	class Ortho : public Common
-	{
-	public:
-		F32 m_left;
-		F32 m_right;
-		F32 m_top;
-		F32 m_bottom;
-		Obb m_obbL;
-		Obb m_obbW; ///< Including shape
-	};
-
-	static constexpr F32 kDefaultNear = 0.1f;
-	static constexpr F32 kDefaultFar = 100.0f;
-	static constexpr F32 kDefaultFovAngle = toRad(45.0f);
-
-	SceneNode* m_node;
-
-	FrustumType m_frustumType = FrustumType::kCount;
-
-	union
-	{
-		Perspective m_perspective;
-		Ortho m_ortho;
-		Common m_common;
-	};
-
-	// View planes
-	Array<Plane, U32(FrustumPlaneType::kCount)> m_viewPlanesL;
-	Array<Plane, U32(FrustumPlaneType::kCount)> m_viewPlanesW;
-
-	Transform m_trf = Transform::getIdentity();
-	Mat4 m_projMat = Mat4::getIdentity(); ///< Projection matrix
-	Mat3x4 m_viewMat = Mat3x4::getIdentity(); ///< View matrix
-	Mat4 m_viewProjMat = Mat4::getIdentity(); ///< View projection matrix
-
-	static constexpr U32 kPrevMatrixHistory = 2;
-	Array<Mat3x4, kPrevMatrixHistory> m_prevViewMats = {Mat3x4::getIdentity(), Mat3x4::getIdentity()};
-	Array<Mat4, kPrevMatrixHistory> m_prevProjMats = {Mat4::getIdentity(), Mat4::getIdentity()};
-	Array<Mat4, kPrevMatrixHistory> m_prevViewProjMats = {Mat4::getIdentity(), Mat4::getIdentity()};
-
-	class
-	{
-	public:
-		Array<F32, kMaxShadowCascades> m_shadowCascadeDistances = {};
-
-		Array<F32, kMaxLodCount - 1> m_maxLodDistances = {};
-
-		U8 m_shadowCascadeCount = 0;
-	} m_misc; ///< Misc stuff that this component just holds.
-
-	class
-	{
-	public:
-		DynamicArray<F32> m_depthMap;
-		U32 m_depthMapWidth = 0;
-		U32 m_depthMapHeight = 0;
-	} m_coverageBuff; ///< Coverage buffer for extra visibility tests.
-
-	FrustumComponentVisibilityTestFlag m_flags = FrustumComponentVisibilityTestFlag::kNone;
-	Bool m_shapeMarkedForUpdate : 1;
-	Bool m_trfMarkedForUpdate : 1;
-	Bool m_miscMarkedForUpdate : 1;
-
-	Bool updateInternal();
-
-	Error update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		ANKI_ASSERT(info.m_node == m_node);
-		updated = updateInternal();
-		return Error::kNone;
-	}
-};
-/// @}
-
-} // end namespace anki

+ 0 - 10
AnKi/Scene/Components/GenericGpuComputeJobComponent.cpp

@@ -1,10 +0,0 @@
-// Copyright (C) 2009-2022, 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 {
-
-} // end namespace anki

+ 0 - 51
AnKi/Scene/Components/GenericGpuComputeJobComponent.h

@@ -1,51 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <AnKi/Scene/Components/SceneComponent.h>
-#include <AnKi/Renderer/RenderQueue.h>
-
-namespace anki {
-
-/// @addtogroup scene
-/// @{
-
-/// Holds some info for GPU compute jobs.
-class GenericGpuComputeJobComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(GenericGpuComputeJobComponent)
-
-public:
-	GenericGpuComputeJobComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId())
-	{
-	}
-
-	~GenericGpuComputeJobComponent()
-	{
-	}
-
-	void setCallback(GenericGpuComputeJobQueueElementCallback callback, const void* userData)
-	{
-		ANKI_ASSERT(callback && userData);
-		m_callback = callback;
-		m_userData = userData;
-	}
-
-	void setupGenericGpuComputeJobQueueElement(GenericGpuComputeJobQueueElement& el)
-	{
-		ANKI_ASSERT(m_callback && m_userData);
-		el.m_callback = m_callback;
-		el.m_userData = m_userData;
-	}
-
-private:
-	GenericGpuComputeJobQueueElementCallback m_callback = nullptr;
-	const void* m_userData = nullptr;
-};
-/// @}
-
-} // end namespace anki

+ 49 - 38
AnKi/Scene/Components/GlobalIlluminationProbeComponent.cpp

@@ -4,23 +4,26 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <AnKi/Scene/Components/GlobalIlluminationProbeComponent.h>
 #include <AnKi/Scene/Components/GlobalIlluminationProbeComponent.h>
+#include <AnKi/Scene/Components/MoveComponent.h>
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Scene/SceneGraph.h>
-#include <AnKi/Resource/ImageResource.h>
-#include <AnKi/Resource/ResourceManager.h>
+#include <AnKi/Core/ConfigSet.h>
 
 
 namespace anki {
 namespace anki {
 
 
 GlobalIlluminationProbeComponent::GlobalIlluminationProbeComponent(SceneNode* node)
 GlobalIlluminationProbeComponent::GlobalIlluminationProbeComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
 	: SceneComponent(node, getStaticClassId())
-	, m_node(node)
 	, m_uuid(node->getSceneGraph().getNewUuid())
 	, m_uuid(node->getSceneGraph().getNewUuid())
-	, m_markedForRendering(false)
-	, m_shapeDirty(true)
+	, m_spatial(this)
 {
 {
-	if(getExternalSubsystems(*m_node).m_resourceManager->loadResource("EngineAssets/GiProbe.ankitex", m_debugImage))
+	for(U32 i = 0; i < 6; ++i)
 	{
 	{
-		ANKI_SCENE_LOGF("Failed to load resources");
+		m_frustums[i].init(FrustumType::kPerspective, &node->getMemoryPool());
+		m_frustums[i].setPerspective(kClusterObjectFrustumNearPlane, 100.0f, kPi / 2.0f, kPi / 2.0f);
+		m_frustums[i].setWorldTransform(
+			Transform(node->getWorldTransform().getOrigin(), Frustum::getOmnidirectionalFrustumRotations()[i], 1.0f));
+		m_frustums[i].setShadowCascadeCount(1);
+		m_frustums[i].update();
 	}
 	}
 }
 }
 
 
@@ -28,46 +31,54 @@ GlobalIlluminationProbeComponent::~GlobalIlluminationProbeComponent()
 {
 {
 }
 }
 
 
-void GlobalIlluminationProbeComponent::draw(RenderQueueDrawContext& ctx) const
+void GlobalIlluminationProbeComponent::onDestroy(SceneNode& node)
 {
 {
-	const Aabb box = getAabbWorldSpace();
+	m_spatial.removeFromOctree(node.getSceneGraph().getOctree());
+}
 
 
-	const Vec3 tsl = (box.getMax().xyz() + box.getMin().xyz()) / 2.0f;
-	const Vec3 scale = (tsl - box.getMin().xyz());
+Error GlobalIlluminationProbeComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+{
+	updated = info.m_node->movedThisFrame() || m_shapeDirty;
 
 
-	// Set non uniform scale.
-	Mat3 rot = Mat3::getIdentity();
-	rot(0, 0) *= scale.x();
-	rot(1, 1) *= scale.y();
-	rot(2, 2) *= scale.z();
+	if(updated) [[unlikely]]
+	{
+		m_shapeDirty = false;
 
 
-	const Mat4 mvp = ctx.m_viewProjectionMatrix * Mat4(tsl.xyz1(), rot, 1.0f);
+		// Set a new UUID to force the renderer to update the probe
+		m_uuid = info.m_node->getSceneGraph().getNewUuid();
 
 
-	const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::kDepthTestOn);
-	if(enableDepthTest)
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::kLess);
-	}
-	else
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::kAlways);
-	}
+		m_worldPos = info.m_node->getWorldTransform().getOrigin().xyz();
 
 
-	m_node->getSceneGraph().getDebugDrawer().drawCubes(
-		ConstWeakArray<Mat4>(&mvp, 1), Vec4(0.729f, 0.635f, 0.196f, 1.0f), 1.0f,
-		ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::kDitheredDepthTestOn), 2.0f, *ctx.m_rebarStagingPool,
-		ctx.m_commandBuffer);
+		F32 effectiveDistance = max(m_halfSize.x(), m_halfSize.y());
+		effectiveDistance = max(effectiveDistance, m_halfSize.z());
+		effectiveDistance =
+			max(effectiveDistance, getExternalSubsystems(*info.m_node).m_config->getSceneProbeEffectiveDistance());
 
 
-	m_node->getSceneGraph().getDebugDrawer().drawBillboardTextures(
-		ctx.m_projectionMatrix, ctx.m_viewMatrix, ConstWeakArray<Vec3>(&m_worldPosition, 1), Vec4(1.0f),
-		ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::kDitheredDepthTestOn), m_debugImage->getTextureView(),
-		ctx.m_sampler, Vec2(0.75f), *ctx.m_rebarStagingPool, ctx.m_commandBuffer);
+		const F32 shadowCascadeDistance = min(
+			effectiveDistance, getExternalSubsystems(*info.m_node).m_config->getSceneProbeShadowEffectiveDistance());
 
 
-	// Restore state
-	if(!enableDepthTest)
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::kLess);
+		for(U32 i = 0; i < 6; ++i)
+		{
+			m_frustums[i].setWorldTransform(
+				Transform(m_worldPos.xyz0(), Frustum::getOmnidirectionalFrustumRotations()[i], 1.0f));
+
+			m_frustums[i].setFar(effectiveDistance);
+			m_frustums[i].setShadowCascadeDistance(0, shadowCascadeDistance);
+
+			// Add something really far to force LOD 0 to be used. The importing tools create LODs with holes some times
+			// and that causes the sky to bleed to GI rendering
+			m_frustums[i].setLodDistances({effectiveDistance - 3.0f * kEpsilonf, effectiveDistance - 2.0f * kEpsilonf,
+										   effectiveDistance - 1.0f * kEpsilonf});
+
+			m_frustums[i].update();
+		}
+
+		const Aabb aabb(-m_halfSize + m_worldPos, m_halfSize + m_worldPos);
+		m_spatial.setBoundingShape(aabb);
+		m_spatial.update(info.m_node->getSceneGraph().getOctree());
 	}
 	}
+
+	return Error::kNone;
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 27 - 38
AnKi/Scene/Components/GlobalIlluminationProbeComponent.h

@@ -6,6 +6,8 @@
 #pragma once
 #pragma once
 
 
 #include <AnKi/Scene/Components/SceneComponent.h>
 #include <AnKi/Scene/Components/SceneComponent.h>
+#include <AnKi/Scene/Frustum.h>
+#include <AnKi/Scene/Spatial.h>
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Collision/Aabb.h>
 #include <AnKi/Collision/Aabb.h>
 
 
@@ -27,19 +29,14 @@ public:
 	/// Set the bounding box size.
 	/// Set the bounding box size.
 	void setBoxVolumeSize(const Vec3& sizeXYZ)
 	void setBoxVolumeSize(const Vec3& sizeXYZ)
 	{
 	{
-		m_halfBoxSize = sizeXYZ / 2.0f;
+		m_halfSize = sizeXYZ / 2.0f;
 		updateMembers();
 		updateMembers();
 		m_shapeDirty = true;
 		m_shapeDirty = true;
 	}
 	}
 
 
 	Vec3 getBoxVolumeSize() const
 	Vec3 getBoxVolumeSize() const
 	{
 	{
-		return m_halfBoxSize * 2.0f;
-	}
-
-	Aabb getAabbWorldSpace() const
-	{
-		return Aabb(-m_halfBoxSize + m_worldPosition, m_halfBoxSize + m_worldPosition);
+		return m_halfSize * 2.0f;
 	}
 	}
 
 
 	/// Set the cell size in meters.
 	/// Set the cell size in meters.
@@ -75,50 +72,47 @@ public:
 	/// Get the cell position that will be rendered this frame.
 	/// Get the cell position that will be rendered this frame.
 	Vec3 getRenderPosition() const
 	Vec3 getRenderPosition() const
 	{
 	{
-		ANKI_ASSERT(m_renderPosition > -m_halfBoxSize + m_worldPosition
-					&& m_renderPosition < m_halfBoxSize + m_worldPosition);
+		ANKI_ASSERT(m_renderPosition > -m_halfSize + m_worldPos && m_renderPosition < m_halfSize + m_worldPos);
 		ANKI_ASSERT(m_markedForRendering);
 		ANKI_ASSERT(m_markedForRendering);
 		return m_renderPosition;
 		return m_renderPosition;
 	}
 	}
 
 
+	WeakArray<Frustum> getFrustums()
+	{
+		return m_frustums;
+	}
+
 	void setupGlobalIlluminationProbeQueueElement(GlobalIlluminationProbeQueueElement& el)
 	void setupGlobalIlluminationProbeQueueElement(GlobalIlluminationProbeQueueElement& el)
 	{
 	{
 		el.m_uuid = m_uuid;
 		el.m_uuid = m_uuid;
 		el.m_feedbackCallback = giProbeQueueElementFeedbackCallback;
 		el.m_feedbackCallback = giProbeQueueElementFeedbackCallback;
 		el.m_feedbackCallbackUserData = this;
 		el.m_feedbackCallbackUserData = this;
-		el.m_debugDrawCallback = [](RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) {
-			ANKI_ASSERT(userData.getSize() == 1);
-			static_cast<const GlobalIlluminationProbeComponent*>(userData[0])->draw(ctx);
-		};
+		el.m_debugDrawCallback = nullptr;
 		el.m_debugDrawCallbackUserData = this;
 		el.m_debugDrawCallbackUserData = this;
 		el.m_renderQueues = {};
 		el.m_renderQueues = {};
-		el.m_aabbMin = -m_halfBoxSize + m_worldPosition;
-		el.m_aabbMax = m_halfBoxSize + m_worldPosition;
+		el.m_aabbMin = -m_halfSize + m_worldPos;
+		el.m_aabbMax = m_halfSize + m_worldPos;
 		el.m_cellCounts = m_cellCounts;
 		el.m_cellCounts = m_cellCounts;
 		el.m_totalCellCount = m_cellCounts.x() * m_cellCounts.y() * m_cellCounts.z();
 		el.m_totalCellCount = m_cellCounts.x() * m_cellCounts.y() * m_cellCounts.z();
-		el.m_cellSizes = (m_halfBoxSize * 2.0f) / Vec3(m_cellCounts);
+		el.m_cellSizes = (m_halfSize * 2.0f) / Vec3(m_cellCounts);
 		el.m_fadeDistance = m_fadeDistance;
 		el.m_fadeDistance = m_fadeDistance;
 	}
 	}
 
 
-	void setWorldPosition(const Vec3& pos)
-	{
-		m_worldPosition = pos;
-		m_shapeDirty = true;
-	}
-
 private:
 private:
-	SceneNode* m_node;
 	U64 m_uuid;
 	U64 m_uuid;
-	Vec3 m_halfBoxSize = Vec3(0.5f);
-	Vec3 m_worldPosition = Vec3(0.0f);
+	Vec3 m_halfSize = Vec3(0.5f);
+	Vec3 m_worldPos = Vec3(0.0f);
 	Vec3 m_renderPosition = Vec3(0.0f);
 	Vec3 m_renderPosition = Vec3(0.0f);
 	UVec3 m_cellCounts = UVec3(2u);
 	UVec3 m_cellCounts = UVec3(2u);
 	F32 m_cellSize = 4.0f; ///< Cell size in meters.
 	F32 m_cellSize = 4.0f; ///< Cell size in meters.
 	F32 m_fadeDistance = 0.2f;
 	F32 m_fadeDistance = 0.2f;
-	Bool m_markedForRendering : 1;
-	Bool m_shapeDirty : 1;
 
 
-	ImageResourcePtr m_debugImage;
+	Array<Frustum, 6> m_frustums;
+
+	Spatial m_spatial;
+
+	Bool m_markedForRendering : 1 = false;
+	Bool m_shapeDirty : 1 = true;
 
 
 	static void giProbeQueueElementFeedbackCallback(Bool fillRenderQueuesOnNextFrame, void* userData,
 	static void giProbeQueueElementFeedbackCallback(Bool fillRenderQueuesOnNextFrame, void* userData,
 													const Vec4& eyeWorldPosition)
 													const Vec4& eyeWorldPosition)
@@ -126,8 +120,8 @@ private:
 		ANKI_ASSERT(userData);
 		ANKI_ASSERT(userData);
 		GlobalIlluminationProbeComponent& self = *static_cast<GlobalIlluminationProbeComponent*>(userData);
 		GlobalIlluminationProbeComponent& self = *static_cast<GlobalIlluminationProbeComponent*>(userData);
 		ANKI_ASSERT(!(fillRenderQueuesOnNextFrame
 		ANKI_ASSERT(!(fillRenderQueuesOnNextFrame
-					  && (eyeWorldPosition.xyz() < -self.m_halfBoxSize + self.m_worldPosition
-						  || eyeWorldPosition.xyz() > self.m_halfBoxSize + self.m_worldPosition)));
+					  && (eyeWorldPosition.xyz() < -self.m_halfSize + self.m_worldPos
+						  || eyeWorldPosition.xyz() > self.m_halfSize + self.m_worldPos)));
 		self.m_markedForRendering = fillRenderQueuesOnNextFrame;
 		self.m_markedForRendering = fillRenderQueuesOnNextFrame;
 		self.m_renderPosition = eyeWorldPosition.xyz();
 		self.m_renderPosition = eyeWorldPosition.xyz();
 	}
 	}
@@ -135,19 +129,14 @@ private:
 	/// Recalc come values.
 	/// Recalc come values.
 	void updateMembers()
 	void updateMembers()
 	{
 	{
-		const Vec3 dist = m_halfBoxSize * 2.0f;
+		const Vec3 dist = m_halfSize * 2.0f;
 		m_cellCounts = UVec3(dist / m_cellSize);
 		m_cellCounts = UVec3(dist / m_cellSize);
 		m_cellCounts = m_cellCounts.max(UVec3(1));
 		m_cellCounts = m_cellCounts.max(UVec3(1));
 	}
 	}
 
 
-	void draw(RenderQueueDrawContext& ctx) const;
+	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 
 
-	Error update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = m_shapeDirty;
-		m_shapeDirty = false;
-		return Error::kNone;
-	}
+	void onDestroy(SceneNode& node);
 };
 };
 /// @}
 /// @}
 
 

+ 0 - 290
AnKi/Scene/Components/GpuParticleEmitterComponent.cpp

@@ -1,290 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/Components/GpuParticleEmitterComponent.h>
-#include <AnKi/Scene/SceneNode.h>
-#include <AnKi/Scene/SceneGraph.h>
-#include <AnKi/Resource/ResourceManager.h>
-#include <AnKi/Shaders/Include/ParticleTypes.h>
-
-namespace anki {
-
-GpuParticleEmitterComponent::GpuParticleEmitterComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
-	, m_node(node)
-{
-}
-
-GpuParticleEmitterComponent::~GpuParticleEmitterComponent()
-{
-}
-
-Error GpuParticleEmitterComponent::loadParticleEmitterResource(CString filename)
-{
-	m_markedForUpdate = true;
-
-	// Create the debug drawer
-	if(!m_dbgImage.isCreated())
-	{
-		ANKI_CHECK(getExternalSubsystems(*m_node).m_resourceManager->loadResource(
-			"EngineAssets/ParticleEmitter.ankitex", m_dbgImage));
-	}
-
-	// Load particle props
-	ANKI_CHECK(getExternalSubsystems(*m_node).m_resourceManager->loadResource(filename, m_particleEmitterResource));
-	const ParticleEmitterProperties& inProps = m_particleEmitterResource->getProperties();
-	m_maxParticleCount = inProps.m_maxNumOfParticles;
-
-	// Create program
-	ANKI_CHECK(getExternalSubsystems(*m_node).m_resourceManager->loadResource(
-		"ShaderBinaries/GpuParticlesSimulation.ankiprogbin", m_prog));
-	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(variant);
-	m_grProg = variant->getProgram();
-	m_workgroupSizeX = variant->getWorkgroupSizes()[0];
-
-	// Create a UBO with the props
-	{
-		BufferInitInfo buffInit("GpuParticlesProps");
-		buffInit.m_mapAccess = BufferMapAccessBit::kWrite;
-		buffInit.m_usage = BufferUsageBit::kUniformCompute;
-		buffInit.m_size = sizeof(GpuParticleEmitterProperties);
-		m_propsBuff = getExternalSubsystems(*m_node).m_grManager->newBuffer(buffInit);
-		GpuParticleEmitterProperties* props =
-			static_cast<GpuParticleEmitterProperties*>(m_propsBuff->map(0, kMaxPtrSize, BufferMapAccessBit::kWrite));
-
-		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->flush(0, kMaxPtrSize);
-		m_propsBuff->unmap();
-	}
-
-	// Create the particle buffer
-	{
-		BufferInitInfo buffInit("GpuParticles");
-		buffInit.m_mapAccess = BufferMapAccessBit::kWrite;
-		buffInit.m_usage = BufferUsageBit::kAllStorage;
-		buffInit.m_size = sizeof(GpuParticle) * m_maxParticleCount;
-		m_particlesBuff = getExternalSubsystems(*m_node).m_grManager->newBuffer(buffInit);
-
-		GpuParticle* particle =
-			static_cast<GpuParticle*>(m_particlesBuff->map(0, kMaxPtrSize, BufferMapAccessBit::kWrite));
-		const GpuParticle* end = particle + m_maxParticleCount;
-		for(; particle < end; ++particle)
-		{
-			particle->m_life = -1.0f; // Force GPU to init the particle
-		}
-
-		m_particlesBuff->flush(0, kMaxPtrSize);
-		m_particlesBuff->unmap();
-	}
-
-	// Create the rand buffer
-	{
-		BufferInitInfo buffInit("GpuParticlesRand");
-		buffInit.m_mapAccess = BufferMapAccessBit::kWrite;
-		buffInit.m_usage = BufferUsageBit::kAllStorage;
-		buffInit.m_size = sizeof(U32) + kMaxRandFactors * sizeof(F32);
-		m_randFactorsBuff = getExternalSubsystems(*m_node).m_grManager->newBuffer(buffInit);
-
-		F32* randFactors = static_cast<F32*>(m_randFactorsBuff->map(0, kMaxPtrSize, BufferMapAccessBit::kWrite));
-
-		*reinterpret_cast<U32*>(randFactors) = kMaxRandFactors;
-		++randFactors;
-
-		const F32* randFactorsEnd = randFactors + kMaxRandFactors;
-		for(; randFactors < randFactorsEnd; ++randFactors)
-		{
-			*randFactors = getRandomRange(0.0f, 1.0f);
-		}
-
-		m_randFactorsBuff->flush(0, kMaxPtrSize);
-		m_randFactorsBuff->unmap();
-	}
-
-	// Create the sampler
-	{
-		SamplerInitInfo sinit("GpuParticles");
-		sinit.m_addressing = SamplingAddressing::kClamp;
-		m_nearestAnyClampSampler = getExternalSubsystems(*m_node).m_grManager->newSampler(sinit);
-	}
-
-	// Find the extend of the particles
-	if(inProps.m_emitterBoundingVolumeMin.x() >= inProps.m_emitterBoundingVolumeMax.x())
-	{
-		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 maxLife = F32(inProps.m_particle.m_maxLife);
-
-		const F32 dt = 1.0f / 30.0f;
-		const Vec3 initialForce = maxGravity * maxMass + maxForce;
-		const Vec3 initialAcceleration = initialForce / maxMass;
-		const Vec3 velocity = initialAcceleration * dt;
-
-		F32 life = maxLife;
-		Vec3 pos(0.0f);
-		while(life > dt)
-		{
-			pos = maxGravity * (dt * dt) + velocity * dt + pos;
-			life -= dt;
-		}
-
-		m_emitterBoundingBoxLocal = Aabb(-Vec3(pos.getLength()), Vec3(pos.getLength()));
-	}
-	else
-	{
-		m_emitterBoundingBoxLocal = Aabb(inProps.m_emitterBoundingVolumeMin, inProps.m_emitterBoundingVolumeMax);
-	}
-
-	return Error::kNone;
-}
-
-void GpuParticleEmitterComponent::simulate(GenericGpuComputeJobQueueElementContext& ctx) const
-{
-	if(ANKI_UNLIKELY(!m_particleEmitterResource.isCreated()))
-	{
-		return;
-	}
-
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	cmdb->bindShaderProgram(m_grProg);
-
-	// Bind resources
-	cmdb->bindStorageBuffer(1, 0, m_particlesBuff, 0, kMaxPtrSize);
-	cmdb->bindUniformBuffer(1, 1, m_propsBuff, 0, kMaxPtrSize);
-	cmdb->bindStorageBuffer(1, 2, m_randFactorsBuff, 0, kMaxPtrSize);
-	cmdb->bindSampler(1, 3, m_nearestAnyClampSampler);
-
-	RebarGpuMemoryToken token;
-	GpuParticleSimulationState* unis = static_cast<GpuParticleSimulationState*>(
-		ctx.m_rebarStagingPool->allocateFrame(sizeof(GpuParticleSimulationState), 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, ctx.m_rebarStagingPool->getBuffer(), token.m_offset, token.m_range);
-
-	// Dispatch
-	const U32 workgroupCount = (m_maxParticleCount + m_workgroupSizeX - 1) / m_workgroupSizeX;
-	cmdb->dispatchCompute(workgroupCount, 1, 1);
-}
-
-void GpuParticleEmitterComponent::draw(RenderQueueDrawContext& ctx) const
-{
-	// TODO
-#if 0
-	if(ANKI_UNLIKELY(!m_particleEmitterResource.isCreated()))
-	{
-		return;
-	}
-
-	CommandBufferPtr& cmdb = ctx.m_commandBuffer;
-
-	if(!ctx.m_debugDraw)
-	{
-		// Program
-		ShaderProgramPtr prog;
-		m_particleEmitterResource->getRenderingInfo(ctx.m_key, prog);
-		cmdb->bindShaderProgram(prog);
-
-		// Resources
-		static const Array<Mat3x4, 1> identity = {Mat3x4::getIdentity()};
-
-		RenderComponent::allocateAndSetupUniforms(m_particleEmitterResource->getMaterial(), ctx, identity, identity,
-												  *ctx.m_rebarStagingPool);
-
-		cmdb->bindStorageBuffer(kMaterialSetLocal, kMaterialBindingFirstNonStandardLocal, m_particlesBuff, 0,
-								kMaxPtrSize);
-
-		// Draw
-		cmdb->setLineWidth(8.0f);
-		cmdb->drawArrays(PrimitiveTopology::kLines, m_maxParticleCount * 2);
-	}
-	else
-	{
-		const Vec4 tsl = (m_worldAabb.getMin() + m_worldAabb.getMax()) / 2.0f;
-		const Vec4 scale = (m_worldAabb.getMax() - m_worldAabb.getMin()) / 2.0f;
-
-		// Set non uniform scale. Add a margin to avoid flickering
-		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.xyz1(), Mat3::getIdentity() * nonUniScale, 1.0f);
-
-		const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::kDepthTestOn);
-		if(enableDepthTest)
-		{
-			cmdb->setDepthCompareOperation(CompareOperation::kLess);
-		}
-		else
-		{
-			cmdb->setDepthCompareOperation(CompareOperation::kAlways);
-		}
-
-		m_node->getSceneGraph().getDebugDrawer().drawCubes(
-			ConstWeakArray<Mat4>(&mvp, 1), Vec4(1.0f, 0.0f, 1.0f, 1.0f), 2.0f,
-			ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::kDitheredDepthTestOn), 2.0f, *ctx.m_rebarStagingPool,
-			cmdb);
-
-		m_node->getSceneGraph().getDebugDrawer().drawBillboardTextures(
-			ctx.m_projectionMatrix, ctx.m_viewMatrix, ConstWeakArray<Vec3>(&m_worldPosition, 1), Vec4(1.0f),
-			ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::kDitheredDepthTestOn), m_dbgImage->getTextureView(),
-			ctx.m_sampler, Vec2(0.75f), *ctx.m_rebarStagingPool, ctx.m_commandBuffer);
-
-		// Restore state
-		if(!enableDepthTest)
-		{
-			cmdb->setDepthCompareOperation(CompareOperation::kLess);
-		}
-	}
-#endif
-}
-
-Error GpuParticleEmitterComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
-{
-	if(ANKI_UNLIKELY(!m_particleEmitterResource.isCreated()))
-	{
-		updated = false;
-		return Error::kNone;
-	}
-
-	updated = m_markedForUpdate;
-	m_markedForUpdate = false;
-
-	m_dt = info.m_dt;
-
-	return Error::kNone;
-}
-
-void GpuParticleEmitterComponent::setWorldTransform(const Transform& trf)
-{
-	m_worldPosition = trf.getOrigin().xyz();
-	m_worldRotation = trf.getRotation();
-
-	m_worldAabb.setMin(m_worldPosition + m_emitterBoundingBoxLocal.getMin().xyz());
-	m_worldAabb.setMax(m_worldPosition + m_emitterBoundingBoxLocal.getMax().xyz());
-
-	m_markedForUpdate = true;
-}
-
-} // end namespace anki

+ 0 - 94
AnKi/Scene/Components/GpuParticleEmitterComponent.h

@@ -1,94 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <AnKi/Scene/Components/SceneComponent.h>
-#include <AnKi/Collision/Aabb.h>
-#include <AnKi/Renderer/RenderQueue.h>
-#include <AnKi/Resource/ParticleEmitterResource.h>
-
-namespace anki {
-
-/// @addtogroup scene
-/// @{
-
-/// GPU particle emitter.
-class GpuParticleEmitterComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(GpuParticleEmitterComponent)
-
-public:
-	GpuParticleEmitterComponent(SceneNode* node);
-
-	~GpuParticleEmitterComponent();
-
-	Error loadParticleEmitterResource(CString filename);
-
-	void setWorldTransform(const Transform& trf);
-
-	ParticleEmitterResourcePtr getParticleEmitterResource() const
-	{
-		return m_particleEmitterResource;
-	}
-
-	/// Callback that will be used by the GenericGpuComputeJobComponent
-	static void simulateCallback(GenericGpuComputeJobQueueElementContext& ctx, const void* userData)
-	{
-		ANKI_ASSERT(userData);
-		static_cast<const GpuParticleEmitterComponent*>(userData)->simulate(ctx);
-	}
-
-	/// Callback that will be used by the RenderComponent
-	static void drawCallback(RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData)
-	{
-		ANKI_ASSERT(userData.getSize() == 1);
-		static_cast<const GpuParticleEmitterComponent*>(userData[0])->draw(ctx);
-	}
-
-	const Aabb& getBoundingVolumeWorldSpace() const
-	{
-		return m_worldAabb;
-	}
-
-	Bool isEnabled() const
-	{
-		return m_particleEmitterResource.isCreated();
-	}
-
-private:
-	static constexpr U32 kMaxRandFactors = 32;
-
-	SceneNode* m_node;
-	ShaderProgramResourcePtr m_prog;
-	ShaderProgramPtr m_grProg;
-	U32 m_workgroupSizeX = 0;
-
-	ParticleEmitterResourcePtr m_particleEmitterResource;
-
-	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_emitterBoundingBoxLocal = Aabb(Vec3(0.0f), Vec3(1.0f));
-	U32 m_maxParticleCount = 0;
-	Second m_dt = 0.0;
-	Vec3 m_worldPosition = Vec3(0.0f); //< Cache it.
-	Mat3x4 m_worldRotation = Mat3x4::getIdentity();
-	Aabb m_worldAabb = Aabb(Vec3(0.0f), Vec3(1.0f));
-
-	ImageResourcePtr m_dbgImage;
-
-	Bool m_markedForUpdate = true;
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
-	void simulate(GenericGpuComputeJobQueueElementContext& ctx) const;
-	void draw(RenderQueueDrawContext& ctx) const;
-};
-/// @}
-
-} // end namespace anki

+ 36 - 12
AnKi/Scene/Components/JointComponent.cpp

@@ -19,17 +19,16 @@ public:
 
 
 JointComponent::~JointComponent()
 JointComponent::~JointComponent()
 {
 {
-	removeAllJoints();
 }
 }
 
 
-void JointComponent::removeAllJoints()
+void JointComponent::onDestroy(SceneNode& node)
 {
 {
 	while(!m_jointList.isEmpty())
 	while(!m_jointList.isEmpty())
 	{
 	{
-		JointNode* node = &m_jointList.getFront();
+		JointNode* jnode = &m_jointList.getFront();
 		m_jointList.popFront();
 		m_jointList.popFront();
 
 
-		deleteInstance(m_node->getMemoryPool(), node);
+		deleteInstance(node.getMemoryPool(), jnode);
 	}
 	}
 }
 }
 
 
@@ -57,7 +56,7 @@ Vec3 JointComponent::computeLocalPivotFromFactors(const PhysicsBodyPtr& body, co
 template<typename TJoint, typename... TArgs>
 template<typename TJoint, typename... TArgs>
 void JointComponent::newJoint(const Vec3& relPosFactor, F32 breakingImpulse, TArgs&&... args)
 void JointComponent::newJoint(const Vec3& relPosFactor, F32 breakingImpulse, TArgs&&... args)
 {
 {
-	BodyComponent* bodyc = m_node->tryGetFirstComponentOfType<BodyComponent>();
+	BodyComponent* bodyc = m_bodyc;
 
 
 	if(bodyc)
 	if(bodyc)
 	{
 	{
@@ -84,7 +83,7 @@ void JointComponent::newPoint2PointJoint(const Vec3& relPosFactor, F32 breakingI
 
 
 void JointComponent::newPoint2PointJoint2(const Vec3& relPosFactorA, const Vec3& relPosFactorB, F32 breakingImpulse)
 void JointComponent::newPoint2PointJoint2(const Vec3& relPosFactorA, const Vec3& relPosFactorB, F32 breakingImpulse)
 {
 {
-	BodyComponent* bodycA = m_node->tryGetFirstComponentOfType<BodyComponent>();
+	BodyComponent* bodycA = m_bodyc;
 	BodyComponent* bodycB = nullptr;
 	BodyComponent* bodycB = nullptr;
 	if(m_node->getParent())
 	if(m_node->getParent())
 	{
 	{
@@ -118,18 +117,16 @@ void JointComponent::newHingeJoint(const Vec3& relPosFactor, const Vec3& axis, F
 
 
 Error JointComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 Error JointComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 {
-	ANKI_ASSERT(info.m_node == m_node);
-
 	// Iterate the joints and check if the connected scene node is not the parent of this node anymore.
 	// Iterate the joints and check if the connected scene node is not the parent of this node anymore.
 	while(true)
 	while(true)
 	{
 	{
 		Bool erasedOne = false;
 		Bool erasedOne = false;
-		for(JointNode& otherNode : m_jointList)
+		for(JointNode& joint : m_jointList)
 		{
 		{
-			if(otherNode.m_parentNode != info.m_node->getParent() || otherNode.m_joint->isBroken())
+			if(joint.m_parentNode != info.m_node->getParent() || joint.m_joint->isBroken())
 			{
 			{
-				m_jointList.erase(&otherNode);
-				deleteInstance(info.m_node->getMemoryPool(), &otherNode);
+				m_jointList.erase(&joint);
+				deleteInstance(info.m_node->getMemoryPool(), &joint);
 				erasedOne = true;
 				erasedOne = true;
 				updated = true;
 				updated = true;
 				break;
 				break;
@@ -145,4 +142,31 @@ Error JointComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 	return Error::kNone;
 	return Error::kNone;
 }
 }
 
 
+void JointComponent::onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added)
+{
+	if(other->getClassId() != BodyComponent::getStaticClassId())
+	{
+		return;
+	}
+
+	BodyComponent* bodyc = static_cast<BodyComponent*>(other);
+
+	Bool jointListInvalid = false;
+	if(added && m_bodyc == nullptr)
+	{
+		m_bodyc = bodyc;
+		jointListInvalid = true;
+	}
+	else if(bodyc == m_bodyc)
+	{
+		m_bodyc = nullptr;
+		jointListInvalid = true;
+	}
+
+	if(jointListInvalid)
+	{
+		onDestroy(*m_node);
+	}
+}
+
 } // end namespace anki
 } // end namespace anki

+ 9 - 6
AnKi/Scene/Components/JointComponent.h

@@ -23,7 +23,6 @@ public:
 		: SceneComponent(node, getStaticClassId())
 		: SceneComponent(node, getStaticClassId())
 		, m_node(node)
 		, m_node(node)
 	{
 	{
-		ANKI_ASSERT(node);
 	}
 	}
 
 
 	~JointComponent();
 	~JointComponent();
@@ -37,22 +36,26 @@ public:
 	/// Create a hinge joint on the BodyComponent of the SceneNode.
 	/// Create a hinge joint on the BodyComponent of the SceneNode.
 	void newHingeJoint(const Vec3& relPosFactor, const Vec3& axis, F32 brakingImpulse = kMaxF32);
 	void newHingeJoint(const Vec3& relPosFactor, const Vec3& axis, F32 brakingImpulse = kMaxF32);
 
 
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
-
 private:
 private:
 	class JointNode;
 	class JointNode;
 
 
-	SceneNode* m_node;
+	SceneNode* m_node = nullptr;
+	BodyComponent* m_bodyc = nullptr;
+
 	IntrusiveList<JointNode> m_jointList;
 	IntrusiveList<JointNode> m_jointList;
 
 
 	/// Given a 3 coodrinates that lie in [-1.0, +1.0] compute a pivot point that lies into the AABB of the collision
 	/// Given a 3 coodrinates that lie in [-1.0, +1.0] compute a pivot point that lies into the AABB of the collision
 	/// shape of the body.
 	/// shape of the body.
 	static Vec3 computeLocalPivotFromFactors(const PhysicsBodyPtr& body, const Vec3& factors);
 	static Vec3 computeLocalPivotFromFactors(const PhysicsBodyPtr& body, const Vec3& factors);
 
 
-	void removeAllJoints();
-
 	template<typename TJoint, typename... TArgs>
 	template<typename TJoint, typename... TArgs>
 	void newJoint(const Vec3& relPosFactor, F32 brakingImpulse, TArgs&&... args);
 	void newJoint(const Vec3& relPosFactor, F32 brakingImpulse, TArgs&&... args);
+
+	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+
+	void onDestroy(SceneNode& node);
+
+	void onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added);
 };
 };
 /// @}
 /// @}
 
 

+ 35 - 3
AnKi/Scene/Components/LensFlareComponent.cpp

@@ -13,17 +13,49 @@ namespace anki {
 LensFlareComponent::LensFlareComponent(SceneNode* node)
 LensFlareComponent::LensFlareComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
 	: SceneComponent(node, getStaticClassId())
 	, m_node(node)
 	, m_node(node)
+	, m_spatial(this)
 {
 {
-	ANKI_ASSERT(node);
 }
 }
 
 
 LensFlareComponent::~LensFlareComponent()
 LensFlareComponent::~LensFlareComponent()
 {
 {
+	m_spatial.removeFromOctree(m_node->getSceneGraph().getOctree());
 }
 }
 
 
-Error LensFlareComponent::loadImageResource(CString filename)
+void LensFlareComponent::loadImageResource(CString filename)
 {
 {
-	return getExternalSubsystems(*m_node).m_resourceManager->loadResource(filename, m_image);
+	ImageResourcePtr image;
+	const Error err = getExternalSubsystems(*m_node).m_resourceManager->loadResource(filename, image);
+	if(err)
+	{
+		ANKI_SCENE_LOGE("Failed to load lens flare image");
+		return;
+	}
+
+	m_image = std::move(image);
+}
+
+Error LensFlareComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+{
+	updated = m_dirty || info.m_node->movedThisFrame();
+
+	if(updated)
+	{
+		m_dirty = false;
+
+		m_worldPosition = info.m_node->getWorldTransform().getOrigin().xyz();
+
+		const Aabb aabb(m_worldPosition - (kAabbSize / 2.0f), m_worldPosition + (kAabbSize / 2.0f));
+		m_spatial.setBoundingShape(aabb);
+		m_spatial.update(info.m_node->getSceneGraph().getOctree());
+	}
+
+	return Error::kNone;
+}
+
+void LensFlareComponent::onDestroy(SceneNode& node)
+{
+	m_spatial.removeFromOctree(node.getSceneGraph().getOctree());
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 13 - 24
AnKi/Scene/Components/LensFlareComponent.h

@@ -6,7 +6,7 @@
 #pragma once
 #pragma once
 
 
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneNode.h>
-#include <AnKi/Gr.h>
+#include <AnKi/Scene/Spatial.h>
 #include <AnKi/Resource/ImageResource.h>
 #include <AnKi/Resource/ImageResource.h>
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Renderer/RenderQueue.h>
 
 
@@ -25,9 +25,9 @@ public:
 
 
 	~LensFlareComponent();
 	~LensFlareComponent();
 
 
-	Error loadImageResource(CString filename);
+	void loadImageResource(CString filename);
 
 
-	Bool isLoaded() const
+	Bool isEnabled() const
 	{
 	{
 		return m_image.isCreated();
 		return m_image.isCreated();
 	}
 	}
@@ -37,16 +37,6 @@ public:
 		return (m_image) ? m_image->getFilename() : CString();
 		return (m_image) ? m_image->getFilename() : CString();
 	}
 	}
 
 
-	void setWorldPosition(const Vec3& worldPosition)
-	{
-		m_worldPosition = worldPosition;
-	}
-
-	const Vec3& getWorldPosition() const
-	{
-		return m_worldPosition;
-	}
-
 	void setFirstFlareSize(const Vec2& size)
 	void setFirstFlareSize(const Vec2& size)
 	{
 	{
 		m_firstFlareSize = size;
 		m_firstFlareSize = size;
@@ -77,11 +67,6 @@ public:
 		return m_colorMul;
 		return m_colorMul;
 	}
 	}
 
 
-	TexturePtr getTexture() const
-	{
-		return m_image->getTexture();
-	}
-
 	void setupLensFlareQueueElement(LensFlareQueueElement& el) const
 	void setupLensFlareQueueElement(LensFlareQueueElement& el) const
 	{
 	{
 		el.m_worldPosition = m_worldPosition;
 		el.m_worldPosition = m_worldPosition;
@@ -89,25 +74,29 @@ public:
 		el.m_colorMultiplier = m_colorMul;
 		el.m_colorMultiplier = m_colorMul;
 		el.m_textureView = m_image->getTextureView().get();
 		el.m_textureView = m_image->getTextureView().get();
 		el.m_userData = this;
 		el.m_userData = this;
-		el.m_drawCallback = debugDrawCallback;
+		el.m_drawCallback = nullptr;
 	}
 	}
 
 
 private:
 private:
+	static constexpr F32 kAabbSize = 25.0_cm;
+
 	Vec4 m_colorMul = Vec4(1.0f); ///< Color multiplier.
 	Vec4 m_colorMul = Vec4(1.0f); ///< Color multiplier.
 
 
 	SceneNode* m_node;
 	SceneNode* m_node;
 	ImageResourcePtr m_image; ///< Array of textures.
 	ImageResourcePtr m_image; ///< Array of textures.
 
 
+	Spatial m_spatial;
+
 	Vec2 m_firstFlareSize = Vec2(1.0f);
 	Vec2 m_firstFlareSize = Vec2(1.0f);
 	Vec2 m_otherFlareSize = Vec2(1.0f);
 	Vec2 m_otherFlareSize = Vec2(1.0f);
 
 
 	Vec3 m_worldPosition = Vec3(0.0f);
 	Vec3 m_worldPosition = Vec3(0.0f);
 
 
-	static void debugDrawCallback([[maybe_unused]] RenderQueueDrawContext& ctx,
-								  [[maybe_unused]] ConstWeakArray<void*> userData)
-	{
-		// Do nothing
-	}
+	Bool m_dirty = true;
+
+	Error update(SceneComponentUpdateInfo& info, Bool& updated);
+
+	void onDestroy(SceneNode& node);
 };
 };
 /// @}
 /// @}
 
 

+ 149 - 65
AnKi/Scene/Components/LightComponent.cpp

@@ -4,7 +4,8 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <AnKi/Scene/Components/LightComponent.h>
 #include <AnKi/Scene/Components/LightComponent.h>
-#include <AnKi/Scene/Components/FrustumComponent.h>
+#include <AnKi/Scene/SceneNode.h>
+#include <AnKi/Scene/Frustum.h>
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Scene/Octree.h>
 #include <AnKi/Scene/Octree.h>
@@ -17,73 +18,178 @@ namespace anki {
 
 
 LightComponent::LightComponent(SceneNode* node)
 LightComponent::LightComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
 	: SceneComponent(node, getStaticClassId())
-	, m_node(node)
 	, m_uuid(node->getSceneGraph().getNewUuid())
 	, m_uuid(node->getSceneGraph().getNewUuid())
 	, m_type(LightComponentType::kPoint)
 	, m_type(LightComponentType::kPoint)
-	, m_shadow(false)
-	, m_markedForUpdate(true)
+	, m_spatial(this)
 {
 {
 	ANKI_ASSERT(m_uuid > 0);
 	ANKI_ASSERT(m_uuid > 0);
 	m_point.m_radius = 1.0f;
 	m_point.m_radius = 1.0f;
 
 
-	if(getExternalSubsystems(*node).m_resourceManager->loadResource("EngineAssets/LightBulb.ankitex", m_pointDebugImage)
-	   || getExternalSubsystems(*node).m_resourceManager->loadResource("EngineAssets/SpotLight.ankitex",
-																	   m_spotDebugImage))
+	setLightComponentType(LightComponentType::kPoint);
+	m_worldTransform = node->getWorldTransform();
+}
+
+LightComponent::~LightComponent()
+{
+}
+
+void LightComponent::setLightComponentType(LightComponentType type)
+{
+	ANKI_ASSERT(type >= LightComponentType::kFirst && type < LightComponentType::kCount);
+	m_type = type;
+	m_markedForUpdate = true;
+	m_forceFullUpdate = true;
+
+	if(type == LightComponentType::kDirectional)
+	{
+		m_spatial.setAlwaysVisible(true);
+		m_spatial.setUpdatesOctreeBounds(false);
+	}
+	else
 	{
 	{
-		ANKI_SCENE_LOGF("Failed to load resources");
+		m_spatial.setAlwaysVisible(false);
+		m_spatial.setUpdatesOctreeBounds(true);
 	}
 	}
 }
 }
 
 
 Error LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 Error LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 {
-	updated = m_markedForUpdate;
+	const Bool moveUpdated = info.m_node->movedThisFrame() || m_forceFullUpdate;
+	const Bool shapeUpdated = m_markedForUpdate || m_forceFullUpdate;
+	updated = moveUpdated || shapeUpdated;
 	m_markedForUpdate = false;
 	m_markedForUpdate = false;
+	m_forceFullUpdate = false;
+
+	if(moveUpdated)
+	{
+		m_worldTransform = info.m_node->getWorldTransform();
+	}
 
 
-	if(updated && m_type == LightComponentType::kSpot)
+	if(updated && m_type == LightComponentType::kPoint)
 	{
 	{
+		const Sphere sphere(m_worldTransform.getOrigin(), m_point.m_radius);
+		m_spatial.setBoundingShape(sphere);
+		m_spatial.update(info.m_node->getSceneGraph().getOctree());
+
+		if(m_shadow)
+		{
+			if(m_frustums == nullptr || m_frustumCount != 6) [[unlikely]]
+			{
+				// Allocate, initialize and update the frustums, just do everything to avoid bugs
+				deleteArray(info.m_node->getMemoryPool(), m_frustums, m_frustumCount);
+				m_frustums = newArray<Frustum>(info.m_node->getMemoryPool(), 6);
+				m_frustumCount = 6;
+
+				for(U32 i = 0; i < 6; i++)
+				{
+					m_frustums[i].init(FrustumType::kPerspective, &info.m_node->getMemoryPool());
+					m_frustums[i].setPerspective(kClusterObjectFrustumNearPlane, m_point.m_radius, kPi / 2.0f,
+												 kPi / 2.0f);
+					m_frustums[i].setWorldTransform(Transform(m_worldTransform.getOrigin(),
+															  Frustum::getOmnidirectionalFrustumRotations()[i], 1.0f));
+					m_frustums[i].update();
+				}
+			}
 
 
-		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);
+			// Update the frustums
+			for(U32 i = 0; i < 6; i++)
+			{
+				if(shapeUpdated)
+				{
+					m_frustums[i].setFar(m_point.m_radius);
+				}
+
+				if(moveUpdated || shapeUpdated)
+				{
+					m_frustums[i].setWorldTransform(Transform(m_worldTransform.getOrigin(),
+															  Frustum::getOmnidirectionalFrustumRotations()[i], 1.0f));
+				}
+
+				m_frustums[i].update();
+			}
+		}
+	}
+	else if(updated && m_type == LightComponentType::kSpot)
+	{
+		// Update texture matrix
+		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);
 		const Mat4 proj = Mat4::calculatePerspectiveProjectionMatrix(m_spot.m_outerAngle, m_spot.m_outerAngle,
 		const Mat4 proj = Mat4::calculatePerspectiveProjectionMatrix(m_spot.m_outerAngle, m_spot.m_outerAngle,
 																	 kClusterObjectFrustumNearPlane, m_spot.m_distance);
 																	 kClusterObjectFrustumNearPlane, m_spot.m_distance);
-		m_spot.m_textureMat = biasMat4 * proj * Mat4(m_worldtransform.getInverse());
+		m_spot.m_textureMat = biasMat4 * proj * Mat4(m_worldTransform.getInverse());
 
 
+		// Update the spatial
 		Array<Vec4, 4> points;
 		Array<Vec4, 4> points;
 		computeEdgesOfFrustum(m_spot.m_distance, m_spot.m_outerAngle, m_spot.m_outerAngle, &points[0]);
 		computeEdgesOfFrustum(m_spot.m_distance, m_spot.m_outerAngle, m_spot.m_outerAngle, &points[0]);
+		Array<Vec3, 5> worldPoints;
 		for(U32 i = 0; i < 4; ++i)
 		for(U32 i = 0; i < 4; ++i)
 		{
 		{
-			m_spot.m_edgePointsWspace[i] = m_worldtransform.transform(points[i].xyz());
+			m_spot.m_edgePointsWspace[i] = m_worldTransform.transform(points[i].xyz());
+			worldPoints[i] = m_spot.m_edgePointsWspace[i].xyz();
 		}
 		}
-	}
+		worldPoints[4] = m_worldTransform.getOrigin().xyz();
 
 
-	// Update the scene bounds always
-	if(m_type == LightComponentType::kDirectional)
+		m_spatial.setBoundingShape(ConstWeakArray<Vec3>(worldPoints));
+		m_spatial.update(info.m_node->getSceneGraph().getOctree());
+
+		if(m_shadow)
+		{
+			if(m_frustums == nullptr || m_frustumCount != 1) [[unlikely]]
+			{
+				// Allocate, initialize and update the frustums, just do everything to avoid bugs
+				deleteArray(info.m_node->getMemoryPool(), m_frustums, m_frustumCount);
+				m_frustums = newArray<Frustum>(info.m_node->getMemoryPool(), 1);
+				m_frustumCount = 1;
+
+				m_frustums[0].init(FrustumType::kPerspective, &info.m_node->getMemoryPool());
+				m_frustums[0].setPerspective(kClusterObjectFrustumNearPlane, m_spot.m_distance, m_spot.m_outerAngle,
+											 m_spot.m_outerAngle);
+				m_frustums[0].setWorldTransform(m_worldTransform);
+				m_frustums[0].update();
+			}
+
+			// Update the frustum
+			if(shapeUpdated)
+			{
+				m_frustums[0].setFar(m_spot.m_distance);
+				m_frustums[0].setFovX(m_spot.m_outerAngle);
+				m_frustums[0].setFovY(m_spot.m_outerAngle);
+			}
+
+			if(moveUpdated)
+			{
+				m_frustums[0].setWorldTransform(m_worldTransform);
+			}
+
+			m_frustums[0].update();
+		}
+	}
+	else if(m_type == LightComponentType::kDirectional)
 	{
 	{
+		// Update the scene bounds always
 		info.m_node->getSceneGraph().getOctree().getActualSceneBounds(m_dir.m_sceneMin, m_dir.m_sceneMax);
 		info.m_node->getSceneGraph().getOctree().getActualSceneBounds(m_dir.m_sceneMin, m_dir.m_sceneMax);
+		updated = updated || m_spatial.update(info.m_node->getSceneGraph().getOctree());
 	}
 	}
 
 
 	return Error::kNone;
 	return Error::kNone;
 }
 }
 
 
-void LightComponent::setupDirectionalLightQueueElement(const FrustumComponent& frustumComp,
-													   DirectionalLightQueueElement& el,
-													   WeakArray<FrustumComponent> cascadeFrustumComponents) const
+void LightComponent::setupDirectionalLightQueueElement(const Frustum& primaryFrustum, DirectionalLightQueueElement& el,
+													   WeakArray<Frustum> cascadeFrustums) const
 {
 {
 	ANKI_ASSERT(m_type == LightComponentType::kDirectional);
 	ANKI_ASSERT(m_type == LightComponentType::kDirectional);
-	ANKI_ASSERT(cascadeFrustumComponents.getSize() <= kMaxShadowCascades);
+	ANKI_ASSERT(cascadeFrustums.getSize() <= kMaxShadowCascades);
 
 
-	const U32 shadowCascadeCount = cascadeFrustumComponents.getSize();
+	const U32 shadowCascadeCount = cascadeFrustums.getSize();
 
 
-	el.m_drawCallback = [](RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) {
-		ANKI_ASSERT(userData.getSize() == 1);
-		static_cast<const LightComponent*>(userData[0])->draw(ctx);
-	};
+	el.m_drawCallback = nullptr;
 	el.m_drawCallbackUserData = this;
 	el.m_drawCallbackUserData = this;
 	el.m_uuid = m_uuid;
 	el.m_uuid = m_uuid;
 	el.m_diffuseColor = m_diffColor.xyz();
 	el.m_diffuseColor = m_diffColor.xyz();
-	el.m_direction = -m_worldtransform.getRotation().getZAxis().xyz();
+	el.m_direction = -m_worldTransform.getRotation().getZAxis().xyz();
 	for(U32 i = 0; i < shadowCascadeCount; ++i)
 	for(U32 i = 0; i < shadowCascadeCount; ++i)
 	{
 	{
-		el.m_shadowCascadesDistances[i] = frustumComp.getShadowCascadeDistance(i);
+		el.m_shadowCascadesDistances[i] = primaryFrustum.getShadowCascadeDistance(i);
 	}
 	}
 	el.m_shadowCascadeCount = U8(shadowCascadeCount);
 	el.m_shadowCascadeCount = U8(shadowCascadeCount);
 	el.m_shadowLayer = kMaxU8;
 	el.m_shadowLayer = kMaxU8;
@@ -94,12 +200,12 @@ void LightComponent::setupDirectionalLightQueueElement(const FrustumComponent& f
 	}
 	}
 
 
 	// Compute the texture matrices
 	// Compute the texture matrices
-	const Mat4 lightTrf(m_worldtransform);
-	if(frustumComp.getFrustumType() == FrustumType::kPerspective)
+	const Mat4 lightTrf(m_worldTransform);
+	if(primaryFrustum.getFrustumType() == FrustumType::kPerspective)
 	{
 	{
 		// Get some stuff
 		// Get some stuff
-		const F32 fovX = frustumComp.getFovX();
-		const F32 fovY = frustumComp.getFovY();
+		const F32 fovX = primaryFrustum.getFovX();
+		const F32 fovY = primaryFrustum.getFovY();
 
 
 		// Compute a sphere per cascade
 		// Compute a sphere per cascade
 		Array<Sphere, kMaxShadowCascades> boundingSpheres;
 		Array<Sphere, kMaxShadowCascades> boundingSpheres;
@@ -121,9 +227,9 @@ void LightComponent::setupDirectionalLightQueueElement(const FrustumComponent& f
 			// --------------------------> x
 			// --------------------------> x
 			//           |
 			//           |
 			// The square distance of A-C is equal to B-C. Solve the equation to find the z.
 			// The square distance of A-C is equal to B-C. Solve the equation to find the z.
-			const F32 f = frustumComp.getShadowCascadeDistance(i); // Cascade far
+			const F32 f = primaryFrustum.getShadowCascadeDistance(i); // Cascade far
 			const F32 n =
 			const F32 n =
-				(i == 0) ? frustumComp.getNear() : frustumComp.getShadowCascadeDistance(i - 1); // Cascade near
+				(i == 0) ? primaryFrustum.getNear() : primaryFrustum.getShadowCascadeDistance(i - 1); // Cascade near
 			const F32 a = f * tan(fovY / 2.0f) * fovX / fovY;
 			const F32 a = f * tan(fovY / 2.0f) * fovX / fovY;
 			const F32 b = n * tan(fovY / 2.0f) * fovX / fovY;
 			const F32 b = n * tan(fovY / 2.0f) * fovX / fovY;
 			const F32 z = (b * b + n * n - a * a - f * f) / (2.0f * (f - n));
 			const F32 z = (b * b + n * n - a * a - f * f) / (2.0f * (f - n));
@@ -138,7 +244,7 @@ void LightComponent::setupDirectionalLightQueueElement(const FrustumComponent& f
 
 
 			// Set the sphere
 			// Set the sphere
 			boundingSpheres[i].setRadius(r);
 			boundingSpheres[i].setRadius(r);
-			boundingSpheres[i].setCenter(frustumComp.getWorldTransform().transform(C));
+			boundingSpheres[i].setCenter(primaryFrustum.getWorldTransform().transform(C));
 		}
 		}
 
 
 		// Compute the matrices
 		// Compute the matrices
@@ -171,7 +277,7 @@ void LightComponent::setupDirectionalLightQueueElement(const FrustumComponent& f
 				sphereRadius, -sphereRadius, sphereRadius, -sphereRadius, kClusterObjectFrustumNearPlane, far);
 				sphereRadius, -sphereRadius, sphereRadius, -sphereRadius, kClusterObjectFrustumNearPlane, far);
 
 
 			// View
 			// View
-			Transform cascadeTransform = m_worldtransform;
+			Transform cascadeTransform = m_worldTransform;
 			cascadeTransform.setOrigin(eye.xyz0());
 			cascadeTransform.setOrigin(eye.xyz0());
 			const Mat4 cascadeViewMat = Mat4(cascadeTransform.getInverse());
 			const Mat4 cascadeViewMat = Mat4(cascadeTransform.getInverse());
 
 
@@ -197,8 +303,8 @@ void LightComponent::setupDirectionalLightQueueElement(const FrustumComponent& f
 			}
 			}
 
 
 			// Light matrix
 			// Light matrix
-			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);
+			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);
 			el.m_textureMatrices[i] = biasMat4 * cascadeProjMat * cascadeViewMat;
 			el.m_textureMatrices[i] = biasMat4 * cascadeProjMat * cascadeViewMat;
 
 
 			// Fill the frustum with the fixed projection parameters from the fixed projection matrix
 			// Fill the frustum with the fixed projection parameters from the fixed projection matrix
@@ -212,9 +318,10 @@ void LightComponent::setupDirectionalLightQueueElement(const FrustumComponent& f
 			extractClipPlane(cascadeProjMat, FrustumPlaneType::kBottom, plane);
 			extractClipPlane(cascadeProjMat, FrustumPlaneType::kBottom, plane);
 			const F32 bottom = plane.getOffset();
 			const F32 bottom = plane.getOffset();
 
 
-			FrustumComponent& cascadeFrustumComp = cascadeFrustumComponents[i];
-			cascadeFrustumComp.setOrthographic(kClusterObjectFrustumNearPlane, far, right, left, top, bottom);
-			cascadeFrustumComp.setWorldTransform(cascadeTransform);
+			Frustum& cascadeFrustum = cascadeFrustums[i];
+			cascadeFrustum.setOrthographic(kClusterObjectFrustumNearPlane, far, right, left, top, bottom);
+			cascadeFrustum.setWorldTransform(cascadeTransform);
+			cascadeFrustum.update();
 		}
 		}
 	}
 	}
 	else
 	else
@@ -223,32 +330,9 @@ void LightComponent::setupDirectionalLightQueueElement(const FrustumComponent& f
 	}
 	}
 }
 }
 
 
-void LightComponent::draw(RenderQueueDrawContext& ctx) const
+void LightComponent::onDestroyReal(SceneNode& node)
 {
 {
-	const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::kDepthTestOn);
-	if(enableDepthTest)
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::kLess);
-	}
-	else
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::kAlways);
-	}
-
-	Vec3 color = m_diffColor.xyz();
-	color /= max(max(color.x(), color.y()), color.z());
-
-	ImageResourcePtr imageResource = (m_type == LightComponentType::kPoint) ? m_pointDebugImage : m_spotDebugImage;
-	m_node->getSceneGraph().getDebugDrawer().drawBillboardTexture(
-		ctx.m_projectionMatrix, ctx.m_viewMatrix, m_worldtransform.getOrigin().xyz(), color.xyz1(),
-		ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::kDitheredDepthTestOn), imageResource->getTextureView(),
-		ctx.m_sampler, Vec2(0.75f), *ctx.m_rebarStagingPool, ctx.m_commandBuffer);
-
-	// Restore state
-	if(!enableDepthTest)
-	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::kLess);
-	}
+	deleteArray(node.getMemoryPool(), m_frustums, m_frustumCount);
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 31 - 43
AnKi/Scene/Components/LightComponent.h

@@ -5,12 +5,16 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <AnKi/Scene/Components/SceneComponent.h>
+#include <AnKi/Scene/Spatial.h>
 #include <AnKi/Math.h>
 #include <AnKi/Math.h>
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Renderer/RenderQueue.h>
-#include <AnKi/Scene/Components/SceneComponent.h>
 
 
 namespace anki {
 namespace anki {
 
 
+// Forward
+class Frustum;
+
 /// @addtogroup scene
 /// @addtogroup scene
 /// @{
 /// @{
 
 
@@ -32,33 +36,15 @@ class LightComponent : public SceneComponent
 public:
 public:
 	LightComponent(SceneNode* node);
 	LightComponent(SceneNode* node);
 
 
-	~LightComponent()
-	{
-	}
+	~LightComponent();
 
 
-	void setLightComponentType(LightComponentType type)
-	{
-		ANKI_ASSERT(type >= LightComponentType::kFirst && type < LightComponentType::kCount);
-		m_type = type;
-		m_markedForUpdate = true;
-	}
+	void setLightComponentType(LightComponentType type);
 
 
 	LightComponentType getLightComponentType() const
 	LightComponentType getLightComponentType() const
 	{
 	{
 		return m_type;
 		return m_type;
 	}
 	}
 
 
-	void setWorldTransform(const Transform& trf)
-	{
-		m_worldtransform = trf;
-		m_markedForUpdate = true;
-	}
-
-	const Transform& getWorldTransform() const
-	{
-		return m_worldtransform;
-	}
-
 	const Vec4& getDiffuseColor() const
 	const Vec4& getDiffuseColor() const
 	{
 	{
 		return m_diffColor;
 		return m_diffColor;
@@ -133,19 +119,23 @@ public:
 	void setShadowEnabled(const Bool x)
 	void setShadowEnabled(const Bool x)
 	{
 	{
 		m_shadow = x;
 		m_shadow = x;
+		m_markedForUpdate = true;
+	}
+
+	ANKI_INTERNAL WeakArray<Frustum> getFrustums() const
+	{
+		ANKI_ASSERT(m_shadow);
+		return WeakArray<Frustum>(m_frustums, m_frustumCount);
 	}
 	}
 
 
 	void setupPointLightQueueElement(PointLightQueueElement& el) const
 	void setupPointLightQueueElement(PointLightQueueElement& el) const
 	{
 	{
 		ANKI_ASSERT(m_type == LightComponentType::kPoint);
 		ANKI_ASSERT(m_type == LightComponentType::kPoint);
 		el.m_uuid = m_uuid;
 		el.m_uuid = m_uuid;
-		el.m_worldPosition = m_worldtransform.getOrigin().xyz();
+		el.m_worldPosition = m_worldTransform.getOrigin().xyz();
 		el.m_radius = m_point.m_radius;
 		el.m_radius = m_point.m_radius;
 		el.m_diffuseColor = m_diffColor.xyz();
 		el.m_diffuseColor = m_diffColor.xyz();
-		el.m_debugDrawCallback = [](RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) {
-			ANKI_ASSERT(userData.getSize() == 1);
-			static_cast<const LightComponent*>(userData[0])->draw(ctx);
-		};
+		el.m_debugDrawCallback = nullptr;
 		el.m_debugDrawCallbackUserData = this;
 		el.m_debugDrawCallbackUserData = this;
 		el.m_shadowLayer = kMaxU8;
 		el.m_shadowLayer = kMaxU8;
 	}
 	}
@@ -154,34 +144,29 @@ public:
 	{
 	{
 		ANKI_ASSERT(m_type == LightComponentType::kSpot);
 		ANKI_ASSERT(m_type == LightComponentType::kSpot);
 		el.m_uuid = m_uuid;
 		el.m_uuid = m_uuid;
-		el.m_worldTransform = Mat4(m_worldtransform);
+		el.m_worldTransform = Mat4(m_worldTransform);
 		el.m_textureMatrix = m_spot.m_textureMat;
 		el.m_textureMatrix = m_spot.m_textureMat;
 		el.m_distance = m_spot.m_distance;
 		el.m_distance = m_spot.m_distance;
 		el.m_outerAngle = m_spot.m_outerAngle;
 		el.m_outerAngle = m_spot.m_outerAngle;
 		el.m_innerAngle = m_spot.m_innerAngle;
 		el.m_innerAngle = m_spot.m_innerAngle;
 		el.m_diffuseColor = m_diffColor.xyz();
 		el.m_diffuseColor = m_diffColor.xyz();
 		el.m_edgePoints = m_spot.m_edgePointsWspace;
 		el.m_edgePoints = m_spot.m_edgePointsWspace;
-		el.m_debugDrawCallback = [](RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) {
-			ANKI_ASSERT(userData.getSize() == 1);
-			static_cast<const LightComponent*>(userData[0])->draw(ctx);
-		};
+		el.m_debugDrawCallback = nullptr;
 		el.m_debugDrawCallbackUserData = this;
 		el.m_debugDrawCallbackUserData = this;
 		el.m_shadowLayer = kMaxU8;
 		el.m_shadowLayer = kMaxU8;
 	}
 	}
 
 
 	/// Setup a directional queue element.
 	/// Setup a directional queue element.
-	/// @param[in] frustumComp The frustum that is looking that directional light. Used to calculate the cascades.
+	/// @param[in] cameraFrustum The frustum that is looking that directional light. Used to calculate the cascades.
 	/// @param[out] el The queue element to fill out.
 	/// @param[out] el The queue element to fill out.
-	/// @param[out] cascadeFrustumComponents Fill those frustums as well. The size of this array is the count of the
-	///             cascades.
-	void setupDirectionalLightQueueElement(const FrustumComponent& frustumComp, DirectionalLightQueueElement& el,
-										   WeakArray<FrustumComponent> cascadeFrustumComponents) const;
+	/// @param[out] cascadeFrustums Fill those frustums as well. The size of this array is the count of the cascades.
+	void setupDirectionalLightQueueElement(const Frustum& cameraFrustum, DirectionalLightQueueElement& el,
+										   WeakArray<Frustum> cascadeFrustums) const;
 
 
 private:
 private:
-	SceneNode* m_node = nullptr;
 	U64 m_uuid;
 	U64 m_uuid;
 	Vec4 m_diffColor = Vec4(0.5f);
 	Vec4 m_diffColor = Vec4(0.5f);
-	Transform m_worldtransform = Transform::getIdentity();
+	Transform m_worldTransform = Transform::getIdentity();
 
 
 	class Point
 	class Point
 	{
 	{
@@ -212,17 +197,20 @@ private:
 	Spot m_spot;
 	Spot m_spot;
 	Dir m_dir;
 	Dir m_dir;
 
 
-	ImageResourcePtr m_pointDebugImage;
-	ImageResourcePtr m_spotDebugImage;
+	Spatial m_spatial;
+
+	Frustum* m_frustums = nullptr;
 
 
 	LightComponentType m_type;
 	LightComponentType m_type;
 
 
-	U8 m_shadow : 1;
-	U8 m_markedForUpdate : 1;
+	U8 m_shadow : 1 = false;
+	U8 m_markedForUpdate : 1 = true;
+	U8 m_forceFullUpdate : 1 = true;
+	U8 m_frustumCount : 4 = 0; ///< The size of m_frustums array.
 
 
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 
 
-	void draw(RenderQueueDrawContext& ctx) const;
+	void onDestroyReal(SceneNode& node);
 };
 };
 /// @}
 /// @}
 
 

+ 75 - 31
AnKi/Scene/Components/ModelComponent.cpp

@@ -16,7 +16,9 @@ namespace anki {
 ModelComponent::ModelComponent(SceneNode* node)
 ModelComponent::ModelComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
 	: SceneComponent(node, getStaticClassId())
 	, m_node(node)
 	, m_node(node)
+	, m_spatial(this)
 {
 {
+	getExternalSubsystems(*node).m_gpuSceneMemoryPool->allocate(sizeof(Mat3x4) * 2, alignof(F32), m_gpuSceneTransforms);
 }
 }
 
 
 ModelComponent::~ModelComponent()
 ModelComponent::~ModelComponent()
@@ -24,21 +26,28 @@ ModelComponent::~ModelComponent()
 	GpuSceneMemoryPool& gpuScene = *getExternalSubsystems(*m_node).m_gpuSceneMemoryPool;
 	GpuSceneMemoryPool& gpuScene = *getExternalSubsystems(*m_node).m_gpuSceneMemoryPool;
 	gpuScene.free(m_gpuSceneMeshLods);
 	gpuScene.free(m_gpuSceneMeshLods);
 	gpuScene.free(m_gpuSceneUniforms);
 	gpuScene.free(m_gpuSceneUniforms);
+	gpuScene.free(m_gpuSceneTransforms);
 
 
 	m_patchInfos.destroy(m_node->getMemoryPool());
 	m_patchInfos.destroy(m_node->getMemoryPool());
+
+	m_spatial.removeFromOctree(m_node->getSceneGraph().getOctree());
 }
 }
 
 
-Error ModelComponent::loadModelResource(CString filename)
+void ModelComponent::loadModelResource(CString filename)
 {
 {
+	ModelResourcePtr rsrc;
+	const Error err = getExternalSubsystems(*m_node).m_resourceManager->loadResource(filename, rsrc);
+	if(err)
+	{
+		ANKI_SCENE_LOGE("Failed to load model resource");
+		return;
+	}
+
 	m_dirty = true;
 	m_dirty = true;
 
 
-	ModelResourcePtr rsrc;
-	ANKI_CHECK(getExternalSubsystems(*m_node).m_resourceManager->loadResource(filename, rsrc));
 	m_model = std::move(rsrc);
 	m_model = std::move(rsrc);
 	const U32 modelPatchCount = m_model->getModelPatches().getSize();
 	const U32 modelPatchCount = m_model->getModelPatches().getSize();
 
 
-	m_castsShadow = false;
-
 	// GPU scene allocations
 	// GPU scene allocations
 	GpuSceneMemoryPool& gpuScene = *getExternalSubsystems(*m_node).m_gpuSceneMemoryPool;
 	GpuSceneMemoryPool& gpuScene = *getExternalSubsystems(*m_node).m_gpuSceneMemoryPool;
 
 
@@ -66,6 +75,7 @@ Error ModelComponent::loadModelResource(CString filename)
 
 
 	// Some other per-patch init
 	// Some other per-patch init
 	m_presentRenderingTechniques = RenderingTechniqueBit::kNone;
 	m_presentRenderingTechniques = RenderingTechniqueBit::kNone;
+	m_castsShadow = false;
 	for(U32 i = 0; i < modelPatchCount; ++i)
 	for(U32 i = 0; i < modelPatchCount; ++i)
 	{
 	{
 		m_patchInfos[i].m_techniques = m_model->getModelPatches()[i].getMaterial()->getRenderingTechniques();
 		m_patchInfos[i].m_techniques = m_model->getModelPatches()[i].getMaterial()->getRenderingTechniques();
@@ -74,13 +84,27 @@ Error ModelComponent::loadModelResource(CString filename)
 
 
 		m_presentRenderingTechniques |= m_model->getModelPatches()[i].getMaterial()->getRenderingTechniques();
 		m_presentRenderingTechniques |= m_model->getModelPatches()[i].getMaterial()->getRenderingTechniques();
 	}
 	}
-
-	return Error::kNone;
 }
 }
 
 
 Error ModelComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 Error ModelComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 {
-	if(ANKI_UNLIKELY(m_dirty && m_model.isCreated()))
+	if(!isEnabled()) [[unlikely]]
+	{
+		updated = false;
+		return Error::kNone;
+	}
+
+	const Bool resourceUpdated = m_dirty;
+	m_dirty = false;
+	const Bool moved = info.m_node->movedThisFrame() || m_firstTimeUpdate;
+	const Bool movedLastFrame = m_movedLastFrame || m_firstTimeUpdate;
+	m_firstTimeUpdate = false;
+	m_movedLastFrame = moved;
+
+	updated = resourceUpdated || moved || movedLastFrame;
+
+	// Upload mesh LODs and uniforms
+	if(resourceUpdated) [[unlikely]]
 	{
 	{
 		GpuSceneMicroPatcher& gpuScenePatcher = *getExternalSubsystems(*info.m_node).m_gpuSceneMicroPatcher;
 		GpuSceneMicroPatcher& gpuScenePatcher = *getExternalSubsystems(*info.m_node).m_gpuSceneMicroPatcher;
 
 
@@ -152,8 +176,36 @@ Error ModelComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 								&allUniforms[0]);
 								&allUniforms[0]);
 	}
 	}
 
 
-	updated = m_dirty;
-	m_dirty = false;
+	// Upload transforms
+	if(moved || movedLastFrame) [[unlikely]]
+	{
+		Array<Mat3x4, 2> trfs;
+		trfs[0] = Mat3x4(info.m_node->getWorldTransform());
+		trfs[1] = Mat3x4(info.m_node->getPreviousWorldTransform());
+
+		getExternalSubsystems(*info.m_node)
+			.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, m_gpuSceneTransforms.m_offset, sizeof(trfs), &trfs[0]);
+	}
+
+	// Spatial update
+	if(moved || resourceUpdated || m_skinComponent) [[unlikely]]
+	{
+		Aabb aabbLocal;
+		if(m_skinComponent == nullptr) [[likely]]
+		{
+			aabbLocal = m_model->getBoundingVolume();
+		}
+		else
+		{
+			aabbLocal = m_skinComponent->getBoneBoundingVolumeLocalSpace();
+		}
+
+		const Aabb aabbWorld = aabbLocal.getTransformed(info.m_node->getWorldTransform());
+
+		m_spatial.setBoundingShape(aabbWorld);
+		m_spatial.update(info.m_node->getSceneGraph().getOctree());
+	}
+
 	return Error::kNone;
 	return Error::kNone;
 }
 }
 
 
@@ -161,7 +213,6 @@ void ModelComponent::setupRenderableQueueElements(U32 lod, RenderingTechnique te
 												  WeakArray<RenderableQueueElement>& outRenderables) const
 												  WeakArray<RenderableQueueElement>& outRenderables) const
 {
 {
 	ANKI_ASSERT(isEnabled());
 	ANKI_ASSERT(isEnabled());
-	ANKI_ASSERT(m_moveComponent);
 
 
 	outRenderables.setArray(nullptr, 0);
 	outRenderables.setArray(nullptr, 0);
 
 
@@ -189,7 +240,7 @@ void ModelComponent::setupRenderableQueueElements(U32 lod, RenderingTechnique te
 	outRenderables.setArray(renderables, renderableCount);
 	outRenderables.setArray(renderables, renderableCount);
 
 
 	// Fill renderables
 	// Fill renderables
-	const Bool moved = m_moveComponent->wasDirtyThisFrame() && technique == RenderingTechnique::kGBuffer;
+	const Bool moved = m_node->movedThisFrame() && technique == RenderingTechnique::kGBuffer;
 	const Bool hasSkin = m_skinComponent != nullptr && m_skinComponent->isEnabled();
 	const Bool hasSkin = m_skinComponent != nullptr && m_skinComponent->isEnabled();
 
 
 	RenderingKey key;
 	RenderingKey key;
@@ -214,7 +265,7 @@ void ModelComponent::setupRenderableQueueElements(U32 lod, RenderingTechnique te
 		patch.getRenderingInfo(key, modelInf);
 		patch.getRenderingInfo(key, modelInf);
 
 
 		queueElem.m_program = modelInf.m_program.get();
 		queueElem.m_program = modelInf.m_program.get();
-		queueElem.m_worldTransformsOffset = m_moveComponent->getTransformsGpuSceneOffset();
+		queueElem.m_worldTransformsOffset = U32(m_gpuSceneTransforms.m_offset);
 		queueElem.m_uniformsOffset = m_patchInfos[i].m_gpuSceneUniformsOffset;
 		queueElem.m_uniformsOffset = m_patchInfos[i].m_gpuSceneUniformsOffset;
 		queueElem.m_geometryOffset =
 		queueElem.m_geometryOffset =
 			U32(m_gpuSceneMeshLods.m_offset + sizeof(GpuSceneMeshLod) * (kMaxLodCount * i + lod));
 			U32(m_gpuSceneMeshLods.m_offset + sizeof(GpuSceneMeshLod) * (kMaxLodCount * i + lod));
@@ -234,27 +285,20 @@ void ModelComponent::onOtherComponentRemovedOrAdded(SceneComponent* other, Bool
 {
 {
 	ANKI_ASSERT(other);
 	ANKI_ASSERT(other);
 
 
-	if(added)
+	if(other->getClassId() != SkinComponent::getStaticClassId())
 	{
 	{
-		if(other->getClassId() == MoveComponent::getStaticClassId())
-		{
-			m_moveComponent = static_cast<MoveComponent*>(other);
-		}
-		else if(other->getClassId() == SkinComponent::getStaticClassId())
-		{
-			m_skinComponent = static_cast<SkinComponent*>(other);
-		}
+		return;
 	}
 	}
-	else
+
+	if(added && m_skinComponent != nullptr)
 	{
 	{
-		if(other == m_moveComponent)
-		{
-			m_moveComponent = nullptr;
-		}
-		else if(other == m_skinComponent)
-		{
-			m_skinComponent = nullptr;
-		}
+		m_skinComponent = static_cast<SkinComponent*>(other);
+		m_dirty = true;
+	}
+	else if(other == m_skinComponent)
+	{
+		m_skinComponent = nullptr;
+		m_dirty = true;
 	}
 	}
 }
 }
 
 

+ 10 - 4
AnKi/Scene/Components/ModelComponent.h

@@ -6,6 +6,7 @@
 #pragma once
 #pragma once
 
 
 #include <AnKi/Scene/Components/SceneComponent.h>
 #include <AnKi/Scene/Components/SceneComponent.h>
+#include <AnKi/Scene/Spatial.h>
 #include <AnKi/Resource/Forward.h>
 #include <AnKi/Resource/Forward.h>
 #include <AnKi/Util/WeakArray.h>
 #include <AnKi/Util/WeakArray.h>
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Renderer/RenderQueue.h>
@@ -25,7 +26,7 @@ public:
 
 
 	~ModelComponent();
 	~ModelComponent();
 
 
-	Error loadModelResource(CString filename);
+	void loadModelResource(CString filename);
 
 
 	const ModelResourcePtr& getModelResource() const
 	const ModelResourcePtr& getModelResource() const
 	{
 	{
@@ -54,16 +55,21 @@ private:
 	};
 	};
 
 
 	SceneNode* m_node = nullptr;
 	SceneNode* m_node = nullptr;
-	MoveComponent* m_moveComponent = nullptr;
 	SkinComponent* m_skinComponent = nullptr;
 	SkinComponent* m_skinComponent = nullptr;
+	Spatial m_spatial;
+
 	ModelResourcePtr m_model;
 	ModelResourcePtr m_model;
 
 
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneMeshLods;
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneMeshLods;
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneUniforms;
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneUniforms;
+	SegregatedListsGpuMemoryPoolToken m_gpuSceneTransforms;
 	DynamicArray<PatchInfo> m_patchInfos;
 	DynamicArray<PatchInfo> m_patchInfos;
 
 
-	Bool m_dirty = true;
-	Bool m_castsShadow = false;
+	Bool m_dirty : 1 = true;
+	Bool m_castsShadow : 1 = false;
+	Bool m_movedLastFrame : 1 = true;
+	Bool m_firstTimeUpdate : 1 = true; ///< Extra flag in case the component is added in a node that hasn't been moved.
+
 	RenderingTechniqueBit m_presentRenderingTechniques = RenderingTechniqueBit::kNone;
 	RenderingTechniqueBit m_presentRenderingTechniques = RenderingTechniqueBit::kNone;
 
 
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);

+ 1 - 89
AnKi/Scene/Components/MoveComponent.cpp

@@ -5,101 +5,13 @@
 
 
 #include <AnKi/Scene/Components/MoveComponent.h>
 #include <AnKi/Scene/Components/MoveComponent.h>
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneNode.h>
-#include <AnKi/Renderer/RenderQueue.h>
 
 
 namespace anki {
 namespace anki {
 
 
-MoveComponent::MoveComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
-	, m_ignoreLocalTransform(false)
-	, m_ignoreParentTransform(false)
-	, m_dirtyThisFrame(true)
-{
-	getExternalSubsystems(*node).m_gpuSceneMemoryPool->allocate(sizeof(Mat3x4) * 2, alignof(F32), m_gpuSceneTransforms);
-	markForUpdate();
-}
-
-MoveComponent::~MoveComponent()
-{
-}
-
 Error MoveComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 Error MoveComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 {
-	const Bool dirty = m_markedForUpdate;
-	updated = dirty;
-
-	SceneNode& node = *info.m_node;
-
-	m_prevWTrf = m_wtrf;
-
-	// If dirty then update world transform
-	if(dirty)
-	{
-		const SceneNode* parent = node.getParent();
-
-		if(parent)
-		{
-			const MoveComponent* parentMove = parent->tryGetFirstComponentOfType<MoveComponent>();
-
-			if(parentMove == nullptr)
-			{
-				// Parent not movable
-				m_wtrf = m_ltrf;
-			}
-			else if(m_ignoreParentTransform)
-			{
-				m_wtrf = m_ltrf;
-			}
-			else if(m_ignoreLocalTransform)
-			{
-				m_wtrf = parentMove->getWorldTransform();
-			}
-			else
-			{
-				m_wtrf = parentMove->getWorldTransform().combineTransformations(m_ltrf);
-			}
-		}
-		else
-		{
-			// No parent
-
-			m_wtrf = m_ltrf;
-		}
-
-		// Now it's a good time to cleanse parent
-		m_markedForUpdate = false;
-	}
-
-	// If this is dirty then make children dirty as well. Don't walk the whole tree because you will re-walk it later
-	if(dirty)
-	{
-		[[maybe_unused]] const Error err = node.visitChildrenMaxDepth(1, [](SceneNode& childNode) -> Error {
-			childNode.iterateComponentsOfType<MoveComponent>([](MoveComponent& mov) {
-				mov.markForUpdate();
-			});
-			return Error::kNone;
-		});
-	}
-
-	// Micro patch
-	const Bool dirtyLastFrame = m_dirtyThisFrame;
-	if(dirty || dirtyLastFrame)
-	{
-		Array<Mat3x4, 2> trfs;
-		trfs[0] = Mat3x4(m_wtrf);
-		trfs[1] = Mat3x4(m_prevWTrf);
-
-		info.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, m_gpuSceneTransforms.m_offset, sizeof(trfs), &trfs[0]);
-	}
-
-	m_dirtyThisFrame = dirty;
-
+	updated = info.m_node->updateTransform(*info.m_framePool);
 	return Error::kNone;
 	return Error::kNone;
 }
 }
 
 
-void MoveComponent::onDestroy(SceneNode& node)
-{
-	getExternalSubsystems(node).m_gpuSceneMemoryPool->free(m_gpuSceneTransforms);
-}
-
 } // end namespace anki
 } // end namespace anki

+ 4 - 153
AnKi/Scene/Components/MoveComponent.h

@@ -6,176 +6,27 @@
 #pragma once
 #pragma once
 
 
 #include <AnKi/Scene/Components/SceneComponent.h>
 #include <AnKi/Scene/Components/SceneComponent.h>
-#include <AnKi/Util/BitMask.h>
-#include <AnKi/Util/Enum.h>
-#include <AnKi/Math.h>
 
 
 namespace anki {
 namespace anki {
 
 
 /// @addtogroup scene
 /// @addtogroup scene
 /// @{
 /// @{
 
 
-/// Interface for movable scene nodes.
+/// A simple implicit component that updates the SceneNode's transform.
 class MoveComponent : public SceneComponent
 class MoveComponent : public SceneComponent
 {
 {
 	ANKI_SCENE_COMPONENT(MoveComponent)
 	ANKI_SCENE_COMPONENT(MoveComponent)
 
 
 public:
 public:
-	MoveComponent(SceneNode* node);
-
-	~MoveComponent();
-
-	/// Get the parent's world transform.
-	void setIgnoreLocalTransform(Bool ignore)
-	{
-		m_ignoreLocalTransform = ignore;
-	}
-
-	/// Ignore parent nodes's transform.
-	void setIgnoreParentTransform(Bool ignore)
-	{
-		m_ignoreParentTransform = ignore;
-	}
-
-	const Transform& getLocalTransform() const
-	{
-		return m_ltrf;
-	}
-
-	void setLocalTransform(const Transform& x)
-	{
-		m_ltrf = x;
-		markForUpdate();
-	}
-
-	void setLocalOrigin(const Vec4& x)
-	{
-		m_ltrf.setOrigin(x);
-		markForUpdate();
-	}
-
-	const Vec4& getLocalOrigin() const
+	MoveComponent(SceneNode* node)
+		: SceneComponent(node, getStaticClassId())
 	{
 	{
-		return m_ltrf.getOrigin();
 	}
 	}
 
 
-	void setLocalRotation(const Mat3x4& x)
-	{
-		m_ltrf.setRotation(x);
-		markForUpdate();
-	}
-
-	const Mat3x4& getLocalRotation() const
-	{
-		return m_ltrf.getRotation();
-	}
-
-	void setLocalScale(F32 x)
-	{
-		m_ltrf.setScale(x);
-		markForUpdate();
-	}
-
-	F32 getLocalScale() const
-	{
-		return m_ltrf.getScale();
-	}
-
-	const Transform& getWorldTransform() const
-	{
-		return m_wtrf;
-	}
-
-	const Transform& getPreviousWorldTransform() const
-	{
-		return m_prevWTrf;
-	}
-
-	/// @name Mess with the local transform
-	/// @{
-	void rotateLocalX(F32 angleRad)
-	{
-		m_ltrf.getRotation().rotateXAxis(angleRad);
-		markForUpdate();
-	}
-	void rotateLocalY(F32 angleRad)
-	{
-		m_ltrf.getRotation().rotateYAxis(angleRad);
-		markForUpdate();
-	}
-	void rotateLocalZ(F32 angleRad)
-	{
-		m_ltrf.getRotation().rotateZAxis(angleRad);
-		markForUpdate();
-	}
-	void moveLocalX(F32 distance)
-	{
-		Vec3 x_axis = m_ltrf.getRotation().getColumn(0);
-		m_ltrf.getOrigin() += Vec4(x_axis, 0.0) * distance;
-		markForUpdate();
-	}
-	void moveLocalY(F32 distance)
-	{
-		Vec3 y_axis = m_ltrf.getRotation().getColumn(1);
-		m_ltrf.getOrigin() += Vec4(y_axis, 0.0) * distance;
-		markForUpdate();
-	}
-	void moveLocalZ(F32 distance)
-	{
-		Vec3 z_axis = m_ltrf.getRotation().getColumn(2);
-		m_ltrf.getOrigin() += Vec4(z_axis, 0.0) * distance;
-		markForUpdate();
-	}
-	void scale(F32 s)
-	{
-		m_ltrf.getScale() *= s;
-		markForUpdate();
-	}
-
-	void lookAtPoint(const Vec4& point)
-	{
-		m_ltrf.lookAt(point, Vec4(0.0f, 1.0f, 0.0f, 0.0f));
-		markForUpdate();
-	}
-	/// @}
-
-	U32 getTransformsGpuSceneOffset() const
-	{
-		ANKI_ASSERT((m_gpuSceneTransforms.m_offset % 4) == 0);
-		return U32(m_gpuSceneTransforms.m_offset);
-	}
-
-	/// It's valid after the component was updated.
-	Bool wasDirtyThisFrame() const
-	{
-		return m_dirtyThisFrame;
-	}
+	~MoveComponent() = default;
 
 
 private:
 private:
-	/// The transformation in local space
-	Transform m_ltrf = Transform::getIdentity();
-
-	/// The transformation in world space (local combined with parent's transformation)
-	Transform m_wtrf = Transform::getIdentity();
-
-	/// Keep the previous transformation for checking if it moved
-	Transform m_prevWTrf = Transform::getIdentity();
-
-	SegregatedListsGpuMemoryPoolToken m_gpuSceneTransforms;
-
-	Bool m_markedForUpdate : 1;
-	Bool m_ignoreLocalTransform : 1;
-	Bool m_ignoreParentTransform : 1;
-	Bool m_dirtyThisFrame : 1;
-
-	void markForUpdate()
-	{
-		m_markedForUpdate = true;
-	}
-
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);
-
-	void onDestroy(SceneNode& node);
 };
 };
 /// @}
 /// @}
 
 

+ 28 - 31
AnKi/Scene/Components/ParticleEmitterComponent.cpp

@@ -196,7 +196,9 @@ public:
 ParticleEmitterComponent::ParticleEmitterComponent(SceneNode* node)
 ParticleEmitterComponent::ParticleEmitterComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
 	: SceneComponent(node, getStaticClassId())
 	, m_node(node)
 	, m_node(node)
+	, m_spatial(this)
 {
 {
+	getExternalSubsystems(*node).m_gpuSceneMemoryPool->allocate(sizeof(Mat3x4), alignof(F32), m_gpuSceneTransform);
 }
 }
 
 
 ParticleEmitterComponent::~ParticleEmitterComponent()
 ParticleEmitterComponent::~ParticleEmitterComponent()
@@ -210,12 +212,24 @@ ParticleEmitterComponent::~ParticleEmitterComponent()
 	gpuScenePool.free(m_gpuSceneAlphas);
 	gpuScenePool.free(m_gpuSceneAlphas);
 	gpuScenePool.free(m_gpuSceneParticles);
 	gpuScenePool.free(m_gpuSceneParticles);
 	gpuScenePool.free(m_gpuSceneUniforms);
 	gpuScenePool.free(m_gpuSceneUniforms);
+	gpuScenePool.free(m_gpuSceneTransform);
+
+	m_spatial.removeFromOctree(m_node->getSceneGraph().getOctree());
 }
 }
 
 
-Error ParticleEmitterComponent::loadParticleEmitterResource(CString filename)
+void ParticleEmitterComponent::loadParticleEmitterResource(CString filename)
 {
 {
 	// Load
 	// Load
-	ANKI_CHECK(getExternalSubsystems(*m_node).m_resourceManager->loadResource(filename, m_particleEmitterResource));
+	ParticleEmitterResourcePtr rsrc;
+	const Error err = getExternalSubsystems(*m_node).m_resourceManager->loadResource(filename, rsrc);
+	if(err)
+	{
+		ANKI_SCENE_LOGE("Failed to load particle emitter");
+		return;
+	}
+
+	m_particleEmitterResource = std::move(rsrc);
+
 	m_props = m_particleEmitterResource->getProperties();
 	m_props = m_particleEmitterResource->getProperties();
 	m_resourceUpdated = true;
 	m_resourceUpdated = true;
 
 
@@ -259,13 +273,11 @@ Error ParticleEmitterComponent::loadParticleEmitterResource(CString filename)
 	gpuScenePool.allocate(sizeof(GpuSceneParticles), alignof(U32), m_gpuSceneParticles);
 	gpuScenePool.allocate(sizeof(GpuSceneParticles), alignof(U32), m_gpuSceneParticles);
 	gpuScenePool.allocate(m_particleEmitterResource->getMaterial()->getPrefilledLocalUniforms().getSizeInBytes(),
 	gpuScenePool.allocate(m_particleEmitterResource->getMaterial()->getPrefilledLocalUniforms().getSizeInBytes(),
 						  alignof(U32), m_gpuSceneUniforms);
 						  alignof(U32), m_gpuSceneUniforms);
-
-	return Error::kNone;
 }
 }
 
 
 Error ParticleEmitterComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 Error ParticleEmitterComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 {
-	if(ANKI_UNLIKELY(!m_particleEmitterResource.isCreated()))
+	if(!m_particleEmitterResource.isCreated()) [[unlikely]]
 	{
 	{
 		updated = false;
 		updated = false;
 		return Error::kNone;
 		return Error::kNone;
@@ -276,18 +288,22 @@ Error ParticleEmitterComponent::update(SceneComponentUpdateInfo& info, Bool& upd
 	F32* scales;
 	F32* scales;
 	F32* alphas;
 	F32* alphas;
 
 
+	Aabb aabbWorld;
 	if(m_simulationType == SimulationType::kSimple)
 	if(m_simulationType == SimulationType::kSimple)
 	{
 	{
 		simulate(info.m_previousTime, info.m_currentTime, WeakArray<SimpleParticle>(m_simpleParticles), positions,
 		simulate(info.m_previousTime, info.m_currentTime, WeakArray<SimpleParticle>(m_simpleParticles), positions,
-				 scales, alphas);
+				 scales, alphas, aabbWorld);
 	}
 	}
 	else
 	else
 	{
 	{
 		ANKI_ASSERT(m_simulationType == SimulationType::kPhysicsEngine);
 		ANKI_ASSERT(m_simulationType == SimulationType::kPhysicsEngine);
 		simulate(info.m_previousTime, info.m_currentTime, WeakArray<PhysicsParticle>(m_physicsParticles), positions,
 		simulate(info.m_previousTime, info.m_currentTime, WeakArray<PhysicsParticle>(m_physicsParticles), positions,
-				 scales, alphas);
+				 scales, alphas, aabbWorld);
 	}
 	}
 
 
+	m_spatial.setBoundingShape(aabbWorld);
+	m_spatial.update(info.m_node->getSceneGraph().getOctree());
+
 	// Upload to the GPU scene
 	// Upload to the GPU scene
 	GpuSceneMicroPatcher& patcher = *info.m_gpuSceneMicroPatcher;
 	GpuSceneMicroPatcher& patcher = *info.m_gpuSceneMicroPatcher;
 	if(m_aliveParticleCount > 0)
 	if(m_aliveParticleCount > 0)
@@ -318,7 +334,7 @@ Error ParticleEmitterComponent::update(SceneComponentUpdateInfo& info, Bool& upd
 
 
 template<typename TParticle>
 template<typename TParticle>
 void ParticleEmitterComponent::simulate(Second prevUpdateTime, Second crntTime, WeakArray<TParticle> particles,
 void ParticleEmitterComponent::simulate(Second prevUpdateTime, Second crntTime, WeakArray<TParticle> particles,
-										Vec3*& positions, F32*& scales, F32*& alphas)
+										Vec3*& positions, F32*& scales, F32*& alphas, Aabb& aabbWorld)
 {
 {
 	// - Deactivate the dead particles
 	// - Deactivate the dead particles
 	// - Calc the AABB
 	// - Calc the AABB
@@ -379,11 +395,11 @@ void ParticleEmitterComponent::simulate(Second prevUpdateTime, Second crntTime,
 		ANKI_ASSERT(maxParticleSize > 0.0f);
 		ANKI_ASSERT(maxParticleSize > 0.0f);
 		const Vec3 min = aabbMin - maxParticleSize;
 		const Vec3 min = aabbMin - maxParticleSize;
 		const Vec3 max = aabbMax + maxParticleSize;
 		const Vec3 max = aabbMax + maxParticleSize;
-		m_worldBoundingVolume = Aabb(min, max);
+		aabbWorld = Aabb(min, max);
 	}
 	}
 	else
 	else
 	{
 	{
-		m_worldBoundingVolume = Aabb(Vec3(0.0f), Vec3(0.001f));
+		aabbWorld = Aabb(Vec3(0.0f), Vec3(0.001f));
 		positions = nullptr;
 		positions = nullptr;
 		alphas = scales = nullptr;
 		alphas = scales = nullptr;
 	}
 	}
@@ -402,8 +418,7 @@ void ParticleEmitterComponent::simulate(Second prevUpdateTime, Second crntTime,
 				continue;
 				continue;
 			}
 			}
 
 
-			particle.revive(
-				m_props, (m_moveComponent) ? m_moveComponent->getWorldTransform() : Transform::getIdentity(), crntTime);
+			particle.revive(m_props, m_node->getWorldTransform(), crntTime);
 
 
 			// do the rest
 			// do the rest
 			++particleCount;
 			++particleCount;
@@ -441,7 +456,7 @@ void ParticleEmitterComponent::setupRenderableQueueElements(RenderingTechnique t
 
 
 	el->m_mergeKey = 0; // Not mergable
 	el->m_mergeKey = 0; // Not mergable
 	el->m_program = prog.get();
 	el->m_program = prog.get();
-	el->m_worldTransformsOffset = m_moveComponent->getTransformsGpuSceneOffset();
+	el->m_worldTransformsOffset = 0;
 	el->m_uniformsOffset = U32(m_gpuSceneUniforms.m_offset);
 	el->m_uniformsOffset = U32(m_gpuSceneUniforms.m_offset);
 	el->m_geometryOffset = U32(m_gpuSceneParticles.m_offset);
 	el->m_geometryOffset = U32(m_gpuSceneParticles.m_offset);
 	el->m_boneTransformsOffset = 0;
 	el->m_boneTransformsOffset = 0;
@@ -453,22 +468,4 @@ void ParticleEmitterComponent::setupRenderableQueueElements(RenderingTechnique t
 	outRenderables.setArray(el, 1);
 	outRenderables.setArray(el, 1);
 }
 }
 
 
-void ParticleEmitterComponent::onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added)
-{
-	if(added)
-	{
-		if(other->getClassId() == MoveComponent::getStaticClassId())
-		{
-			m_moveComponent = static_cast<MoveComponent*>(other);
-		}
-	}
-	else
-	{
-		if(m_moveComponent == other)
-		{
-			m_moveComponent = nullptr;
-		}
-	}
-}
-
 } // end namespace anki
 } // end namespace anki

+ 7 - 14
AnKi/Scene/Components/ParticleEmitterComponent.h

@@ -6,6 +6,7 @@
 #pragma once
 #pragma once
 
 
 #include <AnKi/Scene/Components/SceneComponent.h>
 #include <AnKi/Scene/Components/SceneComponent.h>
+#include <AnKi/Scene/Spatial.h>
 #include <AnKi/Resource/ParticleEmitterResource.h>
 #include <AnKi/Resource/ParticleEmitterResource.h>
 #include <AnKi/Collision/Aabb.h>
 #include <AnKi/Collision/Aabb.h>
 #include <AnKi/Util/WeakArray.h>
 #include <AnKi/Util/WeakArray.h>
@@ -29,12 +30,7 @@ public:
 
 
 	~ParticleEmitterComponent();
 	~ParticleEmitterComponent();
 
 
-	Error loadParticleEmitterResource(CString filename);
-
-	const Aabb& getAabbWorldSpace() const
-	{
-		return m_worldBoundingVolume;
-	}
+	void loadParticleEmitterResource(CString filename);
 
 
 	Bool isEnabled() const
 	Bool isEnabled() const
 	{
 	{
@@ -58,34 +54,31 @@ private:
 
 
 	SceneNode* m_node = nullptr;
 	SceneNode* m_node = nullptr;
 
 
-	MoveComponent* m_moveComponent = nullptr;
-
 	ParticleEmitterProperties m_props;
 	ParticleEmitterProperties m_props;
 
 
+	Spatial m_spatial;
+
 	ParticleEmitterResourcePtr m_particleEmitterResource;
 	ParticleEmitterResourcePtr m_particleEmitterResource;
 	DynamicArray<SimpleParticle> m_simpleParticles;
 	DynamicArray<SimpleParticle> m_simpleParticles;
 	DynamicArray<PhysicsParticle> m_physicsParticles;
 	DynamicArray<PhysicsParticle> m_physicsParticles;
 	Second m_timeLeftForNextEmission = 0.0;
 	Second m_timeLeftForNextEmission = 0.0;
 	U32 m_aliveParticleCount = 0;
 	U32 m_aliveParticleCount = 0;
-	Bool m_resourceUpdated = true;
-
-	Aabb m_worldBoundingVolume = Aabb(Vec3(-1.0f), Vec3(1.0f));
 
 
 	SegregatedListsGpuMemoryPoolToken m_gpuScenePositions;
 	SegregatedListsGpuMemoryPoolToken m_gpuScenePositions;
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneAlphas;
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneAlphas;
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneScales;
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneScales;
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneParticles;
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneParticles;
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneUniforms;
 	SegregatedListsGpuMemoryPoolToken m_gpuSceneUniforms;
+	SegregatedListsGpuMemoryPoolToken m_gpuSceneTransform;
 
 
+	Bool m_resourceUpdated = true;
 	SimulationType m_simulationType = SimulationType::kUndefined;
 	SimulationType m_simulationType = SimulationType::kUndefined;
 
 
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 
 
 	template<typename TParticle>
 	template<typename TParticle>
 	void simulate(Second prevUpdateTime, Second crntTime, WeakArray<TParticle> particles, Vec3*& positions,
 	void simulate(Second prevUpdateTime, Second crntTime, WeakArray<TParticle> particles, Vec3*& positions,
-				  F32*& scales, F32*& alphas);
-
-	void onOtherComponentRemovedOrAdded(SceneComponent* other, Bool added);
+				  F32*& scales, F32*& alphas, Aabb& aabbWorld);
 };
 };
 /// @}
 /// @}
 
 

+ 17 - 1
AnKi/Scene/Components/PlayerControllerComponent.cpp

@@ -14,9 +14,25 @@ PlayerControllerComponent::PlayerControllerComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
 	: SceneComponent(node, getStaticClassId())
 {
 {
 	PhysicsPlayerControllerInitInfo init;
 	PhysicsPlayerControllerInitInfo init;
-	init.m_position = Vec3(0.0f);
+	init.m_position = node->getWorldTransform().getOrigin().xyz();
 	m_player = getExternalSubsystems(*node).m_physicsWorld->newInstance<PhysicsPlayerController>(init);
 	m_player = getExternalSubsystems(*node).m_physicsWorld->newInstance<PhysicsPlayerController>(init);
 	m_player->setUserData(this);
 	m_player->setUserData(this);
+
+	node->setIgnoreParentTransform(true);
+}
+
+Error PlayerControllerComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+{
+	const Transform newTrf = m_player->getTransform();
+	updated = newTrf != m_trf;
+
+	if(updated)
+	{
+		m_trf = newTrf;
+		info.m_node->setLocalTransform(newTrf);
+	}
+
+	return Error::kNone;
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 1 - 22
AnKi/Scene/Components/PlayerControllerComponent.h

@@ -21,16 +21,6 @@ class PlayerControllerComponent : public SceneComponent
 public:
 public:
 	PlayerControllerComponent(SceneNode* node);
 	PlayerControllerComponent(SceneNode* node);
 
 
-	const Transform& getWorldTransform() const
-	{
-		return m_trf;
-	}
-
-	void setWorldTransform(const Transform& trf)
-	{
-		m_player->moveToPosition(trf.getOrigin().xyz());
-	}
-
 	void setVelocity(F32 forwardSpeed, F32 strafeSpeed, F32 jumpSpeed, const Vec4& forwardDir)
 	void setVelocity(F32 forwardSpeed, F32 strafeSpeed, F32 jumpSpeed, const Vec4& forwardDir)
 	{
 	{
 		m_player->setVelocity(forwardSpeed, strafeSpeed, jumpSpeed, forwardDir);
 		m_player->setVelocity(forwardSpeed, strafeSpeed, jumpSpeed, forwardDir);
@@ -41,22 +31,11 @@ public:
 		m_player->moveToPosition(pos);
 		m_player->moveToPosition(pos);
 	}
 	}
 
 
-	PhysicsPlayerControllerPtr getPhysicsPlayerController() const
-	{
-		return m_player;
-	}
-
 private:
 private:
 	PhysicsPlayerControllerPtr m_player;
 	PhysicsPlayerControllerPtr m_player;
 	Transform m_trf = Transform::getIdentity();
 	Transform m_trf = Transform::getIdentity();
 
 
-	Error update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		const Transform newTrf = m_player->getTransform();
-		updated = newTrf != m_trf;
-		m_trf = newTrf;
-		return Error::kNone;
-	}
+	Error update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated);
 };
 };
 /// @}
 /// @}
 
 

+ 58 - 35
AnKi/Scene/Components/ReflectionProbeComponent.cpp

@@ -4,23 +4,28 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <AnKi/Scene/Components/ReflectionProbeComponent.h>
 #include <AnKi/Scene/Components/ReflectionProbeComponent.h>
+#include <AnKi/Scene/Components/MoveComponent.h>
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneNode.h>
-#include <AnKi/Resource/ResourceManager.h>
-#include <AnKi/Resource/ImageResource.h>
+#include <AnKi/Core/ConfigSet.h>
 
 
 namespace anki {
 namespace anki {
 
 
 ReflectionProbeComponent::ReflectionProbeComponent(SceneNode* node)
 ReflectionProbeComponent::ReflectionProbeComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
 	: SceneComponent(node, getStaticClassId())
-	, m_node(node)
 	, m_uuid(node->getSceneGraph().getNewUuid())
 	, m_uuid(node->getSceneGraph().getNewUuid())
-	, m_markedForRendering(false)
-	, m_markedForUpdate(true)
+	, m_spatial(this)
 {
 {
-	if(getExternalSubsystems(*node).m_resourceManager->loadResource("EngineAssets/Mirror.ankitex", m_debugImage))
+	m_worldPos = node->getWorldTransform().getOrigin().xyz();
+
+	for(U32 i = 0; i < 6; ++i)
 	{
 	{
-		ANKI_SCENE_LOGF("Failed to load resources");
+		m_frustums[i].init(FrustumType::kPerspective, &node->getMemoryPool());
+		m_frustums[i].setPerspective(kClusterObjectFrustumNearPlane, 100.0f, kPi / 2.0f, kPi / 2.0f);
+		m_frustums[i].setWorldTransform(
+			Transform(m_worldPos.xyz0(), Frustum::getOmnidirectionalFrustumRotations()[i], 1.0f));
+		m_frustums[i].setShadowCascadeCount(1);
+		m_frustums[i].update();
 	}
 	}
 }
 }
 
 
@@ -28,44 +33,62 @@ ReflectionProbeComponent::~ReflectionProbeComponent()
 {
 {
 }
 }
 
 
-void ReflectionProbeComponent::draw(RenderQueueDrawContext& ctx) const
+Error ReflectionProbeComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 {
-	const Vec3 tsl = m_worldPos;
-	const Vec3 scale = getBoxVolumeSize() / 2.0f;
-
-	// Set non uniform scale.
-	Mat3 rot = Mat3::getIdentity();
-	rot(0, 0) *= scale.x();
-	rot(1, 1) *= scale.y();
-	rot(2, 2) *= scale.z();
-
-	const Mat4 mvp = ctx.m_viewProjectionMatrix * Mat4(tsl.xyz1(), rot, 1.0f);
+	const Bool moved = info.m_node->movedThisFrame();
+	const Bool shapeUpdated = m_dirty;
+	m_dirty = false;
+	updated = moved || shapeUpdated;
 
 
-	const Bool enableDepthTest = ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::kDepthTestOn);
-	if(enableDepthTest)
+	if(moved) [[unlikely]]
 	{
 	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::kLess);
+		m_worldPos = info.m_node->getWorldTransform().getOrigin().xyz();
+
+		for(U32 i = 0; i < 6; ++i)
+		{
+			m_frustums[i].setWorldTransform(
+				Transform(m_worldPos.xyz0(), Frustum::getOmnidirectionalFrustumRotations()[i], 1.0f));
+		}
 	}
 	}
-	else
+
+	if(shapeUpdated) [[unlikely]]
 	{
 	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::kAlways);
-	}
+		F32 effectiveDistance = max(m_halfSize.x(), m_halfSize.y());
+		effectiveDistance = max(effectiveDistance, m_halfSize.z());
+		effectiveDistance =
+			max(effectiveDistance, getExternalSubsystems(*info.m_node).m_config->getSceneProbeEffectiveDistance());
 
 
-	m_node->getSceneGraph().getDebugDrawer().drawCubes(
-		ConstWeakArray<Mat4>(&mvp, 1), Vec4(0.0f, 0.0f, 1.0f, 1.0f), 1.0f,
-		ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::kDitheredDepthTestOn), 2.0f, *ctx.m_rebarStagingPool,
-		ctx.m_commandBuffer);
+		const F32 shadowCascadeDistance = min(
+			effectiveDistance, getExternalSubsystems(*info.m_node).m_config->getSceneProbeShadowEffectiveDistance());
 
 
-	m_node->getSceneGraph().getDebugDrawer().drawBillboardTextures(
-		ctx.m_projectionMatrix, ctx.m_viewMatrix, ConstWeakArray<Vec3>(&m_worldPos, 1), Vec4(1.0f),
-		ctx.m_debugDrawFlags.get(RenderQueueDebugDrawFlag::kDitheredDepthTestOn), m_debugImage->getTextureView(),
-		ctx.m_sampler, Vec2(0.75f), *ctx.m_rebarStagingPool, ctx.m_commandBuffer);
+		for(U32 i = 0; i < 6; ++i)
+		{
+			m_frustums[i].setFar(effectiveDistance);
+			m_frustums[i].setShadowCascadeDistance(0, shadowCascadeDistance);
 
 
-	// Restore state
-	if(!enableDepthTest)
+			// Add something really far to force LOD 0 to be used. The importing tools create LODs with holes some times
+			// and that causes the sky to bleed to GI rendering
+			m_frustums[i].setLodDistances({effectiveDistance - 3.0f * kEpsilonf, effectiveDistance - 2.0f * kEpsilonf,
+										   effectiveDistance - 1.0f * kEpsilonf});
+		}
+	}
+
+	if(updated) [[unlikely]]
 	{
 	{
-		ctx.m_commandBuffer->setDepthCompareOperation(CompareOperation::kLess);
+		// Set a new UUID to force the renderer to update the probe
+		m_uuid = info.m_node->getSceneGraph().getNewUuid();
+
+		for(U32 i = 0; i < 6; ++i)
+		{
+			m_frustums[i].update();
+		}
+
+		Aabb aabbWorld(-m_halfSize + m_worldPos, m_halfSize + m_worldPos);
+		m_spatial.setBoundingShape(aabbWorld);
+		m_spatial.update(info.m_node->getSceneGraph().getOctree());
 	}
 	}
+
+	return Error::kNone;
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 17 - 33
AnKi/Scene/Components/ReflectionProbeComponent.h

@@ -6,6 +6,8 @@
 #pragma once
 #pragma once
 
 
 #include <AnKi/Scene/Components/SceneComponent.h>
 #include <AnKi/Scene/Components/SceneComponent.h>
+#include <AnKi/Scene/Frustum.h>
+#include <AnKi/Scene/Spatial.h>
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Collision/Aabb.h>
 #include <AnKi/Collision/Aabb.h>
 
 
@@ -28,7 +30,7 @@ public:
 	void setBoxVolumeSize(const Vec3& sizeXYZ)
 	void setBoxVolumeSize(const Vec3& sizeXYZ)
 	{
 	{
 		m_halfSize = sizeXYZ / 2.0f;
 		m_halfSize = sizeXYZ / 2.0f;
-		m_markedForUpdate = true;
+		m_dirty = true;
 	}
 	}
 
 
 	Vec3 getBoxVolumeSize() const
 	Vec3 getBoxVolumeSize() const
@@ -36,29 +38,19 @@ public:
 		return m_halfSize * 2.0f;
 		return m_halfSize * 2.0f;
 	}
 	}
 
 
-	Vec3 getWorldPosition() const
-	{
-		return m_worldPos;
-	}
-
-	void setWorldPosition(const Vec3& pos)
+	Bool getMarkedForRendering() const
 	{
 	{
-		m_worldPos = pos;
-		m_markedForUpdate = true;
+		return m_markedForRendering;
 	}
 	}
 
 
-	Aabb getAabbWorldSpace() const
+	ANKI_INTERNAL WeakArray<Frustum> getFrustums()
 	{
 	{
-		return Aabb(-m_halfSize + m_worldPos, m_halfSize + m_worldPos);
-	}
-
-	Bool getMarkedForRendering() const
-	{
-		return m_markedForRendering;
+		return WeakArray<Frustum>(m_frustums);
 	}
 	}
 
 
 	void setupReflectionProbeQueueElement(ReflectionProbeQueueElement& el) const
 	void setupReflectionProbeQueueElement(ReflectionProbeQueueElement& el) const
 	{
 	{
+		ANKI_ASSERT(m_worldPos.x() != kMaxF32);
 		el.m_feedbackCallback = reflectionProbeQueueElementFeedbackCallback;
 		el.m_feedbackCallback = reflectionProbeQueueElementFeedbackCallback;
 		el.m_feedbackCallbackUserData = const_cast<ReflectionProbeComponent*>(this);
 		el.m_feedbackCallbackUserData = const_cast<ReflectionProbeComponent*>(this);
 		el.m_uuid = m_uuid;
 		el.m_uuid = m_uuid;
@@ -66,37 +58,29 @@ public:
 		el.m_aabbMin = -m_halfSize + m_worldPos;
 		el.m_aabbMin = -m_halfSize + m_worldPos;
 		el.m_aabbMax = m_halfSize + m_worldPos;
 		el.m_aabbMax = m_halfSize + m_worldPos;
 		el.m_textureArrayIndex = kMaxU32;
 		el.m_textureArrayIndex = kMaxU32;
-		el.m_debugDrawCallback = [](RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) {
-			ANKI_ASSERT(userData.getSize() == 1);
-			static_cast<const ReflectionProbeComponent*>(userData[0])->draw(ctx);
-		};
+		el.m_debugDrawCallback = nullptr;
 		el.m_debugDrawCallbackUserData = this;
 		el.m_debugDrawCallbackUserData = this;
 	}
 	}
 
 
 private:
 private:
-	SceneNode* m_node = nullptr;
 	U64 m_uuid = 0;
 	U64 m_uuid = 0;
-	Vec3 m_worldPos = Vec3(0.0f);
+	Vec3 m_worldPos = Vec3(kMaxF32);
 	Vec3 m_halfSize = Vec3(1.0f);
 	Vec3 m_halfSize = Vec3(1.0f);
-	Bool m_markedForRendering : 1;
-	Bool m_markedForUpdate : 1;
 
 
-	ImageResourcePtr m_debugImage;
+	Spatial m_spatial;
 
 
-	Error update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = m_markedForUpdate;
-		m_markedForUpdate = false;
-		return Error::kNone;
-	}
+	Array<Frustum, 6> m_frustums;
+
+	Bool m_markedForRendering : 1 = false;
+	Bool m_dirty : 1 = true;
+
+	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 
 
 	static void reflectionProbeQueueElementFeedbackCallback(Bool fillRenderQueuesOnNextFrame, void* userData)
 	static void reflectionProbeQueueElementFeedbackCallback(Bool fillRenderQueuesOnNextFrame, void* userData)
 	{
 	{
 		ANKI_ASSERT(userData);
 		ANKI_ASSERT(userData);
 		static_cast<ReflectionProbeComponent*>(userData)->m_markedForRendering = fillRenderQueuesOnNextFrame;
 		static_cast<ReflectionProbeComponent*>(userData)->m_markedForRendering = fillRenderQueuesOnNextFrame;
 	}
 	}
-
-	void draw(RenderQueueDrawContext& ctx) const;
 };
 };
 /// @}
 /// @}
 
 

+ 2 - 3
AnKi/Scene/Components/SceneComponent.cpp

@@ -82,9 +82,8 @@ SceneComponentRtti::SceneComponentRtti(const char* name, F32 updateWeight, U32 s
 	}
 	}
 }
 }
 
 
-SceneComponent::SceneComponent([[maybe_unused]] SceneNode* node, U8 classId, Bool isFeedbackComponent)
-	: m_classId(classId & 0x7F)
-	, m_feedbackComponent(isFeedbackComponent)
+SceneComponent::SceneComponent([[maybe_unused]] SceneNode* node, U8 classId)
+	: m_classId(classId)
 {
 {
 	ANKI_ASSERT(classId < g_sceneComponentClassCount);
 	ANKI_ASSERT(classId < g_sceneComponentClassCount);
 }
 }

+ 8 - 9
AnKi/Scene/Components/SceneComponent.h

@@ -118,7 +118,7 @@ class SceneComponent
 {
 {
 public:
 public:
 	/// Construct the scene component.
 	/// Construct the scene component.
-	SceneComponent(SceneNode* node, U8 classId, Bool isFeedbackComponent = false);
+	SceneComponent(SceneNode* node, U8 classId);
 
 
 	U8 getClassId() const
 	U8 getClassId() const
 	{
 	{
@@ -130,15 +130,15 @@ public:
 		return m_timestamp;
 		return m_timestamp;
 	}
 	}
 
 
-	Bool isFeedbackComponent() const
-	{
-		return m_feedbackComponent;
-	}
-
 	static const SceneComponentRtti& findClassRtti(CString className);
 	static const SceneComponentRtti& findClassRtti(CString className);
 
 
 	static const SceneComponentRtti& getClassRtti(U8 classId);
 	static const SceneComponentRtti& getClassRtti(U8 classId);
 
 
+	const SceneComponentRtti& getClassRtti() const
+	{
+		return getClassRtti(m_classId);
+	}
+
 	ANKI_INTERNAL void onDestroyReal(SceneNode& node)
 	ANKI_INTERNAL void onDestroyReal(SceneNode& node)
 	{
 	{
 		g_sceneComponentCallbacks.m_onDestroy[m_classId](*this, node);
 		g_sceneComponentCallbacks.m_onDestroy[m_classId](*this, node);
@@ -164,7 +164,7 @@ public:
 	}
 	}
 
 
 protected:
 protected:
-	static SceneGraphExternalSubsystems& getExternalSubsystems(const SceneNode& node);
+	ANKI_PURE static SceneGraphExternalSubsystems& getExternalSubsystems(const SceneNode& node);
 
 
 	/// Pseudo-virtual
 	/// Pseudo-virtual
 	void onDestroy([[maybe_unused]] SceneNode& node)
 	void onDestroy([[maybe_unused]] SceneNode& node)
@@ -190,8 +190,7 @@ protected:
 
 
 private:
 private:
 	Timestamp m_timestamp = 1; ///< Indicates when an update happened
 	Timestamp m_timestamp = 1; ///< Indicates when an update happened
-	U8 m_classId : 7; ///< Cache the type ID.
-	U8 m_feedbackComponent : 1;
+	U8 m_classId; ///< Cache the type ID.
 };
 };
 /// @}
 /// @}
 
 

+ 0 - 9
AnKi/Scene/Components/SceneComponentClasses.defs.h

@@ -7,13 +7,6 @@
 #	define ANKI_SCENE_COMPONENT_SEPERATOR
 #	define ANKI_SCENE_COMPONENT_SEPERATOR
 #endif
 #endif
 
 
-ANKI_DEFINE_SCENE_COMPONENT(Frustum, -1.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
-ANKI_DEFINE_SCENE_COMPONENT(GenericGpuComputeJob, -1.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
-ANKI_DEFINE_SCENE_COMPONENT(Spatial, -1.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
-
 ANKI_DEFINE_SCENE_COMPONENT(Script, 0.0f)
 ANKI_DEFINE_SCENE_COMPONENT(Script, 0.0f)
 ANKI_SCENE_COMPONENT_SEPERATOR
 ANKI_SCENE_COMPONENT_SEPERATOR
 
 
@@ -51,8 +44,6 @@ ANKI_DEFINE_SCENE_COMPONENT(Skybox, 100.0f)
 ANKI_SCENE_COMPONENT_SEPERATOR
 ANKI_SCENE_COMPONENT_SEPERATOR
 ANKI_DEFINE_SCENE_COMPONENT(Ui, 100.0f)
 ANKI_DEFINE_SCENE_COMPONENT(Ui, 100.0f)
 ANKI_SCENE_COMPONENT_SEPERATOR
 ANKI_SCENE_COMPONENT_SEPERATOR
-ANKI_DEFINE_SCENE_COMPONENT(GpuParticleEmitter, 100.0f)
-ANKI_SCENE_COMPONENT_SEPERATOR
 ANKI_DEFINE_SCENE_COMPONENT(LensFlare, 100.0f)
 ANKI_DEFINE_SCENE_COMPONENT(LensFlare, 100.0f)
 ANKI_SCENE_COMPONENT_SEPERATOR
 ANKI_SCENE_COMPONENT_SEPERATOR
 ANKI_DEFINE_SCENE_COMPONENT(Light, 100.0f)
 ANKI_DEFINE_SCENE_COMPONENT(Light, 100.0f)

+ 0 - 4
AnKi/Scene/Components/SceneComponentStatics.cpp

@@ -7,10 +7,7 @@
 #include <AnKi/Scene/Components/CameraComponent.h>
 #include <AnKi/Scene/Components/CameraComponent.h>
 #include <AnKi/Scene/Components/DecalComponent.h>
 #include <AnKi/Scene/Components/DecalComponent.h>
 #include <AnKi/Scene/Components/FogDensityComponent.h>
 #include <AnKi/Scene/Components/FogDensityComponent.h>
-#include <AnKi/Scene/Components/FrustumComponent.h>
-#include <AnKi/Scene/Components/GenericGpuComputeJobComponent.h>
 #include <AnKi/Scene/Components/GlobalIlluminationProbeComponent.h>
 #include <AnKi/Scene/Components/GlobalIlluminationProbeComponent.h>
-#include <AnKi/Scene/Components/GpuParticleEmitterComponent.h>
 #include <AnKi/Scene/Components/JointComponent.h>
 #include <AnKi/Scene/Components/JointComponent.h>
 #include <AnKi/Scene/Components/LensFlareComponent.h>
 #include <AnKi/Scene/Components/LensFlareComponent.h>
 #include <AnKi/Scene/Components/LightComponent.h>
 #include <AnKi/Scene/Components/LightComponent.h>
@@ -22,7 +19,6 @@
 #include <AnKi/Scene/Components/ScriptComponent.h>
 #include <AnKi/Scene/Components/ScriptComponent.h>
 #include <AnKi/Scene/Components/SkinComponent.h>
 #include <AnKi/Scene/Components/SkinComponent.h>
 #include <AnKi/Scene/Components/SkyboxComponent.h>
 #include <AnKi/Scene/Components/SkyboxComponent.h>
-#include <AnKi/Scene/Components/SpatialComponent.h>
 #include <AnKi/Scene/Components/TriggerComponent.h>
 #include <AnKi/Scene/Components/TriggerComponent.h>
 #include <AnKi/Scene/Components/UiComponent.h>
 #include <AnKi/Scene/Components/UiComponent.h>
 
 

+ 23 - 8
AnKi/Scene/Components/ScriptComponent.cpp

@@ -24,23 +24,38 @@ ScriptComponent::~ScriptComponent()
 	deleteInstance(m_node->getMemoryPool(), m_env);
 	deleteInstance(m_node->getMemoryPool(), m_env);
 }
 }
 
 
-Error ScriptComponent::loadScriptResource(CString fname)
+void ScriptComponent::loadScriptResource(CString fname)
 {
 {
 	// Load
 	// Load
-	ANKI_CHECK(getExternalSubsystems(*m_node).m_resourceManager->loadResource(fname, m_script));
+	ScriptResourcePtr rsrc;
+	Error err = getExternalSubsystems(*m_node).m_resourceManager->loadResource(fname, rsrc);
 
 
 	// Create the env
 	// Create the env
-	if(m_env)
+	ScriptEnvironment* newEnv = nullptr;
+	if(!err)
 	{
 	{
-		deleteInstance(m_node->getMemoryPool(), m_env);
+		newEnv = newInstance<ScriptEnvironment>(m_node->getMemoryPool());
+		err = newEnv->init(getExternalSubsystems(*m_node).m_scriptManager);
 	}
 	}
-	m_env = newInstance<ScriptEnvironment>(m_node->getMemoryPool());
-	ANKI_CHECK(m_env->init(getExternalSubsystems(*m_node).m_scriptManager));
 
 
 	// Exec the script
 	// Exec the script
-	ANKI_CHECK(m_env->evalString(m_script->getSource()));
+	if(!err)
+	{
+		err = newEnv->evalString(m_script->getSource());
+	}
 
 
-	return Error::kNone;
+	// Error
+	if(err)
+	{
+		ANKI_SCENE_LOGE("Failed to load the script");
+		deleteInstance(m_node->getMemoryPool(), newEnv);
+	}
+	else
+	{
+		m_script = std::move(rsrc);
+		deleteInstance(m_node->getMemoryPool(), m_env);
+		m_env = newEnv;
+	}
 }
 }
 
 
 Error ScriptComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 Error ScriptComponent::update(SceneComponentUpdateInfo& info, Bool& updated)

+ 1 - 1
AnKi/Scene/Components/ScriptComponent.h

@@ -24,7 +24,7 @@ public:
 
 
 	~ScriptComponent();
 	~ScriptComponent();
 
 
-	Error loadScriptResource(CString fname);
+	void loadScriptResource(CString fname);
 
 
 	Bool isEnabled() const
 	Bool isEnabled() const
 	{
 	{

+ 14 - 5
AnKi/Scene/Components/SkinComponent.cpp

@@ -28,9 +28,19 @@ SkinComponent::~SkinComponent()
 	getExternalSubsystems(*m_node).m_gpuSceneMemoryPool->free(m_boneTransformsGpuSceneOffset);
 	getExternalSubsystems(*m_node).m_gpuSceneMemoryPool->free(m_boneTransformsGpuSceneOffset);
 }
 }
 
 
-Error SkinComponent::loadSkeletonResource(CString fname)
+void SkinComponent::loadSkeletonResource(CString fname)
 {
 {
-	ANKI_CHECK(getExternalSubsystems(*m_node).m_resourceManager->loadResource(fname, m_skeleton));
+	SkeletonResourcePtr rsrc;
+	const Error err = getExternalSubsystems(*m_node).m_resourceManager->loadResource(fname, rsrc);
+	if(err)
+	{
+		ANKI_SCENE_LOGE("Failed to load skeleton");
+		return;
+	}
+
+	m_forceFullUpdate = true;
+
+	m_skeleton = std::move(rsrc);
 
 
 	// Cleanup
 	// Cleanup
 	m_boneTrfs[0].destroy(m_node->getMemoryPool());
 	m_boneTrfs[0].destroy(m_node->getMemoryPool());
@@ -46,8 +56,6 @@ Error SkinComponent::loadSkeletonResource(CString fname)
 
 
 	getExternalSubsystems(*m_node).m_gpuSceneMemoryPool->allocate(sizeof(Mat4) * boneCount * 2, 4,
 	getExternalSubsystems(*m_node).m_gpuSceneMemoryPool->allocate(sizeof(Mat4) * boneCount * 2, 4,
 																  m_boneTransformsGpuSceneOffset);
 																  m_boneTransformsGpuSceneOffset);
-
-	return Error::kNone;
 }
 }
 
 
 void SkinComponent::playAnimation(U32 track, AnimationResourcePtr anim, const AnimationPlayInfo& info)
 void SkinComponent::playAnimation(U32 track, AnimationResourcePtr anim, const AnimationPlayInfo& info)
@@ -174,7 +182,8 @@ Error SkinComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 	}
 	}
 
 
 	// Always update the 1st time
 	// Always update the 1st time
-	updated = updated || (m_absoluteTime == 0.0);
+	updated = updated || m_forceFullUpdate;
+	m_forceFullUpdate = false;
 
 
 	if(updated)
 	if(updated)
 	{
 	{

+ 3 - 1
AnKi/Scene/Components/SkinComponent.h

@@ -47,7 +47,7 @@ public:
 	~SkinComponent();
 	~SkinComponent();
 
 
 	/// Load the skeleton resource.
 	/// Load the skeleton resource.
-	Error loadSkeletonResource(CString filename);
+	void loadSkeletonResource(CString filename);
 
 
 	void playAnimation(U32 track, AnimationResourcePtr anim, const AnimationPlayInfo& info);
 	void playAnimation(U32 track, AnimationResourcePtr anim, const AnimationPlayInfo& info);
 
 
@@ -111,6 +111,8 @@ private:
 	U8 m_crntBoneTrfs = 0;
 	U8 m_crntBoneTrfs = 0;
 	U8 m_prevBoneTrfs = 1;
 	U8 m_prevBoneTrfs = 1;
 
 
+	Bool m_forceFullUpdate = true;
+
 	SegregatedListsGpuMemoryPoolToken m_boneTransformsGpuSceneOffset;
 	SegregatedListsGpuMemoryPoolToken m_boneTransformsGpuSceneOffset;
 
 
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 	Error update(SceneComponentUpdateInfo& info, Bool& updated);

+ 16 - 6
AnKi/Scene/Components/SkyboxComponent.cpp

@@ -15,24 +15,34 @@ namespace anki {
 SkyboxComponent::SkyboxComponent(SceneNode* node)
 SkyboxComponent::SkyboxComponent(SceneNode* node)
 	: SceneComponent(node, getStaticClassId())
 	: SceneComponent(node, getStaticClassId())
 	, m_node(node)
 	, m_node(node)
+	, m_spatial(this)
 {
 {
+	m_spatial.setAlwaysVisible(true);
+	m_spatial.setUpdatesOctreeBounds(false);
 }
 }
 
 
 SkyboxComponent::~SkyboxComponent()
 SkyboxComponent::~SkyboxComponent()
 {
 {
 }
 }
 
 
-void SkyboxComponent::setImage(CString filename)
+void SkyboxComponent::loadImageResource(CString filename)
 {
 {
-	const Error err = getExternalSubsystems(*m_node).m_resourceManager->loadResource(filename, m_image);
+	ImageResourcePtr img;
+	const Error err = getExternalSubsystems(*m_node).m_resourceManager->loadResource(filename, img);
 	if(err)
 	if(err)
 	{
 	{
 		ANKI_SCENE_LOGE("Setting skybox image failed. Ignoring error");
 		ANKI_SCENE_LOGE("Setting skybox image failed. Ignoring error");
+		return;
 	}
 	}
-	else
-	{
-		m_type = SkyboxType::kImage2D;
-	}
+
+	m_image = std::move(img);
+	m_type = SkyboxType::kImage2D;
+}
+
+Error SkyboxComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
+{
+	updated = m_spatial.update(info.m_node->getSceneGraph().getOctree());
+	return Error::kNone;
 }
 }
 
 
 void SkyboxComponent::setupSkyboxQueueElement(SkyboxQueueElement& queueElement) const
 void SkyboxComponent::setupSkyboxQueueElement(SkyboxQueueElement& queueElement) const

+ 5 - 1
AnKi/Scene/Components/SkyboxComponent.h

@@ -6,6 +6,7 @@
 #pragma once
 #pragma once
 
 
 #include <AnKi/Scene/Components/SceneComponent.h>
 #include <AnKi/Scene/Components/SceneComponent.h>
+#include <AnKi/Scene/Spatial.h>
 #include <AnKi/Resource/Forward.h>
 #include <AnKi/Resource/Forward.h>
 #include <AnKi/Math.h>
 #include <AnKi/Math.h>
 
 
@@ -40,7 +41,7 @@ public:
 		m_color = color.max(Vec3(0.0f));
 		m_color = color.max(Vec3(0.0f));
 	}
 	}
 
 
-	void setImage(CString filename);
+	void loadImageResource(CString filename);
 
 
 	void setMinFogDensity(F32 density)
 	void setMinFogDensity(F32 density)
 	{
 	{
@@ -118,6 +119,7 @@ public:
 
 
 private:
 private:
 	SceneNode* m_node;
 	SceneNode* m_node;
+	Spatial m_spatial;
 	SkyboxType m_type = SkyboxType::kSolidColor;
 	SkyboxType m_type = SkyboxType::kSolidColor;
 	Vec3 m_color = Vec3(0.0f, 0.0f, 0.5f);
 	Vec3 m_color = Vec3(0.0f, 0.0f, 0.5f);
 	ImageResourcePtr m_image;
 	ImageResourcePtr m_image;
@@ -134,6 +136,8 @@ private:
 		F32 m_absorptionCoeff = 0.02f;
 		F32 m_absorptionCoeff = 0.02f;
 		Vec3 m_diffuseColor = Vec3(1.0f);
 		Vec3 m_diffuseColor = Vec3(1.0f);
 	} m_fog;
 	} m_fog;
+
+	Error update(SceneComponentUpdateInfo& info, Bool& updated);
 };
 };
 /// @}
 /// @}
 
 

+ 0 - 113
AnKi/Scene/Components/SpatialComponent.cpp

@@ -1,113 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/Components/SpatialComponent.h>
-#include <AnKi/Scene/SceneNode.h>
-#include <AnKi/Scene/SceneGraph.h>
-
-namespace anki {
-
-SpatialComponent::SpatialComponent(SceneNode* node)
-	: SceneComponent(node, getStaticClassId())
-	, m_node(node)
-	, m_markedForUpdate(true)
-	, m_placed(false)
-	, m_updateOctreeBounds(true)
-	, m_alwaysVisible(false)
-{
-	ANKI_ASSERT(node);
-	m_octreeInfo.m_userData = this;
-	setAabbWorldSpace(Aabb(Vec3(-1.0f), Vec3(1.0f)));
-
-	getExternalSubsystems(*node).m_gpuSceneMemoryPool->allocate(sizeof(Vec3) * 2, alignof(F32), m_gpuSceneAabb);
-}
-
-SpatialComponent::~SpatialComponent()
-{
-	if(m_placed)
-	{
-		m_node->getSceneGraph().getOctree().remove(m_octreeInfo);
-	}
-
-	m_convexHullPoints.destroy(m_node->getMemoryPool());
-
-	getExternalSubsystems(*m_node).m_gpuSceneMemoryPool->free(m_gpuSceneAabb);
-}
-
-void SpatialComponent::setConvexHullWorldSpace(const ConvexHullShape& hull)
-{
-	ANKI_ASSERT(hull.getPoints().getSize() > 0);
-
-	if(m_convexHullPoints.getSize() != hull.getPoints().getSize())
-	{
-		m_convexHullPoints.resize(m_node->getMemoryPool(), hull.getPoints().getSize());
-	}
-
-	memcpy(&m_convexHullPoints[0], &hull.getPoints()[0], hull.getPoints().getSizeInBytes());
-
-	m_hull = ConvexHullShape(&m_convexHullPoints[0], m_convexHullPoints.getSize());
-	if(!hull.isTransformIdentity())
-	{
-		m_hull.setTransform(hull.getTransform());
-	}
-
-	m_collisionObjectType = hull.kClassType;
-	m_markedForUpdate = true;
-}
-
-Error SpatialComponent::update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
-{
-	ANKI_ASSERT(info.m_node == m_node);
-
-	updated = m_markedForUpdate;
-	if(updated)
-	{
-		if(!m_alwaysVisible)
-		{
-			// Compute the AABB
-			switch(m_collisionObjectType)
-			{
-			case CollisionShapeType::kAabb:
-				m_derivedAabb = m_aabb;
-				break;
-			case CollisionShapeType::kObb:
-				m_derivedAabb = computeAabb(m_obb);
-				break;
-			case CollisionShapeType::kSphere:
-				m_derivedAabb = computeAabb(m_sphere);
-				break;
-			case CollisionShapeType::kConvexHull:
-				m_derivedAabb = computeAabb(m_hull);
-				break;
-			default:
-				ANKI_ASSERT(0);
-			}
-
-			m_node->getSceneGraph().getOctree().place(m_derivedAabb, &m_octreeInfo, m_updateOctreeBounds);
-		}
-		else
-		{
-			m_node->getSceneGraph().getOctree().placeAlwaysVisible(&m_octreeInfo);
-
-			// Set something random
-			m_derivedAabb = Aabb(Vec3(-1.0f), Vec3(1.0f));
-		}
-
-		m_markedForUpdate = false;
-		m_placed = true;
-
-		// Update the GPU scene
-		Array<Vec3, 2> aabb;
-		aabb[0] = m_derivedAabb.getMin().xyz();
-		aabb[1] = m_derivedAabb.getMax().xyz();
-		info.m_gpuSceneMicroPatcher->newCopy(*info.m_framePool, m_gpuSceneAabb.m_offset, sizeof(aabb), &aabb[0]);
-	}
-
-	m_octreeInfo.reset();
-
-	return Error::kNone;
-}
-
-} // end namespace anki

+ 0 - 151
AnKi/Scene/Components/SpatialComponent.h

@@ -1,151 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <AnKi/Scene/Components/SceneComponent.h>
-#include <AnKi/Scene/Octree.h>
-#include <AnKi/Collision.h>
-#include <AnKi/Util/BitMask.h>
-#include <AnKi/Util/Enum.h>
-#include <AnKi/Util/List.h>
-
-namespace anki {
-
-/// @addtogroup scene
-/// @{
-
-/// Spatial component. It is used by scene nodes that need to be placed inside the visibility structures.
-class SpatialComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(SpatialComponent)
-
-public:
-	SpatialComponent(SceneNode* node);
-
-	~SpatialComponent();
-
-	void setObbWorldSpace(const Obb& obb)
-	{
-		m_obb = obb;
-		m_collisionObjectType = obb.kClassType;
-		m_markedForUpdate = true;
-	}
-
-	void setAabbWorldSpace(const Aabb& aabb)
-	{
-		m_aabb = aabb;
-		m_collisionObjectType = aabb.kClassType;
-		m_markedForUpdate = true;
-	}
-
-	void setSphereWorldSpace(const Sphere& sphere)
-	{
-		m_sphere = sphere;
-		m_collisionObjectType = sphere.kClassType;
-		m_markedForUpdate = true;
-	}
-
-	void setConvexHullWorldSpace(const ConvexHullShape& hull);
-
-	template<typename T>
-	const T& getCollisionShape() const
-	{
-		ANKI_ASSERT(T::kClassType == m_collisionObjectType);
-		return *reinterpret_cast<const T*>(&m_anyShape);
-	}
-
-	CollisionShapeType getCollisionShapeType() const
-	{
-		return m_collisionObjectType;
-	}
-
-	const Aabb& getAabbWorldSpace() const
-	{
-		ANKI_ASSERT(!m_alwaysVisible);
-		return m_derivedAabb;
-	}
-
-	const SceneNode& getSceneNode() const
-	{
-		return *m_node;
-	}
-
-	SceneNode& getSceneNode()
-	{
-		return *m_node;
-	}
-
-	/// Used for sorting spatials. In most object the origin is the center of mass but for cameras the origin is the
-	/// eye point.
-	const Vec3& getSpatialOrigin() const
-	{
-		ANKI_ASSERT(m_origin.x() != kMaxF32);
-		return m_origin;
-	}
-
-	/// See getSpatialOrigin()
-	void setSpatialOrigin(const Vec3& origin)
-	{
-		m_origin = origin;
-	}
-
-	/// Update the "actual scene bounds" of the octree or not.
-	void setUpdateOctreeBounds(Bool update)
-	{
-		m_updateOctreeBounds = update;
-	}
-
-	/// Make it or not always visible.
-	void setAlwaysVisible(Bool alwaysVisible)
-	{
-		m_alwaysVisible = alwaysVisible;
-	}
-
-	/// See if it's always visible or not.
-	Bool getAlwaysVisible() const
-	{
-		return m_alwaysVisible;
-	}
-
-	U32 getAabbGpuSceneOffset() const
-	{
-		ANKI_ASSERT((m_gpuSceneAabb.m_offset % 4) == 0);
-		return U32(m_gpuSceneAabb.m_offset);
-	}
-
-private:
-	SceneNode* m_node;
-
-	union
-	{
-		Obb m_obb;
-		Aabb m_aabb;
-		Sphere m_sphere;
-		ConvexHullShape m_hull;
-		U8 m_anyShape;
-	};
-
-	DynamicArray<Vec4> m_convexHullPoints;
-
-	CollisionShapeType m_collisionObjectType = CollisionShapeType::kCount;
-	Aabb m_derivedAabb; ///< A faster shape
-
-	Vec3 m_origin = Vec3(kMaxF32);
-
-	OctreePlaceable m_octreeInfo;
-
-	SegregatedListsGpuMemoryPoolToken m_gpuSceneAabb;
-
-	Bool m_markedForUpdate : 1;
-	Bool m_placed : 1;
-	Bool m_updateOctreeBounds : 1;
-	Bool m_alwaysVisible : 1;
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated);
-};
-/// @}
-
-} // end namespace anki

+ 20 - 6
AnKi/Scene/Components/TriggerComponent.cpp

@@ -86,19 +86,33 @@ TriggerComponent::~TriggerComponent()
 
 
 void TriggerComponent::setSphereVolumeRadius(F32 radius)
 void TriggerComponent::setSphereVolumeRadius(F32 radius)
 {
 {
+	// Need to re-create it
 	m_shape = getExternalSubsystems(*m_node).m_physicsWorld->newInstance<PhysicsSphere>(radius);
 	m_shape = getExternalSubsystems(*m_node).m_physicsWorld->newInstance<PhysicsSphere>(radius);
 	m_trigger = getExternalSubsystems(*m_node).m_physicsWorld->newInstance<PhysicsTrigger>(m_shape);
 	m_trigger = getExternalSubsystems(*m_node).m_physicsWorld->newInstance<PhysicsTrigger>(m_shape);
 	m_trigger->setUserData(this);
 	m_trigger->setUserData(this);
 	m_trigger->setContactProcessCallback(m_callbacks);
 	m_trigger->setContactProcessCallback(m_callbacks);
+	m_trigger->setTransform(m_node->getWorldTransform());
 }
 }
 
 
-Error TriggerComponent::update([[maybe_unused]] SceneComponentUpdateInfo& info, Bool& updated)
+Error TriggerComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 {
 {
-	updated = m_callbacks->m_updated;
-	m_callbacks->m_updated = false;
-	m_callbacks->m_enterUpdated = false;
-	m_callbacks->m_insideUpdated = false;
-	m_callbacks->m_exitUpdated = false;
+	updated = false;
+
+	if(m_trigger.isCreated()) [[likely]]
+	{
+		updated = m_callbacks->m_updated;
+		m_callbacks->m_updated = false;
+		m_callbacks->m_enterUpdated = false;
+		m_callbacks->m_insideUpdated = false;
+		m_callbacks->m_exitUpdated = false;
+
+		if(info.m_node->movedThisFrame() && m_trigger.isCreated())
+		{
+			updated = true;
+			m_trigger->setTransform(info.m_node->getWorldTransform());
+		}
+	}
+
 	return Error::kNone;
 	return Error::kNone;
 }
 }
 
 

+ 0 - 13
AnKi/Scene/Components/TriggerComponent.h

@@ -28,19 +28,6 @@ public:
 
 
 	void setSphereVolumeRadius(F32 radius);
 	void setSphereVolumeRadius(F32 radius);
 
 
-	Transform getWorldTransform() const
-	{
-		return (m_trigger.isCreated()) ? m_trigger->getTransform() : Transform::getIdentity();
-	}
-
-	void setWorldTransform(const Transform& trf)
-	{
-		if(m_trigger.isCreated())
-		{
-			m_trigger->setTransform(trf);
-		}
-	}
-
 	WeakArray<BodyComponent*> getBodyComponentsEnter()
 	WeakArray<BodyComponent*> getBodyComponentsEnter()
 	{
 	{
 		return WeakArray<BodyComponent*>(m_bodiesEnter);
 		return WeakArray<BodyComponent*>(m_bodiesEnter);

+ 8 - 0
AnKi/Scene/Components/UiComponent.cpp

@@ -4,7 +4,15 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <AnKi/Scene/Components/UiComponent.h>
 #include <AnKi/Scene/Components/UiComponent.h>
+#include <AnKi/Scene/SceneNode.h>
+#include <AnKi/Scene/SceneGraph.h>
 
 
 namespace anki {
 namespace anki {
 
 
+Error UiComponent::updateReal(SceneComponentUpdateInfo& info, Bool& updated)
+{
+	updated = m_spatial.update(info.m_node->getSceneGraph().getOctree());
+	return Error::kNone;
+}
+
 } // end namespace anki
 } // end namespace anki

+ 7 - 0
AnKi/Scene/Components/UiComponent.h

@@ -6,6 +6,7 @@
 #pragma once
 #pragma once
 
 
 #include <AnKi/Scene/Components/SceneComponent.h>
 #include <AnKi/Scene/Components/SceneComponent.h>
+#include <AnKi/Scene/Spatial.h>
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Renderer/RenderQueue.h>
 
 
 namespace anki {
 namespace anki {
@@ -21,7 +22,10 @@ class UiComponent : public SceneComponent
 public:
 public:
 	UiComponent(SceneNode* node)
 	UiComponent(SceneNode* node)
 		: SceneComponent(node, getStaticClassId())
 		: SceneComponent(node, getStaticClassId())
+		, m_spatial(this)
 	{
 	{
+		m_spatial.setAlwaysVisible(true);
+		m_spatial.setUpdatesOctreeBounds(false);
 	}
 	}
 
 
 	void init(UiQueueElementDrawCallback callback, void* userData)
 	void init(UiQueueElementDrawCallback callback, void* userData)
@@ -43,6 +47,9 @@ public:
 private:
 private:
 	UiQueueElementDrawCallback m_drawCallback = nullptr;
 	UiQueueElementDrawCallback m_drawCallback = nullptr;
 	void* m_userData = nullptr;
 	void* m_userData = nullptr;
+	Spatial m_spatial;
+
+	Error updateReal(SceneComponentUpdateInfo& info, Bool& updated);
 };
 };
 /// @}
 /// @}
 
 

+ 3 - 3
AnKi/Scene/ConfigVars.defs.h

@@ -19,9 +19,9 @@ ANKI_CONFIG_VAR_U32(SceneOctreeMaxDepth, 5, 2, 10, "The max depth of the octree"
 ANKI_CONFIG_VAR_F32(SceneEarlyZDistance, (ANKI_PLATFORM_MOBILE) ? 0.0f : 10.0f, 0.0f, kMaxF32,
 ANKI_CONFIG_VAR_F32(SceneEarlyZDistance, (ANKI_PLATFORM_MOBILE) ? 0.0f : 10.0f, 0.0f, kMaxF32,
 					"Objects with distance lower than that will be used in early Z")
 					"Objects with distance lower than that will be used in early Z")
 
 
-ANKI_CONFIG_VAR_F32(SceneReflectionProbeEffectiveDistance, 256.0f, 1.0f, kMaxF32, "How far reflection probes can look")
-ANKI_CONFIG_VAR_F32(SceneReflectionProbeShadowEffectiveDistance, 32.0f, 1.0f, kMaxF32,
-					"How far to render shadows for reflection probes")
+ANKI_CONFIG_VAR_F32(SceneProbeEffectiveDistance, 256.0f, 1.0f, kMaxF32, "How far various probes can render")
+ANKI_CONFIG_VAR_F32(SceneProbeShadowEffectiveDistance, 32.0f, 1.0f, kMaxF32,
+					"How far to render shadows for the various probes")
 
 
 ANKI_CONFIG_VAR_BOOL(SceneRayTracedShadows, true, "Enable or not ray traced shadows. Ignored if RT is not supported")
 ANKI_CONFIG_VAR_BOOL(SceneRayTracedShadows, true, "Enable or not ray traced shadows. Ignored if RT is not supported")
 ANKI_CONFIG_VAR_F32(SceneRayTracingExtendedFrustumDistance, 100.0f, 10.0f, 10000.0f,
 ANKI_CONFIG_VAR_F32(SceneRayTracingExtendedFrustumDistance, 100.0f, 10.0f, 10000.0f,

+ 0 - 93
AnKi/Scene/DecalNode.cpp

@@ -1,93 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/DecalNode.h>
-#include <AnKi/Scene/Components/DecalComponent.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
-#include <AnKi/Scene/Components/SpatialComponent.h>
-
-namespace anki {
-
-/// Decal feedback component.
-class DecalNode::MoveFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(DecalNode::MoveFeedbackComponent)
-
-public:
-	MoveFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		MoveComponent& movec = info.m_node->getFirstComponentOfType<MoveComponent>();
-		if(movec.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			static_cast<DecalNode&>(*info.m_node).onMove(movec);
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(DecalNode::MoveFeedbackComponent, -1.0f)
-
-/// Decal feedback component.
-class DecalNode::ShapeFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(DecalNode::ShapeFeedbackComponent)
-
-public:
-	ShapeFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		DecalComponent& decalc = info.m_node->getFirstComponentOfType<DecalComponent>();
-
-		if(decalc.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			static_cast<DecalNode&>(*info.m_node).onDecalUpdated(decalc);
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(DecalNode::ShapeFeedbackComponent, -1.0f)
-
-DecalNode::DecalNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-	newComponent<MoveComponent>();
-	newComponent<MoveFeedbackComponent>();
-	newComponent<DecalComponent>();
-	newComponent<ShapeFeedbackComponent>();
-	newComponent<SpatialComponent>();
-}
-
-DecalNode::~DecalNode()
-{
-}
-
-void DecalNode::onMove(MoveComponent& movec)
-{
-	getFirstComponentOfType<SpatialComponent>().setSpatialOrigin(movec.getWorldTransform().getOrigin().xyz());
-	getFirstComponentOfType<DecalComponent>().setWorldTransform(movec.getWorldTransform());
-}
-
-void DecalNode::onDecalUpdated(DecalComponent& decalc)
-{
-	getFirstComponentOfType<SpatialComponent>().setObbWorldSpace(decalc.getBoundingVolumeWorldSpace());
-}
-
-} // end namespace anki

+ 0 - 32
AnKi/Scene/DecalNode.h

@@ -1,32 +0,0 @@
-// Copyright (C) 2009-2022, 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>
-
-namespace anki {
-
-/// @addtogroup scene
-/// @{
-
-/// Node that has a decal component.
-class DecalNode : public SceneNode
-{
-public:
-	DecalNode(SceneGraph* scene, CString name);
-
-	~DecalNode();
-
-private:
-	class MoveFeedbackComponent;
-	class ShapeFeedbackComponent;
-
-	void onMove(MoveComponent& movec);
-	void onDecalUpdated(DecalComponent& decalc);
-};
-/// @}
-
-} // end namespace anki

+ 2 - 4
AnKi/Scene/Events/AnimationEvent.cpp

@@ -6,7 +6,6 @@
 #include <AnKi/Scene/Events/AnimationEvent.h>
 #include <AnKi/Scene/Events/AnimationEvent.h>
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Scene/SceneGraph.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
 #include <AnKi/Resource/ResourceManager.h>
 #include <AnKi/Resource/ResourceManager.h>
 
 
 namespace anki {
 namespace anki {
@@ -44,7 +43,7 @@ Error AnimationEvent::init(CString animationFilename, CString channelName, Scene
 	return Error::kNone;
 	return Error::kNone;
 }
 }
 
 
-Error AnimationEvent::update([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime)
+Error AnimationEvent::update([[maybe_unused]] Second prevUpdateTime, Second crntTime)
 {
 {
 	Vec3 pos;
 	Vec3 pos;
 	Quat rot;
 	Quat rot;
@@ -56,8 +55,7 @@ Error AnimationEvent::update([[maybe_unused]] Second prevUpdateTime, [[maybe_unu
 	trf.setRotation(Mat3x4(Vec3(0.0f), rot));
 	trf.setRotation(Mat3x4(Vec3(0.0f), rot));
 	trf.setScale(scale);
 	trf.setScale(scale);
 
 
-	MoveComponent& move = m_associatedNodes[0]->getFirstComponentOfType<MoveComponent>();
-	move.setLocalTransform(trf);
+	m_associatedNodes[0]->setLocalTransform(trf);
 
 
 	return Error::kNone;
 	return Error::kNone;
 }
 }

+ 3 - 9
AnKi/Scene/Events/JitterMoveEvent.cpp

@@ -5,7 +5,6 @@
 
 
 #include <AnKi/Scene/Events/JitterMoveEvent.h>
 #include <AnKi/Scene/Events/JitterMoveEvent.h>
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneNode.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
 #include <AnKi/Util/Functions.h>
 #include <AnKi/Util/Functions.h>
 
 
 namespace anki {
 namespace anki {
@@ -15,10 +14,7 @@ Error JitterMoveEvent::init(Second startTime, Second duration, SceneNode* node)
 	ANKI_ASSERT(node);
 	ANKI_ASSERT(node);
 	Event::init(startTime, duration);
 	Event::init(startTime, duration);
 	m_associatedNodes.emplaceBack(getMemoryPool(), node);
 	m_associatedNodes.emplaceBack(getMemoryPool(), node);
-
-	const MoveComponent& move = node->getFirstComponentOfType<MoveComponent>();
-
-	m_originalPos = move.getLocalTransform().getOrigin();
+	m_originalPos = node->getWorldTransform().getOrigin();
 
 
 	return Error::kNone;
 	return Error::kNone;
 }
 }
@@ -38,15 +34,13 @@ Error JitterMoveEvent::update([[maybe_unused]] Second prevUpdateTime, Second crn
 {
 {
 	SceneNode* node = m_associatedNodes[0];
 	SceneNode* node = m_associatedNodes[0];
 
 
-	MoveComponent& move = node->getFirstComponentOfType<MoveComponent>();
-
-	Transform trf = move.getLocalTransform();
+	Transform trf = node->getLocalTransform();
 
 
 	F32 factor = F32(sin(getDelta(crntTime) * kPi));
 	F32 factor = F32(sin(getDelta(crntTime) * kPi));
 
 
 	trf.getOrigin() = linearInterpolate(m_originalPos, m_newPos, factor);
 	trf.getOrigin() = linearInterpolate(m_originalPos, m_newPos, factor);
 
 
-	move.setLocalTransform(trf);
+	node->setLocalTransform(trf);
 
 
 	return Error::kNone;
 	return Error::kNone;
 }
 }

+ 0 - 100
AnKi/Scene/FogDensityNode.cpp

@@ -1,100 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/FogDensityNode.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
-#include <AnKi/Scene/Components/FogDensityComponent.h>
-#include <AnKi/Scene/Components/SpatialComponent.h>
-
-namespace anki {
-
-class FogDensityNode::MoveFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(FogDensityNode::MoveFeedbackComponent)
-
-public:
-	MoveFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		const MoveComponent& movec = info.m_node->getFirstComponentOfType<MoveComponent>();
-		if(movec.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			static_cast<FogDensityNode&>(*info.m_node).onMoveUpdated(movec);
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(FogDensityNode::MoveFeedbackComponent, -1.0f)
-
-class FogDensityNode::DensityShapeFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(FogDensityNode::DensityShapeFeedbackComponent)
-
-public:
-	DensityShapeFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		const FogDensityComponent& fogc = info.m_node->getFirstComponentOfType<FogDensityComponent>();
-		if(fogc.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			static_cast<FogDensityNode&>(*info.m_node).onDensityShapeUpdated(fogc);
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(FogDensityNode::DensityShapeFeedbackComponent, -1.0f)
-
-FogDensityNode::FogDensityNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-	newComponent<MoveComponent>();
-	newComponent<MoveFeedbackComponent>();
-	newComponent<FogDensityComponent>();
-	newComponent<DensityShapeFeedbackComponent>();
-	newComponent<SpatialComponent>();
-}
-
-FogDensityNode::~FogDensityNode()
-{
-}
-
-void FogDensityNode::onMoveUpdated(const MoveComponent& movec)
-{
-	getFirstComponentOfType<FogDensityComponent>().setWorldPosition(movec.getWorldTransform().getOrigin().xyz());
-	getFirstComponentOfType<SpatialComponent>().setSpatialOrigin(movec.getWorldTransform().getOrigin().xyz());
-}
-
-void FogDensityNode::onDensityShapeUpdated(const FogDensityComponent& fogc)
-{
-	SpatialComponent& spatialc = getFirstComponentOfType<SpatialComponent>();
-
-	if(fogc.isAabb())
-	{
-		spatialc.setAabbWorldSpace(fogc.getAabbWorldSpace());
-	}
-	else
-	{
-		ANKI_ASSERT(fogc.isSphere());
-		spatialc.setSphereWorldSpace(fogc.getSphereWorldSpace());
-	}
-}
-
-} // end namespace anki

+ 0 - 33
AnKi/Scene/FogDensityNode.h

@@ -1,33 +0,0 @@
-// Copyright (C) 2009-2022, 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/Collision/Aabb.h>
-
-namespace anki {
-
-/// @addtogroup scene
-/// @{
-
-/// Fog density node.
-class FogDensityNode : public SceneNode
-{
-public:
-	FogDensityNode(SceneGraph* scene, CString name);
-
-	~FogDensityNode();
-
-private:
-	class MoveFeedbackComponent;
-	class DensityShapeFeedbackComponent;
-
-	void onMoveUpdated(const MoveComponent& movec);
-	void onDensityShapeUpdated(const FogDensityComponent& fogc);
-};
-/// @}
-
-} // end namespace anki

+ 2 - 12
AnKi/Scene/Forward.h

@@ -9,18 +9,8 @@ namespace anki {
 
 
 // Components
 // Components
 class SceneComponent;
 class SceneComponent;
-class FrustumComponent;
-class InstanceComponent;
-class MoveComponent;
-class SpatialComponent;
-class DecalComponent;
-class ReflectionProxyComponent;
-class ReflectionProbeComponent;
-class FogDensityComponent;
-class SkinComponent;
-class ParticleEmitterComponent;
-class GpuParticleEmitterComponent;
-class ModelComponent;
+#define ANKI_DEFINE_SCENE_COMPONENT(name, updateOrder) class name##Component;
+#include <AnKi/Scene/Components/SceneComponentClasses.defs.h>
 
 
 // Nodes
 // Nodes
 class SceneNode;
 class SceneNode;

+ 25 - 15
AnKi/Scene/Frustum.cpp

@@ -8,6 +8,14 @@
 
 
 namespace anki {
 namespace anki {
 
 
+Array<Mat3x4, 6> Frustum::m_omnidirectionalRotations = {
+	Mat3x4(Vec3(0.0f), Mat3(Euler(0.0f, -kPi / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, kPi))),
+	Mat3x4(Vec3(0.0f), Mat3(Euler(0.0f, kPi / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, kPi))),
+	Mat3x4(Vec3(0.0f), Mat3(Euler(kPi / 2.0f, 0.0f, 0.0f))),
+	Mat3x4(Vec3(0.0f), Mat3(Euler(-kPi / 2.0f, 0.0f, 0.0f))),
+	Mat3x4(Vec3(0.0f), Mat3(Euler(0.0f, kPi, 0.0f)) * Mat3(Euler(0.0f, 0.0f, kPi))),
+	Mat3x4(Vec3(0.0f), Mat3(Euler(0.0f, 0.0f, kPi)))};
+
 Frustum::Frustum()
 Frustum::Frustum()
 {
 {
 	// Set some default values
 	// Set some default values
@@ -18,7 +26,7 @@ Frustum::Frustum()
 		m_maxLodDistances[i] = m_common.m_near + dist * F32(i + 1);
 		m_maxLodDistances[i] = m_common.m_near + dist * F32(i + 1);
 	}
 	}
 
 
-	update(true, Transform::getIdentity());
+	update();
 }
 }
 
 
 Frustum::~Frustum()
 Frustum::~Frustum()
@@ -47,7 +55,7 @@ void Frustum::init(FrustumType type, HeapMemoryPool* pool)
 	}
 	}
 }
 }
 
 
-Bool Frustum::update(Bool worldTransformUpdated, const Transform& worldTransform)
+Bool Frustum::update()
 {
 {
 	Bool updated = false;
 	Bool updated = false;
 
 
@@ -63,7 +71,7 @@ Bool Frustum::update(Bool worldTransformUpdated, const Transform& worldTransform
 	m_prevProjMats[0] = m_projMat;
 	m_prevProjMats[0] = m_projMat;
 
 
 	// Update the shape
 	// Update the shape
-	if(m_shapeMarkedForUpdate)
+	if(m_shapeDirty)
 	{
 	{
 		updated = true;
 		updated = true;
 
 
@@ -118,14 +126,14 @@ Bool Frustum::update(Bool worldTransformUpdated, const Transform& worldTransform
 	}
 	}
 
 
 	// Update transform related things
 	// Update transform related things
-	if(worldTransformUpdated)
+	if(m_worldTransformDirty)
 	{
 	{
 		updated = true;
 		updated = true;
-		m_viewMat = Mat3x4(worldTransform.getInverse());
+		m_viewMat = Mat3x4(m_worldTransform.getInverse());
 	}
 	}
 
 
 	// Fixup the misc data
 	// Fixup the misc data
-	if(m_miscMarkedForUpdate)
+	if(m_miscDirty)
 	{
 	{
 		updated = true;
 		updated = true;
 		const F32 frustumFraction = (m_common.m_far - m_common.m_near) / 100.0f;
 		const F32 frustumFraction = (m_common.m_far - m_common.m_near) / 100.0f;
@@ -162,27 +170,28 @@ Bool Frustum::update(Bool worldTransformUpdated, const Transform& worldTransform
 	if(updated)
 	if(updated)
 	{
 	{
 		m_viewProjMat = m_projMat * Mat4(m_viewMat, Vec4(0.0f, 0.0f, 0.0f, 1.0f));
 		m_viewProjMat = m_projMat * Mat4(m_viewMat, Vec4(0.0f, 0.0f, 0.0f, 1.0f));
-		m_shapeMarkedForUpdate = false;
-		m_miscMarkedForUpdate = false;
+		m_shapeDirty = false;
+		m_miscDirty = false;
+		m_worldTransformDirty = false;
 
 
 		if(m_frustumType == FrustumType::kPerspective)
 		if(m_frustumType == FrustumType::kPerspective)
 		{
 		{
-			m_perspective.m_edgesW[0] = worldTransform.getOrigin();
-			m_perspective.m_edgesW[1] = worldTransform.transform(m_perspective.m_edgesL[0]);
-			m_perspective.m_edgesW[2] = worldTransform.transform(m_perspective.m_edgesL[1]);
-			m_perspective.m_edgesW[3] = worldTransform.transform(m_perspective.m_edgesL[2]);
-			m_perspective.m_edgesW[4] = worldTransform.transform(m_perspective.m_edgesL[3]);
+			m_perspective.m_edgesW[0] = m_worldTransform.getOrigin();
+			m_perspective.m_edgesW[1] = m_worldTransform.transform(m_perspective.m_edgesL[0]);
+			m_perspective.m_edgesW[2] = m_worldTransform.transform(m_perspective.m_edgesL[1]);
+			m_perspective.m_edgesW[3] = m_worldTransform.transform(m_perspective.m_edgesL[2]);
+			m_perspective.m_edgesW[4] = m_worldTransform.transform(m_perspective.m_edgesL[3]);
 
 
 			m_perspective.m_hull = ConvexHullShape(&m_perspective.m_edgesW[0], m_perspective.m_edgesW.getSize());
 			m_perspective.m_hull = ConvexHullShape(&m_perspective.m_edgesW[0], m_perspective.m_edgesW.getSize());
 		}
 		}
 		else
 		else
 		{
 		{
-			m_ortho.m_obbW = m_ortho.m_obbL.getTransformed(worldTransform);
+			m_ortho.m_obbW = m_ortho.m_obbL.getTransformed(m_worldTransform);
 		}
 		}
 
 
 		for(FrustumPlaneType planeId : EnumIterable<FrustumPlaneType>())
 		for(FrustumPlaneType planeId : EnumIterable<FrustumPlaneType>())
 		{
 		{
-			m_viewPlanesW[planeId] = m_viewPlanesL[planeId].getTransformed(worldTransform);
+			m_viewPlanesW[planeId] = m_viewPlanesL[planeId].getTransformed(m_worldTransform);
 		}
 		}
 	}
 	}
 
 
@@ -191,6 +200,7 @@ Bool Frustum::update(Bool worldTransformUpdated, const Transform& worldTransform
 
 
 void Frustum::setCoverageBuffer(F32* depths, U32 width, U32 height)
 void Frustum::setCoverageBuffer(F32* depths, U32 width, U32 height)
 {
 {
+	ANKI_ASSERT(m_pool);
 	const U32 elemCount = width * height;
 	const U32 elemCount = width * height;
 	if(m_depthMap.getSize() != elemCount) [[unlikely]]
 	if(m_depthMap.getSize() != elemCount) [[unlikely]]
 	{
 	{

+ 50 - 15
AnKi/Scene/Frustum.h

@@ -15,7 +15,7 @@ namespace anki {
 /// @addtogroup scene
 /// @addtogroup scene
 /// @{
 /// @{
 
 
-/// TODO
+/// A helper class that represents a frustum.
 class Frustum
 class Frustum
 {
 {
 public:
 public:
@@ -55,7 +55,7 @@ public:
 	{
 	{
 		ANKI_ASSERT(near > kEpsilonf);
 		ANKI_ASSERT(near > kEpsilonf);
 		m_common.m_near = near;
 		m_common.m_near = near;
-		m_shapeMarkedForUpdate = true;
+		m_shapeDirty = true;
 	}
 	}
 
 
 	F32 getNear() const
 	F32 getNear() const
@@ -67,7 +67,7 @@ public:
 	{
 	{
 		ANKI_ASSERT(far > kEpsilonf);
 		ANKI_ASSERT(far > kEpsilonf);
 		m_common.m_far = far;
 		m_common.m_far = far;
-		m_shapeMarkedForUpdate = true;
+		m_shapeDirty = true;
 	}
 	}
 
 
 	F32 getFar() const
 	F32 getFar() const
@@ -78,7 +78,7 @@ public:
 	void setFovX(F32 fovx)
 	void setFovX(F32 fovx)
 	{
 	{
 		ANKI_ASSERT(m_frustumType == FrustumType::kPerspective && fovx > 0.0f && fovx < kPi);
 		ANKI_ASSERT(m_frustumType == FrustumType::kPerspective && fovx > 0.0f && fovx < kPi);
-		m_shapeMarkedForUpdate = true;
+		m_shapeDirty = true;
 		m_perspective.m_fovX = fovx;
 		m_perspective.m_fovX = fovx;
 	}
 	}
 
 
@@ -91,7 +91,7 @@ public:
 	void setFovY(F32 fovy)
 	void setFovY(F32 fovy)
 	{
 	{
 		ANKI_ASSERT(m_frustumType == FrustumType::kPerspective && fovy > 0.0f && fovy < kPi);
 		ANKI_ASSERT(m_frustumType == FrustumType::kPerspective && fovy > 0.0f && fovy < kPi);
-		m_shapeMarkedForUpdate = true;
+		m_shapeDirty = true;
 		m_perspective.m_fovY = fovy;
 		m_perspective.m_fovY = fovy;
 	}
 	}
 
 
@@ -110,7 +110,7 @@ public:
 	void setLeft(F32 value)
 	void setLeft(F32 value)
 	{
 	{
 		ANKI_ASSERT(m_frustumType == FrustumType::kOrthographic);
 		ANKI_ASSERT(m_frustumType == FrustumType::kOrthographic);
-		m_shapeMarkedForUpdate = true;
+		m_shapeDirty = true;
 		m_ortho.m_left = value;
 		m_ortho.m_left = value;
 	}
 	}
 
 
@@ -123,7 +123,7 @@ public:
 	void setRight(F32 value)
 	void setRight(F32 value)
 	{
 	{
 		ANKI_ASSERT(m_frustumType == FrustumType::kOrthographic);
 		ANKI_ASSERT(m_frustumType == FrustumType::kOrthographic);
-		m_shapeMarkedForUpdate = true;
+		m_shapeDirty = true;
 		m_ortho.m_right = value;
 		m_ortho.m_right = value;
 	}
 	}
 
 
@@ -136,7 +136,7 @@ public:
 	void setTop(F32 value)
 	void setTop(F32 value)
 	{
 	{
 		ANKI_ASSERT(m_frustumType == FrustumType::kOrthographic);
 		ANKI_ASSERT(m_frustumType == FrustumType::kOrthographic);
-		m_shapeMarkedForUpdate = true;
+		m_shapeDirty = true;
 		m_ortho.m_top = value;
 		m_ortho.m_top = value;
 	}
 	}
 
 
@@ -149,7 +149,7 @@ public:
 	void setBottom(F32 value)
 	void setBottom(F32 value)
 	{
 	{
 		ANKI_ASSERT(m_frustumType == FrustumType::kOrthographic);
 		ANKI_ASSERT(m_frustumType == FrustumType::kOrthographic);
-		m_shapeMarkedForUpdate = true;
+		m_shapeDirty = true;
 		m_ortho.m_bottom = value;
 		m_ortho.m_bottom = value;
 	}
 	}
 
 
@@ -227,7 +227,7 @@ public:
 	void setShadowCascadeDistance(U32 cascade, F32 distance)
 	void setShadowCascadeDistance(U32 cascade, F32 distance)
 	{
 	{
 		m_shadowCascadeDistances[cascade] = distance;
 		m_shadowCascadeDistances[cascade] = distance;
-		m_miscMarkedForUpdate = true;
+		m_miscDirty = true;
 	}
 	}
 
 
 	F32 getShadowCascadeDistance(U32 cascade) const
 	F32 getShadowCascadeDistance(U32 cascade) const
@@ -244,7 +244,7 @@ public:
 	{
 	{
 		ANKI_ASSERT(count <= kMaxShadowCascades);
 		ANKI_ASSERT(count <= kMaxShadowCascades);
 		m_shadowCascadeCount = U8(count);
 		m_shadowCascadeCount = U8(count);
-		m_miscMarkedForUpdate = true;
+		m_miscDirty = true;
 	}
 	}
 
 
 	const ConvexHullShape& getPerspectiveBoundingShapeWorldSpace() const
 	const ConvexHullShape& getPerspectiveBoundingShapeWorldSpace() const
@@ -268,7 +268,7 @@ public:
 	void setLodDistance(U32 lod, F32 maxDistance)
 	void setLodDistance(U32 lod, F32 maxDistance)
 	{
 	{
 		m_maxLodDistances[lod] = maxDistance;
 		m_maxLodDistances[lod] = maxDistance;
-		m_miscMarkedForUpdate = true;
+		m_miscDirty = true;
 	}
 	}
 
 
 	void setLodDistances(const Array<F32, kMaxLodCount>& distances)
 	void setLodDistances(const Array<F32, kMaxLodCount>& distances)
@@ -287,7 +287,35 @@ public:
 		return m_maxLodDistances[lod];
 		return m_maxLodDistances[lod];
 	}
 	}
 
 
-	Bool update(Bool worldTransformUpdated, const Transform& worldTransform);
+	void setWorldTransform(const Transform& worldTransform)
+	{
+		m_worldTransform = worldTransform;
+		m_worldTransformDirty = true;
+	}
+
+	const Transform& getWorldTransform() const
+	{
+		return m_worldTransform;
+	}
+
+	F32 getEarlyZDistance() const
+	{
+		return m_earlyZDistance;
+	}
+
+	void setEarlyZDistance(F32 dist)
+	{
+		ANKI_ASSERT(dist > 0.0f);
+		m_earlyZDistance = dist;
+	}
+
+	Bool update();
+
+	/// Get the precalculated rotations of each of the 6 frustums of an omnidirectional source (eg a point light).
+	static const Array<Mat3x4, 6>& getOmnidirectionalFrustumRotations()
+	{
+		return m_omnidirectionalRotations;
+	}
 
 
 private:
 private:
 	class Common
 	class Common
@@ -335,6 +363,8 @@ private:
 	Array<Plane, U32(FrustumPlaneType::kCount)> m_viewPlanesL;
 	Array<Plane, U32(FrustumPlaneType::kCount)> m_viewPlanesL;
 	Array<Plane, U32(FrustumPlaneType::kCount)> m_viewPlanesW;
 	Array<Plane, U32(FrustumPlaneType::kCount)> m_viewPlanesW;
 
 
+	Transform m_worldTransform = Transform::getIdentity();
+
 	Mat4 m_projMat = Mat4::getIdentity(); ///< Projection matrix
 	Mat4 m_projMat = Mat4::getIdentity(); ///< Projection matrix
 	Mat3x4 m_viewMat = Mat3x4::getIdentity(); ///< View matrix
 	Mat3x4 m_viewMat = Mat3x4::getIdentity(); ///< View matrix
 	Mat4 m_viewProjMat = Mat4::getIdentity(); ///< View projection matrix
 	Mat4 m_viewProjMat = Mat4::getIdentity(); ///< View projection matrix
@@ -347,6 +377,8 @@ private:
 	Array<F32, kMaxShadowCascades> m_shadowCascadeDistances = {};
 	Array<F32, kMaxShadowCascades> m_shadowCascadeDistances = {};
 	Array<F32, kMaxLodCount - 1> m_maxLodDistances = {};
 	Array<F32, kMaxLodCount - 1> m_maxLodDistances = {};
 
 
+	F32 m_earlyZDistance = 0.0f;
+
 	// Coverage buffer stuff
 	// Coverage buffer stuff
 	DynamicArray<F32> m_depthMap;
 	DynamicArray<F32> m_depthMap;
 	U32 m_depthMapWidth = 0;
 	U32 m_depthMapWidth = 0;
@@ -356,8 +388,11 @@ private:
 
 
 	U8 m_shadowCascadeCount = 0;
 	U8 m_shadowCascadeCount = 0;
 
 
-	Bool m_shapeMarkedForUpdate : 1 = true;
-	Bool m_miscMarkedForUpdate : 1 = true;
+	Bool m_shapeDirty : 1 = true;
+	Bool m_miscDirty : 1 = true;
+	Bool m_worldTransformDirty : 1 = true;
+
+	static Array<Mat3x4, 6> m_omnidirectionalRotations;
 };
 };
 /// @}
 /// @}
 
 

+ 0 - 205
AnKi/Scene/GlobalIlluminationProbeNode.cpp

@@ -1,205 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/GlobalIlluminationProbeNode.h>
-#include <AnKi/Scene/Components/FrustumComponent.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
-#include <AnKi/Scene/Components/SpatialComponent.h>
-#include <AnKi/Scene/Components/GlobalIlluminationProbeComponent.h>
-#include <AnKi/Core/ConfigSet.h>
-
-namespace anki {
-
-/// Feedback component
-class GlobalIlluminationProbeNode::MoveFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(GlobalIlluminationProbeNode::MoveFeedbackComponent)
-
-public:
-	MoveFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		MoveComponent& move = info.m_node->getFirstComponentOfType<MoveComponent>();
-		if(move.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			// Move updated
-			GlobalIlluminationProbeNode& dnode = static_cast<GlobalIlluminationProbeNode&>(*info.m_node);
-			dnode.onMoveUpdate(move);
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(GlobalIlluminationProbeNode::MoveFeedbackComponent, -1.0f)
-
-/// Feedback component
-class GlobalIlluminationProbeNode::ShapeFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(GlobalIlluminationProbeNode::ShapeFeedbackComponent)
-
-public:
-	ShapeFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(),
-						 false // Not feedback component. Can't be skipped because of getMarkedForRendering()
-		)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		GlobalIlluminationProbeComponent& probec =
-			info.m_node->getFirstComponentOfType<GlobalIlluminationProbeComponent>();
-		if(probec.getTimestamp() == info.m_node->getGlobalTimestamp() || probec.getMarkedForRendering())
-		{
-			// Move updated
-			GlobalIlluminationProbeNode& dnode = static_cast<GlobalIlluminationProbeNode&>(*info.m_node);
-			dnode.onShapeUpdateOrProbeNeedsRendering();
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(GlobalIlluminationProbeNode::ShapeFeedbackComponent, -1.0f)
-
-GlobalIlluminationProbeNode::GlobalIlluminationProbeNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-	// Move component first
-	newComponent<MoveComponent>();
-
-	// Feedback component
-	newComponent<MoveFeedbackComponent>();
-
-	// GI probe comp
-	newComponent<GlobalIlluminationProbeComponent>();
-
-	// Second feedback component
-	newComponent<ShapeFeedbackComponent>();
-
-	// The frustum components
-	constexpr F32 ang = toRad(90.0f);
-	constexpr F32 zNear = kClusterObjectFrustumNearPlane;
-
-	Mat3 rot;
-	rot = Mat3(Euler(0.0f, -kPi / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, kPi));
-	m_cubeFaceTransforms[0].setRotation(Mat3x4(Vec3(0.0f), rot));
-	rot = Mat3(Euler(0.0f, kPi / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, kPi));
-	m_cubeFaceTransforms[1].setRotation(Mat3x4(Vec3(0.0f), rot));
-	rot = Mat3(Euler(kPi / 2.0f, 0.0f, 0.0f));
-	m_cubeFaceTransforms[2].setRotation(Mat3x4(Vec3(0.0f), rot));
-	rot = Mat3(Euler(-kPi / 2.0f, 0.0f, 0.0f));
-	m_cubeFaceTransforms[3].setRotation(Mat3x4(Vec3(0.0f), rot));
-	rot = Mat3(Euler(0.0f, kPi, 0.0f)) * Mat3(Euler(0.0f, 0.0f, kPi));
-	m_cubeFaceTransforms[4].setRotation(Mat3x4(Vec3(0.0f), rot));
-	rot = Mat3(Euler(0.0f, 0.0f, kPi));
-	m_cubeFaceTransforms[5].setRotation(Mat3x4(Vec3(0.0f), rot));
-
-	for(U i = 0; i < 6; ++i)
-	{
-		m_cubeFaceTransforms[i].setOrigin(Vec4(0.0f));
-		m_cubeFaceTransforms[i].setScale(1.0f);
-
-		FrustumComponent* frc = newComponent<FrustumComponent>();
-		frc->setFrustumType(FrustumType::kPerspective);
-		frc->setPerspective(zNear, 10.0f, ang, ang);
-		frc->setLodDistances({1.0f, 2.0f, 3.0f});
-		frc->setWorldTransform(m_cubeFaceTransforms[i]);
-		frc->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::kNone);
-		frc->setShadowCascadeDistance(0, 10.0f);
-		frc->setShadowCascadeCount(1);
-	}
-
-	// Spatial component
-	SpatialComponent* spatialc = newComponent<SpatialComponent>();
-	spatialc->setUpdateOctreeBounds(false);
-}
-
-GlobalIlluminationProbeNode::~GlobalIlluminationProbeNode()
-{
-}
-
-void GlobalIlluminationProbeNode::onMoveUpdate(MoveComponent& move)
-{
-	GlobalIlluminationProbeComponent& gic = getFirstComponentOfType<GlobalIlluminationProbeComponent>();
-	gic.setWorldPosition(move.getWorldTransform().getOrigin().xyz());
-
-	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
-	sp.setSpatialOrigin(move.getWorldTransform().getOrigin().xyz());
-}
-
-void GlobalIlluminationProbeNode::onShapeUpdateOrProbeNeedsRendering()
-{
-	GlobalIlluminationProbeComponent& gic = getFirstComponentOfType<GlobalIlluminationProbeComponent>();
-
-	// Update the frustum component if the shape needs rendering
-	if(gic.getMarkedForRendering())
-	{
-		// Compute effective distance
-		F32 effectiveDistance = max(gic.getBoxVolumeSize().x(), gic.getBoxVolumeSize().y());
-		effectiveDistance = max(effectiveDistance, gic.getBoxVolumeSize().z());
-		effectiveDistance =
-			max(effectiveDistance, getExternalSubsystems().m_config->getSceneReflectionProbeEffectiveDistance());
-
-		// Update frustum components
-		U count = 0;
-		iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) {
-			Transform trf = m_cubeFaceTransforms[count];
-			trf.setOrigin(gic.getRenderPosition().xyz0());
-
-			frc.setWorldTransform(trf);
-			frc.setFar(effectiveDistance);
-			frc.setShadowCascadeDistance(
-				0, min(effectiveDistance,
-					   getExternalSubsystems().m_config->getSceneReflectionProbeShadowEffectiveDistance()));
-
-			// Add something to avoid complains
-			frc.setLodDistances(
-				{frc.getNear() + kEpsilonf, frc.getNear() + 2.0f * kEpsilonf, frc.getNear() + 3.0f * kEpsilonf});
-			++count;
-		});
-
-		ANKI_ASSERT(count == 6);
-	}
-
-	// Update the spatial comp
-	const Bool shapeWasUpdated = gic.getTimestamp() == getGlobalTimestamp();
-	if(shapeWasUpdated)
-	{
-		// Update only when the shape was actually update
-
-		SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
-		sp.setAabbWorldSpace(gic.getAabbWorldSpace());
-	}
-}
-
-Error GlobalIlluminationProbeNode::frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime)
-{
-	// Check the reflection probe component and if it's marked for rendering enable the frustum components
-	const GlobalIlluminationProbeComponent& gic = getFirstComponentOfType<GlobalIlluminationProbeComponent>();
-
-	constexpr FrustumComponentVisibilityTestFlag kFrustumFlags =
-		FrustumComponentVisibilityTestFlag::kRenderComponents | FrustumComponentVisibilityTestFlag::kLights;
-
-	const FrustumComponentVisibilityTestFlag testFlags =
-		(gic.getMarkedForRendering()) ? kFrustumFlags : FrustumComponentVisibilityTestFlag::kNone;
-
-	iterateComponentsOfType<FrustumComponent>([testFlags](FrustumComponent& frc) {
-		frc.setEnabledVisibilityTests(testFlags);
-	});
-
-	return Error::kNone;
-}
-
-} // end namespace anki

+ 0 - 37
AnKi/Scene/GlobalIlluminationProbeNode.h

@@ -1,37 +0,0 @@
-// Copyright (C) 2009-2022, 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
-/// @{
-
-/// Probe used in global illumination.
-class GlobalIlluminationProbeNode : public SceneNode
-{
-public:
-	GlobalIlluminationProbeNode(SceneGraph* scene, CString name);
-
-	~GlobalIlluminationProbeNode();
-
-	Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
-
-private:
-	class MoveFeedbackComponent;
-	class ShapeFeedbackComponent;
-
-	Array<Transform, 6> m_cubeFaceTransforms;
-
-	void onMoveUpdate(MoveComponent& move);
-	void onShapeUpdateOrProbeNeedsRendering();
-};
-/// @}
-
-} // end namespace anki

+ 0 - 99
AnKi/Scene/GpuParticleEmitterNode.cpp

@@ -1,99 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/GpuParticleEmitterNode.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
-#include <AnKi/Scene/Components/SpatialComponent.h>
-#include <AnKi/Scene/Components/GenericGpuComputeJobComponent.h>
-#include <AnKi/Scene/Components/GpuParticleEmitterComponent.h>
-
-namespace anki {
-
-/// Feedback component
-class GpuParticleEmitterNode::MoveFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(GpuParticleEmitterNode::MoveFeedbackComponent)
-
-public:
-	MoveFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		const MoveComponent& move = info.m_node->getFirstComponentOfType<MoveComponent>();
-		if(move.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			GpuParticleEmitterNode& mnode = static_cast<GpuParticleEmitterNode&>(*info.m_node);
-			mnode.onMoveComponentUpdate(move);
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(GpuParticleEmitterNode::MoveFeedbackComponent, -1.0f)
-
-/// Feedback component
-class GpuParticleEmitterNode::ShapeFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(GpuParticleEmitterNode::ShapeFeedbackComponent)
-
-public:
-	ShapeFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		const GpuParticleEmitterComponent& pec = info.m_node->getFirstComponentOfType<GpuParticleEmitterComponent>();
-		if(pec.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			GpuParticleEmitterNode& mnode = static_cast<GpuParticleEmitterNode&>(*info.m_node);
-			mnode.onShapeUpdate(pec);
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(GpuParticleEmitterNode::ShapeFeedbackComponent, -1.0f)
-
-GpuParticleEmitterNode::GpuParticleEmitterNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-	// Create the components
-	newComponent<MoveComponent>();
-	newComponent<MoveFeedbackComponent>();
-	GpuParticleEmitterComponent* pec = newComponent<GpuParticleEmitterComponent>();
-	newComponent<ShapeFeedbackComponent>();
-	newComponent<SpatialComponent>();
-
-	GenericGpuComputeJobComponent* gpuComp = newComponent<GenericGpuComputeJobComponent>();
-	gpuComp->setCallback(GpuParticleEmitterComponent::simulateCallback, pec);
-}
-
-GpuParticleEmitterNode::~GpuParticleEmitterNode()
-{
-}
-
-void GpuParticleEmitterNode::onMoveComponentUpdate(const MoveComponent& movec)
-{
-	getFirstComponentOfType<SpatialComponent>().setSpatialOrigin(movec.getWorldTransform().getOrigin().xyz());
-	getFirstComponentOfType<GpuParticleEmitterComponent>().setWorldTransform(movec.getWorldTransform());
-}
-
-void GpuParticleEmitterNode::onShapeUpdate(const GpuParticleEmitterComponent& pec)
-{
-	getFirstComponentOfType<SpatialComponent>().setAabbWorldSpace(pec.getBoundingVolumeWorldSpace());
-}
-
-} // end namespace anki

+ 0 - 32
AnKi/Scene/GpuParticleEmitterNode.h

@@ -1,32 +0,0 @@
-// Copyright (C) 2009-2022, 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>
-
-namespace anki {
-
-/// @addtogroup scene
-/// @{
-
-/// The particle emitter scene node. This scene node emitts
-class GpuParticleEmitterNode : public SceneNode
-{
-public:
-	GpuParticleEmitterNode(SceneGraph* scene, CString name);
-
-	~GpuParticleEmitterNode();
-
-private:
-	class MoveFeedbackComponent;
-	class ShapeFeedbackComponent;
-
-	void onMoveComponentUpdate(const MoveComponent& movec);
-	void onShapeUpdate(const GpuParticleEmitterComponent& pec);
-};
-/// @}
-
-} // end namespace anki

+ 0 - 325
AnKi/Scene/LightNode.cpp

@@ -1,325 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/LightNode.h>
-#include <AnKi/Scene/SceneGraph.h>
-#include <AnKi/Scene/Components/LensFlareComponent.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
-#include <AnKi/Scene/Components/SpatialComponent.h>
-#include <AnKi/Scene/Components/FrustumComponent.h>
-#include <AnKi/Shaders/Include/ClusteredShadingTypes.h>
-
-namespace anki {
-
-/// Feedback component.
-class LightNode::OnMovedFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(LightNode::OnMovedFeedbackComponent)
-
-public:
-	OnMovedFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		const MoveComponent& move = info.m_node->getComponentAt<MoveComponent>(0);
-		if(move.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			// Move updated
-			static_cast<LightNode&>(*info.m_node).onMoved(move);
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(LightNode::OnMovedFeedbackComponent, -1.0f)
-
-/// Feedback component.
-class LightNode::OnLightShapeUpdatedFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(LightNode::OnLightShapeUpdatedFeedbackComponent)
-
-public:
-	OnLightShapeUpdatedFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		LightComponent& light = info.m_node->getFirstComponentOfType<LightComponent>();
-		if(light.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			// Shape updated
-			static_cast<LightNode&>(*info.m_node).onLightShapeUpdated(light);
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(LightNode::OnLightShapeUpdatedFeedbackComponent, -1.0f)
-
-LightNode::LightNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-}
-
-LightNode::~LightNode()
-{
-}
-
-void LightNode::frameUpdateCommon()
-{
-	// Update frustum comps shadow info
-	const LightComponent& lc = getFirstComponentOfType<LightComponent>();
-	const Bool castsShadow = lc.getShadowEnabled();
-
-	iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) {
-		if(castsShadow)
-		{
-			frc.setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::kShadowCasterRenderComponents);
-		}
-		else
-		{
-			frc.setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::kNone);
-		}
-	});
-}
-
-void LightNode::onMoveUpdateCommon(const MoveComponent& move)
-{
-	// Update the spatial
-	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
-	sp.setSpatialOrigin(move.getWorldTransform().getOrigin().xyz());
-
-	// Update the lens flare
-	LensFlareComponent& lf = getFirstComponentOfType<LensFlareComponent>();
-	lf.setWorldPosition(move.getWorldTransform().getOrigin().xyz());
-
-	// Update light component
-	getFirstComponentOfType<LightComponent>().setWorldTransform(move.getWorldTransform());
-}
-
-PointLightNode::PointLightNode(SceneGraph* scene, CString name)
-	: LightNode(scene, name)
-{
-	newComponent<MoveComponent>();
-	newComponent<OnMovedFeedbackComponent>();
-
-	LightComponent* lc = newComponent<LightComponent>();
-	lc->setLightComponentType(LightComponentType::kPoint);
-
-	newComponent<LensFlareComponent>();
-	newComponent<OnLightShapeUpdatedFeedbackComponent>();
-	newComponent<SpatialComponent>();
-}
-
-PointLightNode::~PointLightNode()
-{
-	m_shadowData.destroy(getMemoryPool());
-}
-
-void PointLightNode::onMoved(const MoveComponent& move)
-{
-	onMoveUpdateCommon(move);
-
-	// Update the frustums
-	U32 count = 0;
-	iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& fr) {
-		Transform trf = m_shadowData[count].m_localTrf;
-		trf.setOrigin(move.getWorldTransform().getOrigin());
-
-		fr.setWorldTransform(trf);
-		++count;
-	});
-}
-
-void PointLightNode::onLightShapeUpdated(LightComponent& light)
-{
-	iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& fr) {
-		fr.setFar(light.getRadius());
-	});
-
-	SpatialComponent& spatialc = getFirstComponentOfType<SpatialComponent>();
-	spatialc.setSphereWorldSpace(Sphere(light.getWorldTransform().getOrigin(), light.getRadius()));
-}
-
-Error PointLightNode::frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime)
-{
-	// Lazily init
-	const LightComponent& lightc = getFirstComponentOfType<LightComponent>();
-	if(lightc.getShadowEnabled() && m_shadowData.isEmpty())
-	{
-		m_shadowData.create(getMemoryPool(), 6);
-
-		const F32 ang = toRad(90.0f);
-		const F32 dist = lightc.getRadius();
-		const F32 zNear = kClusterObjectFrustumNearPlane;
-
-		Mat3 rot;
-
-		rot = Mat3(Euler(0.0, -kPi / 2.0, 0.0)) * Mat3(Euler(0.0, 0.0, kPi));
-		m_shadowData[0].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot));
-		rot = Mat3(Euler(0.0, kPi / 2.0, 0.0)) * Mat3(Euler(0.0, 0.0, kPi));
-		m_shadowData[1].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot));
-		rot = Mat3(Euler(kPi / 2.0, 0.0, 0.0));
-		m_shadowData[2].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot));
-		rot = Mat3(Euler(-kPi / 2.0, 0.0, 0.0));
-		m_shadowData[3].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot));
-		rot = Mat3(Euler(0.0, kPi, 0.0)) * Mat3(Euler(0.0, 0.0, kPi));
-		m_shadowData[4].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot));
-		rot = Mat3(Euler(0.0, 0.0, kPi));
-		m_shadowData[5].m_localTrf.setRotation(Mat3x4(Vec3(0.0f), rot));
-
-		const Vec4& origin = getFirstComponentOfType<MoveComponent>().getWorldTransform().getOrigin();
-		for(U32 i = 0; i < 6; i++)
-		{
-			Transform trf = m_shadowData[i].m_localTrf;
-			trf.setOrigin(origin);
-
-			FrustumComponent* frc = newComponent<FrustumComponent>();
-			frc->setFrustumType(FrustumType::kPerspective);
-			frc->setPerspective(zNear, dist, ang, ang);
-			frc->setWorldTransform(trf);
-		}
-	}
-
-	frameUpdateCommon();
-
-	return Error::kNone;
-}
-
-class SpotLightNode::OnFrustumUpdatedFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(SpotLightNode::OnFrustumUpdatedFeedbackComponent)
-
-public:
-	OnFrustumUpdatedFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		FrustumComponent& frc = info.m_node->getFirstComponentOfType<FrustumComponent>();
-		if(frc.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			// Shape updated
-			static_cast<SpotLightNode&>(*info.m_node).onFrustumUpdated(frc);
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(SpotLightNode::OnFrustumUpdatedFeedbackComponent, -1.0f)
-
-SpotLightNode::SpotLightNode(SceneGraph* scene, CString name)
-	: LightNode(scene, name)
-{
-	newComponent<MoveComponent>();
-	newComponent<OnMovedFeedbackComponent>();
-
-	LightComponent* lc = newComponent<LightComponent>();
-	lc->setLightComponentType(LightComponentType::kSpot);
-
-	newComponent<LensFlareComponent>();
-
-	newComponent<OnLightShapeUpdatedFeedbackComponent>();
-
-	FrustumComponent* fr = newComponent<FrustumComponent>();
-	fr->setFrustumType(FrustumType::kPerspective);
-	fr->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::kNone);
-
-	newComponent<OnFrustumUpdatedFeedbackComponent>();
-
-	newComponent<SpatialComponent>();
-}
-
-void SpotLightNode::onMoved(const MoveComponent& move)
-{
-	// Update the frustums
-	iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& fr) {
-		fr.setWorldTransform(move.getWorldTransform());
-	});
-
-	onMoveUpdateCommon(move);
-}
-
-void SpotLightNode::onLightShapeUpdated(LightComponent& light)
-{
-	FrustumComponent& frc = getFirstComponentOfType<FrustumComponent>();
-	frc.setPerspective(kClusterObjectFrustumNearPlane, light.getDistance(), light.getOuterAngle(),
-					   light.getOuterAngle());
-}
-
-void SpotLightNode::onFrustumUpdated(FrustumComponent& frc)
-{
-	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
-	sp.setConvexHullWorldSpace(frc.getPerspectiveBoundingShapeWorldSpace());
-}
-
-Error SpotLightNode::frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime)
-{
-	frameUpdateCommon();
-	return Error::kNone;
-}
-
-class DirectionalLightNode::FeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(DirectionalLightNode::FeedbackComponent)
-
-public:
-	FeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-		const MoveComponent& move = info.m_node->getComponentAt<MoveComponent>(0);
-		if(move.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			// Move updated
-			LightComponent& lightc = info.m_node->getFirstComponentOfType<LightComponent>();
-			lightc.setWorldTransform(move.getWorldTransform());
-
-			SpatialComponent& spatialc = info.m_node->getFirstComponentOfType<SpatialComponent>();
-			spatialc.setSpatialOrigin(move.getWorldTransform().getOrigin().xyz());
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(DirectionalLightNode::FeedbackComponent, -1.0f)
-
-DirectionalLightNode::DirectionalLightNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-	newComponent<MoveComponent>();
-	newComponent<FeedbackComponent>();
-
-	LightComponent* lc = newComponent<LightComponent>();
-	lc->setLightComponentType(LightComponentType::kDirectional);
-
-	SpatialComponent* spatialc = newComponent<SpatialComponent>();
-
-	// Make the spatial always visible
-	spatialc->setAlwaysVisible(true);
-}
-
-} // end namespace anki

+ 0 - 93
AnKi/Scene/LightNode.h

@@ -1,93 +0,0 @@
-// Copyright (C) 2009-2022, 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/Scene/Components/LightComponent.h>
-
-namespace anki {
-
-/// @addtogroup scene
-/// @{
-
-/// Light scene node. It can be spot or point.
-class LightNode : public SceneNode
-{
-public:
-	LightNode(SceneGraph* scene, CString name);
-
-	~LightNode();
-
-protected:
-	class OnMovedFeedbackComponent;
-	class OnLightShapeUpdatedFeedbackComponent;
-
-	/// Called when moved
-	void onMoveUpdateCommon(const MoveComponent& move);
-
-	void frameUpdateCommon();
-
-	virtual void onMoved(const MoveComponent& move) = 0;
-
-	virtual void onLightShapeUpdated(LightComponent& light) = 0;
-};
-
-/// Point light
-class PointLightNode : public LightNode
-{
-public:
-	PointLightNode(SceneGraph* scene, CString name);
-	~PointLightNode();
-
-	Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
-
-private:
-	class ShadowCombo
-	{
-	public:
-		Transform m_localTrf = Transform::getIdentity();
-	};
-
-	DynamicArray<ShadowCombo> m_shadowData;
-
-	void onMoved(const MoveComponent& move) override;
-	void onLightShapeUpdated(LightComponent& light) override;
-};
-
-/// Spot light
-class SpotLightNode : public LightNode
-{
-public:
-	SpotLightNode(SceneGraph* scene, CString name);
-
-	Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
-
-private:
-	class OnFrustumUpdatedFeedbackComponent;
-
-	void onMoved(const MoveComponent& move) override;
-	void onLightShapeUpdated(LightComponent& light) override;
-	void onFrustumUpdated(FrustumComponent& frc);
-};
-
-/// Directional light (the sun).
-class DirectionalLightNode : public SceneNode
-{
-public:
-	DirectionalLightNode(SceneGraph* scene, CString name);
-
-private:
-	class FeedbackComponent;
-
-	static void drawCallback([[maybe_unused]] RenderQueueDrawContext& ctx,
-							 [[maybe_unused]] ConstWeakArray<void*> userData)
-	{
-		// TODO
-	}
-};
-/// @}
-
-} // end namespace anki

+ 0 - 110
AnKi/Scene/ModelNode.cpp

@@ -1,110 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/ModelNode.h>
-#include <AnKi/Scene/SceneGraph.h>
-#include <AnKi/Scene/DebugDrawer.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
-#include <AnKi/Scene/Components/SkinComponent.h>
-#include <AnKi/Scene/Components/SpatialComponent.h>
-#include <AnKi/Scene/Components/ModelComponent.h>
-#include <AnKi/Resource/ModelResource.h>
-#include <AnKi/Resource/ResourceManager.h>
-#include <AnKi/Resource/SkeletonResource.h>
-#include <AnKi/Physics/PhysicsWorld.h>
-
-namespace anki {
-
-/// Feedback component.
-class ModelNode::FeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(ModelNode::FeedbackComponent)
-
-public:
-	FeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-		static_cast<ModelNode&>(*info.m_node).feedbackUpdate();
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(ModelNode::FeedbackComponent, -1.0f)
-
-class ModelNode::RenderProxy
-{
-public:
-	ModelNode* m_node = nullptr;
-
-	/// Uncompresses the mesh positions to the local view. The scale should be uniform because it will be applied to
-	/// normals and tangents and non-uniform data will cause problems.
-	Mat4 m_compressedToModelTransform = Mat4::getIdentity();
-};
-
-ModelNode::ModelNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-	newComponent<SkinComponent>();
-	newComponent<ModelComponent>();
-	newComponent<MoveComponent>();
-	newComponent<FeedbackComponent>();
-	newComponent<SpatialComponent>();
-}
-
-ModelNode::~ModelNode()
-{
-}
-
-void ModelNode::feedbackUpdate()
-{
-	const ModelComponent& modelc = getFirstComponentOfType<ModelComponent>();
-	const SkinComponent& skinc = getFirstComponentOfType<SkinComponent>();
-	const MoveComponent& movec = getFirstComponentOfType<MoveComponent>();
-
-	if(!modelc.isEnabled())
-	{
-		// Disable everything
-		ANKI_ASSERT(!"TODO");
-		return;
-	}
-
-	const Timestamp globTimestamp = getGlobalTimestamp();
-	Bool updateSpatial = false;
-
-	// Model update
-	if(modelc.getTimestamp() == globTimestamp)
-	{
-		m_aabbLocal = modelc.getModelResource()->getBoundingVolume();
-		updateSpatial = true;
-	}
-
-	// Skin update
-	if(skinc.isEnabled() && skinc.getTimestamp() == globTimestamp)
-	{
-		m_aabbLocal = skinc.getBoneBoundingVolumeLocalSpace();
-		updateSpatial = true;
-	}
-
-	// Move update
-	if(movec.getTimestamp() == globTimestamp)
-	{
-		getFirstComponentOfType<SpatialComponent>().setSpatialOrigin(movec.getWorldTransform().getOrigin().xyz());
-		updateSpatial = true;
-	}
-
-	// Spatial update
-	if(updateSpatial)
-	{
-		const Aabb aabbWorld = m_aabbLocal.getTransformed(movec.getWorldTransform());
-		getFirstComponentOfType<SpatialComponent>().setAabbWorldSpace(aabbWorld);
-	}
-}
-
-} // end namespace anki

+ 0 - 39
AnKi/Scene/ModelNode.h

@@ -1,39 +0,0 @@
-// Copyright (C) 2009-2022, 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/Collision/Aabb.h>
-#include <AnKi/Util/WeakArray.h>
-
-namespace anki {
-
-// Forward
-class RenderQueueDrawContext;
-class RayTracingInstanceQueueElement;
-
-/// @addtogroup scene
-/// @{
-
-/// The model scene node.
-class ModelNode : public SceneNode
-{
-public:
-	ModelNode(SceneGraph* scene, CString name);
-
-	~ModelNode();
-
-private:
-	class FeedbackComponent;
-	class RenderProxy;
-
-	Aabb m_aabbLocal;
-
-	void feedbackUpdate();
-};
-/// @}
-
-} // end namespace anki

+ 0 - 1
AnKi/Scene/Octree.cpp

@@ -4,7 +4,6 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <AnKi/Scene/Octree.h>
 #include <AnKi/Scene/Octree.h>
-#include <AnKi/Scene/Components/FrustumComponent.h>
 #include <AnKi/Collision/Aabb.h>
 #include <AnKi/Collision/Aabb.h>
 #include <AnKi/Collision/Functions.h>
 #include <AnKi/Collision/Functions.h>
 #include <AnKi/Util/ThreadHive.h>
 #include <AnKi/Util/ThreadHive.h>

+ 0 - 91
AnKi/Scene/ParticleEmitterNode.cpp

@@ -1,91 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/ParticleEmitterNode.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
-#include <AnKi/Scene/Components/SpatialComponent.h>
-#include <AnKi/Scene/Components/ParticleEmitterComponent.h>
-
-namespace anki {
-
-/// Feedback component
-class ParticleEmitterNode::MoveFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(ParticleEmitterNode::MoveFeedbackComponent)
-
-public:
-	MoveFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false; // Don't care about updates for this component
-
-		MoveComponent& move = info.m_node->getFirstComponentOfType<MoveComponent>();
-		if(move.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			static_cast<ParticleEmitterNode&>(*info.m_node).onMoveComponentUpdate(move);
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(ParticleEmitterNode::MoveFeedbackComponent, -1.0f)
-
-/// Feedback component
-class ParticleEmitterNode::ShapeFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(ParticleEmitterNode::ShapeFeedbackComponent)
-
-public:
-	ShapeFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), false // Not feedback because it's always being called
-		)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-		static_cast<ParticleEmitterNode&>(*info.m_node).onShapeUpdate();
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(ParticleEmitterNode::ShapeFeedbackComponent, -1.0f)
-
-ParticleEmitterNode::ParticleEmitterNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-	// Components
-	newComponent<MoveComponent>();
-
-	newComponent<MoveFeedbackComponent>();
-
-	newComponent<ParticleEmitterComponent>();
-
-	newComponent<ShapeFeedbackComponent>();
-	newComponent<SpatialComponent>();
-}
-
-ParticleEmitterNode::~ParticleEmitterNode()
-{
-}
-
-void ParticleEmitterNode::onMoveComponentUpdate(MoveComponent& move)
-{
-	getFirstComponentOfType<SpatialComponent>().setSpatialOrigin(move.getWorldTransform().getOrigin().xyz());
-}
-
-void ParticleEmitterNode::onShapeUpdate()
-{
-	const ParticleEmitterComponent& pec = getFirstComponentOfType<ParticleEmitterComponent>();
-	getFirstComponentOfType<SpatialComponent>().setAabbWorldSpace(pec.getAabbWorldSpace());
-}
-
-} // end namespace anki

+ 0 - 32
AnKi/Scene/ParticleEmitterNode.h

@@ -1,32 +0,0 @@
-// Copyright (C) 2009-2022, 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>
-
-namespace anki {
-
-/// @addtogroup scene
-/// @{
-
-/// The particle emitter scene node. This scene node emitts
-class ParticleEmitterNode : public SceneNode
-{
-public:
-	ParticleEmitterNode(SceneGraph* scene, CString name);
-
-	~ParticleEmitterNode();
-
-private:
-	class MoveFeedbackComponent;
-	class ShapeFeedbackComponent;
-
-	void onMoveComponentUpdate(MoveComponent& move);
-	void onShapeUpdate();
-};
-/// @}
-
-} // end namespace anki

+ 0 - 50
AnKi/Scene/PhysicsDebugNode.cpp

@@ -1,50 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/PhysicsDebugNode.h>
-#include <AnKi/Scene/Components/SpatialComponent.h>
-#include <AnKi/Scene/SceneGraph.h>
-#include <AnKi/Resource/ResourceManager.h>
-
-namespace anki {
-
-PhysicsDebugNode::PhysicsDebugNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-	, m_physDbgDrawer(&scene->getDebugDrawer())
-{
-	// TODO
-#if 0
-	RenderComponent* rcomp = newComponent<RenderComponent>();
-	rcomp->setFlags(RenderComponentFlag::kNone);
-	rcomp->initRaster(
-		[](RenderQueueDrawContext& ctx, ConstWeakArray<void*> userData) {
-			static_cast<PhysicsDebugNode*>(userData[0])->draw(ctx);
-		},
-		this, 0);
-#endif
-
-	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()
-{
-}
-
-void PhysicsDebugNode::draw(RenderQueueDrawContext& ctx)
-{
-#if 0
-	if(ctx.m_debugDraw)
-	{
-		m_physDbgDrawer.start(ctx.m_viewProjectionMatrix, ctx.m_commandBuffer, ctx.m_rebarStagingPool);
-		m_physDbgDrawer.drawWorld(*getExternalSubsystems().m_physicsWorld);
-		m_physDbgDrawer.end();
-	}
-#endif
-}
-
-} // end namespace anki

+ 0 - 27
AnKi/Scene/PhysicsDebugNode.h

@@ -1,27 +0,0 @@
-// Copyright (C) 2009-2022, 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/Scene/DebugDrawer.h>
-
-namespace anki {
-
-/// An always visible node that debug draws primitives of the physics world.
-class PhysicsDebugNode : public SceneNode
-{
-public:
-	PhysicsDebugNode(SceneGraph* scene, CString name);
-
-	~PhysicsDebugNode();
-
-private:
-	PhysicsDebugDrawer m_physDbgDrawer;
-
-	void draw(RenderQueueDrawContext& ctx);
-};
-
-} // end namespace anki

+ 0 - 134
AnKi/Scene/PlayerNode.cpp

@@ -1,134 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/PlayerNode.h>
-#include <AnKi/Scene/SceneGraph.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
-#include <AnKi/Scene/Components/PlayerControllerComponent.h>
-#include <AnKi/Physics/PhysicsPlayerController.h>
-#include <AnKi/Physics/PhysicsWorld.h>
-#include <AnKi/Input/Input.h>
-
-namespace anki {
-
-/// Feedback component.
-class PlayerNode::FeedbackComponent final : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(PlayerNode::FeedbackComponent)
-
-public:
-	FeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), false)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		PlayerControllerComponent& playerc = info.m_node->getFirstComponentOfType<PlayerControllerComponent>();
-		const Input& in = *getExternalSubsystems(*info.m_node).m_input;
-		const F32 ang = toRad(7.0f);
-
-		F32 y = in.getMousePosition().y();
-		F32 x = in.getMousePosition().x();
-		if(playerc.getTimestamp() == info.m_node->getGlobalTimestamp() || y != 0.0 || x != 0.0)
-		{
-			MoveComponent& move = info.m_node->getFirstComponentOfType<MoveComponent>();
-
-			// Set origin
-			Vec4 origin = playerc.getWorldTransform().getOrigin();
-			origin.y() += 1.9f;
-
-			// Set rotation
-			Mat3x4 rot(Vec3(0.0f), Euler(ang * y * 11.25f, ang * x * -20.0f, 0.0f));
-
-			rot = move.getLocalRotation().combineTransformations(rot);
-
-			Vec3 newz = rot.getColumn(2).getNormalized();
-			Vec3 newx = Vec3(0.0, 1.0, 0.0).cross(newz);
-			Vec3 newy = newz.cross(newx);
-			rot.setColumns(newx, newy, newz, Vec3(0.0));
-			rot.reorthogonalize();
-
-			// Update move
-			move.setLocalTransform(Transform(origin, rot, 1.0));
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(PlayerNode::FeedbackComponent, -1.0f)
-
-/// Feedback component.
-class PlayerNode::FeedbackComponent2 final : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(PlayerNode::FeedbackComponent2)
-
-public:
-	FeedbackComponent2(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), false // This shouldn't behave as a feedback component
-		)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		PlayerControllerComponent& playerc = info.m_node->getFirstComponentOfType<PlayerControllerComponent>();
-		MoveComponent& move = info.m_node->getFirstComponentOfType<MoveComponent>();
-		const Input& in = *getExternalSubsystems(*info.m_node).m_input;
-
-		const F32 speed = 0.5;
-
-		Vec4 moveVec(0.0);
-		if(in.getKey(KeyCode::kW))
-		{
-			moveVec.z() += 1.0f;
-		}
-
-		if(in.getKey(KeyCode::kA))
-		{
-			moveVec.x() -= 1.0f;
-		}
-
-		if(in.getKey(KeyCode::kS))
-		{
-			moveVec.z() -= 1.0f;
-		}
-
-		if(in.getKey(KeyCode::kD))
-		{
-			moveVec.x() += 1.0f;
-		}
-
-		Vec4 dir = -move.getLocalRotation().getZAxis().xyz0();
-		dir.y() = 0.0f;
-		dir.normalize();
-
-		playerc.setVelocity(moveVec.z() * speed, moveVec.x() * speed, 0.0, dir);
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(PlayerNode::FeedbackComponent2, -1.0f)
-
-PlayerNode::PlayerNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-	newComponent<PlayerControllerComponent>();
-	newComponent<FeedbackComponent>();
-	newComponent<MoveComponent>();
-	newComponent<FeedbackComponent2>();
-}
-
-PlayerNode::~PlayerNode()
-{
-}
-
-} // end namespace anki

+ 0 - 30
AnKi/Scene/PlayerNode.h

@@ -1,30 +0,0 @@
-// Copyright (C) 2009-2022, 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
-/// @{
-
-/// Player scene node. It uses input and physics to move inside the world.
-class PlayerNode : public SceneNode
-{
-public:
-	PlayerNode(SceneGraph* scene, CString name);
-
-	~PlayerNode();
-
-private:
-	class FeedbackComponent;
-	class FeedbackComponent2;
-};
-/// @}
-
-} // end namespace anki

+ 0 - 196
AnKi/Scene/ReflectionProbeNode.cpp

@@ -1,196 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/ReflectionProbeNode.h>
-#include <AnKi/Scene/Components/ReflectionProbeComponent.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
-#include <AnKi/Scene/Components/FrustumComponent.h>
-#include <AnKi/Scene/Components/SpatialComponent.h>
-#include <AnKi/Core/ConfigSet.h>
-#include <AnKi/Shaders/Include/ClusteredShadingTypes.h>
-
-namespace anki {
-
-/// Feedback component
-class ReflectionProbeNode::MoveFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(ReflectionProbeNode::MoveFeedbackComponent)
-
-public:
-	MoveFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		MoveComponent& move = info.m_node->getFirstComponentOfType<MoveComponent>();
-		if(move.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			// Move updated
-			ReflectionProbeNode& dnode = static_cast<ReflectionProbeNode&>(*info.m_node);
-			dnode.onMoveUpdate(move);
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(ReflectionProbeNode::MoveFeedbackComponent, -1.0f)
-
-/// Feedback component
-class ReflectionProbeNode::ShapeFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(ReflectionProbeNode::ShapeFeedbackComponent)
-
-public:
-	ShapeFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		ReflectionProbeComponent& reflc = info.m_node->getFirstComponentOfType<ReflectionProbeComponent>();
-		if(reflc.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			ReflectionProbeNode& dnode = static_cast<ReflectionProbeNode&>(*info.m_node);
-			dnode.onShapeUpdate(reflc);
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(ReflectionProbeNode::ShapeFeedbackComponent, -1.0f)
-
-ReflectionProbeNode::ReflectionProbeNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-	// Move component first
-	newComponent<MoveComponent>();
-
-	// Feedback component
-	newComponent<MoveFeedbackComponent>();
-
-	// The frustum components
-	const F32 ang = toRad(90.0f);
-
-	Mat3 rot;
-
-	rot = Mat3(Euler(0.0f, -kPi / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, kPi));
-	m_frustumTransforms[0].setRotation(Mat3x4(Vec3(0.0f), rot));
-	rot = Mat3(Euler(0.0f, kPi / 2.0f, 0.0f)) * Mat3(Euler(0.0f, 0.0f, kPi));
-	m_frustumTransforms[1].setRotation(Mat3x4(Vec3(0.0f), rot));
-	rot = Mat3(Euler(kPi / 2.0f, 0.0f, 0.0f));
-	m_frustumTransforms[2].setRotation(Mat3x4(Vec3(0.0f), rot));
-	rot = Mat3(Euler(-kPi / 2.0f, 0.0f, 0.0f));
-	m_frustumTransforms[3].setRotation(Mat3x4(Vec3(0.0f), rot));
-	rot = Mat3(Euler(0.0f, kPi, 0.0f)) * Mat3(Euler(0.0f, 0.0f, kPi));
-	m_frustumTransforms[4].setRotation(Mat3x4(Vec3(0.0f), rot));
-	rot = Mat3(Euler(0.0f, 0.0f, kPi));
-	m_frustumTransforms[5].setRotation(Mat3x4(Vec3(0.0f), rot));
-
-	for(U i = 0; i < 6; ++i)
-	{
-		m_frustumTransforms[i].setOrigin(Vec4(0.0f));
-		m_frustumTransforms[i].setScale(1.0f);
-
-		FrustumComponent* frc = newComponent<FrustumComponent>();
-		frc->setFrustumType(FrustumType::kPerspective);
-		frc->setPerspective(kClusterObjectFrustumNearPlane, 10.0f, ang, ang);
-		frc->setLodDistances({1.0f, 2.0f, 3.0f});
-		frc->setWorldTransform(m_frustumTransforms[i]);
-		frc->setEnabledVisibilityTests(FrustumComponentVisibilityTestFlag::kNone);
-		frc->setShadowCascadeDistance(0, 10.0f);
-		frc->setShadowCascadeCount(1);
-	}
-
-	// Reflection probe comp
-	newComponent<ReflectionProbeComponent>();
-
-	// Feedback
-	newComponent<ShapeFeedbackComponent>();
-
-	// Spatial component
-	SpatialComponent* spatialc = newComponent<SpatialComponent>();
-	spatialc->setUpdateOctreeBounds(false);
-}
-
-ReflectionProbeNode::~ReflectionProbeNode()
-{
-}
-
-void ReflectionProbeNode::onMoveUpdate(MoveComponent& move)
-{
-	// Update frustum components
-	U count = 0;
-	iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) {
-		Transform trf = m_frustumTransforms[count];
-		trf.setOrigin(move.getWorldTransform().getOrigin());
-
-		frc.setWorldTransform(trf);
-		++count;
-	});
-
-	ANKI_ASSERT(count == 6);
-
-	// Update the spatial comp
-	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
-	sp.setSpatialOrigin(move.getWorldTransform().getOrigin().xyz());
-
-	// Update the refl comp
-	ReflectionProbeComponent& reflc = getFirstComponentOfType<ReflectionProbeComponent>();
-	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, getExternalSubsystems().m_config->getSceneReflectionProbeEffectiveDistance());
-
-	// Update frustum components
-	iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) {
-		frc.setFar(effectiveDistance);
-		frc.setShadowCascadeDistance(
-			0,
-			min(effectiveDistance, getExternalSubsystems().m_config->getSceneReflectionProbeShadowEffectiveDistance()));
-
-		// Add something to avoid complains
-		frc.setLodDistances(
-			{frc.getNear() + kEpsilonf, frc.getNear() + 2.0f * kEpsilonf, frc.getNear() + 3.0f * kEpsilonf});
-	});
-
-	// Update the spatial comp
-	SpatialComponent& sp = getFirstComponentOfType<SpatialComponent>();
-	sp.setAabbWorldSpace(reflc.getAabbWorldSpace());
-}
-
-Error ReflectionProbeNode::frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime)
-{
-	// Check the reflection probe component and if it's marked for rendering enable the frustum components
-	const ReflectionProbeComponent& reflc = getFirstComponentOfType<ReflectionProbeComponent>();
-
-	constexpr FrustumComponentVisibilityTestFlag frustumTestFlags =
-		FrustumComponentVisibilityTestFlag::kRenderComponents | FrustumComponentVisibilityTestFlag::kLights;
-
-	const FrustumComponentVisibilityTestFlag testFlags =
-		reflc.getMarkedForRendering() ? frustumTestFlags : FrustumComponentVisibilityTestFlag::kNone;
-
-	iterateComponentsOfType<FrustumComponent>([testFlags](FrustumComponent& frc) {
-		frc.setEnabledVisibilityTests(testFlags);
-	});
-
-	return Error::kNone;
-}
-
-} // end namespace anki

+ 0 - 37
AnKi/Scene/ReflectionProbeNode.h

@@ -1,37 +0,0 @@
-// Copyright (C) 2009-2022, 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
-/// @{
-
-/// Probe used in realtime reflections.
-class ReflectionProbeNode : public SceneNode
-{
-public:
-	ReflectionProbeNode(SceneGraph* scene, CString name);
-
-	~ReflectionProbeNode();
-
-	Error frameUpdate(Second prevUpdateTime, Second crntTime) override;
-
-private:
-	class MoveFeedbackComponent;
-	class ShapeFeedbackComponent;
-
-	Array<Transform, 6> m_frustumTransforms;
-
-	void onMoveUpdate(MoveComponent& move);
-	void onShapeUpdate(ReflectionProbeComponent& reflc);
-};
-/// @}
-
-} // end namespace anki

+ 9 - 26
AnKi/Scene/SceneGraph.cpp

@@ -4,11 +4,8 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Scene/SceneGraph.h>
-#include <AnKi/Scene/CameraNode.h>
-#include <AnKi/Scene/PhysicsDebugNode.h>
-#include <AnKi/Scene/ModelNode.h>
 #include <AnKi/Scene/Octree.h>
 #include <AnKi/Scene/Octree.h>
-#include <AnKi/Scene/Components/FrustumComponent.h>
+#include <AnKi/Scene/Components/CameraComponent.h>
 #include <AnKi/Physics/PhysicsWorld.h>
 #include <AnKi/Physics/PhysicsWorld.h>
 #include <AnKi/Resource/ResourceManager.h>
 #include <AnKi/Resource/ResourceManager.h>
 #include <AnKi/Renderer/MainRenderer.h>
 #include <AnKi/Renderer/MainRenderer.h>
@@ -65,15 +62,11 @@ Error SceneGraph::init(const SceneGraphInitInfo& initInfo)
 	m_octree->init(m_sceneMin, m_sceneMax, m_subsystems.m_config->getSceneOctreeMaxDepth());
 	m_octree->init(m_sceneMin, m_sceneMax, m_subsystems.m_config->getSceneOctreeMaxDepth());
 
 
 	// Init the default main camera
 	// Init the default main camera
-	ANKI_CHECK(newSceneNode<PerspectiveCameraNode>("mainCamera", m_defaultMainCam));
-	m_defaultMainCam->getFirstComponentOfType<FrustumComponent>().setPerspective(0.1f, 1000.0f, toRad(60.0f),
-																				 (1080.0f / 1920.0f) * toRad(60.0f));
+	ANKI_CHECK(newSceneNode<SceneNode>("mainCamera", m_defaultMainCam));
+	CameraComponent* camc = m_defaultMainCam->newComponent<CameraComponent>();
+	camc->setPerspective(0.1f, 1000.0f, toRad(60.0f), (1080.0f / 1920.0f) * toRad(60.0f));
 	m_mainCam = m_defaultMainCam;
 	m_mainCam = m_defaultMainCam;
 
 
-	// Create a special node for debugging the physics world
-	PhysicsDebugNode* pnode;
-	ANKI_CHECK(newSceneNode<PhysicsDebugNode>("_physicsDebugNode", pnode));
-
 	// Other
 	// Other
 	ANKI_CHECK(m_debugDrawer.init(m_subsystems.m_resourceManager, m_subsystems.m_grManager));
 	ANKI_CHECK(m_debugDrawer.init(m_subsystems.m_resourceManager, m_subsystems.m_grManager));
 
 
@@ -243,31 +236,21 @@ Error SceneGraph::updateNode(Second prevTime, Second crntTime, SceneNode& node)
 	componentUpdateInfo.m_framePool = &m_framePool;
 	componentUpdateInfo.m_framePool = &m_framePool;
 	componentUpdateInfo.m_gpuSceneMicroPatcher = m_subsystems.m_gpuSceneMicroPatcher;
 	componentUpdateInfo.m_gpuSceneMicroPatcher = m_subsystems.m_gpuSceneMicroPatcher;
 
 
-	Timestamp componentTimestamp = 0;
 	Bool atLeastOneComponentUpdated = false;
 	Bool atLeastOneComponentUpdated = false;
-	node.iterateComponents([&](SceneComponent& comp, Bool isFeedbackComponent) {
+	node.iterateComponents([&](SceneComponent& comp) {
 		if(err)
 		if(err)
 		{
 		{
 			return;
 			return;
 		}
 		}
 
 
+		componentUpdateInfo.m_node = &node;
 		Bool updated = false;
 		Bool updated = false;
-		if(!atLeastOneComponentUpdated && isFeedbackComponent)
-		{
-			// Skip feedback component if prior components didn't got updated
-		}
-		else
-		{
-			componentUpdateInfo.m_node = &node;
-			err = comp.updateReal(componentUpdateInfo, updated);
-		}
+		err = comp.updateReal(componentUpdateInfo, updated);
 
 
 		if(updated)
 		if(updated)
 		{
 		{
 			ANKI_TRACE_INC_COUNTER(SCENE_COMPONENTS_UPDATED, 1);
 			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);
-			ANKI_ASSERT(componentTimestamp > 0);
 			atLeastOneComponentUpdated = true;
 			atLeastOneComponentUpdated = true;
 		}
 		}
 	});
 	});
@@ -283,9 +266,9 @@ Error SceneGraph::updateNode(Second prevTime, Second crntTime, SceneNode& node)
 	// Frame update
 	// Frame update
 	if(!err)
 	if(!err)
 	{
 	{
-		if(componentTimestamp != 0)
+		if(atLeastOneComponentUpdated)
 		{
 		{
-			node.setComponentMaxTimestamp(componentTimestamp);
+			node.setComponentMaxTimestamp(node.getSceneGraph().m_timestamp);
 		}
 		}
 		else
 		else
 		{
 		{

+ 3 - 3
AnKi/Scene/SceneGraph.h

@@ -17,9 +17,7 @@
 namespace anki {
 namespace anki {
 
 
 // Forward
 // Forward
-class CameraNode;
 class Octree;
 class Octree;
-class PerspectiveCameraNode;
 
 
 /// @addtogroup scene
 /// @addtogroup scene
 /// @{
 /// @{
@@ -159,6 +157,8 @@ public:
 		return m_sceneMax;
 		return m_sceneMax;
 	}
 	}
 
 
+	/// Get a unique UUID.
+	/// @note It's thread-safe.
 	U64 getNewUuid()
 	U64 getNewUuid()
 	{
 	{
 		return m_nodesUuid.fetchAdd(1);
 		return m_nodesUuid.fetchAdd(1);
@@ -191,7 +191,7 @@ private:
 
 
 	SceneNode* m_mainCam = nullptr;
 	SceneNode* m_mainCam = nullptr;
 	Timestamp m_activeCameraChangeTimestamp = 0;
 	Timestamp m_activeCameraChangeTimestamp = 0;
-	PerspectiveCameraNode* m_defaultMainCam = nullptr;
+	SceneNode* m_defaultMainCam = nullptr;
 
 
 	EventManager m_events;
 	EventManager m_events;
 
 

+ 74 - 1
AnKi/Scene/SceneNode.cpp

@@ -5,6 +5,8 @@
 
 
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneNode.h>
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Scene/SceneGraph.h>
+#include <AnKi/Scene/Components/SceneComponent.h>
+#include <AnKi/Scene/Components/MoveComponent.h>
 
 
 namespace anki {
 namespace anki {
 
 
@@ -16,6 +18,9 @@ SceneNode::SceneNode(SceneGraph* scene, CString name)
 	{
 	{
 		m_name.create(getMemoryPool(), name);
 		m_name.create(getMemoryPool(), name);
 	}
 	}
+
+	// Add the implicit MoveComponent
+	newComponent<MoveComponent>();
 }
 }
 
 
 SceneNode::~SceneNode()
 SceneNode::~SceneNode()
@@ -33,7 +38,6 @@ SceneNode::~SceneNode()
 	Base::destroy(pool);
 	Base::destroy(pool);
 	m_name.destroy(pool);
 	m_name.destroy(pool);
 	m_components.destroy(pool);
 	m_components.destroy(pool);
-	m_componentInfos.destroy(pool);
 }
 }
 
 
 void SceneNode::setMarkedForDeletion()
 void SceneNode::setMarkedForDeletion()
@@ -73,4 +77,73 @@ SceneGraphExternalSubsystems& SceneNode::getExternalSubsystems() const
 	return m_scene->m_subsystems;
 	return m_scene->m_subsystems;
 }
 }
 
 
+void SceneNode::newComponentInternal(SceneComponent* newc)
+{
+	m_componentTypeMask |= 1 << newc->getClassId();
+
+	// Inform all other components that some component was added
+	for(SceneComponent* other : m_components)
+	{
+		other->onOtherComponentRemovedOrAddedReal(newc, true);
+	}
+
+	// Inform the current component about others
+	for(SceneComponent* other : m_components)
+	{
+		newc->onOtherComponentRemovedOrAddedReal(other, true);
+	}
+
+	m_components.emplaceBack(getMemoryPool(), newc);
+
+	// Sort based on update weight
+	std::sort(m_components.getBegin(), m_components.getEnd(), [](const SceneComponent* a, const SceneComponent* b) {
+		const F32 weightA = a->getClassRtti().m_updateWeight;
+		const F32 weightB = b->getClassRtti().m_updateWeight;
+		if(weightA != weightB)
+		{
+			return weightA < weightB;
+		}
+		else
+		{
+			return a->getClassId() < b->getClassId();
+		}
+	});
+}
+
+Bool SceneNode::updateTransform(StackMemoryPool& framePool)
+{
+	const Bool needsUpdate = m_localTransformDirty;
+	m_localTransformDirty = false;
+	const Bool updatedLastFrame = m_transformUpdatedThisFrame;
+	m_transformUpdatedThisFrame = needsUpdate;
+
+	if(needsUpdate || updatedLastFrame)
+	{
+		m_prevWTrf = m_wtrf;
+	}
+
+	// Update world transform
+	if(needsUpdate)
+	{
+		const SceneNode* parent = getParent();
+
+		if(parent == nullptr || m_ignoreParentNodeTransform)
+		{
+			m_wtrf = m_ltrf;
+		}
+		else
+		{
+			m_wtrf = parent->getWorldTransform().combineTransformations(m_ltrf);
+		}
+
+		// Make children dirty as well. Don't walk the whole tree because you will re-walk it later
+		[[maybe_unused]] const Error err = visitChildrenMaxDepth(1, [](SceneNode& childNode) -> Error {
+			childNode.m_localTransformDirty = true;
+			return Error::kNone;
+		});
+	}
+
+	return needsUpdate;
+}
+
 } // end namespace anki
 } // end namespace anki

+ 161 - 76
AnKi/Scene/SceneNode.h

@@ -104,10 +104,9 @@ public:
 	template<typename TFunct>
 	template<typename TFunct>
 	void iterateComponents(TFunct func) const
 	void iterateComponents(TFunct func) const
 	{
 	{
-		for(U32 i = 0; i < m_componentInfos.getSize(); ++i)
+		for(U32 i = 0; i < m_components.getSize(); ++i)
 		{
 		{
-			// Use the m_componentInfos to check if it's feedback component for possibly less cache misses
-			func(*m_components[i], m_componentInfos[i].isFeedbackComponent());
+			func(*m_components[i]);
 		}
 		}
 	}
 	}
 
 
@@ -115,10 +114,9 @@ public:
 	template<typename TFunct>
 	template<typename TFunct>
 	void iterateComponents(TFunct func)
 	void iterateComponents(TFunct func)
 	{
 	{
-		for(U32 i = 0; i < m_componentInfos.getSize(); ++i)
+		for(U32 i = 0; i < m_components.getSize(); ++i)
 		{
 		{
-			// Use the m_componentInfos to check if it's feedback component for possibly less cache misses
-			func(*m_components[i], m_componentInfos[i].isFeedbackComponent());
+			func(*m_components[i]);
 		}
 		}
 	}
 	}
 
 
@@ -126,11 +124,14 @@ public:
 	template<typename TComponent, typename TFunct>
 	template<typename TComponent, typename TFunct>
 	void iterateComponentsOfType(TFunct func) const
 	void iterateComponentsOfType(TFunct func) const
 	{
 	{
-		for(U32 i = 0; i < m_componentInfos.getSize(); ++i)
+		if(m_componentTypeMask & (1 << TComponent::getStaticClassId()))
 		{
 		{
-			if(m_componentInfos[i].getComponentClassId() == TComponent::getStaticClassId())
+			for(U32 i = 0; i < m_components.getSize(); ++i)
 			{
 			{
-				func(static_cast<const TComponent&>(*m_components[i]));
+				if(m_components[i]->getClassId() == TComponent::getStaticClassId())
+				{
+					func(static_cast<const TComponent&>(*m_components[i]));
+				}
 			}
 			}
 		}
 		}
 	}
 	}
@@ -139,11 +140,14 @@ public:
 	template<typename TComponent, typename TFunct>
 	template<typename TComponent, typename TFunct>
 	void iterateComponentsOfType(TFunct func)
 	void iterateComponentsOfType(TFunct func)
 	{
 	{
-		for(U32 i = 0; i < m_componentInfos.getSize(); ++i)
+		if(m_componentTypeMask & (1 << TComponent::getStaticClassId()))
 		{
 		{
-			if(m_componentInfos[i].getComponentClassId() == TComponent::getStaticClassId())
+			for(U32 i = 0; i < m_components.getSize(); ++i)
 			{
 			{
-				func(static_cast<TComponent&>(*m_components[i]));
+				if(m_components[i]->getClassId() == TComponent::getStaticClassId())
+				{
+					func(static_cast<TComponent&>(*m_components[i]));
+				}
 			}
 			}
 		}
 		}
 	}
 	}
@@ -152,11 +156,14 @@ public:
 	template<typename TComponent>
 	template<typename TComponent>
 	const TComponent* tryGetFirstComponentOfType() const
 	const TComponent* tryGetFirstComponentOfType() const
 	{
 	{
-		for(U32 i = 0; i < m_componentInfos.getSize(); ++i)
+		if(m_componentTypeMask & (1 << TComponent::getStaticClassId()))
 		{
 		{
-			if(m_componentInfos[i].getComponentClassId() == TComponent::getStaticClassId())
+			for(U32 i = 0; i < m_components.getSize(); ++i)
 			{
 			{
-				return static_cast<const TComponent*>(m_components[i]);
+				if(m_components[i]->getClassId() == TComponent::getStaticClassId())
+				{
+					return static_cast<const TComponent*>(m_components[i]);
+				}
 			}
 			}
 		}
 		}
 		return nullptr;
 		return nullptr;
@@ -191,12 +198,15 @@ public:
 	template<typename TComponent>
 	template<typename TComponent>
 	const TComponent* tryGetNthComponentOfType(U32 nth) const
 	const TComponent* tryGetNthComponentOfType(U32 nth) const
 	{
 	{
-		I32 inth = I32(nth);
-		for(U32 i = 0; i < m_componentInfos.getSize(); ++i)
+		if(m_componentTypeMask & (1 << TComponent::getStaticClassId()))
 		{
 		{
-			if(m_componentInfos[i].getComponentClassId() == TComponent::getStaticClassId() && inth-- == 0)
+			I32 inth = I32(nth);
+			for(U32 i = 0; i < m_components.getSize(); ++i)
 			{
 			{
-				return static_cast<const TComponent*>(m_components[i]);
+				if(m_components[i]->getClassId() == TComponent::getStaticClassId() && inth-- == 0)
+				{
+					return static_cast<const TComponent*>(m_components[i]);
+				}
 			}
 			}
 		}
 		}
 		return nullptr;
 		return nullptr;
@@ -230,7 +240,7 @@ public:
 	template<typename TComponent>
 	template<typename TComponent>
 	TComponent& getComponentAt(U32 idx)
 	TComponent& getComponentAt(U32 idx)
 	{
 	{
-		ANKI_ASSERT(m_componentInfos[idx].getComponentClassId() == TComponent::getStaticClassId());
+		ANKI_ASSERT(m_components[idx]->getClassId() == TComponent::getStaticClassId());
 		SceneComponent* c = m_components[idx];
 		SceneComponent* c = m_components[idx];
 		return *static_cast<TComponent*>(c);
 		return *static_cast<TComponent*>(c);
 	}
 	}
@@ -239,7 +249,7 @@ public:
 	template<typename TComponent>
 	template<typename TComponent>
 	const TComponent& getComponentAt(U32 idx) const
 	const TComponent& getComponentAt(U32 idx) const
 	{
 	{
-		ANKI_ASSERT(m_componentInfos[idx].getComponentClassId() == TComponent::getStaticClassId());
+		ANKI_ASSERT(m_components[idx]->getClassId() == 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);
 	}
 	}
@@ -259,85 +269,160 @@ public:
 		return count;
 		return count;
 	}
 	}
 
 
-protected:
-	SceneGraphExternalSubsystems& getExternalSubsystems() const;
+	/// Ignore parent nodes's transform.
+	void setIgnoreParentTransform(Bool ignore)
+	{
+		m_ignoreParentNodeTransform = ignore;
+	}
 
 
-	/// Create and append a component to the components container. The SceneNode has the ownership.
-	template<typename TComponent>
-	TComponent* newComponent()
+	const Transform& getLocalTransform() const
 	{
 	{
-		TComponent* comp = newInstance<TComponent>(getMemoryPool(), this);
+		return m_ltrf;
+	}
 
 
-		// Inform all other components that some component was added
-		for(SceneComponent* other : m_components)
-		{
-			ANKI_CALL_INTERNAL(other->onOtherComponentRemovedOrAddedReal(comp, true));
-		}
+	void setLocalTransform(const Transform& x)
+	{
+		m_ltrf = x;
+		m_localTransformDirty = true;
+	}
 
 
-		// Inform the current component about others
-		for(SceneComponent* other : m_components)
-		{
-			ANKI_CALL_INTERNAL(comp->onOtherComponentRemovedOrAddedReal(other, true));
-		}
+	void setLocalOrigin(const Vec4& x)
+	{
+		m_ltrf.setOrigin(x);
+		m_localTransformDirty = true;
+	}
 
 
-		m_components.emplaceBack(getMemoryPool(), comp);
-		m_componentInfos.emplaceBack(getMemoryPool(), *comp);
+	const Vec4& getLocalOrigin() const
+	{
+		return m_ltrf.getOrigin();
+	}
 
 
-		return comp;
+	void setLocalRotation(const Mat3x4& x)
+	{
+		m_ltrf.setRotation(x);
+		m_localTransformDirty = true;
 	}
 	}
 
 
-private:
-	/// This class packs a few info used by components.
-	class ComponentsArrayElement
+	const Mat3x4& getLocalRotation() const
 	{
 	{
-	public:
-		ComponentsArrayElement(const SceneComponent& comp)
-		{
-			m_classId = comp.getClassId() & 0x7F;
-			ANKI_ASSERT(m_classId == comp.getClassId());
-			m_feedbackComponent = comp.isFeedbackComponent();
-		}
+		return m_ltrf.getRotation();
+	}
 
 
-		ComponentsArrayElement(const ComponentsArrayElement& b)
-			: m_feedbackComponent(b.m_feedbackComponent)
-			, m_classId(b.m_classId)
-		{
-		}
+	void setLocalScale(F32 x)
+	{
+		m_ltrf.setScale(x);
+		m_localTransformDirty = true;
+	}
 
 
-		ComponentsArrayElement& operator=(const ComponentsArrayElement& b)
-		{
-			m_feedbackComponent = b.m_feedbackComponent;
-			m_classId = b.m_classId;
-			return *this;
-		}
+	F32 getLocalScale() const
+	{
+		return m_ltrf.getScale();
+	}
 
 
-		U8 getComponentClassId() const
-		{
-			return m_classId;
-		}
+	const Transform& getWorldTransform() const
+	{
+		return m_wtrf;
+	}
 
 
-		Bool isFeedbackComponent() const
-		{
-			return m_feedbackComponent;
-		}
+	const Transform& getPreviousWorldTransform() const
+	{
+		return m_prevWTrf;
+	}
 
 
-	private:
-		U8 m_feedbackComponent : 1;
-		U8 m_classId : 7;
-	};
+	/// @name Mess with the local transform
+	/// @{
+	void rotateLocalX(F32 angleRad)
+	{
+		m_ltrf.getRotation().rotateXAxis(angleRad);
+		m_localTransformDirty = true;
+	}
+	void rotateLocalY(F32 angleRad)
+	{
+		m_ltrf.getRotation().rotateYAxis(angleRad);
+		m_localTransformDirty = true;
+	}
+	void rotateLocalZ(F32 angleRad)
+	{
+		m_ltrf.getRotation().rotateZAxis(angleRad);
+		m_localTransformDirty = true;
+	}
+	void moveLocalX(F32 distance)
+	{
+		Vec3 x_axis = m_ltrf.getRotation().getColumn(0);
+		m_ltrf.getOrigin() += Vec4(x_axis, 0.0) * distance;
+		m_localTransformDirty = true;
+	}
+	void moveLocalY(F32 distance)
+	{
+		Vec3 y_axis = m_ltrf.getRotation().getColumn(1);
+		m_ltrf.getOrigin() += Vec4(y_axis, 0.0) * distance;
+		m_localTransformDirty = true;
+	}
+	void moveLocalZ(F32 distance)
+	{
+		Vec3 z_axis = m_ltrf.getRotation().getColumn(2);
+		m_ltrf.getOrigin() += Vec4(z_axis, 0.0) * distance;
+		m_localTransformDirty = true;
+	}
+	void scale(F32 s)
+	{
+		m_ltrf.getScale() *= s;
+		m_localTransformDirty = true;
+	}
 
 
-	static_assert(sizeof(ComponentsArrayElement) == sizeof(U8), "Wrong size");
+	void lookAtPoint(const Vec4& point)
+	{
+		m_ltrf.lookAt(point, Vec4(0.0f, 1.0f, 0.0f, 0.0f));
+		m_localTransformDirty = true;
+	}
+	/// @}
 
 
+	Bool movedThisFrame() const
+	{
+		return m_transformUpdatedThisFrame;
+	}
+
+	ANKI_INTERNAL Bool updateTransform(StackMemoryPool& framePool);
+
+	/// Create and append a component to the components container. The SceneNode has the ownership.
+	template<typename TComponent>
+	TComponent* newComponent()
+	{
+		TComponent* comp = newInstance<TComponent>(getMemoryPool(), this);
+		newComponentInternal(comp);
+		return comp;
+	}
+
+protected:
+	SceneGraphExternalSubsystems& getExternalSubsystems() const;
+
+private:
 	SceneGraph* m_scene = nullptr;
 	SceneGraph* m_scene = nullptr;
 	U64 m_uuid;
 	U64 m_uuid;
 	String m_name; ///< A unique name.
 	String m_name; ///< A unique name.
 
 
 	DynamicArray<SceneComponent*> m_components;
 	DynamicArray<SceneComponent*> m_components;
-	DynamicArray<ComponentsArrayElement> m_componentInfos; ///< Same size as m_components. Used to iterate fast.
+
+	U32 m_componentTypeMask = 0;
 
 
 	Timestamp m_maxComponentTimestamp = 0;
 	Timestamp m_maxComponentTimestamp = 0;
 
 
-	Bool m_markedForDeletion = false;
+	/// The transformation in local space.
+	Transform m_ltrf = Transform::getIdentity();
+
+	/// The transformation in world space (local combined with parent's transformation).
+	Transform m_wtrf = Transform::getIdentity();
+
+	/// Keep the previous transformation for checking if it moved.
+	Transform m_prevWTrf = Transform::getIdentity();
+
+	// Flags
+	Bool m_markedForDeletion : 1 = false;
+	Bool m_localTransformDirty : 1 = true;
+	Bool m_ignoreParentNodeTransform : 1 = false;
+	Bool m_transformUpdatedThisFrame : 1 = true;
+
+	void newComponentInternal(SceneComponent* newc);
 };
 };
 /// @}
 /// @}
 
 

+ 0 - 30
AnKi/Scene/SkyboxNode.cpp

@@ -1,30 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/SkyboxNode.h>
-#include <AnKi/Scene/Components/SkyboxComponent.h>
-#include <AnKi/Scene/Components/SpatialComponent.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
-
-namespace anki {
-
-SkyboxNode::~SkyboxNode()
-{
-}
-
-SkyboxNode::SkyboxNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-	newComponent<SkyboxComponent>();
-
-	SpatialComponent* spatialc = newComponent<SpatialComponent>();
-	spatialc->setAlwaysVisible(true);
-	spatialc->setSpatialOrigin(Vec3(0.0f));
-	spatialc->setUpdateOctreeBounds(false);
-
-	newComponent<MoveComponent>();
-}
-
-} // end namespace anki

+ 0 - 25
AnKi/Scene/SkyboxNode.h

@@ -1,25 +0,0 @@
-// Copyright (C) 2009-2022, 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>
-
-namespace anki {
-
-/// @addtogroup scene
-/// @{
-
-/// Skybox properties node.
-class SkyboxNode : public SceneNode
-{
-public:
-	SkyboxNode(SceneGraph* scene, CString name);
-
-	~SkyboxNode();
-};
-/// @}
-
-} // end namespace anki

+ 46 - 23
AnKi/Scene/Spatial.h

@@ -8,7 +8,6 @@
 #include <AnKi/Scene/Octree.h>
 #include <AnKi/Scene/Octree.h>
 #include <AnKi/Collision.h>
 #include <AnKi/Collision.h>
 
 
-
 namespace anki {
 namespace anki {
 
 
 /// @addtogroup scene
 /// @addtogroup scene
@@ -19,9 +18,10 @@ class Spatial
 {
 {
 public:
 public:
 	Spatial(SceneComponent* owner)
 	Spatial(SceneComponent* owner)
+		: m_owner(owner)
 	{
 	{
 		ANKI_ASSERT(owner);
 		ANKI_ASSERT(owner);
-		m_octreeInfo.m_userData = owner;
+		m_octreeInfo.m_userData = this;
 	}
 	}
 
 
 	Spatial(const Spatial&) = delete;
 	Spatial(const Spatial&) = delete;
@@ -33,6 +33,16 @@ public:
 
 
 	Spatial& operator=(const Spatial&) = delete;
 	Spatial& operator=(const Spatial&) = delete;
 
 
+	const SceneComponent& getSceneComponent() const
+	{
+		return *m_owner;
+	}
+
+	SceneComponent& getSceneComponent()
+	{
+		return *m_owner;
+	}
+
 	const Aabb& getAabbWorldSpace() const
 	const Aabb& getAabbWorldSpace() const
 	{
 	{
 		ANKI_ASSERT(!m_alwaysVisible || !m_dirty);
 		ANKI_ASSERT(!m_alwaysVisible || !m_dirty);
@@ -58,14 +68,14 @@ public:
 	}
 	}
 
 
 	template<typename TCollisionShape>
 	template<typename TCollisionShape>
-	void update(Octree& octree, const TCollisionShape& shape)
+	void setBoundingShape(const TCollisionShape& shape)
 	{
 	{
 		m_aabb = computeAabb(shape);
 		m_aabb = computeAabb(shape);
-		updateCommon(octree);
+		m_dirty = true;
 	}
 	}
 
 
 	template<typename TVec>
 	template<typename TVec>
-	void update(Octree& octree, ConstWeakArray<TVec> points)
+	void setBoundingShape(ConstWeakArray<TVec> points)
 	{
 	{
 		ANKI_ASSERT(pointCount > 0);
 		ANKI_ASSERT(pointCount > 0);
 		TVec min(kMaxF32), max(kMinF32);
 		TVec min(kMaxF32), max(kMinF32);
@@ -76,7 +86,31 @@ public:
 		}
 		}
 		m_aabb.setMin(min.xyz());
 		m_aabb.setMin(min.xyz());
 		m_aabb.setMax(max.xyz());
 		m_aabb.setMax(max.xyz());
-		updateCommon(octree);
+		m_dirty = true;
+	}
+
+	Bool update(Octree& octree)
+	{
+		const Bool updated = m_dirty;
+
+		if(m_dirty)
+		{
+			if(!m_alwaysVisible) [[likely]]
+			{
+				octree.place(m_aabb, &m_octreeInfo, m_updatesOctreeBounds);
+			}
+			else
+			{
+				octree.placeAlwaysVisible(&m_octreeInfo);
+			}
+
+			m_placed = true;
+			m_dirty = false;
+		}
+
+		ANKI_ASSERT(m_placed);
+		m_octreeInfo.reset();
+		return updated;
 	}
 	}
 
 
 	void removeFromOctree(Octree& octree)
 	void removeFromOctree(Octree& octree)
@@ -85,6 +119,7 @@ public:
 		{
 		{
 			octree.remove(m_octreeInfo);
 			octree.remove(m_octreeInfo);
 			m_placed = false;
 			m_placed = false;
+			m_dirty = true;
 		}
 		}
 	}
 	}
 
 
@@ -93,31 +128,19 @@ private:
 
 
 	OctreePlaceable m_octreeInfo;
 	OctreePlaceable m_octreeInfo;
 
 
+	SceneComponent* m_owner;
+
 	Bool m_placed : 1 = false;
 	Bool m_placed : 1 = false;
 	Bool m_updatesOctreeBounds : 1 = true;
 	Bool m_updatesOctreeBounds : 1 = true;
 	Bool m_alwaysVisible : 1 = false;
 	Bool m_alwaysVisible : 1 = false;
-
-	void updateCommon(Octree& octree)
-	{
-		if(!m_alwaysVisible) [[likely]]
-		{
-			octree.place(m_aabb, &m_octreeInfo, m_updatesOctreeBounds);
-		}
-		else
-		{
-			octree.placeAlwaysVisible(&m_octreeInfo);
-		}
-
-		m_placed = true;
-		m_octreeInfo.reset();
-	}
+	Bool m_dirty : 1 = true;
 };
 };
 
 
 template<>
 template<>
-void Spatial::update<Aabb>(Octree& octree, const Aabb& shape)
+inline void Spatial::setBoundingShape<Aabb>(const Aabb& shape)
 {
 {
 	m_aabb = shape;
 	m_aabb = shape;
-	updateCommon(octree);
+	m_dirty = true;
 }
 }
 /// @}
 /// @}
 
 

+ 0 - 21
AnKi/Scene/StaticCollisionNode.cpp

@@ -1,21 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/StaticCollisionNode.h>
-#include <AnKi/Scene/Components/BodyComponent.h>
-
-namespace anki {
-
-StaticCollisionNode::StaticCollisionNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-	newComponent<BodyComponent>();
-}
-
-StaticCollisionNode::~StaticCollisionNode()
-{
-}
-
-} // namespace anki

+ 0 - 27
AnKi/Scene/StaticCollisionNode.h

@@ -1,27 +0,0 @@
-// Copyright (C) 2009-2022, 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/Resource/Forward.h>
-#include <AnKi/Physics/Forward.h>
-
-namespace anki {
-
-/// @addtogroup scene
-/// @{
-
-/// Node that interacts with physics.
-class StaticCollisionNode : public SceneNode
-{
-public:
-	StaticCollisionNode(SceneGraph* scene, CString name);
-
-	~StaticCollisionNode();
-};
-/// @}
-
-} // end namespace anki

+ 0 - 54
AnKi/Scene/TriggerNode.cpp

@@ -1,54 +0,0 @@
-// Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Scene/TriggerNode.h>
-#include <AnKi/Scene/SceneGraph.h>
-#include <AnKi/Scene/Components/MoveComponent.h>
-#include <AnKi/Scene/Components/TriggerComponent.h>
-#include <AnKi/Physics/PhysicsWorld.h>
-#include <AnKi/Physics/PhysicsCollisionShape.h>
-#include <AnKi/Physics/PhysicsTrigger.h>
-
-namespace anki {
-
-class TriggerNode::MoveFeedbackComponent : public SceneComponent
-{
-	ANKI_SCENE_COMPONENT(TriggerNode::MoveFeedbackComponent)
-
-public:
-	MoveFeedbackComponent(SceneNode* node)
-		: SceneComponent(node, getStaticClassId(), true)
-	{
-	}
-
-	Error update(SceneComponentUpdateInfo& info, Bool& updated)
-	{
-		updated = false;
-
-		const MoveComponent& move = info.m_node->getFirstComponentOfType<MoveComponent>();
-		if(move.getTimestamp() == info.m_node->getGlobalTimestamp())
-		{
-			info.m_node->getFirstComponentOfType<TriggerComponent>().setWorldTransform(move.getWorldTransform());
-		}
-
-		return Error::kNone;
-	}
-};
-
-ANKI_SCENE_COMPONENT_STATICS(TriggerNode::MoveFeedbackComponent, -1.0f)
-
-TriggerNode::TriggerNode(SceneGraph* scene, CString name)
-	: SceneNode(scene, name)
-{
-	newComponent<MoveComponent>();
-	newComponent<MoveFeedbackComponent>();
-	newComponent<TriggerComponent>();
-}
-
-TriggerNode::~TriggerNode()
-{
-}
-
-} // end namespace anki

+ 0 - 29
AnKi/Scene/TriggerNode.h

@@ -1,29 +0,0 @@
-// Copyright (C) 2009-2022, 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/Physics/Forward.h>
-
-namespace anki {
-
-/// @addtogroup scene
-/// @{
-
-/// Trigger node.
-class TriggerNode : public SceneNode
-{
-public:
-	TriggerNode(SceneGraph* scene, CString name);
-
-	~TriggerNode();
-
-private:
-	class MoveFeedbackComponent;
-};
-/// @}
-
-} // end namespace anki

+ 267 - 293
AnKi/Scene/Visibility.cpp

@@ -5,7 +5,6 @@
 
 
 #include <AnKi/Scene/VisibilityInternal.h>
 #include <AnKi/Scene/VisibilityInternal.h>
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Scene/SceneGraph.h>
-#include <AnKi/Scene/Components/FrustumComponent.h>
 #include <AnKi/Scene/Components/LensFlareComponent.h>
 #include <AnKi/Scene/Components/LensFlareComponent.h>
 #include <AnKi/Scene/Components/ModelComponent.h>
 #include <AnKi/Scene/Components/ModelComponent.h>
 #include <AnKi/Scene/Components/ReflectionProbeComponent.h>
 #include <AnKi/Scene/Components/ReflectionProbeComponent.h>
@@ -13,12 +12,11 @@
 #include <AnKi/Scene/Components/MoveComponent.h>
 #include <AnKi/Scene/Components/MoveComponent.h>
 #include <AnKi/Scene/Components/FogDensityComponent.h>
 #include <AnKi/Scene/Components/FogDensityComponent.h>
 #include <AnKi/Scene/Components/LightComponent.h>
 #include <AnKi/Scene/Components/LightComponent.h>
-#include <AnKi/Scene/Components/SpatialComponent.h>
 #include <AnKi/Scene/Components/GlobalIlluminationProbeComponent.h>
 #include <AnKi/Scene/Components/GlobalIlluminationProbeComponent.h>
-#include <AnKi/Scene/Components/GenericGpuComputeJobComponent.h>
 #include <AnKi/Scene/Components/ParticleEmitterComponent.h>
 #include <AnKi/Scene/Components/ParticleEmitterComponent.h>
 #include <AnKi/Scene/Components/UiComponent.h>
 #include <AnKi/Scene/Components/UiComponent.h>
 #include <AnKi/Scene/Components/SkyboxComponent.h>
 #include <AnKi/Scene/Components/SkyboxComponent.h>
+#include <AnKi/Scene/Components/CameraComponent.h>
 #include <AnKi/Renderer/MainRenderer.h>
 #include <AnKi/Renderer/MainRenderer.h>
 #include <AnKi/Util/Logger.h>
 #include <AnKi/Util/Logger.h>
 #include <AnKi/Util/ThreadHive.h>
 #include <AnKi/Util/ThreadHive.h>
@@ -26,7 +24,7 @@
 
 
 namespace anki {
 namespace anki {
 
 
-static U8 computeLod(const FrustumComponent& frc, F32 distanceFromTheNearPlane)
+static U8 computeLod(const Frustum& frustum, F32 distanceFromTheNearPlane)
 {
 {
 	static_assert(kMaxLodCount == 3, "Wrong assumption");
 	static_assert(kMaxLodCount == 3, "Wrong assumption");
 	U8 lod;
 	U8 lod;
@@ -35,11 +33,11 @@ static U8 computeLod(const FrustumComponent& frc, F32 distanceFromTheNearPlane)
 		// In RT objects may fall behind the camera, use the max LOD on those
 		// In RT objects may fall behind the camera, use the max LOD on those
 		lod = 2;
 		lod = 2;
 	}
 	}
-	else if(distanceFromTheNearPlane <= frc.getLodDistance(0))
+	else if(distanceFromTheNearPlane <= frustum.getLodDistance(0))
 	{
 	{
 		lod = 0;
 		lod = 0;
 	}
 	}
-	else if(distanceFromTheNearPlane <= frc.getLodDistance(1))
+	else if(distanceFromTheNearPlane <= frustum.getLodDistance(1))
 	{
 	{
 		lod = 1;
 		lod = 1;
 	}
 	}
@@ -51,58 +49,65 @@ static U8 computeLod(const FrustumComponent& frc, F32 distanceFromTheNearPlane)
 	return lod;
 	return lod;
 }
 }
 
 
-static Bool spatialInsideFrustum(const FrustumComponent& frc, const SpatialComponent& spc)
+static FrustumFlags getLightFrustumFlags()
 {
 {
-	switch(spc.getCollisionShapeType())
-	{
-	case CollisionShapeType::kObb:
-		return frc.insideFrustum(spc.getCollisionShape<Obb>());
-		break;
-	case CollisionShapeType::kAabb:
-		return frc.insideFrustum(spc.getCollisionShape<Aabb>());
-		break;
-	case CollisionShapeType::kSphere:
-		return frc.insideFrustum(spc.getCollisionShape<Sphere>());
-		break;
-	case CollisionShapeType::kConvexHull:
-		return frc.insideFrustum(spc.getCollisionShape<ConvexHullShape>());
-		break;
-	default:
-		ANKI_ASSERT(0);
-		return false;
-	}
+	FrustumFlags flags;
+	flags.m_gatherShadowCasterModelComponents = true;
+	return flags;
+}
+
+static FrustumFlags getProbeFrustumFlags()
+{
+	FrustumFlags flags;
+	flags.m_gatherModelComponents = true;
+	flags.m_gatherLightComponents = true;
+	flags.m_gatherSkyComponents = true;
+	return flags;
+}
+
+static FrustumFlags getCameraFrustumFlags()
+{
+	FrustumFlags flags;
+	flags.m_gatherModelComponents = true;
+	flags.m_gatherParticleComponents = true;
+	flags.m_gatherProbeComponents = true;
+	flags.m_gatherLightComponents = true;
+	flags.m_gatherLensFlareComponents = true;
+	flags.m_gatherDecalComponents = true;
+	flags.m_gatherFogDensityComponents = true;
+	flags.m_gatherUiComponents = true;
+	flags.m_gatherSkyComponents = true;
+	flags.m_coverageBuffer = true;
+	flags.m_earlyZ = true;
+	flags.m_nonDirectionalLightsCastShadow = true;
+	return flags;
 }
 }
 
 
-/// Used to silent warnings
-template<typename TComponent>
-Bool getComponent(SceneNode& node, TComponent*& comp)
+static FrustumFlags getCameraExtendedFrustumFlags()
 {
 {
-	comp = node.tryGetFirstComponentOfType<TComponent>();
-	return comp != nullptr;
+	FrustumFlags flags;
+	flags.m_gatherModelComponents = true;
+	flags.m_gatherLightComponents = true;
+	flags.m_gatherSkyComponents = true;
+	return flags;
 }
 }
 
 
-void VisibilityContext::submitNewWork(const FrustumComponent& frc, const FrustumComponent& primaryFrustum,
+void VisibilityContext::submitNewWork(const VisibilityFrustum& frustum, const VisibilityFrustum& primaryFrustum,
 									  RenderQueue& rqueue, ThreadHive& hive)
 									  RenderQueue& rqueue, ThreadHive& hive)
 {
 {
 	ANKI_TRACE_SCOPED_EVENT(SCENE_VIS_SUBMIT_WORK);
 	ANKI_TRACE_SCOPED_EVENT(SCENE_VIS_SUBMIT_WORK);
 
 
-	// Check enabled and make sure that the results are null (this can happen on multiple on circular viewing)
-	if(ANKI_UNLIKELY(frc.getEnabledVisibilityTests() == FrustumComponentVisibilityTestFlag::kNone))
+	rqueue.m_cameraTransform = Mat3x4(frustum.m_frustum->getWorldTransform());
+	rqueue.m_viewMatrix = frustum.m_frustum->getViewMatrix();
+	rqueue.m_projectionMatrix = frustum.m_frustum->getProjectionMatrix();
+	rqueue.m_viewProjectionMatrix = frustum.m_frustum->getViewProjectionMatrix();
+	rqueue.m_previousViewProjectionMatrix = frustum.m_frustum->getPreviousViewProjectionMatrix();
+	rqueue.m_cameraNear = frustum.m_frustum->getNear();
+	rqueue.m_cameraFar = frustum.m_frustum->getFar();
+	if(frustum.m_frustum->getFrustumType() == FrustumType::kPerspective)
 	{
 	{
-		return;
-	}
-
-	rqueue.m_cameraTransform = Mat3x4(frc.getWorldTransform());
-	rqueue.m_viewMatrix = frc.getViewMatrix();
-	rqueue.m_projectionMatrix = frc.getProjectionMatrix();
-	rqueue.m_viewProjectionMatrix = frc.getViewProjectionMatrix();
-	rqueue.m_previousViewProjectionMatrix = frc.getPreviousViewProjectionMatrix();
-	rqueue.m_cameraNear = frc.getNear();
-	rqueue.m_cameraFar = frc.getFar();
-	if(frc.getFrustumType() == FrustumType::kPerspective)
-	{
-		rqueue.m_cameraFovX = frc.getFovX();
-		rqueue.m_cameraFovY = frc.getFovY();
+		rqueue.m_cameraFovX = frustum.m_frustum->getFovX();
+		rqueue.m_cameraFovY = frustum.m_frustum->getFovY();
 	}
 	}
 	else
 	else
 	{
 	{
@@ -113,26 +118,26 @@ void VisibilityContext::submitNewWork(const FrustumComponent& frc, const Frustum
 
 
 	// Check if this frc was tested before
 	// Check if this frc was tested before
 	{
 	{
-		LockGuard<Mutex> l(m_mtx);
+		LockGuard<Mutex> l(m_testedFrustumsMtx);
 
 
 		// Check if already in the list
 		// Check if already in the list
-		for(const FrustumComponent* x : m_testedFrcs)
+		for(const Frustum* x : m_testedFrustums)
 		{
 		{
-			if(x == &frc)
+			if(x == frustum.m_frustum)
 			{
 			{
 				return;
 				return;
 			}
 			}
 		}
 		}
 
 
 		// Not there, push it
 		// Not there, push it
-		m_testedFrcs.pushBack(pool, &frc);
+		m_testedFrustums.pushBack(pool, frustum.m_frustum);
 	}
 	}
 
 
 	// Prepare the ctx
 	// Prepare the ctx
 	FrustumVisibilityContext* frcCtx = newInstance<FrustumVisibilityContext>(pool);
 	FrustumVisibilityContext* frcCtx = newInstance<FrustumVisibilityContext>(pool);
 	frcCtx->m_visCtx = this;
 	frcCtx->m_visCtx = this;
-	frcCtx->m_frc = &frc;
-	frcCtx->m_primaryFrustum = &primaryFrustum;
+	frcCtx->m_frustum = frustum;
+	frcCtx->m_primaryFrustum = primaryFrustum;
 	frcCtx->m_queueViews.create(pool, hive.getThreadCount());
 	frcCtx->m_queueViews.create(pool, hive.getThreadCount());
 	frcCtx->m_visTestsSignalSem = hive.newSemaphore(1);
 	frcCtx->m_visTestsSignalSem = hive.newSemaphore(1);
 	frcCtx->m_renderQueue = &rqueue;
 	frcCtx->m_renderQueue = &rqueue;
@@ -142,7 +147,7 @@ void VisibilityContext::submitNewWork(const FrustumComponent& frc, const Frustum
 
 
 	// Software rasterizer task
 	// Software rasterizer task
 	ThreadHiveSemaphore* prepareRasterizerSem = nullptr;
 	ThreadHiveSemaphore* prepareRasterizerSem = nullptr;
-	if(!!(frc.getEnabledVisibilityTests() & FrustumComponentVisibilityTestFlag::kOccluders) && frc.hasCoverageBuffer())
+	if(frustum.m_coverageBuffer && frustum.m_frustum->hasCoverageBuffer())
 	{
 	{
 		// Gather triangles task
 		// Gather triangles task
 		ThreadHiveTask fillDepthTask =
 		ThreadHiveTask fillDepthTask =
@@ -154,10 +159,12 @@ void VisibilityContext::submitNewWork(const FrustumComponent& frc, const Frustum
 		prepareRasterizerSem = fillDepthTask.m_signalSemaphore;
 		prepareRasterizerSem = fillDepthTask.m_signalSemaphore;
 	}
 	}
 
 
-	if(!!(frc.getEnabledVisibilityTests() & FrustumComponentVisibilityTestFlag::kOccluders))
+	if(frustum.m_coverageBuffer)
 	{
 	{
-		rqueue.m_fillCoverageBufferCallback = FrustumComponent::fillCoverageBufferCallback;
-		rqueue.m_fillCoverageBufferCallbackUserData = static_cast<void*>(const_cast<FrustumComponent*>(&frc));
+		rqueue.m_fillCoverageBufferCallback = [](void* ud, F32* depthValues, U32 width, U32 height) {
+			static_cast<Frustum*>(ud)->setCoverageBuffer(depthValues, width, height);
+		};
+		rqueue.m_fillCoverageBufferCallbackUserData = static_cast<Frustum*>(frustum.m_frustum);
 	}
 	}
 
 
 	// Gather visibles from the octree. No need to signal anything because it will spawn new tasks
 	// Gather visibles from the octree. No need to signal anything because it will spawn new tasks
@@ -183,7 +190,7 @@ void FillRasterizerWithCoverageTask::fill()
 	ConstWeakArray<F32> depthBuff;
 	ConstWeakArray<F32> depthBuff;
 	U32 width;
 	U32 width;
 	U32 height;
 	U32 height;
-	m_frcCtx->m_frc->getCoverageBufferInfo(depthBuff, width, height);
+	m_frcCtx->m_frustum.m_frustum->getCoverageBufferInfo(depthBuff, width, height);
 	ANKI_ASSERT(width > 0 && height > 0 && depthBuff.getSize() > 0);
 	ANKI_ASSERT(width > 0 && height > 0 && depthBuff.getSize() > 0);
 
 
 	// Init the rasterizer
 	// Init the rasterizer
@@ -191,8 +198,9 @@ void FillRasterizerWithCoverageTask::fill()
 	{
 	{
 		m_frcCtx->m_r = newInstance<SoftwareRasterizer>(pool);
 		m_frcCtx->m_r = newInstance<SoftwareRasterizer>(pool);
 		m_frcCtx->m_r->init(&pool);
 		m_frcCtx->m_r->init(&pool);
-		m_frcCtx->m_r->prepare(Mat4(m_frcCtx->m_frc->getPreviousViewMatrix(1), Vec4(0.0f, 0.0f, 0.0f, 1.0f)),
-							   m_frcCtx->m_frc->getPreviousProjectionMatrix(1), width, height);
+		m_frcCtx->m_r->prepare(
+			Mat4(m_frcCtx->m_frustum.m_frustum->getPreviousViewMatrix(1), Vec4(0.0f, 0.0f, 0.0f, 1.0f)),
+			m_frcCtx->m_frustum.m_frustum->getPreviousProjectionMatrix(1), width, height);
 
 
 		// Do the work
 		// Do the work
 		m_frcCtx->m_r->fillDepthBuffer(depthBuff);
 		m_frcCtx->m_r->fillDepthBuffer(depthBuff);
@@ -209,7 +217,7 @@ void GatherVisiblesFromOctreeTask::gather(ThreadHive& hive)
 	m_frcCtx->m_visCtx->m_scene->getOctree().walkTree(
 	m_frcCtx->m_visCtx->m_scene->getOctree().walkTree(
 		testIdx,
 		testIdx,
 		[&](const Aabb& box) {
 		[&](const Aabb& box) {
-			Bool visible = m_frcCtx->m_frc->insideFrustum(box);
+			Bool visible = m_frcCtx->m_frustum.m_frustum->insideFrustum(box);
 			if(visible && m_frcCtx->m_r)
 			if(visible && m_frcCtx->m_r)
 			{
 			{
 				visible = m_frcCtx->m_r->visibilityTest(box);
 				visible = m_frcCtx->m_r->visibilityTest(box);
@@ -219,11 +227,11 @@ void GatherVisiblesFromOctreeTask::gather(ThreadHive& hive)
 		},
 		},
 		[&](void* placeableUserData) {
 		[&](void* placeableUserData) {
 			ANKI_ASSERT(placeableUserData);
 			ANKI_ASSERT(placeableUserData);
-			SpatialComponent* scomp = static_cast<SpatialComponent*>(placeableUserData);
+			Spatial* spatial = static_cast<Spatial*>(placeableUserData);
 
 
 			ANKI_ASSERT(m_spatialCount < m_spatials.getSize());
 			ANKI_ASSERT(m_spatialCount < m_spatials.getSize());
 
 
-			m_spatials[m_spatialCount++] = scomp;
+			m_spatials[m_spatialCount++] = spatial;
 
 
 			if(m_spatialCount == m_spatials.getSize())
 			if(m_spatialCount == m_spatials.getSize())
 			{
 			{
@@ -267,132 +275,62 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 {
 {
 	ANKI_TRACE_SCOPED_EVENT(SCENE_VIS_TEST);
 	ANKI_TRACE_SCOPED_EVENT(SCENE_VIS_TEST);
 
 
-	const FrustumComponent& testedFrc = *m_frcCtx->m_frc;
-	const FrustumComponentVisibilityTestFlag frustumFlags = testedFrc.getEnabledVisibilityTests();
-	ANKI_ASSERT(frustumFlags != FrustumComponentVisibilityTestFlag::kNone);
-	ANKI_ASSERT(m_frcCtx->m_primaryFrustum);
-	const FrustumComponent& primaryFrc = *m_frcCtx->m_primaryFrustum;
+	const Frustum& testedFrustum = *m_frcCtx->m_frustum.m_frustum;
+	ANKI_ASSERT(m_frcCtx->m_primaryFrustum.m_frustum);
+	const FrustumFlags frustumFlags = m_frcCtx->m_frustum;
+	const Frustum& primaryFrustum = *m_frcCtx->m_primaryFrustum.m_frustum;
 
 
-	const SceneNode& testedNode = testedFrc.getSceneNode();
 	StackMemoryPool& pool = m_frcCtx->m_visCtx->m_scene->getFrameMemoryPool();
 	StackMemoryPool& pool = m_frcCtx->m_visCtx->m_scene->getFrameMemoryPool();
 
 
-	Timestamp& timestamp = m_frcCtx->m_queueViews[taskId].m_timestamp;
-	timestamp = testedNode.getComponentMaxTimestamp();
-
-	const Bool wantsEarlyZ =
-		!!(frustumFlags & FrustumComponentVisibilityTestFlag::kEarlyZ) && m_frcCtx->m_visCtx->m_earlyZDist > 0.0f;
+	const Bool wantsEarlyZ = m_frcCtx->m_frustum.m_earlyZ && testedFrustum.getEarlyZDistance() > 0.0f;
 
 
-	const Bool testedNodeIsProbe =
-		testedNode.tryGetFirstComponentOfType<ReflectionProbeComponent>()
-		|| testedNode.tryGetFirstComponentOfType<GlobalIlluminationProbeComponent>(); // TODO hack for now
+	WeakArray<RenderQueue> nextQueues;
+	WeakArray<VisibilityFrustum> nextFrustums;
 
 
 	// Iterate
 	// Iterate
 	RenderQueueView& result = m_frcCtx->m_queueViews[taskId];
 	RenderQueueView& result = m_frcCtx->m_queueViews[taskId];
 	for(U i = 0; i < m_spatialToTestCount; ++i)
 	for(U i = 0; i < m_spatialToTestCount; ++i)
 	{
 	{
-		SpatialComponent* spatialC = m_spatialsToTest[i];
-		ANKI_ASSERT(spatialC);
-		SceneNode& node = spatialC->getSceneNode();
-
-		// Skip if it is the same
-		if(ANKI_UNLIKELY(&testedNode == &node))
-		{
-			continue;
-		}
-
-		// Check what components the frustum needs
-		Bool wantNode = false;
-
-		const ModelComponent* modelc = nullptr;
-		wantNode |= !!(frustumFlags & FrustumComponentVisibilityTestFlag::kRenderComponents)
-					&& getComponent(node, modelc) && modelc->isEnabled();
-
-		wantNode |= !!(frustumFlags & FrustumComponentVisibilityTestFlag::kShadowCasterRenderComponents)
-					&& getComponent(node, modelc) && modelc->isEnabled() && modelc->getCastsShadow();
-
-		// TODO ray tracing
-
-		const ParticleEmitterComponent* partemitc = nullptr;
-		wantNode |= !!(frustumFlags & FrustumComponentVisibilityTestFlag::kRenderComponents)
-					&& getComponent(node, partemitc) && partemitc->isEnabled();
-
-		const LightComponent* lc = nullptr;
-		wantNode |= !!(frustumFlags & FrustumComponentVisibilityTestFlag::kLights) && getComponent(node, lc);
-
-		const LensFlareComponent* lfc = nullptr;
-		wantNode |= !!(frustumFlags & FrustumComponentVisibilityTestFlag::kLensFlares) && getComponent(node, lfc);
-
-		const ReflectionProbeComponent* reflc = nullptr;
-		wantNode |=
-			!!(frustumFlags & FrustumComponentVisibilityTestFlag::kReflectionProbes) && getComponent(node, reflc);
-
-		DecalComponent* decalc = nullptr;
-		wantNode |= !!(frustumFlags & FrustumComponentVisibilityTestFlag::kDecals) && getComponent(node, decalc);
-
-		const FogDensityComponent* fogc = nullptr;
-		wantNode |=
-			!!(frustumFlags & FrustumComponentVisibilityTestFlag::kFogDensityVolumes) && getComponent(node, fogc);
+		Spatial* spatial = m_spatialsToTest[i];
+		ANKI_ASSERT(spatial);
+		SceneComponent& comp = spatial->getSceneComponent();
+		const Aabb& aabb = spatial->getAabbWorldSpace();
 
 
-		GlobalIlluminationProbeComponent* giprobec = nullptr;
-		wantNode |= !!(frustumFlags & FrustumComponentVisibilityTestFlag::kGlobalIlluminationProbes)
-					&& getComponent(node, giprobec);
+		auto isInside = [&] {
+			return spatial->getAlwaysVisible() || (testedFrustum.insideFrustum(aabb) && testAgainstRasterizer(aabb));
+		};
 
 
-		GenericGpuComputeJobComponent* computec = nullptr;
-		wantNode |=
-			!!(frustumFlags & FrustumComponentVisibilityTestFlag::kGenericComputeJobs) && getComponent(node, computec);
-
-		UiComponent* uic = nullptr;
-		wantNode |= !!(frustumFlags & FrustumComponentVisibilityTestFlag::kUi) && getComponent(node, uic);
-
-		SkyboxComponent* skyboxc = nullptr;
-		wantNode |= !!(frustumFlags & FrustumComponentVisibilityTestFlag::kSkybox) && getComponent(node, skyboxc);
-
-		if(ANKI_UNLIKELY(!wantNode))
-		{
-			// Skip node
-			continue;
-		}
-
-		const SpatialComponent* spatialc = node.tryGetFirstComponentOfType<SpatialComponent>();
-		if(ANKI_UNLIKELY(spatialc == nullptr))
+		if(comp.getClassId() == ModelComponent::getStaticClassId())
 		{
 		{
-			continue;
-		}
-
-		if(!spatialc->getAlwaysVisible()
-		   && (!spatialInsideFrustum(testedFrc, *spatialc) || !testAgainstRasterizer(spatialc->getAabbWorldSpace())))
-		{
-			continue;
-		}
-
-		WeakArray<RenderQueue> nextQueues;
-		WeakArray<FrustumComponent> nextQueueFrustumComponents; // Optional
-
-		if(modelc)
-		{
-			const Plane& nearPlane = primaryFrc.getViewPlanes()[FrustumPlaneType::kNear];
-			const F32 distanceFromCamera = max(0.0f, testPlane(nearPlane, spatialc->getAabbWorldSpace()));
+			const ModelComponent& modelc = static_cast<ModelComponent&>(comp);
+			if(!modelc.isEnabled() || (frustumFlags.m_gatherShadowCasterModelComponents && !modelc.getCastsShadow())
+			   || !isInside())
+			{
+				continue;
+			}
 
 
-			const U8 lod = (testedNodeIsProbe) ? 0 : computeLod(primaryFrc, distanceFromCamera);
+			const Plane& nearPlane = primaryFrustum.getViewPlanes()[FrustumPlaneType::kNear];
+			const F32 distanceFromCamera = max(0.0f, testPlane(nearPlane, aabb));
+			const U8 lod = computeLod(primaryFrustum, distanceFromCamera);
 
 
 			WeakArray<RenderableQueueElement> elements;
 			WeakArray<RenderableQueueElement> elements;
-			modelc->setupRenderableQueueElements(lod, RenderingTechnique::kGBuffer, pool, elements);
+			modelc.setupRenderableQueueElements(lod, RenderingTechnique::kGBuffer, pool, elements);
 			for(RenderableQueueElement& el : elements)
 			for(RenderableQueueElement& el : elements)
 			{
 			{
 				el.m_distanceFromCamera = distanceFromCamera;
 				el.m_distanceFromCamera = distanceFromCamera;
 				*result.m_renderables.newElement(pool) = el;
 				*result.m_renderables.newElement(pool) = el;
 			}
 			}
 
 
-			modelc->setupRenderableQueueElements(lod, RenderingTechnique::kForward, pool, elements);
+			modelc.setupRenderableQueueElements(lod, RenderingTechnique::kForward, pool, elements);
 			for(RenderableQueueElement& el : elements)
 			for(RenderableQueueElement& el : elements)
 			{
 			{
 				el.m_distanceFromCamera = distanceFromCamera;
 				el.m_distanceFromCamera = distanceFromCamera;
 				*result.m_forwardShadingRenderables.newElement(pool) = el;
 				*result.m_forwardShadingRenderables.newElement(pool) = el;
 			}
 			}
 
 
-			if(wantsEarlyZ && distanceFromCamera < m_frcCtx->m_visCtx->m_earlyZDist)
+			if(wantsEarlyZ && distanceFromCamera < testedFrustum.getEarlyZDistance())
 			{
 			{
-				modelc->setupRenderableQueueElements(lod, RenderingTechnique::kGBufferEarlyZ, pool, elements);
+				modelc.setupRenderableQueueElements(lod, RenderingTechnique::kGBufferEarlyZ, pool, elements);
 				for(RenderableQueueElement& el : elements)
 				for(RenderableQueueElement& el : elements)
 				{
 				{
 					el.m_distanceFromCamera = distanceFromCamera;
 					el.m_distanceFromCamera = distanceFromCamera;
@@ -400,65 +338,76 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 				}
 				}
 			}
 			}
 		}
 		}
-
-		if(partemitc)
+		else if(comp.getClassId() == ParticleEmitterComponent::getStaticClassId())
 		{
 		{
-			const Plane& nearPlane = primaryFrc.getViewPlanes()[FrustumPlaneType::kNear];
-			const F32 distanceFromCamera = max(0.0f, testPlane(nearPlane, spatialc->getAabbWorldSpace()));
+			const ParticleEmitterComponent& partemitc = static_cast<ParticleEmitterComponent&>(comp);
+			if(!frustumFlags.m_gatherParticleComponents || !partemitc.isEnabled() || !isInside())
+			{
+				continue;
+			}
+
+			const Plane& nearPlane = primaryFrustum.getViewPlanes()[FrustumPlaneType::kNear];
+			const F32 distanceFromCamera = max(0.0f, testPlane(nearPlane, aabb));
 
 
 			WeakArray<RenderableQueueElement> elements;
 			WeakArray<RenderableQueueElement> elements;
-			partemitc->setupRenderableQueueElements(RenderingTechnique::kGBuffer, pool, elements);
+			partemitc.setupRenderableQueueElements(RenderingTechnique::kGBuffer, pool, elements);
 			for(RenderableQueueElement& el : elements)
 			for(RenderableQueueElement& el : elements)
 			{
 			{
 				el.m_distanceFromCamera = distanceFromCamera;
 				el.m_distanceFromCamera = distanceFromCamera;
 				*result.m_renderables.newElement(pool) = el;
 				*result.m_renderables.newElement(pool) = el;
 			}
 			}
 
 
-			partemitc->setupRenderableQueueElements(RenderingTechnique::kForward, pool, elements);
+			partemitc.setupRenderableQueueElements(RenderingTechnique::kForward, pool, elements);
 			for(RenderableQueueElement& el : elements)
 			for(RenderableQueueElement& el : elements)
 			{
 			{
 				el.m_distanceFromCamera = distanceFromCamera;
 				el.m_distanceFromCamera = distanceFromCamera;
 				*result.m_forwardShadingRenderables.newElement(pool) = el;
 				*result.m_forwardShadingRenderables.newElement(pool) = el;
 			}
 			}
 		}
 		}
-
-		if(lc)
+		else if(comp.getClassId() == LightComponent::getStaticClassId())
 		{
 		{
+			const LightComponent& lightc = static_cast<LightComponent&>(comp);
+			if(!frustumFlags.m_gatherLightComponents
+			   || (lightc.getLightComponentType() != LightComponentType::kDirectional && !isInside()))
+			{
+				continue;
+			}
+
 			// Check if it casts shadow
 			// Check if it casts shadow
-			Bool castsShadow = lc->getShadowEnabled();
-			if(castsShadow && lc->getLightComponentType() != LightComponentType::kDirectional)
+			Bool castsShadow = lightc.getShadowEnabled();
+			if(castsShadow && lightc.getLightComponentType() != LightComponentType::kDirectional)
 			{
 			{
 				// Extra check
 				// Extra check
 
 
 				// Compute distance from the frustum
 				// Compute distance from the frustum
-				const Plane& nearPlane = primaryFrc.getViewPlanes()[FrustumPlaneType::kNear];
-				const F32 distFromFrustum = max(0.0f, testPlane(nearPlane, spatialc->getAabbWorldSpace()));
+				const Plane& nearPlane = primaryFrustum.getViewPlanes()[FrustumPlaneType::kNear];
+				const F32 distFromFrustum = max(0.0f, testPlane(nearPlane, aabb));
 
 
 				const F32 shadowEffectiveDistance =
 				const F32 shadowEffectiveDistance =
-					(primaryFrc.getShadowCascadeCount() > 0)
-						? primaryFrc.getShadowCascadeDistance(primaryFrc.getShadowCascadeCount() - 1)
-						: primaryFrc.getFar();
+					(primaryFrustum.getShadowCascadeCount() > 0)
+						? primaryFrustum.getShadowCascadeDistance(primaryFrustum.getShadowCascadeCount() - 1)
+						: primaryFrustum.getFar();
 				castsShadow = distFromFrustum < shadowEffectiveDistance;
 				castsShadow = distFromFrustum < shadowEffectiveDistance;
 			}
 			}
 
 
-			switch(lc->getLightComponentType())
+			switch(lightc.getLightComponentType())
 			{
 			{
 			case LightComponentType::kPoint:
 			case LightComponentType::kPoint:
 			{
 			{
 				PointLightQueueElement* el = result.m_pointLights.newElement(pool);
 				PointLightQueueElement* el = result.m_pointLights.newElement(pool);
-				lc->setupPointLightQueueElement(*el);
+				lightc.setupPointLightQueueElement(*el);
 
 
-				if(castsShadow && !!(frustumFlags & FrustumComponentVisibilityTestFlag::kPointLightShadowsEnabled))
+				if(castsShadow && frustumFlags.m_nonDirectionalLightsCastShadow)
 				{
 				{
-					RenderQueue* a = newArray<RenderQueue>(pool, 6);
-					nextQueues = WeakArray<RenderQueue>(a, 6);
-
-					el->m_shadowRenderQueues[0] = &nextQueues[0];
-					el->m_shadowRenderQueues[1] = &nextQueues[1];
-					el->m_shadowRenderQueues[2] = &nextQueues[2];
-					el->m_shadowRenderQueues[3] = &nextQueues[3];
-					el->m_shadowRenderQueues[4] = &nextQueues[4];
-					el->m_shadowRenderQueues[5] = &nextQueues[5];
+					nextQueues = WeakArray<RenderQueue>(newArray<RenderQueue>(pool, 6), 6);
+					nextFrustums = WeakArray<VisibilityFrustum>(newArray<VisibilityFrustum>(pool, 6), 6);
+
+					for(U32 f = 0; f < 6; ++f)
+					{
+						el->m_shadowRenderQueues[f] = &nextQueues[f];
+						nextFrustums[f].m_frustum = &lightc.getFrustums()[f];
+						static_cast<FrustumFlags&>(nextFrustums[f]) = getLightFrustumFlags();
+					}
 				}
 				}
 				else
 				else
 				{
 				{
@@ -470,13 +419,16 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 			case LightComponentType::kSpot:
 			case LightComponentType::kSpot:
 			{
 			{
 				SpotLightQueueElement* el = result.m_spotLights.newElement(pool);
 				SpotLightQueueElement* el = result.m_spotLights.newElement(pool);
-				lc->setupSpotLightQueueElement(*el);
+				lightc.setupSpotLightQueueElement(*el);
 
 
-				if(castsShadow && !!(frustumFlags & FrustumComponentVisibilityTestFlag::kSpotLightShadowsEnabled))
+				if(castsShadow && frustumFlags.m_nonDirectionalLightsCastShadow)
 				{
 				{
-					RenderQueue* a = newInstance<RenderQueue>(pool);
-					nextQueues = WeakArray<RenderQueue>(a, 1);
-					el->m_shadowRenderQueue = a;
+					nextQueues = WeakArray<RenderQueue>(newInstance<RenderQueue>(pool), 1);
+					el->m_shadowRenderQueue = &nextQueues[0];
+
+					nextFrustums = WeakArray<VisibilityFrustum>(newInstance<VisibilityFrustum>(pool), 1);
+					nextFrustums[0].m_frustum = &lightc.getFrustums()[0];
+					static_cast<FrustumFlags&>(nextFrustums[0]) = getLightFrustumFlags();
 				}
 				}
 				else
 				else
 				{
 				{
@@ -487,170 +439,192 @@ void VisibilityTestTask::test(ThreadHive& hive, U32 taskId)
 			}
 			}
 			case LightComponentType::kDirectional:
 			case LightComponentType::kDirectional:
 			{
 			{
-				ANKI_ASSERT(lc->getShadowEnabled() == true && "Only with shadow for now");
+				ANKI_ASSERT(lightc.getShadowEnabled() == true && "Only with shadow for now");
 
 
-				U32 cascadeCount = max(testedFrc.getShadowCascadeCount(), U32(castsShadow));
+				U32 cascadeCount = max(testedFrustum.getShadowCascadeCount(), U32(castsShadow));
 				if(!castsShadow)
 				if(!castsShadow)
 				{
 				{
 					cascadeCount = 0;
 					cascadeCount = 0;
 				}
 				}
 				else
 				else
 				{
 				{
-					cascadeCount = max<U32>(testedFrc.getShadowCascadeCount(), 1);
+					cascadeCount = max<U32>(testedFrustum.getShadowCascadeCount(), 1);
 				}
 				}
 				ANKI_ASSERT(cascadeCount <= kMaxShadowCascades);
 				ANKI_ASSERT(cascadeCount <= kMaxShadowCascades);
 
 
 				// Create some dummy frustum components and initialize them
 				// Create some dummy frustum components and initialize them
-				WeakArray<FrustumComponent> cascadeFrustumComponents(
-					(cascadeCount) ? static_cast<FrustumComponent*>(
-						pool.allocate(cascadeCount * sizeof(FrustumComponent), alignof(FrustumComponent)))
-								   : nullptr,
-					cascadeCount);
-				for(U32 i = 0; i < cascadeCount; ++i)
+				WeakArray<Frustum> frustums;
+				if(cascadeCount)
 				{
 				{
-					::new(&cascadeFrustumComponents[i]) FrustumComponent(&node);
-					cascadeFrustumComponents[i].setFrustumType(FrustumType::kOrthographic);
+					nextQueues = WeakArray<RenderQueue>(newArray<RenderQueue>(pool, cascadeCount), cascadeCount);
+					nextFrustums =
+						WeakArray<VisibilityFrustum>(newArray<VisibilityFrustum>(pool, cascadeCount), cascadeCount);
+					frustums = WeakArray<Frustum>(newArray<Frustum>(pool, cascadeCount), cascadeCount);
 				}
 				}
 
 
-				lc->setupDirectionalLightQueueElement(testedFrc, result.m_directionalLight, cascadeFrustumComponents);
-
-				nextQueues = WeakArray<RenderQueue>(
-					(cascadeCount) ? newArray<RenderQueue>(pool, cascadeCount) : nullptr, cascadeCount);
 				for(U32 i = 0; i < cascadeCount; ++i)
 				for(U32 i = 0; i < cascadeCount; ++i)
 				{
 				{
+					nextFrustums[i].m_frustum = &frustums[i];
+					static_cast<FrustumFlags&>(nextFrustums[i]) = getLightFrustumFlags();
+
+					frustums[i].init(FrustumType::kOrthographic, nullptr);
+
 					result.m_directionalLight.m_shadowRenderQueues[i] = &nextQueues[i];
 					result.m_directionalLight.m_shadowRenderQueues[i] = &nextQueues[i];
 				}
 				}
 
 
+				lightc.setupDirectionalLightQueueElement(testedFrustum, result.m_directionalLight, frustums);
+
 				// Despite the fact that it's the same light it will have different properties if viewed by different
 				// Despite the fact that it's the same light it will have different properties if viewed by different
 				// cameras. If the renderer finds the same UUID it will think it's cached and use wrong shadow tiles.
 				// cameras. If the renderer finds the same UUID it will think it's cached and use wrong shadow tiles.
 				// That's why we need to change its UUID and bind it to the frustum that is currently viewing the light
 				// That's why we need to change its UUID and bind it to the frustum that is currently viewing the light
-				result.m_directionalLight.m_uuid = testedNode.getUuid();
+				result.m_directionalLight.m_uuid = ptrToNumber(&testedFrustum);
 
 
 				// Manually update the dummy components
 				// Manually update the dummy components
 				for(U32 i = 0; i < cascadeCount; ++i)
 				for(U32 i = 0; i < cascadeCount; ++i)
 				{
 				{
-					cascadeFrustumComponents[i].setEnabledVisibilityTests(
-						FrustumComponentVisibilityTestFlag::kShadowCasterRenderComponents);
-					Bool updated;
-					SceneComponentUpdateInfo scUpdateInfo(0.0, 1.0);
-					scUpdateInfo.m_node = &node;
-					[[maybe_unused]] const Error err = cascadeFrustumComponents[i].updateReal(scUpdateInfo, updated);
-					ANKI_ASSERT(updated == true && !err);
+					[[maybe_unused]] Bool updated = frustums[i].update();
+					ANKI_ASSERT(updated);
 				}
 				}
 
 
-				nextQueueFrustumComponents = cascadeFrustumComponents;
-
 				break;
 				break;
 			}
 			}
 			default:
 			default:
 				ANKI_ASSERT(0);
 				ANKI_ASSERT(0);
 			}
 			}
 		}
 		}
-
-		if(lfc && lfc->isLoaded())
+		else if(comp.getClassId() == LensFlareComponent::getStaticClassId())
 		{
 		{
+			const LensFlareComponent& flarec = static_cast<LensFlareComponent&>(comp);
+
+			if(!frustumFlags.m_gatherLensFlareComponents || !isInside() || !flarec.isEnabled())
+			{
+				continue;
+			}
+
 			LensFlareQueueElement* el = result.m_lensFlares.newElement(pool);
 			LensFlareQueueElement* el = result.m_lensFlares.newElement(pool);
-			lfc->setupLensFlareQueueElement(*el);
+			flarec.setupLensFlareQueueElement(*el);
 		}
 		}
-
-		if(reflc)
+		else if(comp.getClassId() == ReflectionProbeComponent::getStaticClassId())
 		{
 		{
+			if(!frustumFlags.m_gatherProbeComponents || !isInside())
+			{
+				continue;
+			}
+
+			ReflectionProbeComponent& reflc = static_cast<ReflectionProbeComponent&>(comp);
+
 			ReflectionProbeQueueElement* el = result.m_reflectionProbes.newElement(pool);
 			ReflectionProbeQueueElement* el = result.m_reflectionProbes.newElement(pool);
-			reflc->setupReflectionProbeQueueElement(*el);
+			reflc.setupReflectionProbeQueueElement(*el);
 
 
-			if(reflc->getMarkedForRendering())
+			if(reflc.getMarkedForRendering())
 			{
 			{
-				RenderQueue* a = newArray<RenderQueue>(pool, 6);
-				nextQueues = WeakArray<RenderQueue>(a, 6);
-
-				el->m_renderQueues[0] = &nextQueues[0];
-				el->m_renderQueues[1] = &nextQueues[1];
-				el->m_renderQueues[2] = &nextQueues[2];
-				el->m_renderQueues[3] = &nextQueues[3];
-				el->m_renderQueues[4] = &nextQueues[4];
-				el->m_renderQueues[5] = &nextQueues[5];
+				nextQueues = WeakArray<RenderQueue>(newArray<RenderQueue>(pool, 6), 6);
+				nextFrustums = WeakArray<VisibilityFrustum>(newArray<VisibilityFrustum>(pool, 6), 6);
+
+				for(U32 i = 0; i < 6; ++i)
+				{
+					el->m_renderQueues[i] = &nextQueues[i];
+					nextFrustums[i].m_frustum = &reflc.getFrustums()[i];
+					static_cast<FrustumFlags&>(nextFrustums[i]) = getProbeFrustumFlags();
+				}
 			}
 			}
 			else
 			else
 			{
 			{
-				el->m_renderQueues = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+				memset(&el->m_renderQueues[0], 0, sizeof(el->m_renderQueues));
 			}
 			}
 		}
 		}
-
-		if(decalc)
+		else if(comp.getClassId() == DecalComponent::getStaticClassId())
 		{
 		{
+			if(!frustumFlags.m_gatherDecalComponents || !isInside())
+			{
+				continue;
+			}
+
+			const DecalComponent& decalc = static_cast<DecalComponent&>(comp);
 			DecalQueueElement* el = result.m_decals.newElement(pool);
 			DecalQueueElement* el = result.m_decals.newElement(pool);
-			decalc->setupDecalQueueElement(*el);
+			decalc.setupDecalQueueElement(*el);
 		}
 		}
-
-		if(fogc)
+		else if(comp.getClassId() == FogDensityComponent::getStaticClassId())
 		{
 		{
+			if(!frustumFlags.m_gatherFogDensityComponents || !isInside())
+			{
+				continue;
+			}
+
+			const FogDensityComponent& fogc = static_cast<FogDensityComponent&>(comp);
+
 			FogDensityQueueElement* el = result.m_fogDensityVolumes.newElement(pool);
 			FogDensityQueueElement* el = result.m_fogDensityVolumes.newElement(pool);
-			fogc->setupFogDensityQueueElement(*el);
+			fogc.setupFogDensityQueueElement(*el);
 		}
 		}
-
-		if(giprobec)
+		else if(comp.getClassId() == GlobalIlluminationProbeComponent::getStaticClassId())
 		{
 		{
+			if(!frustumFlags.m_gatherProbeComponents || !isInside())
+			{
+				continue;
+			}
+
+			GlobalIlluminationProbeComponent& giprobec = static_cast<GlobalIlluminationProbeComponent&>(comp);
+
 			GlobalIlluminationProbeQueueElement* el = result.m_giProbes.newElement(pool);
 			GlobalIlluminationProbeQueueElement* el = result.m_giProbes.newElement(pool);
-			giprobec->setupGlobalIlluminationProbeQueueElement(*el);
+			giprobec.setupGlobalIlluminationProbeQueueElement(*el);
 
 
-			if(giprobec->getMarkedForRendering())
+			if(giprobec.getMarkedForRendering())
 			{
 			{
-				RenderQueue* a = newArray<RenderQueue>(pool, 6);
-				nextQueues = WeakArray<RenderQueue>(a, 6);
-
-				el->m_renderQueues[0] = &nextQueues[0];
-				el->m_renderQueues[1] = &nextQueues[1];
-				el->m_renderQueues[2] = &nextQueues[2];
-				el->m_renderQueues[3] = &nextQueues[3];
-				el->m_renderQueues[4] = &nextQueues[4];
-				el->m_renderQueues[5] = &nextQueues[5];
+				nextQueues = WeakArray<RenderQueue>(newArray<RenderQueue>(pool, 6), 6);
+				nextFrustums = WeakArray<VisibilityFrustum>(newArray<VisibilityFrustum>(pool, 6), 6);
+
+				for(U32 i = 0; i < 6; ++i)
+				{
+					el->m_renderQueues[i] = &nextQueues[i];
+					nextFrustums[i].m_frustum = &giprobec.getFrustums()[i];
+					static_cast<FrustumFlags&>(nextFrustums[i]) = getProbeFrustumFlags();
+				}
 			}
 			}
 			else
 			else
 			{
 			{
-				el->m_renderQueues = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
+				memset(&el->m_renderQueues[0], 0, sizeof(el->m_renderQueues));
 			}
 			}
 		}
 		}
-
-		if(computec)
+		else if(comp.getClassId() == UiComponent::getStaticClassId())
 		{
 		{
-			GenericGpuComputeJobQueueElement* el = result.m_genericGpuComputeJobs.newElement(pool);
-			computec->setupGenericGpuComputeJobQueueElement(*el);
-		}
+			if(!frustumFlags.m_gatherUiComponents || !isInside())
+			{
+				continue;
+			}
 
 
-		if(uic)
-		{
+			const UiComponent& uic = static_cast<UiComponent&>(comp);
 			UiQueueElement* el = result.m_uis.newElement(pool);
 			UiQueueElement* el = result.m_uis.newElement(pool);
-			uic->setupUiQueueElement(*el);
+			uic.setupUiQueueElement(*el);
 		}
 		}
-
-		if(skyboxc)
+		else if(comp.getClassId() == SkyboxComponent::getStaticClassId())
 		{
 		{
-			skyboxc->setupSkyboxQueueElement(result.m_skybox);
+			if(!frustumFlags.m_gatherSkyComponents || !isInside())
+			{
+				continue;
+			}
+
+			const SkyboxComponent& skyboxc = static_cast<SkyboxComponent&>(comp);
+			skyboxc.setupSkyboxQueueElement(result.m_skybox);
 			result.m_skyboxSet = true;
 			result.m_skyboxSet = true;
 		}
 		}
+		else
+		{
+			ANKI_ASSERT(0);
+		}
 
 
 		// Add more frustums to the list
 		// Add more frustums to the list
 		if(nextQueues.getSize() > 0)
 		if(nextQueues.getSize() > 0)
 		{
 		{
-			U32 count = 0;
-
-			if(ANKI_LIKELY(nextQueueFrustumComponents.getSize() == 0))
-			{
-				node.iterateComponentsOfType<FrustumComponent>([&](FrustumComponent& frc) {
-					m_frcCtx->m_visCtx->submitNewWork(frc, primaryFrc, nextQueues[count++], hive);
-				});
-			}
-			else
+			ANKI_ASSERT(nextFrustums.getSize() == nextQueues.getSize());
+			for(U32 i = 0; i < nextQueues.getSize(); ++i)
 			{
 			{
-				for(FrustumComponent& frc : nextQueueFrustumComponents)
-				{
-					m_frcCtx->m_visCtx->submitNewWork(frc, primaryFrc, nextQueues[count++], hive);
-				}
+				m_frcCtx->m_visCtx->submitNewWork(nextFrustums[i], m_frcCtx->m_primaryFrustum, nextQueues[i], hive);
 			}
 			}
 		}
 		}
 
 
 		// Update timestamp
 		// Update timestamp
-		timestamp = max(timestamp, node.getComponentMaxTimestamp());
+		Timestamp& timestamp = m_frcCtx->m_queueViews[taskId].m_timestamp;
+		timestamp = max(timestamp, comp.getTimestamp());
 	} // end for
 	} // end for
 }
 }
 
 
@@ -711,8 +685,7 @@ void CombineResultsTask::combine()
 
 
 #undef ANKI_VIS_COMBINE
 #undef ANKI_VIS_COMBINE
 
 
-	const Bool isShadowFrustum = !!(m_frcCtx->m_frc->getEnabledVisibilityTests()
-									& FrustumComponentVisibilityTestFlag::kShadowCasterRenderComponents);
+	const Bool isShadowFrustum = m_frcCtx->m_frustum.m_gatherShadowCasterModelComponents;
 
 
 	// Sort some of the arrays
 	// Sort some of the arrays
 	if(!isShadowFrustum)
 	if(!isShadowFrustum)
@@ -855,7 +828,7 @@ void CombineResultsTask::combineQueueElements(StackMemoryPool& pool,
 	}
 	}
 }
 }
 
 
-void SceneGraph::doVisibilityTests(SceneNode& fsn, SceneGraph& scene, RenderQueue& rqueue)
+void SceneGraph::doVisibilityTests(SceneNode& camera, SceneGraph& scene, RenderQueue& rqueue)
 {
 {
 	ANKI_TRACE_SCOPED_EVENT(SCENE_VIS_TESTS);
 	ANKI_TRACE_SCOPED_EVENT(SCENE_VIS_TESTS);
 
 
@@ -863,23 +836,24 @@ void SceneGraph::doVisibilityTests(SceneNode& fsn, SceneGraph& scene, RenderQueu
 
 
 	VisibilityContext ctx;
 	VisibilityContext ctx;
 	ctx.m_scene = &scene;
 	ctx.m_scene = &scene;
-	ctx.m_earlyZDist = scene.m_subsystems.m_config->getSceneEarlyZDistance();
-	const FrustumComponent& mainFrustum = fsn.getFirstComponentOfType<FrustumComponent>();
-	ctx.submitNewWork(mainFrustum, mainFrustum, rqueue, hive);
+	CameraComponent& camerac = camera.getFirstComponentOfType<CameraComponent>();
+	VisibilityFrustum visFrustum;
+	visFrustum.m_frustum = &camerac.getFrustum();
+	static_cast<FrustumFlags&>(visFrustum) = getCameraFrustumFlags();
+	ctx.submitNewWork(visFrustum, visFrustum, rqueue, hive);
 
 
-	const FrustumComponent* extendedFrustum = fsn.tryGetNthComponentOfType<FrustumComponent>(1);
-	if(extendedFrustum)
+	if(camerac.getHasExtendedFrustum())
 	{
 	{
-		// This is the frustum for RT.
-		ANKI_ASSERT(
-			!(extendedFrustum->getEnabledVisibilityTests() & ~FrustumComponentVisibilityTestFlag::kAllRayTracing));
+		VisibilityFrustum evisFrustum;
+		evisFrustum.m_frustum = &camerac.getExtendedFrustum();
+		static_cast<FrustumFlags&>(evisFrustum) = getCameraExtendedFrustumFlags();
 
 
 		rqueue.m_rayTracingQueue = newInstance<RenderQueue>(scene.getFrameMemoryPool());
 		rqueue.m_rayTracingQueue = newInstance<RenderQueue>(scene.getFrameMemoryPool());
-		ctx.submitNewWork(*extendedFrustum, mainFrustum, *rqueue.m_rayTracingQueue, hive);
+		ctx.submitNewWork(evisFrustum, visFrustum, *rqueue.m_rayTracingQueue, hive);
 	}
 	}
 
 
 	hive.waitAllTasks();
 	hive.waitAllTasks();
-	ctx.m_testedFrcs.destroy(scene.getFrameMemoryPool());
+	ctx.m_testedFrustums.destroy(scene.getFrameMemoryPool());
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 34 - 10
AnKi/Scene/VisibilityInternal.h

@@ -7,8 +7,9 @@
 
 
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Scene/SceneGraph.h>
 #include <AnKi/Scene/SoftwareRasterizer.h>
 #include <AnKi/Scene/SoftwareRasterizer.h>
-#include <AnKi/Scene/Components/FrustumComponent.h>
 #include <AnKi/Scene/Octree.h>
 #include <AnKi/Scene/Octree.h>
+#include <AnKi/Scene/Frustum.h>
+#include <AnKi/Scene/Spatial.h>
 #include <AnKi/Util/Thread.h>
 #include <AnKi/Util/Thread.h>
 #include <AnKi/Util/Tracer.h>
 #include <AnKi/Util/Tracer.h>
 #include <AnKi/Renderer/RenderQueue.h>
 #include <AnKi/Renderer/RenderQueue.h>
@@ -110,6 +111,31 @@ public:
 
 
 static_assert(std::is_trivially_destructible<RenderQueueView>::value == true, "Should be trivially destructible");
 static_assert(std::is_trivially_destructible<RenderQueueView>::value == true, "Should be trivially destructible");
 
 
+class FrustumFlags
+{
+public:
+	Bool m_gatherModelComponents : 1 = false;
+	Bool m_gatherShadowCasterModelComponents : 1 = false;
+	Bool m_gatherParticleComponents : 1 = false;
+	Bool m_gatherProbeComponents : 1 = false;
+	Bool m_gatherLightComponents : 1 = false;
+	Bool m_gatherLensFlareComponents : 1 = false;
+	Bool m_gatherDecalComponents : 1 = false;
+	Bool m_gatherFogDensityComponents : 1 = false;
+	Bool m_gatherUiComponents : 1 = false;
+	Bool m_gatherSkyComponents : 1 = false;
+
+	Bool m_coverageBuffer : 1 = false;
+	Bool m_earlyZ : 1 = false;
+	Bool m_nonDirectionalLightsCastShadow : 1 = false;
+};
+
+class VisibilityFrustum : public FrustumFlags
+{
+public:
+	Frustum* m_frustum = nullptr;
+};
+
 /// Data common for all tasks.
 /// Data common for all tasks.
 class VisibilityContext
 class VisibilityContext
 {
 {
@@ -117,12 +143,10 @@ public:
 	SceneGraph* m_scene = nullptr;
 	SceneGraph* m_scene = nullptr;
 	Atomic<U32> m_testsCount = {0};
 	Atomic<U32> m_testsCount = {0};
 
 
-	F32 m_earlyZDist = -1.0f; ///< Cache this.
-
-	List<const FrustumComponent*> m_testedFrcs;
-	Mutex m_mtx;
+	List<const Frustum*> m_testedFrustums;
+	Mutex m_testedFrustumsMtx;
 
 
-	void submitNewWork(const FrustumComponent& frc, const FrustumComponent& primaryFrustum, RenderQueue& result,
+	void submitNewWork(const VisibilityFrustum& frustum, const VisibilityFrustum& primaryFrustum, RenderQueue& result,
 					   ThreadHive& hive);
 					   ThreadHive& hive);
 };
 };
 
 
@@ -133,8 +157,8 @@ class FrustumVisibilityContext
 public:
 public:
 	VisibilityContext* m_visCtx = nullptr;
 	VisibilityContext* m_visCtx = nullptr;
 
 
-	const FrustumComponent* m_frc = nullptr; ///< This is the frustum to be tested.
-	const FrustumComponent* m_primaryFrustum = nullptr; ///< This is the primary camera frustum.
+	VisibilityFrustum m_frustum; ///< This is the frustum to be tested.
+	VisibilityFrustum m_primaryFrustum; ///< This is the primary camera frustum.
 
 
 	// S/W rasterizer members
 	// S/W rasterizer members
 	SoftwareRasterizer* m_r = nullptr;
 	SoftwareRasterizer* m_r = nullptr;
@@ -181,7 +205,7 @@ public:
 	void gather(ThreadHive& hive);
 	void gather(ThreadHive& hive);
 
 
 private:
 private:
-	Array<SpatialComponent*, kMaxSpatialsPerVisTest> m_spatials;
+	Array<Spatial*, kMaxSpatialsPerVisTest> m_spatials;
 	U32 m_spatialCount = 0;
 	U32 m_spatialCount = 0;
 
 
 	/// Submit tasks to test the m_spatials.
 	/// Submit tasks to test the m_spatials.
@@ -196,7 +220,7 @@ class VisibilityTestTask
 public:
 public:
 	FrustumVisibilityContext* m_frcCtx = nullptr;
 	FrustumVisibilityContext* m_frcCtx = nullptr;
 
 
-	Array<SpatialComponent*, kMaxSpatialsPerVisTest> m_spatialsToTest;
+	Array<Spatial*, kMaxSpatialsPerVisTest> m_spatialsToTest;
 	U32 m_spatialToTestCount = 0;
 	U32 m_spatialToTestCount = 0;
 
 
 	VisibilityTestTask(FrustumVisibilityContext* frcCtx)
 	VisibilityTestTask(FrustumVisibilityContext* frcCtx)

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