Game Development Community

dev|Pro Game Development Curriculum

ShapeDamageHud TGEA Ver Port to T3D

by Enel · 04/23/2010 (8:32 am) · 4 comments

it is base Resource http://www.torquepowered.com/community/resources/view/13246

//-----------------------------------------------------------------------------
// gui/game/guiShapeDamageHud.cc
// by Stephen Lujan
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#include "console/console.h"
#include "gui/core/guiControl.h"
#include "gui/3D/guiTSControl.h"
#include "console/consoleTypes.h"
#include "sceneGraph/sceneGraph.h"
#include "T3D/shapeBase.h"
#include "T3D/gameConnection.h"
#include "gfx/gfxDrawUtil.h"

//----------------------------------------------------------------------------
/// Temporarily displays damage or other text above shape objects.
///
/// This GUI control must be a child of a TSControl, and a server connection
/// and control object must be present.
///
/// This is a stand-alone control and relies only on the standard base GuiControl.
class GuiShapeDamageHud : public GuiControl 
{
   typedef GuiControl Parent;
public:
	struct damagePopup
	{
		SimObjectPtr<ShapeBase>   mShape;
		S32					mTime;
		S32					mStartTime;
		ColorF				mTextColor;
		StringTableEntry	mText;
	};

	Vector<damagePopup> mPopups;

protected:
	// field data
   ColorF   mFillColor;
   ColorF   mFrameColor;
   F32      mStartVerticalOffset;
	F32      mEndVerticalOffset;
   F32      mDistanceFade;
   bool     mShowFrame;
   bool     mShowFill;
	U32		mLastTime;

   void drawDamage( Point2I offset, damagePopup *popup, F32 opacity);

public:
   GuiShapeDamageHud();

   // GuiControl
   virtual void onRender(Point2I offset, const RectI &updateRect);

   static void initPersistFields();
   DECLARE_CONOBJECT( GuiShapeDamageHud );
   DECLARE_CATEGORY( "Gui game" );
   DECLARE_DESCRIPTION( "Display DamageHud" );
};


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

IMPLEMENT_CONOBJECT(GuiShapeDamageHud);

/// Default distance for object's information to be displayed.
static const F32 cDefaultVisibleDistance = 500.0f;

GuiShapeDamageHud::GuiShapeDamageHud()
{
   mFillColor.set( 0.25, 0.25, 0.25, 0.25 );
   mFrameColor.set( 0, 1, 0, 1 );
   mShowFrame = mShowFill = true;
   mStartVerticalOffset = 1;
	mEndVerticalOffset = 40;
   mDistanceFade = 0.1;
	mLastTime = Platform::getVirtualMilliseconds();
}

void GuiShapeDamageHud::initPersistFields()
{
   Parent::initPersistFields();
   addGroup("Colors");
   addField( "fillColor",  TypeColorF, Offset( mFillColor, GuiShapeDamageHud ) );
   addField( "frameColor", TypeColorF, Offset( mFrameColor, GuiShapeDamageHud ) );
   endGroup("Colors");

   addGroup("Misc");
   addField( "showFill",   TypeBool, Offset( mShowFill, GuiShapeDamageHud ) );
   addField( "showFrame",  TypeBool, Offset( mShowFrame, GuiShapeDamageHud ) );
   addField( "startingVerticalOffset", TypeF32, Offset( mStartVerticalOffset, GuiShapeDamageHud ) );
   addField( "distanceFade", TypeF32, Offset( mDistanceFade, GuiShapeDamageHud ) );
   endGroup("Misc");
}

