Game Development Community

dev|Pro Game Development Curriculum

fxGuiSnooper Updated for T3D 3.0 MIT w/ AFX 2.0.

by Paul Weston · 02/12/2014 (12:23 pm) · 3 comments

ADMINS - Please post to Resources

Previous T3D port: www.garagegames.com/community/forums/viewthread/127227

This updated code eliminates the playgui being re-drawn within the fxGuiSnooper control, when you mouse over or select an enemy/object with the mouse in AFX.

Problem was related to the AFX highlighting render manager.

AFX highlighting of enemies/objects still works with this code, just does not get applied within the Snooper viewport. All postfx are now disabled within the viewport actually, so if this is an issue for you do not use this version of the resource.

Credit to Alfio for pointing us in the right direction in the thread for the previous port.

Anyhow, here you go, enjoy:

Create a new file called fxGuiSnooper.cpp, paste this code into it, and then add the file to your engine source code. Include it in your project however you like. Then recompile and run.

#include "T3D/gameBase/gameConnection.h"
#include "gui/core/guiCanvas.h"
#include "console/consoleTypes.h"
#include "gui/3d/guiTSControl.h"
#include "gfx/gfxDrawUtil.h"
#include "gui/core/guiControl.h"
#include "renderInstance/renderPassManager.h"
#include "gfx/gfxDevice.h"
#include "gfx/gfxTransformSaver.h"
#include "gfx/primBuilder.h"
#include "scene/sceneManager.h"
#include "scene/sceneRenderState.h"
#include "renderInstance/renderBinManager.h"

class SceneManager;
class SceneRenderState;
class BaseMatInstance;
class SceneObject;


class fxGuiSnooper : public GuiTSCtrl
{
private:
	typedef GuiTSCtrl Parent;

	Point3F				mRotateView;					// View Rotation.
	Point3F				mOffsetView;					// Offset Distance.
	F32					mFov;							// Field of View.
	Point3F				mSweepAmplitude;				// Sweep Amplitude.
	Point3F				mSweepTime;						// Sweep Time.

	bool				mUseOverlayBitmap;				// Use Overlay Bitmap Flag.
	bool				mUseOverlayColour;				// Use Overlay Colour Flag.
	bool				mOverlayTile;					// Overlay Tile Flag.
	ColorF				mOverlayColor;					// Filter Colour Vector.
	bool				mOverlayRedMask;				// Overlay Red Mask Flag.
	bool				mOverlayGreenMask;				// Overlay Green Mask Flag.
	bool				mOverlayBlueMask;				// Overlay Blue Mask Flag.

	StringTableEntry	mObjectName;					// Attached Object Name.
	SceneObject*		mAttachedObject;				// Attached Object.

	U32					mLastTimeStamp;					// Last Time Stamp.
	Point3F				mCurrentSweepMagnitude;			// Current Sweep Phase.
	StringTableEntry	mOverlayBitmapName;				// Overlay Bitmap Name.
	GFXTexHandle		mOverlayTextureHandle;			// Overlay Texture Handle.

    GFXStateBlockRef mSnooperClearSB;
    GFXStateBlockRef mDzEnableSB;
	GFXStateBlockRef mEcwSB;
	GFXStateBlockRef mEcwResetSB;

    virtual void renderScene(const RectI &){};

	void renderWorld(const RectI & updateRect);
	void onRender(Point2I offset, const RectI &updateRect);
	void _renderScene( ObjectRenderInst*, SceneRenderState *state, BaseMatInstance* );

public:
	fxGuiSnooper();

	static void initPersistFields();
	static void consoleInit();

	bool processCameraQuery(CameraQuery * query);

	void setViewObject(const char* ObjectName);
	void setViewRotation(Point3F Rotation);
	void setOverlayBitmap(const char *name);
	void setOverlayColor(ColorF OverlayColor);
	void setOverlayMask(bool RedMask, bool GreenMask, bool BlueMask);

	bool onWake();
	void onSleep();

	DECLARE_CONOBJECT(fxGuiSnooper);
};

