afxParticleEmitter.cpp 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625
  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. mDataBlock = NULL;
  578. pe_vector.set(0,0,1);
  579. pe_vector_norm.set(0,0,1);
  580. tpaths.clear();
  581. tpath_mults.clear();
  582. n_tpath_points = 0;
  583. tpath_points = NULL;
  584. afx_owner = 0;
  585. }
  586. afxParticleEmitter::~afxParticleEmitter()
  587. {
  588. }
  589. bool afxParticleEmitter::onAdd()
  590. {
  591. if( !Parent::onAdd() )
  592. return false;
  593. if (dynamic_cast<afxParticleEmitterData*>(mDataBlock))
  594. init_paths();
  595. return true;
  596. }
  597. void afxParticleEmitter::onRemove()
  598. {
  599. if (dynamic_cast<afxParticleEmitterData*>(mDataBlock))
  600. cleanup_paths();
  601. Parent::onRemove();
  602. }
  603. void afxParticleEmitter::init_paths()
  604. {
  605. if (!mDataBlock)
  606. {
  607. n_tpath_points = 0;
  608. tpath_points = NULL;
  609. return;
  610. }
  611. if (mDataBlock->tPathDataBlocks.size() < 1)
  612. {
  613. n_tpath_points = 0;
  614. tpath_points = NULL;
  615. }
  616. else
  617. {
  618. n_tpath_points = mDataBlock->tPathDataBlocks.size();
  619. tpath_points = new Point3F*[n_tpath_points];
  620. for (U32 i=0; i < n_tpath_points; i++)
  621. {
  622. afxPathData* pd = mDataBlock->tPathDataBlocks[i];
  623. if (!pd)
  624. continue;
  625. if (pd->getSubstitutionCount() > 0 && afx_owner)
  626. {
  627. afxPathData* orig_db = pd;
  628. pd = new afxPathData(*orig_db, true);
  629. orig_db->performSubstitutions(pd, afx_owner);
  630. }
  631. if (pd->num_points > 0)
  632. {
  633. afxPath3D* path = new afxPath3D();
  634. if (pd->times)
  635. path->buildPath( pd->num_points, pd->points, pd->times, pd->delay, 1.0f );
  636. else if (pd->lifetime == 0)
  637. path->buildPath( pd->num_points, pd->points, pd->delay, 1.0f );
  638. else
  639. path->buildPath( pd->num_points, pd->points, pd->delay, pd->delay+pd->lifetime );
  640. path->setLoopType( pd->loop_type );
  641. tpaths.push_back(path);
  642. tpath_mults.push_back( pd->mult );
  643. tpath_points[i] = new Point3F[pd->num_points];
  644. for (U32 j=0; j<pd->num_points; j++)
  645. tpath_points[i][j] = pd->points[j];
  646. }
  647. else
  648. {
  649. Con::warnf("afxParticleEmitter::init_paths() -- pathsTransform datablock (%d) has no points.", i);
  650. }
  651. if (pd->isTempClone())
  652. delete pd;
  653. }
  654. }
  655. }
  656. void afxParticleEmitter::cleanup_paths()
  657. {
  658. if (n_tpath_points < 1)
  659. return;
  660. for (U32 i=0; i < tpaths.size(); i++)
  661. {
  662. if (tpaths[i])
  663. delete tpaths[i];
  664. }
  665. tpaths.clear();
  666. if (tpath_points)
  667. {
  668. if (mDataBlock)
  669. {
  670. for (U32 i=0; i < n_tpath_points; i++)
  671. {
  672. if (tpath_points[i])
  673. delete [] tpath_points[i];
  674. }
  675. }
  676. delete [] tpath_points;
  677. tpath_points = 0;
  678. }
  679. }
  680. void afxParticleEmitter::sub_particleUpdate(Particle* part)
  681. {
  682. if (tpaths.size() < 1)
  683. return;
  684. F32 t = ((F32)part->currentAge)/((F32)part->totalLifetime);
  685. for (U32 i=0; i < tpaths.size(); i++)
  686. {
  687. F32 t_last = part->t_last;
  688. Point3F path_delta = (t_last <= 0.0f) ? tpaths[i]->evaluateAtTime(t) : tpaths[i]->evaluateAtTime(t_last, t);
  689. if (mDataBlock->tPathDataBlocks[i]->concentric)
  690. {
  691. // scale radial vector by x-component of path
  692. part->pos_local += part->radial_v*path_delta.x;
  693. // scale axis vector by z-component of path
  694. part->pos_local += pe_vector_norm*path_delta.z;
  695. // y-component is ignored
  696. }
  697. else
  698. {
  699. part->pos_local += path_delta;
  700. }
  701. }
  702. part->t_last = t;
  703. }
  704. void afxParticleEmitter::preCompute(const MatrixF& mat)
  705. {
  706. // Put vector into the space of the input matrix
  707. pe_vector = mDataBlock->pe_vector;
  708. if (!mDataBlock->pe_vector_is_world)
  709. mat.mulV(pe_vector);
  710. pe_vector_norm = pe_vector;
  711. pe_vector_norm.normalize();
  712. // Transform Paths: rebuild with current matrix
  713. for( U32 i=0; i < tpaths.size(); i++ )
  714. {
  715. for( U32 j=0; j < tpaths[i]->getNumPoints(); j++ )
  716. {
  717. Point3F p = tpath_points[i][j];
  718. mat.mulV(p);
  719. tpaths[i]->setPointPosition(j, p);
  720. }
  721. tpaths[i]->reBuildPath();
  722. }
  723. sub_preCompute(mat);
  724. }
  725. void afxParticleEmitter::afx_emitParticles(const Point3F& point, const bool useLastPosition, const Point3F& velocity, const U32 numMilliseconds)
  726. {
  727. if (mDead) return;
  728. // lifetime over - no more particles
  729. if (mLifetimeMS > 0 && mElapsedTimeMS > mLifetimeMS)
  730. return;
  731. Point3F realStart;
  732. if (useLastPosition && mHasLastPosition)
  733. realStart = mLastPosition;
  734. else
  735. realStart = point;
  736. afx_emitParticles(realStart, point, velocity, numMilliseconds);
  737. }
  738. void afxParticleEmitter::afx_emitParticles(const Point3F& start, const Point3F& end, const Point3F& velocity, const U32 numMilliseconds)
  739. {
  740. if (mDead) return;
  741. // lifetime over - no more particles
  742. if (mLifetimeMS > 0 && mElapsedTimeMS > mLifetimeMS)
  743. return;
  744. U32 currTime = 0;
  745. bool particlesAdded = false;
  746. if (mNextParticleTime != 0)
  747. {
  748. // Need to handle next particle
  749. //
  750. if (mNextParticleTime > numMilliseconds)
  751. {
  752. // Defer to next update
  753. // (Note that this introduces a potential spatial irregularity if the owning
  754. // object is accelerating, and updating at a low frequency)
  755. //
  756. mNextParticleTime -= numMilliseconds;
  757. mInternalClock += numMilliseconds;
  758. mLastPosition = end;
  759. mHasLastPosition = true;
  760. return;
  761. }
  762. else
  763. {
  764. currTime += mNextParticleTime;
  765. mInternalClock += mNextParticleTime;
  766. // Emit particle at curr time
  767. // Create particle at the correct position
  768. Point3F pos;
  769. pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds));
  770. for (S32 p = 0; p < mDataBlock->parts_per_eject; p++)
  771. {
  772. sub_addParticle(pos, velocity, numMilliseconds-currTime, p);
  773. particlesAdded = true;
  774. }
  775. mNextParticleTime = 0;
  776. }
  777. }
  778. while (currTime < numMilliseconds)
  779. {
  780. S32 nextTime = mDataBlock->ejectionPeriodMS;
  781. if (mDataBlock->periodVarianceMS != 0)
  782. {
  783. nextTime += S32(gRandGen.randI() % (2 * mDataBlock->periodVarianceMS + 1)) -
  784. S32(mDataBlock->periodVarianceMS);
  785. }
  786. AssertFatal(nextTime > 0, "Error, next particle ejection time must always be greater than 0");
  787. if (currTime + nextTime > numMilliseconds)
  788. {
  789. mNextParticleTime = (currTime + nextTime) - numMilliseconds;
  790. mInternalClock += numMilliseconds - currTime;
  791. AssertFatal(mNextParticleTime > 0, "Error, should not have deferred this particle!");
  792. break;
  793. }
  794. currTime += nextTime;
  795. mInternalClock += nextTime;
  796. // Create particle at the correct position
  797. Point3F pos;
  798. pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds));
  799. U32 advanceMS = numMilliseconds - currTime;
  800. if (mDataBlock->overrideAdvance == false && advanceMS != 0)
  801. {
  802. for (S32 p = 0; p < mDataBlock->parts_per_eject; p++)
  803. {
  804. sub_addParticle(pos, velocity, numMilliseconds-currTime, p);
  805. particlesAdded = true;
  806. Particle* last_part = part_list_head.next;
  807. if (last_part)
  808. {
  809. if (advanceMS > last_part->totalLifetime)
  810. {
  811. part_list_head.next = last_part->next;
  812. n_parts--;
  813. last_part->next = part_freelist;
  814. part_freelist = last_part;
  815. }
  816. else
  817. {
  818. F32 t = F32(advanceMS) / 1000.0;
  819. Point3F a = last_part->acc;
  820. a -= last_part->vel*last_part->dataBlock->dragCoefficient;
  821. a -= mWindVelocity*last_part->dataBlock->windCoefficient;
  822. //a += Point3F(0, 0, -9.81) * last_part->dataBlock->gravityCoefficient;
  823. a.z += -9.81f*last_part->dataBlock->gravityCoefficient; // as long as gravity is a constant, this is faster
  824. last_part->vel += a * t;
  825. last_part->pos_local += last_part->vel * t;
  826. // allow subclasses to adjust the particle params here
  827. sub_particleUpdate(last_part);
  828. if (last_part->dataBlock->constrain_pos)
  829. last_part->pos = last_part->pos_local + this->pos_pe;
  830. else
  831. last_part->pos = last_part->pos_local;
  832. updateKeyData(last_part);
  833. }
  834. }
  835. }
  836. }
  837. else
  838. {
  839. for (S32 p = 0; p < mDataBlock->parts_per_eject; p++)
  840. {
  841. sub_addParticle(pos, velocity, numMilliseconds-currTime, p);
  842. particlesAdded = true;
  843. }
  844. }
  845. }
  846. if( particlesAdded == true )
  847. updateBBox();
  848. if( n_parts > 0 && mSceneManager == NULL )
  849. {
  850. gClientSceneGraph->addObjectToScene(this);
  851. ClientProcessList::get()->addObject(this);
  852. }
  853. mLastPosition = end;
  854. mHasLastPosition = true;
  855. }
  856. Particle* afxParticleEmitter::alloc_particle()
  857. {
  858. n_parts++;
  859. // this should happen rarely
  860. if (n_parts > n_part_capacity)
  861. {
  862. Particle* store_block = new Particle[16];
  863. part_store.push_back(store_block);
  864. n_part_capacity += 16;
  865. for (S32 i = 0; i < 16; i++)
  866. {
  867. store_block[i].next = part_freelist;
  868. part_freelist = &store_block[i];
  869. }
  870. mDataBlock->allocPrimBuffer(n_part_capacity);
  871. }
  872. Particle* pNew = part_freelist;
  873. part_freelist = pNew->next;
  874. pNew->next = part_list_head.next;
  875. part_list_head.next = pNew;
  876. return pNew;
  877. }
  878. ParticleData* afxParticleEmitter::pick_particle_type()
  879. {
  880. U32 dBlockIndex = (U32)(mCeil(gRandGen.randF() * F32(mDataBlock->particleDataBlocks.size())) - 1);
  881. return mDataBlock->particleDataBlocks[dBlockIndex];
  882. }
  883. bool afxParticleEmitter::onNewDataBlock(GameBaseData* dptr, bool reload)
  884. {
  885. mDataBlock = dynamic_cast<afxParticleEmitterData*>(dptr);
  886. if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
  887. return false;
  888. if (mDataBlock->isTempClone())
  889. return true;
  890. scriptOnNewDataBlock();
  891. return true;
  892. }
  893. void afxParticleEmitter::emitParticlesExt(const MatrixF& xfm, const Point3F& point, const Point3F& velocity, const U32 numMilliseconds)
  894. {
  895. if (mDataBlock->use_emitter_xfm)
  896. {
  897. Point3F zero_point(0.0f, 0.0f, 0.0f);
  898. pos_pe = zero_point;
  899. setTransform(xfm);
  900. preCompute(xfm);
  901. afx_emitParticles(zero_point, true, velocity, numMilliseconds);
  902. }
  903. else
  904. {
  905. pos_pe = point;
  906. preCompute(xfm);
  907. afx_emitParticles(point, true, velocity, numMilliseconds);
  908. }
  909. }
  910. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  911. // VECTOR
  912. afxParticleEmitterVector::afxParticleEmitterVector()
  913. {
  914. mDataBlock = NULL;
  915. }
  916. afxParticleEmitterVector::~afxParticleEmitterVector()
  917. {
  918. }
  919. bool afxParticleEmitterVector::onNewDataBlock(GameBaseData* dptr, bool reload)
  920. {
  921. mDataBlock = dynamic_cast<afxParticleEmitterVectorData*>(dptr);
  922. if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
  923. return false;
  924. if (mDataBlock->isTempClone())
  925. return true;
  926. scriptOnNewDataBlock();
  927. return true;
  928. }
  929. void afxParticleEmitterVector::sub_addParticle(const Point3F& pos, const Point3F& vel, const U32 age_offset, S32 part_idx)
  930. {
  931. Particle* pNew = alloc_particle();
  932. ParticleData* part_db = pick_particle_type();
  933. Point3F pos_start = pos;
  934. if (part_db->constrain_pos)
  935. pos_start.set(0,0,0);
  936. F32 initialVel = mDataBlock->ejectionVelocity;
  937. initialVel += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
  938. if(mDataBlock->fade_velocity)
  939. initialVel *= fade_amt;
  940. F32 ejection_offset = mDataBlock->ejectionOffset;
  941. if(mDataBlock->fade_offset)
  942. ejection_offset *= fade_amt;
  943. pNew->pos = pos_start + (pe_vector_norm * ejection_offset);
  944. pNew->pos_local = pNew->pos;
  945. pNew->vel = pe_vector_norm * initialVel;
  946. if (mDataBlock->orientParticles)
  947. pNew->orientDir = pe_vector_norm;
  948. else
  949. // note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
  950. pNew->orientDir.x = mDegToRad(part_db->start_angle + part_db->angle_variance*2.0f*gRandGen.randF() - part_db->angle_variance);
  951. pNew->acc.set(0, 0, 0);
  952. pNew->currentAge = age_offset;
  953. pNew->t_last = 0.0f;
  954. pNew->radial_v.set(0.0f, 0.0f, 0.0f);
  955. part_db->initializeParticle(pNew, vel);
  956. updateKeyData( pNew );
  957. }
  958. void afxParticleEmitterVector::sub_preCompute(const MatrixF& mat)
  959. {
  960. }
  961. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  962. // CONE
  963. afxParticleEmitterCone::afxParticleEmitterCone()
  964. {
  965. mDataBlock = NULL;
  966. cone_v.set(0,0,1);
  967. cone_s0.set(0,0,1);
  968. cone_s1.set(0,0,1);
  969. }
  970. afxParticleEmitterCone::~afxParticleEmitterCone()
  971. {
  972. }
  973. bool afxParticleEmitterCone::onNewDataBlock(GameBaseData* dptr, bool reload)
  974. {
  975. mDataBlock = dynamic_cast<afxParticleEmitterConeData*>(dptr);
  976. if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
  977. return false;
  978. if (mDataBlock->isTempClone())
  979. return true;
  980. scriptOnNewDataBlock();
  981. return true;
  982. }
  983. void afxParticleEmitterCone::sub_addParticle(const Point3F& pos, const Point3F& vel, const U32 age_offset, S32 part_idx)
  984. {
  985. Particle* pNew = alloc_particle();
  986. ParticleData* part_db = pick_particle_type();
  987. Point3F pos_start = pos;
  988. if (part_db->constrain_pos)
  989. pos_start.set(0,0,0);
  990. F32 initialVel = mDataBlock->ejectionVelocity;
  991. initialVel += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
  992. if(mDataBlock->fade_velocity)
  993. initialVel *= fade_amt;
  994. // Randomly choose a vector between cone_s0 and cone_s1 and normalize:
  995. Point3F vec;
  996. F32 t = mRandF(0.0f, 1.0f);
  997. vec.interpolate(cone_s0, cone_s1, t);
  998. vec.normalize();
  999. // Randomly rotate about cone_v
  1000. F32 theta = mRandF(0.0f, M_2PI_F);
  1001. AngAxisF thetaRot(cone_v, theta);
  1002. MatrixF temp(true);
  1003. thetaRot.setMatrix(&temp);
  1004. temp.mulP(vec);
  1005. F32 ejection_offset = mDataBlock->ejectionOffset;
  1006. if(mDataBlock->fade_offset)
  1007. ejection_offset *= fade_amt;
  1008. pNew->pos = pos_start + (vec * ejection_offset);
  1009. pNew->pos_local = pNew->pos;
  1010. pNew->vel = mDataBlock->ejectionInvert ? vec * -initialVel : vec * initialVel;
  1011. if (mDataBlock->orientParticles)
  1012. pNew->orientDir = vec;
  1013. else
  1014. // note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
  1015. pNew->orientDir.x = mDegToRad(part_db->start_angle + part_db->angle_variance*2.0f*gRandGen.randF() - part_db->angle_variance);
  1016. pNew->acc.set(0, 0, 0);
  1017. pNew->currentAge = age_offset;
  1018. pNew->t_last = 0.0f;
  1019. pNew->radial_v.set(0.0f, 0.0f, 0.0f);
  1020. part_db->initializeParticle(pNew, vel);
  1021. updateKeyData( pNew );
  1022. }
  1023. void afxParticleEmitterCone::sub_preCompute(const MatrixF& mat)
  1024. {
  1025. // Find vectors on the XZ plane corresponding to the inner and outer spread angles:
  1026. // (tan is infinite at PI/4 or 90 degrees)
  1027. cone_v.set( 0.0f, 0.0f, 1.0f );
  1028. cone_s0.x = mTan( mDegToRad( ((afxParticleEmitterConeData*)mDataBlock)->spread_min / 2.0f ));
  1029. cone_s0.y = 0.0f;
  1030. cone_s0.z = 1.0f;
  1031. cone_s1.x = mTan( mDegToRad(((afxParticleEmitterConeData*)mDataBlock)->spread_max / 2.0f ));
  1032. cone_s1.y = 0.0f;
  1033. cone_s1.z = 1.0f;
  1034. Point3F axis;
  1035. F32 theta = mAcos( mDot(cone_v, pe_vector_norm) );
  1036. if( M_PI_F-theta < POINT_EPSILON )
  1037. {
  1038. cone_v.neg();
  1039. cone_s0.neg();
  1040. cone_s1.neg();
  1041. }
  1042. else if( theta > POINT_EPSILON )
  1043. {
  1044. mCross(pe_vector_norm, cone_v, &axis);
  1045. axis.normalize();
  1046. AngAxisF thetaRot(axis, theta);
  1047. MatrixF temp(true);
  1048. thetaRot.setMatrix(&temp);
  1049. temp.mulP(cone_v);
  1050. temp.mulP(cone_s0);
  1051. temp.mulP(cone_s1);
  1052. }
  1053. }
  1054. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  1055. // PATH
  1056. afxParticleEmitterPath::afxParticleEmitterPath()
  1057. {
  1058. mDataBlock = NULL;
  1059. epaths.clear();
  1060. epath_mults.clear();
  1061. n_epath_points = 0;
  1062. epath_points = NULL;
  1063. }
  1064. afxParticleEmitterPath::~afxParticleEmitterPath()
  1065. {
  1066. }
  1067. bool afxParticleEmitterPath::onNewDataBlock(GameBaseData* dptr, bool reload)
  1068. {
  1069. mDataBlock = dynamic_cast<afxParticleEmitterPathData*>(dptr);
  1070. if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
  1071. return false;
  1072. if (mDataBlock->isTempClone())
  1073. return true;
  1074. scriptOnNewDataBlock();
  1075. return true;
  1076. }
  1077. bool afxParticleEmitterPath::onAdd()
  1078. {
  1079. if( !Parent::onAdd() )
  1080. return false;
  1081. if (dynamic_cast<afxParticleEmitterPathData*>(mDataBlock))
  1082. init_paths();
  1083. return true;
  1084. }
  1085. void afxParticleEmitterPath::onRemove()
  1086. {
  1087. if (dynamic_cast<afxParticleEmitterPathData*>(mDataBlock))
  1088. cleanup_paths();
  1089. Parent::onRemove();
  1090. }
  1091. void afxParticleEmitterPath::init_paths()
  1092. {
  1093. if (!mDataBlock || ((afxParticleEmitterPathData*)mDataBlock)->epathDataBlocks.size() < 1)
  1094. {
  1095. n_epath_points = 0;
  1096. epath_points = NULL;
  1097. return;
  1098. }
  1099. n_epath_points = ((afxParticleEmitterPathData*)mDataBlock)->epathDataBlocks.size();
  1100. epath_points = new Point3F*[n_epath_points];
  1101. dMemset(epath_points, 0, n_epath_points*sizeof(Point3F*));
  1102. for (U32 i=0; i < n_epath_points; i++)
  1103. {
  1104. afxPathData* pd = ((afxParticleEmitterPathData*)mDataBlock)->epathDataBlocks[i];
  1105. if (!pd)
  1106. continue;
  1107. if (pd->getSubstitutionCount() > 0 && afx_owner)
  1108. {
  1109. afxPathData* orig_db = pd;
  1110. pd = new afxPathData(*orig_db, true);
  1111. orig_db->performSubstitutions(pd, afx_owner);
  1112. }
  1113. if (pd->num_points > 0)
  1114. {
  1115. afxPath3D* path = new afxPath3D();
  1116. if (pd->times)
  1117. path->buildPath( pd->num_points, pd->points, pd->times, 0.0f, 1.0f );
  1118. else
  1119. path->buildPath( pd->num_points, pd->points, 0.0f, 1.0f );
  1120. epaths.push_back(path);
  1121. epath_mults.push_back( pd->mult );
  1122. epath_points[i] = new Point3F[pd->num_points];
  1123. for (U32 j=0; j<pd->num_points; j++)
  1124. epath_points[i][j] = pd->points[j];
  1125. }
  1126. else
  1127. {
  1128. Con::warnf("afxParticleEmitterPath::init_paths() -- paths datablock (%d) has no points.", i);
  1129. }
  1130. if (pd->isTempClone())
  1131. delete pd;
  1132. }
  1133. }
  1134. void afxParticleEmitterPath::cleanup_paths()
  1135. {
  1136. if (n_epath_points < 1)
  1137. return;
  1138. for (U32 i=0; i < epaths.size(); i++)
  1139. {
  1140. if (epaths[i])
  1141. delete epaths[i];
  1142. }
  1143. epaths.clear();
  1144. if (epath_points)
  1145. {
  1146. if (mDataBlock)
  1147. {
  1148. for (U32 i=0; i < n_epath_points; i++)
  1149. {
  1150. if (epath_points[i])
  1151. delete [] epath_points[i];
  1152. }
  1153. }
  1154. delete [] epath_points;
  1155. epath_points = 0;
  1156. }
  1157. }
  1158. void afxParticleEmitterPath::sub_addParticle(const Point3F& pos, const Point3F& vel, const U32 age_offset, S32 part_idx)
  1159. {
  1160. if (part_idx >= epaths.size() || !epaths[part_idx])
  1161. return;
  1162. Particle* pNew = alloc_particle();
  1163. ParticleData* part_db = pick_particle_type();
  1164. Point3F pos_start = pos;
  1165. if (part_db->constrain_pos)
  1166. pos_start.set(0,0,0);
  1167. F32 initialVel = mDataBlock->ejectionVelocity;
  1168. initialVel += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
  1169. if(mDataBlock->fade_velocity)
  1170. initialVel *= fade_amt;
  1171. // Randomly choose a curve parameter between [0.0,1.0] and evaluate the curve there
  1172. F32 param = mRandF(0.0f, 1.0f);
  1173. Point3F curve_pos = epaths[part_idx]->evaluateAtTime(param);
  1174. Point3F vec;
  1175. switch (((afxParticleEmitterPathData*)mDataBlock)->path_origin_type)
  1176. {
  1177. case afxParticleEmitterPathData::PATHEMIT_ORIGIN :
  1178. vec = curve_pos;
  1179. vec.normalize();
  1180. break;
  1181. case afxParticleEmitterPathData::PATHEMIT_POINT :
  1182. vec = curve_pos-pe_vector;
  1183. vec.normalize();
  1184. break;
  1185. case afxParticleEmitterPathData::PATHEMIT_VECTOR :
  1186. vec = pe_vector_norm;
  1187. break;
  1188. case afxParticleEmitterPathData::PATHEMIT_TANGENT :
  1189. vec = epaths[part_idx]->evaluateTangentAtTime(param);
  1190. vec.normalize();
  1191. break;
  1192. }
  1193. F32 ejection_offset = mDataBlock->ejectionOffset;
  1194. if(mDataBlock->fade_offset)
  1195. ejection_offset *= fade_amt;
  1196. pNew->pos = pos_start + curve_pos + (vec * ejection_offset);
  1197. pNew->pos_local = pNew->pos;
  1198. pNew->vel = mDataBlock->ejectionInvert ? vec * -initialVel : vec * initialVel;
  1199. if (mDataBlock->orientParticles)
  1200. pNew->orientDir = vec;
  1201. else
  1202. // note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
  1203. pNew->orientDir.x = mDegToRad(part_db->start_angle + part_db->angle_variance*2.0f*gRandGen.randF() - part_db->angle_variance);
  1204. pNew->acc.set(0, 0, 0);
  1205. pNew->currentAge = age_offset;
  1206. pNew->t_last = 0.0f;
  1207. pNew->radial_v.set(0.0f, 0.0f, 0.0f);
  1208. part_db->initializeParticle(pNew, vel);
  1209. updateKeyData( pNew );
  1210. }
  1211. void afxParticleEmitterPath::sub_preCompute(const MatrixF& mat)
  1212. {
  1213. for( U32 i=0; i < epaths.size(); i++ )
  1214. {
  1215. for( U32 j=0; j < epaths[i]->getNumPoints(); j++ )
  1216. {
  1217. Point3F p = epath_points[i][j];
  1218. mat.mulV(p);
  1219. p *= epath_mults[i];
  1220. if(mDataBlock->ground_conform) {
  1221. groundConformPoint(p, mat);
  1222. }
  1223. epaths[i]->setPointPosition(j, p);
  1224. }
  1225. epaths[i]->reBuildPath();
  1226. }
  1227. }
  1228. void afxParticleEmitterPath::groundConformPoint(Point3F& point, const MatrixF& mat)
  1229. {
  1230. point += mat.getPosition();
  1231. RayInfo rInfo;
  1232. bool hit = false;
  1233. if (mDataBlock->ground_conform_interiors)
  1234. {
  1235. U32 mask = InteriorLikeObjectType;
  1236. if (mDataBlock->ground_conform_terrain)
  1237. {
  1238. mask |= TerrainObjectType | TerrainLikeObjectType;
  1239. }
  1240. Point3F above_pos(point); above_pos.z += 0.1f;
  1241. Point3F below_pos(point); below_pos.z -= 10000;
  1242. hit = gClientContainer.castRay(above_pos, below_pos, mask, &rInfo);
  1243. if (!hit)
  1244. {
  1245. above_pos.z = point.z + 10000;
  1246. below_pos.z = point.z - 0.1f;
  1247. hit = gClientContainer.castRay(below_pos, above_pos, mask, &rInfo);
  1248. }
  1249. }
  1250. else if (mDataBlock->ground_conform_terrain)
  1251. {
  1252. U32 mask = TerrainObjectType | TerrainLikeObjectType;
  1253. Point3F above_pos(point); above_pos.z += 10000;
  1254. Point3F below_pos(point); below_pos.z -= 10000;
  1255. hit = gClientContainer.castRay(above_pos, below_pos, mask, &rInfo);
  1256. }
  1257. if (hit)
  1258. {
  1259. F32 terrain_z = rInfo.point.z;
  1260. F32 new_z = terrain_z + mDataBlock->ground_conform_height;
  1261. point.z = new_z;
  1262. }
  1263. point -= mat.getPosition();
  1264. }
  1265. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  1266. // DISC
  1267. afxParticleEmitterDisc::afxParticleEmitterDisc()
  1268. {
  1269. mDataBlock = NULL;
  1270. disc_v.set(0,0,1);
  1271. disc_r.set(1,0,0);
  1272. }
  1273. afxParticleEmitterDisc::~afxParticleEmitterDisc()
  1274. {
  1275. }
  1276. bool afxParticleEmitterDisc::onNewDataBlock(GameBaseData* dptr, bool reload)
  1277. {
  1278. mDataBlock = dynamic_cast<afxParticleEmitterDiscData*>(dptr);
  1279. if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
  1280. return false;
  1281. if (mDataBlock->isTempClone())
  1282. return true;
  1283. return true;
  1284. }
  1285. void afxParticleEmitterDisc::sub_addParticle(const Point3F& pos, const Point3F& vel, const U32 age_offset, S32 part_idx)
  1286. {
  1287. Particle* pNew = alloc_particle();
  1288. ParticleData* part_db = pick_particle_type();
  1289. Point3F pos_start = pos;
  1290. if (part_db->constrain_pos)
  1291. pos_start.set(0,0,0);
  1292. F32 initialVel = mDataBlock->ejectionVelocity;
  1293. initialVel += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
  1294. if(mDataBlock->fade_velocity)
  1295. initialVel *= fade_amt;
  1296. // Randomly choose a radius vector
  1297. Point3F r( disc_r );
  1298. F32 radius = mRandF(((afxParticleEmitterDiscData*)mDataBlock)->pe_radius_min, ((afxParticleEmitterDiscData*)mDataBlock)->pe_radius_max);
  1299. r *= radius;
  1300. // Randomly rotate r about disc_v
  1301. F32 theta = mRandF(0.0f, M_2PI_F);
  1302. AngAxisF thetaRot(disc_v, theta);
  1303. MatrixF temp(true);
  1304. thetaRot.setMatrix(&temp);
  1305. temp.mulP(r);
  1306. F32 ejection_offset = mDataBlock->ejectionOffset;
  1307. if(mDataBlock->fade_offset)
  1308. ejection_offset *= fade_amt;
  1309. pNew->pos = pos_start + r + (disc_v * ejection_offset);
  1310. pNew->pos_local = pNew->pos;
  1311. pNew->vel = (mDataBlock->ejectionInvert) ? (disc_v * -initialVel) : (disc_v * initialVel);
  1312. if (mDataBlock->orientParticles)
  1313. pNew->orientDir = disc_v;
  1314. else
  1315. // note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
  1316. pNew->orientDir.x = mDegToRad(part_db->start_angle + part_db->angle_variance*2.0f*gRandGen.randF() - part_db->angle_variance);
  1317. pNew->acc.set(0, 0, 0);
  1318. pNew->currentAge = age_offset;
  1319. pNew->t_last = 0.0f;
  1320. pNew->radial_v = r;
  1321. part_db->initializeParticle(pNew, vel);
  1322. updateKeyData( pNew );
  1323. }
  1324. void afxParticleEmitterDisc::sub_preCompute(const MatrixF& mat)
  1325. {
  1326. disc_v.set(0.0f, 0.0f, 1.0f);
  1327. disc_r.set(1.0f, 0.0f, 0.0f);
  1328. Point3F axis;
  1329. F32 theta = mAcos( mDot(disc_v, pe_vector_norm) );
  1330. if( M_PI_F-theta < POINT_EPSILON )
  1331. {
  1332. disc_v.neg();
  1333. }
  1334. else if( theta > POINT_EPSILON )
  1335. {
  1336. mCross(pe_vector_norm, disc_v, &axis);
  1337. axis.normalize();
  1338. AngAxisF thetaRot(axis, theta);
  1339. MatrixF temp(true);
  1340. thetaRot.setMatrix(&temp);
  1341. temp.mulP(disc_v);
  1342. temp.mulP(disc_r);
  1343. }
  1344. }
  1345. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//