Browse Source

Added overlays that will make 2D rendering easier

Marko Pintera 12 years ago
parent
commit
ddd17628eb

+ 4 - 0
CamelotCore/CamelotCore.vcxproj

@@ -176,6 +176,7 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClInclude Include="Include\CmOverlay.h" />
     <ClInclude Include="Include\CmApplication.h" />
     <ClInclude Include="Include\CmBlendStateRTTI.h" />
     <ClInclude Include="Include\CmBuiltinMaterialManager.h" />
@@ -228,6 +229,7 @@
     <ClInclude Include="Include\CmMaterialManager.h" />
     <ClInclude Include="Include\CmMeshManager.h" />
     <ClInclude Include="Include\CmOcclusionQuery.h" />
+    <ClInclude Include="Include\CmOverlayManager.h" />
     <ClInclude Include="Include\CmPixelBuffer.h" />
     <ClInclude Include="Include\CmGpuProgIncludeImporter.h" />
     <ClInclude Include="Include\CmSpriteTexture.h" />
@@ -301,6 +303,7 @@
   <ItemGroup>
     <ClCompile Include="Include\CmMaterialManager.cpp" />
     <ClCompile Include="Source\CamelotRenderer.cpp" />
+    <ClCompile Include="Source\CmOverlay.cpp" />
     <ClCompile Include="Source\CmApplication.cpp" />
     <ClCompile Include="Source\CmBlendState.cpp" />
     <ClCompile Include="Source\CmCamera.cpp" />
@@ -337,6 +340,7 @@
     <ClCompile Include="Source\CmMeshDataRTTI.cpp" />
     <ClCompile Include="Source\CmMeshManager.cpp" />
     <ClCompile Include="Source\CmOcclusionQuery.cpp" />
+    <ClCompile Include="Source\CmOverlayManager.cpp" />
     <ClCompile Include="Source\CmPixelBuffer.cpp" />
     <ClCompile Include="Source\CmGpuProgIncludeImporter.cpp" />
     <ClCompile Include="Source\CmSprite.cpp" />

+ 12 - 0
CamelotCore/CamelotCore.vcxproj.filters

@@ -465,6 +465,12 @@
     <ClInclude Include="Include\CmGUISkin.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\CmOverlay.h">
+      <Filter>Header Files\2D</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\CmOverlayManager.h">
+      <Filter>Header Files\2D</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\CamelotRenderer.cpp">
@@ -716,5 +722,11 @@
     <ClCompile Include="Source\CmGUISkin.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
+    <ClCompile Include="Source\CmOverlay.cpp">
+      <Filter>Source Files\2D</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\CmOverlayManager.cpp">
+      <Filter>Source Files\2D</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 2 - 0
CamelotCore/Include/CmGUILabel.h

@@ -8,6 +8,8 @@ namespace CamelotEngine
 	class CM_EXPORT GUILabel : public GUIElement
 	{
 	protected:
+		~GUILabel();
+
 		/**
 		 * @copydoc GUIElement::getNumRenderElements()
 		 */

+ 0 - 7
CamelotCore/Include/CmGUIManager.h

@@ -2,7 +2,6 @@
 
 #include "CmPrerequisites.h"
 #include "CmModule.h"
-#include "CmRenderOperation.h"
 
 namespace CamelotEngine
 {
@@ -15,13 +14,7 @@ namespace CamelotEngine
 		void registerWidget(GUIWidget* widget);
 		void unregisterWidget(GUIWidget* widget);
 
-		vector<RenderObject>::type getRenderObjects();
-
 	private:
 		vector<GUIWidget*>::type mWidgets;
-		vector<MeshHandle>::type mMeshes;
-		vector<MaterialHandle>::type mMaterials;
-
-		void updateDirtyMeshes();
 	};
 }

+ 8 - 13
CamelotCore/Include/CmGUIWidget.h

@@ -1,17 +1,16 @@
 #pragma once
 
 #include "CmPrerequisites.h"
