Browse Source

Visibility work

Panagiotis Christopoulos Charitos 10 năm trước cách đây
mục cha
commit
6c93fba81d

+ 44 - 25
include/anki/scene/FrustumComponent.h

@@ -10,6 +10,7 @@
 #include "anki/scene/SpatialComponent.h"
 #include "anki/scene/SpatialComponent.h"
 #include "anki/scene/Common.h"
 #include "anki/scene/Common.h"
 #include "anki/scene/SceneComponent.h"
 #include "anki/scene/SceneComponent.h"
+#include "anki/util/Bitset.h"
 
 
 namespace anki {
 namespace anki {
 
 
@@ -19,7 +20,7 @@ class VisibilityTestResults;
 /// @addtogroup scene
 /// @addtogroup scene
 /// @{
 /// @{
 
 
-/// Frustum component interface for scene nodes. Useful for nodes that are 
+/// Frustum component interface for scene nodes. Useful for nodes that are
 /// frustums like cameras and lights
 /// frustums like cameras and lights
 class FrustumComponent: public SceneComponent
 class FrustumComponent: public SceneComponent
 {
 {
@@ -30,22 +31,23 @@ public:
 		U32 m_lightsCount = 0;
 		U32 m_lightsCount = 0;
 	};
 	};
 
 
-	static Bool classof(const SceneComponent& c)
-	{
-		return c.getType() == Type::FRUSTUM;
-	}
+	/// Flags that affect visibility tests.
+	enum class VisibilityTestFlag: U8
+	{
+		TEST_NONE = 0,
+		TEST_RENDER_COMPONENTS = 1 << 0,
+		TEST_LIGHT_COMPONENTS = 1 << 1,
+		TEST_LENS_FLARE_COMPONENTS = 1 << 2,
+		TEST_SHADOW_CASTERS = 1 << 3,
+		TEST_ALL = TEST_RENDER_COMPONENTS
+			| TEST_LIGHT_COMPONENTS
+			| TEST_LENS_FLARE_COMPONENTS
+			| TEST_SHADOW_CASTERS
+	};
+	ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(VisibilityTestFlag, friend)
 
 
 	/// Pass the frustum here so we can avoid the virtuals
 	/// Pass the frustum here so we can avoid the virtuals
-	FrustumComponent(SceneNode* node, Frustum* frustum)
-	:	SceneComponent(Type::FRUSTUM, node), 
-		m_frustum(frustum),
-		m_flags(0)
-	{
-		// WARNING: Never touch m_frustum in constructor
-		ANKI_ASSERT(frustum);
-		markShapeForUpdate();
-		markTransformForUpdate();
-	}
+	FrustumComponent(SceneNode* node, Frustum* frustum);
 
 
 	Frustum& getFrustum()
 	Frustum& getFrustum()
 	{
 	{
@@ -72,7 +74,7 @@ public:
 		return m_vpm;
 		return m_vpm;
 	}
 	}
 
 
-	/// Parameters used to get the view space position using the depth value 
+	/// Parameters used to get the view space position using the depth value
 	/// and the NDC xy coordinates.
 	/// and the NDC xy coordinates.
 	/// @code
 	/// @code
 	/// vec3 fragPos;
 	/// vec3 fragPos;
@@ -107,13 +109,13 @@ public:
 	/// Call when the shape of the frustum got changed.
 	/// Call when the shape of the frustum got changed.
 	void markShapeForUpdate()
 	void markShapeForUpdate()
 	{
 	{
-		m_flags |= SHAPE_MARKED_FOR_UPDATE;
+		m_flags.enableBits(SHAPE_MARKED_FOR_UPDATE);
 	}
 	}
 
 
 	/// Call when the transformation of the frustum got changed.
 	/// Call when the transformation of the frustum got changed.
 	void markTransformForUpdate()
 	void markTransformForUpdate()
 	{
 	{
-		m_flags |= TRANSFORM_MARKED_FOR_UPDATE;
+		m_flags.enableBits(TRANSFORM_MARKED_FOR_UPDATE);
 	}
 	}
 
 
 	/// Is a spatial inside the frustum?
 	/// Is a spatial inside the frustum?
@@ -131,19 +133,36 @@ public:
 	/// @name SceneComponent overrides
 	/// @name SceneComponent overrides
 	/// @{
 	/// @{
 	ANKI_USE_RESULT Error update(SceneNode&, F32, F32, Bool& updated) override;
 	ANKI_USE_RESULT Error update(SceneNode&, F32, F32, Bool& updated) override;
+	/// @}
 
 
-	void reset() override
+	void setEnabledVisibilityTests(VisibilityTestFlag bits)
 	{
 	{
-		m_visible = nullptr;
+		m_flags.disableBits(VisibilityTestFlag::TEST_ALL);
+		m_flags.enableBits(bits, true);
+	}
+
+	Bool visibilityTestsEnabled(VisibilityTestFlag bits) const
+	{
+		return m_flags.bitsEnabled(bits);
+	}
+
+	Bool anyVisibilityTestEnabled() const
+	{
+		return m_flags.anyBitsEnabled(VisibilityTestFlag::TEST_ALL);
+	}
+
+	static Bool classof(const SceneComponent& c)
+	{
+		return c.getType() == Type::FRUSTUM;
 	}
 	}
-	/// @}
 
 
 private:
 private:
 	enum Flags
 	enum Flags
 	{
 	{
-		SHAPE_MARKED_FOR_UPDATE = 1 << 0,
-		TRANSFORM_MARKED_FOR_UPDATE = 1 << 1,
+		SHAPE_MARKED_FOR_UPDATE = 1 << 4,
+		TRANSFORM_MARKED_FOR_UPDATE = 1 << 5
 	};
 	};
+	ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(Flags, friend)
 
 
 	Frustum* m_frustum;
 	Frustum* m_frustum;
 	Mat4 m_pm = Mat4::getIdentity(); ///< Projection matrix
 	Mat4 m_pm = Mat4::getIdentity(); ///< Projection matrix
@@ -152,12 +171,12 @@ private:
 
 
 	Vec4 m_projParams = Vec4(0.0);
 	Vec4 m_projParams = Vec4(0.0);
 
 
-	/// Visibility stuff. It's per frame so the pointer is invalid on the next 
+	/// Visibility stuff. It's per frame so the pointer is invalid on the next
 	/// frame and before any visibility tests are run
 	/// frame and before any visibility tests are run
 	VisibilityTestResults* m_visible = nullptr;
 	VisibilityTestResults* m_visible = nullptr;
 	VisibilityStats m_stats;
 	VisibilityStats m_stats;
 
 
-	U8 m_flags;
+	Bitset<U8> m_flags;
 
 
 	void computeProjectionParams();
 	void computeProjectionParams();
 };
 };

