Game Development Community

EnergyProjectile for TGEA 1.8.1+

by CdnGater · 05/03/2009 (6:50 am) · 38 comments

This is a first cut at a generic EnergyProjectile that is flexible enough to provide a number of different effects. Just drop the code into the T3D directory in the source, compile and then play with it. Configuring this to be used is like configuring any other weapon, except use EnergyProjectile instead of just the standard Porjectile.


#include "console/consoleTypes.h"
#include "core/stream/bitStream.h"
#include "sceneGraph/sceneState.h"
#include "sceneGraph/sceneGraph.h"
#include "T3D/shapeBase.h"
#include "ts/tsShapeInstance.h"
#include "gfx/primBuilder.h"
#include "gfx/gfxDevice.h"
#include "gfx/gfxTransformSaver.h"
#include "math/mRandom.h"
#include "math/mathIO.h"
#include "math/mathUtils.h"
#include "renderInstance/renderPassManager.h"
#include "T3D/projectile.h"

class ExplosionData;
class ShapeBase;
class TSShapeInstance;
class TSThread;


//--------------------------------------------------------------------------
class EnergyProjectileData : public ProjectileData
{
	typedef ProjectileData Parent;
      
public:

	bool beamEnabled;
	bool beamPulse;
	F32 beamStartRadius;
	F32 beamMidRadius;
	F32 beamEndRadius;
	F32 beamFrontSegmentLength;
	F32 beamBackSegmentLength;
	StringTableEntry beamMaterialList;

	bool sparkEnabled;
	StringTableEntry sparkMaterialList;
	F32 sparkRadius;
	F32 sparkStep;
	F32 sparkRotationStep;

	S32 interval;

	EnergyProjectileData();

	void packData(BitStream*);
	void unpackData(BitStream*);

	static void initPersistFields();
	DECLARE_CONOBJECT(EnergyProjectileData);
};
DECLARE_CONSOLETYPE(EnergyProjectileData)

//--------------------------------------------------------------------------
class EnergyProjectile : public Projectile
{
	typedef Projectile Parent;
	EnergyProjectileData* mDataBlock;

protected:
	GFXTexHandle  mBeamTextureHandles[20];
	MaterialList  mBeamMaterialList;
	GFXTexHandle  mSparkTextureHandles[20];
	MaterialList  mSparkMaterialList;
	S32           mBeamNumTextures;
	S32           mBeamIndex;
	S32           mSparkNumTextures;
	S32           mSparkIndex;
	F32			  mSparkRadius;

	S32           mLastTime;
	F32			  mRange;

	F32			  mSparkRotation;

	Point3F		  mEndPosition;
	Point3F		  mMuzzlePosition;
	Point3F		  mMuzzleVector;
	Point3F		  mMuzzleVelocity;

	void loadDml();

	bool onAdd();
	bool onNewDataBlock(GameBaseData*);
	U32  packUpdate  (NetConnection *conn, U32 mask, BitStream *stream);
	void unpackUpdate(NetConnection *conn,           BitStream *stream);

	void processTick(const Move* move);
	bool prepRenderImage(SceneState*, const U32, const U32, const bool);
	void renderObject(ObjectRenderInst *ri, BaseMatInstance*);
	void RenderBeamSegment( Point3F pStart, Point3F pEnd, F32 startRadius, F32 endRadius, F32 vStart, F32 vEnd );

	GFXStateBlockRef  mBlendInvSrcAlphaSB;
	GFXStateBlockRef  mBlendSB;

	ObjectRenderInst::RenderDelegate mRenderDelegate;
public:

	static void initPersistFields();

	EnergyProjectile();
	~EnergyProjectile();

	DECLARE_CONOBJECT(EnergyProjectile);
};

IMPLEMENT_CO_DATABLOCK_V1(EnergyProjectileData);
IMPLEMENT_CO_NETOBJECT_V1(EnergyProjectile);


//--------------------------------------------------------------------------
//
EnergyProjectileData::EnergyProjectileData()
{
   ProjectileData::ProjectileData();

   beamEnabled = false;
   beamPulse = true;
   beamStartRadius = 0.0;
   beamMidRadius = 0.0;
   beamEndRadius = 0.0;
   beamFrontSegmentLength = 0.0;
   beamBackSegmentLength = 0.0;
   beamMaterialList = NULL;

   sparkEnabled = false;
   sparkMaterialList = NULL;
   sparkRadius = 0.0;
   sparkStep = 0.0;
   sparkRotationStep = 0.0f;

   interval = 0;
}

//--------------------------------------------------------------------------
IMPLEMENT_CONSOLETYPE(EnergyProjectileData)
IMPLEMENT_GETDATATYPE(EnergyProjectileData)
IMPLEMENT_SETDATATYPE(EnergyProjectileData)

