955 lines
29 KiB
C++
Executable File
955 lines
29 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "game/debris.h"
|
|
#include "dgl/dgl.h"
|
|
#include "core/bitStream.h"
|
|
#include "math/mathUtils.h"
|
|
#include "console/consoleTypes.h"
|
|
#include "console/consoleObject.h"
|
|
#include "sim/netConnection.h"
|
|
#include "sceneGraph/sceneState.h"
|
|
#include "sceneGraph/sceneGraph.h"
|
|
#include "sceneGraph/detailManager.h"
|
|
#include "ts/tsShapeInstance.h"
|
|
#include "ts/tsPartInstance.h"
|
|
#include "game/fx/particleEngine.h"
|
|
#include "game/fx/explosion.h"
|
|
|
|
const U32 csmStaticCollisionMask = TerrainObjectType |
|
|
InteriorObjectType;
|
|
|
|
const U32 csmDynamicCollisionMask = StaticShapeObjectType;
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// Debris Data
|
|
//**************************************************************************
|
|
|
|
IMPLEMENT_CO_DATABLOCK_V1(DebrisData);
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Constructor
|
|
//--------------------------------------------------------------------------
|
|
DebrisData::DebrisData()
|
|
{
|
|
dMemset( emitterList, 0, sizeof( emitterList ) );
|
|
dMemset( emitterIDList, 0, sizeof( emitterIDList ) );
|
|
|
|
explosion = NULL;
|
|
explosionId = 0;
|
|
|
|
velocity = 0.0;
|
|
velocityVariance = 0.0;
|
|
elasticity = 0.3;
|
|
friction = 0.2;
|
|
numBounces = 0;
|
|
bounceVariance = 0;
|
|
minSpinSpeed = maxSpinSpeed = 0.0;
|
|
render2D = false;
|
|
staticOnMaxBounce = false;
|
|
explodeOnMaxBounce = false;
|
|
snapOnMaxBounce = false;
|
|
lifetime = 3.0;
|
|
lifetimeVariance = 0.0;
|
|
minSpinSpeed = 0.0;
|
|
maxSpinSpeed = 0.0;
|
|
textureName = NULL;
|
|
mTypeMask |= DebrisObjectType;
|
|
shapeName = NULL;
|
|
fade = true;
|
|
useRadiusMass = false;
|
|
baseRadius = 1.0;
|
|
gravModifier = 1.0;
|
|
terminalVelocity = 0.0;
|
|
ignoreWater = true;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Initialize - Check data
|
|
//--------------------------------------------------------------------------
|
|
bool DebrisData::onAdd()
|
|
{
|
|
if(!Parent::onAdd())
|
|
return false;
|
|
|
|
for( int i=0; i<DDC_NUM_EMITTERS; i++ )
|
|
{
|
|
if( !emitterList[i] && emitterIDList[i] != 0 )
|
|
{
|
|
if( Sim::findObject( emitterIDList[i], emitterList[i] ) == false)
|
|
{
|
|
Con::errorf( ConsoleLogEntry::General, "DebrisData::onAdd: Invalid packet, bad datablockId(emitter): 0x%x", emitterIDList[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!explosion && explosionId != 0)
|
|
{
|
|
if (!Sim::findObject( SimObjectId( explosionId ), explosion ))
|
|
Con::errorf( ConsoleLogEntry::General, "DebrisData::onAdd: Invalid packet, bad datablockId(particle emitter): 0x%x", explosionId);
|
|
}
|
|
|
|
if( textureName )
|
|
{
|
|
texture = TextureHandle( textureName, MeshTexture );
|
|
}
|
|
else
|
|
{
|
|
texture = TextureHandle(); // set default NULL tex
|
|
}
|
|
|
|
|
|
// validate data
|
|
|
|
if( velocityVariance > velocity )
|
|
{
|
|
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: velocityVariance invalid", getName());
|
|
velocityVariance = velocity;
|
|
}
|
|
if( friction < -10.0 || friction > 10.0 )
|
|
{
|
|
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: friction invalid", getName());
|
|
friction = 0.2;
|
|
}
|
|
if( elasticity < -10.0 || elasticity > 10.0 )
|
|
{
|
|
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: elasticity invalid", getName());
|
|
elasticity = 0.2;
|
|
}
|
|
if( lifetime < 0.0 || lifetime > 1000.0 )
|
|
{
|
|
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetime invalid", getName());
|
|
lifetime = 3.0;
|
|
}
|
|
if( lifetimeVariance < 0.0 || lifetimeVariance > lifetime )
|
|
{
|
|
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetimeVariance invalid", getName());
|
|
lifetimeVariance = 0.0;
|
|
}
|
|
if( numBounces < 0 || numBounces > 10000 )
|
|
{
|
|
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: numBounces invalid", getName());
|
|
numBounces = 3;
|
|
}
|
|
if( bounceVariance < 0 || bounceVariance > numBounces )
|
|
{
|
|
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: bounceVariance invalid", getName());
|
|
bounceVariance = 0;
|
|
}
|
|
if( minSpinSpeed < -10000.0 || minSpinSpeed > 10000.0 || minSpinSpeed > maxSpinSpeed )
|
|
{
|
|
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: minSpinSpeed invalid", getName());
|
|
minSpinSpeed = maxSpinSpeed - 1.0;
|
|
}
|
|
if( maxSpinSpeed < -10000.0 || maxSpinSpeed > 10000.0 )
|
|
{
|
|
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: maxSpinSpeed invalid", getName());
|
|
maxSpinSpeed = 0.0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Preload
|
|
//--------------------------------------------------------------------------
|
|
bool DebrisData::preload(bool server, char errorBuffer[256])
|
|
{
|
|
if (Parent::preload(server, errorBuffer) == false)
|
|
return false;
|
|
|
|
if( server )
|
|
return true;
|
|
|
|
if( shapeName && shapeName[0] != '\0' && !bool(shape) )
|
|
{
|
|
shape = ResourceManager->load(shapeName);
|
|
if( bool(shape) == false )
|
|
{
|
|
dSprintf(errorBuffer, sizeof(errorBuffer), "DebrisData::load: Couldn't load shape \"%s\"", shapeName);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
TSShapeInstance* pDummy = new TSShapeInstance(shape, !server);
|
|
delete pDummy;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Initialize console fields (static)
|
|
//--------------------------------------------------------------------------
|
|
IMPLEMENT_CONSOLETYPE(DebrisData)
|
|
IMPLEMENT_SETDATATYPE(DebrisData)
|
|
IMPLEMENT_GETDATATYPE(DebrisData)
|
|
|
|
void DebrisData::initPersistFields()
|
|
{
|
|
Parent::initPersistFields();
|
|
|
|
addGroup("Display");
|
|
addField("texture", TypeFilename, Offset(textureName, DebrisData));
|
|
addField("shapeFile", TypeFilename, Offset(shapeName, DebrisData));
|
|
addField("render2D", TypeBool, Offset(render2D, DebrisData));
|
|
endGroup("Display");
|
|
|
|
addGroup("Datablocks");
|
|
addField("emitters", TypeParticleEmitterDataPtr, Offset(emitterList, DebrisData), DDC_NUM_EMITTERS);
|
|
addField("explosion", TypeExplosionDataPtr, Offset(explosion, DebrisData));
|
|
endGroup("Datablocks");
|
|
|
|
addGroup("Physical Properties");
|
|
addField("elasticity", TypeF32, Offset(elasticity, DebrisData));
|
|
addField("friction", TypeF32, Offset(friction, DebrisData));
|
|
addField("numBounces", TypeS32, Offset(numBounces, DebrisData));
|
|
addField("bounceVariance", TypeS32, Offset(bounceVariance, DebrisData));
|
|
addField("minSpinSpeed", TypeF32, Offset(minSpinSpeed, DebrisData));
|
|
addField("maxSpinSpeed", TypeF32, Offset(maxSpinSpeed, DebrisData));
|
|
addField("gravModifier", TypeF32, Offset(gravModifier, DebrisData));
|
|
addField("terminalVelocity", TypeF32, Offset(terminalVelocity, DebrisData));
|
|
addField("velocity", TypeF32, Offset(velocity, DebrisData));
|
|
addField("velocityVariance", TypeF32, Offset(velocityVariance, DebrisData));
|
|
addField("lifetime", TypeF32, Offset(lifetime, DebrisData));
|
|
addField("lifetimeVariance", TypeF32, Offset(lifetimeVariance, DebrisData));
|
|
addField("useRadiusMass", TypeBool, Offset(useRadiusMass, DebrisData));
|
|
addField("baseRadius", TypeF32, Offset(baseRadius, DebrisData));
|
|
endGroup("Physical Properties");
|
|
|
|
addGroup("Behavior");
|
|
addField("explodeOnMaxBounce", TypeBool, Offset(explodeOnMaxBounce, DebrisData));
|
|
addField("staticOnMaxBounce", TypeBool, Offset(staticOnMaxBounce, DebrisData));
|
|
addField("snapOnMaxBounce", TypeBool, Offset(snapOnMaxBounce, DebrisData));
|
|
addField("fade", TypeBool, Offset(fade, DebrisData));
|
|
addField("ignoreWater", TypeBool, Offset(ignoreWater, DebrisData));
|
|
endGroup("Behavior");
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Pack data
|
|
//--------------------------------------------------------------------------
|
|
void DebrisData::packData(BitStream* stream)
|
|
{
|
|
Parent::packData(stream);
|
|
|
|
stream->write(elasticity);
|
|
stream->write(friction);
|
|
stream->write(numBounces);
|
|
stream->write(bounceVariance);
|
|
stream->write(minSpinSpeed);
|
|
stream->write(maxSpinSpeed);
|
|
stream->write(render2D);
|
|
stream->write(explodeOnMaxBounce);
|
|
stream->write(staticOnMaxBounce);
|
|
stream->write(snapOnMaxBounce);
|
|
stream->write(lifetime);
|
|
stream->write(lifetimeVariance);
|
|
stream->write(minSpinSpeed);
|
|
stream->write(maxSpinSpeed);
|
|
stream->write(velocity);
|
|
stream->write(velocityVariance);
|
|
stream->write(fade);
|
|
stream->write(useRadiusMass);
|
|
stream->write(baseRadius);
|
|
stream->write(gravModifier);
|
|
stream->write(terminalVelocity);
|
|
stream->write(ignoreWater);
|
|
|
|
stream->writeString( textureName );
|
|
stream->writeString( shapeName );
|
|
|
|
for( int i=0; i<DDC_NUM_EMITTERS; i++ )
|
|
{
|
|
if( stream->writeFlag( emitterList[i] != NULL ) )
|
|
{
|
|
stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
|
|
}
|
|
}
|
|
|
|
if( stream->writeFlag( explosion ) )
|
|
{
|
|
stream->writeRangedU32(packed? SimObjectId(explosion):
|
|
explosion->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Unpack data
|
|
//--------------------------------------------------------------------------
|
|
void DebrisData::unpackData(BitStream* stream)
|
|
{
|
|
Parent::unpackData(stream);
|
|
|
|
stream->read(&elasticity);
|
|
stream->read(&friction);
|
|
stream->read(&numBounces);
|
|
stream->read(&bounceVariance);
|
|
stream->read(&minSpinSpeed);
|
|
stream->read(&maxSpinSpeed);
|
|
stream->read(&render2D);
|
|
stream->read(&explodeOnMaxBounce);
|
|
stream->read(&staticOnMaxBounce);
|
|
stream->read(&snapOnMaxBounce);
|
|
stream->read(&lifetime);
|
|
stream->read(&lifetimeVariance);
|
|
stream->read(&minSpinSpeed);
|
|
stream->read(&maxSpinSpeed);
|
|
stream->read(&velocity);
|
|
stream->read(&velocityVariance);
|
|
stream->read(&fade);
|
|
stream->read(&useRadiusMass);
|
|
stream->read(&baseRadius);
|
|
stream->read(&gravModifier);
|
|
stream->read(&terminalVelocity);
|
|
stream->read(&ignoreWater);
|
|
|
|
textureName = stream->readSTString();
|
|
shapeName = stream->readSTString();
|
|
|
|
for( int i=0; i<DDC_NUM_EMITTERS; i++ )
|
|
{
|
|
if( stream->readFlag() )
|
|
{
|
|
emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
|
|
}
|
|
}
|
|
|
|
if(stream->readFlag())
|
|
{
|
|
explosionId = (S32)stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
|
|
}
|
|
else
|
|
{
|
|
explosionId = 0;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//**************************************************************************
|
|
// Debris
|
|
//**************************************************************************
|
|
|
|
IMPLEMENT_CO_NETOBJECT_V1(Debris);
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Initialize debris piece
|
|
//----------------------------------------------------------------------------
|
|
ConsoleMethod( Debris, init, bool, 4, 4, "(Point3F position, Point3F velocity)"
|
|
"Set this piece of debris at the given position with the given velocity.")
|
|
{
|
|
Point3F pos;
|
|
dSscanf( argv[2], "%g %g %g", &pos.x, &pos.y, &pos.z );
|
|
|
|
Point3F vel;
|
|
dSscanf( argv[3], "%g %g %g", &vel.x, &vel.y, &vel.z );
|
|
|
|
object->init( pos, vel );
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Constructor
|
|
//----------------------------------------------------------------------------
|
|
Debris::Debris()
|
|
{
|
|
mTypeMask |= DebrisObjectType;
|
|
|
|
mVelocity = Point3F( 0.0, 0.0, 4.0 );
|
|
mLifetime = gRandGen.randF( 1.0, 10.0 );
|
|
mLastPos = getPosition();
|
|
mNumBounces = gRandGen.randI( 0, 1 );
|
|
mSize = 2.0;
|
|
mElapsedTime = 0.0;
|
|
mShape = NULL;
|
|
mPart = NULL;
|
|
mXRotSpeed = 0.0;
|
|
mZRotSpeed = 0.0;
|
|
mInitialTrans.identity();
|
|
mRadius = 0.2;
|
|
mStatic = false;
|
|
|
|
dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Destructor
|
|
//----------------------------------------------------------------------------
|
|
Debris::~Debris()
|
|
{
|
|
if( mShape )
|
|
{
|
|
delete mShape;
|
|
mShape = NULL;
|
|
}
|
|
|
|
if( mPart )
|
|
{
|
|
delete mPart;
|
|
mPart = NULL;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Init class (static)
|
|
//----------------------------------------------------------------------------
|
|
void Debris::initPersistFields()
|
|
{
|
|
addGroup("Misc");
|
|
addField("lifetime", TypeF32, Offset(mLifetime, Debris));
|
|
endGroup("Misc");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Init
|
|
//----------------------------------------------------------------------------
|
|
void Debris::init( const Point3F &position, const Point3F &velocity )
|
|
{
|
|
setPosition( position );
|
|
setVelocity( velocity );
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// On new data block
|
|
//----------------------------------------------------------------------------
|
|
bool Debris::onNewDataBlock( GameBaseData* dptr )
|
|
{
|
|
mDataBlock = dynamic_cast< DebrisData* >( dptr );
|
|
if( !mDataBlock || !Parent::onNewDataBlock( dptr ) )
|
|
return false;
|
|
|
|
scriptOnNewDataBlock();
|
|
return true;
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// On Add
|
|
//----------------------------------------------------------------------------
|
|
bool Debris::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
|
|
NetConnection* conn = NetConnection::getConnectionToServer();
|
|
if(!conn || !Parent::onAdd())
|
|
return false;
|
|
|
|
F32 lifeVar = (mDataBlock->lifetimeVariance * 2.0f * gRandGen.randF(-1.0,1.0)) - mDataBlock->lifetimeVariance;
|
|
mLifetime = mDataBlock->lifetime + lifeVar;
|
|
|
|
// create emitters
|
|
for( int i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
|
|
{
|
|
if( mDataBlock->emitterList[i] != NULL )
|
|
{
|
|
ParticleEmitter * pEmitter = new ParticleEmitter;
|
|
pEmitter->onNewDataBlock( mDataBlock->emitterList[i] );
|
|
if( !pEmitter->registerObject() )
|
|
{
|
|
Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() );
|
|
delete pEmitter;
|
|
pEmitter = NULL;
|
|
}
|
|
mEmitterList[i] = pEmitter;
|
|
}
|
|
}
|
|
|
|
// set particle sizes based on debris size
|
|
F32 sizeList[ParticleEngine::PC_SIZE_KEYS];
|
|
|
|
if( mEmitterList[0] )
|
|
{
|
|
sizeList[0] = mSize * 0.5;
|
|
sizeList[1] = mSize;
|
|
sizeList[2] = mSize * 1.5;
|
|
|
|
mEmitterList[0]->setSizes( sizeList );
|
|
}
|
|
|
|
if( mEmitterList[1] )
|
|
{
|
|
sizeList[0] = 0.0;
|
|
sizeList[1] = mSize * 0.5;
|
|
sizeList[2] = mSize;
|
|
|
|
mEmitterList[1]->setSizes( sizeList );
|
|
}
|
|
|
|
S32 bounceVar = gRandGen.randI( -mDataBlock->bounceVariance, mDataBlock->bounceVariance );
|
|
mNumBounces = mDataBlock->numBounces + bounceVar;
|
|
|
|
F32 xRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed );
|
|
F32 zRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed );
|
|
zRotSpeed *= gRandGen.randF( 0.1, 0.5 );
|
|
|
|
mRotAngles.set( xRotSpeed, 0.0, zRotSpeed );
|
|
|
|
mElasticity = mDataBlock->elasticity;
|
|
mFriction = mDataBlock->friction;
|
|
|
|
// Setup our bounding box
|
|
if( mDataBlock->shape )
|
|
{
|
|
mObjBox = mDataBlock->shape->bounds;
|
|
}
|
|
else
|
|
{
|
|
mObjBox = Box3F(Point3F(-1, -1, -1), Point3F(1, 1, 1));
|
|
}
|
|
|
|
if( mDataBlock->shape )
|
|
{
|
|
mShape = new TSShapeInstance( mDataBlock->shape, true);
|
|
}
|
|
|
|
if( mPart )
|
|
{
|
|
// use half radius becuase we want debris to stick in ground
|
|
mRadius = mPart->getRadius() * 0.5;
|
|
mObjBox = mPart->getBounds();
|
|
}
|
|
|
|
resetWorldBox();
|
|
|
|
mInitialTrans = getTransform();
|
|
|
|
if( mDataBlock->velocity != 0.0 )
|
|
{
|
|
F32 velocity = mDataBlock->velocity + gRandGen.randF( -mDataBlock->velocityVariance, mDataBlock->velocityVariance );
|
|
|
|
mVelocity.normalizeSafe();
|
|
mVelocity *= velocity;
|
|
}
|
|
|
|
// mass calculations
|
|
if( mDataBlock->useRadiusMass )
|
|
{
|
|
if( mRadius < mDataBlock->baseRadius )
|
|
mRadius = mDataBlock->baseRadius;
|
|
|
|
// linear falloff
|
|
F32 multFactor = mDataBlock->baseRadius / mRadius;
|
|
|
|
mElasticity *= multFactor;
|
|
mFriction *= multFactor;
|
|
mRotAngles *= multFactor;
|
|
}
|
|
|
|
// tell engine the debris exists
|
|
gClientContainer.addObject(this);
|
|
gClientSceneGraph->addObjectToScene(this);
|
|
|
|
removeFromProcessList();
|
|
gClientProcessList.addObject(this);
|
|
|
|
conn->addObject(this);
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// On Remove
|
|
//----------------------------------------------------------------------------
|
|
void Debris::onRemove()
|
|
{
|
|
for( int i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
|
|
{
|
|
if( mEmitterList[i] )
|
|
{
|
|
mEmitterList[i]->deleteWhenEmpty();
|
|
mEmitterList[i] = NULL;
|
|
}
|
|
}
|
|
|
|
if( mPart )
|
|
{
|
|
TSShapeInstance *ss = mPart->getSourceShapeInstance();
|
|
if( ss )
|
|
{
|
|
ss->decDebrisRefCount();
|
|
if( ss->getDebrisRefCount() == 0 )
|
|
{
|
|
delete ss;
|
|
}
|
|
}
|
|
}
|
|
|
|
mSceneManager->removeObjectFromScene(this);
|
|
getContainer()->removeObject(this);
|
|
|
|
Parent::onRemove();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Process tick
|
|
//----------------------------------------------------------------------------
|
|
void Debris::processTick(const Move*)
|
|
{
|
|
// Update real position info.
|
|
setTransform(getRenderTransform());
|
|
|
|
if (mLifetime <= 0.0)
|
|
deleteObject();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Advance Time
|
|
//----------------------------------------------------------------------------
|
|
void Debris::advanceTime( F32 dt )
|
|
{
|
|
mElapsedTime += dt;
|
|
|
|
mLifetime -= dt;
|
|
if( mLifetime <= 0.0 )
|
|
{
|
|
mLifetime = 0.0;
|
|
return;
|
|
}
|
|
|
|
mLastPos = getRenderPosition();
|
|
|
|
if( !mStatic )
|
|
{
|
|
rotate( dt );
|
|
|
|
Point3F nextPos = getRenderPosition();
|
|
computeNewState( nextPos, mVelocity, dt );
|
|
|
|
if( bounce( nextPos, dt ) )
|
|
{
|
|
--mNumBounces;
|
|
if( mNumBounces <= 0 )
|
|
{
|
|
if( mDataBlock->explodeOnMaxBounce )
|
|
{
|
|
explode();
|
|
mLifetime = 0.0;
|
|
}
|
|
|
|
if( mDataBlock->snapOnMaxBounce )
|
|
{
|
|
// orient debris so it's flat
|
|
MatrixF stat = getRenderTransform();
|
|
|
|
Point3F dir;
|
|
stat.getColumn( 1, &dir );
|
|
dir.z = 0.0;
|
|
|
|
MatrixF newTrans = MathUtils::createOrientFromDir( dir );
|
|
|
|
// hack for shell casings to get them above ground. Need something better - bramage
|
|
newTrans.setPosition( getPosition() + Point3F( 0.0, 0.0, 0.10 ) );
|
|
|
|
setRenderTransform( newTrans );
|
|
}
|
|
if( mDataBlock->staticOnMaxBounce )
|
|
{
|
|
mStatic = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MatrixF tmp = getRenderTransform();
|
|
tmp.setPosition( nextPos );
|
|
setRenderTransform(tmp);
|
|
}
|
|
}
|
|
|
|
updateEmitters( (Point3F&)*getRenderPosition(), mVelocity, (U32)(dt * 1000.0));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Rotate debris
|
|
//----------------------------------------------------------------------------
|
|
void Debris::rotate( F32 dt )
|
|
{
|
|
MatrixF curTrans = getRenderTransform();
|
|
curTrans.setPosition( Point3F(0.0, 0.0, 0.0) );
|
|
|
|
Point3F curAngles = mRotAngles * dt * M_PI/180.0;
|
|
MatrixF rotMatrix( EulerF( curAngles.x, curAngles.y, curAngles.z ) );
|
|
|
|
curTrans.mul( rotMatrix );
|
|
curTrans.setPosition( getRenderPosition() );
|
|
setRenderTransform( curTrans );
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Bounce the debris - returns true if debris bounces
|
|
//----------------------------------------------------------------------------
|
|
bool Debris::bounce( const Point3F &nextPos, F32 dt )
|
|
{
|
|
Point3F curPos = getRenderPosition();
|
|
|
|
Point3F dir = nextPos - curPos;
|
|
if( dir.magnitudeSafe() == 0.0 )
|
|
return false;
|
|
|
|
dir.normalizeSafe();
|
|
|
|
Point3F extent = nextPos + dir * mRadius;
|
|
F32 totalDist = Point3F( extent - curPos ).magnitudeSafe();
|
|
F32 moveDist = Point3F( nextPos - curPos ).magnitudeSafe();
|
|
F32 movePercent = (moveDist / totalDist);
|
|
|
|
RayInfo rayInfo;
|
|
U32 collisionMask = csmStaticCollisionMask;
|
|
if( !mDataBlock->ignoreWater )
|
|
collisionMask |= WaterObjectType;
|
|
|
|
if( getContainer()->castRay( curPos, extent, collisionMask, &rayInfo ) )
|
|
{
|
|
|
|
Point3F reflection = mVelocity - rayInfo.normal * (mDot( mVelocity, rayInfo.normal ) * 2.0);
|
|
mVelocity = reflection;
|
|
|
|
Point3F tangent = reflection - rayInfo.normal * mDot( reflection, rayInfo.normal );
|
|
mVelocity -= tangent * mFriction;
|
|
|
|
Point3F velDir = mVelocity;
|
|
velDir.normalizeSafe();
|
|
|
|
mVelocity *= mElasticity;
|
|
|
|
Point3F bouncePos = curPos + dir * rayInfo.t * movePercent;
|
|
bouncePos += mVelocity * dt;
|
|
|
|
MatrixF tmp = getRenderTransform();
|
|
tmp.setPosition( bouncePos );
|
|
setRenderTransform(tmp);
|
|
|
|
mRotAngles *= mElasticity;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Explode
|
|
//----------------------------------------------------------------------------
|
|
void Debris::explode()
|
|
{
|
|
if( !mDataBlock->explosion )
|
|
return;
|
|
|
|
const Point3F explosionPos = getRenderPosition();
|
|
|
|
Explosion* pExplosion = new Explosion;
|
|
pExplosion->onNewDataBlock(mDataBlock->explosion);
|
|
|
|
MatrixF trans( true );
|
|
trans.setPosition( explosionPos );
|
|
|
|
pExplosion->setTransform( trans );
|
|
pExplosion->setInitialState( explosionPos, VectorF(0,0,1), 1);
|
|
if (!pExplosion->registerObject())
|
|
delete pExplosion;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Compute state of debris as if it hasn't collided with anything
|
|
//----------------------------------------------------------------------------
|
|
void Debris::computeNewState( Point3F &newPos, Point3F &newVel, F32 dt )
|
|
{
|
|
// apply gravity
|
|
Point3F force = Point3F(0, 0, -9.81 * mDataBlock->gravModifier );
|
|
|
|
if( mDataBlock->terminalVelocity > 0.0001 && newVel.magnitudeSafe() > mDataBlock->terminalVelocity )
|
|
{
|
|
newVel.normalizeSafe();
|
|
newVel *= mDataBlock->terminalVelocity;
|
|
}
|
|
else
|
|
{
|
|
newVel += force * dt;
|
|
}
|
|
|
|
newPos += newVel * dt;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Update emitters
|
|
//----------------------------------------------------------------------------
|
|
void Debris::updateEmitters( Point3F &pos, Point3F &vel, U32 ms )
|
|
{
|
|
Point3F axis = -vel;
|
|
Point3F lastPos = mLastPos;
|
|
|
|
if( axis.magnitudeSafe() == 0.0 )
|
|
axis = Point3F( 0.0, 0.0, 1.0 );
|
|
|
|
axis.normalizeSafe();
|
|
|
|
for( int i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
|
|
{
|
|
if( mEmitterList[i] )
|
|
{
|
|
mEmitterList[i]->emitParticles( lastPos, pos, axis, vel, ms );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Render debris
|
|
//----------------------------------------------------------------------------
|
|
bool Debris::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) && (mPart || mShape) )
|
|
{
|
|
Point3F cameraOffset;
|
|
mObjToWorld.getColumn(3,&cameraOffset);
|
|
cameraOffset -= state->getCameraPosition();
|
|
F32 dist = cameraOffset.len();
|
|
F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z));
|
|
|
|
if( mShape )
|
|
{
|
|
DetailManager::selectPotentialDetails(mShape,dist,invScale);
|
|
if( mShape->getCurrentDetail() < 0 )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if( mPart )
|
|
{
|
|
DetailManager::selectPotentialDetails(mPart,dist,invScale);
|
|
}
|
|
|
|
SceneRenderImage* image = new SceneRenderImage;
|
|
image->obj = this;
|
|
image->isTranslucent = true;
|
|
image->sortType = SceneRenderImage::Point;
|
|
state->setImageRefPoint(this, image);
|
|
|
|
state->insertRenderImage(image);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Render Object
|
|
//----------------------------------------------------------------------------
|
|
void Debris::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);
|
|
|
|
F32 alpha = 1.0;
|
|
if( mDataBlock->fade && mLifetime < 1.0 )
|
|
alpha = mLifetime;
|
|
|
|
if( (mShape && DetailManager::selectCurrentDetail(mShape)) ||
|
|
(mPart && DetailManager::selectCurrentDetail(mPart)) )
|
|
{
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
dglMultMatrix(&mObjToWorld);
|
|
glScalef(mObjScale.x, mObjScale.y, mObjScale.z);
|
|
|
|
installLights();
|
|
|
|
Point3F cameraOffset;
|
|
mObjToWorld.getColumn(3,&cameraOffset);
|
|
cameraOffset -= state->getCameraPosition();
|
|
F32 fogAmount = state->getHazeAndFog(cameraOffset.len(),cameraOffset.z);
|
|
|
|
if( mShape )
|
|
{
|
|
TSMesh::setOverrideFade( alpha );
|
|
mShape->setupFog(fogAmount, state->getFogColor());
|
|
mShape->render();
|
|
TSMesh::setOverrideFade( 1.0 );
|
|
}
|
|
else
|
|
{
|
|
if (mPart->getCurrentObjectDetail() != -1)
|
|
{
|
|
TSShapeInstance *parent = mPart->getSourceShapeInstance();
|
|
|
|
parent->setupFog(fogAmount, state->getFogColor());
|
|
TSMesh::setOverrideFade( alpha );
|
|
mPart->render();
|
|
TSMesh::setOverrideFade( 1.0 );
|
|
}
|
|
}
|
|
|
|
uninstallLights();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPopMatrix();
|
|
}
|
|
|
|
render2D();
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
dglSetViewport(viewport);
|
|
|
|
dglSetCanonicalState();
|
|
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Render 2D debris
|
|
//----------------------------------------------------------------------------
|
|
void Debris::render2D()
|
|
{
|
|
if( !mDataBlock->render2D )
|
|
return;
|
|
|
|
glBindTexture( GL_TEXTURE_2D, mDataBlock->texture.getGLName() );
|
|
glColor4f( 1.0, 1.0, 1.0, 1.0 );
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
dglDrawBillboard( getPosition(), 0.1, 0.0 );
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
glDisable(GL_BLEND);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Set size
|
|
//----------------------------------------------------------------------------
|
|
void Debris::setSize( F32 size )
|
|
{
|
|
mSize = size;
|
|
}
|