gfxFontRenderBatcher.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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. mFont = NULL;
  27. mLength = 0;
  28. if (!mFontSB)
  29. {
  30. GFXStateBlockDesc f;
  31. f.zDefined = true;
  32. f.zEnable = false;
  33. f.zWriteEnable = false;
  34. f.cullDefined = true;
  35. f.cullMode = GFXCullNone;
  36. f.blendDefined = true;
  37. f.blendEnable = true;
  38. f.blendSrc = GFXBlendSrcAlpha;
  39. f.blendDest = GFXBlendInvSrcAlpha;
  40. f.samplersDefined = true;
  41. f.samplers[0].alphaOp = GFXTOPModulate;
  42. f.samplers[0].magFilter = GFXTextureFilterPoint;
  43. f.samplers[0].minFilter = GFXTextureFilterPoint;
  44. f.samplers[0].addressModeU = GFXAddressClamp;
  45. f.samplers[0].addressModeV = GFXAddressClamp;
  46. f.samplers[0].alphaArg1 = GFXTATexture;
  47. f.samplers[0].alphaArg2 = GFXTADiffuse;
  48. // This is an add operation because in D3D, when a texture of format D3DFMT_A8
  49. // is used, the RGB channels are all set to 0. Therefore a modulate would
  50. // result in the text always being black. This may not be the case in OpenGL
  51. // so it may have to change. -bramage
  52. f.samplers[0].textureColorOp = GFXTOPAdd;
  53. f.setColorWrites(true, true, true, false); // NOTE: comment this out if alpha write is needed
  54. mFontSB = GFX->createStateBlock(f);
  55. }
  56. }
  57. void FontRenderBatcher::render( F32 rot, const Point2F &offset )
  58. {
  59. if( mLength == 0 )
  60. return;
  61. GFX->setStateBlock(mFontSB);
  62. for(U32 i = 0; i < GFX->getNumSamplers(); i++)
  63. GFX->setTexture(i, NULL);
  64. MatrixF rotMatrix;
  65. bool doRotation = rot != 0.f;
  66. if(doRotation)
  67. rotMatrix.set( EulerF( 0.0, 0.0, mDegToRad( rot ) ) );
  68. // Write verts out.
  69. U32 currentPt = 0;
  70. GFXVertexBufferHandle<GFXVertexPCT> verts(GFX, mLength * 6, GFXBufferTypeVolatile);
  71. verts.lock();
  72. for( S32 i = 0; i < mSheets.size(); i++ )
  73. {
  74. // Do some early outs...
  75. if(!mSheets[i])
  76. continue;
  77. if(!mSheets[i]->numChars)
  78. continue;
  79. mSheets[i]->startVertex = currentPt;
  80. const GFXTextureObject *tex = mFont->getTextureHandle(i);
  81. for( S32 j = 0; j < mSheets[i]->numChars; j++ )
  82. {
  83. // Get some general info to proceed with...
  84. const CharMarker &m = mSheets[i]->charIndex[j];
  85. const PlatformFont::CharInfo &ci = mFont->getCharInfo( m.c );
  86. // Where are we drawing it?
  87. F32 drawY = offset.y + mFont->getBaseline() - ci.yOrigin * TEXT_MAG;
  88. F32 drawX = offset.x + m.x + ci.xOrigin;
  89. // Figure some values.
  90. const F32 texWidth = (F32)tex->getWidth();
  91. const F32 texHeight = (F32)tex->getHeight();
  92. const F32 texLeft = (F32)(ci.xOffset) / texWidth;
  93. const F32 texRight = (F32)(ci.xOffset + ci.width) / texWidth;
  94. const F32 texTop = (F32)(ci.yOffset) / texHeight;
  95. const F32 texBottom = (F32)(ci.yOffset + ci.height) / texHeight;
  96. const F32 fillConventionOffset = GFX->getFillConventionOffset();
  97. const F32 screenLeft = drawX - fillConventionOffset;
  98. const F32 screenRight = drawX - fillConventionOffset + ci.width * TEXT_MAG;
  99. const F32 screenTop = drawY - fillConventionOffset;
  100. const F32 screenBottom = drawY - fillConventionOffset + ci.height * TEXT_MAG;
  101. // Build our vertices. We NEVER read back from the buffer, that's
  102. // incredibly slow, so for rotation do it into tmp. This code is
  103. // ugly as sin.
  104. Point3F tmp;
  105. tmp.set( screenLeft, screenTop, 0.f );
  106. if(doRotation)
  107. rotMatrix.mulP( tmp, &verts[currentPt].point);
  108. else
  109. verts[currentPt].point = tmp;
  110. verts[currentPt].color = m.color;
  111. verts[currentPt].texCoord.set( texLeft, texTop );
  112. currentPt++;
  113. tmp.set( screenLeft, screenBottom, 0.f );
  114. if(doRotation)
  115. rotMatrix.mulP( tmp, &verts[currentPt].point);
  116. else
  117. verts[currentPt].point = tmp;
  118. verts[currentPt].color = m.color;
  119. verts[currentPt].texCoord.set( texLeft, texBottom );
  120. currentPt++;
  121. tmp.set( screenRight, screenBottom, 0.f );
  122. if(doRotation)
  123. rotMatrix.mulP( tmp, &verts[currentPt].point);
  124. else
  125. verts[currentPt].point = tmp;
  126. verts[currentPt].color = m.color;
  127. verts[currentPt].texCoord.set( texRight, texBottom );
  128. currentPt++;
  129. tmp.set( screenRight, screenBottom, 0.f );
  130. if(doRotation)
  131. rotMatrix.mulP( tmp, &verts[currentPt].point);
  132. else
  133. verts[currentPt].point = tmp;
  134. verts[currentPt].color = m.color;
  135. verts[currentPt].texCoord.set( texRight, texBottom );
  136. currentPt++;
  137. tmp.set( screenRight, screenTop, 0.f );
  138. if(doRotation)
  139. rotMatrix.mulP( tmp, &verts[currentPt].point);
  140. else
  141. verts[currentPt].point = tmp;
  142. verts[currentPt].color = m.color;
  143. verts[currentPt].texCoord.set( texRight, texTop );
  144. currentPt++;
  145. tmp.set( screenLeft, screenTop, 0.f );
  146. if(doRotation)
  147. rotMatrix.mulP( tmp, &verts[currentPt].point);
  148. else
  149. verts[currentPt].point = tmp;
  150. verts[currentPt].color = m.color;
  151. verts[currentPt].texCoord.set( texLeft, texTop );
  152. currentPt++;
  153. }
  154. }
  155. verts->unlock();
  156. AssertFatal(currentPt <= mLength * 6, "FontRenderBatcher::render - too many verts for length of string!");
  157. GFX->setVertexBuffer(verts);
  158. GFX->setupGenericShaders( GFXDevice::GSAddColorTexture );
  159. // Now do an optimal render!
  160. for( S32 i = 0; i < mSheets.size(); i++ )
  161. {
  162. if(!mSheets[i])
  163. continue;
  164. if(!mSheets[i]->numChars )
  165. continue;
  166. GFX->setTexture( 0, mFont->getTextureHandle(i) );
  167. GFX->drawPrimitive(GFXTriangleList, mSheets[i]->startVertex, mSheets[i]->numChars * 2);
  168. }
  169. }
  170. void FontRenderBatcher::queueChar( UTF16 c, S32 &currentX, GFXVertexColor &currentColor )
  171. {
  172. const PlatformFont::CharInfo &ci = mFont->getCharInfo( c );
  173. U32 sidx = ci.bitmapIndex;
  174. if( ci.width != 0 && ci.height != 0 )
  175. {
  176. SheetMarker &sm = getSheetMarker(sidx);
  177. CharMarker &m = sm.charIndex[sm.numChars];
  178. sm.numChars++;
  179. m.c = c;
  180. m.x = (F32)currentX;
  181. m.color = currentColor;
  182. }
  183. currentX += ci.xIncrement;
  184. }
  185. FontRenderBatcher::SheetMarker & FontRenderBatcher::getSheetMarker( U32 sheetID )
  186. {
  187. // Add empty sheets up to and including the requested sheet if necessary
  188. if (mSheets.size() <= sheetID)
  189. {
  190. S32 oldSize = mSheets.size();
  191. mSheets.setSize( sheetID + 1 );
  192. for ( S32 i = oldSize; i < mSheets.size(); i++ )
  193. mSheets[i] = NULL;
  194. }
  195. // Allocate if it doesn't exist...
  196. if (!mSheets[sheetID])
  197. {
  198. S32 size = sizeof( SheetMarker) + mLength * sizeof( CharMarker );
  199. mSheets[sheetID] = (SheetMarker *)mStorage.alloc(size);
  200. mSheets[sheetID]->numChars = 0;
  201. mSheets[sheetID]->startVertex = 0; // cosmetic initialization
  202. }
  203. return *mSheets[sheetID];
  204. }
  205. void FontRenderBatcher::init( GFont *font, U32 n )
  206. {
  207. // Clear out batched results
  208. dMemset(mSheets.address(), 0, mSheets.memSize());
  209. mSheets.clear();
  210. mStorage.freeBlocks(true);
  211. mFont = font;
  212. mLength = n;
  213. }