Browse Source

Some more work on image reflections

Panagiotis Christopoulos Charitos 10 years ago
parent
commit
c2d20cc659

+ 14 - 13
include/anki/Scene.h

@@ -5,17 +5,18 @@
 
 #pragma once
 
-#include "anki/scene/SceneGraph.h"
-#include "anki/scene/LensFlareComponent.h"
-#include "anki/scene/BodyComponent.h"
-#include "anki/scene/Sector.h"
-#include "anki/scene/ModelNode.h"
-#include "anki/scene/SkinNode.h"
-#include "anki/scene/StaticGeometryNode.h"
-#include "anki/scene/ParticleEmitter.h"
-#include "anki/scene/Camera.h"
-#include "anki/scene/Light.h"
-#include "anki/scene/Path.h"
-#include "anki/scene/StaticCollisionNode.h"
-#include "anki/scene/BodyNode.h"
+#include <anki/scene/SceneGraph.h>
+#include <anki/scene/LensFlareComponent.h>
+#include <anki/scene/BodyComponent.h>
+#include <anki/scene/Sector.h>
+#include <anki/scene/ModelNode.h>
+#include <anki/scene/SkinNode.h>
+#include <anki/scene/StaticGeometryNode.h>
+#include <anki/scene/ParticleEmitter.h>
+#include <anki/scene/Camera.h>
+#include <anki/scene/Light.h>
+#include <anki/scene/Path.h>
+#include <anki/scene/StaticCollisionNode.h>
+#include <anki/scene/BodyNode.h>
+#include <anki/scene/ReflectionProbe.h>
 

+ 4 - 1
include/anki/gr/CommandBuffer.h

@@ -117,7 +117,7 @@ public:
 	void bindResourceGroup(ResourceGroupPtr rc, U slot);
 	/// @}
 
-	/// @name Drawcalls
+	/// @name Jobs
 	/// @{
 	void drawElements(U32 count, U32 instanceCount = 1, U32 firstIndex = 0,
 		U32 baseVertex = 0, U32 baseInstance = 0);
@@ -135,6 +135,9 @@ public:
 	void dispatchCompute(U32 groupCountX, U32 groupCountY, U32 groupCountZ);
 
 	void generateMipmaps(TexturePtr tex);
+
+	void copyTextureToTexture(TexturePtr src, U srcSlice, U srcLevel,
+		TexturePtr dest, U destSlice, U destLevel);
 	/// @}
 
 	/// @name Resource upload

+ 4 - 0
include/anki/gr/gl/TextureImpl.h

@@ -44,6 +44,10 @@ public:
 	/// Generate mipmaps.
 	void generateMipmaps();
 
+	/// Copy a single slice from one texture to another.
+	static void copy(const TextureImpl& src, U srcSlice, U srcLevel,
+		const TextureImpl& dest, U destSlice, U destLevel);
+
 	void bind();
 
 private:

+ 6 - 5
include/anki/scene/Clusterer.h → include/anki/renderer/Clusterer.h

@@ -5,16 +5,17 @@
 
 #pragma once
 
