Game Development Community

GFXDrawUtil::drawBitmapTiledSR: draw a tile fill from a bitmap section (TGEA 1.7.1)

by Jaimi McEntire · 01/28/2009 (7:03 am) · 1 comments

This is a function you can add to GFXDrawUtil that will tile a rectangle from a source rectangle in a texture. This is used by several of my other GUI Controls. Pass the texture, the destination rectangle on the screen, and the source rectangle in the texture. I have tested this on various nVidia and ATI chipsets and did not see any texture artifacts. I will add a port to 1.8.X when I get around to porting to the newer version (afx is ported, 1.8.1 is released, etc.)

In gfxDrawUtil.h, add the following declaration after the declaration for drawBitmapStretchSR:

void drawBitmapTiledSR( GFXTextureObject*texture, const RectI &dstRect, const RectI &srcRect, 
const GFXBitmapFlip in_flip = GFXBitmapFlip_None );

In gfxDrawUtil.cpp, add the function itself after the drawBitmapStretchSR function:

void GFXDrawUtil::drawBitmapTiledSR( GFXTextureObject*texture, const RectI &dstRect, 
const RectI &srcRect, const GFXBitmapFlip in_flip)
{
   // Sanity if no texture is specified.
   if(!texture)
      return;
	int xreps = dstRect.extent.x / srcRect.extent.x;
	if (xreps * srcRect.extent.x != dstRect.extent.x) xreps++;  // add one for the remainder, if any.

	int yreps = dstRect.extent.y / srcRect.extent.y;
	if (yreps * srcRect.extent.y != dstRect.extent.y) yreps++;  // add one for the remainder, if any.


   mDevice->setBaseRenderState();

   GFXVertexBufferHandle<GFXVertexPCT> verts(mDevice, 4, GFXBufferTypeVolatile );

   // Calculate the source coordinates


   // Set all the device properties one time.
   mDevice->setCullMode( GFXCullNone );
   mDevice->setLightingEnable( false );
   mDevice->setAlphaBlendEnable( true );
   mDevice->setSrcBlend( GFXBlendSrcAlpha );
   mDevice->setDestBlend( GFXBlendInvSrcAlpha );
   mDevice->setTextureStageColorOp( 0, GFXTOPModulate );
   mDevice->setTextureStageColorOp( 1, GFXTOPDisable );
   mDevice->setTexture( 0, texture );
   mDevice->setupGenericShaders( GFXDevice::GSModColorTexture );

   // calculate the right and bottom edge of the destination.
   int TexW = srcRect.extent.x;
   int TexH = srcRect.extent.y;
   int ScreenMaxX = dstRect.point.x + dstRect.extent.x;
   int ScreenMaxY = dstRect.point.y + dstRect.extent.y;

   for (int xx = 0; xx<xreps; xx++)
   {
	   for (int yy=0; yy<yreps; yy++)
	   {
		   // TODO: Calculate dest rect.
		   // TODO: Adjust src rect if it's too large for whats left.

			// Get the location on the screen for this rep.
			int dstLeft   = dstRect.point.x + (xx * TexW);
			int dstRight  = dstLeft + TexW;
			int dstTop    = dstRect.point.y + (yy * TexH);
			int dstBottom = dstTop + TexH;

			// Adjust the source and destination rectangles to fit in the dstRect.
			int WidthAdjust  = 0;
			int HeightAdjust = 0;
			if (dstRight > ScreenMaxX)
			{
				WidthAdjust = dstRight - ScreenMaxX;
				dstRight = ScreenMaxX;
			}
			if (dstBottom > ScreenMaxY)
			{
				HeightAdjust = dstBottom - ScreenMaxY;
				dstBottom = ScreenMaxY;
			}

			// Calculate the position on the screen for this rep.
			F32 screenLeft   = (F32)dstLeft;
			F32 screenRight  = (F32)dstRight;
			F32 screenTop    = (F32)dstTop;
			F32 screenBottom = (F32)dstBottom;

			// Calculate the position in the source texture for this rep. We need to do this every time, because there is 
			// most likely an end rep where we are only moving part of the source.
			F32 texLeft   = F32(srcRect.point.x)                                   
/ F32(texture->mTextureSize.x);
			F32 texRight  = F32((srcRect.point.x + srcRect.extent.x)-WidthAdjust)  
/ F32(texture->mTextureSize.x);
			F32 texTop    = F32(srcRect.point.y)                                   
/ F32(texture->mTextureSize.y);
			F32 texBottom = F32((srcRect.point.y + srcRect.extent.y)-HeightAdjust) 
/ F32(texture->mTextureSize.y);

			if( in_flip & GFXBitmapFlip_X ) 
			{
				  F32 temp = texLeft;
				  texLeft = texRight;
				  texRight = temp;
			}
			if( in_flip & GFXBitmapFlip_Y ) 
			{
				  F32 temp = texTop;
				  texTop = texBottom;
				  texBottom = temp;
			}

			const F32 fillConv = mDevice->getFillConventionOffset();
			verts.lock();
			verts[0].point.set( screenLeft  - fillConv, screenTop    - fillConv, 0.f );
			verts[1].point.set( screenRight - fillConv, screenTop    - fillConv, 0.f );
			verts[2].point.set( screenLeft  - fillConv, screenBottom - fillConv, 0.f );
			verts[3].point.set( screenRight - fillConv, screenBottom - fillConv, 0.f );

			verts[0].color = verts[1].color = verts[2].color = verts[3].color = mBitmapModulation;

			verts[0].texCoord.set( texLeft,  texTop );
			verts[1].texCoord.set( texRight, texTop );
			verts[2].texCoord.set( texLeft,  texBottom );
			verts[3].texCoord.set( texRight, texBottom );

			verts.unlock();

			mDevice->setVertexBuffer( verts );
		    mDevice->drawPrimitive( GFXTriangleStrip, 0, 2 );
	   }
   }
   mDevice->setAlphaBlendEnable( false );	
}


This function differs from "doing it manually" by setting the texture and render states only one time.

#1
01/28/2009 (11:36 am)
the CODE tag appears to be truncating lines of source code.... I've updated the block above so that you can still get to all of the code.