Game Development Community

Transparency Mask for GUI objects

by Aldavidson · in Torque 3D Professional · 09/08/2009 (11:09 am) · 8 replies

Hi guys,
Does T3D support a transparency mask for GUI objects?
I'm in the process of creating my overview GUI object, and im wondering what the best aproach is to get the gui object to display in the shape that i want it too.

Below is a screenshot that demonstrates what im trying to acheive:
img197.imageshack.us/img197/3747/overviewl.png
Im trying to exclude anything with the blue lines over it from being rendered (or atleast shown). The best aproach i came up with was to use a transparency mask on the GUI object but i dont know if that is supported by T3D or if their is an alternative method?

Any help guys?

#1
09/08/2009 (1:33 pm)
As far as I know there isn't a transparency mask. From the looks of you image it looks like you already have done the work though to get overhead view into a texture handle. The good news is that once you have that your not to far from where you want to be. In my guiAdvBarHud I have a function called drawPartialDisk based off of the openGL GLU function. You should be able to add this to your control and then use it to render the radar in a circle, or even in a partial circle. Below is the function definition (to large to post full function) you will need to pull down the entire control file to get all the pieces for it. A breakdown of the params are as follows:

center: the center point to start drawing the disk on the screen.
color: Any color modulation you want to apply.
innerRadius: Use this to create a hollow disk or doughnut.
outerRadius: How big to make the disk.
slices: the number of segments that makes up the disk, the more segments the smoother the disk. I use 3 for every 90 degrees.
loops: The number of inner segments usually leave at 1.
startAngle: The angle to start rendering the disk at.
sweepAngle: How much of the disk to draw. use 360 for a complete disk.
texture: The texture to draw onto the disk.

void guiAdvBarHud::drawPartialDisk(const Point2I &center, const ColorI &color, F64 innerRadius, F64 outerRadius, S32 slices, S32 loops, F64 startAngle, F64 sweepAngle,  GFXTextureObject *texture)

Hopefully that helps point you in the right direction. Let me know if you need anything else.
#2
12/22/2009 (8:10 pm)
Hi Ryan, Thanks for your input so far.
Unfortunately my Background (and most other textures seen on the hud) are being rendered with GFXDrawUtil::drawBitmapRotCtr...
void GFXDrawUtil::drawBitmapRotCtr(GFXTextureObject *texture,const RectI& dstRect,const RectI& srcRect,const GFXBitmapFlip in_flip,F32 spinAngle, const Point2F spinOffset, F32 X, F32 Y)
{
    if(!texture)
      return;

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

   F32 texLeft   = F32(srcRect.point.x)                    / F32(texture->mTextureSize.x);
   F32 texRight  = F32(srcRect.point.x + srcRect.extent.x) / F32(texture->mTextureSize.x);
   F32 texTop    = F32(srcRect.point.y)                    / F32(texture->mTextureSize.y);
   F32 texBottom = F32(srcRect.point.y + srcRect.extent.y) / 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;
   }

   // spin angle is in degree's so convert to radians..radians = degrees * pi /180
   spinAngle = spinAngle * 3.14 / 180.0f;

   Point2F screenPoint(X,Y);

   F32 width = dstRect.extent.x;
   width *= 0.5;

   MatrixF rotMatrix( EulerF( 0.0, 0.0, spinAngle ) );

 
   Point3F offset( screenPoint.x, screenPoint.y, 0.0 );

    //const F32 fillConv = mDevice->getFillConventionOffset();
   verts[0].point.set( -width + spinOffset.x, -width + spinOffset.y, 0.0 );
   verts[1].point.set( -width + spinOffset.x,  width + spinOffset.y, 0.0 );
   verts[2].point.set( width + spinOffset.x,  width + spinOffset.y, 0.0 );
   verts[3].point.set( width + spinOffset.x, -width + spinOffset.y, 0.0 );

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

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

    for( int i=0; i<4; i++ )
   {
      rotMatrix.mulP( verts[i].point );
      verts[i].point += offset;
   }

   verts.unlock();
 
   mDevice->setVertexBuffer( verts );
  
 

   mDevice->setStateBlock(mBitmapStretchSB);
   mDevice->setTexture( 0, texture );
   mDevice->setupGenericShaders( GFXDevice::GSModColorTexture );

   mDevice->drawPrimitive(GFXTriangleFan, 0, 2 );

}

