Torque3D Documentation / _generateds / gfxFontRenderBatcher.cpp

gfxFontRenderBatcher.cpp

Engine/source/gfx/gfxFontRenderBatcher.cpp

More...

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 &currentX, GFXVertexColor &currentColor )
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