void EnergyProjectileData::initPersistFields()
{
   Parent::initPersistFields();

   addNamedField(beamEnabled, TypeBool, EnergyProjectileData);
   addNamedField(beamPulse, TypeBool, EnergyProjectileData);
   addNamedField(beamStartRadius, TypeF32, EnergyProjectileData);
   addNamedField(beamMidRadius, TypeF32, EnergyProjectileData);
   addNamedField(beamEndRadius, TypeF32, EnergyProjectileData);
   addNamedField(beamFrontSegmentLength, TypeF32, EnergyProjectileData);
   addNamedField(beamBackSegmentLength, TypeF32, EnergyProjectileData);
   addNamedField(beamMaterialList, TypeFilename, EnergyProjectileData);

   addNamedField(sparkEnabled, TypeBool, EnergyProjectileData);
   addNamedField(sparkMaterialList, TypeFilename, EnergyProjectileData);
   addNamedField(sparkRadius, TypeF32, EnergyProjectileData);
   addNamedField(sparkStep, TypeF32, EnergyProjectileData);
   addNamedField(sparkRotationStep, TypeF32, EnergyProjectileData);
   
   addNamedField(interval, TypeS32, EnergyProjectileData);
}

//--------------------------------------------------------------------------
void EnergyProjectileData::packData(BitStream* stream)
{
   Parent::packData(stream);

   stream->writeFlag(beamEnabled);
   stream->writeFlag(beamPulse);
   stream->writeFloat(beamStartRadius/20.0, 8);
   stream->writeFloat(beamMidRadius/20.0, 8);
   stream->writeFloat(beamEndRadius/20.0, 8);
   stream->writeFloat(beamFrontSegmentLength/20.0, 8);
   stream->writeFloat(beamBackSegmentLength/20.0, 8);
   stream->writeString(beamMaterialList);

   stream->writeFlag(sparkEnabled);
   stream->writeString(sparkMaterialList);
   stream->writeFloat(sparkRadius/20.0, 8);
   stream->writeFloat(sparkStep, 8);
   stream->writeFloat(sparkRotationStep, 8);

   stream->writeInt(interval, 32);
}

void EnergyProjectileData::unpackData(BitStream* stream)
{
   Parent::unpackData(stream);

   beamEnabled = stream->readFlag();
   beamPulse = stream->readFlag();
   beamStartRadius = stream->readFloat(8) * 20;
   beamMidRadius = stream->readFloat(8) * 20;
   beamEndRadius = stream->readFloat(8) * 20;
   beamFrontSegmentLength = stream->readFloat(8) * 20;
   beamBackSegmentLength = stream->readFloat(8) * 20;
   beamMaterialList = StringTable->insert(stream->readSTString());

   sparkEnabled = stream->readFlag();
   sparkMaterialList = StringTable->insert(stream->readSTString());
   sparkRadius = stream->readFloat(8) * 20;
   sparkStep = stream->readFloat(8);
   sparkRotationStep = stream->readFloat(8);

   interval = stream->readInt(32);
}


//--------------------------------------------------------------------------
EnergyProjectile::EnergyProjectile()
{
   mLastTime = 0;
   mBeamIndex = 0;
   mSparkIndex = 0;
   mSparkRadius = 0.0;
   mSparkRotation = 0.0;
   mRange = 0;

   mRenderDelegate.bind(this, &EnergyProjectile::renderObject);
}

EnergyProjectile::~EnergyProjectile()
{
}

//--------------------------------------------------------------------------
void EnergyProjectile::initPersistFields()
{
   Parent::initPersistFields();
   addField("range", TypeF32, Offset(mRange, EnergyProjectile));
}

//--------------------------------------------------------------------------
U32 EnergyProjectile::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
{
   U32 retMask = Parent::packUpdate(con, mask, stream);

   if (stream->writeFlag(mask & GameBase::InitialUpdateMask))
   {
      // Initial update
      stream->write(mRange);
   }
   return retMask;
}

void EnergyProjectile::unpackUpdate(NetConnection* con, BitStream* stream)
{
   Parent::unpackUpdate(con, stream);

   if (stream->readFlag())
   {
      // initial update
      stream->read(&mRange);
   }
}