+ 4 - 1
include/anki/scene/Light.h

@@ -28,12 +28,15 @@ public:
 	~Light();
 	~Light();
 
 
 	ANKI_USE_RESULT Error create(
 	ANKI_USE_RESULT Error create(
-		const CString& name, 
+		const CString& name,
 		LightComponent::LightType type,
 		LightComponent::LightType type,
 		CollisionShape* shape);
 		CollisionShape* shape);
 
 
 	ANKI_USE_RESULT Error loadLensFlare(const CString& filename);
 	ANKI_USE_RESULT Error loadLensFlare(const CString& filename);
 
 
+	ANKI_USE_RESULT Error frameUpdate(
+		F32 prevUpdateTime, F32 crntTime) override;
+
 protected:
 protected:
 	/// Called when moved
 	/// Called when moved
 	void onMoveUpdateCommon(MoveComponent& move);
 	void onMoveUpdateCommon(MoveComponent& move);

+ 0 - 4
include/anki/scene/SceneComponent.h

@@ -56,10 +56,6 @@ public:
 
 
 	Timestamp getGlobalTimestamp() const;
 	Timestamp getGlobalTimestamp() const;
 
 
-	/// Do some reseting before frame starts
-	virtual void reset()
-	{}
-
 	/// Do some updating
 	/// Do some updating
 	/// @param[out] updated true if an update happened
 	/// @param[out] updated true if an update happened
 	virtual ANKI_USE_RESULT Error update(
 	virtual ANKI_USE_RESULT Error update(

+ 0 - 3
include/anki/scene/SpatialComponent.h

@@ -118,9 +118,6 @@ public:
 	/// @name SceneComponent overrides
 	/// @name SceneComponent overrides
 	/// @{
 	/// @{
 	ANKI_USE_RESULT Error update(SceneNode&, F32, F32, Bool& updated) override;
 	ANKI_USE_RESULT Error update(SceneNode&, F32, F32, Bool& updated) override;
-
-	/// Disable some flags
-	void reset() override;
 	/// @}
 	/// @}
 
 
 private:
 private:

+ 29 - 12
include/anki/util/Bitset.h

@@ -21,38 +21,55 @@ public:
 	using Value = T;
 	using Value = T;
 
 
 	Bitset()
 	Bitset()
-	:	m_bitmask(static_cast<Value>(0))
+		: m_bitmask(static_cast<Value>(0))
 	{}
 	{}
 
 
 	Bitset(Value bitmask)
 	Bitset(Value bitmask)
-	:	m_bitmask(bitmask)
+		: m_bitmask(bitmask)
 	{}
 	{}
 
 
 	/// @name Bits manipulation
 	/// @name Bits manipulation
 	/// @{
 	/// @{
-	void enableBits(Value mask)
+	template<typename Y>
+	void enableBits(Y mask)
 	{
 	{
-		m_bitmask |= mask;
+		Value maski = static_cast<Value>(mask);
+		m_bitmask |= maski;
 	}
 	}
 
 
-	void enableBits(Value mask, Bool enable)
+	template<typename Y>
+	void enableBits(Y mask, Bool enable)
 	{
 	{
-		m_bitmask = (enable) ? (m_bitmask | mask) : (m_bitmask & ~mask);
+		Value maski = static_cast<Value>(mask);
+		m_bitmask = (enable) ? (m_bitmask | maski) : (m_bitmask & ~maski);
 	}
 	}
 
 
-	void disableBits(Value mask)
+	template<typename Y>
+	void disableBits(Y mask)
 	{
 	{
-		m_bitmask &= ~mask;
+		Value maski = static_cast<Value>(mask);
+		m_bitmask &= ~maski;
 	}
 	}
 
 
-	void switchBits(Value mask)
+	template<typename Y>
+	void switchBits(Y mask)
 	{
 	{
-		m_bitmask ^= mask;
+		Value maski = static_cast<Value>(mask);
+		m_bitmask ^= maski;
 	}
 	}
 
 
-	Bool bitsEnabled(Value mask) const
+	template<typename Y>
+	Bool bitsEnabled(Y mask) const
 	{
 	{
-		return (m_bitmask & mask) != static_cast<Value>(0);
+		Value maski = static_cast<Value>(mask);
+		return (m_bitmask & maski) == maski;
+	}
+
+	template<typename Y>
+	Bool anyBitsEnabled(Y mask) const
+	{
+		Value maski = static_cast<Value>(mask);
+		return (m_bitmask & maski) != static_cast<Value>(0);
 	}
 	}
 
 
 	Value getBitmask() const
 	Value getBitmask() const

+ 20 - 3
src/scene/FrustumComponent.cpp

@@ -9,6 +9,20 @@
 
 
 namespace anki {
 namespace anki {
 
 
+//==============================================================================
+FrustumComponent::FrustumComponent(SceneNode* node, Frustum* frustum)
+	: SceneComponent(Type::FRUSTUM, node)
+	, m_frustum(frustum)
+	, m_flags(0)
+{
+	// WARNING: Never touch m_frustum in constructor
+	ANKI_ASSERT(frustum);
+	markShapeForUpdate();
+	markTransformForUpdate();
+
+	setEnabledVisibilityTests(VisibilityTestFlag::TEST_ALL);
+}
+
 //==============================================================================
 //==============================================================================
 void FrustumComponent::setVisibilityTestResults(VisibilityTestResults* visible)
 void FrustumComponent::setVisibilityTestResults(VisibilityTestResults* visible)
 {
 {
@@ -22,16 +36,18 @@ void FrustumComponent::setVisibilityTestResults(VisibilityTestResults* visible)
 //==============================================================================
 //==============================================================================
 Error FrustumComponent::update(SceneNode& node, F32, F32, Bool& updated)
 Error FrustumComponent::update(SceneNode& node, F32, F32, Bool& updated)
 {
 {
+	m_visible = nullptr;
+
 	updated = false;
 	updated = false;
 
 
-	if(m_flags & SHAPE_MARKED_FOR_UPDATE)
+	if(m_flags.bitsEnabled(SHAPE_MARKED_FOR_UPDATE))
 	{
 	{
 		updated = true;
 		updated = true;
 		m_pm = m_frustum->calculateProjectionMatrix();
 		m_pm = m_frustum->calculateProjectionMatrix();
 		computeProjectionParams();
 		computeProjectionParams();
 	}
 	}
 
 
-	if(m_flags & TRANSFORM_MARKED_FOR_UPDATE)
+	if(m_flags.bitsEnabled(TRANSFORM_MARKED_FOR_UPDATE))
 	{
 	{
 		updated = true;
 		updated = true;
 		m_vm = Mat4(m_frustum->getTransform().getInverse());
 		m_vm = Mat4(m_frustum->getTransform().getInverse());
@@ -40,7 +56,8 @@ Error FrustumComponent::update(SceneNode& node, F32, F32, Bool& updated)
 	if(updated)
 	if(updated)
 	{
 	{
 		m_vpm = m_pm * m_vm;
 		m_vpm = m_pm * m_vm;
-		m_flags = 0;
+		m_flags.disableBits(
+			SHAPE_MARKED_FOR_UPDATE | TRANSFORM_MARKED_FOR_UPDATE);
 	}
 	}
 
 
 	return ErrorCode::NONE;
 	return ErrorCode::NONE;

+ 42 - 11
src/scene/Light.cpp

@@ -20,7 +20,7 @@ class LightFeedbackComponent: public SceneComponent
 {
 {
 public:
 public:
 	LightFeedbackComponent(SceneNode* node)
 	LightFeedbackComponent(SceneNode* node)
-	:	SceneComponent(SceneComponent::Type::NONE, node)
+		: SceneComponent(SceneComponent::Type::NONE, node)
 	{}
 	{}
 
 
 	Error update(SceneNode& node, F32, F32, Bool& updated) override
 	Error update(SceneNode& node, F32, F32, Bool& updated) override
@@ -52,11 +52,16 @@ public:
 
 
 //==============================================================================
 //==============================================================================
 Light::Light(SceneGraph* scene)
 Light::Light(SceneGraph* scene)
-:	SceneNode(scene)
+	: SceneNode(scene)
 {}
 {}
 
 
 //==============================================================================
 //==============================================================================
-Error Light::create(const CString& name, 
+Light::~Light()
+{}
+
+
+//==============================================================================
+Error Light::create(const CString& name,
 	LightComponent::LightType type,
 	LightComponent::LightType type,
 	CollisionShape* shape)
 	CollisionShape* shape)
 {
 {
@@ -84,8 +89,32 @@ Error Light::create(const CString& name,
 }
 }
 
 
 //==============================================================================
 //==============================================================================
-Light::~Light()
-{}
+Error Light::frameUpdate(F32 prevUpdateTime, F32 crntTime)
+{
+	// Update frustum comps shadow info
+	const LightComponent& lc = getComponent<LightComponent>();
+	Bool castsShadow = lc.getShadowEnabled();
+
+	Error err = iterateComponentsOfType<FrustumComponent>(
+		[&](FrustumComponent& frc) -> Error
+	{
+		if(castsShadow)
+		{
+			frc.setEnabledVisibilityTests(
+				FrustumComponent::VisibilityTestFlag::TEST_SHADOW_CASTERS);
+		}
+		else
+		{
+			frc.setEnabledVisibilityTests(
+				FrustumComponent::VisibilityTestFlag::TEST_NONE);
+		}
+
+		return ErrorCode::NONE;
+	});
+	(void) err;
+
+	return ErrorCode::NONE;
+}
 
 
 //==============================================================================
 //==============================================================================
 void Light::onMoveUpdateCommon(MoveComponent& move)
 void Light::onMoveUpdateCommon(MoveComponent& move)
@@ -138,9 +167,9 @@ Error Light::loadLensFlare(const CString& filename)
 {
 {
 	ANKI_ASSERT(tryGetComponent<LensFlareComponent>() == nullptr);
 	ANKI_ASSERT(tryGetComponent<LensFlareComponent>() == nullptr);
 
 
-	LensFlareComponent* flareComp = 
+	LensFlareComponent* flareComp =
 		getSceneAllocator().newInstance<LensFlareComponent>(this);
 		getSceneAllocator().newInstance<LensFlareComponent>(this);
-	
+
 	Error err = ErrorCode::NONE;
 	Error err = ErrorCode::NONE;
 	if(err = flareComp->create(filename))
 	if(err = flareComp->create(filename))
 	{
 	{
@@ -160,7 +189,7 @@ Error Light::loadLensFlare(const CString& filename)
 
 
 //==============================================================================
 //==============================================================================
 PointLight::PointLight(SceneGraph* scene)
 PointLight::PointLight(SceneGraph* scene)
-:	Light(scene)
+	: Light(scene)
 {}
 {}
 
 
 //==============================================================================
 //==============================================================================
@@ -225,7 +254,7 @@ Error PointLight::frameUpdate(F32 prevUpdateTime, F32 crntTime)
 
 
 //==============================================================================
 //==============================================================================
 SpotLight::SpotLight(SceneGraph* scene)
 SpotLight::SpotLight(SceneGraph* scene)
-:	Light(scene)
+	: Light(scene)
 {}
 {}
 
 
 //==============================================================================
 //==============================================================================
@@ -234,8 +263,10 @@ Error SpotLight::create(const CString& name)
 	ANKI_CHECK(Light::create(
 	ANKI_CHECK(Light::create(
 		name, LightComponent::LightType::SPOT, &m_frustum));
 		name, LightComponent::LightType::SPOT, &m_frustum));
 
 
-	FrustumComponent* fr = 
+	FrustumComponent* fr =
 		getSceneAllocator().newInstance<FrustumComponent>(this, &m_frustum);
 		getSceneAllocator().newInstance<FrustumComponent>(this, &m_frustum);
+	fr->setEnabledVisibilityTests(
+		FrustumComponent::VisibilityTestFlag::TEST_NONE);
 
 
 	addComponent(fr, true);
 	addComponent(fr, true);
 
 
@@ -253,7 +284,7 @@ void SpotLight::onShapeUpdate(LightComponent& light)
 {
 {
 	onShapeUpdateCommon(light);
 	onShapeUpdateCommon(light);
 	m_frustum.setAll(
 	m_frustum.setAll(
-		light.getOuterAngle(), light.getOuterAngle(), 
+		light.getOuterAngle(), light.getOuterAngle(),
 		0.5, light.getDistance());
 		0.5, light.getDistance());
 }
 }
 
 

+ 8 - 2
src/scene/ReflectionProbe.cpp

@@ -71,9 +71,15 @@ Error ReflectionProbe::create(const CString& name, F32 radius)
 
 
 	for(U i = 0; i < 6; ++i)
 	for(U i = 0; i < 6; ++i)
 	{
 	{
-		comp = getSceneAllocator().newInstance<FrustumComponent>(
+		FrustumComponent* frc =
+			getSceneAllocator().newInstance<FrustumComponent>(
 			this, &m_frustums[i]);
 			this, &m_frustums[i]);
-		addComponent(comp, true);
+
+		frc->setEnabledVisibilityTests(
+			FrustumComponent::VisibilityTestFlag::TEST_RENDER_COMPONENTS
+			| FrustumComponent::VisibilityTestFlag::TEST_LIGHT_COMPONENTS);
+
+		addComponent(frc, true);
 
 
 		m_frustums[i].setAll(toRad(45.0), toRad(45.0), 0.5, radius);
 		m_frustums[i].setAll(toRad(45.0), toRad(45.0), 0.5, radius);
 		m_frustums[i].resetTransform(trfs[i]);
 		m_frustums[i].resetTransform(trfs[i]);

+ 0 - 2
src/scene/SceneComponent.cpp

@@ -18,8 +18,6 @@ Timestamp SceneComponent::getGlobalTimestamp() const
 Error SceneComponent::updateReal(SceneNode& node, F32 prevTime, F32 crntTime,
 Error SceneComponent::updateReal(SceneNode& node, F32 prevTime, F32 crntTime,
 	Bool& updated)
 	Bool& updated)
 {
 {
-	reset();
-
 	Error err = update(node, prevTime, crntTime, updated);
 	Error err = update(node, prevTime, crntTime, updated);
 	if(!err && updated)
 	if(!err && updated)
 	{
 	{

+ 2 - 6
src/scene/SpatialComponent.cpp

@@ -32,6 +32,8 @@ SpatialComponent::~SpatialComponent()
 //==============================================================================
 //==============================================================================
 Error SpatialComponent::update(SceneNode&, F32, F32, Bool& updated)
 Error SpatialComponent::update(SceneNode&, F32, F32, Bool& updated)
 {
 {
+	disableBits(Flag::VISIBLE_ANY);
+
 	updated = bitsEnabled(Flag::MARKED_FOR_UPDATE);
 	updated = bitsEnabled(Flag::MARKED_FOR_UPDATE);
 	if(updated)
 	if(updated)
 	{
 	{
@@ -43,10 +45,4 @@ Error SpatialComponent::update(SceneNode&, F32, F32, Bool& updated)
 	return ErrorCode::NONE;
 	return ErrorCode::NONE;
 }
 }
 
 
-//==============================================================================
-void SpatialComponent::reset()
-{
-	disableBits(Flag::VISIBLE_ANY);
-}
-
 } // end namespace anki
 } // end namespace anki

+ 91 - 54
src/scene/Visibility.cpp

@@ -56,14 +56,14 @@ class VisibilityShared
 public:
 public:
 	SceneGraph* m_scene = nullptr;
 	SceneGraph* m_scene = nullptr;
 	Barrier m_barrier;
 	Barrier m_barrier;
-	List<SceneNode*> m_frustumsList; ///< Nodes to test
+	List<FrustumComponent*> m_frustumsList; ///< Frustums to test
 	SpinLock m_lock;
 	SpinLock m_lock;
 
 
 	// Data per thread but that can be accessed by all threads
 	// Data per thread but that can be accessed by all threads
 	Array<VisibilityTestResults*, Threadpool::MAX_THREADS> m_testResults;
 	Array<VisibilityTestResults*, Threadpool::MAX_THREADS> m_testResults;
 
 
 	VisibilityShared(U threadCount)
 	VisibilityShared(U threadCount)
-	:	m_barrier(threadCount)
+		: m_barrier(threadCount)
 	{
 	{
 		memset(&m_testResults[0], 0, sizeof(m_testResults));
 		memset(&m_testResults[0], 0, sizeof(m_testResults));
 	}
 	}
@@ -76,7 +76,7 @@ public:
 	VisibilityShared* m_shared;
 	VisibilityShared* m_shared;
 
 
 	/// Test a frustum component
 	/// Test a frustum component
-	void test(SceneNode& testedNode, U32 threadId, PtrSize threadsCount);
+	void test(FrustumComponent& frcToTest, U32 threadId, PtrSize threadsCount);
 
 
 	void combineTestResults(FrustumComponent& frc, PtrSize threadsCount);
 	void combineTestResults(FrustumComponent& frc, PtrSize threadsCount);
 
 
@@ -90,8 +90,8 @@ public:
 		while(!list.isEmpty())
 		while(!list.isEmpty())
 		{
 		{
 			// Get front and pop it
 			// Get front and pop it
-			SceneNode* node = list.getFront();
-			ANKI_ASSERT(node);
+			FrustumComponent* frc = list.getFront();
+			ANKI_ASSERT(frc);
 
 
 			m_shared->m_barrier.wait();
 			m_shared->m_barrier.wait();
 			if(threadId == 0)
 			if(threadId == 0)
@@ -100,7 +100,7 @@ public:
 			}
 			}
 
 
 			m_shared->m_barrier.wait();
 			m_shared->m_barrier.wait();
-			test(*node, threadId, threadsCount);
+			test(*frc, threadId, threadsCount);
 		}
 		}
 
 
 		return ErrorCode::NONE;
 		return ErrorCode::NONE;
@@ -108,23 +108,19 @@ public:
 };
 };
 
 
 //==============================================================================
 //==============================================================================
-void VisibilityTestTask::test(SceneNode& testedNode,
+void VisibilityTestTask::test(FrustumComponent& testedFrc,
 	U32 threadId, PtrSize threadsCount)
 	U32 threadId, PtrSize threadsCount)
 {
 {
-	FrustumComponent& testedFr =
-		testedNode.getComponent<FrustumComponent>();
-	Bool testedNodeShadowCaster = false;
-	{
-		LightComponent* l = testedNode.tryGetComponent<LightComponent>();
-		testedNodeShadowCaster = l && l->getShadowEnabled();
-	}
+	ANKI_ASSERT(testedFrc.anyVisibilityTestEnabled());
 
 
+	SceneNode& testedNode = testedFrc.getSceneNode();
 	auto alloc = m_shared->m_scene->getFrameAllocator();
 	auto alloc = m_shared->m_scene->getFrameAllocator();
 
 
 	// Init test results
 	// Init test results
 	VisibilityTestResults* visible = alloc.newInstance<VisibilityTestResults>();
 	VisibilityTestResults* visible = alloc.newInstance<VisibilityTestResults>();
 
 
-	FrustumComponent::VisibilityStats stats = testedFr.getLastVisibilityStats();
+	FrustumComponent::VisibilityStats stats =
+		testedFrc.getLastVisibilityStats();
 
 
 	visible->create(alloc, stats.m_renderablesCount, stats.m_lightsCount, 4);
 	visible->create(alloc, stats.m_renderablesCount, stats.m_lightsCount, 4);
 
 
@@ -132,6 +128,18 @@ void VisibilityTestTask::test(SceneNode& testedNode,
 
 
 	List<SceneNode*> frustumsList;
 	List<SceneNode*> frustumsList;
 
 
+	Bool wantsRenderComponents = testedFrc.visibilityTestsEnabled(
+		FrustumComponent::VisibilityTestFlag::TEST_RENDER_COMPONENTS);
+
+	Bool wantsLightComponents = testedFrc.visibilityTestsEnabled(
+		FrustumComponent::VisibilityTestFlag::TEST_LIGHT_COMPONENTS);
+
+	Bool wantsFlareComponents = testedFrc.visibilityTestsEnabled(
+		FrustumComponent::VisibilityTestFlag::TEST_LENS_FLARE_COMPONENTS);
+
+	Bool wantsShadowCasters = testedFrc.visibilityTestsEnabled(
+		FrustumComponent::VisibilityTestFlag::TEST_SHADOW_CASTERS);
+
 #if 0
 #if 0
 	ANKI_LOGW("Running test code");
 	ANKI_LOGW("Running test code");
 
 
@@ -147,7 +155,7 @@ void VisibilityTestTask::test(SceneNode& testedNode,
 	SectorGroup& sectors = m_shared->m_scene->getSectorGroup();
 	SectorGroup& sectors = m_shared->m_scene->getSectorGroup();
 	if(threadId == 0)
 	if(threadId == 0)
 	{
 	{
-		sectors.prepareForVisibilityTests(testedFr);
+		sectors.prepareForVisibilityTests(testedFrc);
 	}
 	}
 	m_shared->m_barrier.wait();
 	m_shared->m_barrier.wait();
 
 
@@ -160,16 +168,43 @@ void VisibilityTestTask::test(SceneNode& testedNode,
 		start, end, [&](SceneNode& node) -> Error
 		start, end, [&](SceneNode& node) -> Error
 #endif
 #endif
 	{
 	{
-		FrustumComponent* fr = node.tryGetComponent<FrustumComponent>();
-
 		// Skip if it is the same
 		// Skip if it is the same
-		if(ANKI_UNLIKELY(&testedFr == fr))
+		if(ANKI_UNLIKELY(&testedNode == &node))
 		{
 		{
 			return ErrorCode::NONE;
 			return ErrorCode::NONE;
 		}
 		}
 
 
-		VisibleNode visibleNode;
-		visibleNode.m_node = &node;
+		// Check what components the frustum needs
+		Bool wantNode = false;
+
+		RenderComponent* rc = node.tryGetComponent<RenderComponent>();
+		if(rc && wantsRenderComponents)
+		{
+			wantNode = true;
+		}
+
+		if(rc && rc->getCastsShadow() && wantsShadowCasters)
+		{
+			wantNode = true;
+		}
+
+		LightComponent* lc = node.tryGetComponent<LightComponent>();
+		if(lc && wantsLightComponents)
+		{
+			wantNode = true;
+		}
+
+		LensFlareComponent* lfc = node.tryGetComponent<LensFlareComponent>();
+		if(lfc && wantsFlareComponents)
+		{
+			wantNode = true;
+		}
+
+		if(ANKI_UNLIKELY(!wantNode))
+		{
+			// Skip node
+			return ErrorCode::NONE;
+		}
 
 
 		// Test all spatial components of that node
 		// Test all spatial components of that node
 		struct SpatialTemp
 		struct SpatialTemp
@@ -184,15 +219,15 @@ void VisibilityTestTask::test(SceneNode& testedNode,
 		Error err = node.iterateComponentsOfType<SpatialComponent>(
 		Error err = node.iterateComponentsOfType<SpatialComponent>(
 			[&](SpatialComponent& sp)
 			[&](SpatialComponent& sp)
 		{
 		{
-			if(testedFr.insideFrustum(sp))
+			if(testedFrc.insideFrustum(sp))
 			{
 			{
 				// Inside
 				// Inside
 				ANKI_ASSERT(spIdx < MAX_U8);
 				ANKI_ASSERT(spIdx < MAX_U8);
 				sps[count++] = SpatialTemp{&sp, static_cast<U8>(spIdx)};
 				sps[count++] = SpatialTemp{&sp, static_cast<U8>(spIdx)};
 
 
-				sp.enableBits(testedNodeShadowCaster
+				/*sp.enableBits(testedNodeShadowCaster
 					? SpatialComponent::Flag::VISIBLE_LIGHT
 					? SpatialComponent::Flag::VISIBLE_LIGHT
-					: SpatialComponent::Flag::VISIBLE_CAMERA);
+					: SpatialComponent::Flag::VISIBLE_CAMERA);*/
 			}
 			}
 
 
 			++spIdx;
 			++spIdx;
@@ -207,7 +242,7 @@ void VisibilityTestTask::test(SceneNode& testedNode,
 		}
 		}
 
 
 		// Sort sub-spatials
 		// Sort sub-spatials
-		Vec4 origin = testedFr.getFrustumOrigin();
+		Vec4 origin = testedFrc.getFrustumOrigin();
 		std::sort(sps.begin(), sps.begin() + count,
 		std::sort(sps.begin(), sps.begin() + count,
 			[origin](const SpatialTemp& a, const SpatialTemp& b) -> Bool
 			[origin](const SpatialTemp& a, const SpatialTemp& b) -> Bool
 		{
 		{
@@ -221,6 +256,9 @@ void VisibilityTestTask::test(SceneNode& testedNode,
 		});
 		});
 
 
 		// Update the visibleNode
 		// Update the visibleNode
+		VisibleNode visibleNode;
+		visibleNode.m_node = &node;
+
 		ANKI_ASSERT(count < MAX_U8);
 		ANKI_ASSERT(count < MAX_U8);
 		visibleNode.m_spatialsCount = count;
 		visibleNode.m_spatialsCount = count;
 		visibleNode.m_spatialIndices = alloc.newArray<U8>(count);
 		visibleNode.m_spatialIndices = alloc.newArray<U8>(count);
@@ -230,41 +268,39 @@ void VisibilityTestTask::test(SceneNode& testedNode,
 			visibleNode.m_spatialIndices[i] = sps[i].m_idx;
 			visibleNode.m_spatialIndices[i] = sps[i].m_idx;
 		}
 		}
 
 
-		// Do something with the result
-		RenderComponent* r = node.tryGetComponent<RenderComponent>();
-		if(testedNodeShadowCaster)
+		if(rc && wantsRenderComponents)
 		{
 		{
-			if(r && r->getCastsShadow())
-			{
-				visible->moveBackRenderable(alloc, visibleNode);
-			}
+			visible->moveBackRenderable(alloc, visibleNode);
 		}
 		}
-		else
+
+		if(rc && wantsShadowCasters && rc->getCastsShadow())
 		{
 		{
-			if(r)
-			{
-				visible->moveBackRenderable(alloc, visibleNode);
-			}
+			visible->moveBackRenderable(alloc, visibleNode);
+		}
 
 
-			LightComponent* l = node.tryGetComponent<LightComponent>();
-			if(l)
-			{
-				visible->moveBackLight(alloc, visibleNode);
+		if(lc && wantsLightComponents)
+		{
+			visible->moveBackLight(alloc, visibleNode);
+		}
 
 
-				if(l->getShadowEnabled() && fr)
-				{
-					LockGuard<SpinLock> l(m_shared->m_lock);
-					m_shared->m_frustumsList.pushBack(alloc, &node);
-				}
-			}
+		if(lfc && wantsFlareComponents)
+		{
+			visible->moveBackLensFlare(alloc, visibleNode);
+		}
 
 
-			LensFlareComponent* lf = node.tryGetComponent<LensFlareComponent>();
-			if(lf)
+		// Add more frustums to the list
+		err = node.iterateComponentsOfType<FrustumComponent>(
+			[&](FrustumComponent& frc)
+		{
+			if(frc.anyVisibilityTestEnabled())
 			{
 			{
-				visible->moveBackLensFlare(alloc, visibleNode);
-				ANKI_ASSERT(visibleNode.m_node);
+				LockGuard<SpinLock> l(m_shared->m_lock);
+				m_shared->m_frustumsList.pushBack(alloc, &frc);
 			}
 			}
-		}
+
+			return ErrorCode::NONE;
+		});
+		(void)err;
 
 
 		return ErrorCode::NONE;
 		return ErrorCode::NONE;
 	}); // end for
 	}); // end for
@@ -275,7 +311,7 @@ void VisibilityTestTask::test(SceneNode& testedNode,
 
 
 	if(threadId == 0)
 	if(threadId == 0)
 	{
 	{
-		combineTestResults(testedFr, threadsCount);
+		combineTestResults(testedFrc, threadsCount);
 	}
 	}
 }
 }
 
 
@@ -411,7 +447,8 @@ Error doVisibilityTests(SceneNode& fsn, SceneGraph& scene, MainRenderer& r)
 
 
 	VisibilityShared shared(threadPool.getThreadsCount());
 	VisibilityShared shared(threadPool.getThreadsCount());
 	shared.m_scene = &scene;
 	shared.m_scene = &scene;
-	shared.m_frustumsList.pushBack(scene.getFrameAllocator(), &fsn);
+	shared.m_frustumsList.pushBack(
+		scene.getFrameAllocator(), &fsn.getComponent<FrustumComponent>());
 
 
 	Array<VisibilityTestTask, Threadpool::MAX_THREADS> tasks;
 	Array<VisibilityTestTask, Threadpool::MAX_THREADS> tasks;
 	for(U i = 0; i < threadPool.getThreadsCount(); i++)
 	for(U i = 0; i < threadPool.getThreadsCount(); i++)