afxParticleEmitter.cpp 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620
  1. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  2. // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  3. // Copyright (C) 2015 Faust Logic, Inc.
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to
  7. // deal in the Software without restriction, including without limitation the
  8. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9. // sell copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21. // IN THE SOFTWARE.
  22. //
  23. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  24. #include "afx/arcaneFX.h"
  25. #include "math/mathIO.h"
  26. #include "scene/sceneManager.h"
  27. #include "T3D/gameBase/gameProcess.h"
  28. #include "afx/util/afxPath.h"
  29. #include "afx/util/afxPath3D.h"
  30. #include "afx/ce/afxParticleEmitter.h"
  31. IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterData);
  32. ConsoleDocClass( afxParticleEmitterData,
  33. "@brief A base datablock inherited by AFX Particle Emitter effects.\n\n"
  34. "A base datablock inherited by AFX Particle Emitter effects."
  35. "\n\n"
  36. "@ingroup afxEffects\n"
  37. "@ingroup AFX\n"
  38. "@ingroup Datablocks\n"
  39. );
  40. afxParticleEmitterData::afxParticleEmitterData()
  41. {
  42. fade_velocity = false; // coordinate velocity amount with fade amout
  43. fade_offset = false; // coordinate ejection-offset amount with fade amount
  44. pe_vector.set(0.0,0.0,0.0);
  45. pe_vector_is_world = false;
  46. tpaths_string = ST_NULLSTRING;
  47. tPathDataBlocks.clear();
  48. tPathDataBlockIds.clear();
  49. }
  50. afxParticleEmitterData::afxParticleEmitterData(const afxParticleEmitterData& other, bool temp_clone) : ParticleEmitterData(other, temp_clone)
  51. {
  52. fade_velocity = other.fade_velocity;
  53. fade_offset = other.fade_offset;
  54. pe_vector = other.pe_vector;
  55. pe_vector_is_world = other.pe_vector_is_world;
  56. tpaths_string = other.tpaths_string;
  57. tPathDataBlocks = other.tPathDataBlocks;
  58. //tPathDataBlockIds = other.tPathDataBlockIds;
  59. }
  60. void afxParticleEmitterData::initPersistFields()
  61. {
  62. addField("fadeVelocity", TypeBool, Offset(fade_velocity, afxParticleEmitterData),
  63. "If true, the initial velocity of emitted particles is multiplied by the fade amount "
  64. "of the containing effect wrapper. As the effect fades-in and out, so does the "
  65. "initial velocity of new particles.");
  66. addField("fadeOffset", TypeBool, Offset(fade_offset, afxParticleEmitterData),
  67. "If true, the ejection offset of emitted particles is multiplied by the fade amount "
  68. "of the containing effect wrapper. As the effect fades-in and out, so does the "
  69. "ejection offset of new particles.");
  70. addField("vector", TypePoint3F, Offset(pe_vector, afxParticleEmitterData),
  71. "General direction vector used for emitting particles. Its exact interpretation is "
  72. "determined by the particle emitter subclass.");
  73. addField("vectorIsWorld", TypeBool, Offset(pe_vector_is_world, afxParticleEmitterData),
  74. "Sets whether the vector field should be interpreted as a vector in the world "
  75. "coordinate system.");
  76. addField("pathsTransform", TypeString, Offset(tpaths_string, afxParticleEmitterData),
  77. "A string of paths to be used as transform paths. Each path name must reference an "
  78. "afxPathData datablock. Transform paths are used to translate particles along a given "
  79. "path or series of paths.");
  80. Parent::initPersistFields();
  81. }
  82. void afxParticleEmitterData::packData(BitStream* stream)
  83. {
  84. Parent::packData(stream);
  85. stream->writeFlag(fade_velocity);
  86. stream->writeFlag(fade_offset);
  87. mathWrite(*stream, pe_vector);
  88. stream->writeFlag(pe_vector_is_world);
  89. stream->write(tPathDataBlockIds.size());
  90. for (int i = 0; i < tPathDataBlockIds.size(); i++)
  91. stream->write(tPathDataBlockIds[i]);
  92. }
  93. void afxParticleEmitterData::unpackData(BitStream* stream)
  94. {
  95. Parent::unpackData(stream);
  96. fade_velocity = stream->readFlag();
  97. fade_offset = stream->readFlag();
  98. mathRead(*stream, &pe_vector);
  99. pe_vector_is_world = stream->readFlag();
  100. U32 n_db; stream->read(&n_db);
  101. tPathDataBlockIds.setSize(n_db);
  102. for (U32 i = 0; i < n_db; i++)
  103. stream->read(&tPathDataBlockIds[i]);
  104. }
  105. bool afxParticleEmitterData::onAdd()
  106. {
  107. if( Parent::onAdd() == false )
  108. return false;
  109. if (tpaths_string != ST_NULLSTRING && tpaths_string[0] == '\0')
  110. {
  111. Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) empty transform paths string.", getName());
  112. return false;
  113. }
  114. if (tpaths_string != ST_NULLSTRING && dStrlen(tpaths_string) > 255)
  115. {
  116. Con::errorf(ConsoleLogEntry::General, "ParticleEmitterData(%s) transform paths string too long [> 255 chars].", getName());
  117. return false;
  118. }
  119. if (tpaths_string != ST_NULLSTRING)
  120. {
  121. Vector<char*> dataBlocks(__FILE__, __LINE__);
  122. dsize_t tokCopyLen = dStrlen(tpaths_string) + 1;
  123. char* tokCopy = new char[tokCopyLen];
  124. dStrcpy(tokCopy, tpaths_string, tokCopyLen);
  125. char* currTok = dStrtok(tokCopy, " \t");
  126. while (currTok != NULL)
  127. {
  128. dataBlocks.push_back(currTok);
  129. currTok = dStrtok(NULL, " \t");
  130. }
  131. if (dataBlocks.size() == 0)
  132. {
  133. Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) invalid transform paths string. No datablocks found", getName());
  134. delete [] tokCopy;
  135. return false;
  136. }
  137. tPathDataBlocks.clear();
  138. tPathDataBlockIds.clear();
  139. for (U32 i = 0; i < dataBlocks.size(); i++)
  140. {
  141. afxPathData* pData = NULL;
  142. if (Sim::findObject(dataBlocks[i], pData) == false)
  143. {
  144. Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) unable to find transform path datablock: %s", getName(), dataBlocks[i]);
  145. }
  146. else
  147. {
  148. tPathDataBlocks.push_back(pData);
  149. tPathDataBlockIds.push_back(pData->getId());
  150. }
  151. }
  152. delete [] tokCopy;
  153. if (tPathDataBlocks.size() == 0)
  154. {
  155. Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) unable to find any transform path datablocks", getName());
  156. return false;
  157. }
  158. }
  159. return true;
  160. }
  161. bool afxParticleEmitterData::preload(bool server, String &errorStr)
  162. {
  163. if (Parent::preload(server, errorStr) == false)
  164. return false;
  165. tPathDataBlocks.clear();
  166. for (U32 i = 0; i < tPathDataBlockIds.size(); i++)
  167. {
  168. afxPathData* pData = NULL;
  169. if (Sim::findObject(tPathDataBlockIds[i], pData) == false)
  170. {
  171. Con::warnf(ConsoleLogEntry::General,
  172. "ParticleEmitterData(%s) unable to find transform path datablock: %d",
  173. getName(), tPathDataBlockIds[i]);
  174. }
  175. else
  176. tPathDataBlocks.push_back(pData);
  177. }
  178. return true;
  179. }
  180. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  181. // VECTOR
  182. IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterVectorData);
  183. ConsoleDocClass( afxParticleEmitterVectorData,
  184. "@brief An AFX customized particle emitter that emits particles along a 3D vector.\n\n"
  185. "An AFX customized particle emitter that emits particles along a 3D vector."
  186. "\n\n"
  187. "@ingroup afxEffects\n"
  188. "@ingroup AFX\n"
  189. "@ingroup Datablocks\n"
  190. );
  191. afxParticleEmitterVectorData::afxParticleEmitterVectorData()
  192. {
  193. }
  194. afxParticleEmitterVectorData::afxParticleEmitterVectorData(const afxParticleEmitterVectorData& other, bool temp_clone) : afxParticleEmitterData(other, temp_clone)
  195. {
  196. }
  197. void afxParticleEmitterVectorData::initPersistFields()
  198. {
  199. Parent::initPersistFields();
  200. }
  201. void afxParticleEmitterVectorData::packData(BitStream* stream)
  202. {
  203. Parent::packData(stream);
  204. }
  205. void afxParticleEmitterVectorData::unpackData(BitStream* stream)
  206. {
  207. Parent::unpackData(stream);
  208. }
  209. bool afxParticleEmitterVectorData::onAdd()
  210. {
  211. if (Parent::onAdd() == false)
  212. return false;
  213. return true;
  214. }
  215. bool afxParticleEmitterVectorData::preload(bool server, String &errorStr)
  216. {
  217. if (Parent::preload(server, errorStr) == false)
  218. return false;
  219. return true;
  220. }
  221. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  222. // CONE
  223. IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterConeData);
  224. ConsoleDocClass( afxParticleEmitterConeData,
  225. "@brief An AFX customized particle emitter that emits particles within a cone shape.\n\n"
  226. "An AFX customized particle emitter that emits particles within a cone shape."
  227. "\n\n"
  228. "@ingroup afxEffects\n"
  229. "@ingroup AFX\n"
  230. "@ingroup Datablocks\n"
  231. );
  232. afxParticleEmitterConeData::afxParticleEmitterConeData()
  233. {
  234. spread_min = 0.0f;
  235. spread_max = 90.0f;
  236. }
  237. afxParticleEmitterConeData::afxParticleEmitterConeData(const afxParticleEmitterConeData& other, bool temp_clone) : afxParticleEmitterData(other, temp_clone)
  238. {
  239. spread_min = other.spread_min;
  240. spread_max = other.spread_max;
  241. }
  242. void afxParticleEmitterConeData::initPersistFields()
  243. {
  244. addField("spreadMin", TypeF32, Offset(spread_min, afxParticleEmitterConeData),
  245. "...");
  246. addField("spreadMax", TypeF32, Offset(spread_max, afxParticleEmitterConeData),
  247. "...");
  248. Parent::initPersistFields();
  249. }
  250. void afxParticleEmitterConeData::packData(BitStream* stream)
  251. {
  252. Parent::packData(stream);
  253. stream->writeRangedU32((U32)spread_min, 0, 180);
  254. stream->writeRangedU32((U32)spread_max, 0, 180);
  255. }
  256. void afxParticleEmitterConeData::unpackData(BitStream* stream)
  257. {
  258. Parent::unpackData(stream);
  259. spread_min = stream->readRangedU32(0, 180);
  260. spread_max = stream->readRangedU32(0, 180);
  261. }
  262. bool afxParticleEmitterConeData::onAdd()
  263. {
  264. if( Parent::onAdd() == false )
  265. return false;
  266. if (spread_min < 0.0f)
  267. {
  268. Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) spreadMin < 0.0", getName());
  269. spread_min = 0.0f;
  270. }
  271. if (spread_max > 180.0f)
  272. {
  273. Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) spreadMax > 180.0f", getName());
  274. spread_max = 180.0f;
  275. }
  276. if (spread_max > 179.5f)
  277. spread_max = 179.5f;
  278. if (spread_min > 179.5f)
  279. spread_min = 179.5f;
  280. if (spread_min > spread_max)
  281. {
  282. Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) spreadMin > spreadMax", getName());
  283. spread_min = spread_max;
  284. }
  285. return true;
  286. }
  287. bool afxParticleEmitterConeData::preload(bool server, String &errorStr)
  288. {
  289. if (Parent::preload(server, errorStr) == false)
  290. return false;
  291. return true;
  292. }
  293. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  294. // PATH
  295. IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterPathData);
  296. ConsoleDocClass( afxParticleEmitterPathData,
  297. "@brief An AFX customized particle emitter that emits particles along a path.\n\n"
  298. "An AFX customized particle emitter that emits particles along a path."
  299. "\n\n"
  300. "@ingroup afxEffects\n"
  301. "@ingroup AFX\n"
  302. "@ingroup Datablocks\n"
  303. );
  304. afxParticleEmitterPathData::afxParticleEmitterPathData()
  305. {
  306. epaths_string = ST_NULLSTRING;
  307. epathDataBlocks.clear();
  308. epathDataBlockIds.clear();
  309. path_origin_type = PATHEMIT_ORIGIN;
  310. ground_conform = false;
  311. ground_conform_terrain = true;
  312. ground_conform_interiors = true;
  313. ground_conform_height = 0.0f;
  314. }
  315. afxParticleEmitterPathData::afxParticleEmitterPathData(const afxParticleEmitterPathData& other, bool temp_clone) : afxParticleEmitterData(other, temp_clone)
  316. {
  317. epaths_string = other.epaths_string;
  318. epathDataBlocks = other.epathDataBlocks;
  319. //epathDataBlockIds = other.epathDataBlockIds;
  320. path_origin_type = other.path_origin_type;
  321. ground_conform = other.ground_conform;
  322. ground_conform_terrain = other.ground_conform_terrain;
  323. ground_conform_interiors = other.ground_conform_interiors;
  324. ground_conform_height = other.ground_conform_height;
  325. }
  326. ImplementEnumType( afxParticleEmitterPath_OriginType, "Possible particle emitter path origin types.\n" "@ingroup afxParticleEmitterPath\n\n" )
  327. { afxParticleEmitterPathData::PATHEMIT_ORIGIN, "origin", "..." },
  328. { afxParticleEmitterPathData::PATHEMIT_POINT, "point", "..." },
  329. { afxParticleEmitterPathData::PATHEMIT_VECTOR, "vector", "..." },
  330. { afxParticleEmitterPathData::PATHEMIT_TANGENT, "tangent", "..." },
  331. EndImplementEnumType;
  332. void afxParticleEmitterPathData::initPersistFields()
  333. {
  334. addField("paths", TypeString, Offset(epaths_string, afxParticleEmitterPathData),
  335. "...");
  336. addField("pathOrigin", TYPEID<afxParticleEmitterPathData::PathOriginType>(), Offset(path_origin_type, afxParticleEmitterPathData),
  337. "...");
  338. // JTF Note: take a look at these and make sure they are ok.
  339. addField("groundConform", TypeBool, Offset(ground_conform, afxParticleEmitterPathData),
  340. "...");
  341. addField("groundConformTerrain", TypeBool, Offset(ground_conform_terrain, afxParticleEmitterPathData),
  342. "...");
  343. addField("groundConformInteriors", TypeBool, Offset(ground_conform_interiors, afxParticleEmitterPathData),
  344. "...");
  345. addField("groundConformHeight", TypeF32, Offset(ground_conform_height, afxParticleEmitterPathData),
  346. "...");
  347. Parent::initPersistFields();
  348. }
  349. void afxParticleEmitterPathData::packData(BitStream* stream)
  350. {
  351. Parent::packData(stream);
  352. stream->write(epathDataBlockIds.size());
  353. for (int i = 0; i < epathDataBlockIds.size(); i++)
  354. stream->write(epathDataBlockIds[i]);
  355. stream->write(path_origin_type);
  356. stream->writeFlag(ground_conform);
  357. stream->writeFlag(ground_conform_terrain);
  358. stream->writeFlag(ground_conform_interiors);
  359. stream->write(ground_conform_height);
  360. }
  361. void afxParticleEmitterPathData::unpackData(BitStream* stream)
  362. {
  363. Parent::unpackData(stream);
  364. U32 n_db; stream->read(&n_db);
  365. epathDataBlockIds.setSize(n_db);
  366. for (U32 i = 0; i < n_db; i++)
  367. stream->read(&epathDataBlockIds[i]);
  368. stream->read(&path_origin_type);
  369. ground_conform = stream->readFlag();
  370. ground_conform_terrain = stream->readFlag();
  371. ground_conform_interiors = stream->readFlag();
  372. stream->read(&ground_conform_height);
  373. }
  374. bool afxParticleEmitterPathData::onAdd()
  375. {
  376. if( Parent::onAdd() == false )
  377. return false;
  378. // path
  379. if (epaths_string != ST_NULLSTRING && epaths_string[0] == '\0')
  380. {
  381. Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) empty paths string.", getName());
  382. return false;
  383. }
  384. if (epaths_string != ST_NULLSTRING && dStrlen(epaths_string) > 255)
  385. {
  386. Con::errorf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) paths string too long [> 255 chars].", getName());
  387. return false;
  388. }
  389. if (epaths_string != ST_NULLSTRING)
  390. {
  391. Vector<char*> dataBlocks(__FILE__, __LINE__);
  392. dsize_t tokCopyLen = dStrlen(epaths_string) + 1;
  393. char* tokCopy = new char[tokCopyLen];
  394. dStrcpy(tokCopy, epaths_string, tokCopyLen);
  395. char* currTok = dStrtok(tokCopy, " \t");
  396. while (currTok != NULL)
  397. {
  398. dataBlocks.push_back(currTok);
  399. currTok = dStrtok(NULL, " \t");
  400. }
  401. if (dataBlocks.size() == 0)
  402. {
  403. Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) invalid paths string. No datablocks found", getName());
  404. delete [] tokCopy;
  405. return false;
  406. }
  407. epathDataBlocks.clear();
  408. epathDataBlockIds.clear();
  409. for (U32 i = 0; i < dataBlocks.size(); i++)
  410. {
  411. afxPathData* pData = NULL;
  412. if (Sim::findObject(dataBlocks[i], pData) == false)
  413. {
  414. Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) unable to find path datablock: %s", getName(), dataBlocks[i]);
  415. }
  416. else
  417. {
  418. epathDataBlocks.push_back(pData);
  419. epathDataBlockIds.push_back(pData->getId());
  420. }
  421. }
  422. delete [] tokCopy;
  423. if (epathDataBlocks.size() == 0)
  424. {
  425. Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) unable to find any path datablocks", getName());
  426. return false;
  427. }
  428. }
  429. return true;
  430. }
  431. bool afxParticleEmitterPathData::preload(bool server, String &errorStr)
  432. {
  433. if (Parent::preload(server, errorStr) == false)
  434. return false;
  435. epathDataBlocks.clear();
  436. for (U32 i = 0; i < epathDataBlockIds.size(); i++)
  437. {
  438. afxPathData* pData = NULL;
  439. if (Sim::findObject(epathDataBlockIds[i], pData) == false)
  440. {
  441. Con::warnf(ConsoleLogEntry::General,
  442. "afxParticleEmitterPathData(%s) unable to find path datablock: %d",
  443. getName(), epathDataBlockIds[i]);
  444. }
  445. else
  446. epathDataBlocks.push_back(pData);
  447. }
  448. parts_per_eject = epathDataBlocks.size();
  449. return true;
  450. }
  451. void afxParticleEmitterPathData::onPerformSubstitutions()
  452. {
  453. Parent::onPerformSubstitutions();
  454. if (epaths_string != ST_NULLSTRING && epaths_string[0] == '\0')
  455. {
  456. Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) empty paths string.", getName());
  457. return;// false;
  458. }
  459. if (epaths_string != ST_NULLSTRING && dStrlen(epaths_string) > 255)
  460. {
  461. Con::errorf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) paths string too long [> 255 chars].", getName());
  462. return;// false;
  463. }
  464. if (epaths_string != ST_NULLSTRING)
  465. {
  466. Vector<char*> dataBlocks(__FILE__, __LINE__);
  467. dsize_t tokCopyLen = dStrlen(epaths_string) + 1;
  468. char* tokCopy = new char[tokCopyLen];
  469. dStrcpy(tokCopy, epaths_string, tokCopyLen);
  470. char* currTok = dStrtok(tokCopy, " \t");
  471. while (currTok != NULL)
  472. {
  473. dataBlocks.push_back(currTok);
  474. currTok = dStrtok(NULL, " \t");
  475. }
  476. if (dataBlocks.size() == 0)
  477. {
  478. Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) invalid paths string. No datablocks found", getName());
  479. delete [] tokCopy;
  480. return;// false;
  481. }
  482. epathDataBlocks.clear();
  483. epathDataBlockIds.clear();
  484. for (U32 i = 0; i < dataBlocks.size(); i++)
  485. {
  486. afxPathData* pData = NULL;
  487. if (Sim::findObject(dataBlocks[i], pData) == false)
  488. {
  489. Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) unable to find path datablock: %s", getName(), dataBlocks[i]);
  490. }
  491. else
  492. {
  493. epathDataBlocks.push_back(pData);
  494. epathDataBlockIds.push_back(pData->getId());
  495. }
  496. }
  497. delete [] tokCopy;
  498. if (epathDataBlocks.size() == 0)
  499. {
  500. Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) unable to find any path datablocks", getName());
  501. return;// false;
  502. }
  503. }
  504. /*epathDataBlocks.clear();
  505. for (U32 i = 0; i < epathDataBlockIds.size(); i++)
  506. {
  507. afxPathData* pData = NULL;
  508. if (Sim::findObject(epathDataBlockIds[i], pData) == false)
  509. {
  510. Con::warnf(ConsoleLogEntry::General,
  511. "afxParticleEmitterPathData(%s) unable to find path datablock: %d",
  512. getName(), epathDataBlockIds[i]);
  513. }
  514. else
  515. epathDataBlocks.push_back(pData);
  516. }
  517. */
  518. parts_per_eject = epathDataBlocks.size();
  519. }
  520. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  521. // DISC
  522. IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterDiscData);
  523. ConsoleDocClass( afxParticleEmitterDiscData,
  524. "@brief An AFX customized particle emitter that emits particles within a disc shape.\n\n"
  525. "An AFX customized particle emitter that emits particles within a disc shape."
  526. "\n\n"
  527. "@ingroup afxEffects\n"
  528. "@ingroup AFX\n"
  529. "@ingroup Datablocks\n"
  530. );
  531. afxParticleEmitterDiscData::afxParticleEmitterDiscData()
  532. {
  533. pe_radius_min = 0.0f;
  534. pe_radius_max = 1.0f;
  535. }
  536. afxParticleEmitterDiscData::afxParticleEmitterDiscData(const afxParticleEmitterDiscData& other, bool temp_clone) : afxParticleEmitterData(other, temp_clone)
  537. {
  538. pe_radius_min = other.pe_radius_min;
  539. pe_radius_max = other.pe_radius_max;
  540. }
  541. void afxParticleEmitterDiscData::initPersistFields()
  542. {
  543. addField("radiusMin", TypeF32, Offset(pe_radius_min, afxParticleEmitterDiscData),
  544. "...");
  545. addField("radiusMax", TypeF32, Offset(pe_radius_max, afxParticleEmitterDiscData),
  546. "...");
  547. Parent::initPersistFields();
  548. }
  549. void afxParticleEmitterDiscData::packData(BitStream* stream)
  550. {
  551. Parent::packData(stream);
  552. stream->writeInt((S32)(pe_radius_min * 100), 16);
  553. stream->writeInt((S32)(pe_radius_max * 100), 16);
  554. }
  555. void afxParticleEmitterDiscData::unpackData(BitStream* stream)
  556. {
  557. Parent::unpackData(stream);
  558. pe_radius_min = stream->readInt(16) / 100.0f;
  559. pe_radius_max = stream->readInt(16) / 100.0f;
  560. }
  561. bool afxParticleEmitterDiscData::onAdd()
  562. {
  563. if( Parent::onAdd() == false )
  564. return false;
  565. return true;
  566. }
  567. bool afxParticleEmitterDiscData::preload(bool server, String &errorStr)
  568. {
  569. if (Parent::preload(server, errorStr) == false)
  570. return false;
  571. return true;
  572. }
  573. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  574. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  575. afxParticleEmitter::afxParticleEmitter()
  576. {
  577. pe_vector.set(0,0,1);
  578. pe_vector_norm.set(0,0,1);
  579. tpaths.clear();
  580. tpath_mults.clear();
  581. n_tpath_points = 0;
  582. tpath_points = NULL;
  583. afx_owner = 0;
  584. }
  585. afxParticleEmitter::~afxParticleEmitter()
  586. {
  587. }
  588. bool afxParticleEmitter::onAdd()
  589. {
  590. if( !Parent::onAdd() )
  591. return false;
  592. if (dynamic_cast<afxParticleEmitterData*>(mDataBlock))
  593. init_paths();
  594. return true;
  595. }
  596. void afxParticleEmitter::onRemove()
  597. {
  598. if (dynamic_cast<afxParticleEmitterData*>(mDataBlock))
  599. cleanup_paths();
  600. Parent::onRemove();
  601. }
  602. void afxParticleEmitter::init_paths()
  603. {
  604. if (!mDataBlock)
  605. {
  606. n_tpath_points = 0;
  607. tpath_points = NULL;
  608. return;
  609. }
  610. if (mDataBlock->tPathDataBlocks.size() < 1)
  611. {
  612. n_tpath_points = 0;
  613. tpath_points = NULL;
  614. }
  615. else
  616. {
  617. n_tpath_points = mDataBlock->tPathDataBlocks.size();
  618. tpath_points = new Point3F*[n_tpath_points];
  619. for (U32 i=0; i < n_tpath_points; i++)
  620. {
  621. afxPathData* pd = mDataBlock->tPathDataBlocks[i];
  622. if (!pd)
  623. continue;
  624. if (pd->getSubstitutionCount() > 0 && afx_owner)
  625. {
  626. afxPathData* orig_db = pd;
  627. pd = new afxPathData(*orig_db, true);
  628. orig_db->performSubstitutions(pd, afx_owner);
  629. }
  630. if (pd->num_points > 0)
  631. {
  632. afxPath3D* path = new afxPath3D();
  633. if (pd->times)
  634. path->buildPath( pd->num_points, pd->points, pd->times, pd->delay, 1.0f );
  635. else if (pd->lifetime == 0)
  636. path->buildPath( pd->num_points, pd->points, pd->delay, 1.0f );
  637. else
  638. path->buildPath( pd->num_points, pd->points, pd->delay, pd->delay+pd->lifetime );
  639. path->setLoopType( pd->loop_type );
  640. tpaths.push_back(path);
  641. tpath_mults.push_back( pd->mult );
  642. tpath_points[i] = new Point3F[pd->num_points];
  643. for (U32 j=0; j<pd->num_points; j++)
  644. tpath_points[i][j] = pd->points[j];
  645. }
  646. else
  647. {
  648. Con::warnf("afxParticleEmitter::init_paths() -- pathsTransform datablock (%d) has no points.", i);
  649. }
  650. if (pd->isTempClone())
  651. delete pd;
  652. }
  653. }
  654. }
  655. void afxParticleEmitter::cleanup_paths()
  656. {
  657. if (n_tpath_points < 1)
  658. return;
  659. for (U32 i=0; i < tpaths.size(); i++)
  660. {
  661. if (tpaths[i])
  662. delete tpaths[i];
  663. }
  664. tpaths.clear();
  665. if (tpath_points)
  666. {
  667. if (mDataBlock)
  668. {
  669. for (U32 i=0; i < n_tpath_points; i++)
  670. {
  671. if (tpath_points[i])
  672. delete [] tpath_points[i];
  673. }
  674. }
  675. delete [] tpath_points;
  676. tpath_points = 0;
  677. }
  678. }
  679. void afxParticleEmitter::sub_particleUpdate(Particle* part)
  680. {
  681. if (tpaths.size() < 1)
  682. return;
  683. F32 t = ((F32)part->currentAge)/((F32)part->totalLifetime);
  684. for (U32 i=0; i < tpaths.size(); i++)
  685. {
  686. F32 t_last = part->t_last;
  687. Point3F path_delta = (t_last <= 0.0f) ? tpaths[i]->evaluateAtTime(t) : tpaths[i]->evaluateAtTime(t_last, t);
  688. if (mDataBlock->tPathDataBlocks[i]->concentric)
  689. {
  690. // scale radial vector by x-component of path
  691. part->pos_local += part->radial_v*path_delta.x;
  692. // scale axis vector by z-component of path
  693. part->pos_local += pe_vector_norm*path_delta.z;
  694. // y-component is ignored
  695. }
  696. else
  697. {
  698. part->pos_local += path_delta;
  699. }
  700. }
  701. part->t_last = t;
  702. }
  703. void afxParticleEmitter::preCompute(const MatrixF& mat)
  704. {
  705. // Put vector into the space of the input matrix
  706. pe_vector = mDataBlock->pe_vector;
  707. if (!mDataBlock->pe_vector_is_world)
  708. mat.mulV(pe_vector);
  709. pe_vector_norm = pe_vector;
  710. pe_vector_norm.normalize();
  711. // Transform Paths: rebuild with current matrix
  712. for( U32 i=0; i < tpaths.size(); i++ )
  713. {
  714. for( U32 j=0; j < tpaths[i]->getNumPoints(); j++ )
  715. {
  716. Point3F p = tpath_points[i][j];
  717. mat.mulV(p);
  718. tpaths[i]->setPointPosition(j, p);
  719. }
  720. tpaths[i]->reBuildPath();
  721. }
  722. sub_preCompute(mat);
  723. }
  724. void afxParticleEmitter::afx_emitParticles(const Point3F& point, const bool useLastPosition, const Point3F& velocity, const U32 numMilliseconds)
  725. {
  726. if (mDead) return;
  727. // lifetime over - no more particles
  728. if (mLifetimeMS > 0 && mElapsedTimeMS > mLifetimeMS)
  729. return;
  730. Point3F realStart;
  731. if (useLastPosition && mHasLastPosition)
  732. realStart = mLastPosition;
  733. else
  734. realStart = point;
  735. afx_emitParticles(realStart, point, velocity, numMilliseconds);
  736. }
  737. void afxParticleEmitter::afx_emitParticles(const Point3F& start, const Point3F& end, const Point3F& velocity, const U32 numMilliseconds)
  738. {
  739. if (mDead) return;
  740. // lifetime over - no more particles
  741. if (mLifetimeMS > 0 && mElapsedTimeMS > mLifetimeMS)
  742. return;
  743. U32 currTime = 0;
  744. bool particlesAdded = false;
  745. if (mNextParticleTime != 0)
  746. {
  747. // Need to handle next particle
  748. //
  749. if (mNextParticleTime > numMilliseconds)
  750. {
  751. // Defer to next update
  752. // (Note that this introduces a potential spatial irregularity if the owning
  753. // object is accelerating, and updating at a low frequency)
  754. //
  755. mNextParticleTime -= numMilliseconds;
  756. mInternalClock += numMilliseconds;
  757. mLastPosition = end;
  758. mHasLastPosition = true;
  759. return;
  760. }
  761. else
  762. {
  763. currTime += mNextParticleTime;
  764. mInternalClock += mNextParticleTime;
  765. // Emit particle at curr time
  766. // Create particle at the correct position
  767. Point3F pos;
  768. pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds));
  769. for (S32 p = 0; p < mDataBlock->parts_per_eject; p++)
  770. {
  771. sub_addParticle(pos, velocity, numMilliseconds-currTime, p);
  772. particlesAdded = true;
  773. }
  774. mNextParticleTime = 0;
  775. }
  776. }
  777. while (currTime < numMilliseconds)
  778. {
  779. S32 nextTime = mDataBlock->ejectionPeriodMS;
  780. if (mDataBlock->periodVarianceMS != 0)
  781. {
  782. nextTime += S32(gRandGen.randI() % (2 * mDataBlock->periodVarianceMS + 1)) -
  783. S32(mDataBlock->periodVarianceMS);
  784. }
  785. AssertFatal(nextTime > 0, "Error, next particle ejection time must always be greater than 0");
  786. if (currTime + nextTime > numMilliseconds)
  787. {
  788. mNextParticleTime = (currTime + nextTime) - numMilliseconds;
  789. mInternalClock += numMilliseconds - currTime;
  790. AssertFatal(mNextParticleTime > 0, "Error, should not have deferred this particle!");
  791. break;
  792. }
  793. currTime += nextTime;
  794. mInternalClock += nextTime;
  795. // Create particle at the correct position
  796. Point3F pos;
  797. pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds));
  798. U32 advanceMS = numMilliseconds - currTime;
  799. if (mDataBlock->overrideAdvance == false && advanceMS != 0)
  800. {
  801. for (S32 p = 0; p < mDataBlock->parts_per_eject; p++)
  802. {
  803. sub_addParticle(pos, velocity, numMilliseconds-currTime, p);
  804. particlesAdded = true;
  805. Particle* last_part = part_list_head.next;
  806. if (last_part)
  807. {
  808. if (advanceMS > last_part->totalLifetime)
  809. {
  810. part_list_head.next = last_part->next;
  811. n_parts--;
  812. last_part->next = part_freelist;
  813. part_freelist = last_part;
  814. }
  815. else
  816. {
  817. F32 t = F32(advanceMS) / 1000.0;
  818. Point3F a = last_part->acc;
  819. a -= last_part->vel*last_part->dataBlock->dragCoefficient;
  820. a -= mWindVelocity*last_part->dataBlock->windCoefficient;
  821. //a += Point3F(0, 0, -9.81) * last_part->dataBlock->gravityCoefficient;
  822. a.z += -9.81f*last_part->dataBlock->gravityCoefficient; // as long as gravity is a constant, this is faster
  823. last_part->vel += a * t;
  824. last_part->pos_local += last_part->vel * t;
  825. // allow subclasses to adjust the particle params here
  826. sub_particleUpdate(last_part);
  827. if (last_part->dataBlock->constrain_pos)
  828. last_part->pos = last_part->pos_local + this->pos_pe;
  829. else
  830. last_part->pos = last_part->pos_local;
  831. updateKeyData(last_part);
  832. }
  833. }
  834. }
  835. }
  836. else
  837. {
  838. for (S32 p = 0; p < mDataBlock->parts_per_eject; p++)
  839. {
  840. sub_addParticle(pos, velocity, numMilliseconds-currTime, p);
  841. particlesAdded = true;
  842. }
  843. }
  844. }
  845. if( particlesAdded == true )
  846. updateBBox();
  847. if( n_parts > 0 && mSceneManager == NULL )
  848. {
  849. gClientSceneGraph->addObjectToScene(this);
  850. ClientProcessList::get()->addObject(this);
  851. }
  852. mLastPosition = end;
  853. mHasLastPosition = true;
  854. }
  855. Particle* afxParticleEmitter::alloc_particle()
  856. {
  857. n_parts++;
  858. // this should happen rarely
  859. if (n_parts > n_part_capacity)
  860. {
  861. Particle* store_block = new Particle[16];
  862. part_store.push_back(store_block);
  863. n_part_capacity += 16;
  864. for (S32 i = 0; i < 16; i++)
  865. {
  866. store_block[i].next = part_freelist;
  867. part_freelist = &store_block[i];
  868. }
  869. mDataBlock->allocPrimBuffer(n_part_capacity);
  870. }
  871. Particle* pNew = part_freelist;
  872. part_freelist = pNew->next;
  873. pNew->next = part_list_head.next;
  874. part_list_head.next = pNew;
  875. return pNew;
  876. }
  877. ParticleData* afxParticleEmitter::pick_particle_type()
  878. {
  879. U32 dBlockIndex = (U32)(mCeil(gRandGen.randF() * F32(mDataBlock->particleDataBlocks.size())) - 1);
  880. return mDataBlock->particleDataBlocks[dBlockIndex];
  881. }
  882. bool afxParticleEmitter::onNewDataBlock(GameBaseData* dptr, bool reload)
  883. {
  884. mDataBlock = dynamic_cast<afxParticleEmitterData*>(dptr);
  885. if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
  886. return false;
  887. if (mDataBlock->isTempClone())
  888. return true;
  889. scriptOnNewDataBlock();
  890. return true;
  891. }
  892. void afxParticleEmitter::emitParticlesExt(const MatrixF& xfm, const Point3F& point, const Point3F& velocity, const U32 numMilliseconds)
  893. {
  894. if (mDataBlock->use_emitter_xfm)
  895. {
  896. Point3F zero_point(0.0f, 0.0f, 0.0f);
  897. pos_pe = zero_point;
  898. setTransform(xfm);
  899. preCompute(xfm);
  900. afx_emitParticles(zero_point, true, velocity, numMilliseconds);
  901. }
  902. else
  903. {
  904. pos_pe = point;
  905. preCompute(xfm);
  906. afx_emitParticles(point, true, velocity, numMilliseconds);
  907. }
  908. }
  909. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  910. // VECTOR
  911. afxParticleEmitterVector::afxParticleEmitterVector()
  912. {
  913. }
  914. afxParticleEmitterVector::~afxParticleEmitterVector()
  915. {
  916. }
  917. bool afxParticleEmitterVector::onNewDataBlock(GameBaseData* dptr, bool reload)
  918. {
  919. mDataBlock = dynamic_cast<afxParticleEmitterVectorData*>(dptr);
  920. if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
  921. return false;
  922. if (mDataBlock->isTempClone())
  923. return true;
  924. scriptOnNewDataBlock();
  925. return true;
  926. }
  927. void afxParticleEmitterVector::sub_addParticle(const Point3F& pos, const Point3F& vel, const U32 age_offset, S32 part_idx)
  928. {
  929. Particle* pNew = alloc_particle();
  930. ParticleData* part_db = pick_particle_type();
  931. Point3F pos_start = pos;
  932. if (part_db->constrain_pos)
  933. pos_start.set(0,0,0);
  934. F32 initialVel = mDataBlock->ejectionVelocity;
  935. initialVel += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
  936. if(mDataBlock->fade_velocity)
  937. initialVel *= fade_amt;
  938. F32 ejection_offset = mDataBlock->ejectionOffset;
  939. if(mDataBlock->fade_offset)
  940. ejection_offset *= fade_amt;
  941. pNew->pos = pos_start + (pe_vector_norm * ejection_offset);
  942. pNew->pos_local = pNew->pos;
  943. pNew->vel = pe_vector_norm * initialVel;
  944. if (mDataBlock->orientParticles)
  945. pNew->orientDir = pe_vector_norm;
  946. else
  947. // note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
  948. pNew->orientDir.x = mDegToRad(part_db->start_angle + part_db->angle_variance*2.0f*gRandGen.randF() - part_db->angle_variance);
  949. pNew->acc.set(0, 0, 0);
  950. pNew->currentAge = age_offset;
  951. pNew->t_last = 0.0f;
  952. pNew->radial_v.set(0.0f, 0.0f, 0.0f);
  953. part_db->initializeParticle(pNew, vel);
  954. updateKeyData( pNew );
  955. }
  956. void afxParticleEmitterVector::sub_preCompute(const MatrixF& mat)
  957. {
  958. }
  959. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  960. // CONE
  961. afxParticleEmitterCone::afxParticleEmitterCone()
  962. {
  963. cone_v.set(0,0,1);
  964. cone_s0.set(0,0,1);
  965. cone_s1.set(0,0,1);
  966. }
  967. afxParticleEmitterCone::~afxParticleEmitterCone()
  968. {
  969. }
  970. bool afxParticleEmitterCone::onNewDataBlock(GameBaseData* dptr, bool reload)
  971. {
  972. mDataBlock = dynamic_cast<afxParticleEmitterConeData*>(dptr);
  973. if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
  974. return false;
  975. if (mDataBlock->isTempClone())
  976. return true;
  977. scriptOnNewDataBlock();
  978. return true;
  979. }
  980. void afxParticleEmitterCone::sub_addParticle(const Point3F& pos, const Point3F& vel, const U32 age_offset, S32 part_idx)
  981. {
  982. Particle* pNew = alloc_particle();
  983. ParticleData* part_db = pick_particle_type();
  984. Point3F pos_start = pos;
  985. if (part_db->constrain_pos)
  986. pos_start.set(0,0,0);
  987. F32 initialVel = mDataBlock->ejectionVelocity;
  988. initialVel += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
  989. if(mDataBlock->fade_velocity)
  990. initialVel *= fade_amt;
  991. // Randomly choose a vector between cone_s0 and cone_s1 and normalize:
  992. Point3F vec;
  993. F32 t = mRandF(0.0f, 1.0f);
  994. vec.interpolate(cone_s0, cone_s1, t);
  995. vec.normalize();
  996. // Randomly rotate about cone_v
  997. F32 theta = mRandF(0.0f, M_2PI_F);
  998. AngAxisF thetaRot(cone_v, theta);
  999. MatrixF temp(true);
  1000. thetaRot.setMatrix(&temp);
  1001. temp.mulP(vec);
  1002. F32 ejection_offset = mDataBlock->ejectionOffset;
  1003. if(mDataBlock->fade_offset)
  1004. ejection_offset *= fade_amt;
  1005. pNew->pos = pos_start + (vec * ejection_offset);
  1006. pNew->pos_local = pNew->pos;
  1007. pNew->vel = mDataBlock->ejectionInvert ? vec * -initialVel : vec * initialVel;
  1008. if (mDataBlock->orientParticles)
  1009. pNew->orientDir = vec;
  1010. else
  1011. // note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
  1012. pNew->orientDir.x = mDegToRad(part_db->start_angle + part_db->angle_variance*2.0f*gRandGen.randF() - part_db->angle_variance);
  1013. pNew->acc.set(0, 0, 0);
  1014. pNew->currentAge = age_offset;
  1015. pNew->t_last = 0.0f;
  1016. pNew->radial_v.set(0.0f, 0.0f, 0.0f);
  1017. part_db->initializeParticle(pNew, vel);
  1018. updateKeyData( pNew );
  1019. }
  1020. void afxParticleEmitterCone::sub_preCompute(const MatrixF& mat)
  1021. {
  1022. // Find vectors on the XZ plane corresponding to the inner and outer spread angles:
  1023. // (tan is infinite at PI/4 or 90 degrees)
  1024. cone_v.set( 0.0f, 0.0f, 1.0f );
  1025. cone_s0.x = mTan( mDegToRad( ((afxParticleEmitterConeData*)mDataBlock)->spread_min / 2.0f ));
  1026. cone_s0.y = 0.0f;
  1027. cone_s0.z = 1.0f;
  1028. cone_s1.x = mTan( mDegToRad(((afxParticleEmitterConeData*)mDataBlock)->spread_max / 2.0f ));
  1029. cone_s1.y = 0.0f;
  1030. cone_s1.z = 1.0f;
  1031. Point3F axis;
  1032. F32 theta = mAcos( mDot(cone_v, pe_vector_norm) );
  1033. if( M_PI_F-theta < POINT_EPSILON )
  1034. {
  1035. cone_v.neg();
  1036. cone_s0.neg();
  1037. cone_s1.neg();
  1038. }
  1039. else if( theta > POINT_EPSILON )
  1040. {
  1041. mCross(pe_vector_norm, cone_v, &axis);
  1042. axis.normalize();
  1043. AngAxisF thetaRot(axis, theta);
  1044. MatrixF temp(true);
  1045. thetaRot.setMatrix(&temp);
  1046. temp.mulP(cone_v);
  1047. temp.mulP(cone_s0);
  1048. temp.mulP(cone_s1);
  1049. }
  1050. }
  1051. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  1052. // PATH
  1053. afxParticleEmitterPath::afxParticleEmitterPath()
  1054. {
  1055. epaths.clear();
  1056. epath_mults.clear();
  1057. n_epath_points = 0;
  1058. epath_points = NULL;
  1059. }
  1060. afxParticleEmitterPath::~afxParticleEmitterPath()
  1061. {
  1062. }
  1063. bool afxParticleEmitterPath::onNewDataBlock(GameBaseData* dptr, bool reload)
  1064. {
  1065. mDataBlock = dynamic_cast<afxParticleEmitterPathData*>(dptr);
  1066. if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
  1067. return false;
  1068. if (mDataBlock->isTempClone())
  1069. return true;
  1070. scriptOnNewDataBlock();
  1071. return true;
  1072. }
  1073. bool afxParticleEmitterPath::onAdd()
  1074. {
  1075. if( !Parent::onAdd() )
  1076. return false;
  1077. if (dynamic_cast<afxParticleEmitterPathData*>(mDataBlock))
  1078. init_paths();
  1079. return true;
  1080. }
  1081. void afxParticleEmitterPath::onRemove()
  1082. {
  1083. if (dynamic_cast<afxParticleEmitterPathData*>(mDataBlock))
  1084. cleanup_paths();
  1085. Parent::onRemove();
  1086. }
  1087. void afxParticleEmitterPath::init_paths()
  1088. {
  1089. if (!mDataBlock || ((afxParticleEmitterPathData*)mDataBlock)->epathDataBlocks.size() < 1)
  1090. {
  1091. n_epath_points = 0;
  1092. epath_points = NULL;
  1093. return;
  1094. }
  1095. n_epath_points = ((afxParticleEmitterPathData*)mDataBlock)->epathDataBlocks.size();
  1096. epath_points = new Point3F*[n_epath_points];
  1097. dMemset(epath_points, 0, n_epath_points*sizeof(Point3F*));
  1098. for (U32 i=0; i < n_epath_points; i++)
  1099. {
  1100. afxPathData* pd = ((afxParticleEmitterPathData*)mDataBlock)->epathDataBlocks[i];
  1101. if (!pd)
  1102. continue;
  1103. if (pd->getSubstitutionCount() > 0 && afx_owner)
  1104. {
  1105. afxPathData* orig_db = pd;
  1106. pd = new afxPathData(*orig_db, true);
  1107. orig_db->performSubstitutions(pd, afx_owner);
  1108. }
  1109. if (pd->num_points > 0)
  1110. {
  1111. afxPath3D* path = new afxPath3D();
  1112. if (pd->times)
  1113. path->buildPath( pd->num_points, pd->points, pd->times, 0.0f, 1.0f );
  1114. else
  1115. path->buildPath( pd->num_points, pd->points, 0.0f, 1.0f );
  1116. epaths.push_back(path);
  1117. epath_mults.push_back( pd->mult );
  1118. epath_points[i] = new Point3F[pd->num_points];
  1119. for (U32 j=0; j<pd->num_points; j++)
  1120. epath_points[i][j] = pd->points[j];
  1121. }
  1122. else
  1123. {
  1124. Con::warnf("afxParticleEmitterPath::init_paths() -- paths datablock (%d) has no points.", i);
  1125. }
  1126. if (pd->isTempClone())
  1127. delete pd;
  1128. }
  1129. }
  1130. void afxParticleEmitterPath::cleanup_paths()
  1131. {
  1132. if (n_epath_points < 1)
  1133. return;
  1134. for (U32 i=0; i < epaths.size(); i++)
  1135. {
  1136. if (epaths[i])
  1137. delete epaths[i];
  1138. }
  1139. epaths.clear();
  1140. if (epath_points)
  1141. {
  1142. if (mDataBlock)
  1143. {
  1144. for (U32 i=0; i < n_epath_points; i++)
  1145. {
  1146. if (epath_points[i])
  1147. delete [] epath_points[i];
  1148. }
  1149. }
  1150. delete [] epath_points;
  1151. epath_points = 0;
  1152. }
  1153. }
  1154. void afxParticleEmitterPath::sub_addParticle(const Point3F& pos, const Point3F& vel, const U32 age_offset, S32 part_idx)
  1155. {
  1156. if (part_idx >= epaths.size() || !epaths[part_idx])
  1157. return;
  1158. Particle* pNew = alloc_particle();
  1159. ParticleData* part_db = pick_particle_type();
  1160. Point3F pos_start = pos;
  1161. if (part_db->constrain_pos)
  1162. pos_start.set(0,0,0);
  1163. F32 initialVel = mDataBlock->ejectionVelocity;
  1164. initialVel += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
  1165. if(mDataBlock->fade_velocity)
  1166. initialVel *= fade_amt;
  1167. // Randomly choose a curve parameter between [0.0,1.0] and evaluate the curve there
  1168. F32 param = mRandF(0.0f, 1.0f);
  1169. Point3F curve_pos = epaths[part_idx]->evaluateAtTime(param);
  1170. Point3F vec;
  1171. switch (((afxParticleEmitterPathData*)mDataBlock)->path_origin_type)
  1172. {
  1173. case afxParticleEmitterPathData::PATHEMIT_ORIGIN :
  1174. vec = curve_pos;
  1175. vec.normalize();
  1176. break;
  1177. case afxParticleEmitterPathData::PATHEMIT_POINT :
  1178. vec = curve_pos-pe_vector;
  1179. vec.normalize();
  1180. break;
  1181. case afxParticleEmitterPathData::PATHEMIT_VECTOR :
  1182. vec = pe_vector_norm;
  1183. break;
  1184. case afxParticleEmitterPathData::PATHEMIT_TANGENT :
  1185. vec = epaths[part_idx]->evaluateTangentAtTime(param);
  1186. vec.normalize();
  1187. break;
  1188. }
  1189. F32 ejection_offset = mDataBlock->ejectionOffset;
  1190. if(mDataBlock->fade_offset)
  1191. ejection_offset *= fade_amt;
  1192. pNew->pos = pos_start + curve_pos + (vec * ejection_offset);
  1193. pNew->pos_local = pNew->pos;
  1194. pNew->vel = mDataBlock->ejectionInvert ? vec * -initialVel : vec * initialVel;
  1195. if (mDataBlock->orientParticles)
  1196. pNew->orientDir = vec;
  1197. else
  1198. // note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
  1199. pNew->orientDir.x = mDegToRad(part_db->start_angle + part_db->angle_variance*2.0f*gRandGen.randF() - part_db->angle_variance);
  1200. pNew->acc.set(0, 0, 0);
  1201. pNew->currentAge = age_offset;
  1202. pNew->t_last = 0.0f;
  1203. pNew->radial_v.set(0.0f, 0.0f, 0.0f);
  1204. part_db->initializeParticle(pNew, vel);
  1205. updateKeyData( pNew );
  1206. }
  1207. void afxParticleEmitterPath::sub_preCompute(const MatrixF& mat)
  1208. {
  1209. for( U32 i=0; i < epaths.size(); i++ )
  1210. {
  1211. for( U32 j=0; j < epaths[i]->getNumPoints(); j++ )
  1212. {
  1213. Point3F p = epath_points[i][j];
  1214. mat.mulV(p);
  1215. p *= epath_mults[i];
  1216. if(mDataBlock->ground_conform) {
  1217. groundConformPoint(p, mat);
  1218. }
  1219. epaths[i]->setPointPosition(j, p);
  1220. }
  1221. epaths[i]->reBuildPath();
  1222. }
  1223. }
  1224. void afxParticleEmitterPath::groundConformPoint(Point3F& point, const MatrixF& mat)
  1225. {
  1226. point += mat.getPosition();
  1227. RayInfo rInfo;
  1228. bool hit = false;
  1229. if (mDataBlock->ground_conform_interiors)
  1230. {
  1231. U32 mask = InteriorLikeObjectType;
  1232. if (mDataBlock->ground_conform_terrain)
  1233. {
  1234. mask |= TerrainObjectType | TerrainLikeObjectType;
  1235. }
  1236. Point3F above_pos(point); above_pos.z += 0.1f;
  1237. Point3F below_pos(point); below_pos.z -= 10000;
  1238. hit = gClientContainer.castRay(above_pos, below_pos, mask, &rInfo);
  1239. if (!hit)
  1240. {
  1241. above_pos.z = point.z + 10000;
  1242. below_pos.z = point.z - 0.1f;
  1243. hit = gClientContainer.castRay(below_pos, above_pos, mask, &rInfo);
  1244. }
  1245. }
  1246. else if (mDataBlock->ground_conform_terrain)
  1247. {
  1248. U32 mask = TerrainObjectType | TerrainLikeObjectType;
  1249. Point3F above_pos(point); above_pos.z += 10000;
  1250. Point3F below_pos(point); below_pos.z -= 10000;
  1251. hit = gClientContainer.castRay(above_pos, below_pos, mask, &rInfo);
  1252. }
  1253. if (hit)
  1254. {
  1255. F32 terrain_z = rInfo.point.z;
  1256. F32 new_z = terrain_z + mDataBlock->ground_conform_height;
  1257. point.z = new_z;
  1258. }
  1259. point -= mat.getPosition();
  1260. }
  1261. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  1262. // DISC
  1263. afxParticleEmitterDisc::afxParticleEmitterDisc()
  1264. {
  1265. disc_v.set(0,0,1);
  1266. disc_r.set(1,0,0);
  1267. }
  1268. afxParticleEmitterDisc::~afxParticleEmitterDisc()
  1269. {
  1270. }
  1271. bool afxParticleEmitterDisc::onNewDataBlock(GameBaseData* dptr, bool reload)
  1272. {
  1273. mDataBlock = dynamic_cast<afxParticleEmitterDiscData*>(dptr);
  1274. if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
  1275. return false;
  1276. if (mDataBlock->isTempClone())
  1277. return true;
  1278. return true;
  1279. }
  1280. void afxParticleEmitterDisc::sub_addParticle(const Point3F& pos, const Point3F& vel, const U32 age_offset, S32 part_idx)
  1281. {
  1282. Particle* pNew = alloc_particle();
  1283. ParticleData* part_db = pick_particle_type();
  1284. Point3F pos_start = pos;
  1285. if (part_db->constrain_pos)
  1286. pos_start.set(0,0,0);
  1287. F32 initialVel = mDataBlock->ejectionVelocity;
  1288. initialVel += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
  1289. if(mDataBlock->fade_velocity)
  1290. initialVel *= fade_amt;
  1291. // Randomly choose a radius vector
  1292. Point3F r( disc_r );
  1293. F32 radius = mRandF(((afxParticleEmitterDiscData*)mDataBlock)->pe_radius_min, ((afxParticleEmitterDiscData*)mDataBlock)->pe_radius_max);
  1294. r *= radius;
  1295. // Randomly rotate r about disc_v
  1296. F32 theta = mRandF(0.0f, M_2PI_F);
  1297. AngAxisF thetaRot(disc_v, theta);
  1298. MatrixF temp(true);
  1299. thetaRot.setMatrix(&temp);
  1300. temp.mulP(r);
  1301. F32 ejection_offset = mDataBlock->ejectionOffset;
  1302. if(mDataBlock->fade_offset)
  1303. ejection_offset *= fade_amt;
  1304. pNew->pos = pos_start + r + (disc_v * ejection_offset);
  1305. pNew->pos_local = pNew->pos;
  1306. pNew->vel = (mDataBlock->ejectionInvert) ? (disc_v * -initialVel) : (disc_v * initialVel);
  1307. if (mDataBlock->orientParticles)
  1308. pNew->orientDir = disc_v;
  1309. else
  1310. // note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
  1311. pNew->orientDir.x = mDegToRad(part_db->start_angle + part_db->angle_variance*2.0f*gRandGen.randF() - part_db->angle_variance);
  1312. pNew->acc.set(0, 0, 0);
  1313. pNew->currentAge = age_offset;
  1314. pNew->t_last = 0.0f;
  1315. pNew->radial_v = r;
  1316. part_db->initializeParticle(pNew, vel);
  1317. updateKeyData( pNew );
  1318. }
  1319. void afxParticleEmitterDisc::sub_preCompute(const MatrixF& mat)
  1320. {
  1321. disc_v.set(0.0f, 0.0f, 1.0f);
  1322. disc_r.set(1.0f, 0.0f, 0.0f);
  1323. Point3F axis;
  1324. F32 theta = mAcos( mDot(disc_v, pe_vector_norm) );
  1325. if( M_PI_F-theta < POINT_EPSILON )
  1326. {
  1327. disc_v.neg();
  1328. }
  1329. else if( theta > POINT_EPSILON )
  1330. {
  1331. mCross(pe_vector_norm, disc_v, &axis);
  1332. axis.normalize();
  1333. AngAxisF thetaRot(axis, theta);
  1334. MatrixF temp(true);
  1335. thetaRot.setMatrix(&temp);
  1336. temp.mulP(disc_v);
  1337. temp.mulP(disc_r);
  1338. }
  1339. }
  1340. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//