//--------------------------------------------------------------------------
void EnergyProjectile::loadDml()
{
	S32 x;
	mBeamNumTextures = 0;      
	mSparkNumTextures = 0; 

	FileStream  *stream = NULL;

	if (mDataBlock->beamEnabled && mDataBlock->beamMaterialList[0])
	{
		stream = FileStream::createAndOpen( mDataBlock->beamMaterialList, Torque::FS::File::Read );
		if(stream)
		{
			char path[1024], *p;
			dStrcpy(path, mDataBlock->beamMaterialList);
			if ((p = dStrrchr(path, '/')) != NULL)
				*p = 0;

			mBeamMaterialList.read(*stream);
			stream->close();
			delete stream;

			mBeamMaterialList.load(path);
			for(x = 0; x < mBeamMaterialList.size(); ++x, ++mBeamNumTextures)
				mBeamTextureHandles[x] = mBeamMaterialList.getMaterial(x); 
		}
	}

	if (mDataBlock->sparkEnabled && mDataBlock->sparkMaterialList[0])
	{
		stream = FileStream::createAndOpen( mDataBlock->sparkMaterialList, Torque::FS::File::Read );
		if(stream)
		{
			char path[1024], *p;
			dStrcpy(path, mDataBlock->sparkMaterialList);
			if ((p = dStrrchr(path, '/')) != NULL)
				*p = 0;

			mSparkMaterialList.read(*stream);
			stream->close();
			delete stream;

			mSparkMaterialList.load(path);
			for(x = 0; x < mSparkMaterialList.size(); ++x, ++mSparkNumTextures)
				mSparkTextureHandles[x] = mSparkMaterialList.getMaterial(x); 
		}
	}
}

//--------------------------------------------------------------------------
bool EnergyProjectile::onAdd()
{
   if(!Parent::onAdd())
      return false;

   if (!isServerObject())
   {
      loadDml();

	  mSparkRadius = mDataBlock->sparkRadius;
	  mMuzzlePosition = mInitialPosition;
	  mMuzzleVelocity = mCurrVelocity;
   }
      
   return true;
}

bool EnergyProjectile::onNewDataBlock(GameBaseData* dptr)
{
   mDataBlock = dynamic_cast<EnergyProjectileData*>(dptr);
   if (!mDataBlock || !Parent::onNewDataBlock(dptr))
      return false;

   return true;
}

void EnergyProjectile::processTick(const Move* move)
{
   GameBase::processTick(move);

   mCurrTick++;
   // See if we can get out of here the easy way ...
   if (isServerObject() && mCurrTick >= mDataBlock->lifetime)
   {
      deleteObject();
      return;
   }
   else if (mHidden == true)
      return;

	if (mDataBlock->beamEnabled && mDataBlock->beamPulse)
	{
		if (mSourceObject)
		{
			MatrixF muzzleTrans;
			mSourceObject->getMuzzleTransform(mSourceObjectSlot, &muzzleTrans);
			muzzleTrans.getColumn(3, &mMuzzlePosition);

			muzzleTrans.getColumn(1, &mMuzzleVector);
			mEndPosition = mMuzzleVector * mRange;
			mEndPosition += mMuzzlePosition;

			mSourceObject->disableCollision();

			RayInfo rInfo;
			if (getContainer()->castRay(mMuzzlePosition, mEndPosition, csmDynamicCollisionMask | csmStaticCollisionMask, &rInfo) == true) 
			{
				mEndPosition = rInfo.point;
			}
			mSourceObject->enableCollision();

			mMuzzleVelocity = mMuzzleVector * mDataBlock->muzzleVelocity;;
		}
		mCurrPosition = mMuzzlePosition + mMuzzleVelocity * (F32(mCurrTick) * (F32(TickMs) / 1000.0f));
		mCurrVelocity = mMuzzleVelocity;
	}

   // ... otherwise, we have to do some simulation work.
   RayInfo rInfo;
   Point3F oldPosition;
   Point3F newPosition;

   oldPosition = mCurrPosition;
   newPosition = oldPosition + mCurrVelocity * (F32(TickMs) / 1000.0f);

   if (bool(mSourceObject))
      mSourceObject->disableCollision();

   if (getContainer()->castRay(oldPosition, newPosition, csmDynamicCollisionMask | csmStaticCollisionMask, &rInfo) == true)
   {
      if(isServerObject() && (rInfo.object->getType() & csmStaticCollisionMask) == 0)
         setMaskBits(BounceMask);

      if(mCurrTick > mDataBlock->armingDelay)
      {
         MatrixF xform(true);
         xform.setColumn(3, rInfo.point);
         setTransform(xform);
         mCurrPosition    = rInfo.point;
         mCurrVelocity    = Point3F(0, 0, 0);

         U32 objectType = rInfo.object->getType();

         if(mSourceObject)
            mSourceObject->enableCollision();

         onCollision(rInfo.point, rInfo.normal, rInfo.object);
         explode(rInfo.point, rInfo.normal, objectType );
      }
      else
      {
         if(mDataBlock->isBallistic)
         {
            Point3F bounceVel = mCurrVelocity - rInfo.normal * (mDot( mCurrVelocity, rInfo.normal ) * 2.0);;
            mCurrVelocity = bounceVel;

            Point3F tangent = bounceVel - rInfo.normal * mDot(bounceVel, rInfo.normal);
            mCurrVelocity  -= tangent * mDataBlock->bounceFriction;

            mCurrVelocity *= mDataBlock->bounceElasticity;

            F32 timeLeft = 1.0f - rInfo.t;
            oldPosition = rInfo.point + rInfo.normal * 0.05f;
            newPosition = oldPosition + (mCurrVelocity * ((timeLeft/1000.0f) * TickMs));
         }
      }
   }

  if (bool(mSourceObject))
     mSourceObject->enableCollision();

   if(isClientObject())
   {
      emitParticles(mCurrPosition, newPosition, mCurrVelocity, TickMs);
      updateSound();
   }

   mCurrDeltaBase = newPosition;
   mCurrBackDelta = mCurrPosition - newPosition;
   mCurrPosition = newPosition;

   MatrixF xform(true);
   xform.setColumn(3, mCurrPosition);
   setTransform(xform);
}

