ParticleSys.cpp 111 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // ParticleSys.cpp ////////////////////////////////////////////////////////////////////////////////
  24. // Particle System implementation
  25. // Author: Michael S. Booth, November 2001
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  28. #define DEFINE_PARTICLE_SYSTEM_NAMES
  29. #include "Common/GameState.h"
  30. #include "Common/INI.h"
  31. #include "Common/PerfTimer.h"
  32. #include "Common/ThingFactory.h"
  33. #include "Common/GameLOD.h"
  34. #include "Common/Xfer.h"
  35. #include "GameClient/Drawable.h"
  36. #include "GameClient/DebugDisplay.h"
  37. #include "GameClient/Display.h"
  38. #include "GameClient/GameClient.h"
  39. #include "GameClient/InGameUI.h"
  40. #include "GameClient/ParticleSys.h"
  41. #include "GameLogic/GameLogic.h"
  42. #include "GameLogic/Object.h"
  43. #include "GameLogic/TerrainLogic.h"
  44. #ifdef _INTERNAL
  45. // for occasional debugging...
  46. //#pragma optimize("", off)
  47. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  48. #endif
  49. //------------------------------------------------------------------------------ Performance Timers
  50. //#include "Common/PerfMetrics.h"
  51. //#include "Common/PerfTimer.h"
  52. //static PerfTimer s_particleSys("ParticleSys::update", false, PERFMETRICS_LOGIC_STARTFRAME, PERFMETRICS_LOGIC_STOPFRAME);
  53. //-------------------------------------------------------------------------------------------------
  54. // the singleton
  55. ParticleSystemManager *TheParticleSystemManager = NULL;
  56. ///////////////////////////////////////////////////////////////////////////////////////////////////
  57. ///////////////////////////////////////////////////////////////////////////////////////////////////
  58. ///////////////////////////////////////////////////////////////////////////////////////////////////
  59. // ------------------------------------------------------------------------------------------------
  60. // ------------------------------------------------------------------------------------------------
  61. ParticleInfo::ParticleInfo( void )
  62. {
  63. //Added By Sadullah Nader
  64. //Initializations inserted
  65. m_angleZ = 0.0f;
  66. m_angularDamping = 0.0f;
  67. m_angularRateZ = 0.0f;
  68. m_colorScale =0.0f;
  69. m_size = 0.0f;
  70. m_sizeRate = 0.0f;
  71. m_sizeRateDamping = 0.0f;
  72. m_velDamping = 0.0f;
  73. m_windRandomness = 0.0f;
  74. m_emitterPos.zero();
  75. m_pos.zero();
  76. m_vel.zero();
  77. m_lifetime = 0;
  78. m_particleUpTowardsEmitter = FALSE;
  79. //
  80. } // end ParticleInfo
  81. // ------------------------------------------------------------------------------------------------
  82. /** CRC */
  83. // ------------------------------------------------------------------------------------------------
  84. void ParticleInfo::crc( Xfer *xfer )
  85. {
  86. } // end crc
  87. // ------------------------------------------------------------------------------------------------
  88. /** Xfer method
  89. * Version Info:
  90. * 1: Initial version */
  91. // ------------------------------------------------------------------------------------------------
  92. void ParticleInfo::xfer( Xfer *xfer )
  93. {
  94. Int i;
  95. // version
  96. XferVersion currentVersion = 1;
  97. XferVersion version = currentVersion;
  98. xfer->xferVersion( &version, currentVersion );
  99. // velocity
  100. xfer->xferCoord3D( &m_vel );
  101. // position
  102. xfer->xferCoord3D( &m_pos );
  103. // emitter position
  104. xfer->xferCoord3D( &m_emitterPos );
  105. // velocity damping
  106. xfer->xferReal( &m_velDamping );
  107. // angle
  108. Real tempAngle=0; //temporary value to save out for backwards compatibility when we supported x,y
  109. xfer->xferReal( &tempAngle );
  110. xfer->xferReal( &tempAngle );
  111. xfer->xferReal( &m_angleZ );
  112. // angular rate
  113. xfer->xferReal( &tempAngle );
  114. xfer->xferReal( &tempAngle );
  115. xfer->xferReal( &m_angularRateZ );
  116. // lifetime
  117. xfer->xferUnsignedInt( &m_lifetime );
  118. // size
  119. xfer->xferReal( &m_size );
  120. // size rate
  121. xfer->xferReal( &m_sizeRate );
  122. // size rate damping
  123. xfer->xferReal( &m_sizeRateDamping );
  124. // alpha keys
  125. for( i = 0; i < MAX_KEYFRAMES; ++i )
  126. {
  127. xfer->xferReal( &m_alphaKey[ i ].value );
  128. xfer->xferUnsignedInt( &m_alphaKey[ i ].frame );
  129. } // end for, i
  130. // color keys
  131. for( i = 0; i < MAX_KEYFRAMES; ++i )
  132. {
  133. xfer->xferRGBColor( &m_colorKey[ i ].color );
  134. xfer->xferUnsignedInt( &m_colorKey[ i ].frame );
  135. } // end for, i
  136. // color scale
  137. xfer->xferReal( &m_colorScale );
  138. // particle up towards emitter
  139. xfer->xferBool( &m_particleUpTowardsEmitter );
  140. // wind randomness
  141. xfer->xferReal( &m_windRandomness );
  142. } // end xfer
  143. // ------------------------------------------------------------------------------------------------
  144. /** Load post process */
  145. // ------------------------------------------------------------------------------------------------
  146. void ParticleInfo::loadPostProcess( void )
  147. {
  148. } // end loadPostProcess
  149. /** Load post process */
  150. // ------------------------------------------------------------------------------------------------
  151. ///////////////////////////////////////////////////////////////////////////////////////////////////
  152. ///////////////////////////////////////////////////////////////////////////////////////////////////
  153. ///////////////////////////////////////////////////////////////////////////////////////////////////
  154. // ------------------------------------------------------------------------------------------------
  155. enum
  156. {
  157. MAX_SIZE_BONUS = 50
  158. };
  159. //todo move this somewhere more useful.
  160. static Real angleBetween(const Coord2D *vecA, const Coord2D *vecB);
  161. ///////////////////////////////////////////////////////////////////////////////////////////////////
  162. // Particle ///////////////////////////////////////////////////////////////////////////////////////
  163. ///////////////////////////////////////////////////////////////////////////////////////////////////
  164. // ------------------------------------------------------------------------------------------------
  165. /** Compute alpha rate to get to next key on given frame */
  166. // ------------------------------------------------------------------------------------------------
  167. void Particle::computeAlphaRate( void )
  168. {
  169. if (m_alphaKey[ m_alphaTargetKey ].frame == 0)
  170. {
  171. m_alphaRate = 0.0f;
  172. return;
  173. }
  174. Real delta = m_alphaKey[ m_alphaTargetKey ].value - m_alphaKey[ m_alphaTargetKey-1 ].value;
  175. UnsignedInt time = m_alphaKey[ m_alphaTargetKey ].frame - m_alphaKey[ m_alphaTargetKey-1 ].frame;
  176. m_alphaRate = delta/time;
  177. }
  178. // ------------------------------------------------------------------------------------------------
  179. /** Compute color rate to get to next key on given frame */
  180. // ------------------------------------------------------------------------------------------------
  181. void Particle::computeColorRate( void )
  182. {
  183. if (m_colorKey[ m_colorTargetKey ].frame == 0)
  184. {
  185. m_colorRate.red = 0.0f;
  186. m_colorRate.green = 0.0f;
  187. m_colorRate.blue = 0.0f;
  188. return;
  189. }
  190. UnsignedInt time = m_colorKey[ m_colorTargetKey ].frame - m_colorKey[ m_colorTargetKey-1 ].frame;
  191. Real delta = m_colorKey[ m_colorTargetKey ].color.red - m_colorKey[ m_colorTargetKey-1 ].color.red;
  192. m_colorRate.red = delta/time;
  193. delta = m_colorKey[ m_colorTargetKey ].color.green - m_colorKey[ m_colorTargetKey-1 ].color.green;
  194. m_colorRate.green = delta/time;
  195. delta = m_colorKey[ m_colorTargetKey ].color.blue - m_colorKey[ m_colorTargetKey-1 ].color.blue;
  196. m_colorRate.blue = delta/time;
  197. }
  198. // ------------------------------------------------------------------------------------------------
  199. /** Construct a particle from a particle template */
  200. // ------------------------------------------------------------------------------------------------
  201. Particle::Particle( ParticleSystem *system, const ParticleInfo *info )
  202. {
  203. m_system = system;
  204. m_isCulled = FALSE;
  205. m_accel.x = 0.0f;
  206. m_accel.y = 0.0f;
  207. m_accel.z = 0.0f;
  208. m_vel = info->m_vel;
  209. m_pos = info->m_pos;
  210. m_angleZ = info->m_angleZ;
  211. //Added By Sadullah Nader
  212. //Initializations inserted
  213. m_lastPos.zero();
  214. //
  215. m_windRandomness = info->m_windRandomness;
  216. m_particleUpTowardsEmitter = info->m_particleUpTowardsEmitter;
  217. m_emitterPos = info->m_emitterPos;
  218. m_angularRateZ = info->m_angularRateZ;
  219. m_angularDamping = info->m_angularDamping;
  220. m_velDamping = info->m_velDamping;
  221. m_lifetime = info->m_lifetime;
  222. m_lifetimeLeft = info->m_lifetime;
  223. m_createTimestamp = TheGameClient->getFrame();
  224. m_personality = 0;
  225. m_size = info->m_size;
  226. m_sizeRate = info->m_sizeRate;
  227. m_sizeRateDamping = info->m_sizeRateDamping;
  228. // set up alpha
  229. for( int i=0; i<MAX_KEYFRAMES; i++ )
  230. m_alphaKey[i] = info->m_alphaKey[i];
  231. m_alpha = m_alphaKey[0].value;
  232. m_alphaTargetKey = 1;
  233. computeAlphaRate();
  234. // set up colors
  235. for( i=0; i<MAX_KEYFRAMES; i++ )
  236. m_colorKey[i] = info->m_colorKey[i];
  237. m_color = m_colorKey[0].color;
  238. m_colorTargetKey = 1;
  239. computeColorRate();
  240. m_colorScale = info->m_colorScale;
  241. m_inSystemList = m_inOverallList = FALSE;
  242. m_systemPrev = m_systemNext = m_overallPrev = m_overallNext = NULL;
  243. // add this particle to the global list, retaining particle creation order
  244. TheParticleSystemManager->addParticle(this, system->getPriority() );
  245. // add this particle to the Particle System list, retaining local creation order
  246. m_system->addParticle(this);
  247. //DEBUG_ASSERTLOG(!(totalParticleCount % 100 == 0), ( "TotalParticleCount = %d\n", m_totalParticleCount ));
  248. }
  249. // ------------------------------------------------------------------------------------------------
  250. /** Destructor */
  251. // ------------------------------------------------------------------------------------------------
  252. Particle::~Particle()
  253. {
  254. // tell the particle system that this particle is gone
  255. m_system->removeParticle( this );
  256. // if this particle was controlling another particle system, destroy that system
  257. if (m_systemUnderControl)
  258. {
  259. m_systemUnderControl->detachControlParticle( this );
  260. m_systemUnderControl->destroy();
  261. }
  262. m_systemUnderControl = NULL;
  263. // remove from the global list
  264. TheParticleSystemManager->removeParticle(this);
  265. //DEBUG_ASSERTLOG(!(totalParticleCount % 100 == 0), ( "TotalParticleCount = %d\n", m_totalParticleCount ));
  266. }
  267. // ------------------------------------------------------------------------------------------------
  268. /** Add the given acceleration */
  269. // ------------------------------------------------------------------------------------------------
  270. void Particle::applyForce( const Coord3D *force )
  271. {
  272. m_accel.x += force->x;
  273. m_accel.y += force->y;
  274. m_accel.z += force->z;
  275. }
  276. // ------------------------------------------------------------------------------------------------
  277. /** Update the behavior of an individual particle */
  278. // ------------------------------------------------------------------------------------------------
  279. Bool Particle::update( void )
  280. {
  281. // integrate acceleration into velocity
  282. m_vel.x += m_accel.x;
  283. m_vel.y += m_accel.y;
  284. m_vel.z += m_accel.z;
  285. m_vel.x *= m_velDamping;
  286. m_vel.y *= m_velDamping;
  287. m_vel.z *= m_velDamping;
  288. // integrate velocity into position
  289. const Coord3D *driftVel = m_system->getDriftVelocity();
  290. m_pos.x += m_vel.x + driftVel->x;
  291. m_pos.y += m_vel.y + driftVel->y;
  292. m_pos.z += m_vel.z + driftVel->z;
  293. // integrate the wind (if specified) into position
  294. ParticleSystemInfo::WindMotion windMotion = m_system->getWindMotion();
  295. // see if we should even do anything
  296. if( windMotion != ParticleSystemInfo::WIND_MOTION_NOT_USED )
  297. doWindMotion();
  298. // update orientation
  299. m_angleZ += m_angularRateZ;
  300. m_angularRateZ *= m_angularDamping;
  301. if (m_particleUpTowardsEmitter) {
  302. // adjust the up position back towards the particle
  303. static const Coord2D upVec = { 0.0f, 1.0f };
  304. Coord2D emitterDir;
  305. emitterDir.x = m_pos.x - m_emitterPos.x;
  306. emitterDir.y = m_pos.y - m_emitterPos.y;
  307. m_angleZ = (angleBetween(&upVec, &emitterDir) + PI);
  308. }
  309. // update size
  310. m_size += m_sizeRate;
  311. m_sizeRate *= m_sizeRateDamping;
  312. //
  313. // Update alpha (if used)
  314. //
  315. if (m_system->getShaderType() != ParticleSystemInfo::ADDITIVE)
  316. {
  317. m_alpha += m_alphaRate;
  318. if (m_alphaTargetKey < MAX_KEYFRAMES && m_alphaKey[ m_alphaTargetKey ].frame)
  319. {
  320. if (TheGameClient->getFrame() - m_createTimestamp >= m_alphaKey[ m_alphaTargetKey ].frame)
  321. {
  322. m_alpha = m_alphaKey[ m_alphaTargetKey ].value;
  323. m_alphaTargetKey++;
  324. computeAlphaRate();
  325. }
  326. }
  327. else
  328. m_alphaRate = 0.0f;
  329. if (m_alpha < 0.0f)
  330. m_alpha = 0.0f;
  331. else if (m_alpha > 1.0f)
  332. m_alpha = 1.0f;
  333. }
  334. //
  335. // Update color
  336. //
  337. m_color.red += m_colorRate.red;
  338. m_color.green += m_colorRate.green;
  339. m_color.blue += m_colorRate.blue;
  340. if (m_colorTargetKey < MAX_KEYFRAMES && m_colorKey[ m_colorTargetKey ].frame)
  341. {
  342. if (TheGameClient->getFrame() - m_createTimestamp >= m_colorKey[ m_colorTargetKey ].frame)
  343. {
  344. // can't set, because of colorscale
  345. // m_color = m_colorKey[ m_colorTargetKey ].color;
  346. m_colorTargetKey++;
  347. computeColorRate();
  348. }
  349. }
  350. else
  351. {
  352. m_colorRate.red = 0.0f;
  353. m_colorRate.green = 0.0f;
  354. m_colorRate.blue = 0.0f;
  355. }
  356. /// @todo Rethink this - at least its name
  357. m_color.red += m_colorScale;
  358. m_color.green += m_colorScale;
  359. m_color.blue += m_colorScale;
  360. if (m_color.red < 0.0f)
  361. m_color.red = 0.0f;
  362. else if (m_color.red > 1.0f)
  363. m_color.red = 1.0f;
  364. if (m_color.red < 0.0f)
  365. m_color.green = 0.0f;
  366. else if (m_color.green > 1.0f)
  367. m_color.green = 1.0f;
  368. if (m_color.blue < 0.0f)
  369. m_color.blue = 0.0f;
  370. else if (m_color.blue > 1.0f)
  371. m_color.blue = 1.0f;
  372. // reset the acceleration for accumulation next frame
  373. m_accel.z=m_accel.y=m_accel.x= 0.0f;
  374. // monitor lifetime
  375. if (m_lifetimeLeft && --m_lifetimeLeft == 0)
  376. return false;
  377. DEBUG_ASSERTCRASH( m_lifetimeLeft, ( "A particle has an infinite lifetime..." ));
  378. // if we've gone totally invisible, destroy ourselves
  379. if (isInvisible())
  380. return false;
  381. return true;
  382. }
  383. // ------------------------------------------------------------------------------------------------
  384. /** Do wind motion as specified by the particle system template, if present */
  385. // ------------------------------------------------------------------------------------------------
  386. void Particle::doWindMotion( void )
  387. {
  388. // get the angle of the wind
  389. Real windAngle = m_system->getWindAngle();
  390. // get the system position
  391. Coord3D systemPos;
  392. m_system->getPosition( &systemPos );
  393. // when we're attached objects and drawables we offset by that position as well
  394. if( ObjectID attachedObj = m_system->getAttachedObject() )
  395. {
  396. Object *obj = TheGameLogic->findObjectByID( attachedObj );
  397. if( obj )
  398. {
  399. const Coord3D *objPos = obj->getPosition();
  400. systemPos.x += objPos->x;
  401. systemPos.y += objPos->y;
  402. systemPos.z += objPos->z;
  403. } // end if
  404. } // end if
  405. else if( DrawableID attachedDraw = m_system->getAttachedDrawable() )
  406. {
  407. Drawable *draw = TheGameClient->findDrawableByID( attachedDraw );
  408. if( draw )
  409. {
  410. const Coord3D *drawPos = draw->getPosition();
  411. systemPos.x += drawPos->x;
  412. systemPos.y += drawPos->y;
  413. systemPos.z += drawPos->z;
  414. } // end if
  415. } // end else if
  416. //
  417. // compute a vector from the system position in the world to the particle ... we will use
  418. // this to compute how much force we apply
  419. //
  420. Coord3D v;
  421. v.x = m_pos.x - systemPos.x;
  422. v.y = m_pos.y - systemPos.y;
  423. v.z = m_pos.z - systemPos.z;
  424. // distance amounts for full force from wind and no force at all
  425. Real fullForceDistance = 75.0f;
  426. Real noForceDistance = 200.0f;
  427. //
  428. // given the distance from the wind position to the particle ... figure out how much
  429. // force we're going to apply to it. When it's further away (outside of the full force
  430. // distance) we will apply only a fraction of the force
  431. //
  432. Real distFromWind = v.length();
  433. if( distFromWind < noForceDistance )
  434. {
  435. Real windForceStrength = 2.0f * m_windRandomness;
  436. // only apply force if still within the circle of influence
  437. if( distFromWind > fullForceDistance )
  438. windForceStrength *= (1.0f - ((distFromWind - fullForceDistance) /
  439. (noForceDistance - fullForceDistance)));
  440. // integate the wind motion into the position
  441. m_pos.x += (Cos( windAngle ) * windForceStrength);
  442. m_pos.y += (Sin( windAngle ) * windForceStrength);
  443. } // end if
  444. } // doWindMotion
  445. // ------------------------------------------------------------------------------------------------
  446. /** Get priority of a particle ... which is the priority of it's attached system */
  447. // ------------------------------------------------------------------------------------------------
  448. ParticlePriorityType Particle::getPriority( void )
  449. {
  450. return m_system->getPriority();
  451. }
  452. // ------------------------------------------------------------------------------------------------
  453. /** Return true if this particle is invisible */
  454. // ------------------------------------------------------------------------------------------------
  455. Bool Particle::isInvisible( void )
  456. {
  457. switch (m_system->getShaderType())
  458. {
  459. case ParticleSystemInfo::ADDITIVE:
  460. // if color is black, this particle is invisible
  461. // check that we're not in the process of going to another color
  462. if (m_colorKey[ m_colorTargetKey ].frame == 0)
  463. {
  464. if ((m_color.red + m_color.green + m_color.blue) <= 0.06f)
  465. return true;
  466. }
  467. return false;
  468. case ParticleSystemInfo::ALPHA:
  469. // if alpha is zero, this particle is invisible
  470. if (m_alpha < 0.02f)
  471. return true;
  472. return false;
  473. case ParticleSystemInfo::ALPHA_TEST:
  474. // hmm... assume these particles are never invisible
  475. return false;
  476. case ParticleSystemInfo::MULTIPLY:
  477. // if color is white, this particle is invisible
  478. // check that we're not in the process of going to another color
  479. if (m_colorKey[ m_colorTargetKey ].frame == 0)
  480. {
  481. if ((m_color.red * m_color.green * m_color.blue) > 0.95f)
  482. return true;
  483. }
  484. return false;
  485. }
  486. // should never get here - if we do, data is incorrect
  487. return true;
  488. }
  489. // ------------------------------------------------------------------------------------------------
  490. /** CRC */
  491. // ------------------------------------------------------------------------------------------------
  492. void Particle::crc( Xfer *xfer )
  493. {
  494. } // end crc
  495. // ------------------------------------------------------------------------------------------------
  496. /** Xfer method
  497. * Version Info:
  498. * 1: Initial version */
  499. // ------------------------------------------------------------------------------------------------
  500. void Particle::xfer( Xfer *xfer )
  501. {
  502. // version
  503. XferVersion currentVersion = 1;
  504. XferVersion version = currentVersion;
  505. xfer->xferVersion( &version, currentVersion );
  506. // base class particle info
  507. ParticleInfo::xfer( xfer );
  508. // personality
  509. xfer->xferUnsignedInt( &m_personality );
  510. // acceleration
  511. xfer->xferCoord3D( &m_accel );
  512. // last position
  513. xfer->xferCoord3D( &m_lastPos );
  514. // lifetime left
  515. xfer->xferUnsignedInt( &m_lifetimeLeft );
  516. // creation timestamp
  517. xfer->xferUnsignedInt( &m_createTimestamp );
  518. // alpha
  519. xfer->xferReal( &m_alpha );
  520. // alpha rate
  521. xfer->xferReal( &m_alphaRate );
  522. // alpha target key
  523. xfer->xferInt( &m_alphaTargetKey );
  524. // color
  525. xfer->xferRGBColor( &m_color );
  526. // color rate
  527. xfer->xferRGBColor( &m_colorRate );
  528. // color target key
  529. xfer->xferInt( &m_colorTargetKey );
  530. // drawable
  531. DrawableID drawableID = INVALID_DRAWABLE_ID;
  532. xfer->xferDrawableID( &drawableID ); //saving for backwards compatibility when we supported drawables.
  533. // system under control as an id
  534. ParticleSystemID systemUnderControlID = m_systemUnderControl ? m_systemUnderControl->getSystemID() : INVALID_PARTICLE_SYSTEM_ID;
  535. xfer->xferUser( &systemUnderControlID, sizeof( ParticleSystemID ) );
  536. } // end xfer
  537. // ------------------------------------------------------------------------------------------------
  538. /** Load post process */
  539. // ------------------------------------------------------------------------------------------------
  540. void Particle::loadPostProcess( void )
  541. {
  542. // call base class post process
  543. ParticleInfo::loadPostProcess();
  544. // tidy up the m_systemUnderControl pointer
  545. if( m_systemUnderControlID != INVALID_PARTICLE_SYSTEM_ID )
  546. {
  547. ParticleSystem *system;
  548. // find system
  549. system = TheParticleSystemManager->findParticleSystem( m_systemUnderControlID );
  550. // set us as the control particle for this system
  551. system->setControlParticle( this );
  552. controlParticleSystem( system );
  553. // sanity
  554. if( m_systemUnderControlID == NULL )
  555. {
  556. DEBUG_CRASH(( "Particle::loadPostProcess - Unable to find system under control pointer\n" ));
  557. throw SC_INVALID_DATA;
  558. } // end if
  559. } // end if
  560. } // end loadPostProcess
  561. ///////////////////////////////////////////////////////////////////////////////////////////////////
  562. ///////////////////////////////////////////////////////////////////////////////////////////////////
  563. ///////////////////////////////////////////////////////////////////////////////////////////////////
  564. // ------------------------------------------------------------------------------------------------
  565. // ------------------------------------------------------------------------------------------------
  566. ParticleSystemInfo::ParticleSystemInfo()
  567. {
  568. m_priority = PARTICLE_PRIORITY_LOWEST;
  569. m_isGroundAligned = false;
  570. m_isEmitAboveGroundOnly = false;
  571. m_isParticleUpTowardsEmitter = false;
  572. //Added By Sadullah Nader
  573. //Initializations inserted
  574. m_driftVelocity.zero();
  575. m_gravity = 0.0f;
  576. m_isEmissionVolumeHollow = FALSE;
  577. m_isOneShot = FALSE;
  578. m_slavePosOffset.zero();
  579. m_systemLifetime = 0;
  580. //
  581. // some default values for the wind motion values
  582. m_windMotion = WIND_MOTION_NOT_USED;
  583. m_windAngle = 0.0f;
  584. m_windAngleChange = 0.15f; // higher is ping pong faster
  585. m_windAngleChangeMin = 0.15f;
  586. m_windAngleChangeMax = 0.45f;
  587. m_windMotionStartAngleMin = 0.0f;
  588. m_windMotionStartAngleMax = PI / 4.0f;
  589. m_windMotionStartAngle = m_windMotionStartAngleMin;
  590. m_windMotionEndAngleMin = TWO_PI - (PI / 4.0f);
  591. m_windMotionEndAngleMax = TWO_PI;
  592. m_windMotionEndAngle = m_windMotionEndAngleMin;
  593. m_windMotionMovingToEndAngle = TRUE;
  594. m_volumeParticleDepth = DEFAULT_VOLUME_PARTICLE_DEPTH;
  595. }
  596. void ParticleSystemInfo::tintAllColors( Color tintColor )
  597. {
  598. RGBColor rgb;
  599. rgb.setFromInt(tintColor);
  600. //This tints all but the first colorKey!!!
  601. for (int key = 1; key < MAX_KEYFRAMES; ++key )
  602. {
  603. m_colorKey[ key ].color.red *= (Real)(rgb.red ) / 255.0f;
  604. m_colorKey[ key ].color.green *= (Real)(rgb.green) / 255.0f;
  605. m_colorKey[ key ].color.blue *= (Real)(rgb.blue ) / 255.0f;
  606. }
  607. } // end loadPostProcess
  608. // ------------------------------------------------------------------------------------------------
  609. /** CRC */
  610. // ------------------------------------------------------------------------------------------------
  611. void ParticleSystemInfo::crc( Xfer *xfer )
  612. {
  613. } // end crc
  614. // ------------------------------------------------------------------------------------------------
  615. /** Xfer method
  616. * Version Info:
  617. * 1: Initial version */
  618. // ------------------------------------------------------------------------------------------------
  619. void ParticleSystemInfo::xfer( Xfer *xfer )
  620. {
  621. Int i;
  622. // version
  623. XferVersion currentVersion = 1;
  624. XferVersion version = currentVersion;
  625. xfer->xferVersion( &version, currentVersion );
  626. // is one shot
  627. xfer->xferBool( &m_isOneShot );
  628. // shader type
  629. xfer->xferUser( &m_shaderType, sizeof( ParticleShaderType ) );
  630. // particle type
  631. xfer->xferUser( &m_particleType, sizeof( ParticleType ) );
  632. // particle type name
  633. xfer->xferAsciiString( &m_particleTypeName );
  634. // angles
  635. GameClientRandomVariable tempRandom; //for backwards compatibility when we supported x,y
  636. xfer->xferUser( &tempRandom, sizeof( GameClientRandomVariable ) );
  637. xfer->xferUser( &tempRandom, sizeof( GameClientRandomVariable ) );
  638. xfer->xferUser( &m_angleZ, sizeof( GameClientRandomVariable ) );
  639. // angular rate
  640. xfer->xferUser( &tempRandom, sizeof( GameClientRandomVariable ) );
  641. xfer->xferUser( &tempRandom, sizeof( GameClientRandomVariable ) );
  642. xfer->xferUser( &m_angularRateZ, sizeof( GameClientRandomVariable ) );
  643. // angular damping
  644. xfer->xferUser( &m_angularDamping, sizeof( GameClientRandomVariable ) );
  645. // velocity damping
  646. xfer->xferUser( &m_velDamping, sizeof( GameClientRandomVariable ) );
  647. // lifetime
  648. xfer->xferUser( &m_lifetime, sizeof( GameClientRandomVariable ) );
  649. // system lifetime
  650. xfer->xferUnsignedInt( &m_systemLifetime );
  651. // start size
  652. xfer->xferUser( &m_startSize, sizeof( GameClientRandomVariable ) );
  653. // start size rate
  654. xfer->xferUser( &m_startSizeRate, sizeof( GameClientRandomVariable ) );
  655. // size rate
  656. xfer->xferUser( &m_sizeRate, sizeof( GameClientRandomVariable ) );
  657. // size rate damping
  658. xfer->xferUser( &m_sizeRateDamping, sizeof( GameClientRandomVariable ) );
  659. // alpha keys
  660. for( i = 0; i < MAX_KEYFRAMES; ++i )
  661. {
  662. xfer->xferUser( &m_alphaKey[ i ].var, sizeof( GameClientRandomVariable ) );
  663. xfer->xferUnsignedInt( &m_alphaKey[ i ].frame );
  664. } // end for, i
  665. // color keys
  666. for( i = 0; i < MAX_KEYFRAMES; ++i )
  667. {
  668. xfer->xferRGBColor( &m_colorKey[ i ].color );
  669. xfer->xferUnsignedInt( &m_colorKey[ i ].frame );
  670. } // end for, i
  671. // color scale
  672. xfer->xferUser( &m_colorScale, sizeof( GameClientRandomVariable ) );
  673. // burst delay
  674. xfer->xferUser( &m_burstDelay, sizeof( GameClientRandomVariable ) );
  675. // burst count
  676. xfer->xferUser( &m_burstCount, sizeof( GameClientRandomVariable ) );
  677. // initial delay
  678. xfer->xferUser( &m_initialDelay, sizeof( GameClientRandomVariable ) );
  679. // drift velocity
  680. xfer->xferCoord3D( &m_driftVelocity );
  681. // gravity
  682. xfer->xferReal( &m_gravity );
  683. // slave system name
  684. xfer->xferAsciiString( &m_slaveSystemName );
  685. // slave position offset
  686. xfer->xferCoord3D( &m_slavePosOffset );
  687. // attached system name
  688. xfer->xferAsciiString( &m_attachedSystemName );
  689. // emission velocity type, this must come before m_emissionVelocity
  690. xfer->xferUser( &m_emissionVelocityType, sizeof( EmissionVelocityType ) );
  691. // particle priority
  692. xfer->xferUser( &m_priority, sizeof( ParticlePriorityType ) );
  693. // emission velocity
  694. switch( m_emissionVelocityType )
  695. {
  696. // --------------------------------------------------------------------------------------------
  697. case ORTHO:
  698. xfer->xferUser( &m_emissionVelocity.ortho.x, sizeof( GameClientRandomVariable ) );
  699. xfer->xferUser( &m_emissionVelocity.ortho.y, sizeof( GameClientRandomVariable ) );
  700. xfer->xferUser( &m_emissionVelocity.ortho.z, sizeof( GameClientRandomVariable ) );
  701. break;
  702. // --------------------------------------------------------------------------------------------
  703. case SPHERICAL:
  704. xfer->xferUser( &m_emissionVelocity.spherical.speed, sizeof( GameClientRandomVariable ) );
  705. break;
  706. // --------------------------------------------------------------------------------------------
  707. case HEMISPHERICAL:
  708. xfer->xferUser( &m_emissionVelocity.hemispherical.speed, sizeof( GameClientRandomVariable ) );
  709. break;
  710. // --------------------------------------------------------------------------------------------
  711. case CYLINDRICAL:
  712. xfer->xferUser( &m_emissionVelocity.cylindrical.radial, sizeof( GameClientRandomVariable ) );
  713. xfer->xferUser( &m_emissionVelocity.cylindrical.normal, sizeof( GameClientRandomVariable ) );
  714. break;
  715. // --------------------------------------------------------------------------------------------
  716. case OUTWARD:
  717. xfer->xferUser( &m_emissionVelocity.outward.speed, sizeof( GameClientRandomVariable ) );
  718. xfer->xferUser( &m_emissionVelocity.outward.otherSpeed, sizeof( GameClientRandomVariable ) );
  719. break;
  720. } // end switch, m_emissionVelocityType
  721. // emission volume type
  722. xfer->xferUser( &m_emissionVolumeType, sizeof( EmissionVolumeType ) );
  723. // emission volume
  724. switch( m_emissionVolumeType )
  725. {
  726. // --------------------------------------------------------------------------------------------
  727. case POINT:
  728. // point has no data, it uses the systems position
  729. break;
  730. // --------------------------------------------------------------------------------------------
  731. case LINE:
  732. xfer->xferCoord3D( &m_emissionVolume.line.start );
  733. xfer->xferCoord3D( &m_emissionVolume.line.end );
  734. break;
  735. // --------------------------------------------------------------------------------------------
  736. case BOX:
  737. xfer->xferCoord3D( &m_emissionVolume.box.halfSize );
  738. break;
  739. // --------------------------------------------------------------------------------------------
  740. case SPHERE:
  741. xfer->xferReal( &m_emissionVolume.sphere.radius );
  742. break;
  743. // --------------------------------------------------------------------------------------------
  744. case CYLINDER:
  745. xfer->xferReal( &m_emissionVolume.cylinder.radius );
  746. xfer->xferReal( &m_emissionVolume.cylinder.length );
  747. break;
  748. } // end switch, m_emissionVolumeType
  749. // is emission volume hollow
  750. xfer->xferBool( &m_isEmissionVolumeHollow );
  751. // is ground aligned
  752. xfer->xferBool( &m_isGroundAligned );
  753. // emit above ground only
  754. xfer->xferBool( &m_isEmitAboveGroundOnly );
  755. // is particle up towards emitter
  756. xfer->xferBool( &m_isParticleUpTowardsEmitter );
  757. // wind motion
  758. xfer->xferUser( &m_windMotion, sizeof( WindMotion ) );
  759. // wind angle
  760. xfer->xferReal( &m_windAngle );
  761. // wind angle change
  762. xfer->xferReal( &m_windAngleChange );
  763. // wind angle change min
  764. xfer->xferReal( &m_windAngleChangeMin );
  765. // wind angle change max
  766. xfer->xferReal( &m_windAngleChangeMax );
  767. // wind motion start angle
  768. xfer->xferReal( &m_windMotionStartAngle );
  769. // wind motion start angle min
  770. xfer->xferReal( &m_windMotionStartAngleMin );
  771. // wind motion start angle max
  772. xfer->xferReal( &m_windMotionStartAngleMax );
  773. // wind motion end angle
  774. xfer->xferReal( &m_windMotionEndAngle );
  775. // wind motion end angle min
  776. xfer->xferReal( &m_windMotionEndAngleMin );
  777. // wind motion end angle max
  778. xfer->xferReal( &m_windMotionEndAngleMax );
  779. // wind motion moving to end angle
  780. xfer->xferByte( &m_windMotionMovingToEndAngle );
  781. } // end xfer
  782. // ------------------------------------------------------------------------------------------------
  783. /** Load post process */
  784. // ------------------------------------------------------------------------------------------------
  785. void ParticleSystemInfo::loadPostProcess( void )
  786. {
  787. } // end loadPostProcess
  788. ///////////////////////////////////////////////////////////////////////////////////////////////////
  789. // ParticleSystem /////////////////////////////////////////////////////////////////////////////////
  790. ///////////////////////////////////////////////////////////////////////////////////////////////////
  791. // ------------------------------------------------------------------------------------------------
  792. /** Read particle system properties from given file */
  793. // ------------------------------------------------------------------------------------------------
  794. ParticleSystem::ParticleSystem( const ParticleSystemTemplate *sysTemplate,
  795. ParticleSystemID id,
  796. Bool createSlaves )
  797. {
  798. m_systemParticlesHead = m_systemParticlesTail = NULL;
  799. m_isFirstPos = true;
  800. m_template = sysTemplate;
  801. m_systemID = id;
  802. //Added By Sadullah Nader
  803. //Initializations inserted
  804. m_lastPos.zero();
  805. m_pos.zero();
  806. m_velCoeff.zero();
  807. //
  808. m_attachedToDrawableID = INVALID_DRAWABLE_ID;
  809. m_attachedToObjectID = INVALID_ID;
  810. m_isLocalIdentity = true;
  811. m_localTransform.Make_Identity();
  812. m_isIdentity = true;
  813. m_transform.Make_Identity();
  814. m_skipParentXfrm = false;
  815. m_isStopped = false;
  816. m_isDestroyed = false;
  817. m_isSaveable = true;
  818. m_slavePosOffset = sysTemplate->m_slavePosOffset;
  819. ///@todo: further formalize this parameter with an UnsignedInt field in the editor
  820. m_volumeParticleDepth = DEFAULT_VOLUME_PARTICLE_DEPTH;
  821. m_driftVelocity = sysTemplate->m_driftVelocity;
  822. m_velCoeff.x = 1.0f;
  823. m_velCoeff.y = 1.0f;
  824. m_velCoeff.z = 1.0f;
  825. m_countCoeff = 1.0f;
  826. m_delayCoeff = 1.0f;
  827. m_sizeCoeff = 1.0f;
  828. m_gravity = sysTemplate->m_gravity;
  829. m_lifetime = sysTemplate->m_lifetime;
  830. m_startSize = sysTemplate->m_startSize;
  831. m_startSizeRate = sysTemplate->m_startSizeRate;
  832. m_sizeRate = sysTemplate->m_sizeRate;
  833. m_sizeRateDamping = sysTemplate->m_sizeRateDamping;
  834. for( int i=0; i<MAX_KEYFRAMES; i++ )
  835. m_alphaKey[i] = sysTemplate->m_alphaKey[i];
  836. for( i=0; i<MAX_KEYFRAMES; i++ )
  837. m_colorKey[i] = sysTemplate->m_colorKey[i];
  838. /// @todo It is confusing to do this conversion here...
  839. Real low = sysTemplate->m_colorScale.getMinimumValue();
  840. Real hi = sysTemplate->m_colorScale.getMaximumValue();
  841. m_colorScale.setRange( low / 255.0f, hi / 255.0f );
  842. m_burstDelay = sysTemplate->m_burstDelay;
  843. m_burstDelayLeft = 0;
  844. m_burstCount = sysTemplate->m_burstCount;
  845. m_isOneShot = sysTemplate->m_isOneShot;
  846. m_delayLeft = (UnsignedInt)sysTemplate->m_initialDelay.getValue();
  847. m_startTimestamp = TheGameClient->getFrame();
  848. m_systemLifetimeLeft = sysTemplate->m_systemLifetime;
  849. if (sysTemplate->m_systemLifetime)
  850. m_isForever = false;
  851. else
  852. m_isForever = true;
  853. m_accumulatedSizeBonus = 0;
  854. m_velDamping = sysTemplate->m_velDamping;
  855. m_angleZ = sysTemplate->m_angleZ;
  856. m_angularRateZ = sysTemplate->m_angularRateZ;
  857. m_angularDamping = sysTemplate->m_angularDamping;
  858. m_priority = sysTemplate->m_priority;
  859. m_emissionVelocityType = sysTemplate->m_emissionVelocityType;
  860. m_emissionVelocity = sysTemplate->m_emissionVelocity;
  861. m_emissionVolumeType = sysTemplate->m_emissionVolumeType;
  862. m_emissionVolume = sysTemplate->m_emissionVolume;
  863. m_isEmissionVolumeHollow = sysTemplate->m_isEmissionVolumeHollow;
  864. m_isGroundAligned = sysTemplate->m_isGroundAligned;
  865. m_isEmitAboveGroundOnly = sysTemplate->m_isEmitAboveGroundOnly;
  866. m_isParticleUpTowardsEmitter = sysTemplate->m_isParticleUpTowardsEmitter;
  867. m_windMotion = sysTemplate->m_windMotion;
  868. m_windAngleChange = sysTemplate->m_windAngleChange;
  869. m_windAngleChangeMin = sysTemplate->m_windAngleChangeMin;
  870. m_windAngleChangeMax = sysTemplate->m_windAngleChangeMax;
  871. m_windMotionStartAngleMin = sysTemplate->m_windMotionStartAngleMin;
  872. m_windMotionStartAngleMax = sysTemplate->m_windMotionStartAngleMax;
  873. m_windMotionEndAngleMin = sysTemplate->m_windMotionEndAngleMin;
  874. m_windMotionEndAngleMax = sysTemplate->m_windMotionEndAngleMax;
  875. m_windMotionMovingToEndAngle = sysTemplate->m_windMotionMovingToEndAngle;
  876. m_windMotionStartAngle = GameClientRandomValueReal( m_windMotionStartAngleMin, m_windMotionStartAngleMax );
  877. m_windMotionEndAngle = GameClientRandomValueReal( m_windMotionEndAngleMin, m_windMotionEndAngleMax );
  878. m_windAngle = GameClientRandomValueReal( m_windMotionStartAngle, m_windMotionEndAngle );
  879. m_shaderType = sysTemplate->m_shaderType;
  880. m_particleType = sysTemplate->m_particleType;
  881. m_particleTypeName = sysTemplate->m_particleTypeName;
  882. m_isStopped = false;
  883. // set up slave particle system, if any
  884. m_masterSystemID = INVALID_PARTICLE_SYSTEM_ID;
  885. m_slaveSystemID = INVALID_PARTICLE_SYSTEM_ID;
  886. m_masterSystem = NULL;
  887. m_slaveSystem = NULL;
  888. if( createSlaves )
  889. {
  890. ParticleSystem *slaveSystem = sysTemplate->createSlaveSystem();
  891. if( slaveSystem )
  892. {
  893. setSlave( slaveSystem );
  894. m_slaveSystem->setMaster( this );
  895. } // end if
  896. } // end if
  897. m_attachedSystemName = sysTemplate->m_attachedSystemName;
  898. m_particleCount = 0;
  899. m_personalityStore = 0;
  900. m_controlParticle = NULL;
  901. TheParticleSystemManager->friend_addParticleSystem(this);
  902. //DEBUG_ASSERTLOG(!(m_totalParticleSystemCount % 10 == 0), ( "TotalParticleSystemCount = %d\n", m_totalParticleSystemCount ));
  903. }
  904. // ------------------------------------------------------------------------------------------------
  905. /** Destroy particle system and all of its particles */
  906. // ------------------------------------------------------------------------------------------------
  907. ParticleSystem::~ParticleSystem()
  908. {
  909. // tell any of our slave systems that we are going away
  910. if( m_slaveSystem )
  911. {
  912. DEBUG_ASSERTCRASH( m_slaveSystem->getMaster() == this, ("~ParticleSystem: Our slave doesn't have us as a master!\n") );
  913. m_slaveSystem->setMaster( NULL );
  914. setSlave( NULL );
  915. } // end if
  916. // tell any master system that *we* are going away
  917. if( m_masterSystem )
  918. {
  919. DEBUG_ASSERTCRASH( m_masterSystem->getSlave() == this, ("~ParticleSystem: Our master doesn't have us as a slave!\n") );
  920. m_masterSystem->setSlave( NULL );
  921. setMaster( NULL );
  922. } // end if
  923. // destroy all particles "in the air"
  924. while (m_systemParticlesHead)
  925. m_systemParticlesHead->deleteInstance();
  926. m_attachedToDrawableID = INVALID_DRAWABLE_ID;
  927. m_attachedToObjectID = INVALID_ID;
  928. // if this system was controlled by a particle, detach
  929. if (m_controlParticle)
  930. m_controlParticle->detachControlledParticleSystem();
  931. m_controlParticle = NULL;
  932. TheParticleSystemManager->friend_removeParticleSystem(this);
  933. //DEBUG_ASSERTLOG(!(m_totalParticleSystemCount % 10 == 0), ( "TotalParticleSystemCount = %d\n", m_totalParticleSystemCount ));
  934. }
  935. // ------------------------------------------------------------------------------------------------
  936. // ------------------------------------------------------------------------------------------------
  937. void ParticleSystem::setMaster( ParticleSystem *master )
  938. {
  939. m_masterSystem = master;
  940. m_masterSystemID = master ? master->getSystemID() : INVALID_PARTICLE_SYSTEM_ID;
  941. } // end set Master
  942. // ------------------------------------------------------------------------------------------------
  943. // ------------------------------------------------------------------------------------------------
  944. void ParticleSystem::setSlave( ParticleSystem *slave )
  945. {
  946. m_slaveSystem = slave;
  947. m_slaveSystemID = slave ? slave->getSystemID() : INVALID_PARTICLE_SYSTEM_ID;
  948. } // end setSlave
  949. // ------------------------------------------------------------------------------------------------
  950. // ------------------------------------------------------------------------------------------------
  951. void ParticleSystem::setSaveable(Bool b)
  952. {
  953. m_isSaveable = b;
  954. if (m_slaveSystem)
  955. m_slaveSystem->setSaveable(b);
  956. }
  957. // ------------------------------------------------------------------------------------------------
  958. /** (Re)start a stopped particle system */
  959. // ------------------------------------------------------------------------------------------------
  960. void ParticleSystem::start( void )
  961. {
  962. m_isStopped = false;
  963. }
  964. // ------------------------------------------------------------------------------------------------
  965. /** Stop a particle system from emitting */
  966. // ------------------------------------------------------------------------------------------------
  967. void ParticleSystem::stop( void )
  968. {
  969. m_isStopped = true;
  970. }
  971. // ------------------------------------------------------------------------------------------------
  972. /** Stop emitting, wait for all of our particles to die, then destroy self. */
  973. // ------------------------------------------------------------------------------------------------
  974. void ParticleSystem::destroy( void )
  975. {
  976. m_isDestroyed = true;
  977. if( m_slaveSystem )
  978. {
  979. m_slaveSystem->destroy(); // If we don't it will leak forever. We are solely responsible for it.
  980. }
  981. }
  982. // ------------------------------------------------------------------------------------------------
  983. /** Get the position of the particle system */
  984. // ------------------------------------------------------------------------------------------------
  985. void ParticleSystem::getPosition( Coord3D *pos )
  986. {
  987. Vector3 vec;
  988. m_localTransform.Get_Translation(&vec);
  989. if (pos)
  990. { pos->x=vec.X;
  991. pos->y=vec.Y;
  992. pos->z=vec.Z;
  993. }
  994. }
  995. // ------------------------------------------------------------------------------------------------
  996. /** Set the position of the particle system */
  997. // ------------------------------------------------------------------------------------------------
  998. void ParticleSystem::setPosition( const Coord3D *pos )
  999. {
  1000. m_localTransform.Set_X_Translation( pos->x );
  1001. m_localTransform.Set_Y_Translation( pos->y );
  1002. m_localTransform.Set_Z_Translation( pos->z );
  1003. m_isLocalIdentity = false;
  1004. }
  1005. // ------------------------------------------------------------------------------------------------
  1006. /** Set the system's local transform */
  1007. // ------------------------------------------------------------------------------------------------
  1008. void ParticleSystem::setLocalTransform( const Matrix3D *matrix )
  1009. {
  1010. m_localTransform = *matrix;
  1011. m_isLocalIdentity = false;
  1012. }
  1013. // ------------------------------------------------------------------------------------------------
  1014. /** Rotate local transform matrix */
  1015. // ------------------------------------------------------------------------------------------------
  1016. void ParticleSystem::rotateLocalTransformX( Real x )
  1017. {
  1018. m_localTransform.Rotate_X( x );
  1019. m_isLocalIdentity = false;
  1020. }
  1021. // ------------------------------------------------------------------------------------------------
  1022. /** Rotate local transform matrix */
  1023. // ------------------------------------------------------------------------------------------------
  1024. void ParticleSystem::rotateLocalTransformY( Real y )
  1025. {
  1026. m_localTransform.Rotate_Y( y );
  1027. m_isLocalIdentity = false;
  1028. }
  1029. // ------------------------------------------------------------------------------------------------
  1030. /** Rotate local transform matrix */
  1031. // ------------------------------------------------------------------------------------------------
  1032. void ParticleSystem::rotateLocalTransformZ( Real z )
  1033. {
  1034. m_localTransform.Rotate_Z( z );
  1035. m_isLocalIdentity = false;
  1036. }
  1037. // ------------------------------------------------------------------------------------------------
  1038. /** Attach this particle system to a Drawable */
  1039. // ------------------------------------------------------------------------------------------------
  1040. void ParticleSystem::attachToDrawable( const Drawable *draw )
  1041. {
  1042. if (draw)
  1043. m_attachedToDrawableID = draw->getID();
  1044. else
  1045. m_attachedToDrawableID = INVALID_DRAWABLE_ID;
  1046. }
  1047. // ------------------------------------------------------------------------------------------------
  1048. /** Attach this particle system to a Drawable */
  1049. // ------------------------------------------------------------------------------------------------
  1050. void ParticleSystem::attachToObject( const Object *obj )
  1051. {
  1052. if (obj)
  1053. m_attachedToObjectID = obj->getID();
  1054. else
  1055. m_attachedToObjectID = INVALID_ID;
  1056. }
  1057. // ------------------------------------------------------------------------------------------------
  1058. /** Compute a random point on a unit sphere
  1059. * @todo The density of random points generated is not uniform within the sphere */
  1060. // ------------------------------------------------------------------------------------------------
  1061. const Coord3D *ParticleSystem::computePointOnUnitSphere( void )
  1062. {
  1063. static Coord3D point;
  1064. do
  1065. {
  1066. point.x = GameClientRandomValueReal( -1.0f, 1.0f );
  1067. point.y = GameClientRandomValueReal( -1.0f, 1.0f );
  1068. point.z = GameClientRandomValueReal( -1.0f, 1.0f );
  1069. }
  1070. while (point.x == 0.0f && point.y == 0.0f && point.z == 0.0f);
  1071. point.normalize();
  1072. return &point;
  1073. }
  1074. // ------------------------------------------------------------------------------------------------
  1075. /** Compute a velocity vector based on emission properties */
  1076. // ------------------------------------------------------------------------------------------------
  1077. const Coord3D *ParticleSystem::computeParticleVelocity( const Coord3D *pos )
  1078. {
  1079. static Coord3D newVel;
  1080. switch( m_emissionVelocityType )
  1081. {
  1082. case ORTHO:
  1083. newVel.x = m_emissionVelocity.ortho.x.getValue();
  1084. newVel.y = m_emissionVelocity.ortho.y.getValue();
  1085. newVel.z = m_emissionVelocity.ortho.z.getValue();
  1086. break;
  1087. case CYLINDRICAL:
  1088. {
  1089. Real radialSpeed, angle;
  1090. radialSpeed = m_emissionVelocity.cylindrical.radial.getValue();
  1091. angle = GameClientRandomValueReal( 0, 2.0f*PI );
  1092. newVel.x = radialSpeed * cos( angle );
  1093. newVel.y = radialSpeed * sin( angle );
  1094. newVel.z = m_emissionVelocity.cylindrical.normal.getValue();
  1095. break;
  1096. }
  1097. // "outward" velocity is directed along the surface normal of the emission volume
  1098. case OUTWARD:
  1099. {
  1100. Real speed = m_emissionVelocity.outward.speed.getValue();
  1101. Real otherSpeed = m_emissionVelocity.outward.otherSpeed.getValue();
  1102. Coord3D sysPos;
  1103. /*
  1104. sysPos.x = m_localTransform.Get_X_Translation();
  1105. sysPos.y = m_localTransform.Get_Y_Translation();
  1106. sysPos.z = m_localTransform.Get_Z_Translation();
  1107. */
  1108. sysPos.x = 0.0f;
  1109. sysPos.y = 0.0f;
  1110. sysPos.z = 0.0f;
  1111. switch( m_emissionVolumeType )
  1112. {
  1113. case CYLINDER:
  1114. Coord2D disk;
  1115. disk.x = pos->x - sysPos.x;
  1116. disk.y = pos->y - sysPos.y;
  1117. disk.normalize();
  1118. newVel.x = speed * disk.x;
  1119. newVel.y = speed * disk.y;
  1120. newVel.z = otherSpeed;
  1121. break;
  1122. case BOX: ///< @todo Implement BOX OUTWARD velocity
  1123. case SPHERE:
  1124. {
  1125. newVel.x = pos->x - sysPos.x;
  1126. newVel.y = pos->y - sysPos.y;
  1127. newVel.z = pos->z - sysPos.z;
  1128. newVel.normalize();
  1129. newVel.x *= speed;
  1130. newVel.y *= speed;
  1131. newVel.z *= speed;
  1132. break;
  1133. }
  1134. case LINE:
  1135. {
  1136. Coord3D along; // unit vector along line direction
  1137. along.x = m_emissionVolume.line.end.x - m_emissionVolume.line.start.x;
  1138. along.y = m_emissionVolume.line.end.y - m_emissionVolume.line.start.y;
  1139. along.z = m_emissionVolume.line.end.z - m_emissionVolume.line.start.z;
  1140. along.normalize();
  1141. Coord3D perp; // unit vector perpendicular to the along/up plane
  1142. Coord3D up; // unit vector in the up direction (Z)
  1143. up.x = 0.0;
  1144. up.y = 0.0;
  1145. up.z = 1.0;
  1146. perp.crossProduct( &up, &along, &perp );
  1147. up.crossProduct( &along, &perp, &up );
  1148. // "speed" is in 'horizontal' plane, and "otherSpeed" is 'vertical'
  1149. newVel.x = speed * perp.x + otherSpeed * up.x;
  1150. newVel.y = speed * perp.y + otherSpeed * up.y;
  1151. newVel.z = speed * perp.z + otherSpeed * up.z;
  1152. break;
  1153. }
  1154. case POINT:
  1155. {
  1156. Coord3D vel = *computePointOnUnitSphere();
  1157. newVel.x = speed * vel.x;
  1158. newVel.y = speed * vel.y;
  1159. newVel.z = speed * vel.z;
  1160. break;
  1161. }
  1162. }
  1163. break;
  1164. }
  1165. case SPHERICAL:
  1166. {
  1167. Real speed = m_emissionVelocity.spherical.speed.getValue();
  1168. Coord3D vel = *computePointOnUnitSphere();
  1169. newVel.x = speed * vel.x;
  1170. newVel.y = speed * vel.y;
  1171. newVel.z = speed * vel.z;
  1172. break;
  1173. }
  1174. case HEMISPHERICAL:
  1175. {
  1176. Coord3D vel;
  1177. Real speed = m_emissionVelocity.spherical.speed.getValue();
  1178. do
  1179. {
  1180. vel.x = GameClientRandomValueReal( -1.0f, 1.0f );
  1181. vel.y = GameClientRandomValueReal( -1.0f, 1.0f );
  1182. vel.z = GameClientRandomValueReal( 0.0f, 1.0f );
  1183. }
  1184. while (vel.x == 0.0f && vel.y == 0.0f && vel.z == 0.0f);
  1185. vel.normalize();
  1186. newVel.x = speed * vel.x;
  1187. newVel.y = speed * vel.y;
  1188. newVel.z = speed * vel.z;
  1189. break;
  1190. }
  1191. default:
  1192. newVel.x = 0.0f;
  1193. newVel.y = 0.0f;
  1194. newVel.z = 0.0f;
  1195. break;
  1196. }
  1197. // scale the velocity by the velocity multiplier
  1198. newVel.x *= m_velCoeff.x*(0.5f+TheGlobalData->m_particleScale/2.0f);
  1199. newVel.y *= m_velCoeff.y*(0.5f+TheGlobalData->m_particleScale/2.0f);
  1200. newVel.z *= m_velCoeff.z*(0.5f+TheGlobalData->m_particleScale/2.0f);
  1201. return &newVel;
  1202. }
  1203. // ------------------------------------------------------------------------------------------------
  1204. /** Compute a position based on emission properties */
  1205. // ------------------------------------------------------------------------------------------------
  1206. const Coord3D *ParticleSystem::computeParticlePosition( void )
  1207. {
  1208. static Coord3D newPos;
  1209. switch( m_emissionVolumeType )
  1210. {
  1211. case CYLINDER:
  1212. {
  1213. Real angle = GameClientRandomValueReal( 0, 2.0f*PI );
  1214. Real radius;
  1215. if (m_isEmissionVolumeHollow)
  1216. radius = m_emissionVolume.cylinder.radius;
  1217. else
  1218. radius = GameClientRandomValueReal( 0.0f, m_emissionVolume.cylinder.radius );
  1219. newPos.x = radius * cos( angle );
  1220. newPos.y = radius * sin( angle );
  1221. Real halfLength = m_emissionVolume.cylinder.length/2.0f;
  1222. newPos.z = GameClientRandomValueReal( -halfLength, halfLength );
  1223. break;
  1224. }
  1225. case SPHERE:
  1226. {
  1227. Real radius;
  1228. if (m_isEmissionVolumeHollow)
  1229. radius = m_emissionVolume.sphere.radius;
  1230. else
  1231. radius = GameClientRandomValueReal( 0.0f, m_emissionVolume.sphere.radius );
  1232. newPos = *computePointOnUnitSphere();
  1233. newPos.x *= radius;
  1234. newPos.y *= radius;
  1235. newPos.z *= radius;
  1236. break;
  1237. }
  1238. case BOX:
  1239. {
  1240. if (m_isEmissionVolumeHollow) {
  1241. // determine which side to generate on.
  1242. // 0 is bottom, 3 is top,
  1243. // 1 is left , 4 is right
  1244. // 2 is front, 5 is right back
  1245. int side = GameClientRandomValue(0, 6);
  1246. if (side % 3 == 0) {
  1247. // generate X, Y
  1248. newPos.x = GameClientRandomValueReal( -m_emissionVolume.box.halfSize.x, m_emissionVolume.box.halfSize.x );
  1249. newPos.y = GameClientRandomValueReal( -m_emissionVolume.box.halfSize.y, m_emissionVolume.box.halfSize.y );
  1250. if (side == 0) {
  1251. newPos.z = -m_emissionVolume.box.halfSize.z;
  1252. } else {
  1253. newPos.z = m_emissionVolume.box.halfSize.z;
  1254. }
  1255. } else if (side % 3 == 1) {
  1256. // generate Y, Z
  1257. newPos.y = GameClientRandomValueReal( -m_emissionVolume.box.halfSize.y, m_emissionVolume.box.halfSize.y );
  1258. newPos.z = GameClientRandomValueReal( -m_emissionVolume.box.halfSize.z, m_emissionVolume.box.halfSize.z );
  1259. if (side == 1) {
  1260. newPos.x = -m_emissionVolume.box.halfSize.x;
  1261. } else {
  1262. newPos.x = m_emissionVolume.box.halfSize.y;
  1263. }
  1264. } else if (side % 3 == 2) {
  1265. // generate X, Z
  1266. newPos.x = GameClientRandomValueReal( -m_emissionVolume.box.halfSize.x, m_emissionVolume.box.halfSize.x );
  1267. newPos.z = GameClientRandomValueReal( -m_emissionVolume.box.halfSize.z, m_emissionVolume.box.halfSize.z );
  1268. if (side == 2) {
  1269. newPos.y = -m_emissionVolume.box.halfSize.y;
  1270. } else {
  1271. newPos.y = m_emissionVolume.box.halfSize.y;
  1272. }
  1273. }
  1274. } else {
  1275. newPos.x = GameClientRandomValueReal( -m_emissionVolume.box.halfSize.x, m_emissionVolume.box.halfSize.x );
  1276. newPos.y = GameClientRandomValueReal( -m_emissionVolume.box.halfSize.y, m_emissionVolume.box.halfSize.y );
  1277. newPos.z = GameClientRandomValueReal( -m_emissionVolume.box.halfSize.z, m_emissionVolume.box.halfSize.z );
  1278. break;
  1279. }
  1280. }
  1281. case LINE:
  1282. {
  1283. Coord3D delta, start, end;
  1284. start = m_emissionVolume.line.start;
  1285. end = m_emissionVolume.line.end;
  1286. delta.x = end.x - start.x;
  1287. delta.y = end.y - start.y;
  1288. delta.z = end.z - start.z;
  1289. Real t = GameClientRandomValueReal( 0.0f, 1.0f );
  1290. newPos.x = start.x + t * delta.x;
  1291. newPos.y = start.y + t * delta.y;
  1292. newPos.z = start.z + t * delta.z;
  1293. break;
  1294. }
  1295. case POINT:
  1296. default:
  1297. newPos.x = 0.0f;
  1298. newPos.y = 0.0f;
  1299. newPos.z = 0.0f;
  1300. break;
  1301. }
  1302. newPos.x *= (0.5f+TheGlobalData->m_particleScale/2.0f);
  1303. newPos.y *= (0.5f+TheGlobalData->m_particleScale/2.0f);
  1304. newPos.z *= (0.5f+TheGlobalData->m_particleScale/2.0f);
  1305. return &newPos;
  1306. }
  1307. // ------------------------------------------------------------------------------------------------
  1308. /** Factory method for particles. */
  1309. // ------------------------------------------------------------------------------------------------
  1310. Particle *ParticleSystem::createParticle( const ParticleInfo *info,
  1311. ParticlePriorityType priority,
  1312. Bool forceCreate )
  1313. {
  1314. //
  1315. // if we aren't absolutely forcing this particle to be created (which is needed when
  1316. // loading and creating particle systems from the save games) we need to check a few
  1317. // restrictions before this particle can really be created
  1318. //
  1319. if( forceCreate == FALSE )
  1320. {
  1321. if (TheGlobalData->m_useFX == FALSE)
  1322. return NULL;
  1323. //
  1324. // Enforce particle limit.
  1325. // If we are at the limit, destroy the oldest particle in order
  1326. // to make room for this one.
  1327. //
  1328. //
  1329. // Check if particle is below priorities we allow for this FPS or if it being skipped because
  1330. // all particesl are being skipped (excluding special fps independent particles at
  1331. // getMinDynamicParticleSkipPriority())
  1332. //
  1333. if( priority < TheGameLODManager->getMinDynamicParticlePriority() ||
  1334. (priority < TheGameLODManager->getMinDynamicParticleSkipPriority() &&
  1335. TheGameLODManager->isParticleSkipped()) )
  1336. return NULL;
  1337. if ( getParticleCount() > 0 && priority == AREA_EFFECT && m_isGroundAligned && TheParticleSystemManager->getFieldParticleCount() > (UnsignedInt)TheGlobalData->m_maxFieldParticleCount )
  1338. return NULL;
  1339. // ALWAYS_RENDER particles are exempt from all count limits, and are always created, regardless of LOD issues.
  1340. if (priority != ALWAYS_RENDER)
  1341. {
  1342. int numInExcess = TheParticleSystemManager->getParticleCount() - (UnsignedInt)TheGlobalData->m_maxParticleCount;
  1343. if ( numInExcess > 0)
  1344. {
  1345. if( TheParticleSystemManager->removeOldestParticles((UnsignedInt) numInExcess, priority) != numInExcess )
  1346. return NULL; // could not remove enough particles, don't create new stuff
  1347. }
  1348. if (TheGlobalData->m_maxParticleCount == 0)
  1349. return NULL;
  1350. }
  1351. } // end if
  1352. Particle *p = newInstance(Particle)( this, info );
  1353. return p;
  1354. }
  1355. // ------------------------------------------------------------------------------------------------
  1356. /** Generate a new, random set of ParticleInfo
  1357. * particleNum and particleCount are used to get 'tween frame particles emitted in the correct
  1358. * place. (jkmcd) */
  1359. // ------------------------------------------------------------------------------------------------
  1360. const ParticleInfo *ParticleSystem::generateParticleInfo( Int particleNum, Int particleCount )
  1361. {
  1362. static ParticleInfo info;
  1363. if (particleCount == 0) {
  1364. DEBUG_CRASH(("particleCount must NOT be 0. Set to 1 or greater."));
  1365. return &info;
  1366. }
  1367. // NOTE: position MUST be computed before velocity, in case OUTWARD velocity is
  1368. // specified, which must know where the particle is in space.
  1369. info.m_pos = *computeParticlePosition();
  1370. info.m_vel = *computeParticleVelocity( &info.m_pos );
  1371. // transform the position and velocity, if necessary
  1372. /// @todo Avoid conversion from Coord3D to Vector3 somehow
  1373. if (m_isIdentity == false)
  1374. {
  1375. // transform particle position to world coordinates
  1376. Vector3 p, pr;
  1377. Coord3D emissionAdjustment; // this is the adjustment for inter-frame emission
  1378. // @todo : This should work, if m_lastPos = m_pos is removed from here but it doesn't.
  1379. // @todo : Investigate why. jkmcd
  1380. if (m_isFirstPos) {
  1381. m_lastPos = m_pos;
  1382. m_isFirstPos = false;
  1383. }
  1384. emissionAdjustment.x = (1 - (INT_TO_REAL(particleNum) / particleCount)) * (m_pos.x - m_lastPos.x);
  1385. emissionAdjustment.y = (1 - (INT_TO_REAL(particleNum) / particleCount)) * (m_pos.y - m_lastPos.y);
  1386. emissionAdjustment.z = (1 - (INT_TO_REAL(particleNum) / particleCount)) * (m_pos.z - m_lastPos.z);
  1387. p.X = info.m_pos.x;
  1388. p.Y = info.m_pos.y;
  1389. p.Z = info.m_pos.z;
  1390. #ifdef ALLOW_TEMPORARIES
  1391. pr = m_transform * p;
  1392. #else
  1393. m_transform.mulVector3(p, pr);
  1394. #endif
  1395. info.m_pos.x = pr.X - emissionAdjustment.x;
  1396. info.m_pos.y = pr.Y - emissionAdjustment.y;
  1397. info.m_pos.z = pr.Z - emissionAdjustment.z;
  1398. // transform particle velocity to world coordinates
  1399. Vector3 v, vr;
  1400. v.X = info.m_vel.x;
  1401. v.Y = info.m_vel.y;
  1402. v.Z = info.m_vel.z;
  1403. Matrix3D::Rotate_Vector( m_transform, v, &vr );
  1404. info.m_vel.x = vr.X;
  1405. info.m_vel.y = vr.Y;
  1406. info.m_vel.z = vr.Z;
  1407. }
  1408. info.m_velDamping = m_velDamping.getValue();
  1409. info.m_angularDamping = m_angularDamping.getValue();
  1410. info.m_angleZ = m_angleZ.getValue();
  1411. info.m_angularRateZ = m_angularRateZ.getValue();
  1412. info.m_lifetime = (UnsignedInt)m_lifetime.getValue();
  1413. info.m_size = m_startSize.getValue()*m_sizeCoeff*TheGlobalData->m_particleScale;
  1414. info.m_sizeRate = m_sizeRate.getValue()*m_sizeCoeff*TheGlobalData->m_particleScale;
  1415. info.m_sizeRateDamping = m_sizeRateDamping.getValue();
  1416. // Keeping a running tally makes each successive particle spawned start a bit bigger (or smaller).
  1417. info.m_size += m_accumulatedSizeBonus;
  1418. m_accumulatedSizeBonus += m_startSizeRate.getValue();
  1419. if( m_accumulatedSizeBonus )
  1420. m_accumulatedSizeBonus = min( m_accumulatedSizeBonus, (float)MAX_SIZE_BONUS );
  1421. for( int i=0; i<MAX_KEYFRAMES; i++ )
  1422. {
  1423. info.m_alphaKey[i].value = m_alphaKey[i].var.getValue();
  1424. info.m_alphaKey[i].frame = m_alphaKey[i].frame;
  1425. info.m_colorKey[i] = m_colorKey[i];
  1426. }
  1427. /*
  1428. info.m_color.red = m_color.red.getValue();
  1429. info.m_color.green = m_color.green.getValue();
  1430. info.m_color.blue = m_color.blue.getValue();
  1431. */
  1432. info.m_colorScale = m_colorScale.getValue();
  1433. #ifdef ALLOW_TEMPORARIES
  1434. Vector3 pos = m_transform * Vector3(0, 0, 0);
  1435. #else
  1436. Vector3 pos;
  1437. m_transform.mulVector3(Vector3(0, 0, 0), pos);
  1438. #endif
  1439. info.m_emitterPos.x = pos.X;
  1440. info.m_emitterPos.y = pos.Y;
  1441. info.m_emitterPos.z = pos.Z;
  1442. info.m_particleUpTowardsEmitter = m_isParticleUpTowardsEmitter;
  1443. info.m_windRandomness = GameClientRandomValueReal( 0.7f, 1.3f );
  1444. return &info;
  1445. }
  1446. // ------------------------------------------------------------------------------------------------
  1447. /** Update this particle system, potentially generating new particles */
  1448. // ------------------------------------------------------------------------------------------------
  1449. Bool ParticleSystem::update( Int localPlayerIndex )
  1450. {
  1451. if (TheGlobalData->m_useFX == FALSE)
  1452. return false;
  1453. // do initial delay ... note, this currently delays the lifetime
  1454. if (m_delayLeft)
  1455. {
  1456. --m_delayLeft;
  1457. // system actually "starts" once initial delay is over
  1458. /// @todo reset start time when system is stopped/started
  1459. if (m_delayLeft == 0)
  1460. m_startTimestamp = TheGameClient->getFrame();
  1461. return true;
  1462. }
  1463. // update the wind motion
  1464. if (m_windMotion != ParticleSystemInfo::WIND_MOTION_NOT_USED )
  1465. updateWindMotion();
  1466. // if this system is attached to a Drawable/Object, update the current transform
  1467. // matrix so generated particles' are relative to the parent Drawable's
  1468. // position and orientation
  1469. Bool transformSet = false;
  1470. const Matrix3D *parentXfrm = NULL;
  1471. Bool isShrouded = false;
  1472. if (m_attachedToDrawableID)
  1473. {
  1474. Drawable *attachedTo = TheGameClient->findDrawableByID( m_attachedToDrawableID );
  1475. if (attachedTo)
  1476. {
  1477. if (attachedTo->getFullyObscuredByShroud())
  1478. isShrouded = true;
  1479. parentXfrm = attachedTo->getTransformMatrix();
  1480. m_lastPos = m_pos;
  1481. m_pos = *attachedTo->getPosition();
  1482. }
  1483. else
  1484. {
  1485. // Drawable has been destroyed - lose our attachment to it
  1486. m_attachedToDrawableID = INVALID_DRAWABLE_ID;
  1487. // destroy ourselves
  1488. destroy();
  1489. }
  1490. }
  1491. else if (m_attachedToObjectID)
  1492. {
  1493. Object *objectAttachedTo = TheGameLogic->findObjectByID( m_attachedToObjectID );
  1494. if (objectAttachedTo)
  1495. {
  1496. if (!isShrouded)
  1497. isShrouded = (objectAttachedTo->getShroudedStatus(localPlayerIndex) >= OBJECTSHROUD_FOGGED);
  1498. const Drawable * draw = objectAttachedTo->getDrawable();
  1499. if ( draw )
  1500. parentXfrm = draw->getTransformMatrix();
  1501. else
  1502. parentXfrm = objectAttachedTo->getTransformMatrix();
  1503. m_lastPos = m_pos;
  1504. m_pos = *objectAttachedTo->getPosition();
  1505. }
  1506. else
  1507. {
  1508. // Drawable has been destroyed - lose our attachment to it
  1509. m_attachedToObjectID = INVALID_ID;
  1510. // destroy ourselves
  1511. destroy();
  1512. }
  1513. }
  1514. if (parentXfrm)
  1515. {
  1516. if (m_skipParentXfrm)
  1517. {
  1518. //this particle system is already in world space so no need to apply parent xform.
  1519. m_transform = m_localTransform;
  1520. }
  1521. else
  1522. {
  1523. // if system has its own local transform, concatenate them
  1524. if (m_isLocalIdentity == false)
  1525. #ifdef ALLOW_TEMPORARIES
  1526. m_transform = (*parentXfrm) * m_localTransform;
  1527. #else
  1528. m_transform.mul(*parentXfrm, m_localTransform);
  1529. #endif
  1530. else
  1531. m_transform = *parentXfrm;
  1532. }
  1533. m_isIdentity = false;
  1534. transformSet = true;
  1535. }
  1536. if (transformSet == false)
  1537. {
  1538. if (m_isLocalIdentity == false)
  1539. {
  1540. m_transform = m_localTransform;
  1541. m_isIdentity = false;
  1542. }
  1543. else
  1544. {
  1545. m_isIdentity = true;
  1546. }
  1547. }
  1548. // if we are controlled by a particle, its position is local origin
  1549. if (m_controlParticle)
  1550. {
  1551. const Coord3D *controlPos = m_controlParticle->getPosition();
  1552. /// @todo Concatenate this, instead of overriding (MSB)
  1553. m_transform.Set_X_Translation( controlPos->x );
  1554. m_transform.Set_Y_Translation( controlPos->y );
  1555. m_transform.Set_Z_Translation( controlPos->z );
  1556. m_isIdentity = false;
  1557. m_lastPos = m_pos;
  1558. m_pos = *controlPos;
  1559. }
  1560. //
  1561. // Generate new particles if the system hasn't been 'stopped' or 'destroyed'
  1562. // If we are a slave system, do not generate particles ourselves - our master will force us to
  1563. //
  1564. if (m_isDestroyed == false)
  1565. {
  1566. if (m_isForever || (m_isForever == false && m_systemLifetimeLeft > 0))
  1567. {
  1568. if (!isShrouded && m_isStopped == false && m_masterSystem == NULL)
  1569. {
  1570. if (m_burstDelayLeft == 0)
  1571. {
  1572. ParticlePriorityType priority = getPriority();
  1573. // emit a burst of particles
  1574. Int count = REAL_TO_INT(m_burstCount.getValue());
  1575. count *= m_countCoeff;
  1576. for( Int i=0; i<count; i++ )
  1577. {
  1578. // generate this particle's unique attributes
  1579. const ParticleInfo *info = generateParticleInfo(i, count);
  1580. if (!m_isEmitAboveGroundOnly || (info->m_pos.z >= TheTerrainLogic->getGroundHeight(info->m_pos.x, info->m_pos.y)))
  1581. {
  1582. // actually create a particle
  1583. Particle *p = createParticle( info, priority );
  1584. if (p == NULL)
  1585. continue;
  1586. if (m_attachedSystemName.isEmpty() == false)
  1587. {
  1588. const ParticleSystemTemplate *tmp = TheParticleSystemManager->findTemplate( m_attachedSystemName );
  1589. if (tmp)
  1590. {
  1591. ParticleSystem *sys = TheParticleSystemManager->createParticleSystem( tmp, TRUE );
  1592. sys->setControlParticle( p );
  1593. p->controlParticleSystem( sys );
  1594. }
  1595. }
  1596. // create a slave particle, if necessary
  1597. if (m_slaveSystem)
  1598. {
  1599. ParticleInfo mergeInfo = ParticleSystem::mergeRelatedParticleSystems(this, m_slaveSystem, false);
  1600. // create slaved particle
  1601. m_slaveSystem->createParticle( &mergeInfo, priority );
  1602. }
  1603. }
  1604. }
  1605. // compute next burst delay
  1606. m_burstDelayLeft = (UnsignedInt)m_burstDelay.getValue();
  1607. m_burstDelayLeft *= m_delayCoeff;
  1608. }
  1609. else
  1610. {
  1611. m_burstDelayLeft--;
  1612. }
  1613. } // end if stopped check
  1614. } // end if system lifetime check
  1615. } // end if is destroyed
  1616. //
  1617. // Update all particles in the system
  1618. //
  1619. Particle *p = m_systemParticlesHead;
  1620. Particle *oldParticle;
  1621. while (p)
  1622. {
  1623. // apply 'gravity' force
  1624. if (m_gravity != 0.0f)
  1625. {
  1626. Coord3D force;
  1627. force.x = 0.0f;
  1628. force.y = 0.0f;
  1629. force.z = m_gravity;
  1630. p->applyForce( &force );
  1631. }
  1632. if (p->update() == false)
  1633. {
  1634. oldParticle = p;
  1635. p = p->m_systemNext;
  1636. oldParticle->deleteInstance();
  1637. } else {
  1638. p = p->m_systemNext;
  1639. }
  1640. }
  1641. //
  1642. // If we have been "destroyed", wait for all of our particles to die off,
  1643. // then destroy ourselves (return false).
  1644. //
  1645. if (m_isDestroyed && !m_systemParticlesHead)
  1646. return false;
  1647. // monitor particle system lifetime
  1648. if (m_isForever == false)
  1649. {
  1650. // decrement lifetime if not zero
  1651. if (m_systemLifetimeLeft)
  1652. m_systemLifetimeLeft--;
  1653. // if there are still particles "in the air", don't destroy yet
  1654. if (getParticleCount())
  1655. return true;
  1656. // check if time is up
  1657. if (m_systemLifetimeLeft == 0)
  1658. return false;
  1659. }
  1660. return true;
  1661. }
  1662. // ------------------------------------------------------------------------------------------------
  1663. /** Update the wind motion */
  1664. // ------------------------------------------------------------------------------------------------
  1665. void ParticleSystem::updateWindMotion( void )
  1666. {
  1667. switch( m_windMotion )
  1668. {
  1669. // --------------------------------------------------------------------------------------------
  1670. case ParticleSystemInfo::WIND_MOTION_PING_PONG:
  1671. {
  1672. Real startAngle = m_windMotionStartAngle;
  1673. Real endAngle = m_windMotionEndAngle;
  1674. // this only works when start angle is less than end angle
  1675. DEBUG_ASSERTCRASH( startAngle < endAngle, ("updateWindMotion: startAngle must be < endAngle\n") );
  1676. // how big is the total angle span
  1677. Real totalSpan = endAngle - startAngle;
  1678. Real halfSpan = totalSpan / 2.0f;
  1679. // given our current angle ... how far away from the "center" of the span are we
  1680. Real diffFromCenter = fabs( halfSpan - m_windAngle + startAngle );
  1681. //
  1682. // given our distance from the center ... we need to compute how much we will change
  1683. // the angle. When we are closer to the center we change it faster (more), and when
  1684. // we are near the edges we change is slower (less)
  1685. //
  1686. Real change = (1.0f - (diffFromCenter / halfSpan)) * m_windAngleChange;
  1687. // we will always change a little bit
  1688. #define MINIMUM_CHANGE 0.005f // lower #'s have softer swings at the edge angles
  1689. if( change < MINIMUM_CHANGE )
  1690. change = MINIMUM_CHANGE;
  1691. //
  1692. // if we are moving toward the end angle we add the change, if we're moving away
  1693. // from it we subtract it
  1694. //
  1695. if( m_windMotionMovingToEndAngle )
  1696. {
  1697. // add angle
  1698. m_windAngle += change;
  1699. // see if we're at the end and should switch directions
  1700. if( m_windAngle >= endAngle )
  1701. {
  1702. // change directions
  1703. m_windMotionMovingToEndAngle = FALSE;
  1704. // pick a new change delta
  1705. m_windAngleChange =
  1706. GameClientRandomValueReal( m_windAngleChangeMin, m_windAngleChangeMax );
  1707. // pick new start and end angles
  1708. m_windMotionStartAngle =
  1709. GameClientRandomValueReal( m_windMotionStartAngleMin,
  1710. m_windMotionStartAngleMax );
  1711. m_windMotionEndAngle =
  1712. GameClientRandomValueReal( m_windMotionEndAngleMin,
  1713. m_windMotionEndAngleMax );
  1714. } // end if
  1715. } // end if
  1716. else
  1717. {
  1718. // subtract angle
  1719. m_windAngle -= change;
  1720. // see if we're at the end and should switch directions
  1721. if( m_windAngle <= startAngle )
  1722. {
  1723. // change directions
  1724. m_windMotionMovingToEndAngle = TRUE;
  1725. // pick a new change delta
  1726. m_windAngleChange =
  1727. GameClientRandomValueReal( m_windAngleChangeMin, m_windAngleChangeMax );
  1728. // pick new start and end angles
  1729. m_windMotionStartAngle =
  1730. GameClientRandomValueReal( m_windMotionStartAngleMin,
  1731. m_windMotionStartAngleMax );
  1732. m_windMotionEndAngle =
  1733. GameClientRandomValueReal( m_windMotionEndAngleMin,
  1734. m_windMotionEndAngleMax );
  1735. } // end if
  1736. } // end else
  1737. break;
  1738. } // end case
  1739. // --------------------------------------------------------------------------------------------
  1740. case ParticleSystemInfo::WIND_MOTION_CIRCULAR:
  1741. {
  1742. // give us a wind angle change if one hasn't been specifed (this plays nice with the particle editor)
  1743. if( m_windAngleChange == 0.0f )
  1744. m_windAngleChange = GameClientRandomValueReal( m_windAngleChangeMin, m_windAngleChangeMax );
  1745. // add to our wind angle
  1746. m_windAngle += m_windAngleChange;
  1747. // keep in 0 to 2PI range just to keep the numbers safe and sane
  1748. if( m_windAngle > TWO_PI )
  1749. m_windAngle -= TWO_PI;
  1750. else if( m_windAngle < 0.0f )
  1751. m_windAngle += TWO_PI;
  1752. break;
  1753. } // end case
  1754. // ---------------------------------------------------------------------------------------------
  1755. default:
  1756. {
  1757. break;
  1758. } // end default
  1759. } // end if
  1760. } // end updateWindMotion
  1761. // ------------------------------------------------------------------------------------------------
  1762. // ------------------------------------------------------------------------------------------------
  1763. void ParticleSystem::addParticle( Particle *particleToAdd )
  1764. {
  1765. if (particleToAdd->m_inSystemList)
  1766. return;
  1767. if (!m_systemParticlesHead)
  1768. {
  1769. m_systemParticlesHead = particleToAdd;
  1770. }
  1771. if (m_systemParticlesTail)
  1772. {
  1773. m_systemParticlesTail->m_systemNext = particleToAdd;
  1774. particleToAdd->m_systemPrev = m_systemParticlesTail;
  1775. }
  1776. else
  1777. {
  1778. particleToAdd->m_systemPrev = NULL;
  1779. }
  1780. m_systemParticlesTail = particleToAdd;
  1781. particleToAdd->m_systemNext = NULL;
  1782. particleToAdd->m_inSystemList = TRUE;
  1783. ++m_particleCount;
  1784. particleToAdd->setPersonality( m_personalityStore++ );
  1785. }
  1786. // ------------------------------------------------------------------------------------------------
  1787. /** Remove given particle from the list - ONLY FOR USE BY PARTICLE */
  1788. // ------------------------------------------------------------------------------------------------
  1789. void ParticleSystem::removeParticle( Particle *particleToRemove )
  1790. {
  1791. if (!particleToRemove->m_inSystemList)
  1792. return;
  1793. // remove links from prev & next objs
  1794. if (particleToRemove->m_systemNext)
  1795. particleToRemove->m_systemNext->m_systemPrev = particleToRemove->m_systemPrev;
  1796. if (particleToRemove->m_systemPrev)
  1797. particleToRemove->m_systemPrev->m_systemNext = particleToRemove->m_systemNext;
  1798. // update head & tail if neccessary
  1799. if (particleToRemove == m_systemParticlesHead)
  1800. m_systemParticlesHead = particleToRemove->m_systemNext;
  1801. if (particleToRemove == m_systemParticlesTail)
  1802. m_systemParticlesTail = particleToRemove->m_systemPrev;
  1803. particleToRemove->m_systemNext = particleToRemove->m_systemPrev = NULL;
  1804. particleToRemove->m_inSystemList = FALSE;
  1805. --m_particleCount;
  1806. }
  1807. // ------------------------------------------------------------------------------------------------
  1808. // ------------------------------------------------------------------------------------------------
  1809. ParticleInfo ParticleSystem::mergeRelatedParticleSystems( ParticleSystem *masterParticleSystem, ParticleSystem *slaveParticleSystem, Bool slaveNeedsFullPromotion)
  1810. {
  1811. if (!masterParticleSystem || !slaveParticleSystem) {
  1812. DEBUG_CRASH(("masterParticleSystem or slaveParticleSystem was NULL. Should not happen. JKMCD"));
  1813. ParticleInfo bogus;
  1814. memset( &bogus, 0, sizeof( bogus ) );
  1815. return bogus;
  1816. }
  1817. // copy info
  1818. ParticleInfo mergeInfo = *masterParticleSystem->generateParticleInfo(1, 1);
  1819. // generate one from the slave system
  1820. const ParticleInfo *info = slaveParticleSystem->generateParticleInfo(1, 1);
  1821. // override unique attributes of slave particle
  1822. mergeInfo.m_lifetime = info->m_lifetime;
  1823. // size becomes a scale factor of master's particles
  1824. mergeInfo.m_size *= info->m_size;
  1825. mergeInfo.m_sizeRate *= info->m_sizeRate;
  1826. mergeInfo.m_sizeRateDamping *= info->m_sizeRateDamping;
  1827. mergeInfo.m_angleZ = info->m_angleZ;
  1828. mergeInfo.m_angularRateZ = info->m_angularRateZ;
  1829. mergeInfo.m_angularDamping = info->m_angularDamping;
  1830. for( int i=0; i<MAX_KEYFRAMES; i++ )
  1831. mergeInfo.m_alphaKey[i] = info->m_alphaKey[i];
  1832. for( i=0; i<MAX_KEYFRAMES; i++ )
  1833. mergeInfo.m_colorKey[i] = info->m_colorKey[i];
  1834. mergeInfo.m_colorScale = info->m_colorScale;
  1835. // offset slave's position relative to master's
  1836. const Coord3D *offset = slaveParticleSystem->getSlavePositionOffset();
  1837. mergeInfo.m_pos.x += offset->x;
  1838. mergeInfo.m_pos.y += offset->y;
  1839. mergeInfo.m_pos.z += offset->z;
  1840. if (slaveNeedsFullPromotion) {
  1841. slaveParticleSystem->m_burstCount = masterParticleSystem->m_burstCount;
  1842. slaveParticleSystem->m_burstDelay = masterParticleSystem->m_burstDelay;
  1843. slaveParticleSystem->m_priority = masterParticleSystem->m_priority;
  1844. slaveParticleSystem->m_emissionVelocity = masterParticleSystem->m_emissionVelocity;
  1845. slaveParticleSystem->m_emissionVelocityType = masterParticleSystem->m_emissionVelocityType;
  1846. slaveParticleSystem->m_emissionVolume = masterParticleSystem->m_emissionVolume;
  1847. slaveParticleSystem->m_emissionVolumeType = masterParticleSystem->m_emissionVolumeType;
  1848. slaveParticleSystem->m_isEmissionVolumeHollow = masterParticleSystem->m_isEmissionVolumeHollow;
  1849. slaveParticleSystem->m_startSize.setRange(masterParticleSystem->m_startSize.getMinimumValue() * slaveParticleSystem->m_startSize.getMinimumValue(),
  1850. masterParticleSystem->m_startSize.getMaximumValue() * slaveParticleSystem->m_startSize.getMaximumValue(),
  1851. masterParticleSystem->m_startSize.getDistributionType());
  1852. slaveParticleSystem->m_sizeRate.setRange(masterParticleSystem->m_sizeRate.getMinimumValue() * slaveParticleSystem->m_sizeRate.getMinimumValue(),
  1853. masterParticleSystem->m_sizeRate.getMaximumValue() * slaveParticleSystem->m_sizeRate.getMaximumValue(),
  1854. masterParticleSystem->m_sizeRate.getDistributionType());
  1855. slaveParticleSystem->m_sizeRateDamping.setRange(masterParticleSystem->m_sizeRateDamping.getMinimumValue() * slaveParticleSystem->m_sizeRateDamping.getMinimumValue(),
  1856. masterParticleSystem->m_sizeRateDamping.getMaximumValue() * slaveParticleSystem->m_sizeRateDamping.getMaximumValue(),
  1857. masterParticleSystem->m_sizeRateDamping.getDistributionType());
  1858. // slaveParticleSystem->m_burstCount.setRange(masterParticleSystem->m_burstCount.getMinimumValue() / 2,
  1859. // masterParticleSystem->m_burstCount.getMaximumValue() / 2,
  1860. // masterParticleSystem->m_burstCount.getDistributionType());
  1861. }
  1862. return mergeInfo;
  1863. }
  1864. // ------------------------------------------------------------------------------------------------
  1865. // ------------------------------------------------------------------------------------------------
  1866. void ParticleSystem::setLifetimeRange( Real min, Real max )
  1867. {
  1868. m_lifetime.setRange( min, max );
  1869. }
  1870. // ------------------------------------------------------------------------------------------------
  1871. // ------------------------------------------------------------------------------------------------
  1872. void ParticleSystem::setControlParticle( Particle *p )
  1873. {
  1874. m_controlParticle = p;
  1875. }
  1876. // ------------------------------------------------------------------------------------------------
  1877. /** CRC */
  1878. // ------------------------------------------------------------------------------------------------
  1879. void ParticleSystem::crc( Xfer *xfer )
  1880. {
  1881. } // end crc
  1882. // ------------------------------------------------------------------------------------------------
  1883. /** Xfer method
  1884. * Version Info:
  1885. * 1: Initial version */
  1886. // ------------------------------------------------------------------------------------------------
  1887. void ParticleSystem::xfer( Xfer *xfer )
  1888. {
  1889. // version
  1890. XferVersion currentVersion = 1;
  1891. XferVersion version = currentVersion;
  1892. xfer->xferVersion( &version, currentVersion );
  1893. // base class info
  1894. ParticleSystemInfo::xfer( xfer );
  1895. // particle system ID
  1896. xfer->xferUser( &m_systemID, sizeof( ParticleSystemID ) );
  1897. // attached to drawable id
  1898. xfer->xferDrawableID( &m_attachedToDrawableID );
  1899. // attached to object id
  1900. xfer->xferObjectID( &m_attachedToObjectID );
  1901. // is local identity
  1902. xfer->xferBool( &m_isLocalIdentity );
  1903. // local transform
  1904. xfer->xferUser( &m_localTransform, sizeof( Matrix3D ) );
  1905. // is identity
  1906. xfer->xferBool( &m_isIdentity );
  1907. // transform
  1908. xfer->xferUser( &m_transform, sizeof( Matrix3D ) );
  1909. // burst delay left
  1910. xfer->xferUnsignedInt( &m_burstDelayLeft );
  1911. // delay left
  1912. xfer->xferUnsignedInt( &m_delayLeft );
  1913. // start timestamp
  1914. xfer->xferUnsignedInt( &m_startTimestamp );
  1915. // system lifetime left
  1916. xfer->xferUnsignedInt( &m_systemLifetimeLeft );
  1917. // personality store
  1918. xfer->xferUnsignedInt( &m_personalityStore );
  1919. // is forever
  1920. xfer->xferBool( &m_isForever );
  1921. // accumulated size bonus
  1922. xfer->xferReal( &m_accumulatedSizeBonus );
  1923. // is stopped
  1924. xfer->xferBool( &m_isStopped );
  1925. // we never save destroyed particle systems so there is no need to consider m_isDestroyed
  1926. // m_isDestroyed <-- do nothing with me
  1927. // ditto for m_isSaveable
  1928. // m_isSaveable <-- do nothing with me
  1929. // velCoeff
  1930. xfer->xferCoord3D( &m_velCoeff );
  1931. // count coeff
  1932. xfer->xferReal( &m_countCoeff );
  1933. // delay coeff
  1934. xfer->xferReal( &m_delayCoeff );
  1935. // size coeff
  1936. xfer->xferReal( &m_sizeCoeff );
  1937. // position
  1938. xfer->xferCoord3D( &m_pos );
  1939. // last position
  1940. xfer->xferCoord3D( &m_lastPos );
  1941. // is first pos
  1942. xfer->xferBool( &m_isFirstPos );
  1943. // slave system id
  1944. xfer->xferUser( &m_slaveSystemID, sizeof( ParticleSystemID ) );
  1945. // master system
  1946. xfer->xferUser( &m_masterSystemID, sizeof( ParticleSystemID ) );
  1947. // particle count
  1948. UnsignedInt particleCount = m_particleCount;
  1949. xfer->xferUnsignedInt( &particleCount );
  1950. // particles
  1951. if( xfer->getXferMode() == XFER_SAVE )
  1952. {
  1953. Particle *particle;
  1954. // go through all particles in this system
  1955. for( particle = m_systemParticlesHead; particle; particle = particle->m_systemNext )
  1956. {
  1957. // write particle information
  1958. xfer->xferSnapshot( particle );
  1959. } // end for, particle
  1960. } // end if, save
  1961. else
  1962. {
  1963. ParticlePriorityType priority = getPriority();
  1964. const ParticleInfo *info = generateParticleInfo( 0, 1 );
  1965. Particle *particle;
  1966. // read each particle data block
  1967. for( UnsignedInt i = 0; i < particleCount; ++i )
  1968. {
  1969. // create a new particle
  1970. particle = createParticle( info, priority, TRUE );
  1971. // sanity
  1972. DEBUG_ASSERTCRASH( particle, ("ParticleSyste::xfer - Unable to create particle for loading\n") );
  1973. // read in the particle data
  1974. xfer->xferSnapshot( particle );
  1975. } // end for i
  1976. } // end else, load
  1977. } // end xfer
  1978. // ------------------------------------------------------------------------------------------------
  1979. /** Load post process */
  1980. // ------------------------------------------------------------------------------------------------
  1981. void ParticleSystem::loadPostProcess( void )
  1982. {
  1983. // call base class post process
  1984. ParticleSystemInfo::loadPostProcess();
  1985. // reconnect slave pointers if needed
  1986. if( m_slaveSystemID != INVALID_PARTICLE_SYSTEM_ID )
  1987. {
  1988. // sanity
  1989. if( m_slaveSystem != NULL )
  1990. {
  1991. DEBUG_CRASH(( "ParticleSystem::loadPostProcess - m_slaveSystem is not NULL but should be\n" ));
  1992. throw SC_INVALID_DATA;
  1993. } // end if
  1994. // assign system
  1995. m_slaveSystem = TheParticleSystemManager->findParticleSystem( m_slaveSystemID );
  1996. // sanity
  1997. if( m_slaveSystem == NULL || m_slaveSystem->isDestroyed() == TRUE )
  1998. {
  1999. DEBUG_CRASH(( "ParticleSystem::loadPostProcess - m_slaveSystem is NULL or destroyed\n" ));
  2000. throw SC_INVALID_DATA;
  2001. } // end if
  2002. } // end if
  2003. // reconnect master pointers if needed
  2004. if( m_masterSystemID != INVALID_PARTICLE_SYSTEM_ID )
  2005. {
  2006. // sanity
  2007. if( m_masterSystem != NULL )
  2008. {
  2009. DEBUG_CRASH(( "ParticleSystem::loadPostProcess - m_masterSystem is not NULL but should be\n" ));
  2010. throw SC_INVALID_DATA;
  2011. } // end if
  2012. // assign system
  2013. m_masterSystem = TheParticleSystemManager->findParticleSystem( m_masterSystemID );
  2014. // sanity
  2015. if( m_masterSystem == NULL || m_masterSystem->isDestroyed() == TRUE )
  2016. {
  2017. DEBUG_CRASH(( "ParticleSystem::loadPostProcess - m_masterSystem is NULL or destroyed\n" ));
  2018. throw SC_INVALID_DATA;
  2019. } // end if
  2020. } // end if
  2021. } // end loadPostProcess
  2022. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2023. // ParticleSystemTemplate /////////////////////////////////////////////////////////////////////////
  2024. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2025. // ------------------------------------------------------------------------------------------------
  2026. /** INI parse data */
  2027. // ------------------------------------------------------------------------------------------------
  2028. const FieldParse ParticleSystemTemplate::m_fieldParseTable[] =
  2029. {
  2030. { "Priority", INI::parseIndexList, ParticlePriorityNames, offsetof( ParticleSystemTemplate, m_priority ) },
  2031. { "IsOneShot", INI::parseBool, NULL, offsetof( ParticleSystemTemplate, m_isOneShot ) },
  2032. { "Shader", INI::parseIndexList, ParticleShaderTypeNames, offsetof( ParticleSystemTemplate, m_shaderType ) },
  2033. { "Type", INI::parseIndexList, ParticleTypeNames, offsetof( ParticleSystemTemplate, m_particleType ) },
  2034. { "ParticleName", INI::parseAsciiString, NULL, offsetof( ParticleSystemTemplate, m_particleTypeName ) },
  2035. { "AngleZ", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_angleZ ) },
  2036. { "AngularRateZ", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_angularRateZ ) },
  2037. { "AngularDamping", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_angularDamping ) },
  2038. { "VelocityDamping", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_velDamping ) },
  2039. { "Gravity", INI::parseReal, NULL, offsetof( ParticleSystemTemplate, m_gravity ) },
  2040. { "SlaveSystem", INI::parseAsciiString, NULL, offsetof( ParticleSystemTemplate, m_slaveSystemName ) },
  2041. { "SlavePosOffset", INI::parseCoord3D, NULL, offsetof( ParticleSystemTemplate, m_slavePosOffset ) },
  2042. { "PerParticleAttachedSystem", INI::parseAsciiString, NULL, offsetof( ParticleSystemTemplate, m_attachedSystemName ) },
  2043. { "Lifetime", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_lifetime ) },
  2044. { "SystemLifetime", INI::parseUnsignedInt, NULL, offsetof( ParticleSystemTemplate, m_systemLifetime ) },
  2045. { "Size", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_startSize ) },
  2046. { "StartSizeRate", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_startSizeRate ) },
  2047. { "SizeRate", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_sizeRate ) },
  2048. { "SizeRateDamping", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_sizeRateDamping ) },
  2049. { "Alpha1", ParticleSystemTemplate::parseRandomKeyframe, NULL, offsetof( ParticleSystemTemplate, m_alphaKey[0] ) },
  2050. { "Alpha2", ParticleSystemTemplate::parseRandomKeyframe, NULL, offsetof( ParticleSystemTemplate, m_alphaKey[1] ) },
  2051. { "Alpha3", ParticleSystemTemplate::parseRandomKeyframe, NULL, offsetof( ParticleSystemTemplate, m_alphaKey[2] ) },
  2052. { "Alpha4", ParticleSystemTemplate::parseRandomKeyframe, NULL, offsetof( ParticleSystemTemplate, m_alphaKey[3] ) },
  2053. { "Alpha5", ParticleSystemTemplate::parseRandomKeyframe, NULL, offsetof( ParticleSystemTemplate, m_alphaKey[4] ) },
  2054. { "Alpha6", ParticleSystemTemplate::parseRandomKeyframe, NULL, offsetof( ParticleSystemTemplate, m_alphaKey[5] ) },
  2055. { "Alpha7", ParticleSystemTemplate::parseRandomKeyframe, NULL, offsetof( ParticleSystemTemplate, m_alphaKey[6] ) },
  2056. { "Alpha8", ParticleSystemTemplate::parseRandomKeyframe, NULL, offsetof( ParticleSystemTemplate, m_alphaKey[7] ) },
  2057. { "Color1", ParticleSystemTemplate::parseRGBColorKeyframe,NULL, offsetof( ParticleSystemTemplate, m_colorKey[0] ) },
  2058. { "Color2", ParticleSystemTemplate::parseRGBColorKeyframe,NULL, offsetof( ParticleSystemTemplate, m_colorKey[1] ) },
  2059. { "Color3", ParticleSystemTemplate::parseRGBColorKeyframe,NULL, offsetof( ParticleSystemTemplate, m_colorKey[2] ) },
  2060. { "Color4", ParticleSystemTemplate::parseRGBColorKeyframe,NULL, offsetof( ParticleSystemTemplate, m_colorKey[3] ) },
  2061. { "Color5", ParticleSystemTemplate::parseRGBColorKeyframe,NULL, offsetof( ParticleSystemTemplate, m_colorKey[4] ) },
  2062. { "Color6", ParticleSystemTemplate::parseRGBColorKeyframe,NULL, offsetof( ParticleSystemTemplate, m_colorKey[5] ) },
  2063. { "Color7", ParticleSystemTemplate::parseRGBColorKeyframe,NULL, offsetof( ParticleSystemTemplate, m_colorKey[6] ) },
  2064. { "Color8", ParticleSystemTemplate::parseRGBColorKeyframe,NULL, offsetof( ParticleSystemTemplate, m_colorKey[7] ) },
  2065. // { "COLOR", ParticleSystemTemplate::parseRandomRGBColor, NULL, offsetof( ParticleSystemTemplate, m_color ) },
  2066. { "ColorScale", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_colorScale ) },
  2067. { "BurstDelay", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_burstDelay ) },
  2068. { "BurstCount", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_burstCount ) },
  2069. { "InitialDelay", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_initialDelay ) },
  2070. { "DriftVelocity", INI::parseCoord3D, NULL, offsetof( ParticleSystemTemplate, m_driftVelocity ) },
  2071. { "VelocityType", INI::parseIndexList, EmissionVelocityTypeNames, offsetof( ParticleSystemTemplate, m_emissionVelocityType ) },
  2072. { "VelOrthoX", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_emissionVelocity.ortho.x ) },
  2073. { "VelOrthoY", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_emissionVelocity.ortho.y ) },
  2074. { "VelOrthoZ", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_emissionVelocity.ortho.z ) },
  2075. { "VelSpherical", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_emissionVelocity.spherical.speed ) },
  2076. { "VelHemispherical", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_emissionVelocity.hemispherical.speed ) },
  2077. { "VelCylindricalRadial", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_emissionVelocity.cylindrical.radial ) },
  2078. { "VelCylindricalNormal", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_emissionVelocity.cylindrical.normal ) },
  2079. { "VelOutward", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_emissionVelocity.outward.speed ) },
  2080. { "VelOutwardOther", INI::parseGameClientRandomVariable, NULL, offsetof( ParticleSystemTemplate, m_emissionVelocity.outward.otherSpeed ) },
  2081. { "VolumeType", INI::parseIndexList, EmissionVolumeTypeNames, offsetof( ParticleSystemTemplate, m_emissionVolumeType ) },
  2082. { "VolLineStart", INI::parseCoord3D, NULL, offsetof( ParticleSystemTemplate, m_emissionVolume.line.start ) },
  2083. { "VolLineEnd", INI::parseCoord3D, NULL, offsetof( ParticleSystemTemplate, m_emissionVolume.line.end ) },
  2084. { "VolBoxHalfSize", INI::parseCoord3D, NULL, offsetof( ParticleSystemTemplate, m_emissionVolume.box.halfSize ) },
  2085. { "VolSphereRadius", INI::parseReal, NULL, offsetof( ParticleSystemTemplate, m_emissionVolume.sphere.radius ) },
  2086. { "VolCylinderRadius", INI::parseReal, NULL, offsetof( ParticleSystemTemplate, m_emissionVolume.cylinder.radius ) },
  2087. { "VolCylinderLength", INI::parseReal, NULL, offsetof( ParticleSystemTemplate, m_emissionVolume.cylinder.length ) },
  2088. { "IsHollow", INI::parseBool, NULL, offsetof( ParticleSystemTemplate, m_isEmissionVolumeHollow ) },
  2089. { "IsGroundAligned", INI::parseBool, NULL, offsetof( ParticleSystemTemplate, m_isGroundAligned ) },
  2090. { "IsEmitAboveGroundOnly", INI::parseBool, NULL, offsetof( ParticleSystemTemplate, m_isEmitAboveGroundOnly) },
  2091. { "IsParticleUpTowardsEmitter", INI::parseBool, NULL, offsetof( ParticleSystemTemplate, m_isParticleUpTowardsEmitter) },
  2092. { "WindMotion", INI::parseIndexList, WindMotionNames, offsetof( ParticleSystemTemplate, m_windMotion ) },
  2093. { "WindAngleChangeMin", INI::parseReal, NULL, offsetof( ParticleSystemTemplate, m_windAngleChangeMin ) },
  2094. { "WindAngleChangeMax", INI::parseReal, NULL, offsetof( ParticleSystemTemplate, m_windAngleChangeMax ) },
  2095. { "WindPingPongStartAngleMin", INI::parseReal, NULL, offsetof( ParticleSystemTemplate, m_windMotionStartAngleMin ) },
  2096. { "WindPingPongStartAngleMax", INI::parseReal, NULL, offsetof( ParticleSystemTemplate, m_windMotionStartAngleMax ) },
  2097. { "WindPingPongEndAngleMin", INI::parseReal, NULL, offsetof( ParticleSystemTemplate, m_windMotionEndAngleMin ) },
  2098. { "WindPingPongEndAngleMax", INI::parseReal, NULL, offsetof( ParticleSystemTemplate, m_windMotionEndAngleMax ) },
  2099. { NULL, NULL, NULL, 0 },
  2100. };
  2101. // ------------------------------------------------------------------------------------------------
  2102. /** Parse a "random keyframe".
  2103. * The format is "FIELD = low high frame". */
  2104. // ------------------------------------------------------------------------------------------------
  2105. void ParticleSystemTemplate::parseRandomKeyframe( INI* ini, void *instance,
  2106. void *store, const void* /*userData*/ )
  2107. {
  2108. RandomKeyframe *key = static_cast<RandomKeyframe *>(store);
  2109. Real low = ini->scanReal(ini->getNextToken());
  2110. Real high = ini->scanReal(ini->getNextToken());
  2111. key->frame = ini->scanUnsignedInt(ini->getNextToken());
  2112. // set the range of the random variable
  2113. key->var.setRange( low, high );
  2114. }
  2115. // ------------------------------------------------------------------------------------------------
  2116. /** Parse a "color keyframe".
  2117. * The format is "FIELD = R:r G:g B:b frame". */
  2118. // ------------------------------------------------------------------------------------------------
  2119. void ParticleSystemTemplate::parseRGBColorKeyframe( INI* ini, void *instance,
  2120. void *store, const void* /*userData*/ )
  2121. {
  2122. RGBColorKeyframe *key = static_cast<RGBColorKeyframe *>(store);
  2123. INI::parseRGBColor( ini, instance, &key->color, NULL );
  2124. INI::parseUnsignedInt( ini, instance, &key->frame, NULL );
  2125. }
  2126. // ------------------------------------------------------------------------------------------------
  2127. /** Parse a RandomVariable RGB color.
  2128. * Note that the components may be negative, as this is used for rates, as well. */
  2129. // ------------------------------------------------------------------------------------------------
  2130. void ParticleSystemTemplate::parseRandomRGBColor( INI* ini, void *instance,
  2131. void *store, const void* /*userData*/ )
  2132. {
  2133. #if 0
  2134. char seps[] = " \n\r\t=:RGB,";
  2135. const char *token;
  2136. Int colors[2][3];
  2137. Int result;
  2138. enum { LO = 0, HI = 1 };
  2139. enum { RED = 0, GREEN = 1, BLUE = 2 };
  2140. // initialize to invalid values
  2141. colors[ LO ][ RED ] = -1;
  2142. colors[ LO ][ GREEN ] = -1;
  2143. colors[ LO ][ BLUE ] = -1;
  2144. colors[ HI ][ RED ] = -1;
  2145. colors[ HI ][ GREEN ] = -1;
  2146. colors[ HI ][ BLUE ] = -1;
  2147. // do each color part
  2148. for( Int i = 0; i < 3; i++ )
  2149. {
  2150. for( Int j = 0; j < 2; j++ )
  2151. {
  2152. // get the color number
  2153. token = ini->getNextToken(seps);
  2154. // convert to number
  2155. colors[j][i] = ini->scanInt(token);
  2156. // check to see if it's within range
  2157. if( colors[j][i] < -255 || colors[j][i] > 255 )
  2158. throw INI_INVALID_DATA;
  2159. } // end for i
  2160. }
  2161. // assign the color components to the "RGBColor" pointer at 'store'
  2162. ParticleSystemInfo::RandomRGBColor *theColor = (ParticleSystemInfo::RandomRGBColor *)store;
  2163. theColor->red.setRange( (Real)colors[ LO ][ RED ] / 255.0f, (Real)colors[ HI ][ RED ] / 255.0f );
  2164. theColor->green.setRange( (Real)colors[ LO ][ GREEN ] / 255.0f, (Real)colors[ HI ][ GREEN ] / 255.0f );
  2165. theColor->blue.setRange( (Real)colors[ LO ][ BLUE ] / 255.0f, (Real)colors[ HI ][ BLUE ] / 255.0f );
  2166. #endif
  2167. }
  2168. // ------------------------------------------------------------------------------------------------
  2169. // ------------------------------------------------------------------------------------------------
  2170. ParticleSystemTemplate::ParticleSystemTemplate( const AsciiString &name ) :
  2171. m_name(name)
  2172. {
  2173. //Added By Sadullah Nader
  2174. //Initializations inserted
  2175. m_slaveTemplate = NULL;
  2176. //
  2177. }
  2178. // ------------------------------------------------------------------------------------------------
  2179. // ------------------------------------------------------------------------------------------------
  2180. ParticleSystemTemplate::~ParticleSystemTemplate()
  2181. {
  2182. }
  2183. // ------------------------------------------------------------------------------------------------
  2184. /** If returns non-NULL, it is a slave system for use ... the create slaves parameter
  2185. * tells *this* slave system whether or not it should create any slaves itself
  2186. * automatically during its own constructor */
  2187. // ------------------------------------------------------------------------------------------------
  2188. ParticleSystem *ParticleSystemTemplate::createSlaveSystem( Bool createSlaves ) const
  2189. {
  2190. if (m_slaveTemplate == NULL && m_slaveSystemName.isEmpty() == false)
  2191. m_slaveTemplate = TheParticleSystemManager->findTemplate( m_slaveSystemName );
  2192. ParticleSystem *slave = NULL;
  2193. if (m_slaveTemplate)
  2194. slave = TheParticleSystemManager->createParticleSystem( m_slaveTemplate, createSlaves );
  2195. return slave;
  2196. }
  2197. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2198. // ParticleSystemManager //////////////////////////////////////////////////////////////////////////
  2199. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2200. // ------------------------------------------------------------------------------------------------
  2201. // ------------------------------------------------------------------------------------------------
  2202. ParticleSystemManager::ParticleSystemManager( void )
  2203. {
  2204. m_uniqueSystemID = INVALID_PARTICLE_SYSTEM_ID;
  2205. m_onScreenParticleCount = 0;
  2206. m_localPlayerIndex = 0;
  2207. //Added By Sadullah Nader
  2208. //Initializations inserted
  2209. m_lastLogicFrameUpdate = 0;
  2210. m_particleCount = 0;
  2211. m_fieldParticleCount = 0;
  2212. m_particleSystemCount = 0;
  2213. //
  2214. for( Int i = 0; i < NUM_PARTICLE_PRIORITIES; ++i )
  2215. {
  2216. m_allParticlesHead[ i ] = NULL;
  2217. m_allParticlesTail[ i ] = NULL;
  2218. } // end for, i
  2219. }
  2220. // ------------------------------------------------------------------------------------------------
  2221. // ------------------------------------------------------------------------------------------------
  2222. ParticleSystemManager::~ParticleSystemManager()
  2223. {
  2224. reset();
  2225. TemplateMap::iterator begin(m_templateMap.begin());
  2226. TemplateMap::iterator end(m_templateMap.end());
  2227. for (; begin != end; ++begin) {
  2228. (*begin).second->deleteInstance();
  2229. }
  2230. }
  2231. // ------------------------------------------------------------------------------------------------
  2232. /** Initialize the manager */
  2233. // ------------------------------------------------------------------------------------------------
  2234. void ParticleSystemManager::init( void )
  2235. {
  2236. /// Read INI data and build templates
  2237. INI ini;
  2238. ini.load( AsciiString( "Data\\INI\\ParticleSystem.ini" ), INI_LOAD_OVERWRITE, NULL );
  2239. // sanity, our lists must be empty!!
  2240. for( Int i = 0; i < NUM_PARTICLE_PRIORITIES; ++i )
  2241. {
  2242. // sanity
  2243. DEBUG_ASSERTCRASH( m_allParticlesHead[ i ] == NULL, ("INIT: ParticleSystem all particles head[%d] is not NULL!\n", i) );
  2244. DEBUG_ASSERTCRASH( m_allParticlesTail[ i ] == NULL, ("INIT: ParticleSystem all particles tail[%d] is not NULL!\n", i) );
  2245. // just to be clean set them to NULL
  2246. m_allParticlesHead[ i ] = NULL;
  2247. m_allParticlesTail[ i ] = NULL;
  2248. } // end for, i
  2249. }
  2250. // ------------------------------------------------------------------------------------------------
  2251. /** Reset the manager and all particle systems */
  2252. // ------------------------------------------------------------------------------------------------
  2253. void ParticleSystemManager::reset( void )
  2254. {
  2255. while (getParticleSystemCount()) {
  2256. if (m_allParticleSystemList.front()) {
  2257. m_allParticleSystemList.front()->deleteInstance();
  2258. }
  2259. }
  2260. // sanity, our lists must be empty!!
  2261. for( Int i = 0; i < NUM_PARTICLE_PRIORITIES; ++i )
  2262. {
  2263. // sanity
  2264. DEBUG_ASSERTCRASH( m_allParticlesHead[ i ] == NULL, ("RESET: ParticleSystem all particles head[%d] is not NULL!\n", i) );
  2265. DEBUG_ASSERTCRASH( m_allParticlesTail[ i ] == NULL, ("RESET: ParticleSystem all particles tail[%d] is not NULL!\n", i) );
  2266. // just to be clean set them to NULL
  2267. m_allParticlesHead[ i ] = NULL;
  2268. m_allParticlesTail[ i ] = NULL;
  2269. } // end for, i
  2270. m_particleCount = 0;
  2271. m_fieldParticleCount = 0;
  2272. m_particleSystemCount = 0;
  2273. m_uniqueSystemID = INVALID_PARTICLE_SYSTEM_ID;
  2274. m_lastLogicFrameUpdate = -1;
  2275. // leave templates as-is
  2276. }
  2277. // ------------------------------------------------------------------------------------------------
  2278. /** Update all particle systems */
  2279. // ------------------------------------------------------------------------------------------------
  2280. //DECLARE_PERF_TIMER(ParticleSystemManager)
  2281. void ParticleSystemManager::update( void )
  2282. {
  2283. if (m_lastLogicFrameUpdate == TheGameLogic->getFrame()) {
  2284. return;
  2285. }
  2286. // update the last logic frame.
  2287. m_lastLogicFrameUpdate = TheGameLogic->getFrame();
  2288. //USE_PERF_TIMER(ParticleSystemManager)
  2289. ParticleSystem *sys;
  2290. for(ParticleSystemListIt it = m_allParticleSystemList.begin(); it != m_allParticleSystemList.end();)
  2291. {
  2292. sys = (*it);
  2293. if (!sys) {
  2294. continue;
  2295. }
  2296. if (sys->update(m_localPlayerIndex) == false)
  2297. {
  2298. ++it;
  2299. sys->deleteInstance();
  2300. } else {
  2301. ++it;
  2302. }
  2303. }
  2304. }
  2305. // ------------------------------------------------------------------------------------------------
  2306. /** sets the count of the particles on screen after each frame */
  2307. // ------------------------------------------------------------------------------------------------
  2308. void ParticleSystemManager::setOnScreenParticleCount(int count)
  2309. {
  2310. m_onScreenParticleCount = count;
  2311. }
  2312. // ------------------------------------------------------------------------------------------------
  2313. /** Given a file containing particle system properties, create a new instance of it */
  2314. // ------------------------------------------------------------------------------------------------
  2315. ParticleSystem *ParticleSystemManager::createParticleSystem( const ParticleSystemTemplate *sysTemplate, Bool createSlaves )
  2316. {
  2317. // sanity
  2318. if (sysTemplate == NULL)
  2319. return NULL;
  2320. m_uniqueSystemID = (ParticleSystemID)((UnsignedInt)m_uniqueSystemID + 1);
  2321. ParticleSystem *sys = newInstance(ParticleSystem)( sysTemplate, m_uniqueSystemID, createSlaves );
  2322. return sys;
  2323. }
  2324. // ------------------------------------------------------------------------------------------------
  2325. /// given a template, instantiate a particle system attached to the given object, and return its ID
  2326. // ------------------------------------------------------------------------------------------------
  2327. ParticleSystemID ParticleSystemManager::createAttachedParticleSystemID(
  2328. const ParticleSystemTemplate *sysTemplate,
  2329. Object* attachTo,
  2330. Bool createSlaves )
  2331. {
  2332. ParticleSystem* pSystem = TheParticleSystemManager->createParticleSystem(sysTemplate, createSlaves);
  2333. if (pSystem && attachTo)
  2334. pSystem->attachToObject(attachTo);
  2335. return pSystem ? pSystem->getSystemID() : INVALID_PARTICLE_SYSTEM_ID;
  2336. }
  2337. // ------------------------------------------------------------------------------------------------
  2338. /** Find a particle system with the matching system id */
  2339. // ------------------------------------------------------------------------------------------------
  2340. ParticleSystem *ParticleSystemManager::findParticleSystem( ParticleSystemID id )
  2341. {
  2342. if (id == INVALID_PARTICLE_SYSTEM_ID)
  2343. return NULL; // my, that was easy
  2344. ParticleSystem *system = NULL;
  2345. for( ParticleSystemListIt it = m_allParticleSystemList.begin(); it != m_allParticleSystemList.end(); ++it ) {
  2346. system = *it;
  2347. if (!system) {
  2348. continue;
  2349. }
  2350. if( system->getSystemID() == id ) {
  2351. return system;
  2352. }
  2353. }
  2354. return NULL;
  2355. } // end findParticleSystem
  2356. // ------------------------------------------------------------------------------------------------
  2357. /** destroy the particle system with the given id (if it still exists) */
  2358. // ------------------------------------------------------------------------------------------------
  2359. void ParticleSystemManager::destroyParticleSystemByID(ParticleSystemID id)
  2360. {
  2361. ParticleSystem* pSystem = findParticleSystem(id);
  2362. if( pSystem )
  2363. pSystem->destroy();
  2364. }
  2365. // ------------------------------------------------------------------------------------------------
  2366. /** Locate an existing ParticleSystemTemplate */
  2367. // ------------------------------------------------------------------------------------------------
  2368. ParticleSystemTemplate *ParticleSystemManager::findTemplate( const AsciiString &name ) const
  2369. {
  2370. ParticleSystemTemplate *sysTemplate = NULL;
  2371. TemplateMap::const_iterator find(m_templateMap.find(name));
  2372. if (find != m_templateMap.end()) {
  2373. sysTemplate = (*find).second;
  2374. }
  2375. return sysTemplate;
  2376. }
  2377. // ------------------------------------------------------------------------------------------------
  2378. /** Create a new ParticleSystemTemplate */
  2379. // ------------------------------------------------------------------------------------------------
  2380. ParticleSystemTemplate *ParticleSystemManager::newTemplate( const AsciiString &name )
  2381. {
  2382. ParticleSystemTemplate *sysTemplate = findTemplate(name);
  2383. if (sysTemplate == NULL) {
  2384. sysTemplate = newInstance(ParticleSystemTemplate)( name );
  2385. if (! m_templateMap.insert(std::make_pair(name, sysTemplate)).second) {
  2386. sysTemplate->deleteInstance();
  2387. sysTemplate = NULL;
  2388. }
  2389. }
  2390. return sysTemplate;
  2391. }
  2392. // ------------------------------------------------------------------------------------------------
  2393. /** Find a particle system's parent. Should really only be called by TheScriptEngine */
  2394. // ------------------------------------------------------------------------------------------------
  2395. ParticleSystemTemplate *ParticleSystemManager::findParentTemplate( const AsciiString &name, Int parentNum ) const
  2396. {
  2397. if (name.isEmpty()) {
  2398. return NULL;
  2399. }
  2400. TemplateMap::const_iterator begin(m_templateMap.begin());
  2401. TemplateMap::const_iterator end(m_templateMap.end());
  2402. for(; begin != end; ++begin) {
  2403. ParticleSystemTemplate *sysTemplate = (*begin).second;
  2404. if (name.compare(sysTemplate->m_slaveSystemName) == 0) {
  2405. if (! parentNum--) {
  2406. return sysTemplate;
  2407. }
  2408. }
  2409. }
  2410. return NULL;
  2411. }
  2412. // ------------------------------------------------------------------------------------------------
  2413. /** Destroy any particle systems that are attached to this object */
  2414. // ------------------------------------------------------------------------------------------------
  2415. void ParticleSystemManager::destroyAttachedSystems( Object *obj )
  2416. {
  2417. // sanity
  2418. if( obj == NULL )
  2419. return;
  2420. // iterate through all systems
  2421. ParticleSystem *system = NULL;
  2422. for( ParticleSystemListIt it = m_allParticleSystemList.begin();
  2423. it != m_allParticleSystemList.end();
  2424. ++it )
  2425. {
  2426. system = *it;
  2427. if( system == NULL )
  2428. continue;
  2429. if( system->getAttachedObject() == obj->getID() )
  2430. system->destroy();
  2431. }
  2432. }
  2433. // ------------------------------------------------------------------------------------------------
  2434. /** Add a particle to the global particle list. */
  2435. // ------------------------------------------------------------------------------------------------
  2436. void ParticleSystemManager::addParticle( Particle *particleToAdd, ParticlePriorityType priority )
  2437. {
  2438. if (particleToAdd->m_inOverallList)
  2439. return;
  2440. if (!m_allParticlesHead[ priority ])
  2441. {
  2442. m_allParticlesHead[ priority ] = particleToAdd;
  2443. }
  2444. if (m_allParticlesTail[ priority ])
  2445. {
  2446. m_allParticlesTail[ priority ]->m_overallNext = particleToAdd;
  2447. particleToAdd->m_overallPrev = m_allParticlesTail[ priority ];
  2448. }
  2449. else
  2450. {
  2451. particleToAdd->m_overallPrev = NULL;
  2452. }
  2453. m_allParticlesTail[ priority ] = particleToAdd;
  2454. particleToAdd->m_overallNext = NULL;
  2455. particleToAdd->m_inOverallList = TRUE;
  2456. ++m_particleCount;
  2457. }
  2458. // ------------------------------------------------------------------------------------------------
  2459. /** Remove a particle from the global particle list. */
  2460. // ------------------------------------------------------------------------------------------------
  2461. void ParticleSystemManager::removeParticle( Particle *particleToRemove)
  2462. {
  2463. if (!particleToRemove->m_inOverallList)
  2464. return;
  2465. // get priority of the particle we're removing
  2466. ParticlePriorityType priority = particleToRemove->getPriority();
  2467. // remove links from prev & next objs
  2468. if (particleToRemove->m_overallNext)
  2469. particleToRemove->m_overallNext->m_overallPrev = particleToRemove->m_overallPrev;
  2470. if (particleToRemove->m_overallPrev)
  2471. particleToRemove->m_overallPrev->m_overallNext = particleToRemove->m_overallNext;
  2472. // update head & tail if neccessary
  2473. if (particleToRemove == m_allParticlesHead[ priority ])
  2474. m_allParticlesHead[ priority ] = particleToRemove->m_overallNext;
  2475. if (particleToRemove == m_allParticlesTail[ priority ])
  2476. m_allParticlesTail[ priority ] = particleToRemove->m_overallPrev;
  2477. particleToRemove->m_overallNext = particleToRemove->m_overallPrev = NULL;
  2478. particleToRemove->m_inOverallList = FALSE;
  2479. --m_particleCount;
  2480. }
  2481. // ------------------------------------------------------------------------------------------------
  2482. /** Add a particle system to the master particle system list. */
  2483. // ------------------------------------------------------------------------------------------------
  2484. void ParticleSystemManager::friend_addParticleSystem( ParticleSystem *particleSystemToAdd )
  2485. {
  2486. m_allParticleSystemList.push_back(particleSystemToAdd);
  2487. ++m_particleSystemCount;
  2488. }
  2489. // ------------------------------------------------------------------------------------------------
  2490. /** Remove a particle system from the master particle system list. */
  2491. // ------------------------------------------------------------------------------------------------
  2492. void ParticleSystemManager::friend_removeParticleSystem( ParticleSystem *particleSystemToRemove )
  2493. {
  2494. ParticleSystemListIt it = std::find(m_allParticleSystemList.begin(), m_allParticleSystemList.end(), particleSystemToRemove);
  2495. if (it != m_allParticleSystemList.end()) {
  2496. m_allParticleSystemList.erase(it);
  2497. --m_particleSystemCount;
  2498. }
  2499. }
  2500. // ------------------------------------------------------------------------------------------------
  2501. /** Remove the oldest N number of particles from the lowest priority lists first. We will
  2502. * not remove particles from any priorities higher or equal to the priorityCap parameter. */
  2503. // ------------------------------------------------------------------------------------------------
  2504. Int ParticleSystemManager::removeOldestParticles( UnsignedInt count,
  2505. ParticlePriorityType priorityCap )
  2506. {
  2507. Int countToRemove = count;
  2508. while (count-- && getParticleCount())
  2509. {
  2510. for( Int i = PARTICLE_PRIORITY_LOWEST;
  2511. i < priorityCap;
  2512. ++i )
  2513. {
  2514. if( m_allParticlesHead[ i ] )
  2515. {
  2516. m_allParticlesHead[ i ]->deleteInstance();
  2517. break; // exit for
  2518. }
  2519. }
  2520. }
  2521. // return the number of particles actually removed
  2522. return countToRemove - count;
  2523. }
  2524. // ------------------------------------------------------------------------------------------------
  2525. /** Preload particle system textures */
  2526. // ------------------------------------------------------------------------------------------------
  2527. void ParticleSystemManager::preloadAssets( TimeOfDay timeOfDay )
  2528. {
  2529. TemplateMap::iterator begin(m_templateMap.begin());
  2530. TemplateMap::iterator end(m_templateMap.end());
  2531. for (; begin != end; ++begin) {
  2532. const ParticleSystemTemplate *tmplate = (*begin).second;
  2533. if (tmplate->m_particleType == ParticleSystemInfo::PARTICLE &&
  2534. (! tmplate->m_particleTypeName.isEmpty()))
  2535. {
  2536. TheDisplay->preloadTextureAssets(tmplate->m_particleTypeName);
  2537. }
  2538. }
  2539. }
  2540. // ------------------------------------------------------------------------------------------------
  2541. /** CRC */
  2542. // ------------------------------------------------------------------------------------------------
  2543. void ParticleSystemManager::crc( Xfer *xfer )
  2544. {
  2545. } // end crc
  2546. // ------------------------------------------------------------------------------------------------
  2547. /** Xfer method
  2548. * Version Info:
  2549. * 1: Initial version */
  2550. // ------------------------------------------------------------------------------------------------
  2551. void ParticleSystemManager::xfer( Xfer *xfer )
  2552. {
  2553. // version
  2554. XferVersion currentVersion = 1;
  2555. XferVersion version = currentVersion;
  2556. xfer->xferVersion( &version, currentVersion );
  2557. // unique system ID counter
  2558. xfer->xferUser( &m_uniqueSystemID, sizeof( ParticleSystemID ) );
  2559. // count of particle systems in the world
  2560. UnsignedInt systemCount = m_particleSystemCount;
  2561. xfer->xferUnsignedInt( &systemCount );
  2562. // particle systems data
  2563. AsciiString systemName;
  2564. ParticleSystem *system;
  2565. if( xfer->getXferMode() == XFER_SAVE )
  2566. {
  2567. // iterate each particle system
  2568. ParticleSystemListIt it;
  2569. for( it = m_allParticleSystemList.begin(); it != m_allParticleSystemList.end(); ++it )
  2570. {
  2571. systemCount--;
  2572. // get system
  2573. system = *it;
  2574. // ignore destroyed systems and non-saveable systems
  2575. if( system->isDestroyed() == TRUE || system->isSaveable() == FALSE ) {
  2576. AsciiString mtString = "";
  2577. xfer->xferAsciiString(&mtString); // write null string as key for destroyed system.
  2578. continue;
  2579. }
  2580. // write template name
  2581. systemName = system->getTemplate()->getName();
  2582. xfer->xferAsciiString( &systemName );
  2583. // write system data
  2584. xfer->xferSnapshot( system );
  2585. } // end for, it
  2586. DEBUG_ASSERTCRASH(systemCount==0, ("Mismatch in write count."));
  2587. } // end if, save
  2588. else
  2589. {
  2590. const ParticleSystemTemplate *systemTemplate;
  2591. // read each particle system
  2592. for( UnsignedInt i = 0; i < systemCount; ++i )
  2593. {
  2594. // read system name and find template
  2595. xfer->xferAsciiString( &systemName );
  2596. if (systemName.isEmpty()) {
  2597. continue; // destroyed particle system.
  2598. }
  2599. systemTemplate = findTemplate( systemName );
  2600. // sanity
  2601. if( systemTemplate == NULL )
  2602. {
  2603. DEBUG_CRASH(( "ParticleSystemManager::xfer - Unknown particle system template '%s'\n",
  2604. systemName.str() ));
  2605. throw SC_INVALID_DATA;
  2606. } // end if
  2607. // create system
  2608. system = createParticleSystem( systemTemplate, FALSE );
  2609. if( system == NULL )
  2610. {
  2611. DEBUG_CRASH(( "ParticleSystemManager::xfer - Unable to allocate particle system '%s'\n",
  2612. systemName.str() ));
  2613. throw SC_INVALID_DATA;
  2614. } // end if
  2615. // read system data
  2616. xfer->xferSnapshot( system );
  2617. } // end for, i
  2618. } // end else, load
  2619. } // end particleSystemManager
  2620. // ------------------------------------------------------------------------------------------------
  2621. /** Load post process */
  2622. // ------------------------------------------------------------------------------------------------
  2623. void ParticleSystemManager::loadPostProcess( void )
  2624. {
  2625. } // end loadPostProcess
  2626. // ------------------------------------------------------------------------------------------------
  2627. /** Output particle system statistics to the screen
  2628. * @todo Implement a real console (MSB) */
  2629. // ------------------------------------------------------------------------------------------------
  2630. void ParticleSystemDebugDisplay( DebugDisplayInterface *dd, void *, FILE *fp )
  2631. {
  2632. if (!dd)
  2633. return;
  2634. dd->setCursorPos( 0, 0 );
  2635. dd->setRightMargin( 2 );
  2636. dd->printf( "Total Particles: %d\n", TheParticleSystemManager->getParticleCount() );
  2637. dd->printf( "Total Particles (On Screen): %d\n", TheParticleSystemManager->getOnScreenParticleCount());
  2638. dd->printf( "Total Particle Systems: %d\n", TheParticleSystemManager->getParticleSystemCount() );
  2639. ParticleSystemManager::ParticleSystemList list = TheParticleSystemManager->getAllParticleSystems();
  2640. ParticleSystemManager::ParticleSystemList::iterator it;
  2641. std::map<AsciiString, Int> templateMap;
  2642. std::map<AsciiString, Int> templateMapParticleCount;
  2643. std::map<AsciiString, Int>::iterator templateMapIt;
  2644. std::map<AsciiString, Int>::iterator templateMapParticleCountIt;
  2645. for ( it = list.begin(); it != list.end(); ++it )
  2646. {
  2647. AsciiString templateName = (*it)->getTemplate()->getName();
  2648. templateMapIt = templateMap.find(templateName);
  2649. if (templateMapIt == templateMap.end())
  2650. {
  2651. templateMap.insert(std::make_pair(templateName, 1));
  2652. templateMapParticleCount.insert(std::make_pair(templateName, (*it)->getParticleCount()));
  2653. }
  2654. else
  2655. {
  2656. ++templateMapIt->second;
  2657. templateMapParticleCountIt = templateMapParticleCount.find(templateName);
  2658. if (templateMapParticleCountIt != templateMapParticleCount.end())
  2659. templateMapParticleCountIt->second += (*it)->getParticleCount();
  2660. }
  2661. }
  2662. for (templateMapIt = templateMap.begin(); templateMapIt != templateMap.end(); ++templateMapIt)
  2663. {
  2664. templateMapParticleCountIt = templateMapParticleCount.find(templateMapIt->first);
  2665. dd->printf(" %s: %d instances", templateMapIt->first.str(), templateMapIt->second);
  2666. if (templateMapParticleCountIt != templateMapParticleCount.end())
  2667. dd->printf(" (Avg per system %.2f)", INT_TO_REAL(templateMapParticleCountIt->second) / templateMapIt->second);
  2668. dd->printf("\n");
  2669. }
  2670. }
  2671. // ------------------------------------------------------------------------------------------------
  2672. // ------------------------------------------------------------------------------------------------
  2673. static Real angleBetween(const Coord2D *vecA, const Coord2D *vecB)
  2674. {
  2675. if (!(vecA && vecA->length() && vecB && vecB->length())) {
  2676. return 0.0;
  2677. }
  2678. Real lengthA = vecA->length();
  2679. Real lengthB = vecB->length();
  2680. Real dotProduct = (vecA->x * vecB->x + vecA->y * vecB->y);
  2681. Real cosTheta = dotProduct / (lengthA * lengthB);
  2682. // If the dotproduct is 0.0, then they are orthogonal
  2683. if (dotProduct == 0.0f) {
  2684. if (vecB->x > 0) {
  2685. return PI;
  2686. }
  2687. return 0.0f;
  2688. }
  2689. Real theta = ACos( cosTheta );
  2690. if (vecB->x > 0) {
  2691. return theta;
  2692. }
  2693. return -theta;
  2694. }