Просмотр исходного кода

A bunch of WIP GUI elements added

Marko Pintera 13 лет назад
Родитель
Сommit
d8fdf8d81c

+ 2 - 2
CamelotClient/CmTestTextSprite.cpp

@@ -29,7 +29,7 @@ namespace CamelotEngine
 		mTextSprite->setText(text);
 		mTextSprite->setFont(font.getInternalPtr(), fontSize);
 
-		UINT32 numTextFaces = mTextSprite->getNumFaces();
+		UINT32 numTextFaces = mTextSprite->getNumQuads(0);
 		UINT32 numVertices = numTextFaces * 4;
 		UINT32 numIndices = numTextFaces * 6;
 
@@ -41,7 +41,7 @@ namespace CamelotEngine
 
 		auto vec2Buffer = new Vector2[numVertices];
 
-		mTextSprite->fillBuffer(vec2Buffer, uvs, indices, 0, numTextFaces);
+		mTextSprite->fillBuffer(vec2Buffer, uvs, indices, 0, numTextFaces, 0);
 
 		for(UINT32 i = 0; i < numVertices; i++)
 			vertices[i] = Vector3(vec2Buffer[i].x, vec2Buffer[i].y, 0.0f);

+ 11 - 0
CamelotCore/CamelotCore.vcxproj

@@ -211,6 +211,11 @@
     <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\CmGUILabel.h" />
+    <ClInclude Include="Include\CmGUIManager.h" />
+    <ClInclude Include="Include\CmGUIWidget.h" />
     <ClInclude Include="Include\CmHardwareBuffer.h" />
     <ClInclude Include="Include\CmHardwareBufferManager.h" />
     <ClInclude Include="Include\CmImageSprite.h" />
@@ -222,6 +227,7 @@
     <ClInclude Include="Include\CmOcclusionQuery.h" />
     <ClInclude Include="Include\CmPixelBuffer.h" />
     <ClInclude Include="Include\CmGpuProgIncludeImporter.h" />
+    <ClInclude Include="Include\CmSpriteTexture.h" />
     <ClInclude Include="Include\CmTextureView.h" />
     <ClInclude Include="Include\CmVertexBuffer.h" />
     <ClInclude Include="Include\CmHighLevelGpuProgram.h" />
@@ -315,6 +321,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\CmGUIWidget.cpp" />
     <ClCompile Include="Source\CmHardwareBufferManager.cpp" />
     <ClCompile Include="Source\CmImportOptions.cpp" />
     <ClCompile Include="Source\CmIndexBuffer.cpp" />

+ 39 - 0
CamelotCore/CamelotCore.vcxproj.filters

@@ -94,6 +94,12 @@
     <Filter Include="Source Files\2D">
       <UniqueIdentifier>{500c1648-85c9-4410-86e3-c16d102b4347}</UniqueIdentifier>
     </Filter>
+    <Filter Include="Header Files\GUI">
+      <UniqueIdentifier>{0a7006f4-fd1d-49f9-92c5-b5c7b64c2578}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\GUI">
+      <UniqueIdentifier>{62806522-68be-448c-8669-b8b211de0e15}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Include\CmConfigOptionMap.h">
@@ -432,6 +438,24 @@
     <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>
+    <ClInclude Include="Include\CmGUILabel.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\CmGUIElement.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\CmGUIManager.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\CmSpriteTexture.h">
+      <Filter>Header Files\2D</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\CamelotRenderer.cpp">
@@ -665,5 +689,20 @@
     <ClCompile Include="Source\CmMeshDataRTTI.cpp">
       <Filter>Source Files\RTTI</Filter>
     </ClCompile>
+    <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>
+    <ClCompile Include="Source\CmGUIElement.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\CmGUIManager.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 11 - 0
CamelotCore/Include/CmGUI.h

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

+ 69 - 0
CamelotCore/Include/CmGUIElement.h

