afxParticleEmitter.cpp 45 KB

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