//------------------------------------------------------------------------------

IMPLEMENT_CONOBJECT(fxGuiSnooper);

//------------------------------------------------------------------------------


fxGuiSnooper::fxGuiSnooper() :
	mRotateView(0,0,0),
	mOffsetView(0,0,0),
	mFov(60.0),
	mSweepAmplitude(0,0,60),
	mSweepTime(5000,5000,5000),
	mCurrentSweepMagnitude(0,0,0),
	mAttachedObject(NULL),
	mOverlayRedMask(true),
	mOverlayGreenMask(true),
	mOverlayBlueMask(true),
	mOverlayTile(false),
	mUseOverlayBitmap(false),
	mUseOverlayColour(false),
	mLastTimeStamp(Platform::getRealMilliseconds())
{
	// Create Empty Attached Object Name.
	mObjectName = StringTable->insert("");

	// Create Empty Overlay Bitmap Name.
	mOverlayBitmapName = StringTable->insert("");

	// Set Default Overlay Colour.
	mOverlayColor.set(1, 1, 1, 0.5f);
};

//------------------------------------------------------------------------------

void fxGuiSnooper::initPersistFields()
{
	// Initialise parents' persistent fields.
	Parent::initPersistFields();

	// Add out own persistent fields.
	addField( "ViewRotation", TypePoint3F, Offset( mRotateView, fxGuiSnooper ) );
	addField( "ViewOffset", TypePoint3F, Offset( mOffsetView, fxGuiSnooper ) );
	addField( "FOV", TypeF32, Offset( mFov, fxGuiSnooper ) );
	addField( "SweepAmplitude", TypePoint3F, Offset( mSweepAmplitude, fxGuiSnooper ) );
	addField( "SweepTime", TypePoint3F, Offset( mSweepTime, fxGuiSnooper ) );
	addField( "AttachedObject", TypeString, Offset( mObjectName, fxGuiSnooper ) );
	addField( "OverlayBitmap", TypeBool, Offset( mUseOverlayBitmap, fxGuiSnooper ) );
	addField( "OverlayTile", TypeBool, Offset( mOverlayTile, fxGuiSnooper ) );
	addField( "OverlayColour", TypeBool, Offset( mUseOverlayColour, fxGuiSnooper ) );
	addField( "BitmapOverlay", TypeFilename, Offset(mOverlayBitmapName, fxGuiSnooper));
	addField( "ColorOverlay", TypeColorF, Offset( mOverlayColor, fxGuiSnooper ) );
	addField( "RedMask", TypeBool, Offset( mOverlayRedMask, fxGuiSnooper ) );
	addField( "GreenMask", TypeBool, Offset( mOverlayGreenMask, fxGuiSnooper ) );
	addField( "BlueMask", TypeBool, Offset( mOverlayBlueMask, fxGuiSnooper ) );
}

//------------------------------------------------------------------------------

bool fxGuiSnooper::onWake()
{
	// Wake-up Parent.
	if (!Parent::onWake()) return false;

	// Set Active.
	setActive(true);

	// Have we an Attached Object Name?
	if (mObjectName)
	{
		// Yes, so attach to it.
		setViewObject(mObjectName);
	}

	// Set Overlay Bitmap.
	setOverlayBitmap(mOverlayBitmapName);

	// Return OK.
	return true;
}

//------------------------------------------------------------------------------

void fxGuiSnooper::onSleep()
{
	// Reset Overlay Texture Handle.
	mOverlayTextureHandle = NULL;
	// Call Parent.
	Parent::onSleep();
}

//------------------------------------------------------------------------------

void fxGuiSnooper::setViewRotation(Point3F Rotation)
{
	// Set the Rotation internally.
	mRotateView = Rotation;
}

//------------------------------------------------------------------------------