@@ -0,0 +1,69 @@
+#pragma once
+
+#include "CmPrerequisites.h"
+#include "CmRect.h"
+
+namespace CamelotEngine
+{
+	class CM_EXPORT GUIElement
+	{
+	public:
+		GUIElement();
+		~GUIElement();
+
+		//  onMouseMove
+		//	onMousePress
+		//	onMouseReleased
+		//	onKeyPressed
+		//	onKeyReleased
+	protected:
+		/**
+		 * @brief	Returns the number of separate render elements in the GUI element.
+		 * 			
+		 * @note	GUI system attempts to reduce the number of GUI meshes so it will group
+		 * 			sprites based on their material and textures. One render elements represents a group
+		 * 			of such sprites that share a material/texture.
+		 *
+		 * @return	The number render elements.
+		 */
+		virtual UINT32 getNumRenderElements() const = 0;
+
+		/**
+		 * @brief	Gets a material for the specified render element index.
+		 * 			
+		 * @see getNumRenderElements()
+		 * 		
+		 * @return	Handle to the material.
+		 */
+		virtual const MaterialHandle& getMaterial(UINT32 renderElementIdx) const = 0;
+
+		/**
+		 * @brief	Returns the number of quads that the specified render element will use. You will need this
+		 * 			value when creating the buffers before calling "fillBuffer.
+		 * 			
+		 * @see getNumRenderElements()
+		 * @see fillBuffer()
+		 * 		
+		 * @note	Number of vertices = Number of quads * 4
+		 *			Number of indices = Number of quads * 6
+		 *			
+		 * @return	Number of quads for the specified render element. 
+		 */
+		virtual UINT32 getNumQuads(UINT32 renderElementIdx) const = 0;
+
+		/**
+		 * @brief	Fill the pre-allocated vertex, uv and index buffers with the mesh data for the specified render element.
+		 *
+		 * @see getNumRenderElements()
+		 * @see	getNumQuads()
+		 * 		
+		 * @param   vertices			Previously allocated buffer where to store the vertices.
+		 * @param   uv					Previously allocated buffer where to store the uv coordinates.
+		 * @param   indices 			Previously allocated buffer where to store the indices.
+		 * @param	startingQuad		At which quad should the method start filling the buffer.
+		 * @param	maxNumQuads			Total number of quads the buffers were allocated for. Used only for memory safety.
+		 * @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;
+	};
+}

+ 38 - 0
CamelotCore/Include/CmGUILabel.h

@@ -0,0 +1,38 @@
+#pragma once
+
+#include "CmPrerequisites.h"
+#include "CmGUIElement.h"
+
+namespace CamelotEngine
+{
+	class CM_EXPORT GUILabel : public GUIElement
+	{
+	protected:
+		/**
+		 * @copydoc GUIElement::getNumRenderElements()
+		 */
+		virtual UINT32 getNumRenderElements() const;
+
+		/**
+		 * @copydoc GUIElement::getMaterial()
+		 */
+		virtual const MaterialHandle& getMaterial(UINT32 renderElementIdx) const;
+
+		/**
+		 * @copydoc GUIElement::getNumQuads()
+		 */
+		virtual UINT32 getNumQuads(UINT32 renderElementIdx) const;
+
+		/**
+		 * @copydoc GUIElement::fillBuffer()
+		 */
+		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;
+	};
+}

+ 22 - 0
CamelotCore/Include/CmGUIManager.h

@@ -0,0 +1,22 @@
+#pragma once
+
+#include "CmPrerequisites.h"
+#include "CmModule.h"
+
+namespace CamelotEngine
+{
+	/**
+	 * @brief	Manages the rendering of all GUI widgets in the scene. 
+	 */
+	class CM_EXPORT GUIManager : public Module<GUIManager>
+	{
+	public:
+		void registerWidget(GUIWidget* widget);
+		void unregisterWidget(GUIWidget* widget);
+
+		void renderGUI();
+
+	private:
+		void updateDirtyMeshes();
+	};
+}

+ 22 - 0
CamelotCore/Include/CmGUIWidget.h

@@ -0,0 +1,22 @@
+#pragma once
+
+#include "CmPrerequisites.h"
+#include "CmComponent.h"
+
+namespace CamelotEngine
+{
+	class CM_EXPORT GUIWidget : public Component
+	{
+	public:
+		~GUIWidget();
+
+		virtual void update();
+
+	private:
+		friend class GameObject;
+
+		GUI* mGUI;
+
+		GUIWidget(GameObjectPtr parent);
+	};
+}