-#include "anki/scene/Common.h"
-#include "anki/Math.h"
-#include "anki/collision/Aabb.h"
-#include "anki/core/Timestamp.h"
+#include <anki/renderer/Common.h>
+#include <anki/Math.h>
+#include <anki/collision/Aabb.h>
+#include <anki/core/Timestamp.h>
 
 namespace anki {
 
 class FrustumComponent;
+class SceneNode;
 
-/// @addtogroup scene
+/// @addtogroup renderer
 /// @{
 
 /// The result of the cluster tests.

+ 4 - 5
include/anki/renderer/Common.h

@@ -3,11 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#ifndef ANKI_RENDERER_COMMON_H
-#define ANKI_RENDERER_COMMON_H
+#pragma once
 
-#include "anki/Gr.h"
-#include "anki/util/Ptr.h"
+#include <anki/Gr.h>
+#include <anki/util/Ptr.h>
 
 namespace anki {
 
@@ -25,6 +24,7 @@ class Bloom;
 class Pps;
 class Dbg;
 class Tiler;
+class Ir;
 
 /// Cut the job submition into multiple chains. We want to avoid feeding
 /// GL with a huge job chain
@@ -32,4 +32,3 @@ const U RENDERER_COMMAND_BUFFERS_COUNT = 2;
 
 } // end namespace anki
 
-#endif

+ 1 - 1
include/anki/renderer/Fs.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include "anki/renderer/RenderingPass.h"
+#include <anki/renderer/RenderingPass.h>
 
 namespace anki {
 

+ 56 - 0
include/anki/renderer/Ir.h

@@ -0,0 +1,56 @@
+// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/renderer/Renderer.h>
+
+namespace anki {
+
+/// @addtogroup renderer
+/// @{
+
+/// Image based reflections.
+class Ir
+{
+anki_internal:
+	Ir()
+	{}
+
+	~Ir();
+
+	ANKI_USE_RESULT Error init(
+		ThreadPool* threadpool,
+		ResourceManager* resources,
+		GrManager* gr,
+		HeapAllocator<U8> alloc,
+		StackAllocator<U8> frameAlloc,
+		const ConfigSet& initializer,
+		const Timestamp* globalTimestamp);
+
+	ANKI_USE_RESULT Error run(SceneNode& frustumNode);
+
+	HeapAllocator<U8> getAllocator() const
+	{
+		return m_r.getAllocator();
+	}
+
+	TexturePtr getCubemapArray() const
+	{
+		return m_cubemapArr;
+	}
+
+private:
+	Renderer m_r;
+	TexturePtr m_cubemapArr;
+	U16 m_cubemapArrSize = 0;
+	U16 m_fbSize = 0;
+
+	ANKI_USE_RESULT Error renderReflection(SceneNode& node);
+};
+/// @}
+
+} // end namespace anki
+

+ 1 - 1
include/anki/renderer/Is.h

@@ -138,7 +138,7 @@ private:
 	Sm m_sm;
 
 	/// Opt because many ask for it
-	SceneNode* m_cam;
+	FrustumComponent* m_frc = nullptr;
 
 	/// If enabled the ground emmits a light
 	Bool8 m_groundLightEnabled;

+ 1 - 0
include/anki/renderer/MainRenderer.h

@@ -65,6 +65,7 @@ private:
 	StackAllocator<U8> m_frameAlloc;
 
 	UniquePtr<Renderer> m_r;
+	UniquePtr<Ir> m_ir;
 
 	ShaderResourcePtr m_blitFrag;
 	PipelinePtr m_blitPpline;

+ 27 - 17
include/anki/renderer/Renderer.h

@@ -5,15 +5,15 @@
 
 #pragma once
 
-#include "anki/renderer/Common.h"
-#include "anki/renderer/Drawer.h"
-#include "anki/Math.h"
-#include "anki/Gr.h"
-#include "anki/scene/Forward.h"
-#include "anki/scene/Clusterer.h"
-#include "anki/resource/Forward.h"
-#include "anki/resource/ShaderResource.h"
-#include "anki/core/Timestamp.h"
+#include <anki/renderer/Common.h>
+#include <anki/renderer/Drawer.h>
+#include <anki/renderer/Clusterer.h>
+#include <anki/Math.h>
+#include <anki/Gr.h>
+#include <anki/scene/Forward.h>
+#include <anki/resource/Forward.h>
+#include <anki/resource/ShaderResource.h>
+#include <anki/core/Timestamp.h>
 
 namespace anki {
 
@@ -96,7 +96,8 @@ public:
 		HeapAllocator<U8> alloc,
 		StackAllocator<U8> frameAlloc,
 		const ConfigSet& config,
-		const Timestamp* globalTimestamp);
+		const Timestamp* globalTimestamp,
+		TexturePtr reflections = TexturePtr());
 
 	/// Set the output of the renderer before calling #render.
 	void setOutputFramebuffer(FramebufferPtr outputFb, U32 width, U32 height)
@@ -107,7 +108,7 @@ public:
 
 	/// This function does all the rendering stages and produces a final result.
 	ANKI_USE_RESULT Error render(
-		SceneNode& frustumableNode,
+		SceneNode& frustumableNode, U frustumIdx,
 		Array<CommandBufferPtr, RENDERER_COMMAND_BUFFERS_COUNT>& cmdBuff);
 
 anki_internal:
@@ -128,14 +129,16 @@ anki_internal:
 		return m_framesNum;
 	}
 
-	const SceneNode& getActiveCamera() const
+	const FrustumComponent& getActiveFrustumComponent() const
 	{
-		return *m_frustumable;
+		ANKI_ASSERT(m_frc);
+		return *m_frc;
 	}
 
-	SceneNode& getActiveCamera()
+	FrustumComponent& getActiveFrustumComponent()
 	{
-		return *m_frustumable;
+		ANKI_ASSERT(m_frc);
+		return *m_frc;
 	}
 
 	const RenderableDrawer& getSceneDrawer() const
@@ -248,7 +251,7 @@ anki_internal:
 		return *m_gr;
 	}
 