//--------------------------------------------------------------------------
bool EnergyProjectile::prepRenderImage(SceneState* state, const U32 stateKey,
                                       const U32 /*startZone*/, const bool /*modifyBaseState*/)
{
   // Return if last state.
   if (isLastState(state, stateKey)) return false;
   // Set Last State.
   setLastState(state, stateKey);

   if (mHidden == true || mFadeValue <= (1.0/255.0))
      return false;

   // Is Object Rendered?
   if (state->isObjectRendered(this))
   {
		ObjectRenderInst *ri = gRenderInstManager->allocInst<ObjectRenderInst>();
		ri->mRenderDelegate = mRenderDelegate;
		ri->state = state;
		ri->type = RenderPassManager::RIT_ObjectTranslucent;
		ri->translucent = true;
		ri->calcSortPoint(this, state->getCameraPosition());
		gRenderInstManager->addInst( ri );
   }

   return false;

}

void EnergyProjectile::renderObject(ObjectRenderInst *ri, BaseMatInstance* overrideMat)
{
	SceneState* state = ri->state;
	MatrixF projection = GFX->getProjectionMatrix();
	RectI viewport = GFX->getViewport();

	GFX->setViewport(viewport);
	GFX->setProjectionMatrix(projection);

	// set up the graphics
	if (mBlendInvSrcAlphaSB.isNull())
	{
		GFXStateBlockDesc desc;
		desc.setCullMode( GFXCullNone );
		desc.setZEnable(false);
		desc.zWriteEnable = false;
		desc.samplersDefined = true;
		desc.samplers[0].textureColorOp = GFXTOPModulate;
		desc.samplers[1].textureColorOp = GFXTOPDisable;
		desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
		mBlendInvSrcAlphaSB = GFX->createStateBlock(desc);

		desc.samplers[0].textureColorOp = GFXTOPDisable;
		mBlendSB = GFX->createStateBlock(desc);
	}
	GFX->setStateBlock(mBlendInvSrcAlphaSB);

	S32 thisTime = Sim::getCurrentTime();
	S32 timeDelta = thisTime - mLastTime;
	if (timeDelta > mDataBlock->interval)
	{
		if (mBeamNumTextures > 0)
		{
			mBeamIndex ++;
			if (mBeamIndex >= mBeamNumTextures)
				mBeamIndex = 0;
		}

		if (mSparkNumTextures > 0)
		{
			mSparkIndex ++;
			if (mSparkIndex >= mSparkNumTextures)
				mSparkIndex = 0;
		}
		mSparkRadius += mDataBlock->sparkStep;
		mSparkRotation += mDataBlock->sparkRotationStep;

		mLastTime = thisTime;
	}

	// Select the objects' texture.
	GFX->disableShaders();
	GFX->setupGenericShaders( GFXDevice::GSModColorTexture );

	if (mDataBlock->beamEnabled && mBeamNumTextures > 0)
	{
		// Set Colour/Alpha.
		GFX->setTexture(0, mBeamTextureHandles[mBeamIndex]);

		if (mDataBlock->beamPulse)
		{
			RenderBeamSegment(mMuzzlePosition, mCurrPosition, mDataBlock->beamStartRadius, mDataBlock->beamMidRadius, 0.0, 0.5 );
			RenderBeamSegment(mCurrPosition, mEndPosition, mDataBlock->beamMidRadius, mDataBlock->beamEndRadius, 0.5, 1.0 );
		}
		else
		{
			Point3F dirV = mCurrVelocity;
			dirV.normalize();

			F32 clientDist = (mInitialPosition - mCurrPosition).len();
			F32 beamFrontLength = mDataBlock->beamFrontSegmentLength;
			if (beamFrontLength > clientDist)
				beamFrontLength = clientDist;
			F32 beamBackLength = mDataBlock->beamBackSegmentLength;
			if (beamBackLength > clientDist)
				beamBackLength = clientDist;

			Point3F posFrontMod = dirV * beamFrontLength;
			Point3F posBackMod = dirV * beamBackLength;

			RenderBeamSegment(mCurrPosition - posBackMod, mCurrPosition, mDataBlock->beamStartRadius, mDataBlock->beamMidRadius, 0.0, 0.5 );
			RenderBeamSegment(mCurrPosition, mCurrPosition + posFrontMod, mDataBlock->beamMidRadius, mDataBlock->beamEndRadius, 0.5, 1.0 );
		}

	}

	if (mDataBlock->sparkEnabled && mSparkNumTextures > 0 && mSparkRadius > 0.0f)
	{
		// Initialize points with basic info
		Point3F points[4];
		points[0] = Point3F(-mSparkRadius, 0.0, -mSparkRadius);
		points[1] = Point3F( mSparkRadius, 0.0, -mSparkRadius);
		points[2] = Point3F( mSparkRadius, 0.0,  mSparkRadius);
		points[3] = Point3F(-mSparkRadius, 0.0,  mSparkRadius);

		GFX->setTexture(0, mSparkTextureHandles[mSparkIndex]);
		// Get info we need to adjust points
		MatrixF camView = GFX->getWorldMatrix();
		camView.transpose();

		F32 sy, cy;
		mSinCos(mDegToRad(mSparkRotation), sy, cy);

		// Finalize points
		for(int i = 0; i < 4; i++)
		{
			// rotate
			points[i].set(cy * points[i].x - sy * points[i].z, 0.0, sy * points[i].x + cy * points[i].z);
			// align with camera
			camView.mulP(points[i]);
			// offset
			points[i] += mCurrPosition;
		}

		PrimBuild::color4f(1,1,1,1);
		PrimBuild::begin( GFXTriangleFan, 4 );
		PrimBuild::texCoord2f(0, 0);
		PrimBuild::vertex3fv(points[0]);
		PrimBuild::texCoord2f(1, 0);
		PrimBuild::vertex3fv(points[1]);
		PrimBuild::texCoord2f(1, 1);
		PrimBuild::vertex3fv(points[2]);
		PrimBuild::texCoord2f(0, 1);
		PrimBuild::vertex3fv(points[3]);
		PrimBuild::end();
	}

	GFX->disableShaders();
}