+ 7 - 0
CamelotCore/Include/CmPrerequisites.h

@@ -148,6 +148,7 @@ namespace CamelotEngine {
 	class ImportOptions;
 	struct FontData;
 	class TextSprite;
+	class SpriteTexture;
 	// Asset import
 	class SpecificImporter;
 	class Importer;
@@ -161,6 +162,12 @@ namespace CamelotEngine {
 	class GameObject;
 	class Component;
 	class SceneManager;
+	// GUI
+	class GUIManager;
+	class GUIWidget;
+	class GUIElement;
+	class GUILabel;
+	class GUI;
 	// RTTI
 	class MeshRTTI;
 	// Desc structs

+ 62 - 9
CamelotCore/Include/CmSprite.h

@@ -19,6 +19,19 @@ namespace CamelotEngine
 		SA_BottomRight
 	};
 
+	struct SpriteRenderElement
+	{
+		SpriteRenderElement()
+			:vertices(nullptr), uvs(nullptr), indexes(nullptr), numQuads(0)
+		{ }
+
+		Vector2* vertices;
+		Vector2* uvs;
+		UINT32* indexes;
+		UINT32 numQuads;
+		MaterialHandle material;
+	};
+
 	class CM_EXPORT Sprite
 	{
 	public:
@@ -36,8 +49,51 @@ namespace CamelotEngine
 		Rect getClipRect() const { return mClipRect; }
 		SpriteAnchor getAnchor() const { return mAnchor; }
 
-		UINT32 fillBuffer(Vector2* vertices, Vector2* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads);
-		UINT32 getNumFaces();
+		/**
+		 * @brief	Returns the number of separate render elements in the sprite. Normally this is one, but some sprites
+		 * 			may consist of multiple materials, in which case each will require it's own mesh (render element)
+		 * 			
+		 * @return	The number render elements.
+		 */
+		UINT32 getNumRenderElements() const;
+
+		/**
+		 * @brief	Gets a material for the specified render element index.
+		 * 			
+		 * @see getNumRenderElements()
+		 * 		
+		 * @return	Handle to the material.
+		 */
+		const MaterialHandle& getMaterial(UINT32 renderElementIdx) const;
+
+		/**
+		 * @brief	Returns the number of quads that the specified render element will use. You will need this
+		 * 			value when creating the buffers before calling "fillBuffer".
+		 * 			
+		 * @see getNumRenderElements()
+		 * @see fillBuffer()
+		 * 		
+		 * @note	Number of vertices = Number of quads * 4
+		 *			Number of indices = Number of quads * 6
+		 *			
+		 * @return	Number of quads for the specified render element. 
+		 */
+		UINT32 getNumQuads(UINT32 renderElementIdx) const;
+
+		/**
+		 * @brief	Fill the pre-allocated vertex, uv and index buffers with the mesh data for the specified render element.
+		 *
+		 * @see getNumRenderElements()
+		 * @see	getNumQuads()
+		 * 		
+		 * @param   vertices			Previously allocated buffer where to store the vertices.
+		 * @param   uv					Previously allocated buffer where to store the uv coordinates.
+		 * @param   indices 			Previously allocated buffer where to store the indices.
+		 * @param	startingQuad		At which quad should the method start filling the buffer.
+		 * @param	maxNumQuads			Total number of quads the buffers were allocated for. Used only for memory safety.
+		 * @param	renderElementIdx	Zero-based index of the render element.
+		 */
+		UINT32 fillBuffer(Vector2* vertices, Vector2* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, UINT32 renderElementIdx) const;
 
 	protected:
 		Point mOffset;
@@ -45,16 +101,13 @@ namespace CamelotEngine
 		Rect mClipRect;
 		SpriteAnchor mAnchor;
 
-		bool mIsDirty;
-		Vector2* mVertices;
-		Vector2* mUVs;
-		UINT32* mIndexes;
-		UINT32 mNumMeshQuads;
+		mutable bool mIsDirty;
+		mutable vector<SpriteRenderElement>::type mCachedRenderElements;
 
 		void setDirty() { mIsDirty = true; }
 		Point getAnchorOffset() const;
 
-		virtual void updateMesh() = 0;
-		void clearMesh();
+		virtual void updateMesh() const = 0;
+		void clearMesh() const;
 	};
 }

