Przeglądaj źródła

Scene picking WIP (untested)

Marko Pintera 11 lat temu
rodzic
commit
0e027b8351

+ 3 - 3
BansheeCore/Include/BsCommonTypes.h

@@ -97,9 +97,9 @@ namespace BansheeEngine
 	 */
     enum CullingMode
     {
-		CULL_NONE = 1, /**< Hardware performs no culling and renders both sides. */
-		CULL_CLOCKWISE = 2, /**< Hardware culls faces that have a clockwise vertex ordering. */
-        CULL_COUNTERCLOCKWISE = 3 /**< Hardware culls faces that have a counter-clockwise vertex ordering. */
+		CULL_NONE = 0, /**< Hardware performs no culling and renders both sides. */
+		CULL_CLOCKWISE = 1, /**< Hardware culls faces that have a clockwise vertex ordering. */
+        CULL_COUNTERCLOCKWISE = 2 /**< Hardware culls faces that have a counter-clockwise vertex ordering. */
     };
 
 	/**

+ 1 - 1
BansheeCore/Include/BsCoreSceneManager.h

@@ -60,5 +60,5 @@ namespace BansheeEngine
 	/**
 	 * @brief	Provides easy access to the scene manager.
 	 */
-	BS_CORE_EXPORT CoreSceneManager& gSceneManager();
+	BS_CORE_EXPORT CoreSceneManager& gCoreSceneManager();
 }

+ 13 - 0
BansheeCore/Include/BsGpuParam.h

@@ -5,8 +5,12 @@
 #include "BsGpuParamBlock.h"
 #include "BsDebug.h"
 #include "BsException.h"
+#include "BsVector2.h"
+#include "BsVector3.h"
+#include "BsVector4.h"
 #include "BsMatrix3.h"
 #include "BsMatrix4.h"
+#include "BsColor.h"
 
 namespace BansheeEngine
 {
@@ -197,12 +201,21 @@ namespace BansheeEngine
 	};
 
 	typedef TGpuDataParam<float> GpuParamFloat;
+	typedef TGpuDataParam<Color> GpuParamColor;
 	typedef TGpuDataParam<Vector2> GpuParamVec2;
 	typedef TGpuDataParam<Vector3> GpuParamVec3;
 	typedef TGpuDataParam<Vector4> GpuParamVec4;
 	typedef TGpuDataParam<Matrix3> GpuParamMat3;
 	typedef TGpuDataParam<Matrix4> GpuParamMat4;
 
+	template class TGpuDataParam<float>;
+	template class TGpuDataParam<Color>;
+	template class TGpuDataParam<Vector2>;
+	template class TGpuDataParam<Vector3>;
+	template class TGpuDataParam<Vector4>;
+	template class TGpuDataParam<Matrix3>;
+	template class TGpuDataParam<Matrix4>;
+
 	/**
 	 * @copydoc GpuDataParamBase
 	 */

+ 20 - 0
BansheeCore/Include/BsMaterial.h

@@ -209,6 +209,13 @@ namespace BansheeEngine
 		 */
 		float getFloat(const String& name, UINT32 arrayIdx = 0) const { return getParamFloat(name).get(arrayIdx); }
 
+		/**
+		 * @brief	Returns a color assigned with the parameter with the specified name.
+		 *
+		 *			Optionally if the parameter is an array you may provide an array index you which to retrieve.
+		 */
+		Color getColor(const String& name, UINT32 arrayIdx = 0) const { return getParamColor(name).get(arrayIdx); }
+
 		/**
 		 * @brief	Returns a 2D vector assigned with the parameter with the specified name.
 		 *
@@ -264,6 +271,19 @@ namespace BansheeEngine
 		 */
 		GpuParamFloat getParamFloat(const String& name) const;
 
+		/**
+		 * @brief	Returns a color GPU parameter. This parameter may be used for
+		 * 			more efficiently getting/setting GPU parameter values than calling
+		 * 			Material::get* / Material::set* methods. 
+		 *
+		 * @note	Expected behavior is that you would retrieve this parameter when
+		 * 			initially constructing the material, and then use it throughout material
+		 * 			lifetime to assign and retrieve parameter values.
+		 * 			
+		 *			If material shader changes this handle will be invalidated.
+		 */
+		GpuParamColor getParamColor(const String& name) const;
+
 		/**
 		 * @brief	Returns a 2D vector GPU parameter. This parameter may be used for
 		 * 			more efficiently getting/setting GPU parameter values than calling

+ 1 - 1
BansheeCore/Include/BsPass.h

@@ -32,7 +32,7 @@ namespace BansheeEngine
 		/**
 		 * @brief	Returns true if this pass has some element of transparency.
 		 */
-		bool isTransparent() const;
+		bool hasBlending() const;
 
 		void setBlendState(HBlendState& blendState);
 		HBlendState getBlendState() const;

+ 17 - 0
BansheeCore/Include/BsRenderer.h

@@ -66,6 +66,23 @@ namespace BansheeEngine
 		 */
 		virtual void renderAll() = 0;
 
+		/**
+		 * @brief	Activates the specified pass on the pipeline.
+		 *
+		 * @param	material	Parent material of the pass.
+		 * @param	passIdx		Index of the pass in the parent material.
+		 *
+		 * @note	Core thread only.
+		 */
+		static void setPass(const MaterialProxy& material, UINT32 passIdx);
+
+		/**
+		 * @brief	Draws the specified mesh proxy with last set pass.
+		 *
+		 * @note	Core thread only.
+		 */
+		static void draw(const MeshProxy& mesh);
+
 		/**
 		 * @brief	Callback that gets triggered before a viewport gets rendered.
 		 */

+ 43 - 4
BansheeCore/Include/BsSceneObject.h

@@ -17,6 +17,15 @@ namespace BansheeEngine
 	 */
 	class BS_CORE_EXPORT SceneObject : public GameObject
 	{
+		/**
+		 * @brief	Flags that signify which part of the SceneObject needs updating.
+		 */
+		enum DirtyFlags
+		{
+			LocalTfrmDirty = 0x01,
+			WorldTfrmDirty = 0x02
+		};
+
 		friend class CoreSceneManager;
 	public:
 		~SceneObject();
@@ -235,11 +244,9 @@ namespace BansheeEngine
 		mutable Vector3 mWorldScale;
 
 		mutable Matrix4 mCachedLocalTfrm;
-		mutable bool mIsCachedLocalTfrmUpToDate;
-
 		mutable Matrix4 mCachedWorldTfrm;
-		mutable bool mIsCachedWorldTfrmUpToDate;
 
+		mutable UINT32 mDirtyFlags;
 		mutable UINT32 mIsCoreDirtyFlags;
 
 		/**
@@ -262,6 +269,16 @@ namespace BansheeEngine
 		 */
 		void updateWorldTfrm() const;
 
+		/**
+		 * @brief	Checks if cached local transform needs updating.
+		 */
+		bool isCachedLocalTfrmUpToDate() const { return (mDirtyFlags & DirtyFlags::LocalTfrmDirty) != 0; }
+
+		/**
+		 * @brief	Checks if cached world transform needs updating.
+		 */
+		bool isCachedWorldTfrmUpToDate() const { return (mDirtyFlags & DirtyFlags::WorldTfrmDirty) != 0; }
+
 		/************************************************************************/
 		/* 								Hierarchy	                     		*/
 		/************************************************************************/
@@ -305,6 +322,21 @@ namespace BansheeEngine
 		 */
 		UINT32 getNumChildren() const { return (UINT32)mChildren.size(); }
 
+		/**
+		 * @brief	Enables or disables this object. Disabled objects also implicitly disable
+		 *			all their child objects. No components on the disabled object are updated.
+		 */
+		void setActive(bool active);
+
+		/**
+		 * @brief	Returns whether or not an object is active.
+		 *
+		 * @param	self	If true, the method will only check if this particular object was activated
+		 *					or deactivated directly via setActive. If false we we also check if any of
+		 *					the objects parents are inactive.
+		 */
+		bool getActive(bool self = false);
+
 		/**
 		 * @brief	Makes a deep copy of this object.
 		 */
@@ -313,6 +345,8 @@ namespace BansheeEngine
 	private:
 		HSceneObject mParent;
 		Vector<HSceneObject> mChildren;
+		bool mActiveSelf;
+		bool mActiveHierarchy;
 
 		/**
 		 * @brief	Adds a child to the child array. This method doesn't check for null or duplicate values.
@@ -330,6 +364,11 @@ namespace BansheeEngine
 		 */
 		void removeChild(const HSceneObject& object);
 
+		/**
+		 * @brief	Changes the object active in hierarchy state.
+		 */
+		void setActiveHierarchy(bool active);
+
 		/************************************************************************/
 		/* 								Component	                     		*/
 		/************************************************************************/
@@ -353,7 +392,7 @@ namespace BansheeEngine
 
 			mComponents.push_back(newComponent);
 
-			gSceneManager().notifyComponentAdded(newComponent);	
+			gCoreSceneManager().notifyComponentAdded(newComponent);	
 
 			return newComponent;
 		}

