2
0

ParticlePlayer.cc 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 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. #include "2d/sceneobject/particlePlayer.h"
  23. // Script bindings.
  24. #include "2d/sceneobject/particlePlayer_ScriptBinding.h"
  25. //------------------------------------------------------------------------------
  26. ParticleSystem::ParticleNode* ParticlePlayer::EmitterNode::createParticle( void )
  27. {
  28. // Sanity!
  29. AssertFatal( mOwner != NULL, "ParticlePlayer::EmitterNode::createParticle() - Cannot create a particle with a NULL owner." );
  30. // Fetch a free node,
  31. ParticleSystem::ParticleNode* pFreeParticleNode = ParticleSystem::Instance->createParticle();
  32. // Insert node into emitter chain.
  33. pFreeParticleNode->mNextNode = mParticleNodeHead.mNextNode;
  34. pFreeParticleNode->mPreviousNode = &mParticleNodeHead;
  35. mParticleNodeHead.mNextNode = pFreeParticleNode;
  36. pFreeParticleNode->mNextNode->mPreviousNode = pFreeParticleNode;
  37. // Configure the node.
  38. mOwner->configureParticle( this, pFreeParticleNode );
  39. return pFreeParticleNode;
  40. }
  41. //------------------------------------------------------------------------------
  42. void ParticlePlayer::EmitterNode::freeParticle( ParticleSystem::ParticleNode* pParticleNode )
  43. {
  44. // Sanity!
  45. AssertFatal( mOwner != NULL, "ParticlePlayer::EmitterNode::freeParticle() - Cannot free a particle with a NULL owner." );
  46. // Remove the node from the emitter chain.
  47. pParticleNode->mPreviousNode->mNextNode = pParticleNode->mNextNode;
  48. pParticleNode->mNextNode->mPreviousNode = pParticleNode->mPreviousNode;
  49. // Free the node.
  50. ParticleSystem::Instance->freeParticle( pParticleNode );
  51. }
  52. //------------------------------------------------------------------------------
  53. void ParticlePlayer::EmitterNode::freeAllParticles( void )
  54. {
  55. // Sanity!
  56. AssertFatal( mOwner != NULL, "ParticlePlayer::EmitterNode::freeAllParticles() - Cannot free all particles with a NULL owner." );
  57. // Free all the nodes,
  58. while( mParticleNodeHead.mNextNode != &mParticleNodeHead )
  59. {
  60. freeParticle( mParticleNodeHead.mNextNode );
  61. }
  62. }
  63. //------------------------------------------------------------------------------
  64. IMPLEMENT_CONOBJECT(ParticlePlayer);
  65. //------------------------------------------------------------------------------
  66. ParticlePlayer::ParticlePlayer() :
  67. mPlaying( false ),
  68. mPaused( false ),
  69. mAge( 0.0f ),
  70. mParticleInterpolation( false ),
  71. mCameraIdleDistance( 0.0f ),
  72. mCameraIdle( false ),
  73. mWaitingForParticles( false ),
  74. mWaitingForDelete( false )
  75. {
  76. // Fetch the particle player scales.
  77. mEmissionRateScale = Con::getFloatVariable( PARTICLE_PLAYER_EMISSION_RATE_SCALE, 1.0f );
  78. mSizeScale = Con::getFloatVariable( PARTICLE_PLAYER_SIZE_SCALE, 1.0f );
  79. mForceScale = Con::getFloatVariable( PARTICLE_PLAYER_FORCE_SCALE, 1.0f );
  80. // Register for refresh notifications.
  81. mParticleAsset.registerRefreshNotify( this );
  82. }
  83. //------------------------------------------------------------------------------
  84. ParticlePlayer::~ParticlePlayer()
  85. {
  86. }
  87. //------------------------------------------------------------------------------
  88. void ParticlePlayer::initPersistFields()
  89. {
  90. // Call parent.
  91. Parent::initPersistFields();
  92. addProtectedField( "Particle", TypeParticleAssetPtr, Offset(mParticleAsset, ParticlePlayer), &setParticle, &defaultProtectedGetFn, defaultProtectedWriteFn, "" );
  93. addProtectedField( "CameraIdleDistance", TypeF32, Offset(mCameraIdleDistance, ParticlePlayer),&defaultProtectedSetFn, &defaultProtectedGetFn, &writeCameraIdleDistance,"" );
  94. addProtectedField( "ParticleInterpolation", TypeBool, Offset(mParticleInterpolation, ParticlePlayer), &defaultProtectedSetFn, &defaultProtectedGetFn, &writeParticleInterpolation,"" );
  95. addProtectedField( "EmissionRateScale", TypeF32, Offset(mEmissionRateScale, ParticlePlayer), &defaultProtectedSetFn, &defaultProtectedGetFn, &writeEmissionRateScale, "" );
  96. addProtectedField( "SizeScale", TypeF32, Offset(mSizeScale, ParticlePlayer), &defaultProtectedSetFn, &defaultProtectedGetFn, &writeSizeScale, "" );
  97. addProtectedField( "ForceScale", TypeF32, Offset(mForceScale, ParticlePlayer), &defaultProtectedSetFn, &defaultProtectedGetFn, &writeForceScale, "" );
  98. }
  99. //------------------------------------------------------------------------------
  100. void ParticlePlayer::copyTo(SimObject* object)
  101. {
  102. // Fetch particle asset object.
  103. ParticlePlayer* pParticlePlayer = static_cast<ParticlePlayer*>( object );
  104. // Sanity!
  105. AssertFatal( pParticlePlayer != NULL, "ParticlePlayer::copyTo() - Object is not the correct type.");
  106. // Copy parent.
  107. Parent::copyTo( object );
  108. // Copy the fields.
  109. pParticlePlayer->setParticle( getParticle() );
  110. pParticlePlayer->setCameraIdleDistance( getCameraIdleDistance() );
  111. pParticlePlayer->setParticleInterpolation( getParticleInterpolation() );
  112. pParticlePlayer->setEmissionRateScale( getEmissionRateScale() );
  113. pParticlePlayer->setSizeScale( getSizeScale() );
  114. pParticlePlayer->setForceScale( getForceScale() );
  115. }
  116. //------------------------------------------------------------------------------
  117. void ParticlePlayer::onAssetRefreshed( AssetPtrBase* pAssetPtrBase )
  118. {
  119. // Initialize the particle asset.
  120. initializeParticleAsset();
  121. }
  122. //-----------------------------------------------------------------------------
  123. void ParticlePlayer::safeDelete( void )
  124. {
  125. // Finish if we already waiting for delete.
  126. if ( mWaitingForDelete )
  127. return;
  128. // Is the player plating?
  129. if ( mPlaying )
  130. {
  131. // Yes, so stop playing and allow it to kill itself.
  132. stop(true, true);
  133. return;
  134. }
  135. // Call parent which will deal with the deletion.
  136. Parent::safeDelete();
  137. }
  138. //-----------------------------------------------------------------------------
  139. void ParticlePlayer::OnRegisterScene( Scene* pScene )
  140. {
  141. // Call parent.
  142. Parent::OnRegisterScene( pScene );
  143. // Add always in scope.
  144. pScene->getWorldQuery()->addAlwaysInScope( this );
  145. // Play the the particles if appropriate.
  146. if ( mParticleAsset.notNull() && mParticleAsset->getEmitterCount() > 0 )
  147. play( true );
  148. }
  149. //-----------------------------------------------------------------------------
  150. void ParticlePlayer::OnUnregisterScene( Scene* pScene )
  151. {
  152. // Remove always in scope.
  153. pScene->getWorldQuery()->removeAlwaysInScope( this );
  154. // Call parent.
  155. Parent::OnUnregisterScene( pScene );
  156. }
  157. //-----------------------------------------------------------------------------
  158. void ParticlePlayer::preIntegrate( const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
  159. {
  160. // Call Parent.
  161. Parent::preIntegrate( totalTime, elapsedTime, pDebugStats );
  162. // Finish if the camera idle distance is zero.
  163. if ( mIsZero(mCameraIdleDistance) || !validRender() )
  164. return;
  165. // Fetch current position.
  166. const Vector2 position = getPosition();
  167. // Calculate camera idle distance squared.
  168. const F32 cameraIdleDistanceSqr = mCameraIdleDistance * mCameraIdleDistance;
  169. // Fetch scene windows.
  170. SimSet& sceneWindows = getScene()->getAttachedSceneWindows();
  171. // Find a scene window that stops the pause.
  172. for( SimSet::iterator itr = sceneWindows.begin(); itr != sceneWindows.end(); itr++ )
  173. {
  174. // Fetch the scene window.
  175. SceneWindow* pSceneWindow = static_cast<SceneWindow*>(*itr);
  176. // Are we within the camera distance?
  177. if ( (pSceneWindow->getCameraPosition() - position).LengthSquared() < cameraIdleDistanceSqr )
  178. {
  179. // Yes, so play.
  180. if ( !getIsPlaying() )
  181. play( true );
  182. // Flag as not camera idle.
  183. mCameraIdle = false;
  184. return;
  185. }
  186. }
  187. // If playing then stop.
  188. if ( getIsPlaying() )
  189. stop( false, false );
  190. // Flag as camera idle.
  191. mCameraIdle = true;
  192. }
  193. //------------------------------------------------------------------------------
  194. void ParticlePlayer::integrateObject( const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
  195. {
  196. // Call parent.
  197. Parent::integrateObject( totalTime, elapsedTime, pDebugStats );
  198. // Finish if no need to integrate.
  199. if ( !mPlaying ||
  200. mPaused ||
  201. mEmitters.size() == 0 )
  202. return;
  203. // Fetch particle asset.
  204. ParticleAsset* pParticleAsset = mParticleAsset;
  205. // Finish if no particle asset assigned.
  206. if ( pParticleAsset == NULL )
  207. return;
  208. // Reset active particle count.
  209. U32 activeParticleCount = 0;
  210. // Is the camera idle?
  211. if ( !mCameraIdle )
  212. {
  213. // No, so update the particle player age.
  214. mAge += elapsedTime;
  215. // Iterate the emitters.
  216. for( typeEmitterVector::iterator emitterItr = mEmitters.begin(); emitterItr != mEmitters.end(); ++emitterItr )
  217. {
  218. // Fetch the emitter node.
  219. EmitterNode* pEmitterNode = *emitterItr;
  220. // Fetch the asset emitter.
  221. ParticleAssetEmitter* pParticleAssetEmitter = pEmitterNode->getAssetEmitter();
  222. // Fetch the first particle node.
  223. ParticleSystem::ParticleNode* pParticleNode = pEmitterNode->getFirstParticle();
  224. // Fetch the particle node head.
  225. ParticleSystem::ParticleNode* pParticleNodeHead = pEmitterNode->getParticleNodeHead();
  226. // Process All particle nodes.
  227. while ( pParticleNode != pParticleNodeHead )
  228. {
  229. // Update the particle age.
  230. pParticleNode->mParticleAge += elapsedTime;
  231. // Has the particle expired?
  232. // NOTE:- If we're in single-particle mode then the particle lives as long as the particle player does.
  233. if ( ( !pParticleAssetEmitter->getSingleParticle() && pParticleNode->mParticleAge > pParticleNode->mParticleLifetime ) ||
  234. ( mIsZero(pParticleNode->mParticleLifetime) ) )
  235. {
  236. // Yes, so fetch next particle before we kill it.
  237. pParticleNode = pParticleNode->mNextNode;
  238. // Kill the particle.
  239. // NOTE:- Because we move to the next particle the particle to kill is now the previous!
  240. pEmitterNode->freeParticle( pParticleNode->mPreviousNode );
  241. }
  242. else
  243. {
  244. // No, so integrate the particle.
  245. integrateParticle( pEmitterNode, pParticleNode, pParticleNode->mParticleAge / pParticleNode->mParticleLifetime, elapsedTime );
  246. // Move to the next particle node.
  247. pParticleNode = pParticleNode->mNextNode;
  248. // Only count particles when not in single-particle mode.
  249. activeParticleCount++;
  250. }
  251. };
  252. // Skip generating new particles if the emitter is paused.
  253. if ( pEmitterNode->getPaused() )
  254. continue;
  255. // Are we in single-particle mode?
  256. if ( pParticleAssetEmitter->getSingleParticle() )
  257. {
  258. // Yes, so do we have a single particle yet?
  259. if ( pParticleNodeHead->mNextNode == pParticleNodeHead )
  260. {
  261. // No, so generate a single particle.
  262. pEmitterNode->createParticle();
  263. }
  264. }
  265. else
  266. {
  267. // Accumulate the last generation time as we need to handle very small time-integrations correctly.
  268. //
  269. // NOTE: We need to do this if there's an emission target but the time-integration is so small
  270. // that rounding results in no emission. Downside to good FPS!
  271. pEmitterNode->setTimeSinceLastGeneration( pEmitterNode->getTimeSinceLastGeneration() + elapsedTime );
  272. // Fetch the particle player age.
  273. const F32 particlePlayerAge = mAge;
  274. // Fetch the quantity base and variation fields.
  275. const ParticleAssetField& quantityBaseField = pParticleAssetEmitter->getQuantityBaseField();
  276. const ParticleAssetField& quantityVaritationField = pParticleAssetEmitter->getQuantityBaseField();
  277. // Fetch the emissions.
  278. const F32 baseEmission = quantityBaseField.getFieldValue( particlePlayerAge );
  279. const F32 varEmission = quantityVaritationField.getFieldValue( particlePlayerAge ) * 0.5f;
  280. // Fetch the emission scale.
  281. const F32 effectEmission = pParticleAsset->getQuantityScaleField().getFieldValue( particlePlayerAge ) * getEmissionRateScale();
  282. // Calculate the local emission.
  283. const F32 localEmission = mClampF( (baseEmission + CoreMath::mGetRandomF(-varEmission, varEmission)) * effectEmission,
  284. quantityBaseField.getMinValue(),
  285. quantityBaseField.getMaxValue() );
  286. // Calculate the final time-independent emission count.
  287. const U32 emissionCount = U32(mFloor( localEmission * pEmitterNode->getTimeSinceLastGeneration() ));
  288. // Do we have an emission?
  289. if ( emissionCount > 0 )
  290. {
  291. // Yes, so remove this emission from accumulated time.
  292. pEmitterNode->setTimeSinceLastGeneration( getMax(0.0f, pEmitterNode->getTimeSinceLastGeneration() - (emissionCount / localEmission) ) );
  293. // Suppress Precision Errors.
  294. if ( mIsZero( pEmitterNode->getTimeSinceLastGeneration() ) )
  295. pEmitterNode->setTimeSinceLastGeneration( 0.0f );
  296. // Generate the required emission.
  297. for ( U32 n = 0; n < emissionCount; n++ )
  298. pEmitterNode->createParticle();
  299. }
  300. }
  301. }
  302. }
  303. // Fetch the particle life-mode.
  304. const ParticleAsset::LifeMode lifeMode = pParticleAsset->getLifeMode();
  305. // Finish if the particle player is in "infinite" mode.
  306. if ( lifeMode == ParticleAsset::INFINITE )
  307. return;
  308. // Are we waiting for particles and there are non left?
  309. if ( mWaitingForParticles )
  310. {
  311. // Yes, so are there any particles left?
  312. if ( activeParticleCount == 0 )
  313. {
  314. // No, so stop the player immediately.
  315. stop( false, mWaitingForDelete );
  316. }
  317. return;
  318. }
  319. // Fetch the particle lifetime.
  320. const F32 lifetime = pParticleAsset->getLifetime();
  321. // Cycle life-mode?
  322. if ( lifeMode == ParticleAsset::CYCLE )
  323. {
  324. // Has the age expired?
  325. if ( mAge >= lifetime )
  326. {
  327. // Yes, so restart the particle.
  328. play( false );
  329. }
  330. return;
  331. }
  332. // Stop life-mode?
  333. if ( lifeMode == ParticleAsset::STOP )
  334. {
  335. // Has the age expired?
  336. if ( mAge >= lifetime )
  337. {
  338. // Yes, so stop the particle.
  339. stop( true, false );
  340. }
  341. return;
  342. }
  343. // kill life-mode?
  344. if ( lifeMode == ParticleAsset::KILL )
  345. {
  346. // Has the age expired?
  347. if ( mAge >= lifetime )
  348. {
  349. // Yes, so kill the particle.
  350. stop( true, true );
  351. }
  352. return;
  353. }
  354. }
  355. //------------------------------------------------------------------------------
  356. void ParticlePlayer::interpolateObject( const F32 timeDelta )
  357. {
  358. // Call parent.
  359. Parent::interpolateObject( timeDelta );
  360. // Finish if no need to interpolate.
  361. if ( !mParticleInterpolation || !mPlaying || mCameraIdle || mPaused )
  362. return;
  363. // Iterate the emitters.
  364. for( typeEmitterVector::iterator emitterItr = mEmitters.begin(); emitterItr != mEmitters.end(); ++emitterItr )
  365. {
  366. // Fetch the emitter node.
  367. EmitterNode* pEmitterNode = *emitterItr;
  368. // Fetch First Particle Node.
  369. ParticleSystem::ParticleNode* pParticleNode = pEmitterNode->getFirstParticle();
  370. // Fetch the particle node head.
  371. ParticleSystem::ParticleNode* pParticleNodeHead = pEmitterNode->getParticleNodeHead();
  372. // Fetch the asset emitter.
  373. ParticleAssetEmitter* pParticleAssetEmitter = pEmitterNode->getAssetEmitter();
  374. // Fetch the local AABB..
  375. const Vector2& localAABB0 = pParticleAssetEmitter->getLocalPivotAABB0();
  376. const Vector2& localAABB1 = pParticleAssetEmitter->getLocalPivotAABB1();
  377. const Vector2& localAABB2 = pParticleAssetEmitter->getLocalPivotAABB2();
  378. const Vector2& localAABB3 = pParticleAssetEmitter->getLocalPivotAABB3();
  379. // Process All particle nodes.
  380. while ( pParticleNode != pParticleNodeHead )
  381. {
  382. // Interpolate the position.
  383. pParticleNode->mRenderTickPosition = (timeDelta * pParticleNode->mPreTickPosition) + ((1.0f-timeDelta) * pParticleNode->mPostTickPosition);
  384. // Set the transform.
  385. pParticleNode->mTransform.p = pParticleNode->mRenderTickPosition;
  386. // Fetch the render size.
  387. const Vector2& renderSize = pParticleNode->mRenderSize;
  388. // Calculate the scaled AABB.
  389. Vector2 scaledAABB[4];
  390. scaledAABB[0] = localAABB0 * renderSize;
  391. scaledAABB[1] = localAABB1 * renderSize;
  392. scaledAABB[2] = localAABB2 * renderSize;
  393. scaledAABB[3] = localAABB3 * renderSize;
  394. // Calculate the world OOBB..
  395. CoreMath::mCalculateOOBB( scaledAABB, pParticleNode->mTransform, pParticleNode->mRenderOOBB );
  396. // Move to the next particle.
  397. pParticleNode = pParticleNode->mNextNode;
  398. }
  399. }
  400. }
  401. //-----------------------------------------------------------------------------
  402. void ParticlePlayer::sceneRender( const SceneRenderState* pSceneRenderState, const SceneRenderRequest* pSceneRenderRequest, BatchRender* pBatchRenderer )
  403. {
  404. // Finish if we can't render.
  405. if ( !mPlaying || mCameraIdle )
  406. return;
  407. // Flush.
  408. pBatchRenderer->flush( getScene()->getDebugStats().batchIsolatedFlush );
  409. // Fetch emitter count.
  410. const U32 emitterCount = mEmitters.size();
  411. // Render all the emitters.
  412. for ( U32 emitterIndex = 0; emitterIndex < emitterCount; ++emitterIndex )
  413. {
  414. // Fetch the emitter node.
  415. EmitterNode* pEmitterNode = mEmitters[emitterIndex];
  416. // Fetch the particle emitter.
  417. ParticleAssetEmitter* pParticleAssetEmitter = pEmitterNode->getAssetEmitter();
  418. // Skip if the emitter is not visible.
  419. if ( !pEmitterNode->getVisible() )
  420. continue;
  421. // Skip if there are no active particles.
  422. if ( !pEmitterNode->getActiveParticles() )
  423. continue;
  424. // Fetch both image and animation assets.
  425. const AssetPtr<ImageAsset>& imageAsset = pParticleAssetEmitter->getImageAsset();
  426. const AssetPtr<AnimationAsset>& animationAsset = pParticleAssetEmitter->getAnimationAsset();
  427. // Fetch static mode.
  428. const bool isStaticMode = pParticleAssetEmitter->isStaticMode();
  429. // Are we in static mode?
  430. if ( isStaticMode )
  431. {
  432. // Yes, so skip if no image available.
  433. if ( imageAsset.isNull() )
  434. continue;
  435. }
  436. else
  437. {
  438. // No, so skip if no animation available.
  439. if ( animationAsset.isNull() )
  440. continue;
  441. }
  442. // Flush.
  443. pBatchRenderer->flush( getScene()->getDebugStats().batchIsolatedFlush );
  444. // Intense particles?
  445. if ( pParticleAssetEmitter->getIntenseParticles() )
  446. {
  447. // Yes, so set additive blending.
  448. pBatchRenderer->setBlendMode( GL_SRC_ALPHA, GL_ONE );
  449. }
  450. else
  451. {
  452. // No, so set standard blend options.
  453. if ( mBlendMode )
  454. {
  455. pBatchRenderer->setBlendMode( mSrcBlendFactor, mDstBlendFactor );
  456. }
  457. else
  458. {
  459. pBatchRenderer->setBlendOff();
  460. }
  461. }
  462. // Set alpha-testing.
  463. pBatchRenderer->setAlphaTestMode( pParticleAssetEmitter->getAlphaTest() );
  464. // Save the transformation.
  465. glPushMatrix();
  466. // Is the Position attached to the emitter?
  467. if ( pParticleAssetEmitter->getAttachPositionToEmitter() )
  468. {
  469. // Yes, so get player position.
  470. const Vector2 renderPosition = getRenderPosition();
  471. // Move into emitter-space.
  472. glTranslatef( renderPosition.x, renderPosition.y, 0.0f );
  473. // Is the rotation attached to the emitter?
  474. if ( pParticleAssetEmitter->getAttachRotationToEmitter() )
  475. {
  476. // Yes, so rotate into emitter-space.
  477. // NOTE:- We need clockwise rotation here.
  478. glRotatef( mRadToDeg(getRenderAngle()), 0.0f, 0.0f, 1.0f );
  479. }
  480. }
  481. // Frame texture.
  482. TextureHandle frameTexture;
  483. // Frame area.
  484. ImageAsset::FrameArea::TexelArea texelFrameArea;
  485. // Are we in static mode?
  486. if ( isStaticMode )
  487. {
  488. // Yes, so fetch the frame texture.
  489. frameTexture = imageAsset->getImageTexture();
  490. // Are we using a random image frame?
  491. if ( !pParticleAssetEmitter->getRandomImageFrame() )
  492. {
  493. // No, so fetch frame area.
  494. texelFrameArea = imageAsset->getImageFrameArea( pParticleAssetEmitter->getImageFrame() ).mTexelArea;
  495. }
  496. }
  497. // Fetch the oldest-in-front flag.
  498. const bool oldestInFront = pParticleAssetEmitter->getOldestInFront();
  499. // Fetch the starting particle (using appropriate particle order).
  500. ParticleSystem::ParticleNode* pParticleNode = oldestInFront ? pEmitterNode->getFirstParticle() : pEmitterNode->getLastParticle();
  501. // Fetch the particle node head.
  502. ParticleSystem::ParticleNode* pParticleNodeHead = pEmitterNode->getParticleNodeHead();
  503. // Process All particle nodes.
  504. while ( pParticleNode != pParticleNodeHead )
  505. {
  506. // Are we in static mode are using a random image frame?
  507. if ( isStaticMode && pParticleAssetEmitter->getRandomImageFrame() )
  508. {
  509. // Yes, so fetch frame area.
  510. texelFrameArea = imageAsset->getImageFrameArea( pParticleNode->mImageFrame ).mTexelArea;
  511. }
  512. // Are we using an animation?
  513. if ( !isStaticMode )
  514. {
  515. // Yes, so fetch current frame area.
  516. texelFrameArea = pParticleNode->mAnimationController.getCurrentImageFrameArea().mTexelArea;
  517. frameTexture = pParticleNode->mAnimationController.getImageTexture();
  518. }
  519. // Fetch the particle render OOBB.
  520. Vector2* renderOOBB = pParticleNode->mRenderOOBB;
  521. // Fetch lower/upper texture coordinates.
  522. const Vector2& texLower = texelFrameArea.mTexelLower;
  523. const Vector2& texUpper = texelFrameArea.mTexelUpper;
  524. // Submit batched quad.
  525. pBatchRenderer->SubmitQuad(
  526. renderOOBB[0],
  527. renderOOBB[1],
  528. renderOOBB[2],
  529. renderOOBB[3],
  530. Vector2( texLower.x, texUpper.y ),
  531. Vector2( texUpper.x, texUpper.y ),
  532. Vector2( texUpper.x, texLower.y ),
  533. Vector2( texLower.x, texLower.y ),
  534. frameTexture,
  535. pParticleNode->mColor );
  536. // Move to next Particle ( using appropriate sort-order ).
  537. pParticleNode = oldestInFront ? pParticleNode->mNextNode : pParticleNode->mPreviousNode;
  538. };
  539. // Flush.
  540. pBatchRenderer->flush( getScene()->getDebugStats().batchIsolatedFlush );
  541. // Restore the transformation.
  542. glPopMatrix();
  543. }
  544. }
  545. //-----------------------------------------------------------------------------
  546. void ParticlePlayer::sceneRenderOverlay( const SceneRenderState* sceneRenderState )
  547. {
  548. // Call parent.
  549. Parent::sceneRenderOverlay( sceneRenderState );
  550. // Get Scene.
  551. Scene* pScene = getScene();
  552. // Finish if no scene.
  553. if ( !pScene )
  554. return;
  555. // Finish if we shouldn't be drawing the debug overlay.
  556. if ( !pScene->getIsEditorScene() || mLessThanOrEqual( mCameraIdleDistance, 0.0f ) || !isEnabled() || !getVisible() )
  557. return;
  558. // Draw camera pause distance.
  559. pScene->mDebugDraw.DrawCircle( getRenderPosition(), mCameraIdleDistance, b2Color(1.0f, 1.0f, 0.0f ) );
  560. }
  561. //-----------------------------------------------------------------------------
  562. void ParticlePlayer::setParticle( const char* pAssetId )
  563. {
  564. // Sanity!
  565. AssertFatal( pAssetId != NULL, "ParticlePlayer::setParticle() - Cannot use a NULL asset Id." );
  566. // Set asset Id.
  567. mParticleAsset = pAssetId;
  568. // Initialize the particle.
  569. initializeParticleAsset();
  570. }
  571. //-----------------------------------------------------------------------------
  572. void ParticlePlayer::setEmitterPaused( const bool paused, const U32 emitterIndex )
  573. {
  574. // Is the emitter index valid?
  575. if ( emitterIndex >= getEmitterCount() )
  576. {
  577. // No, so warn.
  578. Con::warnf( "ParticlePlayer::setEmitterPaused() - Emitter index is out of bounds." );
  579. return;
  580. }
  581. mEmitters[emitterIndex]->setPaused( paused );
  582. }
  583. //-----------------------------------------------------------------------------
  584. bool ParticlePlayer::getEmitterPaused( const U32 emitterIndex )
  585. {
  586. // Is the emitter index valid?
  587. if ( emitterIndex >= getEmitterCount() )
  588. {
  589. // No, so warn.
  590. Con::warnf( "ParticlePlayer::getEmitterPaused() - Emitter index is out of bounds." );
  591. return false;
  592. }
  593. return mEmitters[emitterIndex]->getPaused();
  594. }
  595. //-----------------------------------------------------------------------------
  596. void ParticlePlayer::setEmitterVisible( const bool visible, const U32 emitterIndex )
  597. {
  598. // Is the emitter index valid?
  599. if ( emitterIndex >= getEmitterCount() )
  600. {
  601. // No, so warn.
  602. Con::warnf( "ParticlePlayer::setEmitterVisible() - Emitter index is out of bounds." );
  603. return;
  604. }
  605. mEmitters[emitterIndex]->setVisible( visible );
  606. }
  607. //-----------------------------------------------------------------------------
  608. bool ParticlePlayer::getEmitterVisible( const U32 emitterIndex )
  609. {
  610. // Is the emitter index valid?
  611. if ( emitterIndex >= getEmitterCount() )
  612. {
  613. // No, so warn.
  614. Con::warnf( "ParticlePlayer::getEmitterVisible() - Emitter index is out of bounds." );
  615. return false;
  616. }
  617. return mEmitters[emitterIndex]->getVisible();
  618. }
  619. //-----------------------------------------------------------------------------
  620. bool ParticlePlayer::play( const bool resetParticles )
  621. {
  622. // Cannot do anything if we've not got any emitters!
  623. if ( mParticleAsset.isNull() || mParticleAsset->getEmitterCount() == 0 )
  624. {
  625. // Warn.
  626. Con::warnf("ParticlePlayer::play() - Cannot play; no emitters!");
  627. return false;
  628. }
  629. // Are we in a scene?
  630. if ( getScene() == NULL )
  631. {
  632. // No, so warn.
  633. Con::warnf("ParticlePlayer::play() - Cannot play when not in a scene!");
  634. return false;
  635. }
  636. // Reset the age.
  637. mAge = 0.0f;
  638. // Iterate the emitters.
  639. for( typeEmitterVector::iterator emitterItr = mEmitters.begin(); emitterItr != mEmitters.end(); ++emitterItr )
  640. {
  641. // Fetch the emitter node.
  642. EmitterNode* pEmitterNode = *emitterItr;
  643. // Reset the time since last generation.
  644. pEmitterNode->setTimeSinceLastGeneration( 0.0f );
  645. }
  646. // Reset Waiting for Particles.
  647. mWaitingForParticles = false;
  648. // Reset Waiting for delete.
  649. mWaitingForDelete = false;
  650. // Flag as playing.
  651. mPlaying = true;
  652. // Turn-off paused.
  653. mPaused = false;
  654. // Set unsafe delete status.
  655. setSafeDelete(false);
  656. return true;
  657. }
  658. //-----------------------------------------------------------------------------
  659. void ParticlePlayer::stop( const bool waitForParticles, const bool killEffect )
  660. {
  661. // Finish if we're not playing and there's no kill command.
  662. if ( !mPlaying && !killEffect )
  663. return;
  664. // Fetch emitter count.
  665. const U32 emitterCount = mEmitters.size();
  666. // Are we waiting for particles to end?
  667. if ( waitForParticles )
  668. {
  669. // Yes, so pause all the emitters.
  670. for ( U32 emitterIndex = 0; emitterIndex < emitterCount; ++emitterIndex )
  671. {
  672. // Fetch the emitter.
  673. mEmitters[emitterIndex]->setPaused( true );
  674. }
  675. // Set waiting for particles.
  676. mWaitingForParticles = true;
  677. // Flag as waiting for deletion if killing effect.
  678. if ( killEffect )
  679. mWaitingForDelete = true;
  680. return;
  681. }
  682. // No, so free all particles.
  683. for ( U32 emitterIndex = 0; emitterIndex < emitterCount; ++emitterIndex )
  684. {
  685. mEmitters[emitterIndex]->freeAllParticles();
  686. }
  687. // Reset the age.
  688. mAge = 0.0f;
  689. // Flag as stopped and not waiting.
  690. mPlaying = mWaitingForParticles = mWaitingForDelete = false;
  691. // Turn off paused.
  692. mPaused = false;
  693. // Set safe deletion.
  694. setSafeDelete(true);
  695. // Perform the callback.
  696. if( isMethod( "onStopParticlePlayer" ) )
  697. Con::executef( this, 1, "onStopParticlePlayer" );
  698. // Flag for immediate deletion if killing.
  699. if ( killEffect )
  700. safeDelete();
  701. }
  702. //------------------------------------------------------------------------------
  703. void ParticlePlayer::configureParticle( EmitterNode* pEmitterNode, ParticleSystem::ParticleNode* pParticleNode )
  704. {
  705. // Fetch the particle player age.
  706. const F32 particlePlayerAge = mAge;
  707. // Fetch the particle player position.
  708. const Vector2& particlePlayerPosition = getPosition();
  709. // Default to not suppressing movement.
  710. pParticleNode->mSuppressMovement = false;
  711. // Fetch particle asset.
  712. ParticleAsset* pParticleAsset = mParticleAsset;
  713. // Fetch the asset emitter.
  714. ParticleAssetEmitter* pParticleAssetEmitter = pEmitterNode->getAssetEmitter();
  715. // **********************************************************************************************************************
  716. // Calculate Particle Position.
  717. // **********************************************************************************************************************
  718. // Fetch attachment options.
  719. const bool attachPositionToEmitter = pParticleAssetEmitter->getAttachPositionToEmitter();
  720. // Fetch the emitter offset, angle and size
  721. const Vector2& emitterOffset = pParticleAssetEmitter->getEmitterOffset() * getSizeScale();
  722. const Vector2& emitterSize = pParticleAssetEmitter->getEmitterSize() * getSizeScale();
  723. const F32 emitterAngle = mDegToRad(pParticleAssetEmitter->getEmitterAngle());
  724. // Are we using Single Particle?
  725. if ( pParticleAssetEmitter->getSingleParticle() )
  726. {
  727. // Determine whether to use world-space or emitter-space.
  728. if ( attachPositionToEmitter )
  729. {
  730. pParticleNode->mPosition = emitterOffset;
  731. }
  732. else
  733. {
  734. pParticleNode->mPosition = particlePlayerPosition + emitterOffset;
  735. }
  736. }
  737. else
  738. {
  739. // No, so select Emitter-Type.
  740. switch( pParticleAssetEmitter->getEmitterType() )
  741. {
  742. // Emit at a point defined by the emitters position.
  743. case ParticleAssetEmitter::POINT_EMITTER:
  744. {
  745. // Are we attaching the position to the emitter?
  746. if ( attachPositionToEmitter )
  747. {
  748. // Yes, so transform the particle into emitter-space only.
  749. pParticleNode->mPosition = emitterOffset;
  750. }
  751. else
  752. {
  753. // No, so transform the particle into world-space here.
  754. pParticleNode->mPosition = emitterOffset + particlePlayerPosition;
  755. }
  756. } break;
  757. // Emit along a line defined by the emitters width.
  758. case ParticleAssetEmitter::LINE_EMITTER:
  759. {
  760. // Calculate half-width.
  761. const F32 halfWidth = emitterSize.x * 0.5f;
  762. // Calculate emitter position.
  763. Vector2 emissionPosition( CoreMath::mGetRandomF( -halfWidth, halfWidth ), 0.0f );
  764. // Transform particle position in emitter-space.
  765. pParticleNode->mPosition = b2Mul( b2Rot(emitterAngle), emissionPosition ) + emitterOffset;
  766. // Are we attaching the position to the emitter?
  767. if ( !attachPositionToEmitter )
  768. {
  769. // No, so transform the particle into world-space here.
  770. b2Transform xform( particlePlayerPosition, b2Rot( getAngle()) );
  771. pParticleNode->mPosition = b2Mul( xform, pParticleNode->mPosition );
  772. }
  773. } break;
  774. // Emit inside a box defined by the emitters size.
  775. case ParticleAssetEmitter::BOX_EMITTER:
  776. {
  777. // Calculate half-width/height.
  778. const F32 halfWidth = emitterSize.x * 0.5f;
  779. const F32 halfHeight = emitterSize.y * 0.5f;
  780. // Calculate emitter position.
  781. Vector2 emissionPosition( CoreMath::mGetRandomF( -halfWidth, halfWidth ), CoreMath::mGetRandomF( -halfHeight, halfHeight ) );
  782. // Transform particle position in emitter-space.
  783. pParticleNode->mPosition = b2Mul( b2Rot(emitterAngle), emissionPosition ) + emitterOffset;
  784. // Are we attaching the position to the emitter?
  785. if ( !attachPositionToEmitter )
  786. {
  787. // No, so transform the particle into world-space here.
  788. b2Transform xform( particlePlayerPosition, b2Rot( getAngle()) );
  789. pParticleNode->mPosition = b2Mul( xform, pParticleNode->mPosition );
  790. }
  791. } break;
  792. // Emit from an ellipse with a radii defined by the emitters size.
  793. case ParticleAssetEmitter::DISK_EMITTER:
  794. {
  795. // Calculate the random angle.
  796. const F32 angle = CoreMath::mGetRandomF( 0.0f, b2_pi2 );
  797. #if 1
  798. // Calculate the uniform distribution scale.
  799. const F32 distributionScale = mSqrt( CoreMath::mGetRandomF(0.0f, 1.0f) );
  800. // Calculate the radii.
  801. const F32 radiusX = emitterSize.x * 0.5f * distributionScale;
  802. const F32 radiusY = emitterSize.y * 0.5f * distributionScale;
  803. #else
  804. // Calculate the radii.
  805. const F32 radiusX = emitterSize.x * 0.5f;
  806. const F32 radiusY = emitterSize.y * 0.5f;
  807. #endif
  808. // Calculate emitter position using a uniform distribution.
  809. Vector2 emissionPosition( radiusX * mCos(angle), radiusY * mSin(angle) );
  810. // Transform particle position in emitter-space.
  811. pParticleNode->mPosition = b2Mul( b2Rot(emitterAngle), emissionPosition ) + emitterOffset;
  812. // Are we attaching the position to the emitter?
  813. if ( !attachPositionToEmitter )
  814. {
  815. // No, so transform the particle into world-space here.
  816. b2Transform xform( particlePlayerPosition, b2Rot( getAngle()) );
  817. pParticleNode->mPosition = b2Mul( xform, pParticleNode->mPosition );
  818. }
  819. } break;
  820. // Emit from an ellipse with a radii defined by the emitters size.
  821. case ParticleAssetEmitter::ELLIPSE_EMITTER:
  822. {
  823. // Calculate the random angle.
  824. const F32 angle = CoreMath::mGetRandomF( 0.0f, b2_pi2 );
  825. // Calculate emitter position using a uniform distribution.
  826. Vector2 emissionPosition( emitterSize.x * 0.5f * mCos(angle), emitterSize.y * 0.5f * mSin(angle) );
  827. // Transform particle position in emitter-space.
  828. pParticleNode->mPosition = b2Mul( b2Rot(emitterAngle), emissionPosition ) + emitterOffset;
  829. // Are we attaching the position to the emitter?
  830. if ( !attachPositionToEmitter )
  831. {
  832. // No, so transform the particle into world-space here.
  833. b2Transform xform( particlePlayerPosition, b2Rot( getAngle()) );
  834. pParticleNode->mPosition = b2Mul( xform, pParticleNode->mPosition );
  835. }
  836. } break;
  837. // Emit inside a torus with an outer-diameter defined by the emitters major size axis and an inner-diameter defined by the emitters minor size axis..
  838. case ParticleAssetEmitter::TORUS_EMITTER:
  839. {
  840. // Calculate the random angle.
  841. const F32 angle = CoreMath::mGetRandomF( 0.0f, b2_pi2 );
  842. // Calculate the inner and outer radii.
  843. const F32 outerRadii = emitterSize.getMajorAxis() * 0.5f;
  844. const F32 innerRadii = emitterSize.getMinorAxis() * 0.5f;
  845. #if 1
  846. // Calculate the radius as a uniform distribution.
  847. const F32 radius = innerRadii + ( mSqrt( CoreMath::mGetRandomF(0.0f, 1.0f) ) * (outerRadii-innerRadii) );
  848. #else
  849. // Calculate the radius as a non-uniform distribution.
  850. const F32 radius = CoreMath::mGetRandomF( innerRadii, outerRadii );
  851. #endif
  852. // Calculate emitter position using a uniform distribution.
  853. Vector2 emissionPosition( radius * mCos(angle), radius * mSin(angle) );
  854. // Are we attaching the position to the emitter?
  855. if ( attachPositionToEmitter )
  856. {
  857. // Yes, so transform the particle into emitter-space only.
  858. pParticleNode->mPosition = emissionPosition + emitterOffset;
  859. }
  860. else
  861. {
  862. // No, so transform the particle into world-space here.
  863. pParticleNode->mPosition = emissionPosition + emitterOffset + particlePlayerPosition;
  864. }
  865. } break;
  866. default:
  867. break;
  868. }
  869. }
  870. // **********************************************************************************************************************
  871. // Calculate Particle Lifetime.
  872. // **********************************************************************************************************************
  873. pParticleNode->mParticleAge = 0.0f;
  874. pParticleNode->mParticleLifetime = ParticleAssetField::calculateFieldBVE( pParticleAssetEmitter->getParticleLifeBaseField(),
  875. pParticleAssetEmitter->getParticleLifeVariationField(),
  876. pParticleAsset->getParticleLifeScaleField(),
  877. particlePlayerAge );
  878. // **********************************************************************************************************************
  879. // Calculate Particle Size-X.
  880. // **********************************************************************************************************************
  881. pParticleNode->mSize.x = ParticleAssetField::calculateFieldBVE( pParticleAssetEmitter->getSizeXBaseField(),
  882. pParticleAssetEmitter->getSizeXVariationField(),
  883. pParticleAsset->getSizeXScaleField(),
  884. particlePlayerAge ) * getSizeScale();
  885. // Is the particle using a fixed aspect?
  886. if ( pParticleAssetEmitter->getFixedAspect() )
  887. {
  888. // Yes, so simply copy Size-X.
  889. pParticleNode->mSize.y = pParticleNode->mSize.x;
  890. }
  891. else
  892. {
  893. // No, so calculate the particle Size-Y.
  894. pParticleNode->mSize.y = ParticleAssetField::calculateFieldBVE( pParticleAssetEmitter->getSizeYBaseField(),
  895. pParticleAssetEmitter->getSizeYVariationField(),
  896. pParticleAsset->getSizeYScaleField(),
  897. particlePlayerAge ) * getSizeScale();
  898. }
  899. // Reset the render size.
  900. pParticleNode->mRenderSize.Set(-1.0f, -1.0f);
  901. // **********************************************************************************************************************
  902. // Calculate Speed, Random Motion and Emission Angle.
  903. // **********************************************************************************************************************
  904. // We reset the emission angle/arc in-case we're using single-particle mode as this is the default.
  905. F32 emissionForce = 0;
  906. F32 emissionAngle = 0;
  907. F32 emissionArc = 0;
  908. // Ignore if we're using a single-particle.
  909. if ( !pParticleAssetEmitter->getSingleParticle() )
  910. {
  911. pParticleNode->mSpeed = ParticleAssetField::calculateFieldBVE( pParticleAssetEmitter->getSpeedBaseField(),
  912. pParticleAssetEmitter->getSpeedVariationField(),
  913. pParticleAsset->getSpeedScaleField(),
  914. particlePlayerAge ) * getForceScale();
  915. pParticleNode->mRandomMotion = ParticleAssetField::calculateFieldBVE( pParticleAssetEmitter->getRandomMotionBaseField(),
  916. pParticleAssetEmitter->getRandomMotionVariationField(),
  917. pParticleAsset->getRandomMotionScaleField(),
  918. particlePlayerAge ) * getForceScale();
  919. // Calculate the emission force.
  920. emissionForce = ParticleAssetField::calculateFieldBV( pParticleAssetEmitter->getEmissionForceForceBaseField(),
  921. pParticleAssetEmitter->getEmissionForceVariationField(),
  922. particlePlayerAge) * getForceScale();
  923. // Calculate Emission Angle.
  924. emissionAngle = ParticleAssetField::calculateFieldBV( pParticleAssetEmitter->getEmissionAngleBaseField(),
  925. pParticleAssetEmitter->getEmissionAngleVariationField(),
  926. particlePlayerAge );
  927. // Calculate Emission Arc.
  928. // NOTE:- We're actually interested in half the emission arc!
  929. emissionArc = ParticleAssetField::calculateFieldBV( pParticleAssetEmitter->getEmissionArcBaseField(),
  930. pParticleAssetEmitter->getEmissionArcVariationField(),
  931. particlePlayerAge ) * 0.5f;
  932. // Is the emission rotation linked?
  933. if ( pParticleAssetEmitter->getLinkEmissionRotation() )
  934. {
  935. // Yes, so add the particle player angle.
  936. emissionAngle += getAngle();
  937. }
  938. // Calculate the final emission angle choosing random Arc.
  939. emissionAngle = mFmod( CoreMath::mGetRandomF( emissionAngle-emissionArc, emissionAngle+emissionArc ), 360.0f );
  940. // Calculate the particle velocity.
  941. const F32 emissionAngleRadians = mDegToRad( emissionAngle );
  942. pParticleNode->mVelocity.Set( emissionForce * mCos( emissionAngleRadians ), emissionForce * mSin( emissionAngleRadians ) );
  943. }
  944. // **********************************************************************************************************************
  945. // Calculate Spin.
  946. // **********************************************************************************************************************
  947. pParticleNode->mSpin = ParticleAssetField::calculateFieldBVE( pParticleAssetEmitter->getSpinBaseField(),
  948. pParticleAssetEmitter->getSpinVariationField(),
  949. pParticleAsset->getSpinScaleField(),
  950. particlePlayerAge );
  951. // **********************************************************************************************************************
  952. // Calculate Fixed-Force.
  953. // **********************************************************************************************************************
  954. pParticleNode->mFixedForce = ParticleAssetField::calculateFieldBVE( pParticleAssetEmitter->getFixedForceBaseField(),
  955. pParticleAssetEmitter->getFixedForceVariationField(),
  956. pParticleAsset->getFixedForceScaleField(),
  957. particlePlayerAge ) * getForceScale();
  958. // **********************************************************************************************************************
  959. // Calculate Orientation Angle.
  960. // **********************************************************************************************************************
  961. // Configure particle orientation.
  962. switch( pParticleAssetEmitter->getOrientationType() )
  963. {
  964. // Aligned to initial emission.
  965. case ParticleAssetEmitter::ALIGNED_ORIENTATION:
  966. {
  967. // Use the emission angle with fixed offset.
  968. pParticleNode->mOrientationAngle = mFmod( emissionAngle - pParticleAssetEmitter->getAlignedAngleOffset(), 360.0f );
  969. } break;
  970. // Fixed orientation.
  971. case ParticleAssetEmitter::FIXED_ORIENTATION:
  972. {
  973. // Use a fixed angle.
  974. pParticleNode->mOrientationAngle = mFmod( pParticleAssetEmitter->getFixedAngleOffset(), 360.0f );
  975. } break;
  976. // Random with constraints.
  977. case ParticleAssetEmitter::RANDOM_ORIENTATION:
  978. {
  979. // Used a random angle/arc.
  980. const F32 randomArc = pParticleAssetEmitter->getRandomArc() * 0.5f;
  981. pParticleNode->mOrientationAngle = mFmod( CoreMath::mGetRandomF( pParticleAssetEmitter->getRandomAngleOffset() - randomArc, pParticleAssetEmitter->getRandomAngleOffset() + randomArc ), 360.0f );
  982. } break;
  983. default:
  984. break;
  985. }
  986. // **********************************************************************************************************************
  987. // Calculate RGBA Components.
  988. // **********************************************************************************************************************
  989. // Fetch the channels.
  990. const ParticleAssetField& redChannel = pParticleAssetEmitter->getRedChannelLifeField();
  991. const ParticleAssetField& greenChannel = pParticleAssetEmitter->getGreenChannelLifeField();
  992. const ParticleAssetField& blueChannel = pParticleAssetEmitter->getBlueChannelLifeField();
  993. const ParticleAssetField& alphaChannel = pParticleAssetEmitter->getAlphaChannelLifeField();
  994. const ParticleAssetField& alphaChannelScale = pParticleAsset->getAlphaChannelScaleField();
  995. // Calculate the color.
  996. pParticleNode->mColor.set( mClampF( redChannel.getFieldValue( 0.0f ), redChannel.getMinValue(), redChannel.getMaxValue() ),
  997. mClampF( greenChannel.getFieldValue( 0.0f ),greenChannel.getMinValue(), greenChannel.getMaxValue() ),
  998. mClampF( blueChannel.getFieldValue( 0.0f ), blueChannel.getMinValue(),blueChannel.getMaxValue() ),
  999. mClampF( alphaChannel.getFieldValue( 0.0f ) * alphaChannelScale.getFieldValue( 0.0f ), alphaChannel.getMinValue(), alphaChannel.getMaxValue() ) );
  1000. // **********************************************************************************************************************
  1001. // Image, Frame and Animation Controller.
  1002. // **********************************************************************************************************************
  1003. // Is the emitter in static mode?
  1004. if ( pParticleAssetEmitter->isStaticMode() )
  1005. {
  1006. // Yes, so is random image frame active?
  1007. if ( pParticleAssetEmitter->getRandomImageFrame() )
  1008. {
  1009. // Yes, so fetch the frame count for the image asset.
  1010. const U32 frameCount = pParticleAssetEmitter->getImageAsset()->getFrameCount();
  1011. // Choose a random frame.
  1012. pParticleNode->mImageFrame = (U32)CoreMath::mGetRandomI( 0, frameCount-1 );
  1013. }
  1014. else
  1015. {
  1016. // No, so set the emitter image frame.
  1017. pParticleNode->mImageFrame = pParticleAssetEmitter->getImageFrame();
  1018. }
  1019. }
  1020. else
  1021. {
  1022. // No, so fetch the animation asset.
  1023. const AssetPtr<AnimationAsset>& animationAsset = pParticleAssetEmitter->getAnimationAsset();
  1024. // Is an animation available?
  1025. if ( animationAsset.notNull() )
  1026. {
  1027. // Yes, so play it.
  1028. pParticleNode->mAnimationController.playAnimation( animationAsset.getAssetId(), false );
  1029. }
  1030. }
  1031. // **********************************************************************************************************************
  1032. // Reset Tick Position.
  1033. // **********************************************************************************************************************
  1034. pParticleNode->mPreTickPosition = pParticleNode->mPostTickPosition = pParticleNode->mRenderTickPosition = pParticleNode->mPosition;
  1035. // **********************************************************************************************************************
  1036. // Do a Single Particle Integration to get things going.
  1037. // **********************************************************************************************************************
  1038. integrateParticle( pEmitterNode, pParticleNode, 0.0f, 0.0f );
  1039. }
  1040. //------------------------------------------------------------------------------
  1041. void ParticlePlayer::integrateParticle( EmitterNode* pEmitterNode, ParticleSystem::ParticleNode* pParticleNode, F32 particleAge, F32 elapsedTime )
  1042. {
  1043. // Fetch particle asset.
  1044. ParticleAsset* pParticleAsset = mParticleAsset;
  1045. // Fetch the asset emitter.
  1046. ParticleAssetEmitter* pParticleAssetEmitter = pEmitterNode->getAssetEmitter();
  1047. // **********************************************************************************************************************
  1048. // Copy Old Tick Position.
  1049. // **********************************************************************************************************************
  1050. pParticleNode->mRenderTickPosition = pParticleNode->mPreTickPosition = pParticleNode->mPostTickPosition;
  1051. // **********************************************************************************************************************
  1052. // Scale Size.
  1053. // **********************************************************************************************************************
  1054. // Scale Size-X.
  1055. pParticleNode->mRenderSize.x = mClampF( pParticleNode->mSize.x * pParticleAssetEmitter->getSizeXLifeField().getFieldValue( particleAge ),
  1056. pParticleAssetEmitter->getSizeXBaseField().getMinValue(),
  1057. pParticleAssetEmitter->getSizeXBaseField().getMaxValue());
  1058. // Is the particle using a fixed aspect?
  1059. if ( pParticleAssetEmitter->getFixedAspect() )
  1060. {
  1061. // Yes, so simply copy Size-X.
  1062. pParticleNode->mRenderSize.y = pParticleNode->mRenderSize.x;
  1063. }
  1064. else
  1065. {
  1066. // No, so Scale Size-Y.
  1067. pParticleNode->mRenderSize.y = mClampF( pParticleNode->mSize.y * pParticleAssetEmitter->getSizeYLifeField().getFieldValue( particleAge ),
  1068. pParticleAssetEmitter->getSizeYBaseField().getMinValue(),
  1069. pParticleAssetEmitter->getSizeYBaseField().getMaxValue() );
  1070. }
  1071. // **********************************************************************************************************************
  1072. // Scale Speed.
  1073. // **********************************************************************************************************************
  1074. pParticleNode->mRenderSpeed = mClampF( pParticleNode->mSpeed * pParticleAssetEmitter->getSpeedLifeField().getFieldValue( particleAge ),
  1075. pParticleAssetEmitter->getSpeedBaseField().getMinValue(),
  1076. pParticleAssetEmitter->getSpeedBaseField().getMaxValue() );
  1077. // **********************************************************************************************************************
  1078. // Scale Fixed-Force.
  1079. // **********************************************************************************************************************
  1080. pParticleNode->mRenderFixedForce = mClampF( pParticleNode->mFixedForce * pParticleAssetEmitter->getFixedForceLifeField().getFieldValue( particleAge ),
  1081. pParticleAssetEmitter->getFixedForceBaseField().getMinValue(),
  1082. pParticleAssetEmitter->getFixedForceBaseField().getMaxValue() );
  1083. // **********************************************************************************************************************
  1084. // Scale Random-Motion.
  1085. // **********************************************************************************************************************
  1086. pParticleNode->mRenderRandomMotion = mClampF( pParticleNode->mRandomMotion * pParticleAssetEmitter->getRandomMotionLifeField().getFieldValue( particleAge ),
  1087. pParticleAssetEmitter->getRandomMotionBaseField().getMinValue(),
  1088. pParticleAssetEmitter->getRandomMotionBaseField().getMaxValue() );
  1089. // **********************************************************************************************************************
  1090. // Calculate RGBA Components.
  1091. // **********************************************************************************************************************
  1092. // Fetch the channels.
  1093. const ParticleAssetField& redChannel = pParticleAssetEmitter->getRedChannelLifeField();
  1094. const ParticleAssetField& greenChannel = pParticleAssetEmitter->getGreenChannelLifeField();
  1095. const ParticleAssetField& blueChannel = pParticleAssetEmitter->getBlueChannelLifeField();
  1096. const ParticleAssetField& alphaChannel = pParticleAssetEmitter->getAlphaChannelLifeField();
  1097. const ParticleAssetField& alphaChannelScale = pParticleAsset->getAlphaChannelScaleField();
  1098. // Calculate the color.
  1099. pParticleNode->mColor.set( mClampF( redChannel.getFieldValue( particleAge ), redChannel.getMinValue(), redChannel.getMaxValue() ),
  1100. mClampF( greenChannel.getFieldValue( particleAge ),greenChannel.getMinValue(), greenChannel.getMaxValue() ),
  1101. mClampF( blueChannel.getFieldValue( particleAge ), blueChannel.getMinValue(),blueChannel.getMaxValue() ),
  1102. mClampF( alphaChannel.getFieldValue( particleAge ) * alphaChannelScale.getFieldValue( 0.0f ), alphaChannel.getMinValue(), alphaChannel.getMaxValue() ) );
  1103. // **********************************************************************************************************************
  1104. // Integrate Particle.
  1105. // **********************************************************************************************************************
  1106. // Is the emitter in static mode?
  1107. if ( !pParticleAssetEmitter->isStaticMode() )
  1108. {
  1109. // No, so update animation.
  1110. pParticleNode->mAnimationController.updateAnimation( elapsedTime );
  1111. }
  1112. // **********************************************************************************************************************
  1113. // Calculate New Velocity...
  1114. // **********************************************************************************************************************
  1115. // Calculate the velocity if not a single particle.
  1116. if ( !pParticleAssetEmitter->getSingleParticle() )
  1117. {
  1118. // Calculate random motion (if we've got any).
  1119. if ( mNotZero( pParticleNode->mRenderRandomMotion ) )
  1120. {
  1121. // Fetch random motion.
  1122. const F32 randomMotion = pParticleNode->mRenderRandomMotion * 0.5f;
  1123. // Add time-integrated random motion into velocity.
  1124. pParticleNode->mVelocity += Vector2( CoreMath::mGetRandomF(-randomMotion, randomMotion) * elapsedTime, CoreMath::mGetRandomF(-randomMotion, randomMotion) * elapsedTime );
  1125. }
  1126. // Do we have any fixed force?
  1127. if ( mNotZero( pParticleNode->mRenderFixedForce ) )
  1128. {
  1129. // Yes, so time-integrate a fixed force to the velocity.
  1130. pParticleNode->mVelocity += (pParticleAssetEmitter->getFixedForceDirection() * (pParticleNode->mRenderFixedForce * getForceScale()) * elapsedTime);
  1131. }
  1132. // Are we suppressing movement?
  1133. if ( !pParticleNode->mSuppressMovement )
  1134. {
  1135. // No, so adjust particle position.
  1136. pParticleNode->mPosition += (pParticleNode->mVelocity * pParticleNode->mRenderSpeed * elapsedTime);
  1137. }
  1138. }
  1139. // **********************************************************************************************************************
  1140. // Are we Aligning to motion?
  1141. // **********************************************************************************************************************
  1142. if ( pParticleAssetEmitter->getKeepAligned() && pParticleAssetEmitter->getOrientationType() == ParticleAssetEmitter::ALIGNED_ORIENTATION )
  1143. {
  1144. // Yes, so calculate last movement direction.
  1145. F32 movementAngle = mRadToDeg( mAtan( pParticleNode->mVelocity.x, -pParticleNode->mVelocity.y ) );
  1146. // Adjust for negative ArcTan quadrants.
  1147. if ( movementAngle < 0.0f )
  1148. movementAngle += 360.0f;
  1149. // Set new Orientation Angle.
  1150. pParticleNode->mOrientationAngle = -movementAngle - pParticleAssetEmitter->getAlignedAngleOffset();
  1151. }
  1152. else
  1153. {
  1154. // No, so calculate the render spin.
  1155. pParticleNode->mRenderSpin = pParticleNode->mSpin * pParticleAssetEmitter->getSpinLifeField().getFieldValue( particleAge );
  1156. // Have we got some Spin?
  1157. if ( mNotZero(pParticleNode->mRenderSpin) )
  1158. {
  1159. // Yes, so add into Orientation.
  1160. pParticleNode->mOrientationAngle += pParticleNode->mRenderSpin * elapsedTime;
  1161. // Clamp the orientation angle.
  1162. pParticleNode->mOrientationAngle = mFmod( pParticleNode->mOrientationAngle, 360.0f );
  1163. }
  1164. }
  1165. // Calculate the transform.
  1166. pParticleNode->mTransform.Set( pParticleNode->mPosition, mDegToRad(pParticleNode->mOrientationAngle) );
  1167. // Fetch the local AABB..
  1168. const Vector2& localAABB0 = pParticleAssetEmitter->getLocalPivotAABB0();
  1169. const Vector2& localAABB1 = pParticleAssetEmitter->getLocalPivotAABB1();
  1170. const Vector2& localAABB2 = pParticleAssetEmitter->getLocalPivotAABB2();
  1171. const Vector2& localAABB3 = pParticleAssetEmitter->getLocalPivotAABB3();
  1172. // Fetch the render size.
  1173. const Vector2& renderSize = pParticleNode->mRenderSize;
  1174. // Calculate the scaled AABB.
  1175. Vector2 scaledAABB[4];
  1176. scaledAABB[0] = localAABB0 * renderSize;
  1177. scaledAABB[1] = localAABB1 * renderSize;
  1178. scaledAABB[2] = localAABB2 * renderSize;
  1179. scaledAABB[3] = localAABB3 * renderSize;
  1180. // Calculate the world OOBB..
  1181. CoreMath::mCalculateOOBB( scaledAABB, pParticleNode->mTransform, pParticleNode->mRenderOOBB );
  1182. // **********************************************************************************************************************
  1183. // Set Post Tick Position.
  1184. // **********************************************************************************************************************
  1185. pParticleNode->mPostTickPosition = pParticleNode->mPosition;
  1186. }
  1187. //-----------------------------------------------------------------------------
  1188. void ParticlePlayer::onTamlAddParent( SimObject* pParentObject )
  1189. {
  1190. // Call parent.
  1191. Parent::onTamlAddParent( pParentObject );
  1192. // Play automatically when added to a parent.
  1193. play( true );
  1194. }
  1195. //-----------------------------------------------------------------------------
  1196. void ParticlePlayer::initializeParticleAsset( void )
  1197. {
  1198. // Note if we were playing.
  1199. const bool wasPlaying = getIsPlaying();
  1200. // Destroy any existing particle asset.
  1201. destroyParticleAsset();
  1202. // Finish if no particle asset.
  1203. if ( mParticleAsset.isNull() )
  1204. return;
  1205. // Fetch the particle asset.
  1206. ParticleAsset* pParticleAsset = mParticleAsset;
  1207. // Fetch the emitter count.
  1208. const U32 emitterCount = pParticleAsset->getEmitterCount();
  1209. // Finish if no emitters found.
  1210. if ( emitterCount == 0 )
  1211. return;
  1212. // Add each emitter reference.
  1213. for( U32 emitterIndex = 0; emitterIndex < emitterCount; ++emitterIndex )
  1214. {
  1215. // Fetch the asset emitter.
  1216. ParticleAssetEmitter* pParticleAssetEmitter = pParticleAsset->getEmitter( emitterIndex );
  1217. // Fetch both image and animation assets.
  1218. const AssetPtr<ImageAsset>& imageAsset = pParticleAssetEmitter->getImageAsset();
  1219. const AssetPtr<AnimationAsset>& animationAsset = pParticleAssetEmitter->getAnimationAsset();
  1220. // Skip if the emitter does not have a valid assigned asset to render.
  1221. if (( pParticleAssetEmitter->isStaticMode() && (imageAsset.isNull() || imageAsset->getFrameCount() == 0 ) ) ||
  1222. ( !pParticleAssetEmitter->isStaticMode() && (animationAsset.isNull() || animationAsset->getValidatedAnimationFrames().size() == 0 ) ) )
  1223. continue;
  1224. // Create a new emitter node.
  1225. EmitterNode* pEmitterNode = new EmitterNode( this, pParticleAssetEmitter );
  1226. // Store new emitter node.
  1227. mEmitters.push_back( pEmitterNode );
  1228. }
  1229. // Start playing if we were playing before the update.
  1230. if ( wasPlaying )
  1231. play( false );
  1232. }
  1233. //-----------------------------------------------------------------------------
  1234. void ParticlePlayer::destroyParticleAsset( void )
  1235. {
  1236. // Stop playing.
  1237. stop( false, false );
  1238. // Destroy all emitters.
  1239. while( mEmitters.size() > 0 )
  1240. {
  1241. delete mEmitters[mEmitters.size()-1];
  1242. mEmitters.pop_back();
  1243. }
  1244. mEmitters.clear();
  1245. }