void EnergyProjectile::RenderBeamSegment( Point3F pStart, Point3F pEnd, F32 startRadius, F32 endRadius, F32 vStart, F32 vEnd )
{
	Point3F dirV = mCurrVelocity;
	dirV.normalize();

	MatrixF orient = MathUtils::createOrientFromDir( dirV );
	
	Point3F sPt1, sPt2, ePt1, ePt2;

	PrimBuild::color4f(1,1,1,1);

	{
		sPt1 = Point3F(startRadius,0,0);
		sPt2 = Point3F(-startRadius,0,0);

		orient.mulV( sPt1 );
		sPt1 += pStart;
		orient.mulV( sPt2 );
		sPt2 += pStart;

		ePt1 = Point3F(endRadius,0,0);
		ePt2 = Point3F(-endRadius,0,0);

		orient.mulV( ePt1 );
		ePt1 += pEnd;
		orient.mulV( ePt2 );
		ePt2 += pEnd;

		PrimBuild::begin(GFXTriangleFan, 4);
		PrimBuild::texCoord2f(0, vStart);
		PrimBuild::vertex3f(sPt1.x, sPt1.y, sPt1.z);
		PrimBuild::texCoord2f(1, vStart);
		PrimBuild::vertex3f(sPt2.x, sPt2.y, sPt2.z);
		PrimBuild::texCoord2f(1, vEnd);
		PrimBuild::vertex3f(ePt2.x, ePt2.y, ePt2.z);
		PrimBuild::texCoord2f(0, vEnd);
		PrimBuild::vertex3f(ePt1.x, ePt1.y, ePt1.z);
		PrimBuild::end();
	}

	{
		sPt1 = Point3F(0,0,startRadius);
		sPt2 = Point3F(0,0,-startRadius);

		orient.mulV( sPt1 );
		sPt1 += pStart;
		orient.mulV( sPt2 );
		sPt2 += pStart;

		ePt1 = Point3F(0,0,endRadius);
		ePt2 = Point3F(0,0,-endRadius);

		orient.mulV( ePt1 );
		ePt1 += pEnd;
		orient.mulV( ePt2 );
		ePt2 += pEnd;

		PrimBuild::begin(GFXTriangleFan, 4);
		PrimBuild::texCoord2f(0, vStart);
		PrimBuild::vertex3f(sPt1.x, sPt1.y, sPt1.z);
		PrimBuild::texCoord2f(1, vStart);
		PrimBuild::vertex3f(sPt2.x, sPt2.y, sPt2.z);
		PrimBuild::texCoord2f(1, vEnd);
		PrimBuild::vertex3f(ePt2.x, ePt2.y, ePt2.z);
		PrimBuild::texCoord2f(0, vEnd);
		PrimBuild::vertex3f(ePt1.x, ePt1.y, ePt1.z);
		PrimBuild::end();
	}


	{
		sPt1 = Point3F(startRadius,0,startRadius);
		sPt2 = Point3F(-startRadius,0,-startRadius);

		orient.mulV( sPt1 );
		sPt1 += pStart;
		orient.mulV( sPt2 );
		sPt2 += pStart;

		ePt1 = Point3F(endRadius,0,endRadius);
		ePt2 = Point3F(-endRadius,0,-endRadius);

		orient.mulV( ePt1 );
		ePt1 += pEnd;
		orient.mulV( ePt2 );
		ePt2 += pEnd;

		PrimBuild::begin(GFXTriangleFan, 4);
		PrimBuild::texCoord2f(0, vStart);
		PrimBuild::vertex3f(sPt1.x, sPt1.y, sPt1.z);
		PrimBuild::texCoord2f(1, vStart);
		PrimBuild::vertex3f(sPt2.x, sPt2.y, sPt2.z);
		PrimBuild::texCoord2f(1, vEnd);
		PrimBuild::vertex3f(ePt2.x, ePt2.y, ePt2.z);
		PrimBuild::texCoord2f(0, vEnd);
		PrimBuild::vertex3f(ePt1.x, ePt1.y, ePt1.z);
		PrimBuild::end();
	}

	{
		sPt1 = Point3F(startRadius,0,-startRadius);
		sPt2 = Point3F(-startRadius,0,startRadius);

		orient.mulV( sPt1 );
		sPt1 += pStart;
		orient.mulV( sPt2 );
		sPt2 += pStart;

		ePt1 = Point3F(endRadius,0,-endRadius);
		ePt2 = Point3F(-endRadius,0,endRadius);

		orient.mulV( ePt1 );
		ePt1 += pEnd;
		orient.mulV( ePt2 );
		ePt2 += pEnd;

		PrimBuild::begin(GFXTriangleFan, 4);
		PrimBuild::texCoord2f(0, vStart);
		PrimBuild::vertex3f(sPt1.x, sPt1.y, sPt1.z);
		PrimBuild::texCoord2f(1, vStart);
		PrimBuild::vertex3f(sPt2.x, sPt2.y, sPt2.z);
		PrimBuild::texCoord2f(1, vEnd);
		PrimBuild::vertex3f(ePt2.x, ePt2.y, ePt2.z);
		PrimBuild::texCoord2f(0, vEnd);
		PrimBuild::vertex3f(ePt1.x, ePt1.y, ePt1.z);
		PrimBuild::end();
	}
}