+ 18 - 0
CamelotCore/Include/CmSpriteTexture.h

@@ -0,0 +1,18 @@
+#pragma once
+
+#include "CmPrerequisites.h"
+#include "CmVector2.h"
+
+namespace CamelotEngine
+{
+	class CM_EXPORT SpriteTexture
+	{
+	public:
+		const TextureHandle& getBaseTexture() const { return mBaseTexture; }
+
+	private:
+		TextureHandle mBaseTexture;
+		Vector2 mUVOffset;
+		Vector2 mUVScale;
+	};
+}

+ 1 - 1
CamelotCore/Include/CmTextSprite.h

@@ -42,7 +42,7 @@ namespace CamelotEngine
 		TextHorzAlign mHorzAlign;
 		TextVertAlign mVertAlign;
 
-		virtual void updateMesh();
+		virtual void updateMesh() const;
 
 		const FontData* getFontData() const;
 	};

+ 6 - 0
CamelotCore/Source/CmGUI.cpp

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

+ 14 - 0
CamelotCore/Source/CmGUIElement.cpp

@@ -0,0 +1,14 @@
+#include "CmGUIElement.h"
+
+namespace CamelotEngine
+{
+	GUIElement::GUIElement()
+	{
+
+	}
+
+	GUIElement::~GUIElement()
+	{
+
+	}
+}

+ 31 - 0
CamelotCore/Source/CmGUILabel.cpp

@@ -0,0 +1,31 @@
+#include "CmGUILabel.h"
+#include "CmTextSprite.h"
+
+namespace CamelotEngine
+{
+	GUILabel::GUILabel(const String& text, const FontPtr& font, UINT32 fontSize)
+		:mText(text)
+	{
+		mTextSprite = new TextSprite(text, font, fontSize);
+	}
+
+	UINT32 GUILabel::getNumRenderElements() const
+	{
+		return mTextSprite->getNumRenderElements();
+	}
+
+	const MaterialHandle& GUILabel::getMaterial(UINT32 renderElementIdx) const
+	{
+		return mTextSprite->getMaterial(renderElementIdx);
+	}
+
+	UINT32 GUILabel::getNumQuads(UINT32 renderElementIdx) const
+	{
+		return mTextSprite->getNumQuads(renderElementIdx);
+	}
+
+	void GUILabel::fillBuffer(Vector2* vertices, Vector2* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, UINT32 renderElementIdx) const
+	{
+		mTextSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, renderElementIdx);
+	}
+}

+ 26 - 0
CamelotCore/Source/CmGUIManager.cpp

@@ -0,0 +1,26 @@
+#include "CmGUIManager.h"
+
+namespace CamelotEngine
+{
+	void GUIManager::renderGUI()
+	{
+		updateDirtyMeshes();
+
+		// TODO
+	}
+
+	void GUIManager::registerWidget(GUIWidget* widget)
+	{
+		// TODO
+	}
+
+	void GUIManager::unregisterWidget(GUIWidget* widget)
+	{
+		// TODO
+	}
+
+	void GUIManager::updateDirtyMeshes()
+	{
+
+	}
+}

+ 24 - 0
CamelotCore/Source/CmGUIWidget.cpp

@@ -0,0 +1,24 @@
+#include "CmGUIWidget.h"
+#include "CmGUI.h"
+#include "CmGUIManager.h"
+
+namespace CamelotEngine
+{
+	GUIWidget::GUIWidget(GameObjectPtr parent)
+		:Component(parent), mGUI(new GUI())
+	{
+		GUIManager::instance().registerWidget(this);
+	}
+
+	GUIWidget::~GUIWidget()
+	{
+		GUIManager::instance().unregisterWidget(this);
+
+		delete mGUI;
+	}
+
+	void GUIWidget::update()
+	{
+
+	}
+}

