//----------------------------------------------------------------------------- // 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(¢er); 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= 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; iwriteString(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; jwriteFlag( debrisList[j] ) ) { stream->writeRangedU32( debrisList[j]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); } } S32 i; for( i=0; iwriteFlag( emitterList[i] != NULL ) ) { stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); } } for( i=0; iwriteFlag( 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; iwriteFloat( times[i], 8 ); for( i=0; iwriteRangedU32((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; jreadFlag() ) { debrisIDList[j] = (S32) stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); } } U32 i; for( i=0; ireadFlag() ) { emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); } } for( S32 k=0; kreadFlag() ) { explosionIDList[k] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); } } U32 count = stream->readRangedU32(0, EC_NUM_TIME_KEYS); for( i=0; ireadFloat(8); for( i=0; ireadRangedU32(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 && isClientObject() ) { // 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(); if(!isServerObject()) { 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; ideleteWhenEmpty(); 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(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->addLight(&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; if(!isServerObject()) return; updateEmitters( dt ); if( mExplosionInstance ) mExplosionInstance->advanceTime(dt, mExplosionThread); } //---------------------------------------------------------------------------- // Update emitters //---------------------------------------------------------------------------- void Explosion::updateEmitters( F32 dt ) { Point3F pos = getPosition(); for( int i=0; iemitParticles( pos, pos, mInitialNormal, Point3F( 0.0, 0.0, 0.0 ), (U32)(dt * 1000)); } } } //---------------------------------------------------------------------------- // Launch Debris //---------------------------------------------------------------------------- void Explosion::launchDebris( Point3F &axis ) { if(!isServerObject()) return; bool hasDebris = false; for( int j=0; jdebrisList[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; idebrisThetaMin, 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() { if(!isServerObject()) return; for( S32 i=0; iexplosionList[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; if(!isServerObject()) return true; 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; iemitterList[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; }