瀏覽代碼

More work on GUI

Marko Pintera 12 年之前
父節點
當前提交
9476a511d7

+ 3 - 2
CamelotCore/CamelotCore.vcxproj

@@ -212,11 +212,12 @@
     <ClInclude Include="Include\CmGpuProgramManager.h" />
     <ClInclude Include="Include\CmGpuProgramParams.h" />
     <ClInclude Include="Include\CmGpuProgramRTTI.h" />
-    <ClInclude Include="Include\CmGUI.h" />
     <ClInclude Include="Include\CmGUIElement.h" />
+    <ClInclude Include="Include\CmGUIElementStyle.h" />
     <ClInclude Include="Include\CmGUILabel.h" />
     <ClInclude Include="Include\CmGUIManager.h" />
     <ClInclude Include="Include\CmGUIMaterialManager.h" />
+    <ClInclude Include="Include\CmGUISkin.h" />
     <ClInclude Include="Include\CmGUIWidget.h" />
     <ClInclude Include="Include\CmHardwareBuffer.h" />
     <ClInclude Include="Include\CmHardwareBufferManager.h" />
@@ -323,11 +324,11 @@
     <ClCompile Include="Source\CmGpuProgramImportOptions.cpp" />
     <ClCompile Include="Source\CmGpuProgramManager.cpp" />
     <ClCompile Include="Source\CmGpuProgramParams.cpp" />
-    <ClCompile Include="Source\CmGUI.cpp" />
     <ClCompile Include="Source\CmGUIElement.cpp" />
     <ClCompile Include="Source\CmGUILabel.cpp" />
     <ClCompile Include="Source\CmGUIManager.cpp" />
     <ClCompile Include="Source\CmGUIMaterialManager.cpp" />
+    <ClCompile Include="Source\CmGUISkin.cpp" />
     <ClCompile Include="Source\CmGUIWidget.cpp" />
     <ClCompile Include="Source\CmHardwareBufferManager.cpp" />
     <ClCompile Include="Source\CmImportOptions.cpp" />

+ 9 - 6
CamelotCore/CamelotCore.vcxproj.filters

@@ -438,9 +438,6 @@
     <ClInclude Include="Include\CmImageSprite.h">
       <Filter>Header Files\2D</Filter>
     </ClInclude>
-    <ClInclude Include="Include\CmGUI.h">
-      <Filter>Header Files\GUI</Filter>
-    </ClInclude>
     <ClInclude Include="Include\CmGUIWidget.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
@@ -462,6 +459,12 @@
     <ClInclude Include="Include\CmBuiltinMaterialManager.h">
       <Filter>Source Files\Material</Filter>
     </ClInclude>
+    <ClInclude Include="Include\CmGUIElementStyle.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\CmGUISkin.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\CamelotRenderer.cpp">
@@ -698,9 +701,6 @@
     <ClCompile Include="Source\CmGUIWidget.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
-    <ClCompile Include="Source\CmGUI.cpp">
-      <Filter>Source Files\GUI</Filter>
-    </ClCompile>
     <ClCompile Include="Source\CmGUILabel.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
@@ -713,5 +713,8 @@
     <ClCompile Include="Source\CmGUIMaterialManager.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
+    <ClCompile Include="Source\CmGUISkin.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 0 - 11
CamelotCore/Include/CmGUI.h

@@ -1,11 +0,0 @@
-#pragma once
-
-#include "CmPrerequisites.h"
-
-namespace CamelotEngine
-{
-	class CM_EXPORT GUI
-	{
-
-	};
-}

+ 12 - 7
CamelotCore/Include/CmGUIElement.h

@@ -8,15 +8,9 @@ namespace CamelotEngine
 	class CM_EXPORT GUIElement
 	{
 	public:
-		GUIElement();
+		GUIElement(GUIWidget* parent, const GUISkin* skin);
 		~GUIElement();
 
-		//  onMouseMove
-		//	onMousePress
-		//	onMouseReleased
-		//	onKeyPressed
-		//	onKeyReleased
-	protected:
 		/**
 		 * @brief	Returns the number of separate render elements in the GUI element.
 		 * 			
@@ -65,5 +59,16 @@ namespace CamelotEngine
 		 * @param	renderElementIdx	Zero-based index of the render element.
 		 */
 		virtual void fillBuffer(Vector2* vertices, Vector2* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, UINT32 renderElementIdx) const = 0;
+
+		//  onMouseMove
+		//	onMousePress
+		//	onMouseReleased
+		//	onKeyPressed
+		//	onKeyReleased
+	protected:
+		GUIWidget* mParent;
+		const GUIElementStyle* mStyle;
+
+		virtual const String& getGUITypeName() = 0;
 	};
 }

