Torque3D/Engine/source/T3D/debris.cpp
2013-04-05 12:39:26 -04:00

942 lines
28 KiB
C++

//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "T3D/debris.h"
#include "core/stream/bitStream.h"
#include "math/mathUtils.h"
#include "console/consoleTypes.h"
#include "console/consoleObject.h"
#include "sim/netConnection.h"
#include "scene/sceneRenderState.h"
#include "scene/sceneManager.h"
#include "ts/tsShapeInstance.h"
#include "ts/tsPartInstance.h"
#include "T3D/fx/particleEmitter.h"
#include "T3D/fx/explosion.h"
#include "T3D/gameBase/gameProcess.h"
#include "core/resourceManager.h"
#include "gfx/gfxTransformSaver.h"
#include "console/engineAPI.h"
#include "lighting/lightQuery.h"
const U32 csmStaticCollisionMask = TerrainObjectType;
const U32 csmDynamicCollisionMask = StaticShapeObjectType;
IMPLEMENT_CO_DATABLOCK_V1(DebrisData);
ConsoleDocClass( DebrisData,
"@brief Stores properties for an individual debris type.\n\n"
"DebrisData defines the base properties for a Debris object. Typically you'll want a Debris object to consist of "
"a shape and possibly up to two particle emitters. The DebrisData datablock provides the definition for these items, "
"along with physical properties and how a Debris object will react to other game objects, such as water and terrain.\n"
"@tsexample\n"
"datablock DebrisData(GrenadeDebris)\n"
"{\n"
" shapeFile = \"art/shapes/weapons/ramrifle/debris.dts\";\n"
" emitters[0] = GrenadeDebrisFireEmitter;\n"
" elasticity = 0.4;\n"
" friction = 0.25;\n"
" numBounces = 3;\n"
" bounceVariance = 1;\n"
" explodeOnMaxBounce = false;\n"
" staticOnMaxBounce = false;\n"
" snapOnMaxBounce = false;\n"
" minSpinSpeed = 200;\n"
" maxSpinSpeed = 600;\n"
" lifetime = 4;\n"
" lifetimeVariance = 1.5;\n"
" velocity = 15;\n"
" velocityVariance = 5;\n"
" fade = true;\n"
" useRadiusMass = true;\n"
" baseRadius = 0.3;\n"
" gravModifier = 1.0;\n"
" terminalVelocity = 20;\n"
" ignoreWater = false;\n"
"};\n"
"@endtsexample\n\n"
"@see Debris\n\n"
"@ingroup FX\n"
);
DebrisData::DebrisData()
{
dMemset( emitterList, 0, sizeof( emitterList ) );
dMemset( emitterIDList, 0, sizeof( emitterIDList ) );
explosion = NULL;
explosionId = 0;
velocity = 0.0f;
velocityVariance = 0.0;
elasticity = 0.3f;
friction = 0.2f;
numBounces = 0;
bounceVariance = 0;
minSpinSpeed = maxSpinSpeed = 0.0;
staticOnMaxBounce = false;
explodeOnMaxBounce = false;
snapOnMaxBounce = false;
lifetime = 3.0f;
lifetimeVariance = 0.0f;
minSpinSpeed = 0.0f;
maxSpinSpeed = 0.0f;
textureName = NULL;
shapeName = NULL;
fade = true;
useRadiusMass = false;
baseRadius = 1.0f;
gravModifier = 1.0f;
terminalVelocity = 0.0f;
ignoreWater = true;
}
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);
}
// validate data
if( velocityVariance > velocity )
{
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: velocityVariance invalid", getName());
velocityVariance = velocity;
}
if( friction < -10.0f || friction > 10.0f )
{
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: friction invalid", getName());
friction = 0.2f;
}
if( elasticity < -10.0f || elasticity > 10.0f )
{
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: elasticity invalid", getName());
elasticity = 0.2f;
}
if( lifetime < 0.0f || lifetime > 1000.0f )
{
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetime invalid", getName());
lifetime = 3.0f;
}
if( lifetimeVariance < 0.0f || lifetimeVariance > lifetime )
{
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetimeVariance invalid", getName());
lifetimeVariance = 0.0f;
}
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.0f || minSpinSpeed > 10000.0f || minSpinSpeed > maxSpinSpeed )
{
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: minSpinSpeed invalid", getName());
minSpinSpeed = maxSpinSpeed - 1.0f;
}
if( maxSpinSpeed < -10000.0f || maxSpinSpeed > 10000.0f )
{
Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: maxSpinSpeed invalid", getName());
maxSpinSpeed = 0.0f;
}
return true;
}
bool DebrisData::preload(bool server, String &errorStr)
{
if (Parent::preload(server, errorStr) == false)
return false;
if( server ) return true;
if( shapeName && shapeName[0] != '\0' && !bool(shape) )
{
shape = ResourceManager::get().load(shapeName);
if( bool(shape) == false )
{
errorStr = String::ToString("DebrisData::load: Couldn't load shape \"%s\"", shapeName);
return false;
}
else
{
TSShapeInstance* pDummy = new TSShapeInstance(shape, !server);
delete pDummy;
}
}
return true;
}
void DebrisData::initPersistFields()
{
addGroup("Display");
addField("texture", TypeString, Offset(textureName, DebrisData),
"@brief Texture imagemap to use for this debris object.\n\nNot used any more.\n");
addField("shapeFile", TypeShapeFilename, Offset(shapeName, DebrisData),
"@brief Object model to use for this debris object.\n\nThis shape is optional. You could have Debris made up of only particles.\n");
endGroup("Display");
addGroup("Datablocks");
addField("emitters", TYPEID< ParticleEmitterData >(), Offset(emitterList, DebrisData), DDC_NUM_EMITTERS,
"@brief List of particle emitters to spawn along with this debris object.\n\nThese are optional. You could have Debris made up of only a shape.\n");
addField("explosion", TYPEID< ExplosionData >(), Offset(explosion, DebrisData),
"@brief ExplosionData to spawn along with this debris object.\n\nThis is optional as not all Debris explode.\n");
endGroup("Datablocks");
addGroup("Physical Properties");
addField("elasticity", TypeF32, Offset(elasticity, DebrisData),
"@brief A floating-point value specifying how 'bouncy' this object is.\n\nMust be in the range of -10 to 10.\n");
addField("friction", TypeF32, Offset(friction, DebrisData),
"@brief A floating-point value specifying how much velocity is lost to impact and sliding friction.\n\nMust be in the range of -10 to 10.\n");
addField("numBounces", TypeS32, Offset(numBounces, DebrisData),
"@brief How many times to allow this debris object to bounce until it either explodes, becomes static or snaps (defined in explodeOnMaxBounce, staticOnMaxBounce, snapOnMaxBounce).\n\n"
"Must be within the range of 0 to 10000.\n"
"@see bounceVariance\n");
addField("bounceVariance", TypeS32, Offset(bounceVariance, DebrisData),
"@brief Allowed variance in the value of numBounces.\n\nMust be less than numBounces.\n@see numBounces\n");
addField("minSpinSpeed", TypeF32, Offset(minSpinSpeed, DebrisData),
"@brief Minimum speed that this debris object will rotate.\n\nMust be in the range of -10000 to 1000, and must be less than maxSpinSpeed.\n@see maxSpinSpeed\n");
addField("maxSpinSpeed", TypeF32, Offset(maxSpinSpeed, DebrisData),
"@brief Maximum speed that this debris object will rotate.\n\nMust be in the range of -10000 to 10000.\n@see minSpinSpeed\n");
addField("gravModifier", TypeF32, Offset(gravModifier, DebrisData), "How much gravity affects debris.");
addField("terminalVelocity", TypeF32, Offset(terminalVelocity, DebrisData), "Max velocity magnitude.");
addField("velocity", TypeF32, Offset(velocity, DebrisData),
"@brief Speed at which this debris object will move.\n\n@see velocityVariance\n");
addField("velocityVariance", TypeF32, Offset(velocityVariance, DebrisData),
"@brief Allowed variance in the value of velocity\n\nMust be less than velocity.\n@see velocity\n");
addField("lifetime", TypeF32, Offset(lifetime, DebrisData),
"@brief Amount of time until this debris object is destroyed.\n\nMust be in the range of 0 to 1000.\n@see lifetimeVariance");
addField("lifetimeVariance", TypeF32, Offset(lifetimeVariance, DebrisData),
"@brief Allowed variance in the value of lifetime.\n\nMust be less than lifetime.\n@see lifetime\n");
addField("useRadiusMass", TypeBool, Offset(useRadiusMass, DebrisData),
"@brief Use mass calculations based on radius.\n\nAllows for the adjustment of elasticity and friction based on the Debris size.\n@see baseRadius\n");
addField("baseRadius", TypeF32, Offset(baseRadius, DebrisData),
"@brief Radius at which the standard elasticity and friction apply.\n\nOnly used when useRaduisMass is true.\n@see useRadiusMass.\n");
endGroup("Physical Properties");
addGroup("Behavior");
addField("explodeOnMaxBounce", TypeBool, Offset(explodeOnMaxBounce, DebrisData),
"@brief If true, this debris object will explode after it has bounced max times.\n\nBe sure to provide an ExplosionData datablock for this to take effect.\n@see explosion\n");
addField("staticOnMaxBounce", TypeBool, Offset(staticOnMaxBounce, DebrisData), "If true, this debris object becomes static after it has bounced max times.");
addField("snapOnMaxBounce", TypeBool, Offset(snapOnMaxBounce, DebrisData), "If true, this debris object will snap into a resting position on the last bounce.");
addField("fade", TypeBool, Offset(fade, DebrisData),
"@brief If true, this debris object will fade out when destroyed.\n\nThis fade occurs over the last second of the Debris' lifetime.\n");
addField("ignoreWater", TypeBool, Offset(ignoreWater, DebrisData), "If true, this debris object will not collide with water, acting as if the water is not there.");
endGroup("Behavior");
Parent::initPersistFields();
}
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(explodeOnMaxBounce);
stream->write(staticOnMaxBounce);
stream->write(snapOnMaxBounce);
stream->write(lifetime);
stream->write(lifetimeVariance);
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);
}
}
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(&explodeOnMaxBounce);
stream->read(&staticOnMaxBounce);
stream->read(&snapOnMaxBounce);
stream->read(&lifetime);
stream->read(&lifetimeVariance);
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;
}
}
IMPLEMENT_CO_NETOBJECT_V1(Debris);
ConsoleDocClass( Debris,
"@brief Base debris class. Uses the DebrisData datablock for properties of individual debris objects.\n\n"
"Debris is typically made up of a shape and up to two particle emitters. In most cases Debris objects are "
"not created directly. They are usually produced automatically by other means, such as through the Explosion "
"class. When an explosion goes off, its ExplosionData datablock determines what Debris to emit.\n"
"@tsexample\n"
"datablock ExplosionData(GrenadeLauncherExplosion)\n"
"{\n"
" // Assiging debris data\n"
" debris = GrenadeDebris;\n\n"
" // Adjust how debris is ejected\n"
" debrisThetaMin = 10;\n"
" debrisThetaMax = 60;\n"
" debrisNum = 4;\n"
" debrisNumVariance = 2;\n"
" debrisVelocity = 25;\n"
" debrisVelocityVariance = 5;\n\n"
" // Note: other ExplosionData properties are not listed for this example\n"
"};\n"
"@endtsexample\n\n"
"@note Debris are client side only objects.\n"
"@see DebrisData\n"
"@see ExplosionData\n"
"@see Explosion\n"
"@ingroup FX\n"
);
DefineEngineMethod(Debris, init, bool, (const char* inputPosition, const char* inputVelocity),
("1.0 1.0 1.0", "1.0 0.0 0.0"),
"@brief Manually set this piece of debris at the given position with the given velocity.\n\n"
"Usually you do not manually create Debris objects as they are generated through other means, "
"such as an Explosion. This method exists when you do manually create a Debris object and "
"want to have it start moving.\n"
"@param inputPosition Position to place the debris.\n"
"@param inputVelocity Velocity to move the debris after it has been placed.\n"
"@return Always returns true.\n"
"@tsexample\n"
"// Define the position\n"
"%position = \"1.0 1.0 1.0\";\n\n"
"// Define the velocity\n"
"%velocity = \"1.0 0.0 0.0\";\n\n"
"// Inform the debris object of its new position and velocity\n"
"%debris.init(%position,%velocity);\n"
"@endtsexample\n")
{
Point3F pos;
dSscanf( inputPosition, "%f %f %f", &pos.x, &pos.y, &pos.z );
Point3F vel;
dSscanf( inputVelocity, "%f %f %f", &vel.x, &vel.y, &vel.z );
object->init( pos, vel );
return true;
}
Debris::Debris()
{
mTypeMask |= DebrisObjectType | DynamicShapeObjectType;
mVelocity = Point3F( 0.0f, 0.0f, 4.0f );
mLifetime = gRandGen.randF( 1.0f, 10.0f );
mLastPos = getPosition();
mNumBounces = gRandGen.randI( 0, 1 );
mSize = 2.0f;
mElapsedTime = 0.0f;
mShape = NULL;
mPart = NULL;
mDataBlock = NULL;
mXRotSpeed = 0.0f;
mZRotSpeed = 0.0f;
mInitialTrans.identity();
mRadius = 0.2f;
mStatic = false;
dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
// Only allocated client side.
mNetFlags.set( IsGhost );
}
Debris::~Debris()
{
if( mShape )
{
delete mShape;
mShape = NULL;
}
if( mPart )
{
delete mPart;
mPart = NULL;
}
}
void Debris::initPersistFields()
{
addGroup( "Debris" );
addField( "lifetime", TypeF32, Offset(mLifetime, Debris),
"@brief Length of time for this debris object to exist. When expired, the object will be deleted.\n\n"
"The initial lifetime value comes from the DebrisData datablock.\n"
"@see DebrisData::lifetime\n"
"@see DebrisData::lifetimeVariance\n");
endGroup( "Debris" );
Parent::initPersistFields();
}
void Debris::init( const Point3F &position, const Point3F &velocity )
{
setPosition( position );
setVelocity( velocity );
}
bool Debris::onNewDataBlock( GameBaseData *dptr, bool reload )
{
mDataBlock = dynamic_cast< DebrisData* >( dptr );
if( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
return false;
scriptOnNewDataBlock();
return true;
}
bool Debris::onAdd()
{
if( !Parent::onAdd() )
{
return false;
}
// 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], false );
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[ParticleData::PDC_NUM_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 = (S32)mDataBlock->bounceVariance;
bounceVar = gRandGen.randI( -bounceVar, bounceVar );
mNumBounces = mDataBlock->numBounces + bounceVar;
F32 lifeVar = (mDataBlock->lifetimeVariance * 2.0f * gRandGen.randF(-1.0,1.0)) - mDataBlock->lifetimeVariance;
mLifetime = mDataBlock->lifetime + lifeVar;
F32 xRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed );
F32 zRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed );
zRotSpeed *= gRandGen.randF( 0.1f, 0.5f );
mRotAngles.set( xRotSpeed, 0.0f, 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
gClientSceneGraph->addObjectToScene(this);
removeFromProcessList();
ClientProcessList::get()->addObject(this);
NetConnection* pNC = NetConnection::getConnectionToServer();
AssertFatal(pNC != NULL, "Error, must have a connection to the server!");
pNC->addObject(this);
return true;
}
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;
}
}
}
getSceneManager()->removeObjectFromScene(this);
getContainer()->removeObject(this);
Parent::onRemove();
}
void Debris::processTick(const Move*)
{
if (mLifetime <= 0.0)
deleteObject();
}
void Debris::advanceTime( F32 dt )
{
mElapsedTime += dt;
mLifetime -= dt;
if( mLifetime <= 0.0 )
{
mLifetime = 0.0;
return;
}
mLastPos = getPosition();
if( !mStatic )
{
rotate( dt );
Point3F nextPos = getPosition();
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 = getTransform();
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.0f, 0.0f, 0.10f ) );
setTransform( newTrans );
}
if( mDataBlock->staticOnMaxBounce )
{
mStatic = true;
}
}
}
else
{
setPosition( nextPos );
}
}
Point3F pos( getPosition( ) );
updateEmitters( pos, mVelocity, (U32)(dt * 1000.0));
}
void Debris::rotate( F32 dt )
{
MatrixF curTrans = getTransform();
curTrans.setPosition( Point3F(0.0f, 0.0f, 0.0f) );
Point3F curAngles = mRotAngles * dt * M_PI_F/180.0f;
MatrixF rotMatrix( EulerF( curAngles.x, curAngles.y, curAngles.z ) );
curTrans.mul( rotMatrix );
curTrans.setPosition( getPosition() );
setTransform( curTrans );
}
bool Debris::bounce( const Point3F &nextPos, F32 dt )
{
Point3F curPos = getPosition();
Point3F dir = nextPos - curPos;
if( dir.magnitudeSafe() == 0.0f ) 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.0f);
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;
setPosition( bouncePos );
mRotAngles *= mElasticity;
return true;
}
return false;
}
void Debris::explode()
{
if( !mDataBlock->explosion ) return;
Point3F explosionPos = getPosition();
Explosion* pExplosion = new Explosion;
pExplosion->onNewDataBlock(mDataBlock->explosion, false);
MatrixF trans( true );
trans.setPosition( getPosition() );
pExplosion->setTransform( trans );
pExplosion->setInitialState( explosionPos, VectorF(0,0,1), 1);
if (!pExplosion->registerObject())
delete pExplosion;
}
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 )
{
if( newVel.magnitudeSafe() > mDataBlock->terminalVelocity )
{
newVel.normalizeSafe();
newVel *= mDataBlock->terminalVelocity;
}
else
{
newVel += force * dt;
}
}
else
{
newVel += force * dt;
}
newPos += newVel * dt;
}
void Debris::updateEmitters( Point3F &pos, Point3F &vel, U32 ms )
{
Point3F axis = -vel;
if( axis.magnitudeSafe() == 0.0 )
{
axis = Point3F( 0.0, 0.0, 1.0 );
}
axis.normalizeSafe();
Point3F lastPos = mLastPos;
for( int i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
{
if( mEmitterList[i] )
{
mEmitterList[i]->emitParticles( lastPos, pos, axis, vel, ms );
}
}
}
void Debris::prepRenderImage( SceneRenderState *state )
{
if( !mPart && !mShape )
return;
Point3F cameraOffset;
mObjToWorld.getColumn(3,&cameraOffset);
cameraOffset -= state->getDiffuseCameraPosition();
F32 dist = cameraOffset.len();
F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z));
if( mShape )
{
mShape->setDetailFromDistance( state, dist * invScale );
if( mShape->getCurrentDetail() < 0 )
return;
}
if( mPart )
{
// get the shapeInstance that we are using for the debris parts
TSShapeInstance *si = mPart->getSourceShapeInstance();
if ( si )
si->setDetailFromDistance( state, dist * invScale );
}
prepBatchRender( state );
}
void Debris::prepBatchRender( SceneRenderState *state )
{
if ( !mShape && !mPart )
return;
GFXTransformSaver saver;
F32 alpha = 1.0;
if( mDataBlock->fade )
{
if( mLifetime < 1.0 ) alpha = mLifetime;
}
Point3F cameraOffset;
mObjToWorld.getColumn(3,&cameraOffset);
cameraOffset -= state->getCameraPosition();
// Set up our TS render state.
TSRenderState rdata;
rdata.setSceneState( state );
// We might have some forward lit materials
// so pass down a query to gather lights.
LightQuery query;
query.init( getWorldSphere() );
rdata.setLightQuery( &query );
if( mShape )
{
MatrixF mat = getRenderTransform();
GFX->setWorldMatrix( mat );
rdata.setFadeOverride( alpha );
mShape->render( rdata );
}
else
{
if (mPart->getCurrentObjectDetail() != -1)
{
MatrixF mat = getRenderTransform();
GFX->setWorldMatrix( mat );
rdata.setFadeOverride( alpha );
mPart->render( rdata );
}
}
}
void Debris::setSize( F32 size )
{
mSize = size;
}