-	HeapAllocator<U8>& getAllocator()
+	HeapAllocator<U8> getAllocator() const
 	{
 		return m_alloc;
 	}
@@ -273,6 +276,11 @@ anki_internal:
 		return *m_globalTimestamp;
 	}
 
+	TexturePtr getReflectionsCubemapArr() const
+	{
+		return m_reflectionsCubemapArr;
+	}
+
 private:
 	ThreadPool* m_threadpool;
 	ResourceManager* m_resources;
@@ -317,7 +325,7 @@ private:
 	Timestamp m_projectionParamsUpdateTimestamp = 0;
 	/// @}
 
-	SceneNode* m_frustumable = nullptr; ///< Cache current frustumable node.
+	FrustumComponent* m_frc = nullptr; ///< Cache current frustum component.
 	RenderableDrawer m_sceneDrawer;
 
 	U m_framesNum; ///< Frame number
@@ -325,6 +333,8 @@ private:
 	FramebufferPtr m_outputFb;
 	UVec2 m_outputFbSize;
 
+	TexturePtr m_reflectionsCubemapArr;
+
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 };
 /// @}

+ 17 - 10
include/anki/scene/ReflectionProbe.h

@@ -5,10 +5,10 @@
 
 #pragma once
 
-#include "anki/scene/SceneNode.h"
-#include "anki/collision/Frustum.h"
-#include "anki/collision/Sphere.h"
-#include "anki/Gr.h"
+#include <anki/scene/SceneNode.h>
+#include <anki/collision/Frustum.h>
+#include <anki/collision/Sphere.h>
+#include <anki/Gr.h>
 
 namespace anki {
 
@@ -45,24 +45,31 @@ public:
 
 	ANKI_USE_RESULT Error create(const CString& name, F32 radius);
 
+	U getCubemapArrayIndex() const
+	{
+		ANKI_ASSERT(m_cubemapArrayIdx < 0xFF);
+		return m_cubemapArrayIdx;
+	}
+
+	void setCubemapArrayIndex(U cubemapArrayIdx)
+	{
+		ANKI_ASSERT(cubemapArrayIdx < 0xFF);
+		m_cubemapArrayIdx = cubemapArrayIdx;
+	}
+
 private:
 	class CubeSide
 	{
 	public:
 		PerspectiveFrustum m_frustum;
 		Transform m_localTrf;
-		FramebufferPtr m_fb;
 	};
 
 	Array<CubeSide, 6> m_cubeSides;
-
-	TexturePtr m_colorTex;
-	U32 m_fbSize = 128;
 	Sphere m_spatialSphere;
+	U8 m_cubemapArrayIdx = 0xFF; ///< Used by the renderer
 
 	void onMoveUpdate(MoveComponent& move);
-
-	void createGraphics();
 };
 /// @}
 

+ 4 - 0
src/core/Config.cpp

@@ -61,6 +61,10 @@ Config::Config()
 	newOption("pps.sharpen", true);
 	newOption("pps.gammaCorrection", true);
 
+	// Reflections
+	newOption("ir.rendererSize", 64);
+	newOption("ir.cubemapTextureArraySize", 8);
+
 	// Dbg
 	newOption("dbg.enabled", false);
 

+ 36 - 0
src/gr/gl/CommandBuffer.cpp

@@ -557,5 +557,41 @@ Bool CommandBuffer::isEmpty() const
 	return m_impl->isEmpty();
 }
 
+//==============================================================================
+class CopyTexCommand final: public GlCommand
+{
+public:
+	TexturePtr m_src;
+	U16 m_srcSlice;
+	U16 m_srcLevel;
+	TexturePtr m_dest;
+	U16 m_destSlice;
+	U16 m_destLevel;
+
+	CopyTexCommand(TexturePtr src, U srcSlice, U srcLevel, TexturePtr dest,
+		U destSlice, U destLevel)
+		: m_src(src)
+		, m_srcSlice(srcSlice)
+		, m_srcLevel(srcLevel)
+		, m_dest(dest)
+		, m_destSlice(destSlice)
+		, m_destLevel(destLevel)
+	{}
+
+	Error operator()(GlState&)
+	{
+		TextureImpl::copy(m_src->getImplementation(), m_srcSlice, m_srcLevel,
+			m_dest->getImplementation(), m_destSlice, m_destLevel);
+		return ErrorCode::NONE;
+	}
+};
+
+void CommandBuffer::copyTextureToTexture(TexturePtr src, U srcSlice, U srcLevel,
+	TexturePtr dest, U destSlice, U destLevel)
+{
+	m_impl->pushBackNewCommand<CopyTexCommand>(src, srcSlice, srcLevel, dest,
+		destSlice, destLevel);
+}
+
 } // end namespace anki
 