-#include "CmComponent.h"
+#include "CmOverlay.h"
 
 namespace CamelotEngine
 {
-	class CM_EXPORT GUIWidget : public Component
+	class CM_EXPORT GUIWidget : public Overlay
 	{
 	public:
-		~GUIWidget();
-
-		virtual void update();
+		virtual ~GUIWidget();
 
+		virtual void render(const CameraPtr& camera, DeferredRenderContextPtr& renderContext) const;
 	protected:
 		GUILabel* addLabel(const String& text);
 
@@ -19,18 +18,14 @@ namespace CamelotEngine
 
 	private:
 		friend class GameObject;
-		friend class GUIManager;
 
 		GUIWidget(GameObjectPtr parent);
 
+		void updateMeshes() const;
+
 		vector<GUIElement*>::type mElements;
-		/**
-		 * @brief	GUIWidgets sharing the same mesh group ID will attempted to be merged into
-		 * 			a single render mesh (as much as materials allow).
-		 * 			
-		 * @note	ID of -1 means that the widget mesh should never be grouped.
-		 */
-		INT32 mMeshGroupID;
+		mutable vector<MeshHandle>::type mCachedMeshes;
+		mutable vector<MaterialHandle>::type mCachedMaterials;
 		const GUISkin* mSkin;
 		static GUISkin DefaultSkin;
 	};

+ 26 - 0
CamelotCore/Include/CmOverlay.h

@@ -0,0 +1,26 @@
+#pragma once
+
+#include "CmPrerequisites.h"
+#include "CmComponent.h"
+
+namespace CamelotEngine
+{
+	/**
+	 * @brief	Overlay components are a special type of components that can be attached directly
+	 * 			to a Camera and used for rendering 2D graphics. Camera will render any of its overlay
+	 * 			components after it has rendered the rest of the scene, so these components are usually 
+	 * 			used for GUI elements and full screen effects.
+	 */
+	class CM_EXPORT Overlay : public Component
+	{
+	public:
+		virtual ~Overlay();
+
+		virtual void render(const CameraPtr& camera, DeferredRenderContextPtr& renderContext) const = 0;
+
+	protected:
+		friend class GameObject;
+
+		Overlay(GameObjectPtr parent);
+	};
+}

+ 27 - 0
CamelotCore/Include/CmOverlayManager.h

@@ -0,0 +1,27 @@
+#pragma once
+
+#include "CmPrerequisites.h"
+#include "CmModule.h"
+
+namespace CamelotEngine
+{
+	/**
+	 * @brief	Takes care which overlay gets rendered on which camera.
+	 * 			
+	 * @note	Overlays could have been stored directly on a Camera but this class
+	 * 			was created to decouple the connection.
+	 * 
+	 * @see Overlay
+	 */
+	class CM_EXPORT OverlayManager : public Module<OverlayManager>
+	{
+	public:
+		void render(const CameraPtr& camera, DeferredRenderContextPtr& renderContext) const;
+
+		void attachOverlay(const CameraPtr& camera, const Overlay* overlay);
+		void detachOverlay(const CameraPtr& camera, const Overlay* overlay);
+		void detachOverlayFromAll(const Overlay* overlay);
+	private:
+		unordered_map<const Camera*, unordered_set<const Overlay*>::type>::type mOverlaysPerCamera;
+	};
+}

+ 10 - 0
CamelotCore/Include/CmPass.h

@@ -104,6 +104,16 @@ namespace CamelotEngine
 		/** Gets the compute program used by this pass. */
 		const GpuProgramHandle& getComputeProgram(void) const { return mComputeProgram; }
 
+		/**
+		 * @brief	Makes this pass active. Anything rendered after this command will use this pass.
+		 */
+		void activate(DeferredRenderContextPtr& renderContext) const;
+
+		/**
+		 * @brief	Applies specified parameters to the active pass. 
+		 */
+		void bindParameters(DeferredRenderContextPtr& renderContext, const PassParametersPtr& params) const;
+
 		/************************************************************************/
 		/* 								RTTI		                     		*/
 		/************************************************************************/

+ 4 - 0
CamelotCore/Include/CmPrerequisites.h

@@ -162,6 +162,9 @@ namespace CamelotEngine {
 	class GameObject;
 	class Component;
 	class SceneManager;
+	// 2D
+	class Overlay;
+	class OverlayManager;
 	// GUI
 	class GUIManager;
 	class GUIWidget;
@@ -227,6 +230,7 @@ namespace CamelotEngine
 	typedef std::shared_ptr<const ImportOptions> ConstImportOptionsPtr;
 	typedef std::shared_ptr<Font> FontPtr;
 	typedef std::shared_ptr<TextSprite> TextSpritePtr;
+	typedef std::shared_ptr<Overlay> OverlayPtr;
 }
 
 /************************************************************************/

+ 0 - 6
CamelotCore/Include/CmRenderOperation.h

@@ -82,10 +82,4 @@ namespace CamelotEngine
 	};
 	/** @} */
 	/** @} */
-
-	struct RenderObject
-	{
-		RenderOperation op;
-		MaterialHandle mat;
-	};
 }

+ 0 - 12
CamelotCore/Include/CmRenderer.h

@@ -18,17 +18,5 @@ namespace CamelotEngine
 		 * @brief	 Renders the scene from the perspective of a single camera
 		 */
 		virtual void render(const CameraPtr camera) = 0;
-
-	protected:
-		friend class RenderCommandBuffer;
-		/**
-		 * @brief	Sets the currently active pass.
-		 */
-		virtual void setPass(PassPtr pass) = 0;
-
-		/**
-		 * @brief	Sets the parameters for the current pass;
-		 */
-		virtual void setPassParameters(PassParametersPtr params) = 0;
 	};
 }

+ 3 - 0
CamelotCore/Source/CmApplication.cpp

@@ -27,6 +27,7 @@
 #include "CmFontManager.h"
 #include "CmRenderer.h"
 #include "CmDeferredRenderContext.h"
+#include "CmOverlayManager.h"
 #include "CmGUIMaterialManager.h"
 #include "CmGUIManager.h"
 