+ 1 - 1
BansheeCore/Source/BsCoreApplication.cpp

@@ -173,7 +173,7 @@ namespace BansheeEngine
 			gInput()._update();
 			gTime().update();
 
-			PROFILE_CALL(gSceneManager()._update(), "SceneManager");
+			PROFILE_CALL(gCoreSceneManager()._update(), "SceneManager");
 
 			gCoreThread().queueCommand(std::bind(&CoreApplication::beginCoreProfiling, this));
 			gCoreThread().queueCommand(std::bind(&QueryManager::_update, QueryManager::instancePtr()));

+ 1 - 1
BansheeCore/Source/BsCoreSceneManager.cpp

@@ -46,7 +46,7 @@ namespace BansheeEngine
 	void CoreSceneManager::notifyComponentAdded(const HComponent& component) { }
 	void CoreSceneManager::notifyComponentRemoved(const HComponent& component) { }
 
-	CoreSceneManager& gSceneManager()
+	CoreSceneManager& gCoreSceneManager()
 	{
 		return CoreSceneManager::instance();
 	}

+ 8 - 0
BansheeCore/Source/BsMaterial.cpp

@@ -541,6 +541,14 @@ namespace BansheeEngine
 		return gpuParam;
 	}
 
+	GpuParamColor Material::getParamColor(const String& name) const
+	{
+		TGpuDataParam<Color> gpuParam;
+		getParam(name, gpuParam);
+
+		return gpuParam;
+	}
+
 	GpuParamVec2 Material::getParamVec2(const String& name) const
 	{
 		TGpuDataParam<Vector2> gpuParam;

+ 1 - 1
BansheeCore/Source/BsPass.cpp

@@ -41,7 +41,7 @@ namespace BansheeEngine
 		return *this;
     }
 
