part_emt.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***************************************************************************
  21. * *
  22. * Project Name : G *
  23. * *
  24. * $Archive:: /Commando/Code/ww3d2/part_emt.cpp $*
  25. * *
  26. * $Org Author:: Jani_p $*
  27. * *
  28. * $Author:: Kenny_m $*
  29. * *
  30. * $Modtime:: 08/05/02 10:44a $*
  31. * *
  32. * $Revision:: 14 $*
  33. * *
  34. * 08/05/02 KM Texture class redesign
  35. *-------------------------------------------------------------------------*
  36. * Functions: *
  37. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  38. #include "part_emt.h"
  39. #include "wwdebug.h"
  40. #include "ww3d.h"
  41. #include "assetmgr.h"
  42. #include "part_ldr.h"
  43. #include "w3derr.h"
  44. #include "scene.h"
  45. #include "texture.h"
  46. #include "wwprofile.h"
  47. #include <limits.h>
  48. #include <gcd_lcm.h>
  49. #include "texture.h"
  50. #include "part_ldr.h"
  51. // Global variable which is only used to communicate the worldspace emitter
  52. // velocity from ParticleEmitterClass::Create_New_Particles() to
  53. // ParticleEmitterClass::Initialize_Particle(), for velocity inheritance.
  54. Vector3 InheritedWorldSpaceEmitterVel;
  55. // This debug setting disables particles from being generated
  56. bool ParticleEmitterClass::DebugDisable = false;
  57. // This is used to set the global behavior of emitters...
  58. // Should they be removed from the scene when they complete their
  59. // emissions, or should they stay in the scene. (For editing purposes)
  60. // (gth) 09/17/2000 - particle emitters now have a local RemoveOnComplete flag
  61. // which is initialized to the state of DefaultRemoveOnComplete.
  62. bool ParticleEmitterClass::DefaultRemoveOnComplete = true;
  63. ParticleEmitterClass::ParticleEmitterClass(float emit_rate, unsigned int burst_size,
  64. Vector3Randomizer *pos_rnd, Vector3 base_vel, Vector3Randomizer *vel_rnd, float out_vel,
  65. float vel_inherit_factor,
  66. ParticlePropertyStruct<Vector3> &color,
  67. ParticlePropertyStruct<float> &opacity,
  68. ParticlePropertyStruct<float> &size,
  69. ParticlePropertyStruct<float> &rotation, float orient_rnd,
  70. ParticlePropertyStruct<float> &frames,
  71. ParticlePropertyStruct<float> &blur_times,
  72. Vector3 accel, float max_age, float future_start, TextureClass *tex, ShaderClass shader, int max_particles,
  73. int max_buffer_size, bool pingpong,int render_mode,int frame_mode,
  74. const W3dEmitterLinePropertiesStruct * line_props
  75. ) :
  76. RenderObjClass(),
  77. EmitRate(emit_rate > 0.0f ? (unsigned int)(1000.0f / emit_rate) : 1000U),
  78. BurstSize(burst_size != 0 ? burst_size : 1),
  79. OneTimeBurstSize(1),
  80. OneTimeBurst(false),
  81. PosRand(pos_rnd),
  82. BaseVel(base_vel * 0.001f),
  83. VelRand(vel_rnd),
  84. OutwardVel(out_vel * 0.001f),
  85. VelInheritFactor(vel_inherit_factor),
  86. EmitRemain(0U),
  87. PrevQ(true),
  88. PrevOrig(0.0, 0.0, 0.0),
  89. Active(false),
  90. FirstTime(true),
  91. BufferSceneNeeded(true),
  92. ParticlesLeft(max_particles),
  93. MaxParticles(max_particles),
  94. IsComplete(false),
  95. NameString(::_strdup ("ParticleEmitter")),
  96. UserString(NULL),
  97. RemoveOnComplete(DefaultRemoveOnComplete),
  98. IsInScene(false),
  99. GroupID(0),
  100. Buffer(NULL),
  101. IsInvisible(false)
  102. {
  103. max_age = max_age > 0.0f ? max_age : 1.0f;
  104. VelRand->Scale(0.001f);
  105. // The maximum number of particles is determined by the emission rate, burst size and lifetime.
  106. // However, it is capped both by the particle cap and by the maximum buffer size, if these are
  107. // active.
  108. int max_num = BurstSize * emit_rate * (max_age + 1);
  109. if (max_particles > 0) max_num = MIN(max_num, max_particles);
  110. if (max_buffer_size > 0) max_num = MIN(max_num, max_buffer_size);
  111. max_num = MAX(max_num, 2); // max_num of 1 causes problems
  112. Buffer = W3DNEW ParticleBufferClass(this, max_num, color, opacity, size, rotation, orient_rnd,
  113. frames, blur_times, accel/1000000.0f,max_age, future_start, tex, shader, pingpong, render_mode, frame_mode,
  114. line_props);
  115. SET_REF_OWNER( Buffer );
  116. }
  117. ParticleEmitterClass::ParticleEmitterClass(const ParticleEmitterClass & src) :
  118. RenderObjClass(src),
  119. EmitRate(src.EmitRate),
  120. BurstSize(src.BurstSize),
  121. OneTimeBurstSize(src.OneTimeBurstSize),
  122. OneTimeBurst(src.OneTimeBurst),
  123. PosRand(src.PosRand ? src.PosRand->Clone() : NULL),
  124. BaseVel(src.BaseVel),
  125. VelRand(src.VelRand ? src.VelRand->Clone() : NULL),
  126. OutwardVel(src.OutwardVel),
  127. VelInheritFactor(src.VelInheritFactor),
  128. EmitRemain(src.EmitRemain),
  129. PrevQ(src.PrevQ),
  130. PrevOrig(src.PrevOrig),
  131. Active(true), // default to on
  132. FirstTime(true),
  133. BufferSceneNeeded(true),
  134. ParticlesLeft(src.ParticlesLeft),
  135. MaxParticles(src.MaxParticles),
  136. IsComplete(false),
  137. NameString(::_strdup (src.NameString)),
  138. UserString(::_strdup (src.UserString)),
  139. RemoveOnComplete(src.RemoveOnComplete),
  140. IsInScene(false),
  141. GroupID(0),
  142. Buffer(NULL),
  143. IsInvisible(src.IsInvisible)
  144. {
  145. Buffer = (ParticleBufferClass *) src.Buffer->Clone();
  146. Buffer->Set_Emitter(this);
  147. SET_REF_OWNER( Buffer );
  148. }
  149. ParticleEmitterClass & ParticleEmitterClass::operator = (const ParticleEmitterClass & that)
  150. {
  151. RenderObjClass::operator = (that);
  152. if (this != &that) {
  153. assert(0); // TODO: if you hit this assert, please implement me !!!;-)
  154. }
  155. return * this;
  156. }
  157. ParticleEmitterClass::~ParticleEmitterClass(void)
  158. {
  159. Buffer->Emitter_Is_Dead();
  160. Buffer->Release_Ref();
  161. if (PosRand != NULL) {
  162. delete PosRand;
  163. PosRand = NULL;
  164. }
  165. if (VelRand != NULL) {
  166. delete VelRand;
  167. VelRand = NULL;
  168. }
  169. if (NameString != NULL) {
  170. ::free (NameString);
  171. NameString = NULL;
  172. }
  173. if (UserString != NULL) {
  174. ::free (UserString);
  175. UserString = NULL;
  176. }
  177. return ;
  178. }
  179. ParticleEmitterClass *
  180. ParticleEmitterClass::Create_From_Definition (const ParticleEmitterDefClass &definition)
  181. {
  182. // Assume failure
  183. ParticleEmitterClass *pemitter = NULL;
  184. // Attempt to load the texture for this emitter
  185. const char *ptexture_filename = definition.Get_Texture_Filename ();
  186. TextureClass *ptexture = NULL;
  187. if (ptexture_filename && ptexture_filename[0]) {
  188. ptexture = WW3DAssetManager::Get_Instance()->Get_Texture
  189. (
  190. ptexture_filename,
  191. MIP_LEVELS_ALL,
  192. WW3D_FORMAT_UNKNOWN
  193. );
  194. // false); // no compression for particle textures!
  195. }
  196. ShaderClass shader;
  197. definition.Get_Shader (shader);
  198. if (WW3DAssetManager::Get_Instance()->Get_Activate_Fog_On_Load()) {
  199. shader.Enable_Fog ("ParticleEmitterClass");
  200. }
  201. /*if (ptexture) {
  202. // If texture has an alpha channel do alpha blending instead of additive
  203. // (which is the default for point groups):
  204. srTextureIFace::Dimensions dimensions;
  205. ptexture->getDimensions(dimensions);
  206. if (dimensions.pixelFormat.aBits > 0) {
  207. shader = ShaderClass::_PresetAlphaSpriteShader;
  208. }
  209. }*/
  210. //
  211. // Peek at the definition's keyframes
  212. //
  213. ParticlePropertyStruct<Vector3> color_keys;
  214. ParticlePropertyStruct<float> opacity_keys;
  215. ParticlePropertyStruct<float> size_keys;
  216. ParticlePropertyStruct<float> rotation_keys;
  217. ParticlePropertyStruct<float> frame_keys;
  218. ParticlePropertyStruct<float> blur_time_keys;
  219. definition.Get_Color_Keyframes (color_keys);
  220. definition.Get_Opacity_Keyframes (opacity_keys);
  221. definition.Get_Size_Keyframes (size_keys);
  222. definition.Get_Rotation_Keyframes (rotation_keys);
  223. definition.Get_Frame_Keyframes (frame_keys);
  224. definition.Get_Blur_Time_Keyframes (blur_time_keys);
  225. //
  226. // Create the emitter
  227. //
  228. pemitter = NEW_REF( ParticleEmitterClass, ( definition.Get_Emission_Rate (),
  229. definition.Get_Burst_Size (),
  230. definition.Get_Creation_Volume (),
  231. definition.Get_Velocity (),
  232. definition.Get_Velocity_Random (),
  233. definition.Get_Outward_Vel (),
  234. definition.Get_Vel_Inherit (),
  235. color_keys,
  236. opacity_keys,
  237. size_keys,
  238. rotation_keys,
  239. definition.Get_Initial_Orientation_Random(),
  240. frame_keys,
  241. blur_time_keys,
  242. definition.Get_Acceleration (),
  243. definition.Get_Lifetime (),
  244. definition.Get_Future_Start_Time(),
  245. ptexture,
  246. shader,
  247. definition.Get_Max_Emissions (),
  248. 0,
  249. false,
  250. definition.Get_Render_Mode (),
  251. definition.Get_Frame_Mode (),
  252. definition.Get_Line_Properties ()) );
  253. if (color_keys.KeyTimes != NULL) delete [] color_keys.KeyTimes;
  254. if (color_keys.Values != NULL) delete [] color_keys.Values;
  255. if (opacity_keys.KeyTimes != NULL) delete [] opacity_keys.KeyTimes;
  256. if (opacity_keys.Values != NULL) delete [] opacity_keys.Values;
  257. if (size_keys.KeyTimes != NULL) delete [] size_keys.KeyTimes;
  258. if (size_keys.Values != NULL) delete [] size_keys.Values;
  259. if (rotation_keys.KeyTimes != NULL) delete [] rotation_keys.KeyTimes;
  260. if (rotation_keys.Values != NULL) delete [] rotation_keys.Values;
  261. if (frame_keys.KeyTimes != NULL) delete [] frame_keys.KeyTimes;
  262. if (frame_keys.Values != NULL) delete [] frame_keys.Values;
  263. if (blur_time_keys.KeyTimes != NULL) delete [] blur_time_keys.KeyTimes;
  264. if (blur_time_keys.Values != NULL) delete [] blur_time_keys.Values;
  265. // Pass the name along to the emitter
  266. pemitter->Set_Name (definition.Get_Name ());
  267. // release our reference to particle texture.
  268. if (ptexture) {
  269. REF_PTR_RELEASE(ptexture);
  270. ptexture = 0;
  271. }
  272. // Return a pointer to the new emitter
  273. return pemitter;
  274. }
  275. RenderObjClass * ParticleEmitterClass::Clone(void) const
  276. {
  277. return W3DNEW ParticleEmitterClass(*this);
  278. }
  279. void ParticleEmitterClass::Restart(void)
  280. {
  281. // calling Start will cause all internal counters to reset
  282. Start();
  283. }
  284. void ParticleEmitterClass::Notify_Added(SceneClass * scene)
  285. {
  286. RenderObjClass::Notify_Added(scene);
  287. scene->Register(this,SceneClass::ON_FRAME_UPDATE);
  288. if (FirstTime == false) {
  289. Active = true;
  290. }
  291. IsInScene = true;
  292. }
  293. void ParticleEmitterClass::Notify_Removed(SceneClass * scene)
  294. {
  295. scene->Unregister(this,SceneClass::ON_FRAME_UPDATE);
  296. RenderObjClass::Notify_Removed(scene);
  297. Active = false;
  298. IsInScene = false;
  299. //Buffer->Emitter_Is_Dead();
  300. }
  301. // Scales the size of all particles and effects positions/velocities of
  302. // particles emitted after the Scale() call (but not before)
  303. void ParticleEmitterClass::Scale(float scale)
  304. {
  305. // Scale all velosity and position parameters
  306. if (PosRand) PosRand->Scale(scale);
  307. BaseVel *= scale;
  308. if (VelRand) VelRand->Scale(scale);
  309. OutwardVel *= scale;
  310. // Scale sizes of all particles
  311. Buffer->Scale(scale);
  312. }
  313. // Put particle buffer in scene if this is the first time (clunky code
  314. // - hopefully can be rewritten more cleanly in future)...
  315. void ParticleEmitterClass::On_Frame_Update(void)
  316. {
  317. WWPROFILE("ParticleEmitterClass::On_Frame_Update");
  318. if (Active && !IsComplete) {
  319. if (FirstTime) {
  320. // The particle buffer doesn't have a valid Scene yet - the emitter
  321. // finds out what scene it belongs to (goes up the container tree
  322. // until it finds a non-NULL Scene), and then adds the particle
  323. // buffer to it.
  324. if ( BufferSceneNeeded ) {
  325. if (Is_In_Scene()) {
  326. Buffer->Add(Scene);
  327. BufferSceneNeeded = false;
  328. } else {
  329. return;
  330. }
  331. }
  332. BufferSceneNeeded = false;
  333. // Initialize previous transform:
  334. PrevQ = Build_Quaternion(Get_Transform());
  335. PrevOrig = Get_Transform().Get_Translation();
  336. FirstTime = false;
  337. }
  338. }
  339. if (Is_Complete()) {
  340. if (Is_In_Scene() && Is_Remove_On_Complete_Enabled()) {
  341. Scene->Register(this,SceneClass::RELEASE);
  342. }
  343. }
  344. }
  345. void ParticleEmitterClass::Reset(void)
  346. {
  347. // Note: This flag needs to be set first thing, otherwise
  348. // getting the transform will result in an 'update_x' call
  349. // which in turn results in a 'Set_Animation_Hidden' call, which
  350. // in turn will cause the Update_Visibilty function to call
  351. // Start(). This won't cause a stack overflow like in Start
  352. // but it would do some extra work.
  353. Active = true;
  354. // Initialize previous transform:
  355. PrevQ = Build_Quaternion(Get_Transform());
  356. PrevOrig = Get_Transform().Get_Translation();
  357. // Reset the number of particles to emit
  358. ParticlesLeft = MaxParticles;
  359. EmitRemain = 0;
  360. IsComplete = false;
  361. }
  362. void ParticleEmitterClass::Start(void)
  363. {
  364. // Note: This flag needs to be set first thing, otherwise
  365. // getting the transform will result in an 'update_x' call
  366. // which in turn results in a 'Set_Animation_Hidden' call, which
  367. // in turn will cause the Update_Visibilty function to call
  368. // this method. And then... Stack Overflow! ;)
  369. Active = true;
  370. // Initialize previous transform:
  371. PrevQ = Build_Quaternion(Get_Transform());
  372. PrevOrig = Get_Transform().Get_Translation();
  373. // Reset the number of particles to emit (if necessary)
  374. if (IsComplete == true) {
  375. ParticlesLeft = MaxParticles;
  376. IsComplete = false;
  377. }
  378. // This is to keep track of particles so that
  379. // the line segments can start and stop properly
  380. GroupID++;
  381. Buffer->Set_Current_GroupID(GroupID);
  382. }
  383. void ParticleEmitterClass::Stop(void)
  384. {
  385. Active = false;
  386. }
  387. bool ParticleEmitterClass::Is_Stopped(void)
  388. {
  389. return (Active == false);
  390. }
  391. void ParticleEmitterClass::Set_Position_Randomizer(Vector3Randomizer *rand)
  392. {
  393. if (PosRand) {
  394. delete PosRand;
  395. PosRand =NULL;
  396. }
  397. PosRand = rand;
  398. }
  399. void ParticleEmitterClass::Set_Velocity_Randomizer(Vector3Randomizer *rand)
  400. {
  401. if (VelRand) {
  402. delete VelRand;
  403. VelRand =NULL;
  404. }
  405. VelRand = rand;
  406. if (VelRand) {
  407. VelRand->Scale(0.001f); // Convert from seconds to ms
  408. }
  409. }
  410. Vector3Randomizer *ParticleEmitterClass::Get_Creation_Volume (void) const
  411. {
  412. Vector3Randomizer *randomizer = NULL;
  413. if (PosRand != NULL) {
  414. randomizer = PosRand->Clone ();
  415. //randomizer->Scale (1000.0F);
  416. }
  417. return randomizer;
  418. }
  419. Vector3Randomizer *ParticleEmitterClass::Get_Velocity_Random (void) const
  420. {
  421. Vector3Randomizer *randomizer = NULL;
  422. if (VelRand != NULL) {
  423. randomizer = VelRand->Clone ();
  424. randomizer->Scale (1000.0F);
  425. }
  426. return randomizer;
  427. }
  428. void ParticleEmitterClass::Set_Base_Velocity(const Vector3& base_vel)
  429. {
  430. BaseVel = base_vel * 0.001f; // Convert from seconds to ms
  431. }
  432. void ParticleEmitterClass::Set_Outwards_Velocity(float out_vel)
  433. {
  434. OutwardVel = out_vel * 0.001f; // Convert from seconds to ms
  435. }
  436. void ParticleEmitterClass::Set_Velocity_Inheritance_Factor(float inh_factor)
  437. {
  438. VelInheritFactor = inh_factor;
  439. }
  440. // Emit particles (put in particle buffer). This is called by the particle
  441. // buffer On_Frame_Update() function to avoid order dependence.
  442. void ParticleEmitterClass::Emit(void)
  443. {
  444. WWPROFILE("PartlicleEmitter::Emit");
  445. #ifdef WWDEBUG
  446. if (DebugDisable == true) {
  447. return;
  448. }
  449. #endif
  450. if (Active && !IsComplete) {
  451. Quaternion curr_quat; // Quaternion form of orientation.
  452. Vector3 curr_orig; // Origin.
  453. // Convert current matrix into quaternion + origin form.
  454. curr_quat = Build_Quaternion(Get_Transform());
  455. curr_orig = Get_Transform().Get_Translation();
  456. Create_New_Particles(curr_quat, curr_orig);
  457. PrevQ = curr_quat;
  458. PrevOrig = curr_orig;
  459. } else {
  460. // These need to be updated each frame no matter what
  461. PrevQ = Build_Quaternion(Get_Transform());
  462. PrevOrig = Get_Transform().Get_Translation();
  463. }
  464. }
  465. // Collision sphere is a point - emitter emits also when not visible, so this
  466. // is only important to avoid affecting the collision spheres of composite
  467. // objects into which the emitter is inserted.
  468. void ParticleEmitterClass::Update_Cached_Bounding_Volumes(void) const
  469. {
  470. CachedBoundingSphere.Init(Get_Position(),0.0);
  471. CachedBoundingBox.Center = Get_Position();
  472. CachedBoundingBox.Extent.Set(0,0,0);
  473. Validate_Cached_Bounding_Volumes();
  474. }
  475. // Note that creation location and velocity are in local coordinates, so new
  476. // particles need to be transformed into worldspace. It is important to get
  477. // the correct transform at the exact time of particle creation (for frame-
  478. // rate independence), so the current emitter transform is calculated by
  479. // time-based interpolation between the transforms at the beginning and end
  480. // of the current frame. This interpolation is performed via quaternion-
  481. // slerping the orientation and lerping the origin.
  482. void ParticleEmitterClass::Create_New_Particles(const Quaternion & curr_quat, const Vector3 & curr_orig)
  483. {
  484. Quaternion quat;
  485. Vector3 orig;
  486. // The emit remainder from the previous interval (the time remaining in
  487. // the previous interval when the last particle was emitted) is added to
  488. // the size of the current frame to yield the time currently available
  489. // for emitting particles.
  490. unsigned int frametime = WW3D::Get_Frame_Time();
  491. // Since the particles are written into a wraparound buffer, we can take the time modulo a time
  492. // constant which represents the time it takes to fill up the entire buffer with new particles.
  493. // We will do this so we don't run into performance problems with very large frame times.
  494. if (frametime > 100 * EmitRate) { // If the loop will run over 100 times
  495. unsigned int buf_size = Buffer->Get_Buffer_Size();
  496. unsigned int gcd = Greatest_Common_Divisor(buf_size, BurstSize);
  497. unsigned int bursts = buf_size / gcd;
  498. unsigned int cycle_time = EmitRate * bursts;
  499. if (cycle_time > 1) {
  500. frametime = frametime % cycle_time;
  501. } else {
  502. frametime = 1;
  503. }
  504. }
  505. EmitRemain += frametime;
  506. // The interpolation factor (0: start of interval: 1: end of interval).
  507. // Possibly negative at this point, but after the delta is added to it, it
  508. // will be positive.
  509. float fl_frametime = (float)frametime;
  510. float alpha = 1 - ((float)EmitRemain / fl_frametime);
  511. float d_alpha = (float)EmitRate / fl_frametime;
  512. // Setup the slerp between the two quaternions.
  513. SlerpInfoStruct slerp_info;
  514. Slerp_Setup(PrevQ, curr_quat, &slerp_info);
  515. // Find the velocity of the emitter (for velocity inheritance).
  516. // InheritedWorldSpaceEmitterVel is a global variable which is only used
  517. // to pass this into the following Initialize_Particle() calls without
  518. // having to set it as an argument for each call.
  519. if (VelInheritFactor) {
  520. InheritedWorldSpaceEmitterVel = (curr_orig - PrevOrig) * (VelInheritFactor / fl_frametime);
  521. } else {
  522. InheritedWorldSpaceEmitterVel.Set(0.0, 0.0, 0.0);
  523. }
  524. for (; EmitRemain > EmitRate;) {
  525. // Calculate the new remainder.
  526. EmitRemain -= EmitRate;
  527. // Interpolate the start and end transforms to find the transform at
  528. // the moment of particle creation.
  529. alpha += d_alpha;
  530. quat = Cached_Slerp(PrevQ, curr_quat, alpha, &slerp_info);
  531. Vector3::Lerp(PrevOrig, curr_orig, alpha, &orig);
  532. // Initialize BurstSize new particles with the given age and emitter
  533. // transform (expressed as a quaternion and origin vector), and add it
  534. // to the particle buffer's new particle vector.
  535. unsigned int age = WW3D::Get_Sync_Time() - EmitRemain;
  536. unsigned int burst_size = BurstSize;
  537. if (OneTimeBurst) {
  538. burst_size = OneTimeBurstSize;
  539. OneTimeBurst = false;
  540. }
  541. if ( ParticlesLeft > 0 ) { // if we are counting,
  542. if (burst_size > (unsigned int)ParticlesLeft) {
  543. burst_size = (unsigned int)ParticlesLeft;
  544. ParticlesLeft = 0;
  545. } else {
  546. ParticlesLeft -= burst_size;
  547. }
  548. if ( ParticlesLeft <= 0 ) { // count and if done
  549. IsComplete = true; // stop
  550. }
  551. }
  552. for (unsigned int i = 0; i < burst_size; i++) {
  553. Initialize_Particle(Buffer->Add_Uninitialized_New_Particle(), age, quat, orig);
  554. }
  555. if (IsComplete) break;
  556. }
  557. }
  558. // Initialize one new particle at the given NewParticleStruct address, with
  559. // the given age and emitter transform (expressed as a quaternion and origin
  560. // vector). (must check if address is NULL).
  561. void ParticleEmitterClass::Initialize_Particle(NewParticleStruct * newpart,
  562. unsigned int timestamp, const Quaternion & quat, const Vector3 & orig)
  563. {
  564. // Set time stamp.
  565. newpart->TimeStamp = timestamp;
  566. // Set starting (random) local position.
  567. Vector3 rand_pos;
  568. if (PosRand) {
  569. PosRand->Get_Vector(rand_pos);
  570. } else {
  571. rand_pos.Set(0.0, 0.0, 0.0);
  572. }
  573. // Transform position to worldspace, using the transform at moment of
  574. // particle creation.
  575. newpart->Position = quat.Rotate_Vector(rand_pos) + orig;
  576. // Set (random) local velocity.
  577. Vector3 rand_vel;
  578. if (VelRand) {
  579. VelRand->Get_Vector(rand_vel);
  580. } else {
  581. rand_vel.Set(0.0, 0.0, 0.0);
  582. }
  583. // Add outwards velocity to emitterspace velocity
  584. if (OutwardVel) {
  585. // Find vector pointing outwards (from origin to creation position)
  586. Vector3 outwards;
  587. float pos_l2 = rand_pos.Length2();
  588. if (pos_l2) {
  589. outwards = rand_pos * (OutwardVel * WWMath::Inv_Sqrt(pos_l2));
  590. } else {
  591. outwards.X = OutwardVel;
  592. outwards.Y = 0.0f;
  593. outwards.Z = 0.0f;
  594. }
  595. rand_vel += outwards;
  596. }
  597. // Add base velocity to emitterspace velocity
  598. rand_vel += BaseVel;
  599. // Rotate velocity to worldspace and add emitter's inherited velocity.
  600. newpart->Velocity = InheritedWorldSpaceEmitterVel + quat.Rotate_Vector(rand_vel);
  601. // GroupID
  602. newpart->GroupID = GroupID;
  603. }
  604. ParticleEmitterDefClass *
  605. ParticleEmitterClass::Build_Definition (void) const
  606. {
  607. // Allocate a new emitter definition object
  608. ParticleEmitterDefClass *pdefinition = W3DNEW ParticleEmitterDefClass;
  609. WWASSERT (pdefinition != NULL);
  610. if (pdefinition != NULL) {
  611. // Set the texture's filename
  612. TextureClass *ptexture = Get_Texture ();
  613. if (ptexture != NULL) {
  614. pdefinition->Set_Texture_Filename (ptexture->Get_Texture_Name());
  615. REF_PTR_RELEASE(ptexture);
  616. }
  617. // Now fill the definition with data from this emitter instance
  618. pdefinition->Set_Render_Mode (Get_Render_Mode());
  619. pdefinition->Set_Frame_Mode (Get_Frame_Mode());
  620. pdefinition->Set_Name (Get_Name ());
  621. pdefinition->Set_Lifetime (Get_Lifetime ());
  622. pdefinition->Set_Future_Start_Time (Get_Future_Start_Time());
  623. pdefinition->Set_Emission_Rate (Get_Emission_Rate ());
  624. pdefinition->Set_Max_Emissions (Get_Max_Particles ());
  625. pdefinition->Set_Fade_Time (Get_Fade_Time ());
  626. pdefinition->Set_Gravity (0);
  627. pdefinition->Set_Elasticity (0);
  628. pdefinition->Set_Velocity (Get_Start_Velocity ());
  629. pdefinition->Set_Acceleration (Get_Acceleration ());
  630. pdefinition->Set_Burst_Size (Get_Burst_Size ());
  631. pdefinition->Set_Outward_Vel (Get_Outwards_Vel ());
  632. pdefinition->Set_Vel_Inherit (Get_Velocity_Inherit ());
  633. pdefinition->Set_Shader (Get_Shader ());
  634. //
  635. // Pass the creation volume onto the definition
  636. //
  637. Vector3Randomizer *randomizer = Get_Creation_Volume ();
  638. pdefinition->Set_Creation_Volume (randomizer);
  639. //
  640. // Pass the velocity randomizer onto the definition
  641. //
  642. randomizer = Get_Velocity_Random ();
  643. pdefinition->Set_Velocity_Random (randomizer);
  644. //
  645. // Pass the color keyframes onto the definition
  646. //
  647. ParticlePropertyStruct<Vector3> colors;
  648. Get_Color_Key_Frames (colors);
  649. pdefinition->Set_Color_Keyframes (colors);
  650. if (colors.KeyTimes != NULL) delete [] colors.KeyTimes;
  651. if (colors.Values != NULL) delete [] colors.Values;
  652. //
  653. // Pass the opacity keyframes onto the definition
  654. //
  655. ParticlePropertyStruct<float> opacities;
  656. Get_Opacity_Key_Frames (opacities);
  657. pdefinition->Set_Opacity_Keyframes (opacities);
  658. if (opacities.KeyTimes != NULL) delete [] opacities.KeyTimes;
  659. if (opacities.Values != NULL) delete [] opacities.Values;
  660. //
  661. // Pass the size keyframes onto the definition
  662. //
  663. ParticlePropertyStruct<float> sizes;
  664. Get_Size_Key_Frames (sizes);
  665. pdefinition->Set_Size_Keyframes (sizes);
  666. if (sizes.KeyTimes != NULL) delete [] sizes.KeyTimes;
  667. if (sizes.Values != NULL) delete [] sizes.Values;
  668. //
  669. // Pass the rotation keyframes onto the definition
  670. //
  671. ParticlePropertyStruct<float> rotations;
  672. Get_Rotation_Key_Frames (rotations);
  673. pdefinition->Set_Rotation_Keyframes (rotations, Get_Initial_Orientation_Random());
  674. if (rotations.KeyTimes != NULL) delete [] rotations.KeyTimes;
  675. if (rotations.Values != NULL) delete [] rotations.Values;
  676. //
  677. // Pass the frame keyframes onto the definition
  678. //
  679. ParticlePropertyStruct<float> frames;
  680. Get_Frame_Key_Frames (frames);
  681. pdefinition->Set_Frame_Keyframes (frames);
  682. if (frames.KeyTimes != NULL) delete [] frames.KeyTimes;
  683. if (frames.Values != NULL) delete [] frames.Values;
  684. //
  685. // Pass the blur time keyframes onto the definition
  686. //
  687. ParticlePropertyStruct<float> blur_times;
  688. Get_Blur_Time_Key_Frames (blur_times);
  689. pdefinition->Set_Blur_Time_Keyframes (blur_times);
  690. if (blur_times.KeyTimes != NULL) delete [] blur_times.KeyTimes;
  691. if (blur_times.Values != NULL) delete [] blur_times.Values;
  692. //
  693. // Set up the line parameters
  694. //
  695. pdefinition->Set_Line_Texture_Mapping_Mode(Get_Line_Texture_Mapping_Mode());
  696. pdefinition->Set_Merge_Intersections(Is_Merge_Intersections());
  697. pdefinition->Set_Freeze_Random(Is_Freeze_Random());
  698. pdefinition->Set_Disable_Sorting(Is_Sorting_Disabled());
  699. pdefinition->Set_End_Caps(Are_End_Caps_Enabled());
  700. pdefinition->Set_Subdivision_Level(Get_Subdivision_Level());
  701. pdefinition->Set_Noise_Amplitude(Get_Noise_Amplitude());
  702. pdefinition->Set_Merge_Abort_Factor(Get_Merge_Abort_Factor());
  703. pdefinition->Set_Texture_Tile_Factor(Get_Texture_Tile_Factor());
  704. pdefinition->Set_UV_Offset_Rate(Get_UV_Offset_Rate());
  705. }
  706. // Return a pointer to the new definition
  707. return pdefinition;
  708. }
  709. WW3DErrorType
  710. ParticleEmitterClass::Save (ChunkSaveClass &chunk_save) const
  711. {
  712. // Assume error
  713. WW3DErrorType ret_val = WW3D_ERROR_SAVE_FAILED;
  714. // Build a definition from this emitter instance, and save it
  715. // to the chunk.
  716. ParticleEmitterDefClass *pdefinition = Build_Definition ();
  717. if (pdefinition != NULL) {
  718. ret_val = pdefinition->Save_W3D (chunk_save);
  719. }
  720. // Return the WW3DErrorType return code
  721. return ret_val;
  722. }
  723. void
  724. ParticleEmitterClass::Set_Name (const char *pname)
  725. {
  726. // Free the old name if necessary
  727. if (NameString != NULL) {
  728. ::free (NameString);
  729. NameString = NULL;
  730. }
  731. // Copy the provided name
  732. NameString = ::_strdup (pname);
  733. return ;
  734. }
  735. void
  736. ParticleEmitterClass::Update_On_Visibilty(void)
  737. {
  738. // Simply start or stop the emission based on
  739. // the visibility state of the emitter.
  740. if (Is_Not_Hidden_At_All() && !IsInvisible && Is_Stopped() && IsInScene) {
  741. Start ();
  742. } else if ((!Is_Not_Hidden_At_All() || IsInvisible) && !Is_Stopped()) {
  743. Stop ();
  744. }
  745. return ;
  746. }
  747. void
  748. ParticleEmitterClass::Add_Dependencies_To_List
  749. (
  750. DynamicVectorClass<StringClass> &file_list,
  751. bool textures_only
  752. )
  753. {
  754. //
  755. // Get the texture the emitter is using and add it to our list
  756. //
  757. TextureClass *texture = Get_Texture ();
  758. if (texture != NULL) {
  759. file_list.Add (texture->Get_Full_Path ());
  760. REF_PTR_RELEASE(texture);
  761. }
  762. // Allow the base class to process this call (extremely important)
  763. RenderObjClass::Add_Dependencies_To_List (file_list, textures_only);
  764. return ;
  765. }