+ 47 - 27
CamelotCore/Source/CmSprite.cpp

@@ -4,8 +4,7 @@
 namespace CamelotEngine
 {
 	Sprite::Sprite()
-		:mWidth(0), mHeight(0), mAnchor(SA_TopLeft), mIsDirty(true),
-		mVertices(nullptr), mUVs(nullptr), mIndexes(nullptr), mNumMeshQuads(0)
+		:mWidth(0), mHeight(0), mAnchor(SA_TopLeft), mIsDirty(true)
 	{
 
 	}
@@ -15,7 +14,7 @@ namespace CamelotEngine
 
 	}
 
-	UINT32 Sprite::fillBuffer(Vector2* vertices, Vector2* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads)
+	UINT32 Sprite::getNumRenderElements() const
 	{
 		if(mIsDirty)
 		{
@@ -23,26 +22,26 @@ namespace CamelotEngine
 			mIsDirty = false;
 		}
 
-		UINT32 startVert = startingQuad * 4;
-		UINT32 startIndex = startingQuad * 4;
-
-		UINT32 maxVertIdx = maxNumQuads * 4;
-		UINT32 maxIndexIdx = maxNumQuads * 6;
-
-		UINT32 mNumVertices = mNumMeshQuads * 4;
-		UINT32 mNumIndices = mNumMeshQuads * 6;
+		return (UINT32)mCachedRenderElements.size();
+	}
 
-		assert((startVert + mNumVertices) <= maxVertIdx);
-		assert((startIndex + mNumIndices) <= maxIndexIdx);
+	const MaterialHandle& Sprite::getMaterial(UINT32 renderElementIdx) const
+	{
+		return mCachedRenderElements.at(renderElementIdx).material;
+	}
 
-		memcpy(&vertices[startVert], mVertices, mNumVertices * sizeof(Vector2));
-		memcpy(&uv[startVert], mUVs, mNumVertices * sizeof(Vector2));
-		memcpy(&indices[startIndex], mIndexes, mNumIndices * sizeof(UINT32));
+	UINT32 Sprite::getNumQuads(UINT32 renderElementIdx) const
+	{
+		if(mIsDirty)
+		{
+			updateMesh();
+			mIsDirty = false;
+		}
 
-		return mNumMeshQuads;
+		return mCachedRenderElements.at(renderElementIdx).numQuads;
 	}
 
-	UINT32 Sprite::getNumFaces()
+	UINT32 Sprite::fillBuffer(Vector2* vertices, Vector2* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, UINT32 renderElementIdx) const
 	{
 		if(mIsDirty)
 		{
@@ -50,7 +49,25 @@ namespace CamelotEngine
 			mIsDirty = false;
 		}
 
-		return mNumMeshQuads;
+		auto renderElem = mCachedRenderElements.at(renderElementIdx);
+
+		UINT32 startVert = startingQuad * 4;
+		UINT32 startIndex = startingQuad * 4;
+
+		UINT32 maxVertIdx = maxNumQuads * 4;
+		UINT32 maxIndexIdx = maxNumQuads * 6;
+
+		UINT32 mNumVertices = renderElem.numQuads * 4;
+		UINT32 mNumIndices = renderElem.numQuads * 6;
+
+		assert((startVert + mNumVertices) <= maxVertIdx);
+		assert((startIndex + mNumIndices) <= maxIndexIdx);
+
+		memcpy(&vertices[startVert], renderElem.vertices, mNumVertices * sizeof(Vector2));
+		memcpy(&uv[startVert], renderElem.uvs, mNumVertices * sizeof(Vector2));
+		memcpy(&indices[startIndex], renderElem.indexes, mNumIndices * sizeof(UINT32));
+
+		return renderElem.numQuads;
 	}
 
 	Point Sprite::getAnchorOffset() const
@@ -80,17 +97,20 @@ namespace CamelotEngine
 		return Point();
 	}
 