+ 18 - 0
CamelotCore/Include/CmGUIElementStyle.h

@@ -0,0 +1,18 @@
+#pragma once
+
+#include "CmPrerequisites.h"
+
+namespace CamelotEngine
+{
+	struct CM_EXPORT GUIElementStyle
+	{
+		GUIElementStyle()
+			:fontSize(8)
+		{
+
+		}
+
+		FontPtr font;
+		UINT32 fontSize;
+	};
+}

+ 5 - 4
CamelotCore/Include/CmGUILabel.h

@@ -28,11 +28,12 @@ namespace CamelotEngine
 		 */
 		virtual void fillBuffer(Vector2* vertices, Vector2* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, UINT32 renderElementIdx) const;
 	private:
-		friend class GUI;
-
-		GUILabel(const String& text, const FontPtr& font, UINT32 fontSize);
-
 		TextSprite* mTextSprite;
 		String mText;
+
+		friend class GUIWidget;
+		GUILabel(GUIWidget* parent, const String& text, const GUISkin* skin);
+
+		virtual const String& getGUITypeName();
 	};
 }

+ 6 - 1
CamelotCore/Include/CmGUIManager.h

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

+ 19 - 0
CamelotCore/Include/CmGUISkin.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include "CmPrerequisites.h"
+#include "CmGUIElementStyle.h"
+
+namespace CamelotEngine
+{
+	class CM_EXPORT GUISkin
+	{
+	public:
+		const GUIElementStyle* getStyle(const String& guiElemType) const;
+		void setStyle(const String& guiElemType, const GUIElementStyle& style);
+
+	private:
+		static GUIElementStyle DefaultStyle;
+
+		unordered_map<std::string, GUIElementStyle>::type mStyles;
+	};
+}

+ 17 - 2
CamelotCore/Include/CmGUIWidget.h

@@ -12,11 +12,26 @@ namespace CamelotEngine
 
 		virtual void update();
 
+	protected:
+		GUILabel* addLabel(const String& text);
+
+		const GUISkin* getGUISkin() const;
+
 	private:
 		friend class GameObject;
-
-		GUI* mGUI;
+		friend class GUIManager;
 
 		GUIWidget(GameObjectPtr parent);
+
+		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;
+		const GUISkin* mSkin;
+		static GUISkin DefaultSkin;
 	};
 }

+ 2 - 1
CamelotCore/Include/CmPrerequisites.h

