debris.cpp 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046
  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:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  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. //-----------------------------------------------------------------------------
  22. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  23. // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  24. // Copyright (C) 2015 Faust Logic, Inc.
  25. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  26. #include "platform/platform.h"
  27. #include "T3D/debris.h"
  28. #include "core/stream/bitStream.h"
  29. #include "math/mathUtils.h"
  30. #include "console/consoleTypes.h"
  31. #include "console/consoleObject.h"
  32. #include "sim/netConnection.h"
  33. #include "scene/sceneRenderState.h"
  34. #include "scene/sceneManager.h"
  35. #include "ts/tsShapeInstance.h"
  36. #include "ts/tsPartInstance.h"
  37. #include "T3D/fx/particleEmitter.h"
  38. #include "T3D/fx/explosion.h"
  39. #include "T3D/gameBase/gameProcess.h"
  40. #include "core/resourceManager.h"
  41. #include "gfx/gfxTransformSaver.h"
  42. #include "console/engineAPI.h"
  43. #include "lighting/lightQuery.h"
  44. const U32 csmStaticCollisionMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType;
  45. IMPLEMENT_CO_DATABLOCK_V1(DebrisData);
  46. ConsoleDocClass( DebrisData,
  47. "@brief Stores properties for an individual debris type.\n\n"
  48. "DebrisData defines the base properties for a Debris object. Typically you'll want a Debris object to consist of "
  49. "a shape and possibly up to two particle emitters. The DebrisData datablock provides the definition for these items, "
  50. "along with physical properties and how a Debris object will react to other game objects, such as water and terrain.\n"
  51. "@tsexample\n"
  52. "datablock DebrisData(GrenadeDebris)\n"
  53. "{\n"
  54. " shapeFile = \"art/shapes/weapons/ramrifle/debris.dts\";\n"
  55. " emitters[0] = GrenadeDebrisFireEmitter;\n"
  56. " elasticity = 0.4;\n"
  57. " friction = 0.25;\n"
  58. " numBounces = 3;\n"
  59. " bounceVariance = 1;\n"
  60. " explodeOnMaxBounce = false;\n"
  61. " staticOnMaxBounce = false;\n"
  62. " snapOnMaxBounce = false;\n"
  63. " minSpinSpeed = 200;\n"
  64. " maxSpinSpeed = 600;\n"
  65. " lifetime = 4;\n"
  66. " lifetimeVariance = 1.5;\n"
  67. " velocity = 15;\n"
  68. " velocityVariance = 5;\n"
  69. " fade = true;\n"
  70. " useRadiusMass = true;\n"
  71. " baseRadius = 0.3;\n"
  72. " gravModifier = 1.0;\n"
  73. " terminalVelocity = 20;\n"
  74. " ignoreWater = false;\n"
  75. "};\n"
  76. "@endtsexample\n\n"
  77. "@see Debris\n\n"
  78. "@ingroup FX\n"
  79. );
  80. DebrisData::DebrisData()
  81. {
  82. dMemset( emitterList, 0, sizeof( emitterList ) );
  83. dMemset( emitterIDList, 0, sizeof( emitterIDList ) );
  84. explosion = NULL;
  85. explosionId = 0;
  86. velocity = 0.0f;
  87. velocityVariance = 0.0;
  88. elasticity = 0.3f;
  89. friction = 0.2f;
  90. numBounces = 0;
  91. bounceVariance = 0;
  92. staticOnMaxBounce = false;
  93. explodeOnMaxBounce = false;
  94. snapOnMaxBounce = false;
  95. lifetime = 3.0f;
  96. lifetimeVariance = 0.0f;
  97. minSpinSpeed = 0.0f;
  98. maxSpinSpeed = 0.0f;
  99. textureName = NULL;
  100. fade = true;
  101. useRadiusMass = false;
  102. baseRadius = 1.0f;
  103. gravModifier = 1.0f;
  104. terminalVelocity = 0.0f;
  105. ignoreWater = true;
  106. INIT_ASSET(Shape);
  107. }
  108. //#define TRACK_DEBRIS_DATA_CLONES
  109. #ifdef TRACK_DEBRIS_DATA_CLONES
  110. static int debris_data_clones = 0;
  111. #endif
  112. DebrisData::DebrisData(const DebrisData& other, bool temp_clone) : GameBaseData(other, temp_clone)
  113. {
  114. #ifdef TRACK_DEBRIS_DATA_CLONES
  115. debris_data_clones++;
  116. if (debris_data_clones == 1)
  117. Con::errorf("DebrisData -- Clones are on the loose!");
  118. #endif
  119. velocity = other.velocity;
  120. velocityVariance = other.velocityVariance;
  121. friction = other.friction;
  122. elasticity = other.elasticity;
  123. lifetime = other.lifetime;
  124. lifetimeVariance = other.lifetimeVariance;
  125. numBounces = other.numBounces;
  126. bounceVariance = other.bounceVariance;
  127. minSpinSpeed = other.minSpinSpeed;
  128. maxSpinSpeed = other.maxSpinSpeed;
  129. explodeOnMaxBounce = other.explodeOnMaxBounce;
  130. staticOnMaxBounce = other.staticOnMaxBounce;
  131. snapOnMaxBounce = other.snapOnMaxBounce;
  132. fade = other.fade;
  133. useRadiusMass = other.useRadiusMass;
  134. baseRadius = other.baseRadius;
  135. gravModifier = other.gravModifier;
  136. terminalVelocity = other.terminalVelocity;
  137. ignoreWater = other.ignoreWater;
  138. CLONE_ASSET(Shape);
  139. textureName = other.textureName;
  140. explosionId = other.explosionId; // -- for pack/unpack of explosion ptr
  141. explosion = other.explosion;
  142. dMemcpy( emitterList, other.emitterList, sizeof( emitterList ) );
  143. dMemcpy( emitterIDList, other.emitterIDList, sizeof( emitterIDList ) ); // -- for pack/unpack of emitterList ptrs
  144. }
  145. DebrisData::~DebrisData()
  146. {
  147. if (!isTempClone())
  148. return;
  149. #ifdef TRACK_DEBRIS_DATA_CLONES
  150. if (debris_data_clones > 0)
  151. {
  152. debris_data_clones--;
  153. if (debris_data_clones == 0)
  154. Con::errorf("DebrisData -- Clones eliminated!");
  155. }
  156. else
  157. Con::errorf("DebrisData -- Too many clones deleted!");
  158. #endif
  159. }
  160. DebrisData* DebrisData::cloneAndPerformSubstitutions(const SimObject* owner, S32 index)
  161. {
  162. if (!owner || getSubstitutionCount() == 0)
  163. return this;
  164. DebrisData* sub_debris_db = new DebrisData(*this, true);
  165. performSubstitutions(sub_debris_db, owner, index);
  166. return sub_debris_db;
  167. }
  168. void DebrisData::onPerformSubstitutions()
  169. {
  170. _setShape(getShape());
  171. }
  172. bool DebrisData::onAdd()
  173. {
  174. if(!Parent::onAdd())
  175. return false;
  176. for( S32 i=0; i<DDC_NUM_EMITTERS; i++ )
  177. {
  178. if( !emitterList[i] && emitterIDList[i] != 0 )
  179. {
  180. if( Sim::findObject( emitterIDList[i], emitterList[i] ) == false)
  181. {
  182. Con::errorf( ConsoleLogEntry::General, "DebrisData::onAdd: Invalid packet, bad datablockId(emitter): 0x%x", emitterIDList[i]);
  183. }
  184. }
  185. }
  186. if (!explosion && explosionId != 0)
  187. {
  188. if (!Sim::findObject( SimObjectId( explosionId ), explosion ))
  189. Con::errorf( ConsoleLogEntry::General, "DebrisData::onAdd: Invalid packet, bad datablockId(particle emitter): 0x%x", explosionId);
  190. }
  191. // validate data
  192. if( velocityVariance > velocity )
  193. {
  194. Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: velocityVariance invalid", getName());
  195. velocityVariance = velocity;
  196. }
  197. if( friction < -10.0f || friction > 10.0f )
  198. {
  199. Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: friction invalid", getName());
  200. friction = 0.2f;
  201. }
  202. if( elasticity < -10.0f || elasticity > 10.0f )
  203. {
  204. Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: elasticity invalid", getName());
  205. elasticity = 0.2f;
  206. }
  207. if( lifetime < 0.0f || lifetime > 1000.0f )
  208. {
  209. Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetime invalid", getName());
  210. lifetime = 3.0f;
  211. }
  212. if( lifetimeVariance < 0.0f || lifetimeVariance > lifetime )
  213. {
  214. Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetimeVariance invalid", getName());
  215. lifetimeVariance = 0.0f;
  216. }
  217. if( numBounces < 0 || numBounces > 10000 )
  218. {
  219. Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: numBounces invalid", getName());
  220. numBounces = 3;
  221. }
  222. if( bounceVariance < 0 || bounceVariance > numBounces )
  223. {
  224. Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: bounceVariance invalid", getName());
  225. bounceVariance = 0;
  226. }
  227. if( minSpinSpeed < -10000.0f || minSpinSpeed > 10000.0f || minSpinSpeed > maxSpinSpeed )
  228. {
  229. Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: minSpinSpeed invalid", getName());
  230. minSpinSpeed = maxSpinSpeed - 1.0f;
  231. }
  232. if( maxSpinSpeed < -10000.0f || maxSpinSpeed > 10000.0f )
  233. {
  234. Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: maxSpinSpeed invalid", getName());
  235. maxSpinSpeed = 0.0f;
  236. }
  237. return true;
  238. }
  239. bool DebrisData::preload(bool server, String &errorStr)
  240. {
  241. if (Parent::preload(server, errorStr) == false)
  242. return false;
  243. if( server ) return true;
  244. if (mShapeAsset.notNull())
  245. {
  246. if (!mShape)
  247. {
  248. errorStr = String::ToString("DebrisData::load: Couldn't load shape \"%s\"", mShapeAssetId);
  249. return false;
  250. }
  251. else
  252. {
  253. TSShapeInstance* pDummy = new TSShapeInstance(mShape, !server);
  254. delete pDummy;
  255. if (!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded())
  256. return false;
  257. }
  258. }
  259. return true;
  260. }
  261. void DebrisData::initPersistFields()
  262. {
  263. docsURL;
  264. addGroup("Shapes");
  265. addField("texture", TypeString, Offset(textureName, DebrisData),
  266. "@brief Texture imagemap to use for this debris object.\n\nNot used any more.\n", AbstractClassRep::FIELD_HideInInspectors);
  267. INITPERSISTFIELD_SHAPEASSET(Shape, DebrisData, "Shape to use for this debris object.");
  268. endGroup("Shapes");
  269. addGroup("Particle Effects");
  270. addField("emitters", TYPEID< ParticleEmitterData >(), Offset(emitterList, DebrisData), DDC_NUM_EMITTERS,
  271. "@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");
  272. addGroup("Particle Effects");
  273. addGroup("Datablocks");
  274. addField("explosion", TYPEID< ExplosionData >(), Offset(explosion, DebrisData),
  275. "@brief ExplosionData to spawn along with this debris object.\n\nThis is optional as not all Debris explode.\n");
  276. endGroup("Datablocks");
  277. addGroup("Physics");
  278. addField("elasticity", TypeF32, Offset(elasticity, DebrisData),
  279. "@brief A floating-point value specifying how 'bouncy' this object is.\n\nMust be in the range of -10 to 10.\n");
  280. addField("friction", TypeF32, Offset(friction, DebrisData),
  281. "@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");
  282. addField("numBounces", TypeS32, Offset(numBounces, DebrisData),
  283. "@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"
  284. "Must be within the range of 0 to 10000.\n"
  285. "@see bounceVariance\n");
  286. addField("bounceVariance", TypeS32, Offset(bounceVariance, DebrisData),
  287. "@brief Allowed variance in the value of numBounces.\n\nMust be less than numBounces.\n@see numBounces\n");
  288. addField("minSpinSpeed", TypeF32, Offset(minSpinSpeed, DebrisData),
  289. "@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");
  290. addField("maxSpinSpeed", TypeF32, Offset(maxSpinSpeed, DebrisData),
  291. "@brief Maximum speed that this debris object will rotate.\n\nMust be in the range of -10000 to 10000.\n@see minSpinSpeed\n");
  292. addField("gravModifier", TypeF32, Offset(gravModifier, DebrisData), "How much gravity affects debris.");
  293. addField("terminalVelocity", TypeF32, Offset(terminalVelocity, DebrisData), "Max velocity magnitude.");
  294. addField("velocity", TypeF32, Offset(velocity, DebrisData),
  295. "@brief Speed at which this debris object will move.\n\n@see velocityVariance\n");
  296. addField("velocityVariance", TypeF32, Offset(velocityVariance, DebrisData),
  297. "@brief Allowed variance in the value of velocity\n\nMust be less than velocity.\n@see velocity\n");
  298. addField("lifetime", TypeF32, Offset(lifetime, DebrisData),
  299. "@brief Amount of time until this debris object is destroyed.\n\nMust be in the range of 0 to 1000.\n@see lifetimeVariance");
  300. addField("lifetimeVariance", TypeF32, Offset(lifetimeVariance, DebrisData),
  301. "@brief Allowed variance in the value of lifetime.\n\nMust be less than lifetime.\n@see lifetime\n");
  302. addField("useRadiusMass", TypeBool, Offset(useRadiusMass, DebrisData),
  303. "@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");
  304. addField("baseRadius", TypeF32, Offset(baseRadius, DebrisData),
  305. "@brief Radius at which the standard elasticity and friction apply.\n\nOnly used when useRaduisMass is true.\n@see useRadiusMass.\n");
  306. endGroup("Physics");
  307. addGroup("Behavior");
  308. addField("explodeOnMaxBounce", TypeBool, Offset(explodeOnMaxBounce, DebrisData),
  309. "@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");
  310. addField("staticOnMaxBounce", TypeBool, Offset(staticOnMaxBounce, DebrisData), "If true, this debris object becomes static after it has bounced max times.");
  311. addField("snapOnMaxBounce", TypeBool, Offset(snapOnMaxBounce, DebrisData), "If true, this debris object will snap into a resting position on the last bounce.");
  312. addField("fade", TypeBool, Offset(fade, DebrisData),
  313. "@brief If true, this debris object will fade out when destroyed.\n\nThis fade occurs over the last second of the Debris' lifetime.\n");
  314. addField("ignoreWater", TypeBool, Offset(ignoreWater, DebrisData), "If true, this debris object will not collide with water, acting as if the water is not there.");
  315. endGroup("Behavior");
  316. // disallow some field substitutions
  317. onlyKeepClearSubstitutions("emitters"); // subs resolving to "~~", or "~0" are OK
  318. onlyKeepClearSubstitutions("explosion");
  319. Parent::initPersistFields();
  320. }
  321. void DebrisData::packData(BitStream* stream)
  322. {
  323. Parent::packData(stream);
  324. stream->write(elasticity);
  325. stream->write(friction);
  326. stream->write(numBounces);
  327. stream->write(bounceVariance);
  328. stream->write(minSpinSpeed);
  329. stream->write(maxSpinSpeed);
  330. stream->write(explodeOnMaxBounce);
  331. stream->write(staticOnMaxBounce);
  332. stream->write(snapOnMaxBounce);
  333. stream->write(lifetime);
  334. stream->write(lifetimeVariance);
  335. stream->write(velocity);
  336. stream->write(velocityVariance);
  337. stream->write(fade);
  338. stream->write(useRadiusMass);
  339. stream->write(baseRadius);
  340. stream->write(gravModifier);
  341. stream->write(terminalVelocity);
  342. stream->write(ignoreWater);
  343. stream->writeString( textureName );
  344. PACKDATA_ASSET(Shape);
  345. for( S32 i=0; i<DDC_NUM_EMITTERS; i++ )
  346. {
  347. if( stream->writeFlag( emitterList[i] != NULL ) )
  348. {
  349. stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
  350. }
  351. }
  352. if( stream->writeFlag( explosion ) )
  353. {
  354. stream->writeRangedU32(mPacked ? SimObjectId((uintptr_t)explosion):
  355. explosion->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast);
  356. }
  357. }
  358. void DebrisData::unpackData(BitStream* stream)
  359. {
  360. Parent::unpackData(stream);
  361. stream->read(&elasticity);
  362. stream->read(&friction);
  363. stream->read(&numBounces);
  364. stream->read(&bounceVariance);
  365. stream->read(&minSpinSpeed);
  366. stream->read(&maxSpinSpeed);
  367. stream->read(&explodeOnMaxBounce);
  368. stream->read(&staticOnMaxBounce);
  369. stream->read(&snapOnMaxBounce);
  370. stream->read(&lifetime);
  371. stream->read(&lifetimeVariance);
  372. stream->read(&velocity);
  373. stream->read(&velocityVariance);
  374. stream->read(&fade);
  375. stream->read(&useRadiusMass);
  376. stream->read(&baseRadius);
  377. stream->read(&gravModifier);
  378. stream->read(&terminalVelocity);
  379. stream->read(&ignoreWater);
  380. textureName = stream->readSTString();
  381. UNPACKDATA_ASSET(Shape);
  382. for( S32 i=0; i<DDC_NUM_EMITTERS; i++ )
  383. {
  384. if( stream->readFlag() )
  385. {
  386. emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
  387. }
  388. }
  389. if(stream->readFlag())
  390. {
  391. explosionId = (S32)stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
  392. }
  393. else
  394. {
  395. explosionId = 0;
  396. }
  397. }
  398. IMPLEMENT_CO_NETOBJECT_V1(Debris);
  399. ConsoleDocClass( Debris,
  400. "@brief Base debris class. Uses the DebrisData datablock for properties of individual debris objects.\n\n"
  401. "Debris is typically made up of a shape and up to two particle emitters. In most cases Debris objects are "
  402. "not created directly. They are usually produced automatically by other means, such as through the Explosion "
  403. "class. When an explosion goes off, its ExplosionData datablock determines what Debris to emit.\n"
  404. "@tsexample\n"
  405. "datablock ExplosionData(GrenadeLauncherExplosion)\n"
  406. "{\n"
  407. " // Assiging debris data\n"
  408. " debris = GrenadeDebris;\n\n"
  409. " // Adjust how debris is ejected\n"
  410. " debrisThetaMin = 10;\n"
  411. " debrisThetaMax = 60;\n"
  412. " debrisNum = 4;\n"
  413. " debrisNumVariance = 2;\n"
  414. " debrisVelocity = 25;\n"
  415. " debrisVelocityVariance = 5;\n\n"
  416. " // Note: other ExplosionData properties are not listed for this example\n"
  417. "};\n"
  418. "@endtsexample\n\n"
  419. "@note Debris are client side only objects.\n"
  420. "@see DebrisData\n"
  421. "@see ExplosionData\n"
  422. "@see Explosion\n"
  423. "@ingroup FX\n"
  424. );
  425. DefineEngineMethod(Debris, init, bool, (const char* inputPosition, const char* inputVelocity),
  426. ("1.0 1.0 1.0", "1.0 0.0 0.0"),
  427. "@brief Manually set this piece of debris at the given position with the given velocity.\n\n"
  428. "Usually you do not manually create Debris objects as they are generated through other means, "
  429. "such as an Explosion. This method exists when you do manually create a Debris object and "
  430. "want to have it start moving.\n"
  431. "@param inputPosition Position to place the debris.\n"
  432. "@param inputVelocity Velocity to move the debris after it has been placed.\n"
  433. "@return Always returns true.\n"
  434. "@tsexample\n"
  435. "// Define the position\n"
  436. "%position = \"1.0 1.0 1.0\";\n\n"
  437. "// Define the velocity\n"
  438. "%velocity = \"1.0 0.0 0.0\";\n\n"
  439. "// Inform the debris object of its new position and velocity\n"
  440. "%debris.init(%position,%velocity);\n"
  441. "@endtsexample\n")
  442. {
  443. Point3F pos;
  444. dSscanf( inputPosition, "%f %f %f", &pos.x, &pos.y, &pos.z );
  445. Point3F vel;
  446. dSscanf( inputVelocity, "%f %f %f", &vel.x, &vel.y, &vel.z );
  447. object->init( pos, vel );
  448. return true;
  449. }
  450. Debris::Debris()
  451. {
  452. mTypeMask |= DebrisObjectType | DynamicShapeObjectType;
  453. mVelocity = Point3F( 0.0f, 0.0f, 4.0f );
  454. mLifetime = gRandGen.randF( 1.0f, 10.0f );
  455. mLastPos = getPosition();
  456. mNumBounces = gRandGen.randI( 0, 1 );
  457. mSize = 2.0f;
  458. mElapsedTime = 0.0f;
  459. mShape = NULL;
  460. mPart = NULL;
  461. mDataBlock = NULL;
  462. mXRotSpeed = 0.0f;
  463. mZRotSpeed = 0.0f;
  464. mInitialTrans.identity();
  465. mRadius = 0.2f;
  466. mStatic = false;
  467. mElasticity = 0.5f;
  468. mFriction = 0.5f;
  469. dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
  470. // Only allocated client side.
  471. mNetFlags.set( IsGhost );
  472. ss_object = 0;
  473. ss_index = 0;
  474. }
  475. Debris::~Debris()
  476. {
  477. if( mShape )
  478. {
  479. delete mShape;
  480. mShape = NULL;
  481. }
  482. if( mPart )
  483. {
  484. delete mPart;
  485. mPart = NULL;
  486. }
  487. if (mDataBlock && mDataBlock->isTempClone())
  488. {
  489. delete mDataBlock;
  490. mDataBlock = 0;
  491. }
  492. }
  493. void Debris::initPersistFields()
  494. {
  495. docsURL;
  496. addGroup( "Debris" );
  497. addField( "lifetime", TypeF32, Offset(mLifetime, Debris),
  498. "@brief Length of time for this debris object to exist. When expired, the object will be deleted.\n\n"
  499. "The initial lifetime value comes from the DebrisData datablock.\n"
  500. "@see DebrisData::lifetime\n"
  501. "@see DebrisData::lifetimeVariance\n");
  502. endGroup( "Debris" );
  503. Parent::initPersistFields();
  504. }
  505. void Debris::init( const Point3F &position, const Point3F &velocity )
  506. {
  507. setPosition( position );
  508. setVelocity( velocity );
  509. }
  510. bool Debris::onNewDataBlock( GameBaseData *dptr, bool reload )
  511. {
  512. mDataBlock = dynamic_cast< DebrisData* >( dptr );
  513. if( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
  514. return false;
  515. if (mDataBlock->isTempClone())
  516. return true;
  517. scriptOnNewDataBlock();
  518. return true;
  519. }
  520. bool Debris::onAdd()
  521. {
  522. if( !Parent::onAdd() )
  523. {
  524. return false;
  525. }
  526. if( !mDataBlock )
  527. {
  528. Con::errorf("Debris::onAdd - Fail - No datablock");
  529. return false;
  530. }
  531. // create emitters
  532. for( S32 i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
  533. {
  534. if( mDataBlock->emitterList[i] != NULL )
  535. {
  536. ParticleEmitter * pEmitter = new ParticleEmitter;
  537. pEmitter->onNewDataBlock(mDataBlock->emitterList[i]->cloneAndPerformSubstitutions(ss_object, ss_index), false);
  538. if( !pEmitter->registerObject() )
  539. {
  540. Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() );
  541. delete pEmitter;
  542. pEmitter = NULL;
  543. }
  544. mEmitterList[i] = pEmitter;
  545. }
  546. }
  547. // set particle sizes based on debris size
  548. F32 sizeList[ParticleData::PDC_NUM_KEYS];
  549. if( mEmitterList[0] )
  550. {
  551. sizeList[0] = mSize * 0.5;
  552. sizeList[1] = mSize;
  553. for (U32 i = 2; i < ParticleData::PDC_NUM_KEYS; i++)
  554. sizeList[i] = mSize * 1.5;
  555. mEmitterList[0]->setSizes( sizeList );
  556. }
  557. if( mEmitterList[1] )
  558. {
  559. sizeList[0] = 0.0;
  560. sizeList[1] = mSize * 0.5;
  561. for (U32 i = 2; i < ParticleData::PDC_NUM_KEYS; i++)
  562. sizeList[i] = mSize;
  563. mEmitterList[1]->setSizes( sizeList );
  564. }
  565. S32 bounceVar = (S32)mDataBlock->bounceVariance;
  566. bounceVar = gRandGen.randI( -bounceVar, bounceVar );
  567. mNumBounces = mDataBlock->numBounces + bounceVar;
  568. F32 lifeVar = (mDataBlock->lifetimeVariance * 2.0f * gRandGen.randF(-1.0,1.0)) - mDataBlock->lifetimeVariance;
  569. mLifetime = mDataBlock->lifetime + lifeVar;
  570. F32 xRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed );
  571. F32 zRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed );
  572. zRotSpeed *= gRandGen.randF( 0.1f, 0.5f );
  573. mRotAngles.set( xRotSpeed, 0.0f, zRotSpeed );
  574. mElasticity = mDataBlock->elasticity;
  575. mFriction = mDataBlock->friction;
  576. // Setup our bounding box
  577. if( mDataBlock->mShape )
  578. {
  579. mObjBox = mDataBlock->mShape->mBounds;
  580. }
  581. else
  582. {
  583. mObjBox = Box3F(Point3F(-1, -1, -1), Point3F(1, 1, 1));
  584. }
  585. if( mDataBlock->mShape)
  586. {
  587. mShape = new TSShapeInstance( mDataBlock->mShape, true);
  588. }
  589. if( mPart )
  590. {
  591. // use half radius becuase we want debris to stick in ground
  592. mRadius = mPart->getRadius() * 0.5;
  593. mObjBox = mPart->getBounds();
  594. }
  595. resetWorldBox();
  596. mInitialTrans = getTransform();
  597. if( mDataBlock->velocity != 0.0 )
  598. {
  599. F32 velocity = mDataBlock->velocity + gRandGen.randF( -mDataBlock->velocityVariance, mDataBlock->velocityVariance );
  600. mVelocity.normalizeSafe();
  601. mVelocity *= velocity;
  602. }
  603. // mass calculations
  604. if( mDataBlock->useRadiusMass )
  605. {
  606. if( mRadius < mDataBlock->baseRadius )
  607. {
  608. mRadius = mDataBlock->baseRadius;
  609. }
  610. // linear falloff
  611. F32 multFactor = mDataBlock->baseRadius / mRadius;
  612. mElasticity *= multFactor;
  613. mFriction *= multFactor;
  614. mRotAngles *= multFactor;
  615. }
  616. // tell engine the debris exists
  617. gClientSceneGraph->addObjectToScene(this);
  618. removeFromProcessList();
  619. ClientProcessList::get()->addObject(this);
  620. NetConnection* pNC = NetConnection::getConnectionToServer();
  621. AssertFatal(pNC != NULL, "Error, must have a connection to the server!");
  622. pNC->addObject(this);
  623. return true;
  624. }
  625. void Debris::onRemove()
  626. {
  627. for( S32 i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
  628. {
  629. if( mEmitterList[i] )
  630. {
  631. mEmitterList[i]->deleteWhenEmpty();
  632. mEmitterList[i] = NULL;
  633. }
  634. }
  635. if( mPart )
  636. {
  637. TSShapeInstance *ss = mPart->getSourceShapeInstance();
  638. if( ss )
  639. {
  640. ss->decDebrisRefCount();
  641. if( ss->getDebrisRefCount() == 0 )
  642. {
  643. delete ss;
  644. }
  645. }
  646. }
  647. removeFromScene();
  648. Parent::onRemove();
  649. }
  650. void Debris::processTick(const Move*)
  651. {
  652. if (mLifetime <= 0.0)
  653. deleteObject();
  654. }
  655. void Debris::advanceTime( F32 dt )
  656. {
  657. mElapsedTime += dt;
  658. mLifetime -= dt;
  659. if( mLifetime <= 0.0 )
  660. {
  661. mLifetime = 0.0;
  662. return;
  663. }
  664. mLastPos = getPosition();
  665. if( !mStatic )
  666. {
  667. rotate( dt );
  668. Point3F nextPos = getPosition();
  669. computeNewState( nextPos, mVelocity, dt );
  670. if( bounce( nextPos, dt ) )
  671. {
  672. --mNumBounces;
  673. if( mNumBounces <= 0 )
  674. {
  675. if( mDataBlock->explodeOnMaxBounce )
  676. {
  677. explode();
  678. mLifetime = 0.0;
  679. }
  680. if( mDataBlock->snapOnMaxBounce )
  681. {
  682. // orient debris so it's flat
  683. MatrixF stat = getTransform();
  684. Point3F dir;
  685. stat.getColumn( 1, &dir );
  686. dir.z = 0.0;
  687. MatrixF newTrans = MathUtils::createOrientFromDir( dir );
  688. // hack for shell casings to get them above ground. Need something better - bramage
  689. newTrans.setPosition( getPosition() + Point3F( 0.0f, 0.0f, 0.10f ) );
  690. setTransform( newTrans );
  691. }
  692. if( mDataBlock->staticOnMaxBounce )
  693. {
  694. mStatic = true;
  695. }
  696. }
  697. }
  698. else
  699. {
  700. setPosition( nextPos );
  701. }
  702. }
  703. Point3F pos( getPosition( ) );
  704. updateEmitters( pos, mVelocity, (U32)(dt * 1000.0));
  705. }
  706. void Debris::rotate( F32 dt )
  707. {
  708. MatrixF curTrans = getTransform();
  709. curTrans.setPosition( Point3F(0.0f, 0.0f, 0.0f) );
  710. Point3F curAngles = mRotAngles * dt * M_PI_F/180.0f;
  711. MatrixF rotMatrix( EulerF( curAngles.x, curAngles.y, curAngles.z ) );
  712. curTrans.mul( rotMatrix );
  713. curTrans.setPosition( getPosition() );
  714. setTransform( curTrans );
  715. }
  716. bool Debris::bounce( const Point3F &nextPos, F32 dt )
  717. {
  718. Point3F curPos = getPosition();
  719. Point3F dir = nextPos - curPos;
  720. if( dir.magnitudeSafe() == 0.0f ) return false;
  721. dir.normalizeSafe();
  722. Point3F extent = nextPos + dir * mRadius;
  723. F32 totalDist = Point3F( extent - curPos ).magnitudeSafe();
  724. F32 moveDist = Point3F( nextPos - curPos ).magnitudeSafe();
  725. F32 movePercent = (moveDist / totalDist);
  726. RayInfo rayInfo;
  727. U32 collisionMask = csmStaticCollisionMask;
  728. if( !mDataBlock->ignoreWater )
  729. {
  730. collisionMask |= WaterObjectType;
  731. }
  732. if( getContainer()->castRay( curPos, extent, collisionMask, &rayInfo ) )
  733. {
  734. Point3F reflection = mVelocity - rayInfo.normal * (mDot( mVelocity, rayInfo.normal ) * 2.0f);
  735. mVelocity = reflection;
  736. Point3F tangent = reflection - rayInfo.normal * mDot( reflection, rayInfo.normal );
  737. mVelocity -= tangent * mFriction;
  738. Point3F velDir = mVelocity;
  739. velDir.normalizeSafe();
  740. mVelocity *= mElasticity;
  741. Point3F bouncePos = curPos + dir * rayInfo.t * movePercent;
  742. bouncePos += mVelocity * dt;
  743. setPosition( bouncePos );
  744. mRotAngles *= mElasticity;
  745. return true;
  746. }
  747. return false;
  748. }
  749. void Debris::explode()
  750. {
  751. if( !mDataBlock->explosion ) return;
  752. Point3F explosionPos = getPosition();
  753. Explosion* pExplosion = new Explosion;
  754. pExplosion->setSubstitutionData(ss_object, ss_index);
  755. pExplosion->onNewDataBlock(mDataBlock->explosion->cloneAndPerformSubstitutions(ss_object, ss_index), false);
  756. MatrixF trans( true );
  757. trans.setPosition( getPosition() );
  758. pExplosion->setTransform( trans );
  759. pExplosion->setInitialState( explosionPos, VectorF(0,0,1), 1);
  760. if (!pExplosion->registerObject())
  761. delete pExplosion;
  762. }
  763. void Debris::computeNewState( Point3F &newPos, Point3F &newVel, F32 dt )
  764. {
  765. // apply gravity
  766. Point3F force = Point3F(0, 0, -9.81 * mDataBlock->gravModifier );
  767. if( mDataBlock->terminalVelocity > 0.0001 )
  768. {
  769. if( newVel.magnitudeSafe() > mDataBlock->terminalVelocity )
  770. {
  771. newVel.normalizeSafe();
  772. newVel *= mDataBlock->terminalVelocity;
  773. }
  774. else
  775. {
  776. newVel += force * dt;
  777. }
  778. }
  779. else
  780. {
  781. newVel += force * dt;
  782. }
  783. newPos += newVel * dt;
  784. }
  785. void Debris::updateEmitters( Point3F &pos, Point3F &vel, U32 ms )
  786. {
  787. Point3F axis = -vel;
  788. if( axis.magnitudeSafe() == 0.0 )
  789. {
  790. axis = Point3F( 0.0, 0.0, 1.0 );
  791. }
  792. axis.normalizeSafe();
  793. Point3F lastPos = mLastPos;
  794. for( S32 i=0; i<DebrisData::DDC_NUM_EMITTERS; i++ )
  795. {
  796. if( mEmitterList[i] )
  797. {
  798. mEmitterList[i]->emitParticles( lastPos, pos, axis, vel, ms );
  799. }
  800. }
  801. }
  802. void Debris::prepRenderImage( SceneRenderState *state )
  803. {
  804. if( !mPart && !mShape )
  805. return;
  806. Point3F cameraOffset;
  807. mObjToWorld.getColumn(3,&cameraOffset);
  808. cameraOffset -= state->getDiffuseCameraPosition();
  809. F32 dist = cameraOffset.len();
  810. F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z));
  811. if( mShape )
  812. {
  813. mShape->setDetailFromDistance( state, dist * invScale );
  814. if( mShape->getCurrentDetail() < 0 )
  815. return;
  816. }
  817. if( mPart )
  818. {
  819. // get the shapeInstance that we are using for the debris parts
  820. TSShapeInstance *si = mPart->getSourceShapeInstance();
  821. if ( si )
  822. si->setDetailFromDistance( state, dist * invScale );
  823. }
  824. prepBatchRender( state );
  825. }
  826. void Debris::prepBatchRender( SceneRenderState *state )
  827. {
  828. if ( !mShape && !mPart )
  829. return;
  830. GFXTransformSaver saver;
  831. F32 alpha = 1.0;
  832. if( mDataBlock->fade )
  833. {
  834. if( mLifetime < 1.0 ) alpha = mLifetime;
  835. }
  836. Point3F cameraOffset;
  837. mObjToWorld.getColumn(3,&cameraOffset);
  838. cameraOffset -= state->getCameraPosition();
  839. // Set up our TS render state.
  840. TSRenderState rdata;
  841. rdata.setSceneState( state );
  842. // We might have some forward lit materials
  843. // so pass down a query to gather lights.
  844. LightQuery query;
  845. query.init( getWorldSphere() );
  846. rdata.setLightQuery( &query );
  847. if( mShape )
  848. {
  849. MatrixF mat = getRenderTransform();
  850. GFX->setWorldMatrix( mat );
  851. rdata.setFadeOverride( alpha );
  852. mShape->render( rdata );
  853. }
  854. else
  855. {
  856. if (mPart->getCurrentObjectDetail() != -1)
  857. {
  858. MatrixF mat = getRenderTransform();
  859. GFX->setWorldMatrix( mat );
  860. rdata.setFadeOverride( alpha );
  861. mPart->render( rdata );
  862. }
  863. }
  864. }
  865. void Debris::setSize( F32 size )
  866. {
  867. mSize = size;
  868. }