@@ -87,6 +88,7 @@ namespace CamelotEngine
 		loadPlugin("CamelotFBXImporter"); // TODO - Load this automatically somehow
 		loadPlugin("CamelotFontImporter"); // TODO - Load this automatically somehow
 
+		OverlayManager::startUp(new OverlayManager());
 		GUIMaterialManager::startUp(new GUIMaterialManager());
 		GUIManager::startUp(new GUIManager());
 
@@ -156,6 +158,7 @@ namespace CamelotEngine
 
 		GUIManager::shutDown();
 		GUIMaterialManager::shutDown();
+		OverlayManager::shutDown();
 
 		Importer::shutDown();
 		FontManager::shutDown();

+ 5 - 0
CamelotCore/Source/CmGUILabel.cpp

@@ -14,6 +14,11 @@ namespace CamelotEngine
 		mTextSprite = new TextSprite(text, mStyle->font, mStyle->fontSize);
 	}
 
+	GUILabel::~GUILabel()
+	{
+		delete mTextSprite;
+	}
+
 	UINT32 GUILabel::getNumRenderElements() const
 	{
 		return mTextSprite->getNumRenderElements();

+ 0 - 179
CamelotCore/Source/CmGUIManager.cpp

@@ -8,76 +8,6 @@
 
 namespace CamelotEngine
 {
-	struct MeshGroupID
-	{
-		class HashFunction
-		{
-		public:
-			inline size_t operator()(const MeshGroupID& v) const
-			{
-				size_t seed = 0;
-				hash_combine(seed, v.materialId);
-				hash_combine(seed, v.meshGroupId);
-				return seed;
-			}
-		};
-
-		class EqualFunction
-		{
-		public:
-			inline bool operator()(const MeshGroupID &a, const MeshGroupID &b) const
-			{
-				return a.materialId == b.materialId && a.meshGroupId == b.meshGroupId;
-			}
-		};
-
-		MeshGroupID(UINT32 _meshGroupId, UINT64 _materialId)
-			:meshGroupId(_meshGroupId), materialId(_materialId)
-		{
-
-		}
-
-		UINT32 meshGroupId;
-		UINT64 materialId;
-		
-	};
-
-	vector<RenderObject>::type GUIManager::getRenderObjects()
-	{
-		updateDirtyMeshes();
-
-		// TODO - It might be better to use standard Component system for rendering meshes
-		// (i.e. make GUIManager a component and have it create "Renderable" components that have
-		// meshes attached to them)
-		UINT32 meshIdx = 0;
-		vector<RenderObject>::type renderObjects;
-		for(auto& mesh : mMeshes)
-		{
-			MaterialHandle material = mMaterials[meshIdx];
-
-			if(material == nullptr || !material.isLoaded())
-				continue;
-
-			MeshHandle mesh = mMeshes[meshIdx];
-
-			if(mesh == nullptr || !mesh.isLoaded())
-				continue;
-
-			// TODO - Set current viewport resolution so that GUI can be rendered properly
-			//  - HOW DO I handle multiple GUIWidgets across different windows?
-
-			RenderObject ro;
-			ro.mat = material;
-			ro.op = mesh->getRenderOperation(0);
-
-			renderObjects.push_back(ro);
-
-			meshIdx++;
-		}
-
-		return renderObjects;
-	}
-
 	void GUIManager::registerWidget(GUIWidget* widget)
 	{
 		mWidgets.push_back(widget);
@@ -90,113 +20,4 @@ namespace CamelotEngine
 		if(findIter != end(mWidgets))
 			mWidgets.erase(findIter);
 	}
-
-	void GUIManager::updateDirtyMeshes()
-	{
-		struct TempMeshData
-		{
-			TempMeshData()
-				:numQuads(0), quadOffset(0), vertices(nullptr),
-				uvs(nullptr), indices(nullptr)
-			{ }
-
-			UINT32 numQuads;
-			UINT32 quadOffset;
-			Vector2* vertices;
-			Vector2* uvs;
-			UINT32* indices;
-			MaterialHandle material;
-		};
-
-		std::unordered_map<MeshGroupID, TempMeshData, MeshGroupID::HashFunction, MeshGroupID::EqualFunction> meshDataPerRenderElement;
-
-		// Group meshes based on widget mesh ID and used materials
-		// Determine mesh sizes per group
-		for(auto& widget : mWidgets)
-		{
-			for(auto& elem : widget->mElements)
-			{
-				UINT32 numRenderElems = elem->getNumRenderElements();
-
-				for(UINT32 i = 0; i < numRenderElems; i++)
-				{
-					const MaterialHandle& mat = elem->getMaterial(i);
-
-					MeshGroupID meshGroup(widget->mMeshGroupID, mat->getInternalID()); // TODO - I group based on material ID. So if two widgets used exact copies of the same material
-																					   // this system won't detect it. Find a better way of determining material similarity?
-																					   
-
-					UINT32 numQuads = elem->getNumQuads(i);
-					meshDataPerRenderElement[meshGroup].numQuads += numQuads;
-					meshDataPerRenderElement[meshGroup].material = mat;
-				}
-			}
-		}
-
-		// Allocate buffers for each group
-		UINT32 numMeshes = 0;
-		for(auto& renderElem : meshDataPerRenderElement)
-		{
-			renderElem.second.vertices = new Vector2[renderElem.second.numQuads * 4];
-			renderElem.second.uvs = new Vector2[renderElem.second.numQuads * 4];
-			renderElem.second.indices = new UINT32[renderElem.second.numQuads * 6];
-			numMeshes++;
-		}
-
-		// Fill buffers for each group
-		for(auto& widget : mWidgets)
-		{
-			for(auto& elem : widget->mElements)
-			{
-				UINT32 numRenderElems = elem->getNumRenderElements();
-
-				for(UINT32 i = 0; i < numRenderElems; i++)
-				{
-					const MaterialHandle& mat = elem->getMaterial(i);
-					MeshGroupID meshGroup(widget->mMeshGroupID, mat->getInternalID()); 
-
-					Vector2* vertices = meshDataPerRenderElement[meshGroup].vertices;
-					Vector2* uvs = meshDataPerRenderElement[meshGroup].uvs;
-					UINT32* indices = meshDataPerRenderElement[meshGroup].indices;
-					UINT32 startingQuad = meshDataPerRenderElement[meshGroup].quadOffset;
-					UINT32 maxNumQuads = meshDataPerRenderElement[meshGroup].numQuads;
-
-					elem->fillBuffer(vertices, uvs, indices, startingQuad, maxNumQuads, i);
-
-					UINT32 numQuads = elem->getNumQuads(i);
-					meshDataPerRenderElement[meshGroup].quadOffset += numQuads;
-				}
-			}
-		}
-
-		// Update meshes
-		for(UINT32 i = (UINT32)mMeshes.size(); i < numMeshes; i++)
-		{
-			MeshHandle newMesh = Mesh::create();
-			mMeshes.push_back(newMesh);
-		}
-
-		while((UINT32)mMeshes.size() > numMeshes && (UINT32)mMeshes.size() > 0)
-		{
-			mMeshes.erase(mMeshes.end() - 1); // TODO: Destroying meshes as soon as they're not used might be a perf. penalty?
-											  //  Maybe instead pool the meshes and only actually remove them when a certain number of unused ones exists.
-		}
-
-		mMaterials.resize(numMeshes);
-
-		UINT32 meshIdx = 0;
-		for(auto& renderElem : meshDataPerRenderElement)
-		{
-			std::shared_ptr<MeshData> meshData(new MeshData());
-
-			meshData->setPositions(renderElem.second.vertices, renderElem.second.numQuads * 4);
-			meshData->setUV0(renderElem.second.uvs, renderElem.second.numQuads * 4);
-			meshData->setIndices(renderElem.second.indices, renderElem.second.numQuads * 6);
-
-			mMeshes[meshIdx]->setMeshData(meshData);
-			mMaterials[meshIdx] = renderElem.second.material;
-
-			meshIdx++;
-		}
-	}
 }

+ 137 - 2
CamelotCore/Source/CmGUIWidget.cpp

@@ -2,13 +2,17 @@
 #include "CmGUIManager.h"
 #include "CmGUISkin.h"
 #include "CmGUILabel.h"
+#include "CmDeferredRenderContext.h"
+#include "CmMaterial.h"
+#include "CmPass.h"
+#include "CmMesh.h"
 
 namespace CamelotEngine
 {
 	GUISkin GUIWidget::DefaultSkin;
 
 	GUIWidget::GUIWidget(GameObjectPtr parent)
-		:Component(parent), mSkin(nullptr), mMeshGroupID(0)
+		:Overlay(parent), mSkin(nullptr)
 	{
 		GUIManager::instance().registerWidget(this);
 	}
@@ -41,8 +45,139 @@ namespace CamelotEngine
 			return &DefaultSkin;
 	}
 
-	void GUIWidget::update()
+	void GUIWidget::updateMeshes() const
 	{
+		struct TempMeshData
+		{
+			TempMeshData()
+				:numQuads(0), quadOffset(0), vertices(nullptr),
+				uvs(nullptr), indices(nullptr)
+			{ }
+
+			UINT32 numQuads;
+			UINT32 quadOffset;
+			Vector2* vertices;
+			Vector2* uvs;
+			UINT32* indices;
+			MaterialHandle material;
+		};
+
+		std::unordered_map<UINT64, TempMeshData> meshDataPerRenderElement;
+
+		// Group meshes based on used materials
+		// Determine mesh sizes per group
+		for(auto& elem : mElements)
+		{
+			UINT32 numRenderElems = elem->getNumRenderElements();
 
+			for(UINT32 i = 0; i < numRenderElems; i++)
+			{
+				const MaterialHandle& mat = elem->getMaterial(i);
+
+				UINT64 meshGroup = mat->getInternalID(); // TODO - I group based on material ID. So if two widgets used exact copies of the same material
+				// this system won't detect it. Find a better way of determining material similarity?
+
+				UINT32 numQuads = elem->getNumQuads(i);
+				meshDataPerRenderElement[meshGroup].numQuads += numQuads;
+				meshDataPerRenderElement[meshGroup].material = mat;
+			}
+		}
+
+		// Allocate buffers for each group
+		UINT32 numMeshes = 0;
+		for(auto& renderElem : meshDataPerRenderElement)
+		{
+			renderElem.second.vertices = new Vector2[renderElem.second.numQuads * 4];
+			renderElem.second.uvs = new Vector2[renderElem.second.numQuads * 4];
+			renderElem.second.indices = new UINT32[renderElem.second.numQuads * 6];
+			numMeshes++;
+		}
+
+		// Fill buffers for each group
+		for(auto& elem : mElements)
+		{
+			UINT32 numRenderElems = elem->getNumRenderElements();
+
+			for(UINT32 i = 0; i < numRenderElems; i++)
+			{
+				const MaterialHandle& mat = elem->getMaterial(i);
+				UINT64 meshGroup = mat->getInternalID(); 
+
+				Vector2* vertices = meshDataPerRenderElement[meshGroup].vertices;
+				Vector2* uvs = meshDataPerRenderElement[meshGroup].uvs;
+				UINT32* indices = meshDataPerRenderElement[meshGroup].indices;
+				UINT32 startingQuad = meshDataPerRenderElement[meshGroup].quadOffset;
+				UINT32 maxNumQuads = meshDataPerRenderElement[meshGroup].numQuads;
+
+				elem->fillBuffer(vertices, uvs, indices, startingQuad, maxNumQuads, i);
+
+				UINT32 numQuads = elem->getNumQuads(i);
+				meshDataPerRenderElement[meshGroup].quadOffset += numQuads;
+			}
+		}
+
+		// Update meshes
+		for(UINT32 i = (UINT32)mCachedMeshes.size(); i < numMeshes; i++)
+		{
+			MeshHandle newMesh = Mesh::create();
+			mCachedMeshes.push_back(newMesh);
+		}
+
+		while((UINT32)mCachedMeshes.size() > numMeshes && (UINT32)mCachedMeshes.size() > 0)
+		{
+			mCachedMeshes.erase(mCachedMeshes.end() - 1); // TODO: Destroying meshes as soon as they're not used might be a perf. penalty?
+			//  Maybe instead pool the meshes and only actually remove them when a certain number of unused ones exists.
+		}
+
+		mCachedMaterials.resize(numMeshes);
+
+		UINT32 meshIdx = 0;
+		for(auto& renderElem : meshDataPerRenderElement)
+		{
+			std::shared_ptr<MeshData> meshData(new MeshData());
+
+			meshData->setPositions(renderElem.second.vertices, renderElem.second.numQuads * 4);
+			meshData->setUV0(renderElem.second.uvs, renderElem.second.numQuads * 4);
+			meshData->setIndices(renderElem.second.indices, renderElem.second.numQuads * 6);
+
+			mCachedMeshes[meshIdx]->setMeshData(meshData);
+			mCachedMaterials[meshIdx] = renderElem.second.material;
+
+			meshIdx++;
+		}
+	}
+
+	void GUIWidget::render(const CameraPtr& camera, DeferredRenderContextPtr& renderContext) const
+	{
+		// Mesh is re-created every frame. There might be a better approach that only recreates it upon change,
+		// but for now it seems like too much hassle for something like GUI that is pretty dynamic anyway.
+		updateMeshes();
+
+		// Render the meshes
+		UINT32 meshIdx = 0;
+		for(auto& mesh : mCachedMeshes)
+		{
+			MaterialHandle material = mCachedMaterials[meshIdx];
+
+			if(material == nullptr || !material.isLoaded())
+				continue;
+
+			if(mesh == nullptr || !mesh.isLoaded())
+				continue;
+
+			// TODO - Set current viewport resolution so that GUI can be rendered properly
+			for(UINT32 i = 0; i < material->getNumPasses(); i++)
+			{
+				PassPtr pass = material->getPass(i);
+				pass->activate(renderContext);
+
+				PassParametersPtr paramsPtr = material->getPassParameters(i);
+				pass->bindParameters(renderContext, paramsPtr);
+
+				renderContext->render(mesh->getRenderOperation());
+			}
+
+			meshIdx++;
+		}
 	}
 }

