2
0
Эх сурвалжийг харах

Text rendering using TextUtility now works

Marko Pintera 12 жил өмнө
parent
commit
8ca11e04ad

+ 52 - 370
BansheeEngine/Source/BsTextSprite.cpp

@@ -1,266 +1,13 @@
 #include "BsTextSprite.h"
 #include "BsGUIMaterialManager.h"
-#include "CmDebug.h"
-#include "CmFontDesc.h"
+#include "CmTextUtility.h"
 #include "CmFont.h"
 #include "CmVector2.h"
-#include "CmMath.h"
 
 using namespace CamelotFramework;
 
 namespace BansheeEngine
 {
-	const int SPACE_CHAR = 32;
-
-	class TextWord
-	{
-	public:
-		TextWord(bool spacer)
-			:mWidth(0), mSpacer(spacer), mSpaceWidth(0)
-		{ }
-
-		void addChar(const CHAR_DESC& desc)
-		{
-			mChars.push_back(desc);
-
-			calculateWidth();
-		}
-
-		void addSpace(UINT32 spaceWidth)
-		{
-			mSpaceWidth += spaceWidth;
-
-			calculateWidth();
-		}
-
-		void removeLastChar()
-		{
-			if(mChars.size() > 0)
-			{
-				mChars.erase(mChars.end() - 1);
-				calculateWidth();
-			}
-		}
-
-		UINT32 getWidth() const { return mWidth; }
-		bool isSpacer() const { return mSpacer; }
-
-		const vector<CHAR_DESC>::type& getChars() const { return mChars; }
-
-	private:
-		vector<CHAR_DESC>::type mChars;
-		UINT32 mWidth;
-		bool mSpacer;
-		UINT32 mSpaceWidth;
-
-		void calculateWidth()
-		{
-			if(isSpacer())
-			{
-				mWidth = mSpaceWidth;
-				return;
-			}
-
-			if(mChars.size() == 0)
-			{
-				mWidth = 0;
-				return;
-			}
-
-			mWidth = 0;
-			UINT32 kerning = 0;
-			for(size_t i = 0; i < mChars.size() - 1; i++)
-			{
-				mWidth += mChars[i].xAdvance + kerning;
-
-				kerning = 0;
-				for(size_t j = 0; j < mChars[i].kerningPairs.size(); j++)
-				{
-					if(mChars[i].kerningPairs[j].otherCharId == mChars[i + 1].charId)
-					{
-						kerning = mChars[i].kerningPairs[j].amount;
-						break;
-					}
-				}
-			}
-
-			mWidth += mChars[mChars.size() - 1].xAdvance + kerning;
-		}
-	};
-
-	class TextLine
-	{
-	public:
-		TextLine()
-			:mWidth(0), mLastWord(nullptr)
-		{
-
-		}
-
-		~TextLine()
-		{
-			for(auto iter = mWords.begin(); iter != mWords.end(); ++iter)
-				CM_DELETE(*iter, TextWord, ScratchAlloc);
-		}
-
-		void add(const CHAR_DESC& charDesc)
-		{
-			if(mLastWord == nullptr)
-			{
-				TextWord* newWord = CM_NEW(TextWord, ScratchAlloc) TextWord(false);
-				mLastWord = newWord;
-
-				mWords.push_back(mLastWord);
-
-				mLastWord->addChar(charDesc);
-			}
-			else
-			{
-				if(mLastWord->isSpacer())
-				{
-					TextWord* newWord = CM_NEW(TextWord, ScratchAlloc) TextWord(false);
-					mLastWord = newWord;
-
-					mWords.push_back(mLastWord);
-				}
-
-				mLastWord->addChar(charDesc);
-			}
-
-			calculateBounds();
-		}
-
-		void addSpace(UINT32 spaceWidth)
-		{
-			if(mLastWord == nullptr)
-			{
-				TextWord* newWord = CM_NEW(TextWord, ScratchAlloc) TextWord(true);
-				mLastWord = newWord;
-
-				mWords.push_back(mLastWord);
-
-				mLastWord->addSpace(spaceWidth);
-			}
-			else
-			{
-				TextWord* newWord = CM_NEW(TextWord, ScratchAlloc) TextWord(true); // Each space is counted as its own word, to make certain operations easier
-				mLastWord = newWord;
-
-				mWords.push_back(mLastWord);
-
-				mLastWord->addSpace(spaceWidth);
-			}
-
-			calculateBounds();
-		}
-
-		void addWord(TextWord* word)
-		{
-			mWords.push_back(word);
-			mLastWord = word;
-
-			calculateBounds();
-		}
-
-		TextWord* removeLastWord()
-		{
-			if(mWords.size() == 0)
-				return nullptr;
-
-			TextWord* word = mWords[mWords.size() - 1];
-			mWords.erase(mWords.end() - 1);
-
-			if(mWords.size() > 0)
-				mLastWord = mWords[mWords.size() - 1];
-			else
-				mLastWord = nullptr;
-
-			calculateBounds();
-
-			return word;
-		}
-
-		UINT32 getWidth() const { return mWidth; }
-		Int2 getPosition() const { return mPosition; }
-		void setPosition(const Int2& pos) { mPosition = pos; }
-
-		void fillBuffer(const vector<SpriteRenderElement>::type& renderElements, vector<UINT32>::type& faceOffsets, const FontData& fontData)
-		{
-			UINT32 penX = mPosition.x;
-			UINT32 baselineY = mPosition.y + fontData.fontDesc.baselineOffset;
-			for(auto wordIter = mWords.begin(); wordIter != mWords.end(); ++wordIter)
-			{
-				if((*wordIter)->isSpacer())
-				{
-					penX += fontData.fontDesc.spaceWidth;
-				}
-				else
-				{
-					const vector<CHAR_DESC>::type& chars = (*wordIter)->getChars();
-					UINT32 kerning = 0;
-					for(auto charIter = chars.begin(); charIter != chars.end(); ++charIter)
-					{
-						INT32 curX = penX + charIter->xOffset;
-						INT32 curY = ((INT32)baselineY - charIter->yOffset);
-
-						UINT32 curVert = faceOffsets[charIter->page] * 4;
-						UINT32 curIndex = faceOffsets[charIter->page] * 6;
-
-						const SpriteRenderElement& renderElem = renderElements[charIter->page];
-
-						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);
-
-						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;
-						faceOffsets[charIter->page]++;
-
-						kerning = 0;
-						if((charIter + 1) != chars.end())
-						{
-							for(size_t j = 0; j < charIter->kerningPairs.size(); j++)
-							{
-								if(charIter->kerningPairs[j].otherCharId == (charIter + 1)->charId)
-								{
-									kerning = charIter->kerningPairs[j].amount;
-									break;
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-
-	private:
-		UINT32 mWidth;
-		vector<TextWord*>::type mWords;
-		TextWord* mLastWord;
-		Int2 mPosition;
-
-		void calculateBounds()
-		{
-			mWidth = 0;
-			for(auto iter = mWords.begin(); iter != mWords.end(); ++iter)
-			{
-				mWidth += (*iter)->getWidth();
-			}
-		}
-	};
-
 	TextSprite::TextSprite()
 	{
 
@@ -268,95 +15,18 @@ namespace BansheeEngine
 
 	void TextSprite::update(const TEXT_SPRITE_DESC& desc)
 	{
-		const FontData* fontData = nullptr;
-		if(desc.font != nullptr)
-		{
-			UINT32 nearestSize = desc.font->getClosestAvailableSize(desc.fontSize);
-			fontData = desc.font->getFontDataForSize(nearestSize);
-		}
+		std::shared_ptr<TextUtility::TextData> textData = TextUtility::getTextData(desc.text, desc.font, desc.fontSize, desc.width, desc.height, desc.wordWrap);
 
-		if(fontData == nullptr)
-		{
-			clearMesh();
+		if(textData == nullptr)
 			return;
-		}
-
-		if(fontData->size != desc.fontSize)
-		{
-			LOGWRN("Unable to find font with specified size (" + toString(desc.fontSize) + "). Using nearest available size: " + toString(fontData->size));
-		}
-
-		bool heightIsLimited = desc.height > 0;
-		bool widthIsLimited = desc.width > 0;
 
-		TextLine* curLine = CM_NEW(TextLine, ScratchAlloc) TextLine();
-		vector<TextLine*>::type textLines;
-		textLines.push_back(curLine);
+		const std::vector<TextUtility::TextLine*>& lines = textData->getLines();
+		const std::vector<UINT32>& quadsPerPage = textData->getNumQuadsPerPage();
 
-		UINT32 curHeight = fontData->fontDesc.lineHeight;
-		UINT32 charIdx = 0;
-
-		vector<UINT32>::type newRenderElemSizes;
-		while(true)
+		UINT32 curHeight = 0;
+		for(auto& line : lines)
 		{
-			if(charIdx >= desc.text.size())
-				break;
-
-			if(desc.text[charIdx] == '\n')
-			{
-				if(heightIsLimited && (curHeight + fontData->fontDesc.lineHeight * 2) > desc.height)
-					break; // Max height reached
-
-				curLine = CM_NEW(TextLine, ScratchAlloc) TextLine();
-				textLines.push_back(curLine);
-				curHeight += fontData->fontDesc.lineHeight;
-
-				charIdx++;
-				continue;
-			}
-
-			UINT32 charId = desc.text[charIdx];
-			const CHAR_DESC& charDesc = fontData->getCharDesc(charId);
-
-			if(charId != SPACE_CHAR)
-			{
-				curLine->add(charDesc);
-
-				if(charDesc.page >= (UINT32)newRenderElemSizes.size())
-					newRenderElemSizes.resize(charDesc.page + 1);
-
-				newRenderElemSizes[charDesc.page]++;
-			}
-			else
-				curLine->addSpace(fontData->fontDesc.spaceWidth);
-
-			if(widthIsLimited && curLine->getWidth() > desc.width)
-			{
-				if(desc.wordWrap)
-				{
-					TextWord* lastWord = curLine->removeLastWord();
-					if(lastWord->isSpacer())
-						curLine->addWord(lastWord); // Spaces can stay on previous line even if they don't technically fit
-
-					// No more lines fit vertically so we're done
-					if(heightIsLimited && curHeight > desc.height)
-						break;
-
-					curLine = CM_NEW(TextLine, ScratchAlloc) TextLine();
-					textLines.push_back(curLine);
-					curHeight += fontData->fontDesc.lineHeight;
-
-					if(!lastWord->isSpacer())
-						curLine->addWord(lastWord);
-				}
-				else
-				{
-					// Nothing else we can do, chars don't fit so we are done
-					break;
-				}
-			}
-
-			charIdx++;
+			curHeight += line->getYOffset();
 		}
 
 		// Calc vertical alignment offset
@@ -375,38 +45,15 @@ namespace BansheeEngine
 			break;
 		}
 
-		// Calc horizontal alignment offset and set final line positions
-		Int2 offset = getAnchorOffset(desc.anchor, desc.width, desc.height);
-		UINT32 curY = 0;
-		for(size_t i = 0; i < textLines.size(); i++)
-		{
-			UINT32 horzOffset = 0;
-			switch(desc.horzAlign)
-			{
-			case THA_Left:
-				horzOffset = 0;
-				break;
-			case THA_Right:
-				horzOffset = std::max(0, (INT32)(desc.width - textLines[i]->getWidth()));
-				break;
-			case THA_Center:
-				horzOffset = std::max(0, (INT32)(desc.width - textLines[i]->getWidth())) / 2;
-				break;
-			}
-
-			textLines[i]->setPosition(offset + Int2(horzOffset, vertOffset + curY));
-
-			curY += fontData->fontDesc.lineHeight;
-		}
-
 		// Actually generate a mesh
-		if(mCachedRenderElements.size() < newRenderElemSizes.size())
-			mCachedRenderElements.resize(newRenderElemSizes.size());
+		if(mCachedRenderElements.size() < quadsPerPage.size())
+			mCachedRenderElements.resize(quadsPerPage.size());
 
+		const std::vector<HTexture>& texturePages = textData->getTexturePages();
 		UINT32 texPage = 0;
 		for(auto& cachedElem : mCachedRenderElements)
 		{
-			UINT32 newNumQuads = newRenderElemSizes[texPage];
+			UINT32 newNumQuads = quadsPerPage[texPage];
 			if(newNumQuads != cachedElem.numQuads)
 			{
 				UINT32 oldVertexCount = cachedElem.numQuads * 4;
@@ -422,7 +69,7 @@ namespace BansheeEngine
 				cachedElem.numQuads = newNumQuads;
 			}
 
-			HMaterial newMaterial = GUIMaterialManager::instance().requestTextMaterial(fontData->texturePages[texPage]);
+			HMaterial newMaterial = GUIMaterialManager::instance().requestTextMaterial(texturePages[texPage]);
 			if(cachedElem.material != nullptr)
 				GUIMaterialManager::instance().releaseMaterial(newMaterial);
 
@@ -431,12 +78,47 @@ namespace BansheeEngine
 			texPage++;
 		}
 
+		// Calc horizontal alignment offset and set final line positions
+		Int2 offset = getAnchorOffset(desc.anchor, desc.width, desc.height);
+		UINT32 numPages = (UINT32)quadsPerPage.size();
+		UINT32 curY = 0;
 		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 < lines.size(); i++)
+		{
+			UINT32 horzOffset = 0;
+			switch(desc.horzAlign)
+			{
+			case THA_Left:
+				horzOffset = 0;
+				break;
+			case THA_Right:
+				horzOffset = std::max(0, (INT32)(desc.width - lines[i]->getWidth()));
+				break;
+			case THA_Center:
+				horzOffset = std::max(0, (INT32)(desc.width - lines[i]->getWidth())) / 2;
+				break;
+			}
+
+			Int2 position = offset + Int2(horzOffset, vertOffset + curY);
+			curY += lines[i]->getYOffset();
+
+			for(size_t j = 0; j < numPages; j++)
+			{
+				SpriteRenderElement& renderElem = mCachedRenderElements[j];
+				UINT32 offset = faceOffsets[j];
 
-		for(size_t i = 0; i < textLines.size(); i++)
-			CM_DELETE(textLines[i], TextLine, ScratchAlloc);
+				UINT32 writtenQuads = lines[i]->fillBuffer((UINT32)j, renderElem.vertices, renderElem.uvs, renderElem.indexes, offset, renderElem.numQuads);
+				
+				UINT32 numVertices = writtenQuads * 4;
+				for(size_t i = 0; i < numVertices; i++)
+				{
+					renderElem.vertices[offset + i].x += (float)position.x;
+					renderElem.vertices[offset + i].y += (float)position.y;
+				}
+
+				faceOffsets[j] += writtenQuads;
+			}
+		}
 
 		if(desc.clipRect.width > 0 && desc.clipRect.height > 0)
 		{

+ 27 - 14
CamelotCore/Include/CmTextUtility.h

@@ -36,15 +36,20 @@ namespace CamelotFramework
 		};
 
 	public:
-		class TextLine
+		class CM_EXPORT TextLine
 		{
 		public:
-			TextLine();
+			TextLine(UINT32 baselineOffset, UINT32 lineHeight, UINT32 spaceWidth);
 			~TextLine();
 
 			UINT32 getWidth() const { return mWidth; }
 			UINT32 getHeight() const { return mHeight; }
 
+			/**
+			 * @brief	Returns an offset used to separate two lines.
+			 */
+			UINT32 getYOffset() const { return mLineHeight; }
+
 			/**
 			 * @brief	Gets a number quads used by all characters for every page used by this text line.
 			 *
@@ -55,28 +60,34 @@ namespace CamelotFramework
 			std::vector<UINT32> getNumQuadsPerPage() const;
 
 			/**
-			 * @brief	Fills the vertex/uv/index buffers for the specified page, with all the character data needed for rendering.
+			 * @brief	Fills the vertex/uv/index buffers for the specified page, with all the character data
+			 * 			needed for rendering.
 			 *
-			 * @param	page				The page. 
-			 * @param [out]	vertices		Pre-allocated array where character vertices will be written.
-			 * @param [out]	uvs				Pre-allocated array where character uv coordinates will be written.
-			 * @param [out]	indexes 		Pre-allocated array where character indices will be written.
-			 * @param	offset				Offsets the location at which the method writes to the buffers. Counted as number of quads.
-			 * @param	size				Total number of quads that can fit into the specified buffers.
-			 * @param	fontData			Information describing the font.
+			 * @param	page			The page.
+			 * @param [out]	vertices	Pre-allocated array where character vertices will be written.
+			 * @param [out]	uvs			Pre-allocated array where character uv coordinates will be written.
+			 * @param [out]	indexes 	Pre-allocated array where character indices will be written.
+			 * @param	offset			Offsets the location at which the method writes to the buffers.
+			 * 							Counted as number of quads.
+			 * @param	size			Total number of quads that can fit into the specified buffers.
+			 *
+			 * @return	Number of quads that were written.
 			 */
-			void fillBuffer(UINT32 page, Vector2* vertices, Vector2* uvs, UINT32* indexes, UINT32 offset, UINT32 size, const FontData& fontData) const;
+			UINT32 fillBuffer(UINT32 page, Vector2* vertices, Vector2* uvs, UINT32* indexes, UINT32 offset, UINT32 size) const;
 
 		private:
 			friend class TextUtility;
 
 			UINT32 mWidth;
 			UINT32 mHeight;
+			UINT32 mBaselineOffset;
+			UINT32 mLineHeight;
+			UINT32 mSpaceWidth;
 			vector<TextWord*>::type mWords;
 			TextWord* mLastWord;
 
 			void add(const CHAR_DESC& charDesc);
-			void addSpace(UINT32 spaceWidth);
+			void addSpace();
 			void addWord(TextWord* word);
 
 			TextWord* removeLastWord();
@@ -84,20 +95,22 @@ namespace CamelotFramework
 			void calculateBounds();
 		};
 
-		class TextData
+		class CM_EXPORT TextData
 		{
 		public:
 			~TextData();
 
 			const std::vector<TextLine*>& getLines() const { return mLines; }
+			const std::vector<HTexture>& getTexturePages() const { return mTexturePages; }
 			const std::vector<UINT32>& getNumQuadsPerPage() const  { return mQuadsPerPage; }
 		private:
 			friend class TextUtility;
 
 			std::vector<UINT32> mQuadsPerPage;
 			std::vector<TextLine*> mLines;
+			std::vector<HTexture> mTexturePages;
 		};
 
-		std::shared_ptr<TextUtility::TextData> getTextData(const String& text, const HFont& font, UINT32 fontSize, UINT32 width = 0, UINT32 height = 0, bool wordWrap = false);
+		static std::shared_ptr<TextUtility::TextData> getTextData(const String& text, const HFont& font, UINT32 fontSize, UINT32 width = 0, UINT32 height = 0, bool wordWrap = false);
 	};
 }

+ 26 - 13
CamelotCore/Source/CmTextUtility.cpp

@@ -72,8 +72,8 @@ namespace CamelotFramework
 		mWidth += mChars[mChars.size() - 1].xAdvance + kerning;
 	}
 
-	TextUtility::TextLine::TextLine()
-		:mWidth(0), mHeight(0), mLastWord(nullptr)
+	TextUtility::TextLine::TextLine(UINT32 baselineOffset, UINT32 lineHeight, UINT32 spaceWidth)
+		:mWidth(0), mHeight(0), mLastWord(nullptr), mBaselineOffset(baselineOffset), mLineHeight(lineHeight), mSpaceWidth(spaceWidth)
 	{
 
 	}
@@ -111,7 +111,7 @@ namespace CamelotFramework
 		calculateBounds();
 	}
 
-	void TextUtility::TextLine::addSpace(UINT32 spaceWidth)
+	void TextUtility::TextLine::addSpace()
 	{
 		if(mLastWord == nullptr)
 		{
@@ -120,7 +120,7 @@ namespace CamelotFramework
 
 			mWords.push_back(mLastWord);
 
-			mLastWord->addSpace(spaceWidth);
+			mLastWord->addSpace(mSpaceWidth);
 		}
 		else
 		{
@@ -129,7 +129,7 @@ namespace CamelotFramework
 
 			mWords.push_back(mLastWord);
 
-			mLastWord->addSpace(spaceWidth);
+			mLastWord->addSpace(mSpaceWidth);
 		}
 
 		calculateBounds();
@@ -183,15 +183,16 @@ namespace CamelotFramework
 		return quadsPerPage;
 	}
 
-	void TextUtility::TextLine::fillBuffer(UINT32 page, Vector2* vertices, Vector2* uvs, UINT32* indexes, UINT32 offset, UINT32 size, const FontData& fontData) const
+	UINT32 TextUtility::TextLine::fillBuffer(UINT32 page, Vector2* vertices, Vector2* uvs, UINT32* indexes, UINT32 offset, UINT32 size) const
 	{
+		UINT32 numQuads = 0;
+
 		UINT32 penX = 0;
-		UINT32 baselineY = fontData.fontDesc.baselineOffset;
 		for(auto wordIter = mWords.begin(); wordIter != mWords.end(); ++wordIter)
 		{
 			if((*wordIter)->isSpacer())
 			{
-				penX += fontData.fontDesc.spaceWidth;
+				penX += mSpaceWidth;
 			}
 			else
 			{
@@ -200,7 +201,7 @@ namespace CamelotFramework
 				for(auto charIter = chars.begin(); charIter != chars.end(); ++charIter)
 				{
 					INT32 curX = penX + charIter->xOffset;
-					INT32 curY = ((INT32)baselineY - charIter->yOffset);
+					INT32 curY = ((INT32)mBaselineOffset - charIter->yOffset);
 
 					penX += charIter->xAdvance + kerning;
 					
@@ -241,9 +242,15 @@ namespace CamelotFramework
 					indexes[curIndex + 5] = curVert + 2;
 
 					offset++;
+					numQuads++;
+
+					if(offset > size)
+						CM_EXCEPT(InternalErrorException, "Out of buffer bounds. Buffer size: " + toString(size));
 				}
 			}
 		}
+
+		return numQuads;
 	}
 
 	void TextUtility::TextLine::calculateBounds()
@@ -284,7 +291,7 @@ namespace CamelotFramework
 		bool widthIsLimited = width > 0;
 
 		std::shared_ptr<TextUtility::TextData> textData(CM_NEW(TextData, PoolAlloc) TextData(), &MemAllocDeleter<TextData, PoolAlloc>::deleter);
-		TextLine* curLine = CM_NEW(TextLine, ScratchAlloc) TextLine();
+		TextLine* curLine = CM_NEW(TextLine, ScratchAlloc) TextLine(fontData->fontDesc.baselineOffset, fontData->fontDesc.lineHeight, fontData->fontDesc.spaceWidth);
 		textData->mLines.push_back(curLine);
 
 		UINT32 curHeight = fontData->fontDesc.lineHeight;
@@ -300,7 +307,7 @@ namespace CamelotFramework
 				if(heightIsLimited && (curHeight + fontData->fontDesc.lineHeight * 2) > height)
 					break; // Max height reached
 
-				curLine = CM_NEW(TextLine, ScratchAlloc) TextLine();
+				curLine = CM_NEW(TextLine, ScratchAlloc) TextLine(fontData->fontDesc.baselineOffset, fontData->fontDesc.lineHeight, fontData->fontDesc.spaceWidth);
 				textData->mLines.push_back(curLine);
 				curHeight += fontData->fontDesc.lineHeight;
 
@@ -316,12 +323,18 @@ namespace CamelotFramework
 				curLine->add(charDesc);
 
 				if(charDesc.page >= (UINT32)textData->mQuadsPerPage.size())
+				{
 					textData->mQuadsPerPage.resize(charDesc.page + 1);
+					textData->mTexturePages.resize(charDesc.page + 1);
+				}
 
 				textData->mQuadsPerPage[charDesc.page]++;
+
+				if(textData->mTexturePages[charDesc.page] == nullptr)
+					textData->mTexturePages[charDesc.page] = fontData->texturePages[charDesc.page];
 			}
 			else
-				curLine->addSpace(fontData->fontDesc.spaceWidth);
+				curLine->addSpace();
 
 			if(widthIsLimited && curLine->getWidth() > width)
 			{
@@ -335,7 +348,7 @@ namespace CamelotFramework
 					if(heightIsLimited && curHeight > height)
 						break;
 
-					curLine = CM_NEW(TextLine, ScratchAlloc) TextLine();
+					curLine = CM_NEW(TextLine, ScratchAlloc) TextLine(fontData->fontDesc.baselineOffset, fontData->fontDesc.lineHeight, fontData->fontDesc.spaceWidth);
 					textData->mLines.push_back(curLine);
 					curHeight += fontData->fontDesc.lineHeight;