-    bool Pass::isTransparent() const
+    bool Pass::hasBlending() const
     {
 		bool transparent = false;
 

+ 135 - 0
BansheeCore/Source/BsRenderer.cpp

@@ -1,6 +1,141 @@
 #include "BsRenderer.h"
+#include "BsCoreThread.h"
+#include "BsRenderSystem.h"
+#include "BsMaterialProxy.h"
+#include "BsMeshProxy.h"
+#include "BsMesh.h"
+#include "BsBlendState.h"
+#include "BsDepthStencilState.h"
+#include "BsRasterizerState.h"
 
 namespace BansheeEngine
 {
+	void Renderer::setPass(const MaterialProxy& material, UINT32 passIdx)
+	{
+		THROW_IF_NOT_CORE_THREAD;
 
+		RenderSystem& rs = RenderSystem::instance();
+
+		const MaterialProxyPass& pass = material.passes[passIdx];
+		if (pass.vertexProg)
+		{
+			rs.bindGpuProgram(pass.vertexProg);
+			rs.bindGpuParams(GPT_VERTEX_PROGRAM, material.params[pass.vertexProgParamsIdx]);
+		}
+		else
+			rs.unbindGpuProgram(GPT_VERTEX_PROGRAM);
+
+		if (pass.fragmentProg)
+		{
+			rs.bindGpuProgram(pass.fragmentProg);
+			rs.bindGpuParams(GPT_FRAGMENT_PROGRAM, material.params[pass.fragmentProgParamsIdx]);
+		}
+		else
+			rs.unbindGpuProgram(GPT_FRAGMENT_PROGRAM);
+
+		if (pass.geometryProg)
+		{
+			rs.bindGpuProgram(pass.geometryProg);
+			rs.bindGpuParams(GPT_GEOMETRY_PROGRAM, material.params[pass.geometryProgParamsIdx]);
+		}
+		else
+			rs.unbindGpuProgram(GPT_GEOMETRY_PROGRAM);
+
+		if (pass.hullProg)
+		{
+			rs.bindGpuProgram(pass.hullProg);
+			rs.bindGpuParams(GPT_HULL_PROGRAM, material.params[pass.hullProgParamsIdx]);
+		}
+		else
+			rs.unbindGpuProgram(GPT_HULL_PROGRAM);
+
+		if (pass.domainProg)
+		{
+			rs.bindGpuProgram(pass.domainProg);
+			rs.bindGpuParams(GPT_DOMAIN_PROGRAM, material.params[pass.domainProgParamsIdx]);
+		}
+		else
+			rs.unbindGpuProgram(GPT_DOMAIN_PROGRAM);
+
+		if (pass.computeProg)
+		{
+			rs.bindGpuProgram(pass.computeProg);
+			rs.bindGpuParams(GPT_COMPUTE_PROGRAM, material.params[pass.computeProgParamsIdx]);
+		}
+		else
+			rs.unbindGpuProgram(GPT_COMPUTE_PROGRAM);
+
+		// TODO - Try to limit amount of state changes, if previous state is already the same
+
+		// Set up non-texture related pass settings
+		if (pass.blendState != nullptr)
+			rs.setBlendState(pass.blendState.getInternalPtr());
+		else
+			rs.setBlendState(BlendState::getDefault());
+
+		if (pass.depthStencilState != nullptr)
+			rs.setDepthStencilState(pass.depthStencilState.getInternalPtr(), pass.stencilRefValue);
+		else
+			rs.setDepthStencilState(DepthStencilState::getDefault(), pass.stencilRefValue);
+
+		if (pass.rasterizerState != nullptr)
+			rs.setRasterizerState(pass.rasterizerState.getInternalPtr());
+		else
+			rs.setRasterizerState(RasterizerState::getDefault());
+	}
+
+	void Renderer::draw(const MeshProxy& meshProxy)
+	{
+		THROW_IF_NOT_CORE_THREAD;
+
+		RenderSystem& rs = RenderSystem::instance();
+		MeshBasePtr mesh;
+
+		if (!meshProxy.mesh.expired())
+			mesh = meshProxy.mesh.lock();
+		else
+			return;
+
+		std::shared_ptr<VertexData> vertexData = mesh->_getVertexData();
+
+		rs.setVertexDeclaration(vertexData->vertexDeclaration);
+		auto vertexBuffers = vertexData->getBuffers();
+
+		if (vertexBuffers.size() > 0)
+		{
+			VertexBufferPtr buffers[MAX_BOUND_VERTEX_BUFFERS];
+
+			UINT32 endSlot = 0;
+			UINT32 startSlot = MAX_BOUND_VERTEX_BUFFERS;
+			for (auto iter = vertexBuffers.begin(); iter != vertexBuffers.end(); ++iter)
+			{
+				if (iter->first >= MAX_BOUND_VERTEX_BUFFERS)
+					BS_EXCEPT(InvalidParametersException, "Buffer index out of range");
+
+				startSlot = std::min(iter->first, startSlot);
+				endSlot = std::max(iter->first, endSlot);
+			}
+
+			for (auto iter = vertexBuffers.begin(); iter != vertexBuffers.end(); ++iter)
+			{
+				buffers[iter->first - startSlot] = iter->second;
+			}
+
+			rs.setVertexBuffers(startSlot, buffers, endSlot - startSlot + 1);
+		}
+
+		SubMesh subMesh = meshProxy.subMesh;
+		rs.setDrawOperation(subMesh.drawOp);
+
+		IndexBufferPtr indexBuffer = mesh->_getIndexBuffer();
+
+		UINT32 indexCount = subMesh.indexCount;
+		if (indexCount == 0)
+			indexCount = indexBuffer->getNumIndices();
+
+		rs.setIndexBuffer(indexBuffer);
+		rs.drawIndexed(subMesh.indexOffset + mesh->_getIndexOffset(), indexCount, mesh->_getVertexOffset(), vertexData->vertexCount);
+
+		mesh->_notifyUsedOnGPU();
+	}
 }

+ 42 - 17
BansheeCore/Source/BsSceneObject.cpp

@@ -12,8 +12,8 @@ namespace BansheeEngine
 	SceneObject::SceneObject(const String& name)
 		:GameObject(), mPosition(Vector3::ZERO), mRotation(Quaternion::IDENTITY), mScale(Vector3::ONE),
 		mWorldPosition(Vector3::ZERO), mWorldRotation(Quaternion::IDENTITY), mWorldScale(Vector3::ONE),
-		mCachedLocalTfrm(Matrix4::IDENTITY), mIsCachedLocalTfrmUpToDate(false),
-		mCachedWorldTfrm(Matrix4::IDENTITY), mIsCachedWorldTfrmUpToDate(false), mIsCoreDirtyFlags(0xFFFFFFFF)
+		mCachedLocalTfrm(Matrix4::IDENTITY), mDirtyFlags(0xFFFFFFFF), mCachedWorldTfrm(Matrix4::IDENTITY), 
+		mIsCoreDirtyFlags(0xFFFFFFFF), mActiveSelf(true), mActiveHierarchy(true)
 	{
 		setName(name);
 	}
@@ -31,7 +31,7 @@ namespace BansheeEngine
 	{
 		HSceneObject newObject = createInternal(name);
 
-		gSceneManager().registerNewSO(newObject);
+		gCoreSceneManager().registerNewSO(newObject);
 
 		return newObject;
 	}
@@ -69,7 +69,7 @@ namespace BansheeEngine
 
 		for(auto iter = mComponents.begin(); iter != mComponents.end(); ++iter)
 		{
-			gSceneManager().notifyComponentRemoved((*iter));
+			gCoreSceneManager().notifyComponentRemoved((*iter));
 			GameObjectManager::instance().unregisterObject(*iter);
 			(*iter).destroy();
 		}
@@ -145,7 +145,7 @@ namespace BansheeEngine
 
 	const Vector3& SceneObject::getWorldPosition() const
 	{ 
-		if(!mIsCachedWorldTfrmUpToDate)
+		if (!isCachedWorldTfrmUpToDate())
 			updateWorldTfrm();
 
 		return mWorldPosition; 
@@ -153,7 +153,7 @@ namespace BansheeEngine
 
 	const Quaternion& SceneObject::getWorldRotation() const 
 	{ 
-		if(!mIsCachedWorldTfrmUpToDate)
+		if (!isCachedWorldTfrmUpToDate())
 			updateWorldTfrm();
 
 		return mWorldRotation; 
@@ -161,7 +161,7 @@ namespace BansheeEngine
 
 	const Vector3& SceneObject::getWorldScale() const 
 	{ 
-		if(!mIsCachedWorldTfrmUpToDate)
+		if (!isCachedWorldTfrmUpToDate())
 			updateWorldTfrm();
 
 		return mWorldScale; 
@@ -180,7 +180,7 @@ namespace BansheeEngine
 
 	const Matrix4& SceneObject::getWorldTfrm() const
 	{
-		if(!mIsCachedWorldTfrmUpToDate)
+		if (!isCachedWorldTfrmUpToDate())
 			updateWorldTfrm();
 
 		return mCachedWorldTfrm;
@@ -188,7 +188,7 @@ namespace BansheeEngine
 
 	const Matrix4& SceneObject::getLocalTfrm() const
 	{
-		if(!mIsCachedLocalTfrmUpToDate)
+		if (!isCachedLocalTfrmUpToDate())
 			updateLocalTfrm();
 
 		return mCachedLocalTfrm;
@@ -272,17 +272,16 @@ namespace BansheeEngine
 
 	void SceneObject::updateTransformsIfDirty()
 	{
-		if (!mIsCachedLocalTfrmUpToDate)
+		if (!isCachedLocalTfrmUpToDate())
 			updateLocalTfrm();
 
-		if (!mIsCachedWorldTfrmUpToDate)
+		if (!isCachedWorldTfrmUpToDate())
 			updateWorldTfrm();
 	}
 
 	void SceneObject::markTfrmDirty() const
 	{
-		mIsCachedLocalTfrmUpToDate = false;
-		mIsCachedWorldTfrmUpToDate = false;
+		mDirtyFlags |= DirtyFlags::LocalTfrmDirty | DirtyFlags::WorldTfrmDirty;
 		mIsCoreDirtyFlags = 0xFFFFFFFF;
 
 		for(auto iter = mChildren.begin(); iter != mChildren.end(); ++iter)
@@ -322,14 +321,14 @@ namespace BansheeEngine
 			mWorldScale = mScale;
 		}
 
-		mIsCachedWorldTfrmUpToDate = true;
+		mDirtyFlags &= ~DirtyFlags::WorldTfrmDirty;
 	}
 
 	void SceneObject::updateLocalTfrm() const
 	{
 		mCachedLocalTfrm.setTRS(mPosition, mRotation, mScale);
 
-		mIsCachedLocalTfrmUpToDate = true;
+		mDirtyFlags &= ~DirtyFlags::LocalTfrmDirty;
 	}
 
 	/************************************************************************/
@@ -396,6 +395,32 @@ namespace BansheeEngine
 		}
 	}
 
+	void SceneObject::setActive(bool active)
+	{
+		mActiveSelf = active;
+		setActiveHierarchy(active);
+	}
+
+	void SceneObject::setActiveHierarchy(bool active) 
+	{ 
+		mActiveHierarchy = active && mActiveSelf; 
+
+		for (auto child : mChildren)
+		{
+			child->setActiveHierarchy(mActiveHierarchy);
+		}
+
+		mIsCoreDirtyFlags = 0xFFFFFFFF;
+	}
+
+	bool SceneObject::getActive(bool self)
+	{
+		if (self)
+			return mActiveSelf;
+		else
+			return mActiveHierarchy;
+	}
+
 	HSceneObject SceneObject::clone()
 	{
 		UINT32 bufferSize = 0;
@@ -433,7 +458,7 @@ namespace BansheeEngine
 
 		if(iter != mComponents.end())
 		{
-			gSceneManager().notifyComponentRemoved((*iter));
+			gCoreSceneManager().notifyComponentRemoved((*iter));
 			GameObjectManager::instance().unregisterObject(component);
 
 			(*iter)->onDestroyed();
@@ -467,7 +492,7 @@ namespace BansheeEngine
 		newComponent->mParent = mThisHandle;
 		mComponents.push_back(newComponent);
 
-		gSceneManager().notifyComponentAdded(newComponent);
+		gCoreSceneManager().notifyComponentAdded(newComponent);
 	}
 
 	RTTITypeBase* SceneObject::getRTTIStatic()

+ 2 - 0
BansheeEditor/BansheeEditor.vcxproj

@@ -290,6 +290,7 @@
     <ClInclude Include="Include\BsGUIToggleField.h" />
     <ClInclude Include="Include\BsGUIVector3Field.h" />
     <ClInclude Include="Include\BsGUIVector4Field.h" />
+    <ClInclude Include="Include\BsScenePicking.h" />
     <ClInclude Include="Include\BsProjectLibraryEntriesRTTI.h" />
     <ClInclude Include="Include\BsEditorPrerequisites.h" />
     <ClInclude Include="Include\BsEditorWidget.h" />
@@ -364,6 +365,7 @@
     <ClCompile Include="Source\BsGUIWindowFrameWidget.cpp" />
     <ClCompile Include="Source\BsGUIWindowDropArea.cpp" />
     <ClCompile Include="Source\BsMainEditorWindow.cpp" />
+    <ClCompile Include="Source\BsScenePicking.cpp" />
     <ClCompile Include="Source\BsProjectLibrary.cpp" />
     <ClCompile Include="Source\BsProjectLibraryEntries.cpp" />
     <ClCompile Include="Source\BsProjectResourceMeta.cpp" />

+ 6 - 0
BansheeEditor/BansheeEditor.vcxproj.filters

@@ -210,6 +210,9 @@
     <ClInclude Include="Include\BsBuiltinEditorResources.h">
       <Filter>Header Files\Editor</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsScenePicking.h">
+      <Filter>Header Files\Editor</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsEditorWidgetContainer.cpp">
@@ -368,5 +371,8 @@
     <ClCompile Include="Source\BsBuiltinEditorResources.cpp">
       <Filter>Source Files\Editor</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsScenePicking.cpp">
+      <Filter>Source Files\Editor</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 28 - 2
BansheeEditor/Include/BsBuiltinEditorResources.h

@@ -25,6 +25,16 @@ namespace BansheeEngine
 		 */
 		HMaterial createSceneGridMaterial() const;
 
+		/**
+		 * @brief	Creates a material used for picking non-transparent objects in scene view.
+		 */
+		HMaterial createPicking(CullingMode cullMode) const;
+
+		/**
+		 * @brief	Creates a material used for picking transparent objects in scene view.
+		 */
+		HMaterial createPickingAlpha(CullingMode cullMode) const;
+
 		static const String ObjectFieldStyleName;
 		static const String ObjectFieldLabelStyleName;
 		static const String ObjectFieldDropBtnStyleName;
@@ -57,21 +67,33 @@ namespace BansheeEngine
 		static void importGUITexture(const WString& name);
 
 		/**
-		 * @brief	Loads an compiles a shader for dock overlay rendering.
+		 * @brief	Loads and compiles a shader for dock overlay rendering.
 		 */
 		void initDockDropOverlayShader();
 
 		/**
-		 * @brief	Loads an compiles a shader for scene grid rendering.
+		 * @brief	Loads and compiles a shader for scene grid rendering.
 		 */
 		void initSceneGridShader();
 
+		/**
+		 * @brief	Loads and compiles a shader used for scene picking (non alpha).
+		 */
+		void initPickingShader(CullingMode cullMode);
+
+		/**
+		 * @brief	Loads and compiles a shader used for scene picking (alpha).
+		 */
+		void initPickingAlphaShader(CullingMode cullMode);
+
 		RenderSystemPlugin mRenderSystemPlugin;
 		WString mActiveShaderSubFolder;
 		String mActiveRenderSystem;
 
 		ShaderPtr mShaderDockOverlay;
 		ShaderPtr mShaderSceneGrid;
+		ShaderPtr mShaderPicking[3];
+		ShaderPtr mShaderPickingAlpha[3];
 
 		GUISkin mSkin;
 
@@ -211,5 +233,9 @@ namespace BansheeEngine
 		static const WString ShaderDockOverlayPSFile;
 		static const WString SceneGridVSFile;
 		static const WString SceneGridPSFile;
+		static const WString PickingVSFile;
+		static const WString PickingPSFile;
+		static const WString PickingAlphaVSFile;
+		static const WString PickingAlphaPSFile;
 	};
 }

+ 76 - 0
BansheeEditor/Include/BsScenePicking.h

@@ -0,0 +1,76 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsModule.h"
+#include "BsMatrix4.h"
+#include "BsGpuParam.h"
+
+namespace BansheeEngine
+{
+	class PickResult
+	{
+	public:
+		enum class Type
+		{
+			SceneObject,
+			Gizmo,
+			None
+		};
+
+		HSceneObject sceneObject;
+		UINT32 gizmoId;
+		Type type;
+	};
+
+	class ScenePicking : public Module<ScenePicking>
+	{
+		struct RenderablePickData
+		{
+			MeshProxyPtr mesh;
+			UINT32 index;
+			Matrix4 wvpTransform;
+			bool alpha;
+			CullingMode cullMode;
+		};
+
+		struct MaterialData
+		{
+			// Sim thread
+			HMaterial mMatPicking;
+			HMaterial mMatPickingAlpha;
+
+			// Core thread
+			MaterialProxyPtr mMatPickingProxy;
+			MaterialProxyPtr mMatPickingAlphaProxy;
+
+			GpuParamMat4 mParamPickingWVP;
+			GpuParamMat4 mParamPickingAlphaWVP;
+			GpuParamColor mParamPickingColor;
+			GpuParamColor mParamPickingAlphaColor;
+			GpuParamTexture mParamPickingAlphaTexture;
+		};
+
+	public:
+		ScenePicking();
+
+		HSceneObject pickClosestSceneObject(const HCamera& cam, const Vector2I& position, const Vector2I& area);
+		PickResult pickClosestObject(const HCamera& cam, const Vector2I& position, const Vector2I& area);
+		Vector<HSceneObject> pickSceneObjects(const HCamera& cam, const Vector2I& position, const Vector2I& area);
+		Vector<PickResult> pickObjects(const HCamera& cam, const Vector2I& position, const Vector2I& area);
+
+	private:
+		typedef Set<RenderablePickData, std::function<bool(const RenderablePickData&, const RenderablePickData&)>> RenderableSet;
+
+		void initializeCore();
+
+		Color encodeIndex(UINT32 index);
+		UINT32 decodeIndex(Color color);
+
+		void corePickObjects(const Viewport& viewport, const RenderableSet& renderables, const Vector2I& position,
+			const Vector2I& area, AsyncOp& asyncOp);
+
+		static const float ALPHA_CUTOFF;
+
+		MaterialData mMaterialData[3];
+	};
+}

+ 91 - 1
BansheeEditor/Source/BsBuiltinEditorResources.cpp

@@ -193,6 +193,10 @@ namespace BansheeEngine
 	const WString BuiltinEditorResources::ShaderDockOverlayPSFile = L"dockDropOverlayPS.gpuprog";
 	const WString BuiltinEditorResources::SceneGridVSFile = L"sceneGridVS.gpuprog";
 	const WString BuiltinEditorResources::SceneGridPSFile = L"sceneGridPS.gpuprog";
+	const WString BuiltinEditorResources::PickingVSFile = L"pickingVS.gpuprog";
+	const WString BuiltinEditorResources::PickingPSFile = L"pickingPS.gpuprog";
+	const WString BuiltinEditorResources::PickingAlphaVSFile = L"pickingAlphaVS.gpuprog";
+	const WString BuiltinEditorResources::PickingAlphaPSFile = L"pickingAlphaPS.gpuprog";
 
 	BuiltinEditorResources::BuiltinEditorResources(RenderSystemPlugin activeRSPlugin)
 		:mRenderSystemPlugin(activeRSPlugin)
@@ -217,6 +221,12 @@ namespace BansheeEngine
 
 		initDockDropOverlayShader();
 		initSceneGridShader();
+		initPickingShader(CULL_NONE);
+		initPickingShader(CULL_CLOCKWISE);
+		initPickingShader(CULL_COUNTERCLOCKWISE);
+		initPickingAlphaShader(CULL_NONE);
+		initPickingAlphaShader(CULL_CLOCKWISE);
+		initPickingAlphaShader(CULL_COUNTERCLOCKWISE);
 
 		Path fontPath = FileSystem::getWorkingDirectoryPath();
 		fontPath.append(DefaultSkinFolder);
@@ -1042,14 +1052,26 @@ namespace BansheeEngine
 			{ SceneGridPSFile,			"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", HLSL11ShaderSubFolder },
 			{ ShaderDockOverlayVSFile,	"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", HLSL11ShaderSubFolder },
 			{ ShaderDockOverlayPSFile,	"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", HLSL11ShaderSubFolder },
+			{ PickingVSFile,			"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", HLSL11ShaderSubFolder },
+			{ PickingPSFile,			"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", HLSL11ShaderSubFolder },
+			{ PickingAlphaVSFile,		"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"hlsl", HLSL11ShaderSubFolder },
+			{ PickingAlphaPSFile,		"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"hlsl", HLSL11ShaderSubFolder },
 			{ SceneGridVSFile,			"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_2_0,		"hlsl", HLSL9ShaderSubFolder },
 			{ SceneGridPSFile,			"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_2_0,		"hlsl", HLSL9ShaderSubFolder },
 			{ ShaderDockOverlayVSFile,	"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_2_0,		"hlsl", HLSL9ShaderSubFolder },
 			{ ShaderDockOverlayPSFile,	"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_2_0,		"hlsl", HLSL9ShaderSubFolder },
+			{ PickingVSFile,			"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_2_0,		"hlsl", HLSL9ShaderSubFolder },
+			{ PickingPSFile,			"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_2_0,		"hlsl", HLSL9ShaderSubFolder },
+			{ PickingAlphaVSFile,		"vs_main",	GPT_VERTEX_PROGRAM,		GPP_VS_2_0,		"hlsl", HLSL9ShaderSubFolder },
+			{ PickingAlphaPSFile,		"ps_main",	GPT_FRAGMENT_PROGRAM,	GPP_PS_2_0,		"hlsl", HLSL9ShaderSubFolder },
 			{ SceneGridVSFile,			"main",		GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"glsl", GLSLShaderSubFolder },
 			{ SceneGridPSFile,			"main",		GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"glsl", GLSLShaderSubFolder },
 			{ ShaderDockOverlayVSFile,	"main",		GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"glsl", GLSLShaderSubFolder },
-			{ ShaderDockOverlayPSFile,	"main",		GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"glsl", GLSLShaderSubFolder }
+			{ ShaderDockOverlayPSFile,	"main",		GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"glsl", GLSLShaderSubFolder },
+			{ PickingVSFile,			"main",		GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"glsl", GLSLShaderSubFolder },
+			{ PickingPSFile,			"main",		GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"glsl", GLSLShaderSubFolder },
+			{ PickingAlphaVSFile,		"main",		GPT_VERTEX_PROGRAM,		GPP_VS_4_0,		"glsl", GLSLShaderSubFolder },
+			{ PickingAlphaPSFile,		"main",		GPT_FRAGMENT_PROGRAM,	GPP_PS_4_0,		"glsl", GLSLShaderSubFolder }
 		};
 
 		if (FileSystem::exists(DefaultSkinFolderRaw))
@@ -1241,6 +1263,60 @@ namespace BansheeEngine
 		newPass->setRasterizerState(rasterizerState);
 	}
 
+	void BuiltinEditorResources::initPickingShader(CullingMode cullMode)
+	{
+		UINT32 modeIdx = (UINT32)cullMode;
+
+		HGpuProgram vsProgram = getGpuProgram(PickingVSFile);
+		HGpuProgram psProgram = getGpuProgram(PickingPSFile);
+
+		mShaderPicking[modeIdx] = Shader::create("PickingShader");
+		mShaderPicking[modeIdx]->addParameter("colorIndex", "colorIndex", GPDT_FLOAT4);
+		mShaderPicking[modeIdx]->addParameter("matWorldViewProj", "matWorldViewProj", GPDT_MATRIX_4X4);
+
+		TechniquePtr newTechnique = mShaderPicking[modeIdx]->addTechnique(mActiveRenderSystem, RendererInvariant);
+		PassPtr newPass = newTechnique->addPass();
+		newPass->setVertexProgram(vsProgram);
+		newPass->setFragmentProgram(psProgram);
+
+		RASTERIZER_STATE_DESC rasterizerDesc;
+		rasterizerDesc.scissorEnable = true;
+		rasterizerDesc.cullMode = cullMode;
+
+		HRasterizerState rasterizerState = RasterizerState::create(rasterizerDesc);
+		newPass->setRasterizerState(rasterizerState);
+	}
+
+	void BuiltinEditorResources::initPickingAlphaShader(CullingMode cullMode)
+	{
+		UINT32 modeIdx = (UINT32)cullMode;
+
+		HGpuProgram vsProgram = getGpuProgram(PickingAlphaVSFile);
+		HGpuProgram psProgram = getGpuProgram(PickingAlphaPSFile);
+
+		mShaderPickingAlpha[modeIdx] = Shader::create("PickingAlphaShader");
+
+		mShaderPickingAlpha[modeIdx]->addParameter("mainTexSamp", "mainTexSamp", GPOT_SAMPLER2D);
+		mShaderPickingAlpha[modeIdx]->addParameter("mainTexSamp", "mainTexture", GPOT_SAMPLER2D);
+
+		mShaderPickingAlpha[modeIdx]->addParameter("mainTexture", "mainTexture", GPOT_TEXTURE2D);
+
+		mShaderPickingAlpha[modeIdx]->addParameter("colorIndex", "colorIndex", GPDT_FLOAT4);
+		mShaderPickingAlpha[modeIdx]->addParameter("matWorldViewProj", "matWorldViewProj", GPDT_MATRIX_4X4);
+
+		TechniquePtr newTechnique = mShaderPickingAlpha[modeIdx]->addTechnique(mActiveRenderSystem, RendererInvariant);
+		PassPtr newPass = newTechnique->addPass();
+		newPass->setVertexProgram(vsProgram);
+		newPass->setFragmentProgram(psProgram);
+
+		RASTERIZER_STATE_DESC rasterizerDesc;
+		rasterizerDesc.scissorEnable = true;
+		rasterizerDesc.cullMode = cullMode;
+
+		HRasterizerState rasterizerState = RasterizerState::create(rasterizerDesc);
+		newPass->setRasterizerState(rasterizerState);
+	}
+
 	HMaterial BuiltinEditorResources::createDockDropOverlayMaterial() const
 	{
 		return Material::create(mShaderDockOverlay);
@@ -1250,4 +1326,18 @@ namespace BansheeEngine
 	{
 		return Material::create(mShaderSceneGrid);
 	}
+
+	HMaterial BuiltinEditorResources::createPicking(CullingMode cullMode) const
+	{
+		UINT32 modeIdx = (UINT32)cullMode;
+
+		return Material::create(mShaderPicking[modeIdx]);
+	}
+
+	HMaterial BuiltinEditorResources::createPickingAlpha(CullingMode cullMode) const
+	{
+		UINT32 modeIdx = (UINT32)cullMode;
+
+		return Material::create(mShaderPickingAlpha[modeIdx]);
+	}
 }

+ 1 - 1
BansheeEditor/Source/BsGUISceneTreeView.cpp

@@ -175,7 +175,7 @@ namespace BansheeEngine
 
 	void GUISceneTreeView::updateTreeElementHierarchy()
 	{
-		HSceneObject root = gSceneManager().getRootNode();
+		HSceneObject root = gCoreSceneManager().getRootNode();
 		mRootElement.mSceneObject = root;
 		mRootElement.mId = root->getInstanceId();
 		mRootElement.mSortedIdx = 0;

+ 329 - 0
BansheeEditor/Source/BsScenePicking.cpp

@@ -0,0 +1,329 @@
+#include "BsScenePicking.h"
+#include "BsSceneManager.h"
+#include "BsColor.h"
+#include "BsMatrix4.h"
+#include "BsDebug.h"
+#include "BsMath.h"
+#include "BsRenderable.h"
+#include "BsSceneObject.h"
+#include "BsMesh.h"
+#include "BsConvexVolume.h"
+#include "BsCamera.h"
+#include "BsCoreThread.h"
+#include "BsRenderSystem.h"
+#include "BsMaterial.h"
+#include "BsPass.h"
+#include "BsBlendState.h"
+#include "BsDepthStencilState.h"
+#include "BsRasterizerState.h"
+#include "BsRenderTarget.h"
+#include "BsPixelData.h"
+#include "BsGpuParams.h"
+#include "BsBuiltinEditorResources.h"
+#include "BsShader.h"
+#include "BsRenderer.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	const float ScenePicking::ALPHA_CUTOFF = 0.5f;
+
+	ScenePicking::ScenePicking()
+	{
+		for (UINT32 i = 0; i < 3; i++)
+		{
+			mMaterialData[i].mMatPicking = BuiltinEditorResources::instance().createPicking((CullingMode)i);
+			mMaterialData[i].mMatPickingAlpha = BuiltinEditorResources::instance().createPickingAlpha((CullingMode)i);
+
+			mMaterialData[i].mMatPickingProxy = mMaterialData[i].mMatPicking->_createProxy();
+			mMaterialData[i].mMatPickingAlphaProxy = mMaterialData[i].mMatPickingAlpha->_createProxy();
+		}
+
+		gCoreAccessor().queueCommand(std::bind(&ScenePicking::initializeCore, this));
+	}
+
+	void ScenePicking::initializeCore()
+	{
+		for (UINT32 i = 0; i < 3; i++)
+		{
+			MaterialData& md = mMaterialData[i];
+
+			{
+				// TODO - Make a better interface when dealing with parameters through proxies?
+				MaterialProxyPtr proxy = md.mMatPickingProxy;
+				GpuParamsPtr vertParams = proxy->params[proxy->passes[0].vertexProgParamsIdx];
+
+				vertParams->getParam("matWorldViewProj", md.mParamPickingWVP);
+
+				GpuParamsPtr fragParams = proxy->params[proxy->passes[0].fragmentProgParamsIdx];
+
+				fragParams->getParam("colorIndex", md.mParamPickingColor);
+			}
+
+			{
+				MaterialProxyPtr proxy = md.mMatPickingAlphaProxy;
+				GpuParamsPtr vertParams = proxy->params[proxy->passes[0].vertexProgParamsIdx];
+
+				vertParams->getParam("matWorldViewProj", md.mParamPickingAlphaWVP);
+
+				GpuParamsPtr fragParams = proxy->params[proxy->passes[0].fragmentProgParamsIdx];
+
+				fragParams->getParam("colorIndex", md.mParamPickingAlphaColor);
+				fragParams->getTextureParam("mainTexture", md.mParamPickingAlphaTexture);
+
+				GpuParamFloat alphaCutoffParam;
+				fragParams->getParam("alphaCutoff", alphaCutoffParam);
+				alphaCutoffParam.set(ALPHA_CUTOFF);
+			}
+		}
+	}
+
+	HSceneObject ScenePicking::pickClosestSceneObject(const HCamera& cam, const Vector2I& position, const Vector2I& area)
+	{
+		Vector<PickResult> selectedObjects = pickObjects(cam, position, area);
+		for (auto& object : selectedObjects)
+		{
+			if (object.type == PickResult::Type::SceneObject)
+				return object.sceneObject;
+		}
+
+		return HSceneObject();
+	}
+
+	PickResult ScenePicking::pickClosestObject(const HCamera& cam, const Vector2I& position, const Vector2I& area)
+	{
+		Vector<PickResult> selectedObjects = pickObjects(cam, position, area);
+		if (selectedObjects.size() == 0)
+			return { HSceneObject(), 0, PickResult::Type::None };
+
+		return selectedObjects[0];
+	}
+
+	Vector<HSceneObject> ScenePicking::pickSceneObjects(const HCamera& cam, const Vector2I& position, const Vector2I& area)
+	{
+		Vector<HSceneObject> results;
+		Vector<PickResult> selectedObjects = pickObjects(cam, position, area);
+		for (auto& object : selectedObjects)
+		{
+			if (object.type == PickResult::Type::SceneObject)
+				results.push_back(object.sceneObject);
+		}
+
+		return results;
+	}
+
+	Vector<PickResult> ScenePicking::pickObjects(const HCamera& cam, const Vector2I& position, const Vector2I& area)
+	{
+		auto comparePickElement = [&] (const ScenePicking::RenderablePickData& a, const ScenePicking::RenderablePickData& b)
+		{
+			// Sort by alpha setting first, then by cull mode, then by index
+			if (a.alpha == b.alpha)
+			{
+				if (a.cullMode == b.cullMode)
+					return a.index > b.index;
+				else
+					return (UINT32)a.cullMode > (UINT32)b.cullMode;
+			}
+			else
+				return (UINT32)a.alpha > (UINT32)b.alpha;
+		};
+
+		const Vector<HRenderable>& renderables = SceneManager::instance().getAllRenderables();
+		RenderableSet pickData(comparePickElement);
+		Map<UINT32, HRenderable> idxToRenderable;
+
+		for (auto& renderable : renderables)
+		{
+			HSceneObject so = renderable->SO();
+
+			if (!so->getActive())
+				continue;
+
+			HMesh mesh = renderable->getMesh();
+			if (!mesh)
+				continue;
+
+			Bounds worldBounds = mesh->getBounds();
+			Matrix4 worldTransform = so->getWorldTfrm();
+			worldBounds.transformAffine(worldTransform);
+
+			const ConvexVolume& frustum = cam->getWorldFrustum();
+			if (frustum.intersects(worldBounds.getSphere()))
+			{
+				// More precise with the box
+				if (frustum.intersects(worldBounds.getBox()))
+				{
+					for (UINT32 i = 0; i < mesh->getNumSubMeshes(); i++)
+					{
+						UINT32 idx = (UINT32)pickData.size();
+
+						HMaterial originalMat = renderable->getMaterial(i);
+						if (!originalMat)
+							continue;
+
+						PassPtr firstPass;
+						if (originalMat->getNumPasses() == 0)
+							continue;
+
+						firstPass = originalMat->getPass(0); // Note: We only ever check the first pass, problem?
+						bool useAlphaShader = firstPass->hasBlending();
+						CullingMode cullMode = firstPass->getRasterizerState()->getCullMode();
+
+						MeshProxyPtr meshProxy;
+
+						if (mesh->_isCoreDirty(MeshDirtyFlag::Proxy))
+							meshProxy = mesh->_createProxy(i);
+						else
+							meshProxy = mesh->_getActiveProxy(i);
+
+						idxToRenderable[idx] = renderable;
+						pickData.insert({ meshProxy, idx, worldTransform, useAlphaShader, cullMode });
+					}
+				}
+			}
+		}
+
+		Viewport vp = cam->getViewport()->clone();
+		AsyncOp op = gCoreAccessor().queueReturnCommand(std::bind(&ScenePicking::corePickObjects, this, vp, std::cref(pickData), position, area, _1));
+		gCoreAccessor().submitToCoreThread(true);
+
+		assert(op.hasCompleted());
+
+		Vector<UINT32>& selectedObjects = op.getReturnValue<Vector<UINT32>>();
+		Vector<PickResult> results;
+
+		for (auto& selectedObject : selectedObjects)
+		{
+			results.push_back({ idxToRenderable[selectedObject]->SO(), 0, PickResult::Type::SceneObject });
+		}
+
+		return results;
+	}
+
+	void ScenePicking::corePickObjects(const Viewport& vp, const RenderableSet& renderables, const Vector2I& position,
+		const Vector2I& area, AsyncOp& asyncOp)
+	{
+		RenderSystem& rs = RenderSystem::instance();
+
+		RenderTargetPtr rt = vp.getTarget();
+		if (rt->getCore()->getProperties().isWindow())
+		{
+			// TODO: When I do implement this then I will likely want a method in RenderTarget that unifies both render window and render texture readback
+			BS_EXCEPT(NotImplementedException, "Picking is not supported on render windows as framebuffer readback methods aren't implemented");
+		}
+
+		RenderTexturePtr rtt = std::static_pointer_cast<RenderTexture>(rt);
+		TexturePtr outputTexture = rtt->getBindableColorTexture().getInternalPtr();
+
+		rs.setViewport(vp);
+		rs.clearRenderTarget(FBT_COLOR, Color::White);
+		rs.setScissorRect(position.x, position.y, position.x + area.x, position.y + area.y);
+
+		Renderer::setPass(*mMaterialData[0].mMatPickingProxy, 0);
+		bool activeMaterialIsAlpha = false;
+		CullingMode activeMaterialCull = (CullingMode)0;
+
+		rs.beginFrame();
+		for (auto& renderable : renderables)
+		{
+			if (activeMaterialIsAlpha != renderable.alpha || activeMaterialCull != renderable.cullMode)
+			{
+				activeMaterialIsAlpha = renderable.alpha;
+				activeMaterialCull = renderable.cullMode;
+
+				if (activeMaterialIsAlpha)
+					Renderer::setPass(*mMaterialData[(UINT32)activeMaterialCull].mMatPickingAlphaProxy, 0);
+				else
+					Renderer::setPass(*mMaterialData[(UINT32)activeMaterialCull].mMatPickingProxy, 0);
+			}
+			
+			Color color = encodeIndex(renderable.index);
+			MaterialData& md = mMaterialData[(UINT32)activeMaterialCull];
+
+			if (activeMaterialIsAlpha)
+			{
+				md.mParamPickingAlphaWVP.set(renderable.wvpTransform);
+				md.mParamPickingAlphaColor.set(color);
+				md.mParamPickingAlphaTexture.set(HTexture()); // TODO - Get main texture from original
+			}
+			else
+			{
+				md.mParamPickingWVP.set(renderable.wvpTransform);
+				md.mParamPickingColor.set(color);
+			}
+		}
+		rs.endFrame();
+
+		PixelDataPtr outputPixelData = outputTexture->allocateSubresourceBuffer(0);
+		GpuResourceDataPtr outputData = outputPixelData;
+		AsyncOp unused;
+
+		rs.readSubresource(outputTexture, 0, outputData, unused);
+
+		Map<UINT32, UINT32> selectionScores;
+		UINT32 numPixels = outputPixelData->getWidth() * outputPixelData->getHeight();
+		for (UINT32 y = 0; y < outputPixelData->getHeight(); y++)
+		{
+			for (UINT32 x = 0; x < outputPixelData->getWidth(); x++)
+			{
+				Color color = outputPixelData->getColorAt(x, y);
+				UINT32 index = decodeIndex(color);
+
+				if (index == 0x00FFFFFF) // Nothing selected
+					continue;
+
+				auto iterFind = selectionScores.find(index);
+				if (iterFind == selectionScores.end())
+					selectionScores[index] = 1;
+				else
+					iterFind->second++;
+			}
+		}
+
+		// Sort by score
+		struct SelectedObject { UINT32 index; UINT32 score; };
+
+		Vector<SelectedObject> selectedObjects(selectionScores.size());
+		UINT32 idx = 0;
+		for (auto& selectionScore : selectionScores)
+		{
+			selectedObjects[idx++] = { selectionScore.second, selectionScore.first };
+		}
+
+		std::sort(selectedObjects.begin(), selectedObjects.end(),
+			[&](const SelectedObject& a, const SelectedObject& b)
+		{
+			return b.score < a.score;
+		});
+
+		Vector<UINT32> results;
+		for (auto& selectedObject : selectedObjects)
+			results.push_back(selectedObject.index);
+
+		asyncOp._completeOperation(selectedObjects);
+	}
+
+	Color ScenePicking::encodeIndex(UINT32 index)
+	{
+		Color encoded;
+		encoded.r = (index & 0xFF) / 255.0f;
+		encoded.g = ((index >> 8) & 0xFF) / 255.0f;
+		encoded.b = ((index >> 16) & 0xFF) / 255.0f;
+		encoded.a = 1.0f;
+
+		if (((index >> 24) & 0xFF))
+			LOGERR("Index when picking out of valid range.");
+
+		return encoded;
+	}
+
+	UINT32 ScenePicking::decodeIndex(Color color)
+	{
+		UINT32 r = Math::roundToInt(color.r * 255.0f);
+		UINT32 g = Math::roundToInt(color.g * 255.0f);
+		UINT32 b = Math::roundToInt(color.b * 255.0f);
+
+		return (r & 0xFF) | ((g & 0xFF) << 8) | ((b & 0xFF) << 16);
+	}
+}

+ 8 - 1
BansheeEngine/Include/BsCamera.h

@@ -180,10 +180,17 @@ namespace BansheeEngine
 		virtual bool isCustomProjectionMatrixEnabled() const { return mCustomProjMatrix; }
 
 		/** 
-		 * @brief	Returns a convex volume representing the visible area of the camera.
+		 * @brief	Returns a convex volume representing the visible area of the camera,
+		 *			in local space.
          */
         virtual const ConvexVolume& getFrustum() const;
 
+		/** 
+		 * @brief	Returns a convex volume representing the visible area of the camera,
+		 *			in world space.
+         */
+        virtual ConvexVolume getWorldFrustum() const;
+
 		/**
 		 * @brief	Returns the bounding of the frustum.
 		 */

+ 10 - 4
BansheeEngine/Include/BsRenderable.h

@@ -67,6 +67,11 @@ namespace BansheeEngine
 		 */
 		UINT64 getLayer() const { return mLayer; }
 
+		/**
+		 * @brief	Returns the mesh used for rendering.
+		 */
+		HMesh getMesh() const { return mMeshData.mesh; }
+
 		/**
 		 * @brief	Returns the material used for rendering a sub-mesh with
 		 *			the specified index.
@@ -88,6 +93,11 @@ namespace BansheeEngine
 		 */
 		void _markCoreClean();
 
+		/**
+		 * @brief	Marks the core data as dirty.
+		 */
+		void _markCoreDirty() const { mCoreDirtyFlags = 0xFFFFFFFF; }
+
 		/**
 		 * @brief	Creates a new core proxy from the currently set Renderable data. Core proxies ensure
 		 *			that the core thread has all the necessary Renderable data, while avoiding the need
@@ -115,10 +125,6 @@ namespace BansheeEngine
 		 */
 		void updateResourceLoadStates() const;
 
-		/**
-		 * @brief	Marks the core data as dirty.
-		 */
-		void markCoreDirty() const { mCoreDirtyFlags = 0xFFFFFFFF; }
 	private:
 		MeshData mMeshData;
 		Vector<MaterialData> mMaterialData;

+ 11 - 1
BansheeEngine/Include/BsSceneManager.h

@@ -30,6 +30,16 @@ namespace BansheeEngine
 		 */
 		virtual void updateRenderableTransforms() = 0;
 
+		/**
+		 * @copydoc	CoreSceneManager::instance
+		 */
+		static SceneManager& instance();
+
+		/**
+		 * @copydoc	CoreSceneManager::instancePtr
+		 */
+		static SceneManager* instancePtr();
+
 		/**
 		 * @brief	Triggered whenever a renderable is removed from a SceneObject.
 		 */
@@ -44,5 +54,5 @@ namespace BansheeEngine
 	/**
 	 * @copydoc	SceneManager
 	 */
-	BS_EXPORT SceneManager& gBsSceneManager();
+	BS_EXPORT SceneManager& gSceneManager();
 }

+ 16 - 0
BansheeEngine/Source/BsCamera.cpp

@@ -107,6 +107,22 @@ namespace BansheeEngine
 		return mFrustum;
 	}
 
+	ConvexVolume Camera::getWorldFrustum() const
+	{
+		const Vector<Plane>& frustumPlanes = getFrustum().getPlanes();
+		Matrix4 worldMatrix = SO()->getWorldTfrm();
+
+		Vector<Plane> worldPlanes(frustumPlanes.size());
+		UINT32 i = 0;
+		for (auto& plane : frustumPlanes)
+		{
+			worldPlanes[i] = worldMatrix.multiply3x4(plane);
+			i++;
+		}
+
+		return ConvexVolume(worldPlanes);
+	}
+
 	void Camera::calcProjectionParameters(float& left, float& right, float& bottom, float& top) const
 	{ 
 		if (mCustomProjMatrix)

+ 5 - 5
BansheeEngine/Source/BsRenderable.cpp

@@ -33,14 +33,14 @@ namespace BansheeEngine
 		mMeshData = mesh;
 		mMaterialData.resize(mesh->getNumSubMeshes());
 
-		markCoreDirty();
+		_markCoreDirty();
 	}
 
 	void Renderable::setMaterial(UINT32 idx, HMaterial material)
 	{
 		mMaterialData[idx] = material;
 
-		markCoreDirty();
+		_markCoreDirty();
 	}
 
 	void Renderable::setMaterial(HMaterial material)
@@ -64,7 +64,7 @@ namespace BansheeEngine
 			BS_EXCEPT(InvalidParametersException, "Invalid layer provided. Only one layer bit may be set.");
 
 		mLayer = layer;
-		markCoreDirty();
+		_markCoreDirty();
 	}
 
 	bool Renderable::_isCoreDirty() const
@@ -103,7 +103,7 @@ namespace BansheeEngine
 		{
 			mMeshData.isLoaded = true;
 
-			markCoreDirty();
+			_markCoreDirty();
 		}
 
 		for (auto& materialData : mMaterialData)
@@ -112,7 +112,7 @@ namespace BansheeEngine
 			{
 				materialData.isLoaded = true;
 
-				markCoreDirty();
+				_markCoreDirty();
 			}
 		}
 	}

+ 12 - 3
BansheeEngine/Source/BsSceneManager.cpp

@@ -1,10 +1,19 @@
 #include "BsSceneManager.h"
-#include "BsSceneManager.h"
 
 namespace BansheeEngine
 {
-	SceneManager& gBsSceneManager()
+	SceneManager& SceneManager::instance()
+	{
+		return static_cast<SceneManager&>(CoreSceneManager::instance());
+	}
+
+	SceneManager* SceneManager::instancePtr()
+	{
+		return static_cast<SceneManager*>(CoreSceneManager::instancePtr());
+	}
+
+	SceneManager& gSceneManager()
 	{
-		return static_cast<SceneManager&>(gSceneManager());
+		return static_cast<SceneManager&>(gCoreSceneManager());
 	}
 }

+ 0 - 17
BansheeRenderer/Include/BsBansheeRenderer.h

@@ -152,23 +152,6 @@ namespace BansheeEngine
 		 */
 		virtual void render(const CameraProxy& cameraProxy, const RenderQueuePtr& renderQueue);
 
-		/**
-		 * @brief	Activates the specified pass on the pipeline.
-		 *
-		 * @param	material	Parent material of the pass.
-		 * @param	passIdx		Index of the pass in the parent material.
-		 *
-		 * @note	Core thread only.
-		 */
-		void setPass(const MaterialProxy& material, UINT32 passIdx);
-
-		/**
-		 * @brief	Draws the specified mesh proxy with last set pass.
-		 *
-		 * @note	Core thread only.
-		 */
-		void draw(const MeshProxy& mesh);
-
 		/**
 		 * @brief	Called by the scene manager whenever a Renderable component has been
 		 *			removed from the scene.

+ 42 - 160
BansheeRenderer/Source/BsBansheeRenderer.cpp

@@ -37,8 +37,8 @@ namespace BansheeEngine
 {
 	BansheeRenderer::BansheeRenderer()
 	{
-		mRenderableRemovedConn = gBsSceneManager().onRenderableRemoved.connect(std::bind(&BansheeRenderer::renderableRemoved, this, _1));
-		mCameraRemovedConn = gBsSceneManager().onCameraRemoved.connect(std::bind(&BansheeRenderer::cameraRemoved, this, _1));
+		mRenderableRemovedConn = gSceneManager().onRenderableRemoved.connect(std::bind(&BansheeRenderer::renderableRemoved, this, _1));
+		mCameraRemovedConn = gSceneManager().onCameraRemoved.connect(std::bind(&BansheeRenderer::cameraRemoved, this, _1));
 	}
 
 	BansheeRenderer::~BansheeRenderer()
@@ -200,7 +200,7 @@ namespace BansheeEngine
 
 	void BansheeRenderer::renderAll() 
 	{
-		gBsSceneManager().updateRenderableTransforms();
+		gSceneManager().updateRenderableTransforms();
 
 		// Remove proxies from deleted Renderables
 		for (auto& proxy : mDeletedRenderableProxies)
@@ -210,7 +210,7 @@ namespace BansheeEngine
 		}
 
 		// Add or update Renderable proxies
-		const Vector<HRenderable>& allRenderables = gBsSceneManager().getAllRenderables();
+		const Vector<HRenderable>& allRenderables = gSceneManager().getAllRenderables();
 		Vector<HSceneObject> dirtySceneObjects;
 		Vector<HRenderable> dirtyRenderables;
 
@@ -220,43 +220,54 @@ namespace BansheeEngine
 			bool addedNewProxy = false;
 			RenderableProxyPtr proxy = renderable->_getActiveProxy();
 
-			if (renderable->_isCoreDirty())
+			if (renderable->SO()->getActive())
 			{
-				if (proxy != nullptr)
-					gCoreAccessor().queueCommand(std::bind(&BansheeRenderer::removeRenderableProxy, this, proxy));
+				if (renderable->_isCoreDirty())
+				{
+					if (proxy != nullptr)
+						gCoreAccessor().queueCommand(std::bind(&BansheeRenderer::removeRenderableProxy, this, proxy));
 
-				proxy = renderable->_createProxy();
-				renderable->_setActiveProxy(proxy);
+					proxy = renderable->_createProxy();
+					renderable->_setActiveProxy(proxy);
 
-				if (proxy != nullptr)
-					gCoreAccessor().queueCommand(std::bind(&BansheeRenderer::addRenderableProxy, this, proxy));
+					if (proxy != nullptr)
+						gCoreAccessor().queueCommand(std::bind(&BansheeRenderer::addRenderableProxy, this, proxy));
 
-				dirtyRenderables.push_back(renderable);
-				dirtySceneObjects.push_back(renderable->SO());
-				addedNewProxy = true;
-			}
-			else if (renderable->SO()->_isCoreDirty())
-			{
-				assert(proxy != nullptr);
-
-				gCoreAccessor().queueCommand(std::bind(&BansheeRenderer::updateRenderableProxy, this, proxy, renderable->SO()->getWorldTfrm()));
+					dirtyRenderables.push_back(renderable);
+					dirtySceneObjects.push_back(renderable->SO());
+					addedNewProxy = true;
+				}
+				else if (renderable->SO()->_isCoreDirty())
+				{
+					if (proxy != nullptr)
+						gCoreAccessor().queueCommand(std::bind(&BansheeRenderer::updateRenderableProxy, this, proxy, renderable->SO()->getWorldTfrm()));
 
-				dirtySceneObjects.push_back(renderable->SO());
-			}
+					dirtySceneObjects.push_back(renderable->SO());
+				}
 
-			if (!addedNewProxy && proxy != nullptr)
-			{
-				for (UINT32 i = 0; i < (UINT32)proxy->renderableElements.size(); i++)
+				if (!addedNewProxy && proxy != nullptr)
 				{
-					HMaterial mat = renderable->getMaterial(i);
-					if (mat != nullptr && mat.isLoaded() && mat->_isCoreDirty(MaterialDirtyFlag::Params))
+					for (UINT32 i = 0; i < (UINT32)proxy->renderableElements.size(); i++)
 					{
-						gCoreAccessor().queueCommand(std::bind(&BansheeRenderer::updateMaterialProxy, this, 
-							proxy->renderableElements[i]->material, mat->_getDirtyProxyParams(frameAlloc)));
-						mat->_markCoreClean(MaterialDirtyFlag::Params);
+						HMaterial mat = renderable->getMaterial(i);
+						if (mat != nullptr && mat.isLoaded() && mat->_isCoreDirty(MaterialDirtyFlag::Params))
+						{
+							gCoreAccessor().queueCommand(std::bind(&BansheeRenderer::updateMaterialProxy, this,
+								proxy->renderableElements[i]->material, mat->_getDirtyProxyParams(frameAlloc)));
+							mat->_markCoreClean(MaterialDirtyFlag::Params);
+						}
 					}
 				}
 			}
+			else // If inactive we remove the proxy until re-activated
+			{
+				if (proxy != nullptr)
+				{
+					gCoreAccessor().queueCommand(std::bind(&BansheeRenderer::removeRenderableProxy, this, proxy));
+					renderable->_setActiveProxy(nullptr);
+					renderable->_markCoreDirty();
+				}
+			}
 		}
 
 		// Mark all renderables as clean (needs to be done after all proxies are updated as
@@ -275,7 +286,7 @@ namespace BansheeEngine
 		}
 
 		// Add or update Camera proxies
-		const Vector<HCamera>& allCameras = gBsSceneManager().getAllCameras();
+		const Vector<HCamera>& allCameras = gSceneManager().getAllCameras();
 		for (auto& camera : allCameras)
 		{
 			if (camera->_isCoreDirty())
@@ -519,133 +530,4 @@ namespace BansheeEngine
 
 		renderQueue->clear();
 	}
-
-	void BansheeRenderer::setPass(const MaterialProxy& material, UINT32 passIdx)
-	{
-		THROW_IF_NOT_CORE_THREAD;
-
-		RenderSystem& rs = RenderSystem::instance();
-
-		const MaterialProxyPass& pass = material.passes[passIdx];
-		if (pass.vertexProg)
-		{
-			rs.bindGpuProgram(pass.vertexProg);
-			rs.bindGpuParams(GPT_VERTEX_PROGRAM, material.params[pass.vertexProgParamsIdx]);
-		}
-		else
-			rs.unbindGpuProgram(GPT_VERTEX_PROGRAM);
-
-		if (pass.fragmentProg)
-		{
-			rs.bindGpuProgram(pass.fragmentProg);
-			rs.bindGpuParams(GPT_FRAGMENT_PROGRAM, material.params[pass.fragmentProgParamsIdx]);
-		}
-		else
-			rs.unbindGpuProgram(GPT_FRAGMENT_PROGRAM);
-
-		if (pass.geometryProg)
-		{
-			rs.bindGpuProgram(pass.geometryProg);
-			rs.bindGpuParams(GPT_GEOMETRY_PROGRAM, material.params[pass.geometryProgParamsIdx]);
-		}
-		else
-			rs.unbindGpuProgram(GPT_GEOMETRY_PROGRAM);
-
-		if (pass.hullProg)
-		{
-			rs.bindGpuProgram(pass.hullProg);
-			rs.bindGpuParams(GPT_HULL_PROGRAM, material.params[pass.hullProgParamsIdx]);
-		}
-		else
-			rs.unbindGpuProgram(GPT_HULL_PROGRAM);
-
-		if (pass.domainProg)
-		{
-			rs.bindGpuProgram(pass.domainProg);
-			rs.bindGpuParams(GPT_DOMAIN_PROGRAM, material.params[pass.domainProgParamsIdx]);
-		}
-		else
-			rs.unbindGpuProgram(GPT_DOMAIN_PROGRAM);
-
-		if (pass.computeProg)
-		{
-			rs.bindGpuProgram(pass.computeProg);
-			rs.bindGpuParams(GPT_COMPUTE_PROGRAM, material.params[pass.computeProgParamsIdx]);
-		}
-		else
-			rs.unbindGpuProgram(GPT_COMPUTE_PROGRAM);
-
-		// TODO - Try to limit amount of state changes, if previous state is already the same
-
-		// Set up non-texture related pass settings
-		if (pass.blendState != nullptr)
-			rs.setBlendState(pass.blendState.getInternalPtr());
-		else
-			rs.setBlendState(BlendState::getDefault());
-
-		if (pass.depthStencilState != nullptr)
-			rs.setDepthStencilState(pass.depthStencilState.getInternalPtr(), pass.stencilRefValue);
-		else
-			rs.setDepthStencilState(DepthStencilState::getDefault(), pass.stencilRefValue);
-
-		if (pass.rasterizerState != nullptr)
-			rs.setRasterizerState(pass.rasterizerState.getInternalPtr());
-		else
-			rs.setRasterizerState(RasterizerState::getDefault());
-	}
-
-	void BansheeRenderer::draw(const MeshProxy& meshProxy)
-	{
-		THROW_IF_NOT_CORE_THREAD;
-
-		RenderSystem& rs = RenderSystem::instance();
-		MeshBasePtr mesh;
-
-		if (!meshProxy.mesh.expired())
-			mesh = meshProxy.mesh.lock(); 
-		else
-			return;
-
-		std::shared_ptr<VertexData> vertexData = mesh->_getVertexData();
-
-		rs.setVertexDeclaration(vertexData->vertexDeclaration);
-		auto vertexBuffers = vertexData->getBuffers();
-
-		if (vertexBuffers.size() > 0)
-		{
-			VertexBufferPtr buffers[MAX_BOUND_VERTEX_BUFFERS];
-
-			UINT32 endSlot = 0;
-			UINT32 startSlot = MAX_BOUND_VERTEX_BUFFERS;
-			for (auto iter = vertexBuffers.begin(); iter != vertexBuffers.end(); ++iter)
-			{
-				if (iter->first >= MAX_BOUND_VERTEX_BUFFERS)
-					BS_EXCEPT(InvalidParametersException, "Buffer index out of range");
-
-				startSlot = std::min(iter->first, startSlot);
-				endSlot = std::max(iter->first, endSlot);
-			}
-
-			for (auto iter = vertexBuffers.begin(); iter != vertexBuffers.end(); ++iter)
-			{
-				buffers[iter->first - startSlot] = iter->second;
-			}
-
-			rs.setVertexBuffers(startSlot, buffers, endSlot - startSlot + 1);
-		}
-
-		SubMesh subMesh = meshProxy.subMesh;
-		rs.setDrawOperation(subMesh.drawOp);
-
-		IndexBufferPtr indexBuffer = mesh->_getIndexBuffer();
-
-		UINT32 indexCount = subMesh.indexCount;
-		if (indexCount == 0)
-			indexCount = indexBuffer->getNumIndices();
-
-		rs.setIndexBuffer(indexBuffer);
-		rs.drawIndexed(subMesh.indexOffset + mesh->_getIndexOffset(), indexCount, mesh->_getVertexOffset(), vertexData->vertexCount);
-
-		mesh->_notifyUsedOnGPU();
-	}
 }

+ 1 - 5
SceneView.txt

@@ -4,11 +4,7 @@ TODO:
 
 Weekend:
  - SceneGrid is very ugly. Consider using default lines for now and come back with a better approach later.
-
- I have no way of read data from window framebuffer. Will need special methods for it?
-  - Think about is this even needed? If I do post-processing I'm going to draw everything to render textures and then
-    just blit to back buffer. I think.
- OpenGL texture read/write from render textures isn't hooked up at all
+ - In Picking code I don't set main texture when rendering with alpha
 
 ----------------------------------------------------------------------
 Handles