gfxFontRenderBatcher.cpp 8.5 KB

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