Edit: I did not like the way the beams were being rendered. This way it much better. Also added a screenshot, nothing fancy just to show a blaster effect.

Page«First 1 2 Next»
#21
05/06/2009 (6:04 pm)
Try to comment out your emitter and see if you still see that "thin blue line":

//particleEmitter = Gun_1aEmitter;

I did that and my entire projectile completely disappeared. Your code should be creating the 'beam' without using an emitter, so that line shouldn't be needed...
#22
05/06/2009 (6:07 pm)

Can you show me your ShapeBaseImageData that you are using for your gun?

#23
05/06/2009 (6:11 pm)
The thin blue line I mentioned is due to me using the energy.png from the swarmgun as I had indicated and nothing to do with the emitters.

#24
05/06/2009 (6:16 pm)
Here it is:

datablock ShapeBaseImageData(Gun_1aImage)
{
   // Basic Item properties
   shapeFile = "~/data/shapes/weapons/Gun_1a/swarmgun.dts";
   emap = true;

   // Specify mount point & offset for 3rd person, and eye offset
   // for first person rendering.
   mountPoint = 0;
   firstPerson = false;
   offset = "0 0.25 0";
   eyeOffset = "0.45 0.55 -0.5";
   
   // The model may be backwards
   // rotation = "0.0 0.0 1.0 180.0";
   // eyeRotation = "0.0 0.0 1.0 180.0";

   // When firing from a point offset from the eye, muzzle correction
   // will adjust the muzzle vector to point to the eye LOS point.
   // Since this weapon doesn't actually fire from the muzzle point,
   // we need to turn this off.
   correctMuzzleVector = true;

   // Add the WeaponImage namespace as a parent, WeaponImage namespace
   // provides some hooks into the inventory system.
   className = "WeaponImage";

   // Projectile && Ammo.
   item = Gun_1a;
   ammo = Gun_1aAmmo;
   projectile = Gun_1aProjectile;
   projectileType = EnergyProjectile;
   casing = Gun_1aShell;
   
   shellExitDir        = "1.0 0.3 1.0";
   shellExitOffset     = "0.15 -0.56 -0.1";
   shellExitVariance   = 15.0;
   shellVelocity       = 3.0;

   // Images have a state system which controls how the animations
   // are run, which sounds are played, script callbacks, etc. This
   // state system is downloaded to the client so that clients can
   // predict state changes and animate accordingly.  The following
   // system supports basic ready->fire->reload transitions as
   // well as a no-ammo->dryfire idle state.

   // Initial start up state
   stateName[0]                     = "Preactivate";
   stateTransitionOnLoaded[0]       = "Activate";
   stateTransitionOnNoAmmo[0]       = "NoAmmo";

   // Activating the gun.  Called when the weapon is first
   // mounted and there is ammo.
   stateName[1]                     = "Activate";
   stateTransitionOnTimeout[1]      = "Ready";
   stateTimeoutValue[1]             = 0.5;
   stateSequence[1]                 = "Activate";

   // Ready to fire, just waiting for the trigger
   stateName[2]                     = "Ready";
   stateTransitionOnNoAmmo[2]       = "NoAmmo";
   stateTransitionOnTriggerDown[2]  = "Fire";

   // Fire the weapon. Calls the fire script which does
   // the actual work.
   stateName[3]                     = "Fire";
   stateTransitionOnTimeout[3]      = "Reload";
   stateTimeoutValue[3]             = 0.2;
   stateFire[3]                     = true;
   stateRecoil[3]                   = LightRecoil;
   stateAllowImageChange[3]         = false;
   stateSequence[3]                 = "Fire";
   stateScript[3]                   = "onFire";
   //stateEmitter[3]                  = Gun_1aFireEmitter;
   //stateEmitterTime[3]              = 0.3;
   stateSound[3]                    = Gun_1aFireSound;

   // Play the relead animation, and transition into
   stateName[4]                     = "Reload";
   stateTransitionOnNoAmmo[4]       = "NoAmmo";
   stateTransitionOnTimeout[4]      = "Ready";
   stateTimeoutValue[4]             = 0.2;
   stateAllowImageChange[4]         = false;
   stateSequence[4]                 = "Reload";
   stateEjectShell[4]               = true;
   stateSound[4]                    = Gun_1aReloadSound;

   // No ammo in the weapon, just idle until something
   // shows up. Play the dry fire sound if the trigger is
   // pulled.
   stateName[5]                     = "NoAmmo";
   stateTransitionOnAmmo[5]         = "Reload";
   stateSequence[5]                 = "NoAmmo";
   stateTransitionOnTriggerDown[5]  = "DryFire";

   // No ammo dry fire
   stateName[6]                     = "DryFire";
   stateTimeoutValue[6]             = 0.2;
   stateTransitionOnTimeout[6]      = "NoAmmo";
   stateScript[6]                   = "onDryFire";
};
#25
05/06/2009 (6:21 pm)
Humm, do you have any Ammo ? Gun_1aAmmo