+ 33 - 0
src/gr/gl/TextureImpl.cpp

@@ -460,4 +460,37 @@ U32 TextureImpl::computeMaxMipmapCount(U32 w, U32 h)
 	return count;
 }
 
+//==============================================================================
+void TextureImpl::copy(const TextureImpl& src, U srcSlice, U srcLevel,
+	const TextureImpl& dest, U destSlice, U destLevel)
+{
+	ANKI_ASSERT(src.m_internalFormat == dest.m_internalFormat);
+	ANKI_ASSERT(src.m_format == dest.m_format);
+	ANKI_ASSERT(src.m_type == dest.m_type);
+
+	U width = src.m_width >> srcLevel;
+	U height = src.m_height >> srcLevel;
+
+	ANKI_ASSERT(width > 0 && height > 0);
+	ANKI_ASSERT(width == (dest.m_width >> destLevel)
+		&& height == (dest.m_height >> destLevel));
+
+	glCopyImageSubData(
+		src.getGlName(),
+		src.m_target,
+		srcLevel,
+		0,
+		0,
+		srcSlice,
+		dest.getGlName(),
+		dest.m_target,
+		destLevel,
+		0,
+		0,
+		destSlice,
+		width,
+		height,
+		1);
+}
+
 } // end namespace anki

+ 5 - 5
src/scene/Clusterer.cpp → src/renderer/Clusterer.cpp