//----------------------------------------------------------------------------
/// Core rendering method for this control.
///
///
/// Information is offset from the center of the object's bounding box,
/// unless the object is a PlayerObjectType, in which case the eye point
/// is used.
///
/// @param   updateRect   Extents of control.
void GuiShapeDamageHud::onRender( Point2I, const RectI &updateRect)
{
	U32 time = Platform::getVirtualMilliseconds();
	S32 deltaTime= time - mLastTime;
	//Con::errorf("%d = %d - %d", deltaTime, time, mLastTime);
	mLastTime = time;

	//no impossibilities extending life of popups
	if (deltaTime < 0)
		deltaTime = 0;
	//if something is stuck lets not delete popups all at once
	if (deltaTime > 500)
		deltaTime = 500;

   // Background fill first
   if (mShowFill)
      GFX->getDrawUtil()->drawRectFill(updateRect, mFillColor);

   // Must be in a TS Control
   GuiTSCtrl *parent = dynamic_cast<GuiTSCtrl*>(getParent());
   if (!parent) return;

   // Must have a connection and control object
   GameConnection* conn = GameConnection::getConnectionToServer();
   if (!conn)
      return;

   GameBase* control = conn->getControlObject();
   if (!control)
      return;

   // Get control camera info
   MatrixF cam;
   Point3F camPos;
   VectorF camDir;
   conn->getControlCameraTransform(0,&cam);
   cam.getColumn(3, &camPos);
   cam.getColumn(1, &camDir);

   F32 camFov;
   conn->getControlCameraFov(&camFov);
   camFov = mDegToRad(camFov) / 2;
	// the next line is optional just to widen the viewcone a little bit for when 40% of a player is visible
   // but their center isn't. Increase the viewcone by 5 degrees:
   camFov += 0.08; //mDegToRad(5) / 2;
   F32 cosCamFov = mCos(camFov);

   // Visible distance info & name fading
   F32 visDistance = gClientSceneGraph->getVisibleDistance();
   F32 visDistanceSqr = visDistance * visDistance;
   F32 fadeDistance = visDistance * mDistanceFade;

   // Collision info. We're going to be running LOS tests and we
   // don't want to collide with the control object.
   static U32 losMask = TerrainObjectType | InteriorObjectType; //| ShapeBaseObjectType;
   control->disableCollision();

	Vector<damagePopup>::iterator i;
   for(i = mPopups.begin(); i != mPopups.end(); i++)
   {
      damagePopup *current = &(*i);
      if( !current )
		{
			//Con::errorf("Damage popup doesn't exist!");
			continue;
		}

		if (current->mTime < 0)
		{
			//Con::errorf("deleting expired popup at time %d", time);
			mPopups.erase(i);
			i--;
			continue;
		}

		ShapeBase* shape;   
		shape = static_cast<ShapeBase*> (Sim::findObject(current->mShape->getId()));   
		if (!shape)   
        {      
            //Con::errorf("Damage popup has a shape that doesn't exist.");   
            //AssertFatal(current->mShape, "this shape doesn't exist");   
            mPopups.erase(i);   
            i--;   
            continue;   
        }  


		if( !(shape->getType() & ShapeBaseObjectType))
		{	
			Con::errorf("Damage popup has a shape that isn't derived from shapeBase.");
			continue;
		}
		
		//Con::errorf("decreasing popup life %d - %d", current->mTime, deltaTime);
		current->mTime -= deltaTime;
		// Target pos to test, if it's a player run the LOS to his eye
		// point, otherwise we'll grab the generic box center.
		Point3F shapePos;
		if (shape->getType() & PlayerObjectType) 
		{
			MatrixF eye;

			// Use the render eye transform, otherwise we'll see jittering
			// Stephen: why always access violations here? grrrrrr
			shape->getRenderEyeTransform(&eye);
			eye.getColumn(3, &shapePos);
		}
		else 
		{
			 // Use the render transform instead of the box center
			 // otherwise it'll jitter.
			MatrixF srtMat = shape->getRenderTransform();
			srtMat.getColumn(3, &shapePos);
		}
		VectorF shapeDir = shapePos - camPos;

		// test early to see if it's behind us.
      // no need to normalize shapeDir for this,
      // we'll normalize it later if needed.
      F32 dot = mDot(shapeDir, camDir);
      if (dot < 0)
		{
			//Con::errorf("Damage popup was behind camera.");
         continue;
		}

      // Test to see if it's in range
      F32 shapeDist = shapeDir.lenSquared();
      if (shapeDist == 0 || shapeDist > visDistanceSqr)
		{
			//Con::errorf("Damage popup was not in range.");
         continue;
		}
      shapeDist = mSqrt(shapeDist);

      // Test to see if it's within our viewcone, this test doesn't
      // actually match the viewport very well, should consider
      // projection and box test.
      dot /= shapeDist;
      if (dot < cosCamFov)
		{
			//Con::errorf("Damage popup was not in viewcone.");
         continue;
		}

      // Test to see if it's behind something, and we want to
      // ignore anything it's mounted on when we run the LOS.
      RayInfo info;
      shape->disableCollision();
      SceneObject *mount = shape->getObjectMount();

      if (mount)
         mount->disableCollision();

      bool los = !gClientContainer.castRay(camPos, shapePos,losMask, &info);
      shape->enableCollision();

      if (mount)
         mount->enableCollision();

      if (!los)
		{
			//Con::errorf("Damage popup did not have line of sight.");
         continue;
		}


      // Project the shape pos into screen space and calculate
      // the distance opacity used to fade the labels into the
      // distance.
      Point3F projPnt;
      //shapePos.z += mVerticalOffset;

		//perhaps instantiating these as guimembers instead of every message every frame would be better
		F32 heightMultiplier= ((F32)current->mTime / (F32)current->mStartTime);
		F32 pixelHeight= ((1.0-heightMultiplier)*mEndVerticalOffset) + ((heightMultiplier)*mStartVerticalOffset);
		//Con::errorf("before projPnt.y = %.2f", projPnt.y);
		
      if (!parent->project(shapePos, &projPnt))
		{
			//Con::errorf("Damage popup could not project to screen coordinates.");
         continue;
		}
		projPnt.y -= pixelHeight;
		if (projPnt.y < 1)
			projPnt.y = 1;
		/*
		projPnt.x -= 0.6*pixelHeight;
		if (projPnt.x < 1)
			projPnt.x = 1;
		*/
		//Con::errorf("heightMultiplier = %.2f  pixelHeight = %.2f after projPnt.y = %.2f", heightMultiplier, pixelHeight, projPnt.y);
      F32 opacity = (shapeDist < fadeDistance)? 1.0:
         1.0 - (shapeDist - fadeDistance) / (visDistance - fadeDistance);
		//spend last half of life fading out
		F32 ageOpacity = (2.0*(F32)current->mTime / (F32)current->mStartTime);
		if (ageOpacity < opacity)
			opacity= ageOpacity;
      // Render the text
      drawDamage(Point2I((S32)projPnt.x, (S32)projPnt.y),current,opacity);
   }

   // Restore control object collision
   control->enableCollision();

}