I'm not sure how on earth to get from this stage of having a rotated texture being displayed on screen to getting it only rendered in a circle.... (alas i have no clue on how to use your code with my needs).

Is it possible to somehow "cut-out" a circle shaped set of verts from the 4 verts produced from drawBitmapRotCtr and then display that?

Any help?
#3
12/22/2009 (10:01 pm)
It should be possible to create a hybrid of the two to accomplish what you need. I'm in the middle a project at the moment but I will see if I can put something together.
#4
12/23/2009 (7:29 am)
Thanks alot Ryan, I'll continue to give this a stab as well.
#5
12/31/2009 (4:25 pm)
@Aldavidson, from the image above I am assuming you are using a camera to take an overhead image in real time. Does the image rotate with the player or does it always stay in a fixed orientation? If it rotates with the player are you rotating the camera to match the players rotation before you collect the image?

I'm just trying to determine how you are aligning your image. If the image is already set up when it comes into the method above then that makes it a bit easier. If the image needs to be rotated in the function that complicates it a little.
#6
12/31/2009 (6:18 pm)
Ok, I think I may have what you need. In gfxDrawUtil.h add this:

void GFXDrawUtil::drawDisk(const Point2I &center, const ColorI &color, F64 outerRadius, F32 spinAngle, GFXTextureObject *texture);

int gfxDrawUtil.cpp add:
void GFXDrawUtil::drawDisk(const Point2I &center, const ColorI &color, F64 outerRadius, F32 spinAngle, GFXTextureObject *texture);

