gfxFontRenderBatcher.cpp
Engine/source/gfx/gfxFontRenderBatcher.cpp
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "gfx/gfxFontRenderBatcher.h" 25#include "gfx/gFont.h" 26 27FontRenderBatcher::FontRenderBatcher() : mStorage(8096) 28{ 29 mFont = NULL; 30 mLength = 0; 31 if (!mFontSB) 32 { 33 GFXStateBlockDesc f; 34 f.zDefined = true; 35 f.zEnable = false; 36 f.zWriteEnable = false; 37 f.cullDefined = true; 38 f.cullMode = GFXCullNone; 39 f.blendDefined = true; 40 f.blendEnable = true; 41 f.blendSrc = GFXBlendSrcAlpha; 42 f.blendDest = GFXBlendInvSrcAlpha; 43 f.samplersDefined = true; 44 f.samplers[0].magFilter = GFXTextureFilterPoint; 45 f.samplers[0].minFilter = GFXTextureFilterPoint; 46 f.samplers[0].addressModeU = GFXAddressClamp; 47 f.samplers[0].addressModeV = GFXAddressClamp; 48 49 f.setColorWrites(true, true, true, false); // NOTE: comment this out if alpha write is needed 50 mFontSB = GFX->createStateBlock(f); 51 } 52} 53 54void FontRenderBatcher::render( F32 rot, const Point2F &offset ) 55{ 56 if( mLength == 0 ) 57 return; 58 59 GFX->setStateBlock(mFontSB); 60 for(U32 i = 0; i < GFX->getNumSamplers(); i++) 61 GFX->setTexture(i, NULL); 62 63 MatrixF rotMatrix; 64 65 bool doRotation = rot != 0.f; 66 if(doRotation) 67 rotMatrix.set( EulerF( 0.0, 0.0, mDegToRad( rot ) ) ); 68 69 // Write verts out. 70 U32 currentPt = 0; 71 GFXVertexBufferHandle<GFXVertexPCT> verts(GFX, mLength * 6, GFXBufferTypeVolatile); 72 verts.lock(); 73 74 for( S32 i = 0; i < mSheets.size(); i++ ) 75 { 76 // Do some early outs... 77 if(!mSheets[i]) 78 continue; 79 80 if(!mSheets[i]->numChars) 81 continue; 82 83 mSheets[i]->startVertex = currentPt; 84 const GFXTextureObject *tex = mFont->getTextureHandle(i); 85 86 for( S32 j = 0; j < mSheets[i]->numChars; j++ ) 87 { 88 // Get some general info to proceed with... 89 const CharMarker &m = mSheets[i]->charIndex[j]; 90 const PlatformFont::CharInfo &ci = mFont->getCharInfo( m.c ); 91 92 // Where are we drawing it? 93 F32 drawY = offset.y + mFont->getBaseline() - ci.yOrigin * TEXT_MAG; 94 F32 drawX = offset.x + m.x + ci.xOrigin; 95 96 // Figure some values. 97 const F32 texWidth = (F32)tex->getWidth(); 98 const F32 texHeight = (F32)tex->getHeight(); 99 const F32 texLeft = (F32)(ci.xOffset) / texWidth; 100 const F32 texRight = (F32)(ci.xOffset + ci.width) / texWidth; 101 const F32 texTop = (F32)(ci.yOffset) / texHeight; 102 const F32 texBottom = (F32)(ci.yOffset + ci.height) / texHeight; 103 104 const F32 fillConventionOffset = GFX->getFillConventionOffset(); 105 const F32 screenLeft = drawX - fillConventionOffset; 106 const F32 screenRight = drawX - fillConventionOffset + ci.width * TEXT_MAG; 107 const F32 screenTop = drawY - fillConventionOffset; 108 const F32 screenBottom = drawY - fillConventionOffset + ci.height * TEXT_MAG; 109 110 // Build our vertices. We NEVER read back from the buffer, that's 111 // incredibly slow, so for rotation do it into tmp. This code is 112 // ugly as sin. 113 Point3F tmp; 114 115 tmp.set( screenLeft, screenTop, 0.f ); 116 if(doRotation) 117 rotMatrix.mulP( tmp, &verts[currentPt].point); 118 else 119 verts[currentPt].point = tmp; 120 verts[currentPt].color = m.color; 121 verts[currentPt].texCoord.set( texLeft, texTop ); 122 currentPt++; 123 124 tmp.set( screenLeft, screenBottom, 0.f ); 125 if(doRotation) 126 rotMatrix.mulP( tmp, &verts[currentPt].point); 127 else 128 verts[currentPt].point = tmp; 129 verts[currentPt].color = m.color; 130 verts[currentPt].texCoord.set( texLeft, texBottom ); 131 currentPt++; 132 133 tmp.set( screenRight, screenBottom, 0.f ); 134 if(doRotation) 135 rotMatrix.mulP( tmp, &verts[currentPt].point); 136 else 137 verts[currentPt].point = tmp; 138 verts[currentPt].color = m.color; 139 verts[currentPt].texCoord.set( texRight, texBottom ); 140 currentPt++; 141 142 tmp.set( screenRight, screenBottom, 0.f ); 143 if(doRotation) 144 rotMatrix.mulP( tmp, &verts[currentPt].point); 145 else 146 verts[currentPt].point = tmp; 147 verts[currentPt].color = m.color; 148 verts[currentPt].texCoord.set( texRight, texBottom ); 149 currentPt++; 150 151 tmp.set( screenRight, screenTop, 0.f ); 152 if(doRotation) 153 rotMatrix.mulP( tmp, &verts[currentPt].point); 154 else 155 verts[currentPt].point = tmp; 156 verts[currentPt].color = m.color; 157 verts[currentPt].texCoord.set( texRight, texTop ); 158 currentPt++; 159 160 tmp.set( screenLeft, screenTop, 0.f ); 161 if(doRotation) 162 rotMatrix.mulP( tmp, &verts[currentPt].point); 163 else 164 verts[currentPt].point = tmp; 165 verts[currentPt].color = m.color; 166 verts[currentPt].texCoord.set( texLeft, texTop ); 167 currentPt++; 168 } 169 } 170 171 verts->unlock(); 172 173 AssertFatal(currentPt <= mLength * 6, "FontRenderBatcher::render - too many verts for length of string!"); 174 175 GFX->setVertexBuffer(verts); 176 GFX->setupGenericShaders( GFXDevice::GSAddColorTexture ); 177 178 // Now do an optimal render! 179 for( S32 i = 0; i < mSheets.size(); i++ ) 180 { 181 if(!mSheets[i]) 182 continue; 183 184 if(!mSheets[i]->numChars ) 185 continue; 186 187 GFX->setTexture( 0, mFont->getTextureHandle(i) ); 188 GFX->drawPrimitive(GFXTriangleList, mSheets[i]->startVertex, mSheets[i]->numChars * 2); 189 } 190} 191 192void FontRenderBatcher::queueChar( UTF16 c, S32 ¤tX, GFXVertexColor ¤tColor ) 193{ 194 const PlatformFont::CharInfo &ci = mFont->getCharInfo( c ); 195 U32 sidx = ci.bitmapIndex; 196 197 if( ci.width != 0 && ci.height != 0 ) 198 { 199 SheetMarker &sm = getSheetMarker(sidx); 200 201 CharMarker &m = sm.charIndex[sm.numChars]; 202 sm.numChars++; 203 204 m.c = c; 205 m.x = (F32)currentX; 206 m.color = currentColor; 207 } 208 209 currentX += ci.xIncrement; 210} 211 212FontRenderBatcher::SheetMarker & FontRenderBatcher::getSheetMarker( U32 sheetID ) 213{ 214 // Add empty sheets up to and including the requested sheet if necessary 215 if (mSheets.size() <= sheetID) 216 { 217 S32 oldSize = mSheets.size(); 218 mSheets.setSize( sheetID + 1 ); 219 for ( S32 i = oldSize; i < mSheets.size(); i++ ) 220 mSheets[i] = NULL; 221 } 222 223 // Allocate if it doesn't exist... 224 if (!mSheets[sheetID]) 225 { 226 S32 size = sizeof( SheetMarker) + mLength * sizeof( CharMarker ); 227 mSheets[sheetID] = (SheetMarker *)mStorage.alloc(size); 228 mSheets[sheetID]->numChars = 0; 229 mSheets[sheetID]->startVertex = 0; // cosmetic initialization 230 } 231 232 return *mSheets[sheetID]; 233} 234 235void FontRenderBatcher::init( GFont *font, U32 n ) 236{ 237 // Clear out batched results 238 dMemset(mSheets.address(), 0, mSheets.memSize()); 239 mSheets.clear(); 240 mStorage.freeBlocks(true); 241 242 mFont = font; 243 mLength = n; 244} 245