Lasers can't fire without ammo ;)

try changing
ammo = Gun_1aAmmo;

to

ammo = RocketLauncherAmmo;

Assuming you still have the orginal SwarmGun still there, or give yourself some of the Gun_1aAmmo.

My lasers still need ammo and use clips of power to fire.

#26
05/06/2009 (6:34 pm)
I'm using this to equip the weapon:

function armHGa()  // Development function.  Arms the target player with this weapon in mount 0, gives ammo (doesn't deplete currently)
{                            // For testing purposes.
    // mount the weapon
    $player.mountImage( Gun_1aImage, 0 );
    // give some ammo
    $player.setImageAmmo( 0, true );
}

And I can confirm the weapon is firing and destroying objects in my level, just cant see the projectile at all

Oh, I unequip the previous weapon first through console:
$player.unmountimage(0)
#27
05/06/2009 (6:55 pm)
Ok, I am at a loss then.

Lets try from the top again.

Can you get yourself a new copy of TGEA 1.8.1 and install it?

Create the file T3D/EnergyProjectile.cpp and copy the stuff I posted.

Make the appropriot changes to the Projectile class.

Now open the template project and compile

Get the red_beam.png and red.dml from the resource I linked and put them in TGEA_1_8_1/Projects/Template/Game/ScriptsAndAssets/Data/Shapes/Weapons/Laserbeam

Now open the the file
TGEA_1_8_1/Projects/Template/Game/ScriptsAndAssets/Server/scripts/weapons/SwarmGun.cs

Look for datablock EnergyProjectileData(RocketLauncherProjectile) and make it to look like the following
datablock EnergyProjectileData(RocketLauncherProjectile)
{
   projectileShapeName = "~/data/shapes/weapons/SwarmGun/rocket.dts";
   directDamage        = 20;
   radiusDamage        = 20;
   damageRadius        = 5;
   areaImpulse         = 2000;
   
   explosion           = RocketExplosion;

   particleEmitter     = RocketEmitter;

   muzzleVelocity      = 100;
   velInheritFactor    = 1;

   armingDelay         = 0;
   lifetime            = 6000;
   fadeDelay           = 1500;
   bounceElasticity    = 0;
   bounceFriction      = 0;
   isBallistic         = true;
   gravityMod = 0.10;

   hasLight    = true;
   lightRadius = 4.0;
   lightColor  = "0.5 0.5 0";

   beamEnabled = true;   
   beamPulse = true;   
   beamStartRadius = 0.2;   
   beamMidRadius = 0.3;   
   beamEndRadius = 0.2;   
   beamFrontSegmentLength = 5;   
   beamBackSegmentLength = 10;   
   beamMaterialList = "~/data/shapes/weapons/laserbeam/red.dml";   
  
   sparkEnabled = false;   
   sparkMaterialList = "~/data/shapes/weapons/laserbeam/spark.dml";   
   sparkRadius = 0.3;   
   sparkStep = 0.0;   
   sparkRotationStep = 0.0;   
  
   interval = 5;  
};

