gfxFontRenderBatcher.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  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. mFontSB = GFX->createStateBlock(f);
  52. }
  53. }
  54. void FontRenderBatcher::render( F32 rot, const Point2F &offset )
  55. {
  56. if( mLength == 0 )
  57. return;
  58. GFX->setStateBlock(mFontSB);
  59. for(U32 i = 0; i < GFX->getNumSamplers(); i++)
  60. GFX->setTexture(i, NULL);
  61. MatrixF rotMatrix;
  62. bool doRotation = rot != 0.f;
  63. if(doRotation)
  64. rotMatrix.set( EulerF( 0.0, 0.0, mDegToRad( rot ) ) );
  65. // Write verts out.
  66. U32 currentPt = 0;
  67. GFXVertexBufferHandle<GFXVertexPCT> verts(GFX, mLength * 6, GFXBufferTypeVolatile);
  68. verts.lock();
  69. for( S32 i = 0; i < mSheets.size(); i++ )
  70. {
  71. // Do some early outs...
  72. if(!mSheets[i])
  73. continue;
  74. if(!mSheets[i]->numChars)
  75. continue;
  76. mSheets[i]->startVertex = currentPt;
  77. const GFXTextureObject *tex = mFont->getTextureHandle(i);
  78. for( S32 j = 0; j < mSheets[i]->numChars; j++ )
  79. {
  80. // Get some general info to proceed with...
  81. const CharMarker &m = mSheets[i]->charIndex[j];
  82. const PlatformFont::CharInfo &ci = mFont->getCharInfo( m.c );
  83. // Where are we drawing it?
  84. F32 drawY = offset.y + mFont->getBaseline() - ci.yOrigin * TEXT_MAG;
  85. F32 drawX = offset.x + m.x + ci.xOrigin;
  86. // Figure some values.
  87. const F32 texWidth = (F32)tex->getWidth();
  88. const F32 texHeight = (F32)tex->getHeight();
  89. const F32 texLeft = (F32)(ci.xOffset) / texWidth;
  90. const F32 texRight = (F32)(ci.xOffset + ci.width) / texWidth;
  91. const F32 texTop = (F32)(ci.yOffset) / texHeight;
  92. const F32 texBottom = (F32)(ci.yOffset + ci.height) / texHeight;
  93. const F32 fillConventionOffset = GFX->getFillConventionOffset();
  94. const F32 screenLeft = drawX - fillConventionOffset;
  95. const F32 screenRight = drawX - fillConventionOffset + ci.width * TEXT_MAG;
  96. const F32 screenTop = drawY - fillConventionOffset;
  97. const F32 screenBottom = drawY - fillConventionOffset + ci.height * TEXT_MAG;
  98. // Build our vertices. We NEVER read back from the buffer, that's
  99. // incredibly slow, so for rotation do it into tmp. This code is
  100. // ugly as sin.
  101. Point3F tmp;
  102. tmp.set( screenLeft, screenTop, 0.f );
  103. if(doRotation)
  104. rotMatrix.mulP( tmp, &verts[currentPt].point);
  105. else
  106. verts[currentPt].point = tmp;
  107. verts[currentPt].color = m.color;
  108. verts[currentPt].texCoord.set( texLeft, texTop );
  109. currentPt++;
  110. tmp.set( screenLeft, screenBottom, 0.f );
  111. if(doRotation)
  112. rotMatrix.mulP( tmp, &verts[currentPt].point);
  113. else
  114. verts[currentPt].point = tmp;
  115. verts[currentPt].color = m.color;
  116. verts[currentPt].texCoord.set( texLeft, texBottom );
  117. currentPt++;
  118. tmp.set( screenRight, screenBottom, 0.f );
  119. if(doRotation)
  120. rotMatrix.mulP( tmp, &verts[currentPt].point);
  121. else
  122. verts[currentPt].point = tmp;
  123. verts[currentPt].color = m.color;
  124. verts[currentPt].texCoord.set( texRight, texBottom );
  125. currentPt++;
  126. tmp.set( screenRight, screenBottom, 0.f );
  127. if(doRotation)
  128. rotMatrix.mulP( tmp, &verts[currentPt].point);
  129. else
  130. verts[currentPt].point = tmp;
  131. verts[currentPt].color = m.color;
  132. verts[currentPt].texCoord.set( texRight, texBottom );
  133. currentPt++;
  134. tmp.set( screenRight, screenTop, 0.f );
  135. if(doRotation)
  136. rotMatrix.mulP( tmp, &verts[currentPt].point);
  137. else
  138. verts[currentPt].point = tmp;
  139. verts[currentPt].color = m.color;
  140. verts[currentPt].texCoord.set( texRight, texTop );
  141. currentPt++;
  142. tmp.set( screenLeft, screenTop, 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( texLeft, texTop );
  149. currentPt++;
  150. }
  151. }
  152. verts->unlock();
  153. AssertFatal(currentPt <= mLength * 6, "FontRenderBatcher::render - too many verts for length of string!");
  154. GFX->setVertexBuffer(verts);
  155. GFX->setupGenericShaders( GFXDevice::GSAddColorTexture );
  156. // Now do an optimal render!
  157. for( S32 i = 0; i < mSheets.size(); i++ )
  158. {
  159. if(!mSheets[i])
  160. continue;
  161. if(!mSheets[i]->numChars )
  162. continue;
  163. GFX->setTexture( 0, mFont->getTextureHandle(i) );
  164. GFX->drawPrimitive(GFXTriangleList, mSheets[i]->startVertex, mSheets[i]->numChars * 2);
  165. }
  166. }
  167. void FontRenderBatcher::queueChar( UTF16 c, S32 &currentX, GFXVertexColor &currentColor )
  168. {
  169. const PlatformFont::CharInfo &ci = mFont->getCharInfo( c );
  170. U32 sidx = ci.bitmapIndex;
  171. if( ci.width != 0 && ci.height != 0 )
  172. {
  173. SheetMarker &sm = getSheetMarker(sidx);
  174. CharMarker &m = sm.charIndex[sm.numChars];
  175. sm.numChars++;
  176. m.c = c;
  177. m.x = (F32)currentX;
  178. m.color = currentColor;
  179. }
  180. currentX += ci.xIncrement;
  181. }
  182. FontRenderBatcher::SheetMarker & FontRenderBatcher::getSheetMarker( U32 sheetID )
  183. {
  184. // Add empty sheets up to and including the requested sheet if necessary
  185. if (mSheets.size() <= sheetID)
  186. {
  187. S32 oldSize = mSheets.size();
  188. mSheets.setSize( sheetID + 1 );
  189. for ( S32 i = oldSize; i < mSheets.size(); i++ )
  190. mSheets[i] = NULL;
  191. }
  192. // Allocate if it doesn't exist...
  193. if (!mSheets[sheetID])
  194. {
  195. S32 size = sizeof( SheetMarker) + mLength * sizeof( CharMarker );
  196. mSheets[sheetID] = (SheetMarker *)mStorage.alloc(size);
  197. mSheets[sheetID]->numChars = 0;
  198. mSheets[sheetID]->startVertex = 0; // cosmetic initialization
  199. }
  200. return *mSheets[sheetID];
  201. }
  202. void FontRenderBatcher::init( GFont *font, U32 n )
  203. {
  204. // Clear out batched results
  205. dMemset(mSheets.address(), 0, mSheets.memSize());
  206. mSheets.clear();
  207. mStorage.freeBlocks(true);
  208. mFont = font;
  209. mLength = n;
  210. }