123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "gfx/gfxFontRenderBatcher.h"
- #include "gfx/gFont.h"
- FontRenderBatcher::FontRenderBatcher() : mStorage(8096)
- {
- mFont = NULL;
- mLength = 0;
- if (!mFontSB)
- {
- GFXStateBlockDesc f;
- f.zDefined = true;
- f.zEnable = false;
- f.zWriteEnable = false;
- f.cullDefined = true;
- f.cullMode = GFXCullNone;
- f.blendDefined = true;
- f.blendEnable = true;
- f.blendSrc = GFXBlendSrcAlpha;
- f.blendDest = GFXBlendInvSrcAlpha;
- f.samplersDefined = true;
- f.samplers[0].magFilter = GFXTextureFilterPoint;
- f.samplers[0].minFilter = GFXTextureFilterPoint;
- f.samplers[0].addressModeU = GFXAddressClamp;
- f.samplers[0].addressModeV = GFXAddressClamp;
- f.setColorWrites(true, true, true, false); // NOTE: comment this out if alpha write is needed
- mFontSB = GFX->createStateBlock(f);
- }
- }
- void FontRenderBatcher::render( F32 rot, const Point2F &offset )
- {
- if( mLength == 0 )
- return;
- GFX->setStateBlock(mFontSB);
- for(U32 i = 0; i < GFX->getNumSamplers(); i++)
- GFX->setTexture(i, NULL);
- MatrixF rotMatrix;
- bool doRotation = rot != 0.f;
- if(doRotation)
- rotMatrix.set( EulerF( 0.0, 0.0, mDegToRad( rot ) ) );
- // Write verts out.
- U32 currentPt = 0;
- GFXVertexBufferHandle<GFXVertexPCT> verts(GFX, mLength * 6, GFXBufferTypeVolatile);
- verts.lock();
- for( S32 i = 0; i < mSheets.size(); i++ )
- {
- // Do some early outs...
- if(!mSheets[i])
- continue;
- if(!mSheets[i]->numChars)
- continue;
- mSheets[i]->startVertex = currentPt;
- const GFXTextureObject *tex = mFont->getTextureHandle(i);
- for( S32 j = 0; j < mSheets[i]->numChars; j++ )
- {
- // Get some general info to proceed with...
- const CharMarker &m = mSheets[i]->charIndex[j];
- const PlatformFont::CharInfo &ci = mFont->getCharInfo( m.c );
- // Where are we drawing it?
- F32 drawY = offset.y + mFont->getBaseline() - ci.yOrigin * TEXT_MAG;
- F32 drawX = offset.x + m.x + ci.xOrigin;
- // Figure some values.
- const F32 texWidth = (F32)tex->getWidth();
- const F32 texHeight = (F32)tex->getHeight();
- const F32 texLeft = (F32)(ci.xOffset) / texWidth;
- const F32 texRight = (F32)(ci.xOffset + ci.width) / texWidth;
- const F32 texTop = (F32)(ci.yOffset) / texHeight;
- const F32 texBottom = (F32)(ci.yOffset + ci.height) / texHeight;
- const F32 fillConventionOffset = GFX->getFillConventionOffset();
- const F32 screenLeft = drawX - fillConventionOffset;
- const F32 screenRight = drawX - fillConventionOffset + ci.width * TEXT_MAG;
- const F32 screenTop = drawY - fillConventionOffset;
- const F32 screenBottom = drawY - fillConventionOffset + ci.height * TEXT_MAG;
- // Build our vertices. We NEVER read back from the buffer, that's
- // incredibly slow, so for rotation do it into tmp. This code is
- // ugly as sin.
- Point3F tmp;
- tmp.set( screenLeft, screenTop, 0.f );
- if(doRotation)
- rotMatrix.mulP( tmp, &verts[currentPt].point);
- else
- verts[currentPt].point = tmp;
- verts[currentPt].color = m.color;
- verts[currentPt].texCoord.set( texLeft, texTop );
- currentPt++;
- tmp.set( screenLeft, screenBottom, 0.f );
- if(doRotation)
- rotMatrix.mulP( tmp, &verts[currentPt].point);
- else
- verts[currentPt].point = tmp;
- verts[currentPt].color = m.color;
- verts[currentPt].texCoord.set( texLeft, texBottom );
- currentPt++;
- tmp.set( screenRight, screenBottom, 0.f );
- if(doRotation)
- rotMatrix.mulP( tmp, &verts[currentPt].point);
- else
- verts[currentPt].point = tmp;
- verts[currentPt].color = m.color;
- verts[currentPt].texCoord.set( texRight, texBottom );
- currentPt++;
- tmp.set( screenRight, screenBottom, 0.f );
- if(doRotation)
- rotMatrix.mulP( tmp, &verts[currentPt].point);
- else
- verts[currentPt].point = tmp;
- verts[currentPt].color = m.color;
- verts[currentPt].texCoord.set( texRight, texBottom );
- currentPt++;
- tmp.set( screenRight, screenTop, 0.f );
- if(doRotation)
- rotMatrix.mulP( tmp, &verts[currentPt].point);
- else
- verts[currentPt].point = tmp;
- verts[currentPt].color = m.color;
- verts[currentPt].texCoord.set( texRight, texTop );
- currentPt++;
- tmp.set( screenLeft, screenTop, 0.f );
- if(doRotation)
- rotMatrix.mulP( tmp, &verts[currentPt].point);
- else
- verts[currentPt].point = tmp;
- verts[currentPt].color = m.color;
- verts[currentPt].texCoord.set( texLeft, texTop );
- currentPt++;
- }
- }
- verts->unlock();
- AssertFatal(currentPt <= mLength * 6, "FontRenderBatcher::render - too many verts for length of string!");
- GFX->setVertexBuffer(verts);
- GFX->setupGenericShaders( GFXDevice::GSAddColorTexture );
- // Now do an optimal render!
- for( S32 i = 0; i < mSheets.size(); i++ )
- {
- if(!mSheets[i])
- continue;
- if(!mSheets[i]->numChars )
- continue;
-
- GFX->setTexture( 0, mFont->getTextureHandle(i) );
- GFX->drawPrimitive(GFXTriangleList, mSheets[i]->startVertex, mSheets[i]->numChars * 2);
- }
- }
- void FontRenderBatcher::queueChar( UTF16 c, S32 ¤tX, GFXVertexColor ¤tColor )
- {
- const PlatformFont::CharInfo &ci = mFont->getCharInfo( c );
- U32 sidx = ci.bitmapIndex;
- if( ci.width != 0 && ci.height != 0 )
- {
- SheetMarker &sm = getSheetMarker(sidx);
- CharMarker &m = sm.charIndex[sm.numChars];
- sm.numChars++;
- m.c = c;
- m.x = (F32)currentX;
- m.color = currentColor;
- }
- currentX += ci.xIncrement;
- }
- FontRenderBatcher::SheetMarker & FontRenderBatcher::getSheetMarker( U32 sheetID )
- {
- // Add empty sheets up to and including the requested sheet if necessary
- if (mSheets.size() <= sheetID)
- {
- S32 oldSize = mSheets.size();
- mSheets.setSize( sheetID + 1 );
- for ( S32 i = oldSize; i < mSheets.size(); i++ )
- mSheets[i] = NULL;
- }
- // Allocate if it doesn't exist...
- if (!mSheets[sheetID])
- {
- S32 size = sizeof( SheetMarker) + mLength * sizeof( CharMarker );
- mSheets[sheetID] = (SheetMarker *)mStorage.alloc(size);
- mSheets[sheetID]->numChars = 0;
- mSheets[sheetID]->startVertex = 0; // cosmetic initialization
- }
- return *mSheets[sheetID];
- }
- void FontRenderBatcher::init( GFont *font, U32 n )
- {
- // Clear out batched results
- dMemset(mSheets.address(), 0, mSheets.memSize());
- mSheets.clear();
- mStorage.freeBlocks(true);
- mFont = font;
- mLength = n;
- }
|