ParticleEmitter.cpp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  1. //
  2. // Copyright (c) 2008-2014 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "Precompiled.h"
  23. #include "Context.h"
  24. #include "Log.h"
  25. #include "Material.h"
  26. #include "ParticleEmitter.h"
  27. #include "Profiler.h"
  28. #include "ResourceCache.h"
  29. #include "ResourceEvents.h"
  30. #include "Scene.h"
  31. #include "SceneEvents.h"
  32. #include "XMLFile.h"
  33. #include "DebugNew.h"
  34. namespace Urho3D
  35. {
  36. extern const char* GEOMETRY_CATEGORY;
  37. static const char* emitterTypeNames[] =
  38. {
  39. "Sphere",
  40. "Box",
  41. 0
  42. };
  43. static const unsigned MAX_PARTICLES_IN_FRAME = 100;
  44. static const unsigned DEFAULT_NUM_PARTICLES = 10;
  45. static const Vector2 DEFAULT_PARTICLE_SIZE(0.1f, 0.1f);
  46. static const float DEFAULT_EMISSION_RATE = 10.0f;
  47. static const float MIN_EMISSION_RATE = 0.01f;
  48. static const float DEFAULT_TIME_TO_LIVE = 1.0f;
  49. static const float DEFAULT_VELOCITY = 1.0f;
  50. static const Vector3 DEFAULT_DIRECTION_MIN(-1.0f, -1.0f, -1.0f);
  51. static const Vector3 DEFAULT_DIRECTION_MAX(1.0f, 1.0f, 1.0f);
  52. template<> EmitterType Variant::Get<EmitterType>() const
  53. {
  54. return (EmitterType)GetInt();
  55. }
  56. ParticleEmitter::ParticleEmitter(Context* context) :
  57. BillboardSet(context),
  58. emitterType_(EMITTER_SPHERE),
  59. emitterSize_(Vector3::ZERO),
  60. directionMin_(DEFAULT_DIRECTION_MIN),
  61. directionMax_(DEFAULT_DIRECTION_MAX),
  62. constantForce_(Vector3::ZERO),
  63. sizeMin_(DEFAULT_PARTICLE_SIZE),
  64. sizeMax_(DEFAULT_PARTICLE_SIZE),
  65. dampingForce_(0.0f),
  66. periodTimer_(0.0f),
  67. emissionTimer_(0.0f),
  68. activeTime_(0.0f),
  69. inactiveTime_(0.0f),
  70. emissionRateMin_(DEFAULT_EMISSION_RATE),
  71. emissionRateMax_(DEFAULT_EMISSION_RATE),
  72. timeToLiveMin_(DEFAULT_TIME_TO_LIVE),
  73. timeToLiveMax_(DEFAULT_TIME_TO_LIVE),
  74. velocityMin_(DEFAULT_VELOCITY),
  75. velocityMax_(DEFAULT_VELOCITY),
  76. rotationMin_(0.0f),
  77. rotationMax_(0.0f),
  78. rotationSpeedMin_(0.0f),
  79. rotationSpeedMax_(0.0f),
  80. sizeAdd_(0.0f),
  81. sizeMul_(1.0f),
  82. emitting_(true),
  83. updateInvisible_(false),
  84. lastTimeStep_(0.0f),
  85. lastUpdateFrameNumber_(M_MAX_UNSIGNED)
  86. {
  87. SetColor(Color::WHITE);
  88. SetNumParticles(DEFAULT_NUM_PARTICLES);
  89. }
  90. ParticleEmitter::~ParticleEmitter()
  91. {
  92. }
  93. void ParticleEmitter::RegisterObject(Context* context)
  94. {
  95. context->RegisterFactory<ParticleEmitter>(GEOMETRY_CATEGORY);
  96. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
  97. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_RESOURCEREF, "Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()), AM_DEFAULT);
  98. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_INT, "Max Particles", GetNumParticles, SetNumParticles, unsigned, DEFAULT_NUM_PARTICLES, AM_DEFAULT);
  99. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Emission Rate Min", emissionRateMin_, DEFAULT_EMISSION_RATE, AM_DEFAULT);
  100. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Emission Rate Max", emissionRateMax_, DEFAULT_EMISSION_RATE, AM_DEFAULT);
  101. ENUM_ATTRIBUTE(ParticleEmitter, "Emitter Type", emitterType_, emitterTypeNames, EMITTER_SPHERE, AM_DEFAULT);
  102. ATTRIBUTE(ParticleEmitter, VAR_VECTOR3, "Emitter Size", emitterSize_, Vector3::ZERO, AM_DEFAULT);
  103. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Active Time", activeTime_, 0.0f, AM_DEFAULT);
  104. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Inactive Time", inactiveTime_, 0.0f, AM_DEFAULT);
  105. ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Is Emitting", emitting_, true, AM_DEFAULT);
  106. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Update When Invisible", GetUpdateInvisible, SetUpdateInvisible, bool, false, AM_DEFAULT);
  107. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Relative Position", IsRelative, SetRelative, bool, true, AM_DEFAULT);
  108. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Relative Scale", IsScaled, SetScaled, bool, true, AM_DEFAULT);
  109. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Sort By Distance", IsSorted, SetSorted, bool, false, AM_DEFAULT);
  110. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Face Camera", GetFaceCamera, SetFaceCamera, bool, true, AM_DEFAULT);
  111. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Time To Live Min", timeToLiveMin_, DEFAULT_TIME_TO_LIVE, AM_DEFAULT);
  112. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Time To Live Max", timeToLiveMax_, DEFAULT_TIME_TO_LIVE, AM_DEFAULT);
  113. ATTRIBUTE(ParticleEmitter, VAR_VECTOR2, "Particle Size Min", sizeMin_, DEFAULT_PARTICLE_SIZE, AM_DEFAULT);
  114. ATTRIBUTE(ParticleEmitter, VAR_VECTOR2, "Particle Size Max", sizeMax_, DEFAULT_PARTICLE_SIZE, AM_DEFAULT);
  115. ATTRIBUTE(ParticleEmitter, VAR_VECTOR3, "Direction Min", directionMin_, DEFAULT_DIRECTION_MIN, AM_DEFAULT);
  116. ATTRIBUTE(ParticleEmitter, VAR_VECTOR3, "Direction Max", directionMax_, DEFAULT_DIRECTION_MAX, AM_DEFAULT);
  117. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Velocity Min", velocityMin_, DEFAULT_VELOCITY, AM_DEFAULT);
  118. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Velocity Max", velocityMax_, DEFAULT_VELOCITY, AM_DEFAULT);
  119. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Rotation Min", rotationMin_, 0.0f, AM_DEFAULT);
  120. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Rotation Max", rotationMax_, 0.0f, AM_DEFAULT);
  121. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Rotation Speed Min", rotationSpeedMin_, 0.0f, AM_DEFAULT);
  122. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Rotation Speed Max", rotationSpeedMax_, 0.0f, AM_DEFAULT);
  123. ATTRIBUTE(ParticleEmitter, VAR_VECTOR3, "Constant Force", constantForce_, Vector3::ZERO, AM_DEFAULT);
  124. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Damping Force", dampingForce_, 0.0f, AM_DEFAULT);
  125. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Size Add", sizeAdd_, 0.0f, AM_DEFAULT);
  126. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Size Mul", sizeMul_, 1.0f, AM_DEFAULT);
  127. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_VARIANTVECTOR, "Particle Colors", GetColorsAttr, SetColorsAttr, VariantVector, Variant::emptyVariantVector, AM_DEFAULT);
  128. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_VARIANTVECTOR, "UV Animation", GetTextureFramesAttr, SetTextureFramesAttr, VariantVector, Variant::emptyVariantVector, AM_DEFAULT);
  129. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
  130. ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Cast Shadows", castShadows_, false, AM_DEFAULT);
  131. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
  132. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
  133. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
  134. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Period Timer", periodTimer_, 0.0f, AM_FILE);
  135. ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Emission Timer", emissionTimer_, 0.0f, AM_FILE);
  136. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_VARIANTVECTOR, "Particles", GetParticlesAttr, SetParticlesAttr, VariantVector, Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
  137. ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_VARIANTVECTOR, "Billboards", GetBillboardsAttr, SetBillboardsAttr, VariantVector, Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
  138. }
  139. void ParticleEmitter::OnSetEnabled()
  140. {
  141. BillboardSet::OnSetEnabled();
  142. Scene* scene = GetScene();
  143. if (scene)
  144. {
  145. if (IsEnabledEffective())
  146. SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(ParticleEmitter, HandleScenePostUpdate));
  147. else
  148. UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
  149. }
  150. }
  151. void ParticleEmitter::Update(const FrameInfo& frame)
  152. {
  153. // Cancel update if has only moved but does not actually need to animate the particles
  154. if (!needUpdate_)
  155. return;
  156. // If there is an amount mismatch between particles and billboards, correct it
  157. if (particles_.Size() != billboards_.Size())
  158. SetNumBillboards(particles_.Size());
  159. bool needCommit = false;
  160. // Check active/inactive period switching
  161. periodTimer_ += lastTimeStep_;
  162. if (emitting_)
  163. {
  164. if (activeTime_ && periodTimer_ >= activeTime_)
  165. {
  166. emitting_ = false;
  167. periodTimer_ -= activeTime_;
  168. }
  169. }
  170. else
  171. {
  172. if (inactiveTime_ && periodTimer_ >= inactiveTime_)
  173. {
  174. emitting_ = true;
  175. periodTimer_ -= inactiveTime_;
  176. }
  177. // If emitter has an indefinite stop interval, keep period timer reset to allow restarting emission in the editor
  178. if (inactiveTime_ == 0.0f)
  179. periodTimer_ = 0.0f;
  180. }
  181. // Check for emitting new particles
  182. if (emitting_)
  183. {
  184. emissionTimer_ += lastTimeStep_;
  185. float intervalMin = 1.0f / Max(emissionRateMax_, MIN_EMISSION_RATE);
  186. float intervalMax = 1.0f / Max(emissionRateMin_, MIN_EMISSION_RATE);
  187. // If emission timer has a longer delay than max. interval, clamp it
  188. if (emissionTimer_ < -intervalMax)
  189. emissionTimer_ = -intervalMax;
  190. unsigned counter = MAX_PARTICLES_IN_FRAME;
  191. while (emissionTimer_ > 0.0f && counter)
  192. {
  193. emissionTimer_ -= Lerp(intervalMin, intervalMax, Random(1.0f));
  194. if (EmitNewParticle())
  195. {
  196. --counter;
  197. needCommit = true;
  198. }
  199. else
  200. break;
  201. }
  202. }
  203. // Update existing particles
  204. Vector3 relativeConstantForce = node_->GetWorldRotation().Inverse() * constantForce_;
  205. // If billboards are not relative, apply scaling to the position update
  206. Vector3 scaleVector = Vector3::ONE;
  207. if (scaled_ && !relative_)
  208. scaleVector = node_->GetWorldScale();
  209. for (unsigned i = 0; i < particles_.Size(); ++i)
  210. {
  211. Particle& particle = particles_[i];
  212. Billboard& billboard = billboards_[i];
  213. if (billboard.enabled_)
  214. {
  215. needCommit = true;
  216. // Time to live
  217. if (particle.timer_ >= particle.timeToLive_)
  218. {
  219. billboard.enabled_ = false;
  220. continue;
  221. }
  222. particle.timer_ += lastTimeStep_;
  223. // Velocity & position
  224. if (constantForce_ != Vector3::ZERO)
  225. {
  226. if (relative_)
  227. particle.velocity_ += lastTimeStep_ * relativeConstantForce;
  228. else
  229. particle.velocity_ += lastTimeStep_ * constantForce_;
  230. }
  231. if (dampingForce_ != 0.0f)
  232. {
  233. Vector3 force = -dampingForce_ * particle.velocity_;
  234. particle.velocity_ += lastTimeStep_ * force;
  235. }
  236. billboard.position_ += lastTimeStep_ * particle.velocity_ * scaleVector;
  237. // Rotation
  238. billboard.rotation_ += lastTimeStep_ * particle.rotationSpeed_;
  239. // Scaling
  240. if (sizeAdd_ != 0.0f || sizeMul_ != 1.0f)
  241. {
  242. particle.scale_ += lastTimeStep_ * sizeAdd_;
  243. if (sizeMul_ != 1.0f)
  244. particle.scale_ *= (lastTimeStep_ * (sizeMul_ - 1.0f)) + 1.0f;
  245. billboard.size_ = particle.size_ * particle.scale_;
  246. }
  247. // Color interpolation
  248. unsigned& index = particle.colorIndex_;
  249. if (index < colorFrames_.Size())
  250. {
  251. if (index < colorFrames_.Size() - 1)
  252. {
  253. if (particle.timer_ >= colorFrames_[index + 1].time_)
  254. ++index;
  255. }
  256. if (index < colorFrames_.Size() - 1)
  257. billboard.color_ = colorFrames_[index].Interpolate(colorFrames_[index + 1], particle.timer_);
  258. else
  259. billboard.color_ = colorFrames_[index].color_;
  260. }
  261. // Texture animation
  262. unsigned& texIndex = particle.texIndex_;
  263. if (textureFrames_.Size() && texIndex < textureFrames_.Size() - 1)
  264. {
  265. if (particle.timer_ >= textureFrames_[texIndex + 1].time_)
  266. {
  267. billboard.uv_ = textureFrames_[texIndex + 1].uv_;
  268. ++texIndex;
  269. }
  270. }
  271. }
  272. }
  273. if (needCommit)
  274. Commit();
  275. needUpdate_ = false;
  276. }
  277. bool ParticleEmitter::Load(XMLFile* file)
  278. {
  279. if (!file)
  280. {
  281. LOGERROR("Null particle emitter parameter file");
  282. return false;
  283. }
  284. XMLElement rootElem = file->GetRoot();
  285. if (!rootElem)
  286. {
  287. LOGERROR("Particle emitter parameter file does not have a valid root element");
  288. return false;
  289. }
  290. if (rootElem.HasChild("material"))
  291. SetMaterial(GetSubsystem<ResourceCache>()->GetResource<Material>(rootElem.GetChild("material").GetAttribute("name")));
  292. if (rootElem.HasChild("numparticles"))
  293. SetNumParticles(rootElem.GetChild("numparticles").GetInt("value"));
  294. if (rootElem.HasChild("updateinvisible"))
  295. updateInvisible_ = rootElem.GetChild("updateinvisible").GetBool("enable");
  296. if (rootElem.HasChild("relative"))
  297. relative_ = rootElem.GetChild("relative").GetBool("enable");
  298. if (rootElem.HasChild("scaled"))
  299. scaled_ = rootElem.GetChild("scaled").GetBool("enable");
  300. if (rootElem.HasChild("sorted"))
  301. sorted_ = rootElem.GetChild("sorted").GetBool("enable");
  302. if (rootElem.HasChild("animlodbias"))
  303. SetAnimationLodBias(rootElem.GetChild("relative").GetFloat("value"));
  304. if (rootElem.HasChild("emittertype"))
  305. {
  306. String type = rootElem.GetChild("emittertype").GetAttributeLower("value");
  307. if (type == "point")
  308. {
  309. // Point emitter type is deprecated, handled as zero sized sphere
  310. emitterType_ = EMITTER_SPHERE;
  311. emitterSize_ = Vector3::ZERO;
  312. }
  313. else if (type == "box")
  314. emitterType_ = EMITTER_BOX;
  315. else if (type == "sphere")
  316. emitterType_ = EMITTER_SPHERE;
  317. else
  318. LOGERROR("Unknown particle emitter type " + type);
  319. }
  320. if (rootElem.HasChild("emittersize"))
  321. emitterSize_ = rootElem.GetChild("emittersize").GetVector3("value");
  322. if (rootElem.HasChild("emitterradius"))
  323. emitterSize_.x_ = emitterSize_.y_ = emitterSize_.z_ = rootElem.GetChild("emitterradius").GetFloat("value");
  324. if (rootElem.HasChild("direction"))
  325. GetVector3MinMax(rootElem.GetChild("direction"), directionMin_, directionMax_);
  326. if (rootElem.HasChild("constantforce"))
  327. constantForce_ = rootElem.GetChild("constantforce").GetVector3("value");
  328. if (rootElem.HasChild("dampingforce"))
  329. dampingForce_ = rootElem.GetChild("dampingforce").GetFloat("value");
  330. if (rootElem.HasChild("activetime"))
  331. activeTime_ = rootElem.GetChild("activetime").GetFloat("value");
  332. if (activeTime_ < 0.0f)
  333. activeTime_ = M_INFINITY;
  334. if (rootElem.HasChild("inactivetime"))
  335. inactiveTime_ = rootElem.GetChild("inactivetime").GetFloat("value");
  336. if (inactiveTime_ < 0.0f)
  337. inactiveTime_ = M_INFINITY;
  338. if (rootElem.HasChild("emissionrate"))
  339. GetFloatMinMax(rootElem.GetChild("emissionrate"), emissionRateMin_, emissionRateMax_);
  340. if (rootElem.HasChild("interval"))
  341. {
  342. float intervalMin = 0.0f;
  343. float intervalMax = 0.0f;
  344. GetFloatMinMax(rootElem.GetChild("interval"), intervalMin, intervalMax);
  345. emissionRateMax_ = 1.0f / intervalMin;
  346. emissionRateMin_ = 1.0f / intervalMax;
  347. }
  348. if (rootElem.HasChild("particlesize"))
  349. GetVector2MinMax(rootElem.GetChild("particlesize"), sizeMin_, sizeMax_);
  350. if (rootElem.HasChild("timetolive"))
  351. GetFloatMinMax(rootElem.GetChild("timetolive"), timeToLiveMin_, timeToLiveMax_);
  352. if (rootElem.HasChild("velocity"))
  353. GetFloatMinMax(rootElem.GetChild("velocity"), velocityMin_, velocityMax_);
  354. if (rootElem.HasChild("rotation"))
  355. GetFloatMinMax(rootElem.GetChild("rotation"), rotationMin_, rotationMax_);
  356. if (rootElem.HasChild("rotationspeed"))
  357. GetFloatMinMax(rootElem.GetChild("rotationspeed"), rotationSpeedMin_, rotationSpeedMax_);
  358. if (rootElem.HasChild("sizedelta"))
  359. {
  360. XMLElement deltaElem = rootElem.GetChild("sizedelta");
  361. if (deltaElem.HasAttribute("add"))
  362. sizeAdd_ = deltaElem.GetFloat("add");
  363. if (deltaElem.HasAttribute("mul"))
  364. sizeMul_ = deltaElem.GetFloat("mul");
  365. }
  366. if (rootElem.HasChild("color"))
  367. SetColor(rootElem.GetChild("color").GetColor("value"));
  368. if (rootElem.HasChild("colorfade"))
  369. {
  370. Vector<ColorFrame> fades;
  371. XMLElement colorFadeElem = rootElem.GetChild("colorfade");
  372. while (colorFadeElem)
  373. {
  374. fades.Push(ColorFrame(colorFadeElem.GetColor("color"), colorFadeElem.GetFloat("time")));
  375. colorFadeElem = colorFadeElem.GetNext("colorfade");
  376. }
  377. SetColors(fades);
  378. }
  379. if (rootElem.HasChild("texanim"))
  380. {
  381. Vector<TextureFrame> animations;
  382. XMLElement animElem = rootElem.GetChild("texanim");
  383. while (animElem)
  384. {
  385. TextureFrame animation;
  386. animation.uv_ = animElem.GetRect("uv");
  387. animation.time_ = animElem.GetFloat("time");
  388. animations.Push(animation);
  389. animElem = animElem.GetNext("texanim");
  390. }
  391. textureFrames_ = animations;
  392. }
  393. MarkNetworkUpdate();
  394. return true;
  395. }
  396. bool ParticleEmitter::Save(XMLFile* file) const
  397. {
  398. if (!file)
  399. {
  400. LOGERROR("Null particle emitter parameter file");
  401. return false;
  402. }
  403. XMLElement rootElem = file->CreateRoot("particleemitter");
  404. XMLElement childElem = rootElem.CreateChild("material");
  405. childElem.SetAttribute("name", GetResourceName(batches_[0].material_));
  406. childElem = rootElem.CreateChild("numparticles");
  407. childElem.SetInt("value", particles_.Size());
  408. childElem = rootElem.CreateChild("updateinvisible");
  409. childElem.SetBool("enable", updateInvisible_);
  410. childElem = rootElem.CreateChild("relative");
  411. childElem.SetBool("enable", relative_);
  412. childElem = rootElem.CreateChild("scaled");
  413. childElem.SetBool("enable", scaled_);
  414. childElem = rootElem.CreateChild("sorted");
  415. childElem.SetBool("enable", sorted_);
  416. childElem = rootElem.CreateChild("animlodbias");
  417. childElem.SetFloat("value", animationLodBias_);
  418. childElem = rootElem.CreateChild("emittertype");
  419. childElem.SetAttribute("value", emitterTypeNames[emitterType_]);
  420. childElem = rootElem.CreateChild("emittersize");
  421. childElem.SetVector3("value", emitterSize_);
  422. childElem = rootElem.CreateChild("direction");
  423. childElem.SetVector3("min", directionMin_);
  424. childElem.SetVector3("max", directionMax_);
  425. childElem = rootElem.CreateChild("constantforce");
  426. childElem.SetVector3("value", constantForce_);
  427. childElem = rootElem.CreateChild("dampingforce");
  428. childElem.SetFloat("value", dampingForce_);
  429. childElem = rootElem.CreateChild("activetime");
  430. childElem.SetFloat("value", activeTime_);
  431. childElem = rootElem.CreateChild("inactivetime");
  432. childElem.SetFloat("value", inactiveTime_);
  433. childElem = rootElem.CreateChild("emissionrate");
  434. childElem.SetFloat("min", emissionRateMin_);
  435. childElem.SetFloat("max", emissionRateMax_);
  436. childElem = rootElem.CreateChild("particlesize");
  437. childElem.SetVector2("min", sizeMin_);
  438. childElem.SetVector2("max", sizeMax_);
  439. childElem = rootElem.CreateChild("timetolive");
  440. childElem.SetFloat("min", timeToLiveMin_);
  441. childElem.SetFloat("max", timeToLiveMax_);
  442. childElem = rootElem.CreateChild("velocity");
  443. childElem.SetFloat("min", velocityMin_);
  444. childElem.SetFloat("max", velocityMax_);
  445. childElem = rootElem.CreateChild("rotation");
  446. childElem.SetFloat("min", rotationMin_);
  447. childElem.SetFloat("max", rotationMax_);
  448. childElem = rootElem.CreateChild("rotationspeed");
  449. childElem.SetFloat("min", rotationSpeedMin_);
  450. childElem.SetFloat("max", rotationSpeedMax_);
  451. childElem = rootElem.CreateChild("sizedelta");
  452. childElem.SetFloat("add", sizeAdd_);
  453. childElem.SetFloat("mul", sizeMul_);
  454. if (colorFrames_.Size() == 1)
  455. {
  456. childElem = rootElem.CreateChild("color");
  457. childElem.SetColor("value", colorFrames_[0].color_);
  458. }
  459. if (colorFrames_.Size() > 1)
  460. {
  461. for (unsigned i = 0; i < colorFrames_.Size(); ++i)
  462. {
  463. childElem = rootElem.CreateChild("colorfade");
  464. childElem.SetColor("color", colorFrames_[i].color_);
  465. childElem.SetFloat("time", colorFrames_[i].time_);
  466. }
  467. }
  468. for (unsigned i = 0; i < textureFrames_.Size(); ++i)
  469. {
  470. childElem = rootElem.CreateChild("texanim");
  471. childElem.SetRect("uv", textureFrames_[i].uv_);
  472. childElem.SetFloat("time", textureFrames_[i].time_);
  473. }
  474. return true;
  475. }
  476. void ParticleEmitter::SetNumParticles(unsigned num)
  477. {
  478. // Prevent negative value being assigned from the editor
  479. if (num > M_MAX_INT)
  480. num = 0;
  481. if (num > MAX_BILLBOARDS)
  482. num = MAX_BILLBOARDS;
  483. particles_.Resize(num);
  484. SetNumBillboards(num);
  485. }
  486. void ParticleEmitter::SetEmissionRate(float rate)
  487. {
  488. SetMinEmissionRate(rate);
  489. SetMaxEmissionRate(rate);
  490. }
  491. void ParticleEmitter::SetMinEmissionRate(float rate)
  492. {
  493. emissionRateMin_ = Max(rate, MIN_EMISSION_RATE);
  494. MarkNetworkUpdate();
  495. }
  496. void ParticleEmitter::SetMaxEmissionRate(float rate)
  497. {
  498. emissionRateMax_ = Max(rate, MIN_EMISSION_RATE);
  499. MarkNetworkUpdate();
  500. }
  501. void ParticleEmitter::SetEmitterType(EmitterType type)
  502. {
  503. emitterType_ = type;
  504. MarkNetworkUpdate();
  505. }
  506. void ParticleEmitter::SetEmitterSize(const Vector3& size)
  507. {
  508. emitterSize_ = size;
  509. MarkNetworkUpdate();
  510. }
  511. void ParticleEmitter::SetActiveTime(float time)
  512. {
  513. activeTime_ = time;
  514. MarkNetworkUpdate();
  515. }
  516. void ParticleEmitter::SetInactiveTime(float time)
  517. {
  518. inactiveTime_ = time;
  519. MarkNetworkUpdate();
  520. }
  521. void ParticleEmitter::SetEmitting(bool enable, bool resetPeriod)
  522. {
  523. if (enable != emitting_ || resetPeriod)
  524. {
  525. emitting_ = enable;
  526. periodTimer_ = 0.0f;
  527. MarkNetworkUpdate();
  528. }
  529. }
  530. void ParticleEmitter::SetUpdateInvisible(bool enable)
  531. {
  532. updateInvisible_ = enable;
  533. MarkNetworkUpdate();
  534. }
  535. void ParticleEmitter::SetTimeToLive(float time)
  536. {
  537. SetMinTimeToLive(time);
  538. SetMaxTimeToLive(time);
  539. }
  540. void ParticleEmitter::SetMinTimeToLive(float time)
  541. {
  542. timeToLiveMin_ = Max(time, 0.0f);
  543. MarkNetworkUpdate();
  544. }
  545. void ParticleEmitter::SetMaxTimeToLive(float time)
  546. {
  547. timeToLiveMax_ = Max(time, 0.0f);
  548. MarkNetworkUpdate();
  549. }
  550. void ParticleEmitter::SetParticleSize(const Vector2& size)
  551. {
  552. SetMinParticleSize(size);
  553. SetMaxParticleSize(size);
  554. }
  555. void ParticleEmitter::SetMinParticleSize(const Vector2& size)
  556. {
  557. sizeMin_ = size;
  558. MarkNetworkUpdate();
  559. }
  560. void ParticleEmitter::SetMaxParticleSize(const Vector2& size)
  561. {
  562. sizeMax_ = size;
  563. MarkNetworkUpdate();
  564. }
  565. void ParticleEmitter::SetMinDirection(const Vector3& direction)
  566. {
  567. directionMin_ = direction;
  568. MarkNetworkUpdate();
  569. }
  570. void ParticleEmitter::SetMaxDirection(const Vector3& direction)
  571. {
  572. directionMin_ = direction;
  573. MarkNetworkUpdate();
  574. }
  575. void ParticleEmitter::SetVelocity(float velocity)
  576. {
  577. SetMinVelocity(velocity);
  578. SetMaxVelocity(velocity);
  579. }
  580. void ParticleEmitter::SetMinVelocity(float velocity)
  581. {
  582. velocityMin_ = velocity;
  583. MarkNetworkUpdate();
  584. }
  585. void ParticleEmitter::SetMaxVelocity(float velocity)
  586. {
  587. velocityMax_ = velocity;
  588. MarkNetworkUpdate();
  589. }
  590. void ParticleEmitter::SetRotation(float rotation)
  591. {
  592. SetMinRotation(rotation);
  593. SetMaxRotation(rotation);
  594. }
  595. void ParticleEmitter::SetMinRotation(float rotation)
  596. {
  597. rotationMin_ = rotation;
  598. MarkNetworkUpdate();
  599. }
  600. void ParticleEmitter::SetMaxRotation(float rotation)
  601. {
  602. rotationMax_ = rotation;
  603. MarkNetworkUpdate();
  604. }
  605. void ParticleEmitter::SetRotationSpeed(float speed)
  606. {
  607. SetMinRotationSpeed(speed);
  608. SetMaxRotationSpeed(speed);
  609. }
  610. void ParticleEmitter::SetMinRotationSpeed(float speed)
  611. {
  612. rotationSpeedMin_ = speed;
  613. MarkNetworkUpdate();
  614. }
  615. void ParticleEmitter::SetMaxRotationSpeed(float speed)
  616. {
  617. rotationSpeedMax_ = speed;
  618. MarkNetworkUpdate();
  619. }
  620. void ParticleEmitter::SetConstantForce(const Vector3& force)
  621. {
  622. constantForce_ = force;
  623. MarkNetworkUpdate();
  624. }
  625. void ParticleEmitter::SetDampingForce(float force)
  626. {
  627. dampingForce_ = force;
  628. MarkNetworkUpdate();
  629. }
  630. void ParticleEmitter::SetSizeAdd(float sizeAdd)
  631. {
  632. sizeAdd_ = sizeAdd;
  633. MarkNetworkUpdate();
  634. }
  635. void ParticleEmitter::SetSizeMul(float sizeMul)
  636. {
  637. sizeMul_ = sizeMul;
  638. MarkNetworkUpdate();
  639. }
  640. void ParticleEmitter::SetColor(const Color& color)
  641. {
  642. colorFrames_.Clear();
  643. colorFrames_.Push(ColorFrame(color));
  644. MarkNetworkUpdate();
  645. }
  646. void ParticleEmitter::SetColors(const Vector<ColorFrame>& colors)
  647. {
  648. if (!colors.Size())
  649. return;
  650. colorFrames_ = colors;
  651. MarkNetworkUpdate();
  652. }
  653. void ParticleEmitter::SetNumColors(unsigned num)
  654. {
  655. colorFrames_.Resize(num);
  656. MarkNetworkUpdate();
  657. }
  658. void ParticleEmitter::SetTextureFrames(const Vector<TextureFrame>& animation)
  659. {
  660. textureFrames_ = animation;
  661. MarkNetworkUpdate();
  662. }
  663. void ParticleEmitter::SetNumTextureFrames(unsigned num)
  664. {
  665. textureFrames_.Resize(num);
  666. }
  667. void ParticleEmitter::SetParticlesAttr(VariantVector value)
  668. {
  669. unsigned index = 0;
  670. SetNumParticles(index < value.Size() ? value[index++].GetUInt() : 0);
  671. for (PODVector<Particle>::Iterator i = particles_.Begin(); i != particles_.End() && index < value.Size(); ++i)
  672. {
  673. i->velocity_ = value[index++].GetVector3();
  674. i->size_ = value[index++].GetVector2();
  675. i->timer_ = value[index++].GetFloat();
  676. i->timeToLive_ = value[index++].GetFloat();
  677. i->scale_ = value[index++].GetFloat();
  678. i->rotationSpeed_ = value[index++].GetFloat();
  679. i->colorIndex_ = value[index++].GetInt();
  680. i->texIndex_ = value[index++].GetInt();
  681. }
  682. }
  683. VariantVector ParticleEmitter::GetParticlesAttr() const
  684. {
  685. VariantVector ret;
  686. ret.Reserve(particles_.Size() * 8 + 1);
  687. ret.Push(particles_.Size());
  688. for (PODVector<Particle>::ConstIterator i = particles_.Begin(); i != particles_.End(); ++i)
  689. {
  690. ret.Push(i->velocity_);
  691. ret.Push(i->size_);
  692. ret.Push(i->timer_);
  693. ret.Push(i->timeToLive_);
  694. ret.Push(i->scale_);
  695. ret.Push(i->rotationSpeed_);
  696. ret.Push(i->colorIndex_);
  697. ret.Push(i->texIndex_);
  698. }
  699. return ret;
  700. }
  701. void ParticleEmitter::SetColorsAttr(VariantVector value)
  702. {
  703. unsigned index = 0;
  704. unsigned numColors = index < value.Size() ? value[index++].GetUInt() : 0;
  705. // Prevent negative value being assigned from the editor
  706. if (numColors > M_MAX_INT)
  707. numColors = 0;
  708. colorFrames_.Resize(numColors);
  709. for (Vector<ColorFrame>::Iterator i = colorFrames_.Begin(); i < colorFrames_.End() && index < value.Size(); ++i)
  710. {
  711. i->color_ = value[index++].GetColor();
  712. i->time_ = value[index++].GetFloat();
  713. }
  714. }
  715. VariantVector ParticleEmitter::GetColorsAttr() const
  716. {
  717. VariantVector ret;
  718. ret.Reserve(colorFrames_.Size() * 2 + 1);
  719. ret.Push(colorFrames_.Size());
  720. for (Vector<ColorFrame>::ConstIterator i = colorFrames_.Begin(); i < colorFrames_.End(); ++i)
  721. {
  722. ret.Push(i->color_);
  723. ret.Push(i->time_);
  724. }
  725. return ret;
  726. }
  727. void ParticleEmitter::SetTextureFramesAttr(VariantVector value)
  728. {
  729. unsigned index = 0;
  730. unsigned numFrames = index < value.Size() ? value[index++].GetUInt() : 0;
  731. // Prevent negative value being assigned from the editor
  732. if (numFrames > M_MAX_INT)
  733. numFrames = 0;
  734. textureFrames_.Resize(numFrames);
  735. for (Vector<TextureFrame>::Iterator i = textureFrames_.Begin(); i < textureFrames_.End() && index < value.Size(); ++i)
  736. {
  737. i->uv_ = value[index++].GetVector4();
  738. i->time_ = value[index++].GetFloat();
  739. }
  740. }
  741. VariantVector ParticleEmitter::GetTextureFramesAttr() const
  742. {
  743. VariantVector ret;
  744. ret.Reserve(textureFrames_.Size() * 2 + 1);
  745. ret.Push(textureFrames_.Size());
  746. for (Vector<TextureFrame>::ConstIterator i = textureFrames_.Begin(); i < textureFrames_.End(); ++i)
  747. {
  748. ret.Push(i->uv_.ToVector4());
  749. ret.Push(i->time_);
  750. }
  751. return ret;
  752. }
  753. void ParticleEmitter::OnNodeSet(Node* node)
  754. {
  755. BillboardSet::OnNodeSet(node);
  756. if (node)
  757. {
  758. Scene* scene = GetScene();
  759. if (scene && IsEnabledEffective())
  760. SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(ParticleEmitter, HandleScenePostUpdate));
  761. }
  762. }
  763. bool ParticleEmitter::EmitNewParticle()
  764. {
  765. unsigned index = GetFreeParticle();
  766. if (index == M_MAX_UNSIGNED)
  767. return false;
  768. assert(index < particles_.Size());
  769. Particle& particle = particles_[index];
  770. Billboard& billboard = billboards_[index];
  771. Vector3 startPos;
  772. Vector3 startDir;
  773. switch (emitterType_)
  774. {
  775. case EMITTER_SPHERE:
  776. {
  777. Vector3 dir(
  778. Random(2.0f) - 1.0f,
  779. Random(2.0f) - 1.0f,
  780. Random(2.0f) - 1.0f
  781. );
  782. dir.Normalize();
  783. startPos = emitterSize_ * dir * 0.5f;
  784. }
  785. break;
  786. case EMITTER_BOX:
  787. startPos = Vector3(
  788. Random(emitterSize_.x_) - emitterSize_.x_ * 0.5f,
  789. Random(emitterSize_.y_) - emitterSize_.y_ * 0.5f,
  790. Random(emitterSize_.z_) - emitterSize_.z_ * 0.5f
  791. );
  792. break;
  793. }
  794. startDir = Vector3(
  795. Lerp(directionMin_.x_, directionMax_.x_, Random(1.0f)),
  796. Lerp(directionMin_.y_, directionMax_.y_, Random(1.0f)),
  797. Lerp(directionMin_.z_, directionMax_.z_, Random(1.0f))
  798. );
  799. startDir.Normalize();
  800. if (!relative_)
  801. {
  802. startPos = node_->GetWorldTransform() * startPos;
  803. startDir = node_->GetWorldRotation() * startDir;
  804. };
  805. particle.velocity_ = Lerp(velocityMin_, velocityMax_, Random(1.0f)) * startDir;
  806. particle.size_ = sizeMin_.Lerp(sizeMax_, Random(1.0f));
  807. particle.timer_ = 0.0f;
  808. particle.timeToLive_ = Lerp(timeToLiveMin_, timeToLiveMax_, Random(1.0f));
  809. particle.scale_ = 1.0f;
  810. particle.rotationSpeed_ = Lerp(rotationSpeedMin_, rotationSpeedMax_, Random(1.0f));
  811. particle.colorIndex_ = 0;
  812. particle.texIndex_ = 0;
  813. billboard.position_ = startPos;
  814. billboard.size_ = particles_[index].size_;
  815. billboard.uv_ = textureFrames_.Size() ? textureFrames_[0].uv_ : Rect::POSITIVE;
  816. billboard.rotation_ = Lerp(rotationMin_, rotationMax_, Random(1.0f));
  817. billboard.color_ = colorFrames_[0].color_;
  818. billboard.enabled_ = true;
  819. return true;
  820. }
  821. unsigned ParticleEmitter::GetFreeParticle() const
  822. {
  823. for (unsigned i = 0; i < billboards_.Size(); ++i)
  824. {
  825. if (!billboards_[i].enabled_)
  826. return i;
  827. }
  828. return M_MAX_UNSIGNED;
  829. }
  830. void ParticleEmitter::GetFloatMinMax(const XMLElement& element, float& minValue, float& maxValue)
  831. {
  832. if (element.IsNull())
  833. return;
  834. if (element.HasAttribute("value"))
  835. minValue = maxValue = element.GetFloat("value");
  836. if (element.HasAttribute("min") && element.HasAttribute("max"))
  837. {
  838. minValue = element.GetFloat("min");
  839. maxValue = element.GetFloat("max");
  840. }
  841. }
  842. void ParticleEmitter::GetVector2MinMax(const XMLElement& element, Vector2& minValue, Vector2& maxValue)
  843. {
  844. if (element.IsNull())
  845. return;
  846. if (element.HasAttribute("value"))
  847. minValue = maxValue = element.GetVector2("value");
  848. if (element.HasAttribute("min") && element.HasAttribute("max"))
  849. {
  850. minValue = element.GetVector2("min");
  851. maxValue = element.GetVector2("max");
  852. }
  853. }
  854. void ParticleEmitter::GetVector3MinMax(const XMLElement& element, Vector3& minValue, Vector3& maxValue)
  855. {
  856. if (element.IsNull())
  857. return;
  858. if (element.HasAttribute("value"))
  859. minValue = maxValue = element.GetVector3("value");
  860. if (element.HasAttribute("min") && element.HasAttribute("max"))
  861. {
  862. minValue = element.GetVector3("min");
  863. maxValue = element.GetVector3("max");
  864. }
  865. }
  866. void ParticleEmitter::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
  867. {
  868. // Store scene's timestep and use it instead of global timestep, as time scale may be other than 1
  869. using namespace ScenePostUpdate;
  870. lastTimeStep_ = eventData[P_TIMESTEP].GetFloat();
  871. // If no invisible update, check that the billboardset is in view (framenumber has changed)
  872. if (updateInvisible_ || viewFrameNumber_ != lastUpdateFrameNumber_)
  873. {
  874. lastUpdateFrameNumber_ = viewFrameNumber_;
  875. needUpdate_ = true;
  876. MarkForUpdate();
  877. }
  878. }
  879. }