+ 16 - 0
CamelotCore/Source/CmOverlay.cpp

@@ -0,0 +1,16 @@
+#include "CmOverlay.h"
+#include "CmOverlayManager.h"
+
+namespace CamelotEngine
+{
+	Overlay::Overlay(GameObjectPtr parent)
+		:Component(parent)
+	{
+
+	}
+
+	Overlay::~Overlay()
+	{
+		OverlayManager::instance().detachOverlayFromAll(this);
+	}
+}

+ 37 - 0
CamelotCore/Source/CmOverlayManager.cpp

@@ -0,0 +1,37 @@
+#include "CmOverlayManager.h"
+#include "CmCamera.h"
+#include "CmOverlay.h"
+
+namespace CamelotEngine
+{
+	void OverlayManager::render(const CameraPtr& camera, DeferredRenderContextPtr& renderContext) const
+	{
+		auto overlays = mOverlaysPerCamera.find(camera.get());
+
+		if(overlays == mOverlaysPerCamera.end())
+			return;
+
+		for(auto& overlay : overlays->second)
+		{
+			overlay->render(camera, renderContext);
+		}
+	}
+
+	void OverlayManager::attachOverlay(const CameraPtr& camera, const Overlay* overlay)
+	{
+		mOverlaysPerCamera[camera.get()].insert(overlay);
+	}
+
+	void OverlayManager::detachOverlay(const CameraPtr& camera, const Overlay* overlay)
+	{
+		mOverlaysPerCamera[camera.get()].erase(overlay);
+	}
+
+	void OverlayManager::detachOverlayFromAll(const Overlay* overlay)
+	{
+		for(auto& overlays : mOverlaysPerCamera)
+		{
+			overlays.second.erase(overlay);
+		}
+	}
+}