void GFXDrawUtil::drawDisk(const Point2I &center, const ColorI &color, F64 outerRadius, F32 spinAngle, GFXTextureObject *texture)
{
	//This is the number of segments in the disk
	S32 slices = 48;
	//This is the start angle for the disk
	F64 startAngle = 0;
	//This is the final angle for the disk
	F64 sweepAngle = 360;
	//This is the number of loops only one is supported
	S32 loops = 1;

	//Define our state blocks
	GFXStateBlockDesc mDiskBitmapTexture;
	GFXStateBlockRef diskBitmapTextureSB;
	mDiskBitmapTexture.setCullMode(GFXCullNone);
	mDiskBitmapTexture.zEnable = false;
	mDiskBitmapTexture.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
	mDiskBitmapTexture.samplersDefined = true;
	mDiskBitmapTexture.samplers[0].textureColorOp = GFXTOPModulate;
	mDiskBitmapTexture.samplers[1].textureColorOp = GFXTOPDisable;
	diskBitmapTextureSB = GFX->createStateBlock(mDiskBitmapTexture);

	GFXStateBlockDesc mDiskNoBitmapTexture;
	GFXStateBlockRef noDiskBitmapTextureSB;
	mDiskNoBitmapTexture.setCullMode(GFXCullNone);
	mDiskNoBitmapTexture.zEnable = false;
	mDiskNoBitmapTexture.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
	noDiskBitmapTextureSB = GFX->createStateBlock(mDiskNoBitmapTexture);


	//Set up the texture
	GFX->setTexture(0, texture);

	//Determine which state blocks to use
	if (!texture){
		PrimBuild::color4i(color.red,color.green,color.blue,color.alpha);
		GFX->setStateBlock(noBitmapTextureSB);
		GFX->setupGenericShaders( GFXDevice::GSColor );
	}
	else{
		F32 newAlpha = color.alpha/255.0f;
		PrimBuild::color4f(1, 1, 1, newAlpha);
		GFX->setStateBlock(bitmapTextureSB);
		GFX->setupGenericShaders( GFXDevice::GSModColorTexture );
	}

    //Define our variables for rendering coordinates
    F32 sinCache[48], cosCache[48];
    F32 angle, radiusLow;
    F32 texLow, angleOffset;

	GFX->pushWorldMatrix();

	MatrixF t(true); 
	t.setPosition( Point3F( center.x, center.y, 0 ) ); 
	GFX->multWorld( t ); 

    /* Cache is the vertex locations cache */
    angleOffset = startAngle / 180.0 * M_PI;

	S32 i;
    for (i = 0; i <= slices; i++) 
	{
		angle = angleOffset + ((M_PI * sweepAngle) / 180.0) * i / slices;
		sinCache[i] = mSin(angle);
		cosCache[i] = mCos(angle);
    }

	//Make sure we end where we started
	sinCache[slices] = sinCache[0];
	cosCache[slices] = cosCache[0];

	// spin angle is in degree's so convert to radians..radians = degrees * pi /180
	spinAngle = spinAngle * 3.14 / 180.0f;
	MatrixF rotMatrix( EulerF( 0.0, 0.0, spinAngle ) );

	//Begin rendering the disk
	/* Triangle strip for inner polygons */
	PrimBuild::begin(GFXTriangleFan,(slices * loops) + 2);
	PrimBuild::texCoord2f(0.5, 0.5);
			    
	PrimBuild::vertex3f(0.0, 0.0, 0.0);
	radiusLow = outerRadius;
	texLow = radiusLow / outerRadius / 2;

	for (i = slices; i >= 0; i--){
		//Plot the texture and vertex coordinates
		Point3F ptTexCoord((texLow * sinCache[i] + 0.5), (texLow * cosCache[i] + 0.5), 0.0);
		Point3F ptVertexCoord(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0);

		//Rotate the vertex coordinate
		rotMatrix.mulP( ptVertexCoord );

		//Send to the PrimBuilder
		PrimBuild::texCoord2f(ptTexCoord.x, ptTexCoord.y);
		PrimBuild::vertex3f(ptVertexCoord.x, ptVertexCoord.y, ptVertexCoord.z);
		
	}

	//This causes the disk to be rendered
	PrimBuild::end();
 
	GFX->popWorldMatrix();
}

Use that instead of your function and you should be good to go. Let me know if you have any questions.
#7
11/07/2011 (7:35 am)
Sorry for necroing this thread.

Ryan, thanks for that snippet! Today I was wondering how to solve the same problem, and your idea set me in the right direction. So I wanted to share my solution in exchange.

It's compatible with T3D 1.1 Final and works just like the rest of the drawBitmap... functions. The width and height of the disc is set by the width and height of the destination rectangle. You can specify a texture for it. Only the part covered by the circle will be displayed. (We use a large pre-drawn minimap that has only a given area displayed at a time underneath a circular compass)

It is hardwired to work with 48 fans (or slices) to give you a smooth circular shape.

Add this to gfxDrawUtil.cpp:
void GFXDrawUtil::drawBitmapStretchSRDisk( GFXTextureObject *texture, const RectI &dstRect, const RectI &srcRect, const GFXBitmapFlip in_flip, const GFXTextureFilterType filter , bool in_wrap /*= true*/ ) 
{
   RectF dstRectF = RectF((F32)dstRect.point.x,(F32)dstRect.point.y,(F32)dstRect.extent.x,(F32)dstRect.extent.y);
   RectF srcRectF = RectF((F32)srcRect.point.x,(F32)srcRect.point.y,(F32)srcRect.extent.x,(F32)srcRect.extent.y);
   drawBitmapStretchSRDisk(texture,dstRectF,srcRectF,in_flip,filter,in_wrap);
}