void fxGuiSnooper::setViewObject(const char* ObjectName)
{
	// Get Root Group.
	SimGroup* SG = Sim::getRootGroup();

	// Interate Sim Group.
	for (SimSetIterator itr(SG); *itr; ++itr)
	{
		// Yes, so cast our Object.
		SceneObject* SceneObj = static_cast<SceneObject*>(*itr);
		// Check that it's a Server Object.
		if (SceneObj->isServerObject())
		{
			const char* getName;

			getName = SceneObj->getName();

			// Yes, so is this our Object?
			if (getName && dStrcmp(getName, ObjectName) == 0)
			{
				// Store Scene Object.
				mAttachedObject = SceneObj;

				// Return OK.
				return;
			}
		}
	}

	// Reset Object.
	mAttachedObject = NULL;
}

//------------------------------------------------------------------------------

void fxGuiSnooper::setOverlayBitmap(const char *name)
{
	// Set Overlay Bitmap Name.
	mOverlayBitmapName = StringTable->insert(name);

	if (*mOverlayBitmapName)
		// Yes, so get Texture Handle.
	    mOverlayTextureHandle.set( mOverlayBitmapName, &GFXDefaultGUIProfile, "" );
	else
		// No, so reset Texture Handle.
		mOverlayTextureHandle = NULL;

	// Update.
	setUpdate();
}   

//------------------------------------------------------------------------------

void fxGuiSnooper::setOverlayColor(ColorF OverlayColor)
{
	// Set the Overlay Colour internally.
	mOverlayColor = OverlayColor;
}

//------------------------------------------------------------------------------

void fxGuiSnooper::setOverlayMask(bool RedMask, bool GreenMask, bool BlueMask)
{
	// Set the Overlay Masks internally.
	mOverlayRedMask		= RedMask;
	mOverlayGreenMask	= GreenMask;
	mOverlayBlueMask	= BlueMask;
}

//------------------------------------------------------------------------------

ConsoleMethod(fxGuiSnooper,setViewRotation,void,5,5,"Sets View Rotation.")
{
	Point3F		Rotation;

	// Fetch the fxGuiSnooper object.
	fxGuiSnooper *Viewport = static_cast<fxGuiSnooper*>(object);

	// Fetch Rotation.
	Rotation.set(	mDegToRad(dAtof(argv[2])),
					mDegToRad(dAtof(argv[3])),
					mDegToRad(dAtof(argv[4])));

	// Set Rotation.
	Viewport->setViewRotation(Rotation);
}

//------------------------------------------------------------------------------

ConsoleMethod(fxGuiSnooper,setViewObject,void,3,3,"Sets View to Object.")
{
	// Fetch the fxGuiSnooper object.
	fxGuiSnooper *Viewport = static_cast<fxGuiSnooper*>(object);

	// Set the GuiFilter Filter Colour.
	Viewport->setViewObject(argv[2]);
}

//------------------------------------------------------------------------------

ConsoleMethod(fxGuiSnooper,setOverlayBitmap,void,3,3,"Sets Overlay Bitmap.")
{
	// Fetch the fxGuiSnooper object.
	fxGuiSnooper *Viewport = static_cast<fxGuiSnooper*>(object);

	// Set Overlay Bitmap.
	Viewport->setOverlayBitmap(argv[2]);
}

//------------------------------------------------------------------------------

ConsoleMethod(fxGuiSnooper,setOverlayColor,void,5,6,"Sets Overlay Color.")
{
	F32		r,g,b,a;
	ColorF	TempColor;

	// Fetch the fxGuiSnooper object.
	fxGuiSnooper *Viewport = static_cast<fxGuiSnooper*>(object);

	// Convert RGB Ascii parms to float.
	r = dAtof(argv[2]);
	g = dAtof(argv[3]);
	b = dAtof(argv[4]);

	// Did we get an alpha param?
	if (argc == 6)
		// Yep, so convert it.
		a = dAtof(argv[5]);
	else
		// Nope, so default to 1.
		a = 1;

	// Setup our temporary colour vector.
	TempColor.set(r,g,b,a);

	// Set the fxGuiSnooper Overlay Colour.
	Viewport->setOverlayColor(TempColor);
}