+ 86 - 0
CamelotCore/Source/CmPass.cpp

@@ -3,6 +3,8 @@
 #include "CmBlendState.h"
 #include "CmDepthStencilState.h"
 #include "CmPassRTTI.h"
+#include "CmDeferredRenderContext.h"
+#include "CmMaterial.h"
 #include "CmException.h"
 
 namespace CamelotEngine
@@ -103,6 +105,90 @@ namespace CamelotEngine
 		return mStencilRefValue;
 	}
 	//----------------------------------------------------------------------
+	void Pass::activate(DeferredRenderContextPtr& renderContext) const
+	{
+		GpuProgramHandle vertProgram = getVertexProgram();
+		if(vertProgram)
+			renderContext->bindGpuProgram(vertProgram);
+		else
+			renderContext->unbindGpuProgram(GPT_VERTEX_PROGRAM);
+
+		GpuProgramHandle fragProgram = getFragmentProgram();
+		if(fragProgram)
+			renderContext->bindGpuProgram(fragProgram);
+		else
+			renderContext->unbindGpuProgram(GPT_FRAGMENT_PROGRAM);
+
+		GpuProgramHandle geomProgram = getGeometryProgram();
+		if(geomProgram)
+			renderContext->bindGpuProgram(geomProgram);
+		else
+			renderContext->unbindGpuProgram(GPT_GEOMETRY_PROGRAM);
+
+		GpuProgramHandle hullProgram = getHullProgram();
+		if(hullProgram)
+			renderContext->bindGpuProgram(hullProgram);
+		else
+			renderContext->unbindGpuProgram(GPT_HULL_PROGRAM);
+
+		GpuProgramHandle domainProgram = getDomainProgram();
+		if(domainProgram)
+			renderContext->bindGpuProgram(domainProgram);
+		else
+			renderContext->unbindGpuProgram(GPT_DOMAIN_PROGRAM);
+
+		// TODO - Try to limit amount of state changes, if previous state is already the same (especially with textures)
+
+		// TODO: Disable remaining texture units
+		//renderSystem->_disableTextureUnitsFrom(pass->getNumTextures());
+
+		// Set up non-texture related pass settings
+		BlendStateHandle blendState = getBlendState();
+		if(blendState != nullptr)
+			renderContext->setBlendState(blendState.getInternalPtr());
+		else
+			renderContext->setBlendState(BlendState::getDefault());
+
+		DepthStencilStateHandle depthStancilState = getDepthStencilState();
+		if(depthStancilState != nullptr)
+			renderContext->setDepthStencilState(depthStancilState.getInternalPtr(), getStencilRefValue());
+		else
+			renderContext->setDepthStencilState(DepthStencilState::getDefault(), getStencilRefValue());
+
+		RasterizerStateHandle rasterizerState = getRasterizerState();
+		if(rasterizerState != nullptr)
+			renderContext->setRasterizerState(rasterizerState.getInternalPtr());
+		else
+			renderContext->setRasterizerState(RasterizerState::getDefault());
+	}
+	//----------------------------------------------------------------------
+	void Pass::bindParameters(DeferredRenderContextPtr& renderContext, const PassParametersPtr& params) const
+	{
+		GpuProgramHandle vertProgram = getVertexProgram();
+		if(vertProgram)
+			renderContext->bindGpuParams(GPT_VERTEX_PROGRAM, params->mVertParams);
+
+		GpuProgramHandle fragProgram = getFragmentProgram();
+		if(fragProgram)
+			renderContext->bindGpuParams(GPT_FRAGMENT_PROGRAM, params->mFragParams);
+
+		GpuProgramHandle geomProgram = getGeometryProgram();
+		if(geomProgram)
+			renderContext->bindGpuParams(GPT_GEOMETRY_PROGRAM, params->mGeomParams);
+
+		GpuProgramHandle hullProgram = getHullProgram();
+		if(hullProgram)
+			renderContext->bindGpuParams(GPT_HULL_PROGRAM, params->mHullParams);
+
+		GpuProgramHandle domainProgram = getDomainProgram();
+		if(domainProgram)
+			renderContext->bindGpuParams(GPT_DOMAIN_PROGRAM, params->mDomainParams);
+
+		GpuProgramHandle computeProgram = getComputeProgram();
+		if(computeProgram)
+			renderContext->bindGpuParams(GPT_COMPUTE_PROGRAM, params->mComputeParams);
+	}
+	//----------------------------------------------------------------------
 	RTTITypeBase* Pass::getRTTIStatic()
 	{
 		return PassRTTI::instance();

+ 30 - 10
CamelotCore/Source/CmTextSprite.cpp

@@ -249,10 +249,11 @@ namespace CamelotEngine
 	{
 		const FontData* fontData = getFontData();
 
-		clearMesh();
-
 		if(fontData == nullptr)
+		{
+			clearMesh();
 			return;
+		}
 
 		if(fontData->size != mFontSize)
 		{
@@ -269,6 +270,7 @@ namespace CamelotEngine
 		UINT32 curHeight = fontData->fontDesc.lineHeight;
 		UINT32 charIdx = 0;
 
+		vector<UINT32>::type newRenderElemSizes;
 		while(true)
 		{
 			if(charIdx >= mText.size())
@@ -294,10 +296,10 @@ namespace CamelotEngine
 
 			if(charDesc.charId != SPACE_CHAR)
 			{
-				if(charDesc.page >= (UINT32)mCachedRenderElements.size())
-					mCachedRenderElements.resize(charDesc.page + 1);
+				if(charDesc.page >= (UINT32)newRenderElemSizes.size())
+					newRenderElemSizes.resize(charDesc.page + 1);
 
-				mCachedRenderElements[charDesc.page].numQuads++;
+				newRenderElemSizes[charDesc.page]++;
 			}
 
 			if(widthIsLimited && curLine->getWidth() > mWidth)
@@ -370,13 +372,31 @@ namespace CamelotEngine
 		}
 
 		// Actually generate a mesh
+		if(mCachedRenderElements.size() < newRenderElemSizes.size())
+			mCachedRenderElements.resize(newRenderElemSizes.size());
+
 		UINT32 texPage = 0;
-		for(auto& renderElem : mCachedRenderElements)
+		for(auto& cachedElem : mCachedRenderElements)
 		{
-			renderElem.vertices = new Vector2[renderElem.numQuads * 4];
-			renderElem.uvs = new Vector2[renderElem.numQuads * 4];
-			renderElem.indexes = new UINT32[renderElem.numQuads * 6];
-			renderElem.material = GUIMaterialManager::instance().requestTextMaterial(fontData->texturePages[texPage]);
+			UINT32 newNumQuads = newRenderElemSizes[texPage];
+			if(newNumQuads != cachedElem.numQuads)
+			{
+				if(cachedElem.vertices != nullptr) delete[] cachedElem.vertices;
+				if(cachedElem.uvs != nullptr) delete[] cachedElem.uvs;
+				if(cachedElem.indexes != nullptr) delete[] cachedElem.indexes;
+
+				cachedElem.vertices = new Vector2[newNumQuads * 4];
+				cachedElem.uvs = new Vector2[newNumQuads * 4];
+				cachedElem.indexes = new UINT32[newNumQuads * 6];
+				cachedElem.numQuads = newNumQuads;
+			}
+
+			MaterialHandle newMaterial = GUIMaterialManager::instance().requestTextMaterial(fontData->texturePages[texPage]);
+			if(cachedElem.material != nullptr)
+				GUIMaterialManager::instance().releaseMaterial(newMaterial);
+
+			cachedElem.material = newMaterial;
+
 			texPage++;
 		}
 

+ 0 - 11
CamelotForwardRenderer/Include/CmForwardRenderer.h

@@ -17,17 +17,6 @@ namespace CamelotEngine
 		virtual void render(const CameraPtr camera);
 
 	protected:
-		PassPtr mActivePass;
 		RenderCommandBuffer* mCommandBuffer;
-
-		/**
-		 * @brief	Overriden from Renderer
-		 */
-		virtual void setPass(PassPtr pass);
-
-		/**
-		 * @brief	Overriden from Renderer
-		 */
-		virtual void setPassParameters(PassParametersPtr params);
 	};
 }

+ 7 - 105
CamelotForwardRenderer/Source/CmForwardRenderer.cpp

@@ -12,6 +12,7 @@
 #include "CmApplication.h"
 #include "CmViewport.h"
 #include "CmRenderTarget.h"
+#include "CmOverlayManager.h"
 
 namespace CamelotEngine
 {
@@ -84,121 +85,22 @@ namespace CamelotEngine
 
 			for(UINT32 i = 0; i < material->getNumPasses(); i++)
 			{
-				setPass(material->getPass(i));
+				PassPtr pass = material->getPass(i);
+				pass->activate(renderContext);
 
 				PassParametersPtr paramsPtr = material->getPassParameters(i);
-				setPassParameters(paramsPtr);
+				pass->bindParameters(renderContext, paramsPtr);
 
 				renderContext->render(mesh->getRenderOperation());
 			}
 		}
 
+		// Render overlays for this camera
+		OverlayManager::instance().render(camera, renderContext);
+
 		renderContext->endFrame();
 
 		// TODO - Sort renderables
 		// Render them
 	}