-	void Sprite::clearMesh()
+	void Sprite::clearMesh() const
 	{
-		if(mVertices != nullptr)
-			delete[] mVertices;
+		for(auto& renderElem : mCachedRenderElements)
+		{
+			if(renderElem.vertices != nullptr)
+				delete[] renderElem.vertices;
 
-		if(mUVs != nullptr)
-			delete[] mUVs;
+			if(renderElem.uvs != nullptr)
+				delete[] renderElem.uvs;
 
-		if(mIndexes != nullptr)
-			delete[] mIndexes;
+			if(renderElem.indexes != nullptr)
+				delete[] renderElem.indexes;
+		}
 
-		mNumMeshQuads = 0;
+		mCachedRenderElements.clear();
 	}
 }

+ 39 - 42
CamelotCore/Source/CmTextSprite.cpp

@@ -154,14 +154,8 @@ namespace CamelotEngine
 		Point getPosition() const { return mPosition; }
 		void setPosition(const Point& pos) { mPosition = pos; }
 
-		UINT32 fillBuffer(Vector2* vertices, Vector2* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, const FontData& fontData)
+		void fillBuffer(const vector<SpriteRenderElement>::type& renderElements, vector<UINT32>::type& faceOffsets, const FontData& fontData)
 		{
-			UINT32 curVert = startingQuad * 4;
-			UINT32 curIndex = startingQuad * 6;
-
-			UINT32 maxVertIdx = maxNumQuads * 4;
-			UINT32 maxIndexIdx = maxNumQuads * 6;
-
 			UINT32 penX = mPosition.x;
 			UINT32 baselineY = mPosition.y + fontData.fontDesc.baselineOffset;
 			for(auto wordIter = mWords.begin(); wordIter != mWords.end(); ++wordIter)
@@ -176,33 +170,34 @@ namespace CamelotEngine
 					UINT32 kerning = 0;
 					for(auto charIter = chars.begin(); charIter != chars.end(); ++charIter)
 					{
-						assert(curVert < maxVertIdx);
-						assert(curIndex < maxIndexIdx);
-
 						INT32 curX = penX + charIter->xOffset;
-						INT32 curY = -(baselineY - charIter->yOffset);
+						INT32 curY = -((INT32)baselineY - charIter->yOffset);
+
+						UINT32 curVert = faceOffsets[charIter->page] * 4;
+						UINT32 curIndex = faceOffsets[charIter->page] * 6;
 
-						vertices[curVert + 0] = Vector2((float)curX, (float)curY);
-						vertices[curVert + 1] = Vector2((float)(curX + charIter->width), (float)curY);
-						vertices[curVert + 2] = Vector2((float)curX, (float)curY - (float)charIter->height);
-						vertices[curVert + 3] = Vector2((float)(curX + charIter->width), (float)curY - (float)charIter->height);
+						const SpriteRenderElement& renderElem = renderElements[charIter->page];
 
-						uv[curVert + 0] = Vector2(charIter->uvX, charIter->uvY);
-						uv[curVert + 1] = Vector2(charIter->uvX + charIter->uvWidth, charIter->uvY);
-						uv[curVert + 2] = Vector2(charIter->uvX, charIter->uvY + charIter->uvHeight);
-						uv[curVert + 3] = Vector2(charIter->uvX + charIter->uvWidth, charIter->uvY + charIter->uvHeight);
+						renderElem.vertices[curVert + 0] = Vector2((float)curX, (float)curY);
+						renderElem.vertices[curVert + 1] = Vector2((float)(curX + charIter->width), (float)curY);
+						renderElem.vertices[curVert + 2] = Vector2((float)curX, (float)curY - (float)charIter->height);
+						renderElem.vertices[curVert + 3] = Vector2((float)(curX + charIter->width), (float)curY - (float)charIter->height);
 
-						indices[curIndex + 0] = curVert + 0;
-						indices[curIndex + 1] = curVert + 1;
-						indices[curIndex + 2] = curVert + 2;
-						indices[curIndex + 3] = curVert + 1;
-						indices[curIndex + 4] = curVert + 3;
-						indices[curIndex + 5] = curVert + 2;
+						renderElem.uvs[curVert + 0] = Vector2(charIter->uvX, charIter->uvY);
+						renderElem.uvs[curVert + 1] = Vector2(charIter->uvX + charIter->uvWidth, charIter->uvY);
+						renderElem.uvs[curVert + 2] = Vector2(charIter->uvX, charIter->uvY + charIter->uvHeight);
+						renderElem.uvs[curVert + 3] = Vector2(charIter->uvX + charIter->uvWidth, charIter->uvY + charIter->uvHeight);
+
+						renderElem.indexes[curIndex + 0] = curVert + 0;
+						renderElem.indexes[curIndex + 1] = curVert + 1;
+						renderElem.indexes[curIndex + 2] = curVert + 2;
+						renderElem.indexes[curIndex + 3] = curVert + 1;
+						renderElem.indexes[curIndex + 4] = curVert + 3;
+						renderElem.indexes[curIndex + 5] = curVert + 2;
 
 						penX += charIter->xAdvance + kerning;
-						curVert += 4;
-						curIndex += 6;
-						
+						faceOffsets[charIter->page]++;
+
 						kerning = 0;
 						if((charIter + 1) != chars.end())
 						{
@@ -218,8 +213,6 @@ namespace CamelotEngine
 					}
 				}
 			}