@@ -3,11 +3,11 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#include "anki/scene/Clusterer.h"
-#include "anki/scene/FrustumComponent.h"
-#include "anki/scene/MoveComponent.h"
-#include "anki/scene/SceneNode.h"
-#include "anki/util/Rtti.h"
+#include <anki/renderer/Clusterer.h>
+#include <anki/scene/FrustumComponent.h>
+#include <anki/scene/MoveComponent.h>
+#include <anki/scene/SceneNode.h>
+#include <anki/util/Rtti.h>
 
 namespace anki {
 

+ 2 - 3
src/renderer/Dbg.cpp

@@ -19,7 +19,6 @@
 #include "anki/collision/ConvexHullShape.h"
 #include "anki/util/Rtti.h"
 #include "anki/Ui.h" /// XXX
-#include "anki/scene/Clusterer.h" /// XXX
 
 namespace anki {
 
@@ -84,8 +83,8 @@ Error Dbg::run(CommandBufferPtr& cmdb)
 
 	cmdb->bindFramebuffer(m_fb);
 
-	SceneNode& cam = m_r->getActiveCamera();
-	FrustumComponent& camFr = cam.getComponent<FrustumComponent>();
+	FrustumComponent& camFr = m_r->getActiveFrustumComponent();
+	SceneNode& cam = camFr.getSceneNode();
 	m_drawer->prepareDraw(cmdb);
 	m_drawer->setViewProjectionMatrix(camFr.getViewProjectionMatrix());
 	m_drawer->setModelMatrix(Mat4::getIdentity());

+ 1 - 2
src/renderer/Fs.cpp

@@ -60,8 +60,7 @@ Error Fs::run(CommandBufferPtr& cmdb)
 {
 	cmdb->bindFramebuffer(m_fb);
 
-	SceneNode& cam = m_r->getActiveCamera();
-	FrustumComponent& camFr = cam.getComponent<FrustumComponent>();
+	FrustumComponent& camFr = m_r->getActiveFrustumComponent();
 
 	cmdb->bindResourceGroup(m_globalResources[getGlobalTimestamp() % 3], 1);
 

+ 138 - 0
src/renderer/Ir.cpp

@@ -0,0 +1,138 @@
+// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <anki/renderer/Ir.h>
+#include <anki/renderer/Is.h>
+#include <anki/core/Config.h>
+#include <anki/scene/SceneNode.h>
+#include <anki/scene/Visibility.h>
+#include <anki/scene/FrustumComponent.h>
+#include <anki/scene/ReflectionProbe.h>
+
+namespace anki {
+
+//==============================================================================
+Ir::~Ir()
+{}
+
+//==============================================================================
+Error Ir::init(
+	ThreadPool* threadpool,
+	ResourceManager* resources,
+	GrManager* gr,
+	HeapAllocator<U8> alloc,
+	StackAllocator<U8> frameAlloc,
+	const ConfigSet& initializer,
+	const Timestamp* globalTimestamp)
+{
+	ANKI_LOGI("Initializing IR (Image Reflections)");
+	m_fbSize = initializer.getNumber("ir.rendererSize");
+
+	if(m_fbSize < Renderer::TILE_SIZE)
+	{
+		ANKI_LOGE("Too low ir.rendererSize");
+		return ErrorCode::USER_DATA;
+	}
+
+	m_cubemapArrSize = initializer.getNumber("ir.cubemapTextureArraySize");
+
+	if(m_cubemapArrSize < 2)
+	{
+		ANKI_LOGE("Too low ir.cubemapTextureArraySize");
+		return ErrorCode::USER_DATA;
+	}
+
+	// Init the renderer
+	Config config;
+	config.set("dbg.enabled", false);
+	config.set("is.sm.bilinearEnabled", true);
+	config.set("is.groundLightEnabled", false);
+	config.set("is.sm.enabled", true);
+	config.set("is.sm.maxLights", 8);
+	config.set("is.sm.poissonEnabled", false);
+	config.set("is.sm.resolution", 16);
+	config.set("lf.maxFlares", 8);
+	config.set("pps.enabled", false);
+	config.set("renderingQuality", 1.0);
+	config.set("width", m_fbSize);
+	config.set("height", m_fbSize);
+	config.set("lodDistance", 10.0);
+	config.set("samples", 1);
+
+	ANKI_CHECK(m_r.init(threadpool, resources, gr, alloc, frameAlloc, config,
+		globalTimestamp));
+
+	// Init the texture
+	TextureInitializer texinit;
+
+	texinit.m_width = m_fbSize;
+	texinit.m_height = m_fbSize;
+	texinit.m_depth = m_cubemapArrSize;
+	texinit.m_type = TextureType::CUBE;
+	texinit.m_format = Is::RT_PIXEL_FORMAT;
+	texinit.m_mipmapsCount = 1;
+	texinit.m_samples = 1;
+	texinit.m_sampling.m_minMagFilter = SamplingFilter::LINEAR;
+
+	m_cubemapArr = gr->newInstance<Texture>(texinit);
+
+	return ErrorCode::NONE;
+}
+
+//==============================================================================
+Error Ir::run(SceneNode& frustumNode)
+{
+	FrustumComponent& frc = frustumNode.getComponent<FrustumComponent>();
+	VisibilityTestResults& visRez = frc.getVisibilityTestResults();
+
+	if(visRez.getReflectionProbeCount() == 0)
+	{
+		// Early out
+		return ErrorCode::NONE;
+	}
+
+	const VisibleNode* begin = visRez.getReflectionProbesBegin();
+	const VisibleNode* end = visRez.getReflectionProbesBegin();
+	while(begin != end)
+	{
+		ANKI_CHECK(renderReflection(*begin->m_node));
+	}
+
+	return ErrorCode::NONE;
+}
+
+//==============================================================================
+Error Ir::renderReflection(SceneNode& node)
+{
+	const ReflectionProbeComponent& reflc =
+		node.getComponent<ReflectionProbeComponent>();
+
+	for(U i = 0; i < 6; ++i)
+	{
+		Array<CommandBufferPtr, RENDERER_COMMAND_BUFFERS_COUNT> cmdb;
+		for(U j = 0; j < cmdb.getSize(); ++j)
+		{
+			cmdb[j] = m_r.getGrManager().newInstance<CommandBuffer>();
+		}
+
+		// Render
+		ANKI_CHECK(m_r.render(node, i, cmdb));
+
+		// Copy textures
+		cmdb[1]->copyTextureToTexture(m_r.getIs().getRt(), 0, 0,
+			m_cubemapArr, i, 0);
+
+		// Flush
+		for(U j = 0; j < cmdb.getSize(); ++j)
+		{
+			cmdb[j]->flush();
+		}
+	}
+
+	return ErrorCode::NONE;
+}
+
+} // end namespace anki
+

+ 7 - 8
src/renderer/Is.cpp

@@ -359,9 +359,8 @@ Error Is::initInternal(const ConfigSet& config)
 Error Is::lightPass(CommandBufferPtr& cmdBuff)
 {
 	ThreadPool& threadPool = m_r->getThreadPool();
-	m_cam = &m_r->getActiveCamera();
-	FrustumComponent& fr = m_cam->getComponent<FrustumComponent>();
-	VisibilityTestResults& vi = fr.getVisibilityTestResults();
+	m_frc = &m_r->getActiveFrustumComponent();
+	VisibilityTestResults& vi = m_frc->getVisibilityTestResults();
 
 	m_currentFrame = getGlobalTimestamp() % MAX_FRAMES_IN_FLIGHT;
 
@@ -482,7 +481,7 @@ Error Is::lightPass(CommandBufferPtr& cmdBuff)
 	setState(cmdBuff);
 
 	// Update uniforms
-	updateCommonBlock(cmdBuff, fr);
+	updateCommonBlock(cmdBuff, *m_frc);
 
 	// Sync
 	ANKI_CHECK(threadPool.waitForAllThreadsToFinish());
@@ -515,8 +514,9 @@ Error Is::lightPass(CommandBufferPtr& cmdBuff)
 void Is::binLights(U32 threadId, PtrSize threadsCount, TaskCommonData& task)
 {
 	U lightsCount = task.m_lightsEnd - task.m_lightsBegin;
-	const FrustumComponent& camfrc = m_cam->getComponent<FrustumComponent>();
-	const MoveComponent& cammove = m_cam->getComponent<MoveComponent>();
+	const FrustumComponent& camfrc = *m_frc;
+	const MoveComponent& cammove =
+		m_frc->getSceneNode().getComponent<MoveComponent>();
 
 	// Iterate lights and bin them
 	PtrSize start, end;
@@ -807,8 +807,7 @@ void Is::updateCommonBlock(CommandBufferPtr& cmdb, const FrustumComponent& fr)
 	Vec3 groundLightDir;
 	if(m_groundLightEnabled)
 	{
-		const Mat4& viewMat =
-			m_cam->getComponent<FrustumComponent>().getViewMatrix();
+		const Mat4& viewMat = m_frc->getViewMatrix();
 		blk->m_groundLightDirTime =
 			Vec4(-viewMat.getColumn(1).xyz(), HighRezTimer::getCurrentTime());
 	}

+ 2 - 4
src/renderer/Lf.cpp

@@ -165,8 +165,7 @@ Error Lf::initInternal(const ConfigSet& config)
 void Lf::runOcclusionTests(CommandBufferPtr& cmdb)
 {
 	// Retrieve some things
-	FrustumComponent& camFr =
-		m_r->getActiveCamera().getComponent<FrustumComponent>();
+	FrustumComponent& camFr = m_r->getActiveFrustumComponent();
 	VisibilityTestResults& vi = camFr.getVisibilityTestResults();
 
 	U totalCount = min<U>(vi.getLensFlaresCount(), m_maxFlares);
@@ -228,8 +227,7 @@ void Lf::runOcclusionTests(CommandBufferPtr& cmdb)
 void Lf::run(CommandBufferPtr& cmdb)
 {
 	// Retrieve some things
-	SceneNode& cam = m_r->getActiveCamera();
-	FrustumComponent& camFr = cam.getComponent<FrustumComponent>();
+	FrustumComponent& camFr = m_r->getActiveFrustumComponent();
 	VisibilityTestResults& vi = camFr.getVisibilityTestResults();
 
 	U totalCount = min<U>(vi.getLensFlaresCount(), m_maxFlares);

+ 29 - 17
src/renderer/MainRenderer.cpp

@@ -3,20 +3,21 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#include "anki/renderer/MainRenderer.h"
-#include "anki/renderer/Renderer.h"
-#include "anki/renderer/Is.h"
-#include "anki/renderer/Pps.h"
-#include "anki/renderer/Dbg.h"
-#include "anki/renderer/Ms.h"
-#include "anki/scene/SceneGraph.h"
-#include "anki/scene/Camera.h"
-#include "anki/util/Logger.h"
-#include "anki/util/File.h"
-#include "anki/util/Filesystem.h"
-#include "anki/core/Counters.h"
-#include "anki/core/App.h"
-#include "anki/misc/ConfigSet.h"
+#include <anki/renderer/MainRenderer.h>
+#include <anki/renderer/Renderer.h>
+#include <anki/renderer/Is.h>
+#include <anki/renderer/Pps.h>
+#include <anki/renderer/Dbg.h>
+#include <anki/renderer/Ms.h>
+#include <anki/renderer/Ir.h>
+#include <anki/scene/SceneGraph.h>
+#include <anki/scene/Camera.h>
+#include <anki/util/Logger.h>
+#include <anki/util/File.h>
+#include <anki/util/Filesystem.h>
+#include <anki/core/Counters.h>
+#include <anki/core/App.h>
+#include <anki/misc/ConfigSet.h>
 
 namespace anki {
 
@@ -63,6 +64,11 @@ Error MainRenderer::create(
 	ANKI_CHECK(m_r->init(threadpool, resources, gr, m_alloc,
 		m_frameAlloc, config2, globalTimestamp));
 
+	// Init IR
+	m_ir.reset(m_alloc.newInstance<Ir>());
+	ANKI_CHECK(m_ir->init(threadpool, resources, gr, m_alloc,
+		m_frameAlloc, config, globalTimestamp));
+
 	// Set the default preprocessor string
 	m_materialShaderSource.sprintf(
 		m_alloc,
@@ -136,9 +142,12 @@ Error MainRenderer::render(SceneGraph& scene)
 		m_r->setOutputFramebuffer(FramebufferPtr(), 0, 0);
 	}
 
+	// Run reflection passes
+	ANKI_CHECK(m_ir->run(scene.getActiveCamera()));
+
 	// Run renderer
 	m_r->getIs().setAmbientColor(scene.getAmbientColor());
-	ANKI_CHECK(m_r->render(scene.getActiveCamera(), cmdbs));
+	ANKI_CHECK(m_r->render(scene.getActiveCamera(), 0, cmdbs));
 
 	if(!rDrawToDefault)
 	{
@@ -151,8 +160,11 @@ Error MainRenderer::render(SceneGraph& scene)
 		m_r->drawQuad(cmdb);
 	}
 
-	// Flush the last command buffer
-	cmdb->flush();
+	// Flush the command buffers
+	for(U i = 0; i < RENDERER_COMMAND_BUFFERS_COUNT; i++)
+	{
+		cmdbs[i]->flush();
+	}
 
 	// Set the hints
 	for(U i = 0; i < RENDERER_COMMAND_BUFFERS_COUNT; i++)

+ 1 - 3
src/renderer/Ms.cpp

@@ -124,9 +124,7 @@ Error Ms::run(CommandBufferPtr& cmdb)
 	cmdb->bindFramebuffer(m_planes[planeId].m_fb);
 
 	// render all
-	SceneNode& cam = m_r->getActiveCamera();
-
-	FrustumComponent& frc = cam.getComponent<FrustumComponent>();
+	FrustumComponent& frc = m_r->getActiveFrustumComponent();
 	SArray<CommandBufferPtr> cmdbs(
 		&m_secondLevelCmdbs[0], m_secondLevelCmdbs.getSize());
 	ANKI_CHECK(m_r->getSceneDrawer().render(

+ 1 - 2
src/renderer/Pps.cpp

@@ -219,8 +219,7 @@ void Pps::run(CommandBufferPtr& cmdb)
 		cmdb->writeBuffer(m_uniformsBuff, 0, sizeof(*unis), unis);
 		unis->m_fogColorFogFactor = Vec4(m_fogColor, m_fogFactor);
 
-		const SceneNode& cam = m_r->getActiveCamera();
-		const FrustumComponent& frc = cam.getComponent<FrustumComponent>();
+		const FrustumComponent& frc = m_r->getActiveFrustumComponent();
 		unis->m_nearFarPad2 = Vec4(frc.getFrustum().getNear(),
 			frc.getFrustum().getFar(), 0.0, 0.0);
 	}

+ 25 - 10
src/renderer/Renderer.cpp

@@ -36,7 +36,8 @@ Error Renderer::init(
 	HeapAllocator<U8> alloc,
 	StackAllocator<U8> frameAlloc,
 	const ConfigSet& config,
-	const Timestamp* globalTimestamp)
+	const Timestamp* globalTimestamp,
+	TexturePtr reflections)
 {
 	m_globalTimestamp = globalTimestamp;
 	m_threadpool = threadpool;
@@ -44,6 +45,7 @@ Error Renderer::init(
 	m_gr = gl;
 	m_alloc = alloc;
 	m_frameAlloc = frameAlloc;
+	m_reflectionsCubemapArr = reflections;
 
 	Error err = initInternal(config);
 	if(err)
@@ -128,23 +130,38 @@ Error Renderer::initInternal(const ConfigSet& config)
 }
 
 //==============================================================================
-Error Renderer::render(SceneNode& frustumableNode,
+Error Renderer::render(SceneNode& frustumableNode, U frustumIdx,
 	Array<CommandBufferPtr, RENDERER_COMMAND_BUFFERS_COUNT>& cmdb)
 {
-	m_frustumable = &frustumableNode;
+	m_frc = nullptr;
+	Error err = frustumableNode.iterateComponentsOfType<FrustumComponent>(
+		[&](FrustumComponent& frc) -> Error
+	{
+		if(frustumIdx == 0)
+		{
+			m_frc = &frc;
+		}
+		else
+		{
+			--frustumIdx;
+		}
+
+		return ErrorCode::NONE;
+	});
+	(void)err;
+	ANKI_ASSERT(m_frc && "Not enough frustum components");
+
 	m_frameAlloc.getMemoryPool().reset();
 
 	// Calc a few vars
 	//
-	const FrustumComponent& frc =
-		m_frustumable->getComponent<FrustumComponent>();
-	if(frc.getProjectionParameters() != m_projectionParams)
+	if(m_frc->getProjectionParameters() != m_projectionParams)
 	{
-		m_projectionParams = frc.getProjectionParameters();
+		m_projectionParams = m_frc->getProjectionParameters();
 		m_projectionParamsUpdateTimestamp = getGlobalTimestamp();
 	}
 
-	ANKI_ASSERT(frc.getFrustum().getType() == Frustum::Type::PERSPECTIVE);
+	ANKI_ASSERT(m_frc->getFrustum().getType() == Frustum::Type::PERSPECTIVE);
 	m_clusterer.prepare(getThreadPool(), frustumableNode);
 
 	ANKI_COUNTER_START_TIMER(RENDERER_MS_TIME);
@@ -157,8 +174,6 @@ Error Renderer::render(SceneNode& frustumableNode,
 
 	m_tiler->run(cmdb[0]);
 
-	cmdb[0]->flush();
-
 	ANKI_COUNTER_START_TIMER(RENDERER_IS_TIME);
 	ANKI_CHECK(m_is->run(cmdb[1]));
 	ANKI_COUNTER_STOP_TIMER_INC(RENDERER_IS_TIME);

+ 1 - 2
src/renderer/Ssao.cpp

@@ -270,8 +270,7 @@ void Ssao::run(CommandBufferPtr& cmdb)
 	cmdb->bindResourceGroup(m_rcFirst, 0);
 
 	// Write common block
-	const FrustumComponent& camFr =
-		m_r->getActiveCamera().getComponent<FrustumComponent>();
+	const FrustumComponent& camFr = m_r->getActiveFrustumComponent();
 
 	if(m_commonUboUpdateTimestamp
 			< m_r->getProjectionParametersUpdateTimestamp()

+ 6 - 31
src/scene/ReflectionProbe.cpp

@@ -44,6 +44,10 @@ public:
 // ReflectionProbe                                                             =
 //==============================================================================
 
+//==============================================================================
+ReflectionProbe::~ReflectionProbe()
+{}
+
 //==============================================================================
 Error ReflectionProbe::create(const CString& name, F32 radius)
 {
@@ -104,44 +108,15 @@ Error ReflectionProbe::create(const CString& name, F32 radius)
 	m_spatialSphere.setRadius(radius);
 	comp = getSceneAllocator().newInstance<SpatialComponent>(
 		this, &m_spatialSphere);
+	addComponent(comp, true);
 
-	// Reflection probe
+	// Reflection probe comp
 	comp = getSceneAllocator().newInstance<ReflectionProbeComponent>(this);
 	addComponent(comp, true);
 
-	// Create graphics objects
-	createGraphics();
-
 	return ErrorCode::NONE;
 }
 
-//==============================================================================
-void ReflectionProbe::createGraphics()
-{
-	// Create textures
-	TextureInitializer init;
-	init.m_type = TextureType::CUBE;
-	init.m_width = init.m_height = m_fbSize;
-	init.m_format = Is::RT_PIXEL_FORMAT;
-	init.m_sampling.m_minMagFilter = SamplingFilter::LINEAR;
-
-	m_colorTex = getSceneGraph().getGrManager().newInstance<Texture>(init);
-
-	// Create framebuffers
-	for(U i = 0; i < 6; ++i)
-	{
-		FramebufferInitializer fbInit;
-		fbInit.m_colorAttachmentsCount = 1;
-		fbInit.m_colorAttachments[0].m_texture = m_colorTex;
-		fbInit.m_colorAttachments[0].m_layer = i;
-		fbInit.m_colorAttachments[0].m_loadOperation =
-			AttachmentLoadOperation::DONT_CARE;
-
-		m_cubeSides[i].m_fb =
-			getSceneGraph().getGrManager().newInstance<Framebuffer>(fbInit);
-	}
-}
-
 //==============================================================================
 void ReflectionProbe::onMoveUpdate(MoveComponent& move)
 {

+ 7 - 0
testapp/Main.cpp

@@ -95,6 +95,13 @@ Error init()
 		1.0));
 #endif
 
+	{
+		ReflectionProbe* refl;
+		scene.newSceneNode<ReflectionProbe>("refl", refl, 6.0f);
+		move = refl->tryGetComponent<MoveComponent>();
+		move->setLocalOrigin(Vec4(147.392776, -10.132728, 14.607138, 0.0));
+	}
+
 #if 0
 	PointLight* plight;
 	scene.newSceneNode<PointLight>("spot0", plight);