//------------------------------------------------------------------------------

ConsoleMethod(fxGuiSnooper,setOverlayMask,void,5,5,"Sets Overlay Masks.")
{
	F32		r,g,b;

	// Fetch the fxGuiSnooper object.
	fxGuiSnooper *Viewport = static_cast<fxGuiSnooper*>(object);

	// Convert RGB Ascii parms to float.
	r = dAtof(argv[2]);
	g = dAtof(argv[3]);
	b = dAtof(argv[4]);

	// Set the fxGuiSnooper Masks.
	Viewport->setOverlayMask(r,g,b);
}

//------------------------------------------------------------------------------

static void cfxGuiBitmapSetOverlayBitmap(SimObject *obj, S32, const char **argv)
{
	// Fetch HUD Control.
	fxGuiSnooper *ctrl = static_cast<fxGuiSnooper*>(obj);

	// Set Overlay Bitmap.
	ctrl->setOverlayBitmap(argv[2]);
}

//------------------------------------------------------------------------------

void fxGuiSnooper::consoleInit()
{
   Con::addCommand("fxGuiSnooper", "setOverlayBitmap",  cfxGuiBitmapSetOverlayBitmap, "fxGuiSnooper.setOverlayBitmap(Bitmap)", 3, 3);
}

//------------------------------------------------------------------------------

void fxGuiSnooper::_renderScene( ObjectRenderInst*, SceneRenderState *state, BaseMatInstance* )  
{  
    GFXTransformSaver saver;  
  
    state->usePostEffects(false);  
  
    renderScene(mSaveViewport);  
  
    GFX->setClipRect(mSaveViewport);  
    GFX->setStateBlock(mDefaultGuiSB);  
}  

void fxGuiSnooper::renderWorld(const RectI & updateRect)  
{  
    GFXTransformSaver saver;  
  
    gClientSceneGraph->setDisplayTargetResolution(getExtent());  
  
    // Use a render instance to do the scene   
    // rendering after HDR is processed and while the depth   
    // buffer is still intact.  
    RenderPassManager *rpm = gClientSceneGraph->getDefaultRenderPass();  
    ObjectRenderInst *inst = rpm->allocInst<ObjectRenderInst>();  
    inst->type = RenderPassManager::RIT_Editor;  
    inst->renderDelegate.bind(this, &fxGuiSnooper::_renderScene);  
    rpm->addInst(inst);  
  
    // Render Client Scene Graph.  
    gClientSceneGraph->renderScene(SPT_Diffuse);  
}  

//------------------------------------------------------------------------------