-
-			return (curVert / 4) - startingQuad;
 		}
 
 	private:
@@ -250,7 +243,7 @@ namespace CamelotEngine
 
 	}
 
-	void TextSprite::updateMesh()
+	void TextSprite::updateMesh() const
 	{
 		const FontData* fontData = getFontData();
 
@@ -273,7 +266,7 @@ namespace CamelotEngine
 
 		UINT32 curHeight = fontData->fontDesc.lineHeight;
 		UINT32 charIdx = 0;
-		mNumMeshQuads = 0;
+
 		while(true)
 		{
 			if(charIdx >= mText.size())
@@ -298,7 +291,12 @@ namespace CamelotEngine
 			curLine->add(charDesc);
 
 			if(charDesc.charId != SPACE_CHAR)
-				mNumMeshQuads++;
+			{
+				if(charDesc.page >= (UINT32)mCachedRenderElements.size())
+					mCachedRenderElements.resize(charDesc.page + 1);
+
+				mCachedRenderElements[charDesc.page].numQuads++;
+			}
 
 			if(widthIsLimited && curLine->getWidth() > mWidth)
 			{
@@ -370,18 +368,17 @@ namespace CamelotEngine
 		}
 
 		// Actually generate a mesh
-		mVertices = new Vector2[mNumMeshQuads * 4];
-		mUVs = new Vector2[mNumMeshQuads * 4];
-		mIndexes = new UINT32[mNumMeshQuads * 6];
-
-		UINT32 curFace = 0;
-		for(size_t i = 0; i < textLines.size(); i++)
+		for(auto& renderElem : mCachedRenderElements)
 		{
-			UINT32 newFaces = textLines[i]->fillBuffer(mVertices, mUVs, mIndexes, curFace, mNumMeshQuads, *fontData);
-
-			curFace += newFaces;
+			renderElem.vertices = new Vector2[renderElem.numQuads * 4];
+			renderElem.uvs = new Vector2[renderElem.numQuads * 4];
+			renderElem.indexes = new UINT32[renderElem.numQuads * 6];
 		}
 
+		vector<UINT32>::type faceOffsets(mCachedRenderElements.size(), 0);
+		for(size_t i = 0; i < textLines.size(); i++)
+			textLines[i]->fillBuffer(mCachedRenderElements, faceOffsets, *fontData);
+
 		for(size_t i = 0; i < textLines.size(); i++)
 			delete textLines[i];
 

+ 4 - 0
TODO.txt

@@ -23,6 +23,10 @@ Move Debug to CamelotCore and add SetFillMode
 
 Test:
 Need sorted rendering as fonts render before scene and blending doesn't work as it should
+ - Implemented 2D renderer and ensure it runs after everything else
+ - Or just add render queue ID to meshes?
+Start work on GUILabel element?
+ - Work on other sprites after that.
 Better font (Terminal?)
 Test word wrap, implement clipping
 Add HLSL9 and GLSL text materials & shaders