//----------------------------------------------------------------------------
/// Render popup text.
///
/// Helper function for GuiShapeDamageHud::onRender
///
/// @param   offset  Screen coordinates to render name label. (Text is centered
///                  horizontally about this location, with bottom of text at
///                  specified y position.)
/// @param   popup	the damage popup struct
/// @param   opacity Opacity of name (a fraction).
void GuiShapeDamageHud::drawDamage(Point2I offset, damagePopup *popup, F32 opacity)
{
	//Con::errorf("executing GuiShapeDamageHud::drawDamage() at shape %d at time %d",popup->mShape->getId(), Platform::getVirtualMilliseconds());
   // Center the name
   offset.x -= mProfile->mFont->getStrWidth((const UTF8 *)popup->mText) / 2;
   offset.y -= mProfile->mFont->getHeight();

   // Deal with opacity and draw.
   popup->mTextColor.alpha = opacity;
   GFX->getDrawUtil()->setBitmapModulation(popup->mTextColor);
   GFX->getDrawUtil()->drawText(mProfile->mFont, offset, popup->mText);
   GFX->getDrawUtil()->clearBitmapModulation();
}

//New popups are trigger by the scripts.
ConsoleMethod( GuiShapeDamageHud, addPopup, bool, 6, 6, "int time, ColorF color, int object, string text")
{
	GameConnection* conn = GameConnection::getConnectionToServer();
   if (!conn)
      return false;

	// Allocate a new popup.
	GuiShapeDamageHud::damagePopup newPopup;
	// PARSE SHIZZLE
	newPopup.mTime = dAtoi(argv[2]);
	newPopup.mStartTime = newPopup.mTime;
	dSscanf(argv[3], "%f %f %f", &newPopup.mTextColor.red, &newPopup.mTextColor.green, &newPopup.mTextColor.blue);

	ShapeBase* shape;   
    shape = static_cast<ShapeBase*> (Sim::findObject(argv[4])); //(conn->findObject(argv[4]));   
    if (!shape)   
    {   
        Con::errorf("New damage popup couldn't find the shape below! Check guiShapeDamageHud.cc");   
        Con::errorf(argv[4]);   
        return false;   
    }   
    newPopup.mShape = shape;

	newPopup.mText = StringTable->insert(argv[5]);

	// Store it in the list at the back to get rendered last
	object->mPopups.push_back(newPopup);
	//for now i'll assume it worked
	return true;
}