void GFXDrawUtil::drawBitmapStretchSRDisk(GFXTextureObject* texture, const RectF &dstRect, const RectF &srcRect, const GFXBitmapFlip in_flip /*= GFXBitmapFlip_None*/, const GFXTextureFilterType filter /*= GFXTextureFilterPoint */ , bool in_wrap /*= true*/)
{
   if (!texture)
      return;

   S32 fans = 48;

   GFXVertexBufferHandle<GFXVertexPCT> verts(mDevice, fans+2, GFXBufferTypeVolatile );
   verts.lock();

   F32 texLeft   = (srcRect.point.x)                    / (texture->mTextureSize.x);
   F32 texRight  = (srcRect.point.x + srcRect.extent.x) / (texture->mTextureSize.x);
   F32 texTop    = (srcRect.point.y)                    / (texture->mTextureSize.y);
   F32 texBottom = (srcRect.point.y + srcRect.extent.y) / (texture->mTextureSize.y);

   F32 screenLeft   = dstRect.point.x;
   F32 screenRight  = (dstRect.point.x + dstRect.extent.x);
   F32 screenTop    = dstRect.point.y;
   F32 screenBottom = (dstRect.point.y + dstRect.extent.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();
   Point2F screenRadii = Point2F((screenRight - screenLeft) / 2, (screenBottom - screenTop) / 2);
   Point2F texRadii = Point2F((texRight - texLeft) / 2, (texBottom - texTop) / 2);

   verts[0].point.set( screenLeft + screenRadii.x - fillConv,  screenTop + screenRadii.y - fillConv, 0.f ); // fan center
   verts[0].color = mBitmapModulation;
   verts[0].texCoord.set( texLeft + texRadii.x, texTop + texRadii.y); // fan texture center

   // fans number of points around the center point
   F64 angle;
   for (S32 v=0;v<=fans;v++) {
      angle = v * (360.0f / fans) * M_PI_F / 180.0f; // in radians
      verts[v+1].point.set( screenLeft + (screenRadii.x * (1 + mSin(angle))) - fillConv, screenTop + (screenRadii.y * (1 - mCos(angle))) - fillConv, 0.f);
      verts[v+1].color = mBitmapModulation;
      verts[v+1].texCoord.set( texLeft + texRadii.x * (1 + mSin(angle)), texTop + (texRadii.y * (1 - mCos(angle))) );
   }

   verts.unlock();

   mDevice->setVertexBuffer( verts );

   switch (filter)
   {
   case GFXTextureFilterPoint :
      mDevice->setStateBlock(in_wrap ? mBitmapStretchWrapSB : mBitmapStretchSB);
      break;
   case GFXTextureFilterLinear :
      mDevice->setStateBlock(in_wrap ? mBitmapStretchWrapLinearSB : mBitmapStretchLinearSB);
      break;
   default:
      AssertFatal(false, "No GFXDrawUtil state block defined for this filter type!");
      mDevice->setStateBlock(mBitmapStretchSB);
      break;
   }   
   mDevice->setTexture( 0, texture );
   mDevice->setupGenericShaders( GFXDevice::GSModColorTexture );

   mDevice->drawPrimitive( GFXTriangleFan, 0, fans );
}

...and don't forget the header file:
void drawBitmapStretchSRDisk(GFXTextureObject* texture, const RectI &dstRect, const RectI &srcRect, const GFXBitmapFlip in_flip = GFXBitmapFlip_None, const GFXTextureFilterType filter = GFXTextureFilterPoint, bool in_wrap = true );
   void drawBitmapStretchSRDisk(GFXTextureObject* texture, const RectF &dstRect, const RectF &srcRect, const GFXBitmapFlip in_flip = GFXBitmapFlip_None, const GFXTextureFilterType filter = GFXTextureFilterPoint, bool in_wrap = true );

Thanks again, hope this comes handy for someone.

Edit: work in progress shot of the thing...

pub.bitgap.com/mmap.jpg
#8
11/07/2011 (2:04 pm)
Konrad, glad you necroed this! =) That is something I've been needing as well. Thanks!