void fxGuiSnooper::onRender(Point2I offset, const RectI &updateRect)
{	
	// Call Parent Render.
	Parent::onRender(offset, updateRect);

	// Set Clipping Rectangle to GUI Bounds.
	GFX->setClipRect(getBounds());

	// Do we have an attached Object?
	if (!mAttachedObject)
	{
		// No, so signal to user this problem ...
		ColorF ErrorColor(1,0,0);
		GFX->getDrawUtil()->drawRectFill(updateRect, ErrorColor);
		ErrorColor.set(1,1,1);
		char buf[256];
		dSprintf(buf, sizeof(buf), "*** Object not selected ***");
		GFX->getDrawUtil()->setBitmapModulation(ErrorColor);
		GFX->getDrawUtil()->drawText(mProfile->mFont, offset, buf);
		GFX->getDrawUtil()->clearBitmapModulation();

		// Return Error.
		return;
	}

	// Are we using the Overlay Bitmap?
	if (mUseOverlayBitmap)
	{
		// Yes, so do we have a texture Handle?
		if (mOverlayTextureHandle)
		{
			// Yes, so clear Bitmap Modulation.
			GFX->getDrawUtil()->clearBitmapModulation();

			// Are we tiling the Overlay Bitmap?
			if(mOverlayTile)
			{
				RectI SrcRegion;
				RectI DstRegion;

				// Yes, so fetch texture object.
				GFXTextureObject* TextureObj = mOverlayTextureHandle;

				// Calculate Tesselation Count.
				float XTess = ((float)getWidth()/(float)TextureObj->mBitmapSize.x)+1;
				float YTess = ((float)getHeight()/(float)TextureObj->mBitmapSize.y)+1;

				for(int y = 0; y < YTess; ++y)
				{
					for(int x = 0; x < XTess; ++x)
					{
						// Calculate Source Region.
						SrcRegion.set(0,0,TextureObj->mBitmapSize.x, TextureObj->mBitmapSize.y);

						// Calculate Destination Region.
						DstRegion.set(((TextureObj->mBitmapSize.x*x)+offset.x),
									  ((TextureObj->mBitmapSize.y*y)+offset.y),
									  TextureObj->mBitmapSize.x,	
									  TextureObj->mBitmapSize.y);

						// Draw Tiled Bitmap.
						GFX->getDrawUtil()->drawBitmapStretchSR(TextureObj, DstRegion, SrcRegion);
					}
				}
			}
			else
			{
				// No, so draw stretched Bitmap.
				GFX->getDrawUtil()->drawBitmapStretch(mOverlayTextureHandle, getBounds());
			}
		}
	}

	// Are we using the Overlay Colour?
	if (mUseOverlayColour)
	{
		// Set Colour Mask.
		
		// Declare StateBlocks
		GFXStateBlockDesc ecw;
		ecw.colorWriteRed   |= ( mOverlayRedMask ? GFXCOLORWRITEENABLE_RED		: 0 ); 
		ecw.colorWriteGreen |= ( mOverlayGreenMask ? GFXCOLORWRITEENABLE_GREEN	: 0 );
		ecw.colorWriteBlue  |= ( mOverlayBlueMask ? GFXCOLORWRITEENABLE_BLUE	: 0 );
		ecw.colorWriteAlpha = GFXCOLORWRITEENABLE_ALPHA;
		mEcwSB = GFX->createStateBlock(ecw);
		
		GFXStateBlockDesc ecwReset;
		ecwReset.colorWriteRed   = GFXCOLORWRITEENABLE_RED; 
		ecwReset.colorWriteGreen = GFXCOLORWRITEENABLE_GREEN;
		ecwReset.colorWriteBlue  = GFXCOLORWRITEENABLE_BLUE;
		ecwReset.colorWriteAlpha = GFXCOLORWRITEENABLE_ALPHA;
		mEcwResetSB = GFX->createStateBlock(ecwReset);

		GFX->setStateBlock(mEcwSB);

		// Draw our filled rectangle with the Filter Colour.
		GFX->getDrawUtil()->drawRectFill(updateRect, mOverlayColor);

		// Reset the Colour Mask.
		GFX->setStateBlock(mEcwResetSB);
	}
}

//------------------------------------------------------------------------------