#1
04/23/2010 (12:26 pm)
We added a bit of extra code that also checks a timer and pushes them up if them are too close (so they don't stack on top of each other). Example here pushes up if there is 300ms between the last popup and the current one. There is some code that would need to be added to ShapeBase as well.

void GuiShapeDamageHud::pushPopups(void)
{
	Vector<damagePopup>::iterator i;
    for(i = mPopups.begin(); i != mPopups.end(); i++)
    {
       damagePopup *current = &(*i);
	   current->mTime -= 300;
	   current->mStartTime += 300;
	}
	return;
}

//Modified to work with the kit.  This function should work in both TGE and TGEA
ConsoleMethod( GuiShapeDamageHud, addPopup, bool, 6, 6, "int time, ColorF color, int object, string text")
{
	U32 time = Platform::getVirtualMilliseconds();
	U32 diff;
	S32 ghostID = dAtoi(argv[4]);
	GameConnection* conn = GameConnection::getConnectionToServer();
    if (!conn)
      return false;
	if (ghostID == -1)
		return false;
	NetObject* shape = conn->resolveGhost(ghostID);


	// Allocate a new popup.s
	GuiShapeDamageHud::damagePopup newPopup;
	// PARSE SHIZZLE
	newPopup.mTime = dAtoi(argv[2]);
	newPopup.mStartTime = newPopup.mTime;
	dSscanf(argv[3], "%f %f %f", &newPopup.mTextColor.red, &newPopup.mTextColor.green, &newPopup.mTextColor.blue);
	newPopup.mShape = static_cast<ShapeBase*> (shape); //(conn->findObject(argv[4]));
	if (!newPopup.mShape)
	{
		Con::errorf("New damage popup couldn't find the shape below! Check guiShapeDamageHud.cc");
		Con::errorf(argv[4]);
		return false;
	}
	newPopup.mText= StringTable->insert(argv[5]);
    
	//Checking for overlap here...if they are push all the other popups ahead
	if (newPopup.mShape->getPopupTime() && time - newPopup.mShape->getPopupTime() < 300)
	   object->pushPopups();

    newPopup.mShape->setPopupTime(time);

	// Store it in the list at the back to get rendered last
	object->mPopups.push_back(newPopup);
	//for now i'll assume it worked
	return true;
}

Shapebase

void ShapeBase::setPopupTime(U32 time)
{
    mLastPopUPTime = time;
}

U32 ShapeBase::getPopupTime()
{
	return mLastPopUPTime;
}
#2
04/23/2010 (9:46 pm)
Thx! Joshua~
#3
04/26/2010 (2:07 pm)
cool stuff here ;D....
#4
10/29/2010 (12:54 am)
I'm having a problem where on the client's screen they don't see the damage showing up. Is there a fix for that? Using T3D beta 3