浏览代码

More work on light probes

BearishSun 8 年之前
父节点
当前提交
13111a798c

+ 3 - 0
Source/BansheeCore/CMakeSources.cmake

@@ -101,6 +101,7 @@ set(BS_BANSHEECORE_INC_RENDERER
 	"Include/BsRendererExtension.h"
 	"Include/BsReflectionProbe.h"
 	"Include/BsSkybox.h"
+	"Include/BsLightProbeVolume.h"
 )
 
 set(BS_BANSHEECORE_SRC_LOCALIZATION
@@ -361,6 +362,7 @@ set(BS_BANSHEECORE_INC_RTTI
 	"Include/BsReflectionProbeRTTI.h"
 	"Include/BsCReflectionProbeRTTI.h"
 	"Include/BsSkyboxRTTI.h"
+	"Include/BsLightProbeVolumeRTTI.h"
 )
 
 set(BS_BANSHEECORE_SRC_RENDERER
@@ -375,6 +377,7 @@ set(BS_BANSHEECORE_SRC_RENDERER
 	"Source/BsRendererExtension.cpp"
 	"Source/BsReflectionProbe.cpp"
 	"Source/BsSkybox.cpp"
+	"Source/BsLightProbeVolume.cpp"
 )
 
 set(BS_BANSHEECORE_SRC_RESOURCES

+ 3 - 2
Source/BansheeCore/Include/BsCorePrerequisites.h

@@ -569,8 +569,9 @@ namespace bs
 		TID_ReflectionProbe = 1131,
 		TID_CReflectionProbe = 1132,
 		TID_CachedTextureData = 1133,
-        TID_Skybox = 1134,
-        TID_CSkybox = 1135,
+		TID_Skybox = 1134,
+		TID_CSkybox = 1135,
+		TID_LightProbeVolume = 1136,
 
 		// Moved from Engine layer
 		TID_CCamera = 30000,

+ 209 - 0
Source/BansheeCore/Include/BsLightProbeVolume.h

@@ -0,0 +1,209 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsCoreObject.h"
+#include "BsAABox.h"
+#include "BsVector3.h"
+#include "BsQuaternion.h"
+
+namespace bs
+{
+	/** @addtogroup Implementation
+	 *  @{
+	 */
+
+	/** Potential states the light probe can be in. */
+	enum class LightProbeFlags
+	{
+		 Empty, Clean, Dirty, Removed 
+	};
+
+	/** Base class for both sim and core thread LightProbeVolume implementations. */
+	class BS_CORE_EXPORT LightProbeVolumeBase
+	{
+	public:
+		LightProbeVolumeBase();
+		virtual ~LightProbeVolumeBase() { }
+
+		/**	Returns the position of the volume, in world space. */
+		Vector3 getPosition() const { return mPosition; }
+
+		/**	Sets the position of the volume, in world space. */
+		void setPosition(const Vector3& position) { mPosition = position; _markCoreDirty(); }
+
+		/**	Returns the rotation of the volume, in world space. */
+		Quaternion getRotation() const { return mRotation; }
+
+		/**	Sets the rotation of the light, in world space. */
+		void setRotation(const Quaternion& rotation) { mRotation = rotation; _markCoreDirty(); }
+
+		/**	Checks whether the light volume should be used during rendering or not. */
+		bool getIsActive() const { return mIsActive; }
+
+		/**	Sets whether the light volume should be used during rendering or not. */
+		void setIsActive(bool active) { mIsActive = active; _markCoreDirty(); }
+
+		/** 
+		 * Marks the simulation thread object as dirty and notifies the system its data should be synced with its core 
+		 * thread counterpart. 
+		 */
+		virtual void _markCoreDirty() { }
+
+	protected:
+		Vector3 mPosition; /**< World space position. */
+		Quaternion mRotation; /**< World space rotation. */
+		bool mIsActive; /**< Whether the light volume should be used during rendering or not. */
+	};
+
+	/** @} */
+	/** @addtogroup Renderer-Engine-Internal
+	 *  @{
+	 */
+
+	namespace ct { class LightProbeVolume; }
+
+	/** 
+	 * Allows you to define a volume of light probes that will be used for indirect lighting. Lighting information in the
+	 * scene will be interpolated from nearby probes to calculate the amount of indirect lighting at that position. It is
+	 * up to the caller to place the light probes in areas where the lighting changes in order to yield the best results.
+	 *
+	 * The volume can never have less than 4 probes.
+	 */
+	class BS_CORE_EXPORT LightProbeVolume : public IReflectable, public CoreObject, public LightProbeVolumeBase
+	{
+		/** Internal information about a single light probe. */
+		struct ProbeInfo
+		{
+			ProbeInfo() {}
+			ProbeInfo(LightProbeFlags flags, const Vector3& position)
+				:flags(flags), position(position) 
+			{ }
+
+			LightProbeFlags flags;
+			Vector3 position;
+		};
+	public:
+		/** Adds a new probe at the specified position and returns a handle to the probe. */
+		UINT32 addProbe(const Vector3& position);
+
+		/** Updates the position of the probe with the specified handle. */
+		void setProbePosition(UINT32 handle, const Vector3& position);
+
+		/** Retrieves the position of the probe with the specified handle. */
+		Vector3 getProbePosition(UINT32 handle) const;
+
+		/** 
+		 * Removes the probe with the specified handle. Note that if this is one of the last four remaining probes in the
+		 * volume it cannot be removed.
+		 */
+		void removeProbe(UINT32 handle);
+
+		/**	Retrieves an implementation of the object usable only from the core thread. */
+		SPtr<ct::LightProbeVolume> getCore() const;
+
+		/**
+		 * Creates a new light volume with probes aligned in a grid pattern.
+		 * 
+		 * @param[in]	volume		Axis aligned volume to be covered by the light probes.
+		 * @param[in]	density		Density of light probes in each direction. Starting on one side of the volume, a new
+		 *							probe will be added every 1/density meters (per-axis). Note that one probe will be
+		 *							placed at the start and at the end of the volume, regardless of density. This means the
+		 *							smallest volume will have 8 probes.
+		 */
+		static SPtr<LightProbeVolume> create(const AABox& volume = AABox::UNIT_BOX, const Vector3& density = Vector3::ONE);
+	protected:
+		friend class ct::LightProbeVolume;
+
+		LightProbeVolume(const AABox& volume, const Vector3& density);
+
+		/** @copydoc CoreObject::createCore */
+		SPtr<ct::CoreObject> createCore() const override;
+
+		/** @copydoc LightProbeVolumeBase::_markCoreDirty */
+		void _markCoreDirty() override;
+
+		/** @copydoc CoreObject::syncToCore */
+		CoreSyncData syncToCore(FrameAlloc* allocator) override;
+
+		/**	Creates a light volume with without initializing it. Used for serialization. */
+		static SPtr<LightProbeVolume> createEmpty();
+
+	private:
+		UnorderedMap<UINT32, ProbeInfo> mProbes;
+		UINT32 mNextProbeId = 0;
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+	public:
+		friend class LightProbeVolumeRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		RTTITypeBase* getRTTI() const override;
+
+	protected:
+		LightProbeVolume(); // Serialization only
+	};
+
+	namespace ct
+	{
+	/** Vector representing spherical harmonic coefficients for 3 bands. */
+	struct SHVector3
+	{
+		float coeffs[9];
+	};
+
+	/** Vector representing spherical coefficients for 3 bands, separate for red, green and blue components. */
+	struct SHVector3RGB
+	{
+		SHVector3 R, G, B;
+	};
+
+	/** Information about a single light probe in a light probe volume. */
+	struct LightProbeInfo
+	{
+		LightProbeFlags flags;
+
+		union
+		{
+			/** Index into the GPU buffer where probe coefficients are stored. -1 if not assigned. Transient. */
+			UINT32 bufferIdx; 
+			/** Index to next empty spot in the probe array. -1 if none. Only used if probe is empty or removed. */
+			UINT32 nextEmptyIdx; 
+		};
+	};
+
+	/** Core thread usable version of bs::LightProbeVolume. */
+	class BS_CORE_EXPORT LightProbeVolume : public CoreObject, public LightProbeVolumeBase
+	{
+	public:
+		~LightProbeVolume();
+
+		/**	Sets an ID that can be used for uniquely identifying this object by the renderer. */
+		void setRendererId(UINT32 id) { mRendererId = id; }
+
+		/**	Retrieves an ID that can be used for uniquely identifying this object by the renderer. */
+		UINT32 getRendererId() const { return mRendererId; }
+	protected:
+		friend class bs::LightProbeVolume;
+
+		LightProbeVolume(const UnorderedMap<UINT32, bs::LightProbeVolume::ProbeInfo>& probes);
+
+		/** @copydoc CoreObject::initialize */
+		void initialize() override;
+
+		/** @copydoc CoreObject::syncToCore */
+		void syncToCore(const CoreSyncData& data) override;
+
+		UINT32 mRendererId = 0;
+		UnorderedMap<UINT32, UINT32> mProbeMap; // Map from static indices to compact list of probes
+
+		Vector<Vector3> mProbePositions;
+		Vector<LightProbeInfo> mProbeInfos;
+		UINT32 mNextFreeIdx = -1;
+	};
+	}
+
+	/** @} */
+}

+ 55 - 0
Source/BansheeCore/Include/BsLightProbeVolumeRTTI.h

@@ -0,0 +1,55 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsRTTIType.h"
+#include "BsLightProbeVolume.h"
+
+namespace bs
+{
+	/** @cond RTTI */
+	/** @addtogroup RTTI-Impl-Engine
+	 *  @{
+	 */
+
+	class BS_CORE_EXPORT LightProbeVolumeRTTI : public RTTIType <LightProbeVolume, IReflectable, LightProbeVolumeRTTI>
+	{
+	private:
+		BS_BEGIN_RTTI_MEMBERS
+			BS_RTTI_MEMBER_PLAIN(mPosition, 0)
+			BS_RTTI_MEMBER_PLAIN(mRotation, 1)
+		BS_END_RTTI_MEMBERS
+	public:
+		LightProbeVolumeRTTI()
+			:mInitMembers(this)
+		{ }
+
+		void onDeserializationEnded(IReflectable* obj, const UnorderedMap<String, UINT64>& params) override
+		{
+			// Note: Since this is a CoreObject I should call initialize() right after deserialization,
+			// but since this specific type is used in Components we delay initialization until Component
+			// itself does it. Keep this is mind in case this ever needs to be deserialized for non-Component 
+			// purposes (you'll need to call initialize manually).
+		}
+
+		const String& getRTTIName() override
+		{
+			static String name = "LightProbeVolume";
+			return name;
+		}
+
+		UINT32 getRTTIId() override
+		{
+			return TID_LightProbeVolume;
+		}
+
+		SPtr<IReflectable> newRTTIObject() override
+		{
+			return LightProbeVolume::createEmpty();
+		}
+	};
+
+	/** @} */
+	/** @endcond */
+}

+ 22 - 0
Source/BansheeCore/Include/BsRenderer.h

@@ -9,6 +9,7 @@
 namespace bs 
 { 
 	class RendererExtension;
+	class LightProbeVolume;
 	struct PostProcessSettings;
 
 	namespace ct
@@ -141,6 +142,27 @@ namespace bs
 		 */
 		virtual void notifyReflectionProbeRemoved(ReflectionProbe* probe) { }
 
+		/**
+		 * Called whenever a new light probe volume is created.
+		 *
+		 * @note	Core thread.
+		 */
+		virtual void notifyLightProbeVolumeAdded(LightProbeVolume* volume) { }
+
+		/**
+		 * Called whenever a light probe volume is updated.
+		 *
+		 * @note	Core thread.
+		 */
+		virtual void notifyLightProbeVolumeUpdated(LightProbeVolume* volume) { }
+
+		/**
+		 * Called whenever a light probe volume is destroyed.
+		 *
+		 * @note	Core thread.
+		 */
+		virtual void notifyLightProbeVolumeRemoved(LightProbeVolume* volume) { }
+
 		/**
 		 * Called whenever a skybox is created.
 		 *

+ 279 - 0
Source/BansheeCore/Source/BsLightProbeVolume.cpp

@@ -0,0 +1,279 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsLightProbeVolume.h"
+#include "BsLightProbeVolumeRTTI.h"
+#include "BsFrameAlloc.h"
+#include "BsRenderer.h"
+#include "BsLight.h"
+
+namespace bs
+{
+	LightProbeVolumeBase::LightProbeVolumeBase()
+		: mPosition(BsZero), mRotation(BsIdentity), mIsActive(true)
+	{ }
+
+	LightProbeVolume::LightProbeVolume()
+	{ }
+
+	LightProbeVolume::LightProbeVolume(const AABox& volume, const Vector3& density)
+	{
+		// TODO - Generates probes in the grid volume
+	}
+
+	UINT32 LightProbeVolume::addProbe(const Vector3& position)
+	{
+		UINT32 handle = mNextProbeId++;
+		mProbes[handle] = ProbeInfo(LightProbeFlags::Dirty, position);
+
+		_markCoreDirty();
+		return handle;
+	}
+
+	void LightProbeVolume::removeProbe(UINT32 handle)
+	{
+		auto iterFind = mProbes.find(handle);
+		if (iterFind != mProbes.end())
+		{
+			iterFind->second.flags = LightProbeFlags::Removed;
+			_markCoreDirty();
+		}
+	}
+
+	void LightProbeVolume::setProbePosition(UINT32 handle, const Vector3& position)
+	{
+		auto iterFind = mProbes.find(handle);
+		if (iterFind != mProbes.end())
+		{
+			iterFind->second.flags = LightProbeFlags::Dirty;
+			iterFind->second.position = position;
+			_markCoreDirty();
+		}
+	}
+
+	Vector3 LightProbeVolume::getProbePosition(UINT32 handle) const
+	{
+		auto iterFind = mProbes.find(handle);
+		if (iterFind != mProbes.end())
+			return iterFind->second.position;
+		
+		return Vector3::ZERO;
+	}
+
+	SPtr<ct::LightProbeVolume> LightProbeVolume::getCore() const
+	{
+		return std::static_pointer_cast<ct::LightProbeVolume>(mCoreSpecific);
+	}
+
+	SPtr<LightProbeVolume> LightProbeVolume::create(const AABox& volume, const Vector3& density)
+	{
+		LightProbeVolume* probeVolume = new (bs_alloc<LightProbeVolume>()) LightProbeVolume(volume, density);
+		SPtr<LightProbeVolume> probeVolumePtr = bs_core_ptr<LightProbeVolume>(probeVolume);
+		probeVolumePtr->_setThisPtr(probeVolumePtr);
+		probeVolumePtr->initialize();
+
+		return probeVolumePtr;
+	}
+
+	SPtr<LightProbeVolume> LightProbeVolume::createEmpty()
+	{
+		LightProbeVolume* probeVolume = new (bs_alloc<LightProbeVolume>()) LightProbeVolume();
+		SPtr<LightProbeVolume> probleVolumePtr = bs_core_ptr<LightProbeVolume>(probeVolume);
+		probleVolumePtr->_setThisPtr(probleVolumePtr);
+
+		return probleVolumePtr;
+	}
+
+	SPtr<ct::CoreObject> LightProbeVolume::createCore() const
+	{
+		ct::LightProbeVolume* handler = new (bs_alloc<ct::LightProbeVolume>()) ct::LightProbeVolume(mProbes);
+		SPtr<ct::LightProbeVolume> handlerPtr = bs_shared_ptr<ct::LightProbeVolume>(handler);
+		handlerPtr->_setThisPtr(handlerPtr);
+
+		return handlerPtr;
+	}
+
+	CoreSyncData LightProbeVolume::syncToCore(FrameAlloc* allocator)
+	{
+		UINT32 size = 0;
+		UINT8* buffer = nullptr;
+
+		bs_frame_mark();
+		{
+			FrameVector<std::pair<UINT32, ProbeInfo>> dirtyProbes;
+			FrameVector<UINT32> removedProbes;
+			for (auto& probe : mProbes)
+			{
+				if (probe.second.flags == LightProbeFlags::Dirty)
+				{
+					dirtyProbes.push_back(std::make_pair(probe.first, probe.second));
+					probe.second.flags = LightProbeFlags::Clean;
+				}
+				else if (probe.second.flags == LightProbeFlags::Removed)
+				{
+					removedProbes.push_back(probe.first);
+					probe.second.flags = LightProbeFlags::Empty;
+				}
+			}
+
+			UINT32 numDirtyProbes = (UINT32)dirtyProbes.size();
+			UINT32 numRemovedProbes = (UINT32)removedProbes.size();
+
+			size += rttiGetElemSize(mPosition);
+			size += rttiGetElemSize(mRotation);
+			size += rttiGetElemSize(mIsActive);
+			size += rttiGetElemSize(numDirtyProbes);
+			size += rttiGetElemSize(numRemovedProbes);
+			size += (sizeof(UINT32) + sizeof(Vector3) + sizeof(LightProbeFlags)) * numDirtyProbes;
+			size += sizeof(UINT32) * numRemovedProbes;
+
+			buffer = allocator->alloc(size);
+
+			char* dataPtr = (char*)buffer;
+			dataPtr = rttiWriteElem(mPosition, dataPtr);
+			dataPtr = rttiWriteElem(mRotation, dataPtr);
+			dataPtr = rttiWriteElem(mIsActive, dataPtr);
+			dataPtr = rttiWriteElem(numDirtyProbes, dataPtr);
+			dataPtr = rttiWriteElem(numRemovedProbes, dataPtr);
+
+			for (auto& entry : dirtyProbes)
+			{
+				dataPtr = rttiWriteElem(entry.first, dataPtr);
+				dataPtr = rttiWriteElem(entry.second.position, dataPtr);
+				dataPtr = rttiWriteElem(entry.second.flags, dataPtr);
+			}
+
+			for(auto& entry : removedProbes)
+				dataPtr = rttiWriteElem(entry, dataPtr);
+		}
+		bs_frame_clear();
+
+		return CoreSyncData(buffer, size);
+	}
+
+	void LightProbeVolume::_markCoreDirty()
+	{
+		markCoreDirty();
+	}
+
+	RTTITypeBase* LightProbeVolume::getRTTIStatic()
+	{
+		return LightProbeVolumeRTTI::instance();
+	}
+
+	RTTITypeBase* LightProbeVolume::getRTTI() const
+	{
+		return LightProbeVolume::getRTTIStatic();
+	}
+
+	namespace ct
+	{
+	LightProbeVolume::LightProbeVolume(const UnorderedMap<UINT32, bs::LightProbeVolume::ProbeInfo>& probes)
+	{
+		UINT32 probeIdx = 0;
+		for(auto& entry : probes)
+		{
+			mProbeMap[entry.first] = probeIdx;
+			mProbePositions[probeIdx] = entry.second.position;
+			
+			LightProbeInfo probeInfo;
+			probeInfo.flags = LightProbeFlags::Dirty;
+			probeInfo.bufferIdx = -1;
+
+			mProbeInfos[probeIdx] = probeInfo;
+			probeIdx++;
+		}
+	}
+
+	LightProbeVolume::~LightProbeVolume()
+	{
+		gRenderer()->notifyLightProbeVolumeRemoved(this);
+	}
+
+	void LightProbeVolume::initialize()
+	{
+		gRenderer()->notifyLightProbeVolumeAdded(this);
+
+		CoreObject::initialize();
+	}
+
+	void LightProbeVolume::syncToCore(const CoreSyncData& data)
+	{
+		char* dataPtr = (char*)data.getBuffer();
+
+		bool oldIsActive = mIsActive;
+
+		dataPtr = rttiReadElem(mPosition, dataPtr);
+		dataPtr = rttiReadElem(mRotation, dataPtr);
+		dataPtr = rttiReadElem(mIsActive, dataPtr);
+
+		UINT32 numDirtyProbes, numRemovedProbes;
+		dataPtr = rttiReadElem(numDirtyProbes, dataPtr);
+		dataPtr = rttiReadElem(numRemovedProbes, dataPtr);
+
+		for (UINT32 i = 0; i < numDirtyProbes; ++i)
+		{
+			UINT32 idx;
+			dataPtr = rttiReadElem(idx, dataPtr);
+
+			Vector3 position;
+			dataPtr = rttiReadElem(position, dataPtr);
+
+			LightProbeFlags flags;
+			dataPtr = rttiReadElem(flags, dataPtr);
+
+			auto iterFind = mProbeMap.find(idx);
+			if(iterFind != mProbeMap.end())
+			{
+				UINT32 compactIdx = iterFind->second;
+				
+				mProbeInfos[compactIdx].flags = LightProbeFlags::Dirty;
+				mProbePositions[compactIdx] = position;
+			}
+			else
+			{
+				UINT32 compactIdx = (UINT32)mProbeInfos.size();
+
+				LightProbeInfo info;
+				info.flags = LightProbeFlags::Dirty;
+				info.bufferIdx = -1;
+
+				mProbeInfos.push_back(info);
+				mProbePositions.push_back(position);
+
+				mProbeMap[idx] = compactIdx;
+			}
+		}
+
+		for (UINT32 i = 0; i < numRemovedProbes; ++i)
+		{
+			UINT32 idx;
+			dataPtr = rttiReadElem(idx, dataPtr);
+
+			auto iterFind = mProbeMap.find(idx);
+			if(iterFind != mProbeMap.end())
+			{
+				UINT32 compactIdx = iterFind->second;
+				
+				LightProbeInfo& info = mProbeInfos[compactIdx];
+				info.flags = LightProbeFlags::Removed;
+				info.nextEmptyIdx = mNextFreeIdx;
+				
+				mProbeMap.erase(iterFind);
+				mNextFreeIdx = compactIdx;
+			}
+		}
+
+		if (oldIsActive != mIsActive)
+		{
+			if (mIsActive)
+				gRenderer()->notifyLightProbeVolumeAdded(this);
+			else
+				gRenderer()->notifyLightProbeVolumeRemoved(this);
+		}
+		else
+		{
+			if(mIsActive)
+				gRenderer()->notifyLightProbeVolumeUpdated(this);
+		}
+	}
+}}

+ 1 - 0
Source/BansheeUtility/Include/BsAABox.h

@@ -137,6 +137,7 @@ namespace bs
 		bool operator!= (const AABox& rhs) const;
 
 		static const AABox BOX_EMPTY;
+		static const AABox UNIT_BOX;
 
 		/** 
 		 * Indices that can be used for rendering a box constructed from 8 corner vertices, using AABox::Corner for 

+ 1 - 0
Source/BansheeUtility/Source/BsAABox.cpp

@@ -9,6 +9,7 @@
 namespace bs
 {
 	const AABox AABox::BOX_EMPTY;
+	const AABox AABox::UNIT_BOX = AABox(Vector3(-0.5f, -0.5f, -0.5f), Vector3(0.5f, 0.5f, 0.5f));
 
 	const UINT32 AABox::CUBE_INDICES[36] =
 	{

+ 9 - 0
Source/RenderBeast/Include/BsRenderBeast.h

@@ -111,6 +111,15 @@ namespace bs
 		/** @copydoc Renderer::notifyReflectionProbeRemoved */
 		void notifyReflectionProbeRemoved(ReflectionProbe* probe) override;
 
+		/** @copydoc Renderer::notifyLightProbeVolumeAdded */
+		void notifyLightProbeVolumeAdded(LightProbeVolume* volume) override;
+
+		/** @copydoc Renderer::notifyLightProbeVolumeUpdated */
+		void notifyLightProbeVolumeUpdated(LightProbeVolume* volume) override;
+
+		/** @copydoc Renderer::notifyLightProbeVolumeRemoved */
+		void notifyLightProbeVolumeRemoved(LightProbeVolume* volume) override;
+
 		/** @copydoc Renderer::notifySkyboxAdded */
 		void notifySkyboxAdded(Skybox* skybox) override;
 

+ 17 - 27
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -231,6 +231,21 @@ namespace bs { namespace ct
 		mScene->unregisterReflectionProbe(probe);
 	}
 
+	void RenderBeast::notifyLightProbeVolumeAdded(LightProbeVolume* volume)
+	{
+		assert(false); // TODO
+	}
+
+	void RenderBeast::notifyLightProbeVolumeUpdated(LightProbeVolume* volume)
+	{
+		assert(false); // TODO
+	}
+
+	void RenderBeast::notifyLightProbeVolumeRemoved(LightProbeVolume* volume)
+	{
+		assert(false); // TODO
+	}
+
 	void RenderBeast::notifySkyboxAdded(Skybox* skybox)
 	{
 		mSkybox = skybox;
@@ -633,16 +648,8 @@ namespace bs { namespace ct
 		renderTargets->allocate(RTT_SceneColor);
 		imageBasedLightingMat->execute(renderTargets, perCameraBuffer, mPreintegratedEnvBRDF);
 
-
-
-
-		// DEBUG ONLY
-		//if (useSSAO)
-		//	renderTargets->release(RTT_AmbientOcclusion);
-
-
-
-
+		if (useSSAO)
+			renderTargets->release(RTT_AmbientOcclusion);
 
 		renderTargets->release(RTT_LightAccumulation);
 		renderTargets->release(RTT_GBuffer);
@@ -726,23 +733,6 @@ namespace bs { namespace ct
 		if (isMSAA)
 			renderTargets->release(RTT_ResolvedDepth);
 
-
-
-
-
-		// DEBUG ONLY
-		if(useSSAO)
-		{
-			rapi.setRenderTarget(viewInfo->getProperties().target);
-			gRendererUtility().blit(renderTargets->get(RTT_AmbientOcclusion));
-
-			renderTargets->release(RTT_AmbientOcclusion);
-		}
-
-
-
-
-
 		// Trigger overlay callbacks
 		if (viewProps.triggerCallbacks)
 		{