@@ -167,7 +167,8 @@ namespace CamelotEngine {
 	class GUIWidget;
 	class GUIElement;
 	class GUILabel;
-	class GUI;
+	struct GUIElementStyle;
+	class GUISkin;
 	// RTTI
 	class MeshRTTI;
 	// Desc structs

+ 6 - 0
CamelotCore/Include/CmRenderOperation.h

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

+ 0 - 6
CamelotCore/Source/CmGUI.cpp

@@ -1,6 +0,0 @@
-#include "CmGUI.h"
-
-namespace CamelotEngine
-{
-
-}

+ 3 - 2
CamelotCore/Source/CmGUIElement.cpp

@@ -1,10 +1,11 @@
 #include "CmGUIElement.h"
+#include "CmGUISkin.h"
 
 namespace CamelotEngine
 {
-	GUIElement::GUIElement()
+	GUIElement::GUIElement(GUIWidget* parent, const GUISkin* skin)
+		:mParent(parent)
 	{
-
 	}
 
 	GUIElement::~GUIElement()

+ 14 - 3
CamelotCore/Source/CmGUILabel.cpp

@@ -1,12 +1,17 @@
 #include "CmGUILabel.h"
+#include "CmGUIElementStyle.h"
 #include "CmTextSprite.h"
+#include "CmGUISkin.h"
 
 namespace CamelotEngine
 {
-	GUILabel::GUILabel(const String& text, const FontPtr& font, UINT32 fontSize)
-		:mText(text)
+	GUILabel::GUILabel(GUIWidget* parent, const String& text, const GUISkin* skin)
+		:GUIElement(parent, skin), mText(text)
 	{
-		mTextSprite = new TextSprite(text, font, fontSize);
+		// This is calling a virtual method but it's okay because we always want the one
+		// existing on this class.
+		mStyle = skin->getStyle(getGUITypeName());
+		mTextSprite = new TextSprite(text, mStyle->font, mStyle->fontSize);
 	}
 
 	UINT32 GUILabel::getNumRenderElements() const
@@ -28,4 +33,10 @@ namespace CamelotEngine
 	{
 		mTextSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, renderElementIdx);
 	}
+
+	const String& GUILabel::getGUITypeName()
+	{
+		static String typeName = "Label";
+		return typeName;
+	}
 }

+ 180 - 4
CamelotCore/Source/CmGUIManager.cpp

@@ -1,26 +1,202 @@
 #include "CmGUIManager.h"
+#include "CmGUIWidget.h"
+#include "CmGUIElement.h"
+#include "CmMaterial.h"
+#include "CmMeshData.h"
+#include "CmMesh.h"
+#include "CmUtil.h"
 
 namespace CamelotEngine
 {
-	void GUIManager::renderGUI()
+	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
+		// 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)
 	{
-		// TODO
+		mWidgets.push_back(widget);
 	}
 
 	void GUIManager::unregisterWidget(GUIWidget* widget)
 	{
-		// TODO
+		auto findIter = std::find(begin(mWidgets), end(mWidgets), widget);
+
+		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++;
+		}
 	}
 }

+ 25 - 0
CamelotCore/Source/CmGUISkin.cpp

@@ -0,0 +1,25 @@
+#include "CmGUISkin.h"
+#include "CmGUIElementStyle.h"
+#include "CmDebug.h"
+
+namespace CamelotEngine
+{
+	GUIElementStyle GUISkin::DefaultStyle;
+
+	const GUIElementStyle* GUISkin::getStyle(const String& guiElemType) const
+	{
+		auto iterFind = mStyles.find(guiElemType);
+
+		if(iterFind != mStyles.end())
+			return &iterFind->second;
+
+		LOGWRN("Cannot find GUI style with name: " + guiElemType + ". Returning default style.");
+
+		return &DefaultStyle;
+	}
+
+	void GUISkin::setStyle(const String& guiElemType, const GUIElementStyle& style)
+	{
+		mStyles[guiElemType] = style;
+	}
+}

+ 27 - 3
CamelotCore/Source/CmGUIWidget.cpp

@@ -1,11 +1,14 @@
 #include "CmGUIWidget.h"
-#include "CmGUI.h"
 #include "CmGUIManager.h"
+#include "CmGUISkin.h"
+#include "CmGUILabel.h"
 
 namespace CamelotEngine
 {
+	GUISkin GUIWidget::DefaultSkin;
+
 	GUIWidget::GUIWidget(GameObjectPtr parent)
-		:Component(parent), mGUI(new GUI())
+		:Component(parent), mSkin(nullptr), mMeshGroupID(0)
 	{
 		GUIManager::instance().registerWidget(this);
 	}
@@ -14,7 +17,28 @@ namespace CamelotEngine
 	{
 		GUIManager::instance().unregisterWidget(this);
 
-		delete mGUI;
+		for(auto& elem : mElements)
+		{
+			delete elem;
+		}
+
+		mElements.clear();
+	}
+
+	GUILabel* GUIWidget::addLabel(const String& text)
+	{
+		GUILabel* label = new GUILabel(this, text, getGUISkin());
+		mElements.push_back(label);
+
+		return label;
+	}
+
+	const GUISkin* GUIWidget::getGUISkin() const
+	{
+		if(mSkin != nullptr)
+			return mSkin;
+		else
+			return &DefaultSkin;
 	}
 
 	void GUIWidget::update()

+ 2 - 0
TODO.txt

@@ -26,6 +26,8 @@ Add TextUtil class
  - Maybe even move line and word classes to it
 
 Immediate TODO:
+ IMPORTANT - GUIElement in its constructor calls a virtual method to detect actual type, which won't work
+
 Implement image shader for D3D11BuiltinMaterialManager
 Improve text shader so it doesn't have resolution values hardcoded in
 Implement D3D9 and GL BuiltInMaterialManager