-
-	void ForwardRenderer::setPass(PassPtr pass)
-	{
-		// TODO - When applying passes, don't re-apply states that are already the same as from previous pass.
-		// Also check if it's maybe the exactly the same pass as the previous one.
-
-		mActivePass = pass;
-
-		DeferredRenderContextPtr renderContext = gApplication().getPrimaryRenderContext();
-
-		GpuProgramHandle vertProgram = pass->getVertexProgram();
-		if(vertProgram)
-		{
-			renderContext->bindGpuProgram(vertProgram);
-		}
-		else
-		{
-			//if(renderSystem->isGpuProgramBound(GPT_VERTEX_PROGRAM))
-				renderContext->unbindGpuProgram(GPT_VERTEX_PROGRAM);
-		}
-
-		GpuProgramHandle fragProgram = pass->getFragmentProgram();
-		if(fragProgram)
-		{
-			renderContext->bindGpuProgram(fragProgram);
-		}
-		else
-		{
-			//if(renderSystem->isGpuProgramBound(GPT_FRAGMENT_PROGRAM))
-				renderContext->unbindGpuProgram(GPT_FRAGMENT_PROGRAM);
-		}
-
-		GpuProgramHandle geomProgram = pass->getGeometryProgram();
-		if(geomProgram)
-		{
-			renderContext->bindGpuProgram(geomProgram);
-		}	
-		else
-		{
-			//if(renderSystem->isGpuProgramBound(GPT_GEOMETRY_PROGRAM))
-				renderContext->unbindGpuProgram(GPT_GEOMETRY_PROGRAM);
-		}
-
-		// The rest of the settings are the same no matter whether we use programs or not
-		BlendStateHandle blendState = pass->getBlendState();
-		if(blendState != nullptr)
-			renderContext->setBlendState(blendState.getInternalPtr());
-		else
-			renderContext->setBlendState(BlendState::getDefault());
-		
-		// TODO - Try to limit amount of state changes, if previous state is already the same (especially with textures)
-
-		// TODO: Disable remaining texture units
-		//renderSystem->_disableTextureUnitsFrom(pass->getNumTextures());
-
-
-		// Set up non-texture related material settings
-		// Stencil & depth buffer settings
-		DepthStencilStateHandle depthStancilState = pass->getDepthStencilState();
-		if(depthStancilState != nullptr)
-			renderContext->setDepthStencilState(depthStancilState.getInternalPtr(), pass->getStencilRefValue());
-		else
-			renderContext->setDepthStencilState(DepthStencilState::getDefault(), pass->getStencilRefValue());
-
-		RasterizerStateHandle rasterizerState = pass->getRasterizerState();
-		if(rasterizerState != nullptr)
-			renderContext->setRasterizerState(rasterizerState.getInternalPtr());
-		else
-			renderContext->setRasterizerState(RasterizerState::getDefault());
-	}
-
-	void ForwardRenderer::setPassParameters(PassParametersPtr params)
-	{
-		// TODO - When applying passes, don't re-apply states that are already the same as from previous pass.
-		DeferredRenderContextPtr renderContext = gApplication().getPrimaryRenderContext();
-
-		if(mActivePass == nullptr)
-			CM_EXCEPT(InternalErrorException, "Trying to set pass parameters, but no pass is set.");
-
-		GpuProgramHandle vertProgram = mActivePass->getVertexProgram();
-		if(vertProgram)
-			renderContext->bindGpuParams(GPT_VERTEX_PROGRAM, params->mVertParams);
-
-		GpuProgramHandle fragProgram = mActivePass->getFragmentProgram();
-		if(fragProgram)
-			renderContext->bindGpuParams(GPT_FRAGMENT_PROGRAM, params->mFragParams);
-
-		GpuProgramHandle geomProgram = mActivePass->getGeometryProgram();
-		if(geomProgram)
-			renderContext->bindGpuParams(GPT_GEOMETRY_PROGRAM, params->mGeomParams);
-
-		GpuProgramHandle hullProgram = mActivePass->getHullProgram();
-		if(hullProgram)
-			renderContext->bindGpuParams(GPT_HULL_PROGRAM, params->mHullParams);
-
-		GpuProgramHandle domainProgram = mActivePass->getDomainProgram();
-		if(domainProgram)
-			renderContext->bindGpuParams(GPT_DOMAIN_PROGRAM, params->mDomainParams);
-
-		GpuProgramHandle computeProgram = mActivePass->getComputeProgram();
-		if(computeProgram)
-			renderContext->bindGpuParams(GPT_COMPUTE_PROGRAM, params->mComputeParams);
-	}
 }

+ 2 - 0
TODO.txt

@@ -25,6 +25,8 @@ Add TextUtil class
  - Ability to calculate a size of a line (will need this if I want to add "..." to text that doesn't fit)
  - Maybe even move line and word classes to it
 
+ When rendering GUIWidget its individual elements need to be sorted based on Z depth so the transparency works as expected
+
 Immediate TODO:
  IMPORTANT - GUIElement in its constructor calls a virtual method to detect actual type, which won't work