tge/engine/game/fx/explosion.cc
2017-04-17 06:17:10 -06:00

1127 lines
35 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "game/fx/explosion.h"
#include "console/consoleTypes.h"
#include "console/typeValidators.h"
#include "dgl/dgl.h"
#include "platform/platformAudio.h"
#include "audio/audioDataBlock.h"
#include "sceneGraph/sceneGraph.h"
#include "sceneGraph/sceneState.h"
#include "core/bitStream.h"
#include "sim/netConnection.h"
#include "ts/tsShape.h"
#include "ts/tsShapeInstance.h"
#include "math/mRandom.h"
#include "math/mathIO.h"
#include "math/mathUtils.h"
#include "game/debris.h"
#include "game/gameConnection.h"
#include "game/fx/particleEngine.h"
#include "game/fx/cameraFXMgr.h"
#include "util/safeDelete.h"
IMPLEMENT_CONOBJECT(Explosion);
#define MaxLightRadius 20
MRandomLCG sgRandom(0xdeadbeef);
ConsoleFunction( calcExplosionCoverage, F32, 4, 4, "(Point3F source, SceneObject originator, bitset coverageMask)")
{
Point3F pos, center;
dSscanf(argv[1], "%g %g %g", &pos.x, &pos.y, &pos.z);
S32 id = dAtoi(argv[2]);
U32 covMask = (U32)dAtoi(argv[3]);
SceneObject* sceneObject = NULL;
if (Sim::findObject(id, sceneObject) == false)
{
Con::warnf(ConsoleLogEntry::General, "calcExplosionCoverage: couldn't find object: %s", argv[2]);
return 1.0;
}
if (sceneObject->isClientObject() || sceneObject->getContainer() == NULL)
{
Con::warnf(ConsoleLogEntry::General, "calcExplosionCoverage: object is on the client, or not in the container system");
return 1.0;
}
sceneObject->getObjBox().getCenter(&center);
center.convolve(sceneObject->getScale());
sceneObject->getTransform().mulP(center);
RayInfo rayInfo;
sceneObject->disableCollision();
if (sceneObject->getContainer()->castRay(pos, center, covMask, &rayInfo) == true)
{
// Try casting up and then out
if (sceneObject->getContainer()->castRay(pos, pos + Point3F(0, 0, 1), covMask, &rayInfo) == false)
{
if (sceneObject->getContainer()->castRay(pos + Point3F(0, 0, 1), center, covMask, &rayInfo) == false)
{
sceneObject->enableCollision();
return 1.0;
}
}
sceneObject->enableCollision();
return 0.0;
}
else
{
sceneObject->enableCollision();
return 1.0;
}
}
//----------------------------------------------------------------------------
//
IMPLEMENT_CO_DATABLOCK_V1(ExplosionData);
ExplosionData::ExplosionData()
{
dtsFileName = NULL;
particleDensity = 10;
particleRadius = 1;
faceViewer = false;
soundProfile = NULL;
particleEmitter = NULL;
soundProfileId = 0;
particleEmitterId = 0;
explosionScale.set(1, 1, 1);
playSpeed = 1.0;
dMemset( emitterList, 0, sizeof( emitterList ) );
dMemset( emitterIDList, 0, sizeof( emitterIDList ) );
dMemset( debrisList, 0, sizeof( debrisList ) );
dMemset( debrisIDList, 0, sizeof( debrisIDList ) );
debrisThetaMin = 0;
debrisThetaMax = 90;
debrisPhiMin = 0;
debrisPhiMax = 360;
debrisNum = 1;
debrisNumVariance = 0;
debrisVelocity = 2.0;
debrisVelocityVariance = 0.0;
dMemset( explosionList, 0, sizeof( explosionList ) );
dMemset( explosionIDList, 0, sizeof( explosionIDList ) );
delayMS = 0;
delayVariance = 0;
lifetimeMS = 1000;
lifetimeVariance = 0;
offset = 0;
shockwave = NULL;
shockwaveID = 0;
shockwaveOnTerrain = false;
shakeCamera = false;
camShakeFreq.set( 10.0, 10.0, 10.0 );
camShakeAmp.set( 1.0, 1.0, 1.0 );
camShakeDuration = 1.5;
camShakeRadius = 10.0;
camShakeFalloff = 10.0;
for( U32 i=0; i<EC_NUM_TIME_KEYS; i++ )
{
times[i] = 1.0;
}
times[0] = 0.0;
for( U32 j=0; j<EC_NUM_TIME_KEYS; j++ )
{
sizes[j].set( 1.0, 1.0, 1.0 );
}
//
lightStartRadius = lightEndRadius = 0;
lightStartColor.set(1,1,1);
lightEndColor.set(1,1,1);
}
IMPLEMENT_CONSOLETYPE(ExplosionData)
IMPLEMENT_SETDATATYPE(ExplosionData)
IMPLEMENT_GETDATATYPE(ExplosionData)
void ExplosionData::initPersistFields()
{
Parent::initPersistFields();
addField("explosionShape", TypeFilename, Offset(dtsFileName, ExplosionData));
addField("soundProfile", TypeAudioProfilePtr, Offset(soundProfile, ExplosionData));
addField("faceViewer", TypeBool, Offset(faceViewer, ExplosionData));
addField("particleEmitter", TypeParticleEmitterDataPtr, Offset(particleEmitter, ExplosionData));
addField("particleDensity", TypeS32, Offset(particleDensity, ExplosionData));
addField("particleRadius", TypeF32, Offset(particleRadius, ExplosionData));
addField("explosionScale", TypePoint3F, Offset(explosionScale, ExplosionData));
addField("playSpeed", TypeF32, Offset(playSpeed, ExplosionData));
addField("emitter", TypeParticleEmitterDataPtr, Offset(emitterList, ExplosionData), EC_NUM_EMITTERS);
addField("debris", TypeDebrisDataPtr, Offset(debrisList, ExplosionData), EC_NUM_DEBRIS_TYPES);
addField("debrisThetaMin", TypeF32, Offset(debrisThetaMin, ExplosionData));
addField("debrisThetaMax", TypeF32, Offset(debrisThetaMax, ExplosionData));
addField("debrisPhiMin", TypeF32, Offset(debrisPhiMin, ExplosionData));
addField("debrisPhiMax", TypeF32, Offset(debrisPhiMax, ExplosionData));
addField("debrisNum", TypeS32, Offset(debrisNum, ExplosionData));
addField("debrisNumVariance", TypeS32, Offset(debrisNumVariance, ExplosionData));
addField("debrisVelocity", TypeF32, Offset(debrisVelocity, ExplosionData));
addField("debrisVelocityVariance", TypeF32, Offset(debrisVelocityVariance, ExplosionData));
addField("subExplosion", TypeExplosionDataPtr, Offset(explosionList, ExplosionData), EC_MAX_SUB_EXPLOSIONS );
addField("delayMS", TypeS32, Offset(delayMS, ExplosionData));
addField("delayVariance", TypeS32, Offset(delayVariance, ExplosionData));
addField("lifetimeMS", TypeS32, Offset(lifetimeMS, ExplosionData));
addField("lifetimeVariance", TypeS32, Offset(lifetimeVariance, ExplosionData));
addField("offset", TypeF32, Offset(offset, ExplosionData));
addField("times", TypeF32, Offset(times, ExplosionData), EC_NUM_TIME_KEYS );
addField("sizes", TypePoint3F, Offset(sizes, ExplosionData), EC_NUM_TIME_KEYS );
addField("shakeCamera", TypeBool, Offset(shakeCamera, ExplosionData));
addField("camShakeFreq", TypePoint3F, Offset(camShakeFreq, ExplosionData));
addField("camShakeAmp", TypePoint3F, Offset(camShakeAmp, ExplosionData));
addField("camShakeDuration", TypeF32, Offset(camShakeDuration, ExplosionData));
addField("camShakeRadius", TypeF32, Offset(camShakeRadius, ExplosionData));
addField("camShakeFalloff", TypeF32, Offset(camShakeFalloff, ExplosionData));
addNamedFieldV(lightStartRadius, TypeF32, ExplosionData, new FRangeValidator(0, MaxLightRadius));
addNamedFieldV(lightEndRadius, TypeF32, ExplosionData, new FRangeValidator(0, MaxLightRadius));
addNamedField(lightStartColor, TypeColorF, ExplosionData);
addNamedField(lightEndColor, TypeColorF, ExplosionData);
}
bool ExplosionData::onAdd()
{
if (Parent::onAdd() == false)
return false;
if (!soundProfile && soundProfileId != 0)
if (Sim::findObject(soundProfileId, soundProfile) == false)
Con::errorf(ConsoleLogEntry::General, "Error, unable to load sound profile for explosion datablock");
if (!particleEmitter && particleEmitterId != 0)
if (Sim::findObject(particleEmitterId, particleEmitter) == false)
Con::errorf(ConsoleLogEntry::General, "Error, unable to load particle emitter for explosion datablock");
if (explosionScale.x < 0.01 || explosionScale.y < 0.01 || explosionScale.z < 0.01)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s)::onAdd: ExplosionScale components must be >= 0.01", getName());
explosionScale.x = explosionScale.x < 0.01 ? 0.01 : explosionScale.x;
explosionScale.y = explosionScale.y < 0.01 ? 0.01 : explosionScale.y;
explosionScale.z = explosionScale.z < 0.01 ? 0.01 : explosionScale.z;
}
if (debrisThetaMin < 0.0f)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisThetaMin < 0.0", getName());
debrisThetaMin = 0.0f;
}
if (debrisThetaMax > 180.0f)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisThetaMax > 180.0", getName());
debrisThetaMax = 180.0f;
}
if (debrisThetaMin > debrisThetaMax)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisThetaMin > debrisThetaMax", getName());
debrisThetaMin = debrisThetaMax;
}
if (debrisPhiMin < 0.0f)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisPhiMin < 0.0", getName());
debrisPhiMin = 0.0f;
}
if (debrisPhiMax > 360.0f)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisPhiMax > 360.0", getName());
debrisPhiMax = 360.0f;
}
if (debrisPhiMin > debrisPhiMax)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisPhiMin > debrisPhiMax", getName());
debrisPhiMin = debrisPhiMax;
}
if (debrisNum > 1000)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisNum > 1000", getName());
debrisNum = 1000;
}
if (debrisNumVariance > 1000)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisNumVariance > 1000", getName());
debrisNumVariance = 1000;
}
if (debrisVelocity < 0.1)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisVelocity < 0.1", getName());
debrisVelocity = 0.1;
}
if (debrisVelocityVariance > 1000)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisVelocityVariance > 1000", getName());
debrisVelocityVariance = 1000;
}
if (playSpeed < 0.05)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) playSpeed < 0.05", getName());
playSpeed = 0.05;
}
if (lifetimeMS < 1)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) lifetimeMS < 1", getName());
lifetimeMS = 1;
}
if (lifetimeVariance > lifetimeMS)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) lifetimeVariance > lifetimeMS", getName());
lifetimeVariance = lifetimeMS;
}
if (delayMS < 0)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) delayMS < 0", getName());
delayMS = 0;
}
if (delayVariance > delayMS)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) delayVariance > delayMS", getName());
delayVariance = delayMS;
}
if (offset < 0.0)
{
Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) offset < 0.0", getName());
offset = 0.0;
}
S32 i;
for( i=0; i<EC_NUM_DEBRIS_TYPES; i++ )
{
if( !debrisList[i] && debrisIDList[i] != 0 )
{
if( !Sim::findObject( debrisIDList[i], debrisList[i] ) )
{
Con::errorf( ConsoleLogEntry::General, "ExplosionData::onAdd: Invalid packet, bad datablockId(debris): 0x%x", debrisIDList[i] );
}
}
}
for( i=0; i<EC_NUM_EMITTERS; i++ )
{
if( !emitterList[i] && emitterIDList[i] != 0 )
{
if( Sim::findObject( emitterIDList[i], emitterList[i] ) == false)
{
Con::errorf( ConsoleLogEntry::General, "ExplosionData::onAdd: Invalid packet, bad datablockId(particle emitter): 0x%x", emitterIDList[i] );
}
}
}
for( S32 k=0; k<EC_MAX_SUB_EXPLOSIONS; k++ )
{
if( !explosionList[k] && explosionIDList[k] != 0 )
{
if( Sim::findObject( explosionIDList[k], explosionList[k] ) == false)
{
Con::errorf( ConsoleLogEntry::General, "ExplosionData::onAdd: Invalid packet, bad datablockId(explosion): 0x%x", explosionIDList[k] );
}
}
}
return true;
}
void ExplosionData::packData(BitStream* stream)
{
Parent::packData(stream);
stream->writeString(dtsFileName);
if (stream->writeFlag(soundProfile != NULL))
stream->writeRangedU32(soundProfile->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
if (stream->writeFlag(particleEmitter))
stream->writeRangedU32(particleEmitter->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
stream->writeInt(particleDensity, 14);
stream->write(particleRadius);
stream->writeFlag(faceViewer);
if(stream->writeFlag(explosionScale.x != 1 || explosionScale.y != 1 || explosionScale.z != 1))
{
stream->writeInt((S32)(explosionScale.x * 100), 16);
stream->writeInt((S32)(explosionScale.y * 100), 16);
stream->writeInt((S32)(explosionScale.z * 100), 16);
}
stream->writeInt((S32)(playSpeed * 20), 14);
stream->writeRangedU32((U32)debrisThetaMin, 0, 180);
stream->writeRangedU32((U32)debrisThetaMax, 0, 180);
stream->writeRangedU32((U32)debrisPhiMin, 0, 360);
stream->writeRangedU32((U32)debrisPhiMax, 0, 360);
stream->writeRangedU32((U32)debrisNum, 0, 1000);
stream->writeRangedU32(debrisNumVariance, 0, 1000);
stream->writeInt((S32)(debrisVelocity * 10), 14);
stream->writeRangedU32((U32)(debrisVelocityVariance * 10), 0, 10000);
stream->writeInt(delayMS >> 5, 16);
stream->writeInt(delayVariance >> 5, 16);
stream->writeInt(lifetimeMS >> 5, 16);
stream->writeInt(lifetimeVariance >> 5, 16);
stream->write(offset);
stream->writeFlag( shakeCamera );
stream->write(camShakeFreq.x);
stream->write(camShakeFreq.y);
stream->write(camShakeFreq.z);
stream->write(camShakeAmp.x);
stream->write(camShakeAmp.y);
stream->write(camShakeAmp.z);
stream->write(camShakeDuration);
stream->write(camShakeRadius);
stream->write(camShakeFalloff);
for( S32 j=0; j<EC_NUM_DEBRIS_TYPES; j++ )
{
if( stream->writeFlag( debrisList[j] ) )
{
stream->writeRangedU32( debrisList[j]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
}
S32 i;
for( i=0; i<EC_NUM_EMITTERS; i++ )
{
if( stream->writeFlag( emitterList[i] != NULL ) )
{
stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
}
for( i=0; i<EC_MAX_SUB_EXPLOSIONS; i++ )
{
if( stream->writeFlag( explosionList[i] != NULL ) )
{
stream->writeRangedU32( explosionList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
}
U32 count;
for(count = 0; count < EC_NUM_TIME_KEYS; count++)
if(times[i] >= 1)
break;
count++;
if(count > EC_NUM_TIME_KEYS)
count = EC_NUM_TIME_KEYS;
stream->writeRangedU32(count, 0, EC_NUM_TIME_KEYS);
for( i=0; i<count; i++ )
stream->writeFloat( times[i], 8 );
for( i=0; i<count; i++ )
{
stream->writeRangedU32((U32)(sizes[i].x * 100), 0, 16000);
stream->writeRangedU32((U32)(sizes[i].y * 100), 0, 16000);
stream->writeRangedU32((U32)(sizes[i].z * 100), 0, 16000);
}
// Dynamic light info
stream->writeFloat(lightStartRadius/MaxLightRadius, 8);
stream->writeFloat(lightEndRadius/MaxLightRadius, 8);
stream->writeFloat(lightStartColor.red,7);
stream->writeFloat(lightStartColor.green,7);
stream->writeFloat(lightStartColor.blue,7);
stream->writeFloat(lightEndColor.red,7);
stream->writeFloat(lightEndColor.green,7);
stream->writeFloat(lightEndColor.blue,7);
}
void ExplosionData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
dtsFileName = stream->readSTString();
if (stream->readFlag())
soundProfileId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
soundProfileId = 0;
if (stream->readFlag())
particleEmitterId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
particleEmitterId = 0;
particleDensity = stream->readInt(14);
stream->read(&particleRadius);
faceViewer = stream->readFlag();
if(stream->readFlag())
{
explosionScale.x = stream->readInt(16) / 100.0f;
explosionScale.y = stream->readInt(16) / 100.0f;
explosionScale.z = stream->readInt(16) / 100.0f;
}
else
explosionScale.set(1,1,1);
playSpeed = stream->readInt(14) / 20.0f;
debrisThetaMin = stream->readRangedU32(0, 180);
debrisThetaMax = stream->readRangedU32(0, 180);
debrisPhiMin = stream->readRangedU32(0, 360);
debrisPhiMax = stream->readRangedU32(0, 360);
debrisNum = stream->readRangedU32(0, 1000);
debrisNumVariance = stream->readRangedU32(0, 1000);
debrisVelocity = stream->readInt(14) / 10.0f;
debrisVelocityVariance = stream->readRangedU32(0, 10000) / 10.0f;
delayMS = stream->readInt(16) << 5;
delayVariance = stream->readInt(16) << 5;
lifetimeMS = stream->readInt(16) << 5;
lifetimeVariance = stream->readInt(16) << 5;
stream->read(&offset);
shakeCamera = stream->readFlag();
stream->read(&camShakeFreq.x);
stream->read(&camShakeFreq.y);
stream->read(&camShakeFreq.z);
stream->read(&camShakeAmp.x);
stream->read(&camShakeAmp.y);
stream->read(&camShakeAmp.z);
stream->read(&camShakeDuration);
stream->read(&camShakeRadius);
stream->read(&camShakeFalloff);
for( S32 j=0; j<EC_NUM_DEBRIS_TYPES; j++ )
{
if( stream->readFlag() )
{
debrisIDList[j] = (S32) stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
}
U32 i;
for( i=0; i<EC_NUM_EMITTERS; i++ )
{
if( stream->readFlag() )
{
emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
}
for( S32 k=0; k<EC_MAX_SUB_EXPLOSIONS; k++ )
{
if( stream->readFlag() )
{
explosionIDList[k] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
}
U32 count = stream->readRangedU32(0, EC_NUM_TIME_KEYS);
for( i=0; i<count; i++ )
times[i] = stream->readFloat(8);
for( i=0; i<count; i++ )
{
sizes[i].x = stream->readRangedU32(0, 16000) / 100.0f;
sizes[i].y = stream->readRangedU32(0, 16000) / 100.0f;
sizes[i].z = stream->readRangedU32(0, 16000) / 100.0f;
}
//
lightStartRadius = stream->readFloat(8) * MaxLightRadius;
lightEndRadius = stream->readFloat(8) * MaxLightRadius;
lightStartColor.red = stream->readFloat(7);
lightStartColor.green = stream->readFloat(7);
lightStartColor.blue = stream->readFloat(7);
lightEndColor.red = stream->readFloat(7);
lightEndColor.green = stream->readFloat(7);
lightEndColor.blue = stream->readFloat(7);
}
bool ExplosionData::preload(bool server, char errorBuffer[256])
{
if (Parent::preload(server, errorBuffer) == false)
return false;
if (dtsFileName && dtsFileName[0])
{
explosionShape = ResourceManager->load(dtsFileName);
if (!bool(explosionShape))
{
dSprintf(errorBuffer, sizeof(errorBuffer), "ExplosionData: Couldn't load shape \"%s\"", dtsFileName);
return false;
}
// Resolve animations
explosionAnimation = explosionShape->findSequence("ambient");
// Preload textures with a dummy instance...
TSShapeInstance* pDummy = new TSShapeInstance(explosionShape, !server);
delete pDummy;
}
else
{
explosionShape = NULL;
explosionAnimation = -1;
}
return true;
}
//--------------------------------------------------------------------------
Explosion::Explosion()
{
mTypeMask |= ExplosionObjectType;
mExplosionInstance = NULL;
mExplosionThread = NULL;
dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
mFade = 1;
mDelayMS = 0;
mCurrMS = 0;
mEndingMS = 1000;
mActive = false;
mCollideType = 0;
mInitialNormal.set( 0.0, 0.0, 1.0 );
mRandAngle = sgRandom.randF( 0.0, 1.0 ) * M_PI * 2.0;
}
Explosion::~Explosion()
{
if( mExplosionInstance )
{
delete mExplosionInstance;
mExplosionInstance = NULL;
mExplosionThread = NULL;
}
}
void Explosion::setInitialState(const Point3F& point, const Point3F& normal, const F32 fade)
{
setPosition(point);
mInitialNormal = normal;
mFade = fade;
mFog = 0.0f;
}
//--------------------------------------------------------------------------
void Explosion::initPersistFields()
{
Parent::initPersistFields();
//
}
//--------------------------------------------------------------------------
bool Explosion::onAdd()
{
// first check if we have a server connection, if we dont then this is on the server
// and we should exit, then check if the parent fails to add the object
GameConnection* conn = GameConnection::getConnectionToServer();
if(!conn || !Parent::onAdd())
return false;
mDelayMS = mDataBlock->delayMS + sgRandom.randI( -mDataBlock->delayVariance, mDataBlock->delayVariance );
mEndingMS = mDataBlock->lifetimeMS + sgRandom.randI( -mDataBlock->lifetimeVariance, mDataBlock->lifetimeVariance );
if( mFabs( mDataBlock->offset ) > 0.001 )
{
MatrixF axisOrient = MathUtils::createOrientFromDir( mInitialNormal );
MatrixF trans = getTransform();
Point3F randVec;
randVec.x = sgRandom.randF( -1.0, 1.0 );
randVec.y = sgRandom.randF( 0.0, 1.0 );
randVec.z = sgRandom.randF( -1.0, 1.0 );
randVec.normalize();
randVec *= mDataBlock->offset;
axisOrient.mulV( randVec );
trans.setPosition( trans.getPosition() + randVec );
setTransform( trans );
}
// shake camera
if( mDataBlock->shakeCamera)
{
// first check if explosion is near player
GameConnection* connection = GameConnection::getConnectionToServer();
ShapeBase *obj = connection->getControlObject();
bool applyShake = true;
if( obj )
{
ShapeBase* cObj = obj;
while((cObj = cObj->getControlObject()) != 0)
{
if(cObj->useObjsEyePoint())
{
applyShake = false;
break;
}
}
}
if( applyShake && obj )
{
VectorF diff = obj->getPosition() - getPosition();
F32 dist = diff.len();
if( dist < mDataBlock->camShakeRadius )
{
CameraShake *camShake = new CameraShake;
camShake->setDuration( mDataBlock->camShakeDuration );
camShake->setFrequency( mDataBlock->camShakeFreq );
F32 falloff = dist / mDataBlock->camShakeRadius;
falloff = 1 + falloff * 10.0;
falloff = 1.0 / (falloff * falloff);
VectorF shakeAmp = mDataBlock->camShakeAmp * falloff;
camShake->setAmplitude( shakeAmp );
camShake->setFalloff( mDataBlock->camShakeFalloff );
camShake->init();
gCamFXMgr.addFX( camShake );
}
}
}
// Try to explode!
if( mDelayMS == 0 && !explode() )
return false;
mRandomVal = sgRandom.randF();
gClientContainer.addObject(this);
gClientSceneGraph->addObjectToScene(this);
removeFromProcessList();
gClientProcessList.addObject(this);
NetConnection* pNC = NetConnection::getConnectionToServer();
AssertFatal(pNC != NULL, "Error, must have a connection to the server!");
pNC->addObject(this);
// Initialize the light structure and register as a dynamic light
if (mDataBlock->lightStartRadius != 0 || mDataBlock->lightEndRadius)
{
mLight.mType = LightInfo::Point;
mLight.mRadius = mDataBlock->lightStartRadius;
mLight.mColor = mDataBlock->lightStartColor;
Sim::getLightSet()->addObject(this);
}
return true;
}
void Explosion::onRemove()
{
for( int i=0; i<ExplosionData::EC_NUM_EMITTERS; i++ )
{
if( mEmitterList[i] )
{
mEmitterList[i]->deleteWhenEmpty();
mEmitterList[i] = NULL;
}
}
if (mSceneManager != NULL)
mSceneManager->removeObjectFromScene(this);
if (getContainer() != NULL)
getContainer()->removeObject(this);
Parent::onRemove();
}
bool Explosion::onNewDataBlock(GameBaseData* dptr)
{
mDataBlock = dynamic_cast<ExplosionData*>(dptr);
if (!mDataBlock || !Parent::onNewDataBlock(dptr))
return false;
scriptOnNewDataBlock();
return true;
}
//--------------------------------------------------------------------------
bool Explosion::prepRenderImage(SceneState* state, const U32 stateKey,
const U32 /*startZone*/, const bool /*modifyBaseState*/)
{
if (isLastState(state, stateKey))
return false;
setLastState(state, stateKey);
// This should be sufficient for most objects that don't manage zones, and
// don't need to return a specialized RenderImage...
if (state->isObjectRendered(this))
{
Point3F explosionPos;
mObjToWorld.getColumn(3,&explosionPos);
if (mExplosionInstance)
{
Point3F cameraOffset = explosionPos - state->getCameraPosition();
mFog = state->getHazeAndFog(cameraOffset.len(),cameraOffset.z);
}
else
{
mFog = 0.0;
}
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
image->isTranslucent = true;
image->sortType = SceneRenderImage::Point;
image->textureSortKey = (U32)(dsize_t)mDataBlock;
state->setImageRefPoint(this, image);
state->insertRenderImage(image);
}
return false;
}
void Explosion::setCurrentScale()
{
F32 t = F32(mCurrMS) / F32(mEndingMS);
for( U32 i = 1; i < ExplosionData::EC_NUM_TIME_KEYS; i++ )
{
if( mDataBlock->times[i] >= t )
{
F32 firstPart = t - mDataBlock->times[i-1];
F32 total = mDataBlock->times[i] -
mDataBlock->times[i-1];
firstPart /= total;
mObjScale = (mDataBlock->sizes[i-1] * (1.0 - firstPart)) +
(mDataBlock->sizes[i] * firstPart);
return;
}
}
}
void Explosion::prepModelView(SceneState* state)
{
MatrixF rotMatrix( true );
Point3F targetVector;
if (mDataBlock->faceViewer == true)
{
targetVector = getPosition() - state->getCameraPosition();
targetVector.normalize();
rotMatrix.set( EulerF( 0.0, mRandAngle, 0.0 ) );
}
else
{
targetVector = mInitialNormal;
}
// rotate explosion each time so it's a little different
MatrixF explOrient = MathUtils::createOrientFromDir( targetVector );
explOrient.mul( rotMatrix );
explOrient.setPosition( getPosition() );
dglMultMatrix( &explOrient );
setCurrentScale();
glScalef(mObjScale.x, mObjScale.y, mObjScale.z);
}
void Explosion::renderObject(SceneState* state, SceneRenderImage*)
{
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
RectI viewport;
glMatrixMode(GL_PROJECTION);
glPushMatrix();
dglGetViewport(&viewport);
// Uncomment this if this is a "simple" (non-zone managing) object
state->setupObjectProjection(this);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
prepModelView(state);
if( mExplosionInstance )
{
mExplosionInstance->animate();
if (mFade == 1.0)
{
mExplosionInstance->setupFog(mFog, state->getFogColor());
}
else
{
mExplosionInstance->setupFog(0.0, state->getFogColor());
mExplosionInstance->setAlphaAlways(mFade * (1.0 - mFog));
}
mExplosionInstance->render();
}
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glScalef( 1.0, 1.0, 1.0 );
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
dglSetViewport(viewport);
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
}
//--------------------------------------------------------------------------
void Explosion::registerLights(LightManager * lightManager, bool lightingScene)
{
if (lightingScene)
return;
// Update the light's info and add it to the scene, the light will
// only be visible for this current frame.
getRenderTransform().getColumn(3, &mLight.mPos);
F32 t = F32(mCurrMS) / F32(mEndingMS);
mLight.mRadius = mDataBlock->lightStartRadius +
(mDataBlock->lightEndRadius - mDataBlock->lightStartRadius) * t;
mLight.mColor = mDataBlock->lightStartColor +
(mDataBlock->lightEndColor - mDataBlock->lightStartColor) * t;
lightManager->sgRegisterGlobalLight(&mLight);
}
//--------------------------------------------------------------------------
void Explosion::processTick(const Move*)
{
mCurrMS += TickMs;
if( mCurrMS >= mEndingMS )
deleteObject();
if( (mCurrMS > mDelayMS) && !mActive )
explode();
}
void Explosion::advanceTime(F32 dt)
{
if (dt == 0.0)
return;
GameConnection* conn = GameConnection::getConnectionToServer();
if(!conn)
return;
updateEmitters( dt );
if( mExplosionInstance )
mExplosionInstance->advanceTime(dt, mExplosionThread);
}
//----------------------------------------------------------------------------
// Update emitters
//----------------------------------------------------------------------------
void Explosion::updateEmitters( F32 dt )
{
Point3F pos = getPosition();
for( int i=0; i<ExplosionData::EC_NUM_EMITTERS; i++ )
{
if( mEmitterList[i] )
{
mEmitterList[i]->emitParticles( pos, pos, mInitialNormal, Point3F( 0.0, 0.0, 0.0 ), (U32)(dt * 1000));
}
}
}
//----------------------------------------------------------------------------
// Launch Debris
//----------------------------------------------------------------------------
void Explosion::launchDebris( Point3F &axis )
{
GameConnection* conn = GameConnection::getConnectionToServer();
if(!conn)
return;
bool hasDebris = false;
for( int j=0; j<ExplosionData::EC_NUM_DEBRIS_TYPES; j++ )
{
if( mDataBlock->debrisList[j] )
{
hasDebris = true;
break;
}
}
if( !hasDebris )
{
return;
}
Point3F axisx;
if (mFabs(axis.z) < 0.999f)
mCross(axis, Point3F(0, 0, 1), &axisx);
else
mCross(axis, Point3F(0, 1, 0), &axisx);
axisx.normalize();
Point3F pos = getPosition() + Point3F( 0.0, 0.0, 0.5 );
U32 numDebris = mDataBlock->debrisNum + sgRandom.randI( -mDataBlock->debrisNumVariance, mDataBlock->debrisNumVariance );
for( int i=0; i<numDebris; i++ )
{
Point3F launchDir = MathUtils::randomDir( axis, mDataBlock->debrisThetaMin, mDataBlock->debrisThetaMax,
mDataBlock->debrisPhiMin, mDataBlock->debrisPhiMax );
F32 debrisVel = mDataBlock->debrisVelocity + mDataBlock->debrisVelocityVariance * sgRandom.randF( -1.0, 1.0 );
launchDir *= debrisVel;
Debris *debris = new Debris;
debris->setDataBlock( mDataBlock->debrisList[0] );
debris->setTransform( getTransform() );
debris->init( pos, launchDir );
if( !debris->registerObject() )
{
Con::warnf( ConsoleLogEntry::General, "Could not register debris for class: %s", mDataBlock->getName() );
delete debris;
debris = NULL;
}
}
}
//----------------------------------------------------------------------------
// Spawn sub explosions
//----------------------------------------------------------------------------
void Explosion::spawnSubExplosions()
{
GameConnection* conn = GameConnection::getConnectionToServer();
if(!conn)
return;
for( S32 i=0; i<ExplosionData::EC_MAX_SUB_EXPLOSIONS; i++ )
{
if( mDataBlock->explosionList[i] )
{
MatrixF trans = getTransform();
Explosion* pExplosion = new Explosion;
pExplosion->setDataBlock( mDataBlock->explosionList[i] );
pExplosion->setTransform( trans );
pExplosion->setInitialState( trans.getPosition(), mInitialNormal, 1);
if (!pExplosion->registerObject())
delete pExplosion;
}
}
}
//----------------------------------------------------------------------------
// Explode
//----------------------------------------------------------------------------
bool Explosion::explode()
{
mActive = true;
GameConnection* conn = GameConnection::getConnectionToServer();
if(!conn)
return false;
launchDebris( mInitialNormal );
spawnSubExplosions();
if (bool(mDataBlock->explosionShape) && mDataBlock->explosionAnimation != -1)
{
mExplosionInstance = new TSShapeInstance(mDataBlock->explosionShape, true);
mExplosionThread = mExplosionInstance->addThread();
mExplosionInstance->setSequence(mExplosionThread, mDataBlock->explosionAnimation, 0);
mExplosionInstance->setTimeScale(mExplosionThread, mDataBlock->playSpeed);
mCurrMS = 0;
mEndingMS = U32(mExplosionInstance->getScaledDuration(mExplosionThread) * 1000.0f);
mObjScale.convolve(mDataBlock->explosionScale);
mObjBox = mDataBlock->explosionShape->bounds;
resetWorldBox();
}
if (mDataBlock->soundProfile)
alxPlay(mDataBlock->soundProfile, &getTransform() );
if (mDataBlock->particleEmitter)
{
ParticleEmitter* emitter = new ParticleEmitter;
emitter->setDataBlock(mDataBlock->particleEmitter);
emitter->registerObject();
emitter->emitParticles(getPosition(), mInitialNormal, mDataBlock->particleRadius,
Point3F(0, 0, 0), U32(mDataBlock->particleDensity * mFade));
emitter->deleteWhenEmpty();
}
for( int i=0; i<ExplosionData::EC_NUM_EMITTERS; i++ )
{
if( mDataBlock->emitterList[i] != NULL )
{
ParticleEmitter * pEmitter = new ParticleEmitter;
pEmitter->setDataBlock( mDataBlock->emitterList[i] );
if( !pEmitter->registerObject() )
{
Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() );
SAFE_DELETE(pEmitter);
}
mEmitterList[i] = pEmitter;
}
}
return true;
}