Scroll Down to datablock ShapeBaseImageData(RocketLauncherImage)

change
projectileType = Projectile;

to look like

projectileType = EnergyProjectile;


Once done, go to TGEA_1_8_1/Projects/Template/Game and run the newly created EXE, if you compiled in debug, Template_DEBUG.exe

Thats all the changes I did to a stock TGEA 1.8.1 and it worked. Does this work for you?

Had another thought, your on windows? or Mac?

PS. You wont see the projectile, but its used for collision. You should be seeing beams though, and why your not, is confusing.

#28
05/06/2009 (7:22 pm)
Hmmmm that works perfectly...

I guess I am completely at a loss as well...


BTW:

Rather than redo the EnergyProjectile.cpp, projectile.cpp, and projectile.h, I simply used the ones I made early from the one that was NOT working, and it DID work, so the problem is NOT with those 3 files...
#29
05/06/2009 (7:38 pm)
That is good to know.

The only thing I can think of, in your original project is to add some debug logging.

Around line 460 in EnergyProjectile, find

ObjectRenderInst *ri = gRenderInstManager->allocInst<ObjectRenderInst>();

add

Con::printf("** Queue Laser Render Object ** ");
ObjectRenderInst *ri = gRenderInstManager->allocInst<ObjectRenderInst>();


Around line 527 in EnergyProjectile, look for

if (mDataBlock->beamEnabled && mBeamNumTextures > 0)

add

Con::printf("** Laser Render %d %d **",mDataBlock->beamEnabled, mBeamNumTextures ); // Note, should probably display 1 1 for the values
if (mDataBlock->beamEnabled && mBeamNumTextures > 0)


Run your app, and see if you see those messages.

Otherwise, without getting your full sourcecode and app so I can debug it, I don't think there is any more I can do as it works in stock TGEA 1.8.1.






#30
05/06/2009 (7:44 pm)
Well, I got it down to my weapon's .cs file. I completely replaced it with the default swarmgun.cs, added in your changes, and it is now working in my game. :)

Now I just need to merge in my code 1 line at a time to see when it breaks....

Oh, and even though I am using the red laser beam, I am getting a white beam in my game...

When I ran it in the Template project, it was indeed red... The weird part is that I used the swarmgun.cs from the working Template project with the red beam to make my new one, so I don't see why it's coming out white...

But hey, I got the gun in the game! :)
#31
05/06/2009 (11:12 pm)
Well I got it to work but made a couple of observations that probably need fixed.

1. The rendering cuts out when shooting objects at close range.

2. The muzzle point/start of the rendering should probably move with the position of the muzzle. In other words, doing a side step doesn't leave a floating render if you have more than one image in the dml.

Just a couple of suggestions. The second one should be very easy to fix, not sure about the first though. I am probably going to try to do it on my end so if I get there first I will share.
#32
05/07/2009 (8:13 am)
EDITED:

Don't forget:

Change:
datablock ProjectileData(Gun_1Projectile)

To:
datablock EnergyProjectileData(Gun_1Projectile)


LOL!!

So I am getting this to work with all my guns now... no idea why it wouldn't work yesterday.

Last problem then:

I am using the red_beam.png and red.dml from that laser resource, but my beam is white... any ideas?
#33
05/11/2009 (7:43 am)
Seems like the beam type should overload the collision method and do a simple ray cast from the muzzle point . . . I like them being derived from projectile so we can just load them into weapons, but maybe EnergyBeam should be a new projectile type that handles collisions and rendering differently?
#34
05/11/2009 (7:47 am)
sorry to be rude. Nice work BTW!
#35
08/26/2009 (11:29 pm)
Simon, I have integrated this into my Enhanced Projectiles system so it could use all of the things I added with that resource and of course with my version which is far more updated than the original there are even more options. What I wanted to ask is if you would mind me releasing my new version of my resource with my integrated version of yours combined?
#36
08/26/2009 (11:39 pm)

Go right ahead, glad someone else has gotten something out of it and enhanced it even more.
#37
01/18/2012 (10:11 am)
I ported this to T3D 1.2 today, I will be looking at posting a new resource with this file.
#38
01/18/2012 (7:34 pm)
Preview of the weapon in action using the energyProjectile in T3D 1.2, resource pending.
Page«First 1 2 Next»