Game Development Community

Rotating Images

by Robert Fritzen · in Torque 3D Beginner · 01/06/2014 (11:23 am) · 6 replies

So I've decided to give T3D pack development another spin (pun intended for my question) and I can't seem to find anything in the GFX codebase on rotating texture objects (namely gui bitmap controls).

Does anyone know how I can go about rotating bitmap images for usage in GUIs, I need this for a little "toy" I've been working on.

#1
01/06/2014 (2:03 pm)
You'll have to create yourself your own version of GFXDrawUtil::drawBitmapStretchSR() that rotates the vertices points the same way that GFXDrawUtil::draw2DSquare() does.
#2
01/23/2014 (7:56 am)
I seem to be having more of a problem with this than I originally imagined. I've got the code written in a point that it "seems" like it should work, but the problem here is that upon performing the call to the code, it just renders a gray block, and no parts of the image in question whatsoever.

void drawRotatableBitmap(GFXTextureObject* texture, const RectF &dstRect, const RectF &srcRect, F32 spinAngle) {
   // Sanity if no texture is specified.
	if(!texture) {
      return;   
	}

	Point3F offset( dstRect.point.x, dstRect.point.y, 0.0 ), temp;

   GFXVertexBufferHandle<GFXVertexPCT> verts(GFX, 4, 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);

	const F32 fillConv = GFX->getFillConventionOffset(); //mDevice->getFillConventionOffset();
   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 );

	ColorI modulation;
	GFX->getDrawUtil()->getBitmapModulation(&modulation);
	verts[0].color = verts[1].color = verts[2].color = verts[3].color = modulation;

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

	if (spinAngle == 0.0f) {
		for( S32 i = 0; i < 4; i++ ) {
			verts[i].texCoord += offset.asPoint2F();
		}
	}
	else {
      MatrixF rotMatrix( EulerF( 0.0, 0.0, spinAngle ) );

      for( S32 i = 0; i < 4; i++ ) {
			temp.set(verts[i].texCoord.x, verts[i].texCoord.y, 0.0f);
			rotMatrix.mulP( temp );
			//temp now contains the multiplied vertex coords, set accordingly and apply offset
			verts[i].texCoord.x = temp.x;
			verts[i].texCoord.y = temp.y;
			verts[i].texCoord += offset.asPoint2F();
      }
   }

   verts.unlock();

   GFX->setVertexBuffer( verts );

   // Define the state block for the GFX mode.
   GFXStateBlockDesc bitmapStretchSR;
   bitmapStretchSR.setCullMode(GFXCullNone);
   bitmapStretchSR.setZReadWrite(false);
   bitmapStretchSR.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
   bitmapStretchSR.samplersDefined = true;
   bitmapStretchSR.samplers[0] = GFXSamplerStateDesc::getClampLinear();
   GFXStateBlockRef mBitmapStretchLinearSB = GFX->createStateBlock(bitmapStretchSR);
	
	GFX->setStateBlock(mBitmapStretchLinearSB);
 
   GFX->setTexture( 0, texture );
   GFX->setupGenericShaders( GFXDevice::GSModColorTexture );

   GFX->drawPrimitive( GFXTriangleStrip, 0, 2 );
}

I believe the problem lies with Matrix::MulP, seeing as TexCoord, the coordinates I need to "rotate" is in a Point2F form where as mulP requires a Point3F. I'm not trying to rotate the control, but the image inside the control.

The reason this is a problem is because Matrix Multiplication is obviously handled on a row by row basis and simply defining a zero in the 3rd coordinate probably won't work. Any suggestions?
#3
01/23/2014 (10:36 pm)
I got it all fixed and working for you shown with this screenshot drawing some icons in my text list control:
dottools.net/downloads/imgs/t3d/modernTextListCtrl_RotatedIcons.png
Using this function call:
drawRotatableBitmap(bitmap->texture, bmPos, GFXBitmapFlip_None, GFXTextureFilterPoint, true, mDegToRad(-90.0f));

Actual implementation:
void drawRotatableBitmap(GFXTextureObject* texture, const RectF &dstRect, const RectF &srcRect, F32 spinAngle)
{
	// Sanity if no texture is specified.
	if(!texture)
		return;

	GFXDevice		*mDevice			= GFX;
	GFXVertexColor	mBitmapModulation	= ColorI( 255, 255, 255, 255 );


	GFXVertexBufferHandle<GFXVertexPCT> verts(mDevice, 4, 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);


	const F32 fillConv = mDevice->getFillConventionOffset();
	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 );

	if(spinAngle != 0.f)
	{
		MatrixF rotMatrix( EulerF( 0.0, 0.0, spinAngle ) );
		Point3F	offset( dstRect.point.x + dstRect.extent.x / 2,
						dstRect.point.y + dstRect.extent.y / 2,
						0.0f);

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

	verts.unlock();

	mDevice->setVertexBuffer( verts );

	// Define the state block for the GFX mode.
	// DrawBitmapStretchSR
	GFXStateBlockDesc bitmapStretchSR;
	bitmapStretchSR.setCullMode(GFXCullNone);
	bitmapStretchSR.setZReadWrite(false);
	bitmapStretchSR.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
	bitmapStretchSR.samplersDefined = true;

	// Linear: Create clamp SB
	bitmapStretchSR.samplers[0] = GFXSamplerStateDesc::getClampLinear();
	GFXStateBlockRef mBitmapStretchLinearSB = mDevice->createStateBlock(bitmapStretchSR);

	mDevice->setStateBlock(mBitmapStretchLinearSB);
	mDevice->setTexture( 0, texture );
	mDevice->setupGenericShaders( GFXDevice::GSModColorTexture );
	mDevice->drawPrimitive( GFXTriangleStrip, 0, 2 );

}

void drawRotatableBitmap(GFXTextureObject* texture, const Point2I &in_rAt, const GFXBitmapFlip in_flip, const GFXTextureFilterType filter , bool in_wrap /*= true*/, F32 spinAngle /*= 0.0f*/)
{
   AssertFatal( texture != 0, "No texture specified for drawBitmap()" );

   RectF subRegion( 0.0f, 0.0f, (F32)texture->mBitmapSize.x, (F32)texture->mBitmapSize.y );
   RectF stretch( (F32)in_rAt.x, (F32)in_rAt.y, (F32)texture->mBitmapSize.x, (F32)texture->mBitmapSize.y );
   drawRotatableBitmap( texture, stretch, subRegion, spinAngle );
}


The second drawRotatableBitmap function only exists to pretty much match GDXDrawUtil::drawBitmap() function with the addition of the rotation angle parameter. Enjoy!
#4
01/24/2014 (8:11 am)
And it's reasons like this that I just love this community. Thanks a bunch, you saved me a ton of time trying to figure that out.. :x

EDIT: Here's to show what I'm "up to" per say. :)
I've got to fix the math calculations a little bit, and a few more renders are needed, but overall, this is good progress to me.

Because My Picture is Too Big
#5
01/24/2014 (10:41 am)
Ah, the ol' rotating minimap....

I'm still trying to get the minimap resource to render more than a gray box, and keep it from crashing when I open the GUI editor... lol
#6
01/24/2014 (10:45 am)
@Richard: This is not the mini-map resource, It's one I'm writing myself for my new pack, using nothing more than mathematical distance calculations :)

Also, since I'm doing pack spoilers right now, I thought I'd share this little thing I made earlier:
Shiny Eye Candy.

All I can say, is this pack is likely to be the most exciting and resourceful one I've made to date.