Call the correct system rename
[Torque-3d.git] / Engine / source / T3D / debris.cpp
blob61ec9c3b6bf5c326f75463241deca58148d513c0
1 //-----------------------------------------------------------------------------
2 // Copyright (c) 2012 GarageGames, LLC
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to
6 // deal in the Software without restriction, including without limitation the
7 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 // sell copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 // IN THE SOFTWARE.
21 //-----------------------------------------------------------------------------
23 #include "platform/platform.h"
24 #include "T3D/debris.h"
26 #include "core/stream/bitStream.h"
27 #include "math/mathUtils.h"
28 #include "console/consoleTypes.h"
29 #include "console/consoleObject.h"
30 #include "sim/netConnection.h"
31 #include "scene/sceneRenderState.h"
32 #include "scene/sceneManager.h"
33 #include "ts/tsShapeInstance.h"
34 #include "ts/tsPartInstance.h"
35 #include "T3D/fx/particleEmitter.h"
36 #include "T3D/fx/explosion.h"
37 #include "T3D/gameBase/gameProcess.h"
38 #include "core/resourceManager.h"
39 #include "gfx/gfxTransformSaver.h"
40 #include "console/engineAPI.h"
41 #include "lighting/lightQuery.h"
44 const U32 csmStaticCollisionMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType;
46 IMPLEMENT_CO_DATABLOCK_V1(DebrisData);
48 ConsoleDocClass( DebrisData,
49 "@brief Stores properties for an individual debris type.\n\n"
51 "DebrisData defines the base properties for a Debris object. Typically you'll want a Debris object to consist of "
52 "a shape and possibly up to two particle emitters. The DebrisData datablock provides the definition for these items, "
53 "along with physical properties and how a Debris object will react to other game objects, such as water and terrain.\n"
55 "@tsexample\n"
56 "datablock DebrisData(GrenadeDebris)\n"
57 "{\n"
58 " shapeFile = \"art/shapes/weapons/ramrifle/debris.dts\";\n"
59 " emitters[0] = GrenadeDebrisFireEmitter;\n"
60 " elasticity = 0.4;\n"
61 " friction = 0.25;\n"
62 " numBounces = 3;\n"
63 " bounceVariance = 1;\n"
64 " explodeOnMaxBounce = false;\n"
65 " staticOnMaxBounce = false;\n"
66 " snapOnMaxBounce = false;\n"
67 " minSpinSpeed = 200;\n"
68 " maxSpinSpeed = 600;\n"
69 " lifetime = 4;\n"
70 " lifetimeVariance = 1.5;\n"
71 " velocity = 15;\n"
72 " velocityVariance = 5;\n"
73 " fade = true;\n"
74 " useRadiusMass = true;\n"
75 " baseRadius = 0.3;\n"
76 " gravModifier = 1.0;\n"
77 " terminalVelocity = 20;\n"
78 " ignoreWater = false;\n"
79 "};\n"
80 "@endtsexample\n\n"
81 "@see Debris\n\n"
82 "@ingroup FX\n"
85 DebrisData::DebrisData()
87 dMemset( emitterList, 0, sizeof( emitterList ) );
88 dMemset( emitterIDList, 0, sizeof( emitterIDList ) );
90 explosion = NULL;
91 explosionId = 0;
93 velocity = 0.0f;
94 velocityVariance = 0.0;
95 elasticity = 0.3f;
96 friction = 0.2f;
97 numBounces = 0;
98 bounceVariance = 0;
99 staticOnMaxBounce = false;
100 explodeOnMaxBounce = false;
101 snapOnMaxBounce = false;
102 lifetime = 3.0f;
103 lifetimeVariance = 0.0f;
104 minSpinSpeed = 0.0f;
105 maxSpinSpeed = 0.0f;
106 textureName = NULL;
107 shapeName = NULL;
108 fade = true;
109 useRadiusMass = false;
110 baseRadius = 1.0f;
111 gravModifier = 1.0f;
112 terminalVelocity = 0.0f;
113 ignoreWater = true;
116 bool DebrisData::onAdd()
118 if(!Parent::onAdd())
119 return false;
121 for( S32 i=0; i<DDC_NUM_EMITTERS; i++ )
123 if( !emitterList[i] && emitterIDList[i] != 0 )
125 if( Sim::findObject( emitterIDList[i], emitterList[i] ) == false)
127 Con::errorf( ConsoleLogEntry::General, "DebrisData::onAdd: Invalid packet, bad datablockId(emitter): 0x%x", emitterIDList[i]);
132 if (!explosion && explosionId != 0)
134 if (!Sim::findObject( SimObjectId( explosionId ), explosion ))
135 Con::errorf( ConsoleLogEntry::General, "DebrisData::onAdd: Invalid packet, bad datablockId(particle emitter): 0x%x", explosionId);
138 // validate data
140 if( velocityVariance > velocity )
142 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: velocityVariance invalid", getName());
143 velocityVariance = velocity;
145 if( friction < -10.0f || friction > 10.0f )
147 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: friction invalid", getName());
148 friction = 0.2f;
150 if( elasticity < -10.0f || elasticity > 10.0f )
152 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: elasticity invalid", getName());
153 elasticity = 0.2f;
155 if( lifetime < 0.0f || lifetime > 1000.0f )
157 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetime invalid", getName());
158 lifetime = 3.0f;
160 if( lifetimeVariance < 0.0f || lifetimeVariance > lifetime )
162 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetimeVariance invalid", getName());
163 lifetimeVariance = 0.0f;
165 if( numBounces < 0 || numBounces > 10000 )
167 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: numBounces invalid", getName());
168 numBounces = 3;
170 if( bounceVariance < 0 || bounceVariance > numBounces )
172 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: bounceVariance invalid", getName());
173 bounceVariance = 0;
175 if( minSpinSpeed < -10000.0f || minSpinSpeed > 10000.0f || minSpinSpeed > maxSpinSpeed )
177 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: minSpinSpeed invalid", getName());
178 minSpinSpeed = maxSpinSpeed - 1.0f;
180 if( maxSpinSpeed < -10000.0f || maxSpinSpeed > 10000.0f )
182 Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: maxSpinSpeed invalid", getName());
183 maxSpinSpeed = 0.0f;
186 return true;
189 bool DebrisData::preload(bool server, String &errorStr)
191 if (Parent::preload(server, errorStr) == false)
192 return false;
194 if( server ) return true;
196 if( shapeName && shapeName[0] != '\0' && !bool(shape) )
198 shape = ResourceManager::get().load(shapeName);
199 if( bool(shape) == false )
201 errorStr = String::ToString("DebrisData::load: Couldn't load shape \"%s\"", shapeName);
202 return false;
204 else
206 TSShapeInstance* pDummy = new TSShapeInstance(shape, !server);
207 delete pDummy;
212 return true;
215 void DebrisData::initPersistFields()
217 addGroup("Display");
218 addField("texture", TypeString, Offset(textureName, DebrisData),
219 "@brief Texture imagemap to use for this debris object.\n\nNot used any more.\n");
220 addField("shapeFile", TypeShapeFilename, Offset(shapeName, DebrisData),
221 "@brief Object model to use for this debris object.\n\nThis shape is optional. You could have Debris made up of only particles.\n");
222 endGroup("Display");
224 addGroup("Datablocks");
225 addField("emitters", TYPEID< ParticleEmitterData >(), Offset(emitterList, DebrisData), DDC_NUM_EMITTERS,
226 "@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");
227 addField("explosion", TYPEID< ExplosionData >(), Offset(explosion, DebrisData),
228 "@brief ExplosionData to spawn along with this debris object.\n\nThis is optional as not all Debris explode.\n");
229 endGroup("Datablocks");
231 addGroup("Physical Properties");
232 addField("elasticity", TypeF32, Offset(elasticity, DebrisData),
233 "@brief A floating-point value specifying how 'bouncy' this object is.\n\nMust be in the range of -10 to 10.\n");
234 addField("friction", TypeF32, Offset(friction, DebrisData),
235 "@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");
236 addField("numBounces", TypeS32, Offset(numBounces, DebrisData),
237 "@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"
238 "Must be within the range of 0 to 10000.\n"
239 "@see bounceVariance\n");
240 addField("bounceVariance", TypeS32, Offset(bounceVariance, DebrisData),
241 "@brief Allowed variance in the value of numBounces.\n\nMust be less than numBounces.\n@see numBounces\n");
242 addField("minSpinSpeed", TypeF32, Offset(minSpinSpeed, DebrisData),
243 "@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");
244 addField("maxSpinSpeed", TypeF32, Offset(maxSpinSpeed, DebrisData),
245 "@brief Maximum speed that this debris object will rotate.\n\nMust be in the range of -10000 to 10000.\n@see minSpinSpeed\n");
246 addField("gravModifier", TypeF32, Offset(gravModifier, DebrisData), "How much gravity affects debris.");
247 addField("terminalVelocity", TypeF32, Offset(terminalVelocity, DebrisData), "Max velocity magnitude.");
248 addField("velocity", TypeF32, Offset(velocity, DebrisData),
249 "@brief Speed at which this debris object will move.\n\n@see velocityVariance\n");
250 addField("velocityVariance", TypeF32, Offset(velocityVariance, DebrisData),
251 "@brief Allowed variance in the value of velocity\n\nMust be less than velocity.\n@see velocity\n");
252 addField("lifetime", TypeF32, Offset(lifetime, DebrisData),
253 "@brief Amount of time until this debris object is destroyed.\n\nMust be in the range of 0 to 1000.\n@see lifetimeVariance");
254 addField("lifetimeVariance", TypeF32, Offset(lifetimeVariance, DebrisData),
255 "@brief Allowed variance in the value of lifetime.\n\nMust be less than lifetime.\n@see lifetime\n");
256 addField("useRadiusMass", TypeBool, Offset(useRadiusMass, DebrisData),
257 "@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");
258 addField("baseRadius", TypeF32, Offset(baseRadius, DebrisData),
259 "@brief Radius at which the standard elasticity and friction apply.\n\nOnly used when useRaduisMass is true.\n@see useRadiusMass.\n");
260 endGroup("Physical Properties");
262 addGroup("Behavior");
263 addField("explodeOnMaxBounce", TypeBool, Offset(explodeOnMaxBounce, DebrisData),
264 "@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");
265 addField("staticOnMaxBounce", TypeBool, Offset(staticOnMaxBounce, DebrisData), "If true, this debris object becomes static after it has bounced max times.");
266 addField("snapOnMaxBounce", TypeBool, Offset(snapOnMaxBounce, DebrisData), "If true, this debris object will snap into a resting position on the last bounce.");
267 addField("fade", TypeBool, Offset(fade, DebrisData),
268 "@brief If true, this debris object will fade out when destroyed.\n\nThis fade occurs over the last second of the Debris' lifetime.\n");
269 addField("ignoreWater", TypeBool, Offset(ignoreWater, DebrisData), "If true, this debris object will not collide with water, acting as if the water is not there.");
270 endGroup("Behavior");
272 Parent::initPersistFields();
275 void DebrisData::packData(BitStream* stream)
277 Parent::packData(stream);
279 stream->write(elasticity);
280 stream->write(friction);
281 stream->write(numBounces);
282 stream->write(bounceVariance);
283 stream->write(minSpinSpeed);
284 stream->write(maxSpinSpeed);
285 stream->write(explodeOnMaxBounce);
286 stream->write(staticOnMaxBounce);
287 stream->write(snapOnMaxBounce);
288 stream->write(lifetime);
289 stream->write(lifetimeVariance);
290 stream->write(velocity);
291 stream->write(velocityVariance);
292 stream->write(fade);
293 stream->write(useRadiusMass);
294 stream->write(baseRadius);
295 stream->write(gravModifier);
296 stream->write(terminalVelocity);
297 stream->write(ignoreWater);
299 stream->writeString( textureName );
300 stream->writeString( shapeName );
302 for( S32 i=0; i<DDC_NUM_EMITTERS; i++ )
304 if( stream->writeFlag( emitterList[i] != NULL ) )
306 stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
310 if( stream->writeFlag( explosion ) )
312 stream->writeRangedU32(packed? SimObjectId((uintptr_t)explosion):
313 explosion->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
318 void DebrisData::unpackData(BitStream* stream)
320 Parent::unpackData(stream);
322 stream->read(&elasticity);
323 stream->read(&friction);
324 stream->read(&numBounces);
325 stream->read(&bounceVariance);
326 stream->read(&minSpinSpeed);
327 stream->read(&maxSpinSpeed);
328 stream->read(&explodeOnMaxBounce);
329 stream->read(&staticOnMaxBounce);
330 stream->read(&snapOnMaxBounce);
331 stream->read(&lifetime);
332 stream->read(&lifetimeVariance);
333 stream->read(&velocity);
334 stream->read(&velocityVariance);
335 stream->read(&fade);
336 stream->read(&useRadiusMass);
337 stream->read(&baseRadius);
338 stream->read(&gravModifier);
339 stream->read(&terminalVelocity);
340 stream->read(&ignoreWater);
342 textureName = stream->readSTString();
343 shapeName = stream->readSTString();
345 for( S32 i=0; i<DDC_NUM_EMITTERS; i++ )
347 if( stream->readFlag() )
349 emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
353 if(stream->readFlag())
355 explosionId = (S32)stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
357 else
359 explosionId = 0;
365 IMPLEMENT_CO_NETOBJECT_V1(Debris);
367 ConsoleDocClass( Debris,
368 "@brief Base debris class. Uses the DebrisData datablock for properties of individual debris objects.\n\n"
370 "Debris is typically made up of a shape and up to two particle emitters. In most cases Debris objects are "
371 "not created directly. They are usually produced automatically by other means, such as through the Explosion "
372 "class. When an explosion goes off, its ExplosionData datablock determines what Debris to emit.\n"
374 "@tsexample\n"
375 "datablock ExplosionData(GrenadeLauncherExplosion)\n"
376 "{\n"
377 " // Assiging debris data\n"
378 " debris = GrenadeDebris;\n\n"
379 " // Adjust how debris is ejected\n"
380 " debrisThetaMin = 10;\n"
381 " debrisThetaMax = 60;\n"
382 " debrisNum = 4;\n"
383 " debrisNumVariance = 2;\n"
384 " debrisVelocity = 25;\n"
385 " debrisVelocityVariance = 5;\n\n"
386 " // Note: other ExplosionData properties are not listed for this example\n"
387 "};\n"
388 "@endtsexample\n\n"
390 "@note Debris are client side only objects.\n"
392 "@see DebrisData\n"
393 "@see ExplosionData\n"
394 "@see Explosion\n"
396 "@ingroup FX\n"
399 DefineEngineMethod(Debris, init, bool, (const char* inputPosition, const char* inputVelocity),
400 ("1.0 1.0 1.0", "1.0 0.0 0.0"),
401 "@brief Manually set this piece of debris at the given position with the given velocity.\n\n"
403 "Usually you do not manually create Debris objects as they are generated through other means, "
404 "such as an Explosion. This method exists when you do manually create a Debris object and "
405 "want to have it start moving.\n"
407 "@param inputPosition Position to place the debris.\n"
408 "@param inputVelocity Velocity to move the debris after it has been placed.\n"
409 "@return Always returns true.\n"
411 "@tsexample\n"
412 "// Define the position\n"
413 "%position = \"1.0 1.0 1.0\";\n\n"
414 "// Define the velocity\n"
415 "%velocity = \"1.0 0.0 0.0\";\n\n"
416 "// Inform the debris object of its new position and velocity\n"
417 "%debris.init(%position,%velocity);\n"
418 "@endtsexample\n")
420 Point3F pos;
421 dSscanf( inputPosition, "%f %f %f", &pos.x, &pos.y, &pos.z );
423 Point3F vel;
424 dSscanf( inputVelocity, "%f %f %f", &vel.x, &vel.y, &vel.z );
426 object->init( pos, vel );
428 return true;
431 Debris::Debris()
433 mTypeMask |= DebrisObjectType | DynamicShapeObjectType;
435 mVelocity = Point3F( 0.0f, 0.0f, 4.0f );
436 mLifetime = gRandGen.randF( 1.0f, 10.0f );
437 mLastPos = getPosition();
438 mNumBounces = gRandGen.randI( 0, 1 );
439 mSize = 2.0f;
440 mElapsedTime = 0.0f;
441 mShape = NULL;
442 mPart = NULL;
443 mDataBlock = NULL;
444 mXRotSpeed = 0.0f;
445 mZRotSpeed = 0.0f;
446 mInitialTrans.identity();
447 mRadius = 0.2f;
448 mStatic = false;
450 dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
452 // Only allocated client side.
453 mNetFlags.set( IsGhost );
456 Debris::~Debris()
458 if( mShape )
460 delete mShape;
461 mShape = NULL;
464 if( mPart )
466 delete mPart;
467 mPart = NULL;
471 void Debris::initPersistFields()
473 addGroup( "Debris" );
475 addField( "lifetime", TypeF32, Offset(mLifetime, Debris),
476 "@brief Length of time for this debris object to exist. When expired, the object will be deleted.\n\n"
477 "The initial lifetime value comes from the DebrisData datablock.\n"
478 "@see DebrisData::lifetime\n"
479 "@see DebrisData::lifetimeVariance\n");
481 endGroup( "Debris" );
483 Parent::initPersistFields();
486 void Debris::init( const Point3F &position, const Point3F &velocity )
488 setPosition( position );
489 setVelocity( velocity );
492 bool Debris::onNewDataBlock( GameBaseData *dptr, bool reload )
494 mDataBlock = dynamic_cast< DebrisData* >( dptr );
495 if( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
496 return false;
498 scriptOnNewDataBlock();
499 return true;
503 bool Debris::onAdd()
505 if( !Parent::onAdd() )
507 return false;
510 if( !mDataBlock )
512 Con::errorf("Debris::onAdd - Fail - No datablock");
513 return false;
516 // create emitters
517 for( S32 i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
519 if( mDataBlock->emitterList[i] != NULL )
521 ParticleEmitter * pEmitter = new ParticleEmitter;
522 pEmitter->onNewDataBlock( mDataBlock->emitterList[i], false );
523 if( !pEmitter->registerObject() )
525 Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() );
526 delete pEmitter;
527 pEmitter = NULL;
529 mEmitterList[i] = pEmitter;
533 // set particle sizes based on debris size
534 F32 sizeList[ParticleData::PDC_NUM_KEYS];
536 if( mEmitterList[0] )
538 sizeList[0] = mSize * 0.5;
539 sizeList[1] = mSize;
540 sizeList[2] = mSize * 1.5;
542 mEmitterList[0]->setSizes( sizeList );
545 if( mEmitterList[1] )
547 sizeList[0] = 0.0;
548 sizeList[1] = mSize * 0.5;
549 sizeList[2] = mSize;
551 mEmitterList[1]->setSizes( sizeList );
554 S32 bounceVar = (S32)mDataBlock->bounceVariance;
555 bounceVar = gRandGen.randI( -bounceVar, bounceVar );
556 mNumBounces = mDataBlock->numBounces + bounceVar;
558 F32 lifeVar = (mDataBlock->lifetimeVariance * 2.0f * gRandGen.randF(-1.0,1.0)) - mDataBlock->lifetimeVariance;
559 mLifetime = mDataBlock->lifetime + lifeVar;
561 F32 xRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed );
562 F32 zRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed );
563 zRotSpeed *= gRandGen.randF( 0.1f, 0.5f );
565 mRotAngles.set( xRotSpeed, 0.0f, zRotSpeed );
567 mElasticity = mDataBlock->elasticity;
568 mFriction = mDataBlock->friction;
570 // Setup our bounding box
571 if( mDataBlock->shape )
573 mObjBox = mDataBlock->shape->bounds;
575 else
577 mObjBox = Box3F(Point3F(-1, -1, -1), Point3F(1, 1, 1));
580 if( mDataBlock->shape )
582 mShape = new TSShapeInstance( mDataBlock->shape, true);
585 if( mPart )
587 // use half radius becuase we want debris to stick in ground
588 mRadius = mPart->getRadius() * 0.5;
589 mObjBox = mPart->getBounds();
592 resetWorldBox();
594 mInitialTrans = getTransform();
596 if( mDataBlock->velocity != 0.0 )
598 F32 velocity = mDataBlock->velocity + gRandGen.randF( -mDataBlock->velocityVariance, mDataBlock->velocityVariance );
600 mVelocity.normalizeSafe();
601 mVelocity *= velocity;
604 // mass calculations
605 if( mDataBlock->useRadiusMass )
607 if( mRadius < mDataBlock->baseRadius )
609 mRadius = mDataBlock->baseRadius;
612 // linear falloff
613 F32 multFactor = mDataBlock->baseRadius / mRadius;
615 mElasticity *= multFactor;
616 mFriction *= multFactor;
617 mRotAngles *= multFactor;
621 // tell engine the debris exists
622 gClientSceneGraph->addObjectToScene(this);
624 removeFromProcessList();
625 ClientProcessList::get()->addObject(this);
627 NetConnection* pNC = NetConnection::getConnectionToServer();
628 AssertFatal(pNC != NULL, "Error, must have a connection to the server!");
629 pNC->addObject(this);
631 return true;
634 void Debris::onRemove()
636 for( S32 i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
638 if( mEmitterList[i] )
640 mEmitterList[i]->deleteWhenEmpty();
641 mEmitterList[i] = NULL;
645 if( mPart )
647 TSShapeInstance *ss = mPart->getSourceShapeInstance();
648 if( ss )
650 ss->decDebrisRefCount();
651 if( ss->getDebrisRefCount() == 0 )
653 delete ss;
658 removeFromScene();
660 Parent::onRemove();
663 void Debris::processTick(const Move*)
665 if (mLifetime <= 0.0)
666 deleteObject();
669 void Debris::advanceTime( F32 dt )
671 mElapsedTime += dt;
673 mLifetime -= dt;
674 if( mLifetime <= 0.0 )
676 mLifetime = 0.0;
677 return;
680 mLastPos = getPosition();
682 if( !mStatic )
684 rotate( dt );
686 Point3F nextPos = getPosition();
687 computeNewState( nextPos, mVelocity, dt );
689 if( bounce( nextPos, dt ) )
691 --mNumBounces;
692 if( mNumBounces <= 0 )
694 if( mDataBlock->explodeOnMaxBounce )
696 explode();
697 mLifetime = 0.0;
699 if( mDataBlock->snapOnMaxBounce )
701 // orient debris so it's flat
702 MatrixF stat = getTransform();
704 Point3F dir;
705 stat.getColumn( 1, &dir );
706 dir.z = 0.0;
708 MatrixF newTrans = MathUtils::createOrientFromDir( dir );
710 // hack for shell casings to get them above ground. Need something better - bramage
711 newTrans.setPosition( getPosition() + Point3F( 0.0f, 0.0f, 0.10f ) );
713 setTransform( newTrans );
715 if( mDataBlock->staticOnMaxBounce )
717 mStatic = true;
721 else
723 setPosition( nextPos );
727 Point3F pos( getPosition( ) );
728 updateEmitters( pos, mVelocity, (U32)(dt * 1000.0));
732 void Debris::rotate( F32 dt )
734 MatrixF curTrans = getTransform();
735 curTrans.setPosition( Point3F(0.0f, 0.0f, 0.0f) );
737 Point3F curAngles = mRotAngles * dt * M_PI_F/180.0f;
738 MatrixF rotMatrix( EulerF( curAngles.x, curAngles.y, curAngles.z ) );
740 curTrans.mul( rotMatrix );
741 curTrans.setPosition( getPosition() );
742 setTransform( curTrans );
745 bool Debris::bounce( const Point3F &nextPos, F32 dt )
747 Point3F curPos = getPosition();
749 Point3F dir = nextPos - curPos;
750 if( dir.magnitudeSafe() == 0.0f ) return false;
751 dir.normalizeSafe();
752 Point3F extent = nextPos + dir * mRadius;
753 F32 totalDist = Point3F( extent - curPos ).magnitudeSafe();
754 F32 moveDist = Point3F( nextPos - curPos ).magnitudeSafe();
755 F32 movePercent = (moveDist / totalDist);
757 RayInfo rayInfo;
758 U32 collisionMask = csmStaticCollisionMask;
759 if( !mDataBlock->ignoreWater )
761 collisionMask |= WaterObjectType;
764 if( getContainer()->castRay( curPos, extent, collisionMask, &rayInfo ) )
767 Point3F reflection = mVelocity - rayInfo.normal * (mDot( mVelocity, rayInfo.normal ) * 2.0f);
768 mVelocity = reflection;
770 Point3F tangent = reflection - rayInfo.normal * mDot( reflection, rayInfo.normal );
771 mVelocity -= tangent * mFriction;
773 Point3F velDir = mVelocity;
774 velDir.normalizeSafe();
776 mVelocity *= mElasticity;
778 Point3F bouncePos = curPos + dir * rayInfo.t * movePercent;
779 bouncePos += mVelocity * dt;
781 setPosition( bouncePos );
783 mRotAngles *= mElasticity;
785 return true;
789 return false;
793 void Debris::explode()
796 if( !mDataBlock->explosion ) return;
798 Point3F explosionPos = getPosition();
800 Explosion* pExplosion = new Explosion;
801 pExplosion->onNewDataBlock(mDataBlock->explosion, false);
803 MatrixF trans( true );
804 trans.setPosition( getPosition() );
806 pExplosion->setTransform( trans );
807 pExplosion->setInitialState( explosionPos, VectorF(0,0,1), 1);
808 if (!pExplosion->registerObject())
809 delete pExplosion;
812 void Debris::computeNewState( Point3F &newPos, Point3F &newVel, F32 dt )
814 // apply gravity
815 Point3F force = Point3F(0, 0, -9.81 * mDataBlock->gravModifier );
817 if( mDataBlock->terminalVelocity > 0.0001 )
819 if( newVel.magnitudeSafe() > mDataBlock->terminalVelocity )
821 newVel.normalizeSafe();
822 newVel *= mDataBlock->terminalVelocity;
824 else
826 newVel += force * dt;
829 else
831 newVel += force * dt;
834 newPos += newVel * dt;
838 void Debris::updateEmitters( Point3F &pos, Point3F &vel, U32 ms )
841 Point3F axis = -vel;
843 if( axis.magnitudeSafe() == 0.0 )
845 axis = Point3F( 0.0, 0.0, 1.0 );
847 axis.normalizeSafe();
850 Point3F lastPos = mLastPos;
852 for( S32 i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
854 if( mEmitterList[i] )
856 mEmitterList[i]->emitParticles( lastPos, pos, axis, vel, ms );
862 void Debris::prepRenderImage( SceneRenderState *state )
864 if( !mPart && !mShape )
865 return;
867 Point3F cameraOffset;
868 mObjToWorld.getColumn(3,&cameraOffset);
869 cameraOffset -= state->getDiffuseCameraPosition();
870 F32 dist = cameraOffset.len();
871 F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z));
873 if( mShape )
875 mShape->setDetailFromDistance( state, dist * invScale );
876 if( mShape->getCurrentDetail() < 0 )
877 return;
880 if( mPart )
882 // get the shapeInstance that we are using for the debris parts
883 TSShapeInstance *si = mPart->getSourceShapeInstance();
884 if ( si )
885 si->setDetailFromDistance( state, dist * invScale );
888 prepBatchRender( state );
891 void Debris::prepBatchRender( SceneRenderState *state )
893 if ( !mShape && !mPart )
894 return;
896 GFXTransformSaver saver;
898 F32 alpha = 1.0;
899 if( mDataBlock->fade )
901 if( mLifetime < 1.0 ) alpha = mLifetime;
904 Point3F cameraOffset;
905 mObjToWorld.getColumn(3,&cameraOffset);
906 cameraOffset -= state->getCameraPosition();
908 // Set up our TS render state.
909 TSRenderState rdata;
910 rdata.setSceneState( state );
912 // We might have some forward lit materials
913 // so pass down a query to gather lights.
914 LightQuery query;
915 query.init( getWorldSphere() );
916 rdata.setLightQuery( &query );
918 if( mShape )
920 MatrixF mat = getRenderTransform();
921 GFX->setWorldMatrix( mat );
923 rdata.setFadeOverride( alpha );
924 mShape->render( rdata );
926 else
928 if (mPart->getCurrentObjectDetail() != -1)
930 MatrixF mat = getRenderTransform();
931 GFX->setWorldMatrix( mat );
933 rdata.setFadeOverride( alpha );
934 mPart->render( rdata );
939 void Debris::setSize( F32 size )
941 mSize = size;