bool fxGuiSnooper::processCameraQuery(CameraQuery * query)
{
	Point3F		CameraRotation;							// Rotated View.
	float		VisibleDistance = 1100.0f;				// Visible Distance.


	// Get Game Connection.
	GameConnection* pConnection = dynamic_cast<GameConnection *>(NetConnection::getConnectionToServer());

	// Did we get the connection?
	if (pConnection)
	{
		// Have we got an Attached Object?
		if (mAttachedObject)
		{
			// Current Sweep.
			EulerF mCurrentSweep;

			// Create Camera Matrix.
			MatrixF	Camera(true);

			// Craete Rotation Quaternion.
			QuatF	QRotation;

			// Get Time Elapsed.
			U32 CurrentTime = Platform::getRealMilliseconds();
			U32 TimeElapsed = CurrentTime - mLastTimeStamp;
			mLastTimeStamp = CurrentTime;

			// Calculate new Sweep.
			Point3F NewSweep(	(U32)(360.0f / mSweepTime[0] * TimeElapsed) % 360,
								(U32)(360.0f / mSweepTime[1] * TimeElapsed) % 360,
								(U32)(360.0f / mSweepTime[2] * TimeElapsed) % 360);

			// Add to Current Sweep.
			mCurrentSweepMagnitude += NewSweep;

			// Calculate Current Sweep Angle.
			mCurrentSweep.set(	mDegToRad((mSweepAmplitude[0] * mCos(mDegToRad(mCurrentSweepMagnitude[0])))/2 + mRotateView[0]),
								mDegToRad((mSweepAmplitude[1] * mCos(mDegToRad(mCurrentSweepMagnitude[1])))/2 + mRotateView[1]),
								mDegToRad((mSweepAmplitude[2] * mCos(mDegToRad(mCurrentSweepMagnitude[2])))/2 + mRotateView[2]));

			// Set-up Quaternion Rotation.
			QRotation.set(mCurrentSweep);

			// Set Camera Matrix to new Rotation.
			QRotation.setMatrix(&Camera);

			// Set Position @ Attached Object.
			Camera.setColumn(3, mAttachedObject->getBoxCenter() + mOffsetView);

			// Set Camera Matrix.
			query->cameraMatrix = Camera;

			// Set Near/Far Planes.
			query->nearPlane = 0.1;
			query->farPlane = getMax(VisibleDistance, 50.f);

			// Set FOV.
			query->fov = mDegToRad(mFov);

			// Return OK.
			return(true);
		}
	}

	// Return Error.
	return(false);
}

Within the game, hit F11 and go to the Object editor. Then add a new object to the scene to be used as the camera for the viewport. We used a transparent cube so it would not be visible in the scene, and so that the camera point of view would not be obscured by any textures, etc on the model. NAME THIS NEW OBJECT. SAVE THE MISSION.

Now switch to the GUI editor. Under "Library" in the "3D" group, you will see fxGuiSnooper as a new control.

Add a new fxGuiSnooper control to your playgui, position it somewhere that works for you and resize however you like. This will be the viewport where the remote camera will render to.

Now edit the "AttachedObject" parameter for the fxGuiSnooper GUI control you just created... ENTER THE SAME NAME YOU USED FOR THE CAMERA OBJECT ABOVE. SAVE THE GUI.

Now exit the editor.

Presto! You should be seeing the remote camera's point of view in the viewport.

NOTES ON fxGuiSnooper PARAMETERS:
- To make camera point straight down, change ViewRotation to "-90 0 0"
- To see more in the viewport, change FOV to "100"
- To stop camera sweeping, change SweepAmplitude to "0 0 0"

That's about it... Hope it works for everyone. Let me know if there are any issues.

Cheers
P

#1
02/13/2014 (6:58 am)
This is fantastic!

Recompiled with this version and it's working fine now. I've been trying to track this bug down for a few days.

I had to set my view rotation to this as mine was drawing upside down. =)

ViewRotation = "-90 0 180";


Thanks for sharing, Paul

#2
02/13/2014 (10:50 am)
Excellent!

So glad it it worked for you... Because yes it was a drag how the entire GUI was redrawing when mousing over things.

Here is a little teaser, showing what is possible when you combine this resource with the "GUI on Objects" code... That's another old resource that we were able to get going by putting together all the various comments from the original resource thread.

Note the security feed on the left block coming from a camera that was placed close by... And yes the animated camera panning displays on the block. The center block shows a 3D in-game version of our 2D compass GUI which can be seen in the top left corner of the HUD (ignore the incorrect compass headings, we have since removed them since they were inaccurate anyways). And the block on the right is showing the standard Options Gui. None of these are clickable mind you, but they work great for display purposes... Think ammo counters shown right on weapons, etc.

And... Wait for it... All of this stuff works perfectly when playing with the Oculus Rift, whereas 2D GUIs do not :)

We're thinking of putting this "security monitors on objects" concept out as a pack in the near future - with some custom-built security monitor objects and cameras, and an extension to the World Editor to make placing them easy. Stay tuned!

Cheers

riversidefantasy.ca/NCC1701/special-objects.jpg
#3
03/13/2014 (1:46 pm)
very sweet, thank you....