ParticleEffect.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  1. // Copyright (c) 2008-2023 the Urho3D project
  2. // License: MIT
  3. #include "../Precompiled.h"
  4. #include "../Core/Context.h"
  5. #include "../Core/StringUtils.h"
  6. #include "../Graphics/Material.h"
  7. #include "../Graphics/ParticleEffect.h"
  8. #include "../Graphics/BillboardSet.h"
  9. #include "../IO/Log.h"
  10. #include "../Resource/ResourceCache.h"
  11. #include "../Resource/XMLFile.h"
  12. #include "../DebugNew.h"
  13. namespace Urho3D
  14. {
  15. extern const char* faceCameraModeNames[];
  16. static const char* emitterTypeNames[] =
  17. {
  18. "Sphere",
  19. "Box",
  20. "SphereVolume",
  21. "Cylinder",
  22. "Ring",
  23. nullptr
  24. };
  25. static const Vector2 DEFAULT_PARTICLE_SIZE(0.1f, 0.1f);
  26. static const float DEFAULT_EMISSION_RATE = 10.0f;
  27. static const float MIN_EMISSION_RATE = 0.01f;
  28. static const float DEFAULT_TIME_TO_LIVE = 1.0f;
  29. static const float DEFAULT_VELOCITY = 1.0f;
  30. static const Vector3 DEFAULT_DIRECTION_MIN(-1.0f, -1.0f, -1.0f);
  31. static const Vector3 DEFAULT_DIRECTION_MAX(1.0f, 1.0f, 1.0f);
  32. ParticleEffect::ParticleEffect(Context* context) :
  33. Resource(context),
  34. numParticles_(DEFAULT_NUM_PARTICLES),
  35. updateInvisible_(false),
  36. relative_(true),
  37. scaled_(true),
  38. sorted_(false),
  39. fixedScreenSize_(false),
  40. animationLodBias_(0.0f),
  41. emitterType_(EMITTER_SPHERE),
  42. emitterSize_(Vector3::ZERO),
  43. directionMin_(DEFAULT_DIRECTION_MIN),
  44. directionMax_(DEFAULT_DIRECTION_MAX),
  45. constantForce_(Vector3::ZERO),
  46. dampingForce_(0.0f),
  47. activeTime_(0.0f),
  48. inactiveTime_(0.0f),
  49. emissionRateMin_(DEFAULT_EMISSION_RATE),
  50. emissionRateMax_(DEFAULT_EMISSION_RATE),
  51. sizeMin_(DEFAULT_PARTICLE_SIZE),
  52. sizeMax_(DEFAULT_PARTICLE_SIZE),
  53. timeToLiveMin_(DEFAULT_TIME_TO_LIVE),
  54. timeToLiveMax_(DEFAULT_TIME_TO_LIVE),
  55. velocityMin_(DEFAULT_VELOCITY),
  56. velocityMax_(DEFAULT_VELOCITY),
  57. rotationMin_(0.0f),
  58. rotationMax_(0.0f),
  59. rotationSpeedMin_(0.0f),
  60. rotationSpeedMax_(0.0f),
  61. sizeAdd_(0.0f),
  62. sizeMul_(1.0f),
  63. faceCameraMode_(FC_ROTATE_XYZ)
  64. {
  65. }
  66. ParticleEffect::~ParticleEffect() = default;
  67. void ParticleEffect::RegisterObject(Context* context)
  68. {
  69. context->RegisterFactory<ParticleEffect>();
  70. }
  71. bool ParticleEffect::BeginLoad(Deserializer& source)
  72. {
  73. loadMaterialName_.Clear();
  74. XMLFile file(context_);
  75. if (!file.Load(source))
  76. {
  77. URHO3D_LOGERROR("Load particle effect file failed");
  78. return false;
  79. }
  80. XMLElement rootElem = file.GetRoot();
  81. bool success = Load(rootElem);
  82. if (success)
  83. SetMemoryUse(source.GetSize());
  84. return success;
  85. }
  86. bool ParticleEffect::EndLoad()
  87. {
  88. // Apply the material now
  89. if (!loadMaterialName_.Empty())
  90. {
  91. SetMaterial(GetSubsystem<ResourceCache>()->GetResource<Material>(loadMaterialName_));
  92. loadMaterialName_.Clear();
  93. }
  94. return true;
  95. }
  96. bool ParticleEffect::Load(const XMLElement& source)
  97. {
  98. // Reset to defaults first so that missing parameters in case of a live reload behave as expected
  99. material_.Reset();
  100. numParticles_ = DEFAULT_NUM_PARTICLES;
  101. updateInvisible_ = false;
  102. relative_ = true;
  103. scaled_ = true;
  104. sorted_ = false;
  105. fixedScreenSize_ = false;
  106. animationLodBias_ = 0.0f;
  107. emitterType_ = EMITTER_SPHERE;
  108. emitterSize_ = Vector3::ZERO;
  109. directionMin_ = DEFAULT_DIRECTION_MIN;
  110. directionMax_ = DEFAULT_DIRECTION_MAX;
  111. constantForce_ = Vector3::ZERO;
  112. dampingForce_ = 0.0f;
  113. activeTime_ = 0.0f;
  114. inactiveTime_ = 0.0;
  115. emissionRateMin_ = DEFAULT_EMISSION_RATE;
  116. emissionRateMax_ = DEFAULT_EMISSION_RATE;
  117. sizeMin_ = DEFAULT_PARTICLE_SIZE;
  118. sizeMax_ = DEFAULT_PARTICLE_SIZE;
  119. timeToLiveMin_ = DEFAULT_TIME_TO_LIVE;
  120. timeToLiveMax_ = DEFAULT_TIME_TO_LIVE;
  121. velocityMin_ = DEFAULT_VELOCITY;
  122. velocityMax_ = DEFAULT_VELOCITY;
  123. rotationMin_ = 0.0f;
  124. rotationMax_ = 0.0f;
  125. rotationSpeedMin_ = 0.0f;
  126. rotationSpeedMax_ = 0.0f;
  127. sizeAdd_ = 0.0f;
  128. sizeMul_ = 1.0f;
  129. colorFrames_.Clear();
  130. textureFrames_.Clear();
  131. faceCameraMode_ = FC_ROTATE_XYZ;
  132. if (source.IsNull())
  133. {
  134. URHO3D_LOGERROR("Can not load particle effect from null XML element");
  135. return false;
  136. }
  137. if (source.HasChild("material"))
  138. {
  139. loadMaterialName_ = source.GetChild("material").GetAttribute("name");
  140. // If async loading, can not GetResource() the material. But can do a background request for it
  141. if (GetAsyncLoadState() == ASYNC_LOADING)
  142. GetSubsystem<ResourceCache>()->BackgroundLoadResource<Material>(loadMaterialName_, true, this);
  143. }
  144. if (source.HasChild("numparticles"))
  145. SetNumParticles((unsigned)source.GetChild("numparticles").GetI32("value"));
  146. if (source.HasChild("updateinvisible"))
  147. updateInvisible_ = source.GetChild("updateinvisible").GetBool("enable");
  148. if (source.HasChild("relative"))
  149. relative_ = source.GetChild("relative").GetBool("enable");
  150. if (source.HasChild("scaled"))
  151. scaled_ = source.GetChild("scaled").GetBool("enable");
  152. if (source.HasChild("sorted"))
  153. sorted_ = source.GetChild("sorted").GetBool("enable");
  154. if (source.HasChild("fixedscreensize"))
  155. fixedScreenSize_ = source.GetChild("fixedscreensize").GetBool("enable");
  156. if (source.HasChild("animlodbias"))
  157. SetAnimationLodBias(source.GetChild("animlodbias").GetFloat("value"));
  158. if (source.HasChild("emittertype"))
  159. {
  160. String type = source.GetChild("emittertype").GetAttributeLower("value");
  161. if (type == "point")
  162. {
  163. // Point emitter type is deprecated, handled as zero sized sphere
  164. emitterType_ = EMITTER_SPHERE;
  165. emitterSize_ = Vector3::ZERO;
  166. }
  167. else
  168. emitterType_ = (EmitterType)GetStringListIndex(type.CString(), emitterTypeNames, EMITTER_SPHERE);
  169. }
  170. if (source.HasChild("emittersize"))
  171. emitterSize_ = source.GetChild("emittersize").GetVector3("value");
  172. if (source.HasChild("emitterradius"))
  173. emitterSize_.x_ = emitterSize_.y_ = emitterSize_.z_ = source.GetChild("emitterradius").GetFloat("value");
  174. if (source.HasChild("direction"))
  175. GetVector3MinMax(source.GetChild("direction"), directionMin_, directionMax_);
  176. if (source.HasChild("constantforce"))
  177. constantForce_ = source.GetChild("constantforce").GetVector3("value");
  178. if (source.HasChild("dampingforce"))
  179. dampingForce_ = source.GetChild("dampingforce").GetFloat("value");
  180. if (source.HasChild("activetime"))
  181. activeTime_ = source.GetChild("activetime").GetFloat("value");
  182. if (activeTime_ < 0.0f)
  183. activeTime_ = M_INFINITY;
  184. if (source.HasChild("inactivetime"))
  185. inactiveTime_ = source.GetChild("inactivetime").GetFloat("value");
  186. if (inactiveTime_ < 0.0f)
  187. inactiveTime_ = M_INFINITY;
  188. if (source.HasChild("emissionrate"))
  189. GetFloatMinMax(source.GetChild("emissionrate"), emissionRateMin_, emissionRateMax_);
  190. if (source.HasChild("interval"))
  191. {
  192. float intervalMin = 0.0f;
  193. float intervalMax = 0.0f;
  194. GetFloatMinMax(source.GetChild("interval"), intervalMin, intervalMax);
  195. emissionRateMax_ = 1.0f / intervalMin;
  196. emissionRateMin_ = 1.0f / intervalMax;
  197. }
  198. if (source.HasChild("particlesize"))
  199. GetVector2MinMax(source.GetChild("particlesize"), sizeMin_, sizeMax_);
  200. if (source.HasChild("timetolive"))
  201. GetFloatMinMax(source.GetChild("timetolive"), timeToLiveMin_, timeToLiveMax_);
  202. if (source.HasChild("velocity"))
  203. GetFloatMinMax(source.GetChild("velocity"), velocityMin_, velocityMax_);
  204. if (source.HasChild("rotation"))
  205. GetFloatMinMax(source.GetChild("rotation"), rotationMin_, rotationMax_);
  206. if (source.HasChild("rotationspeed"))
  207. GetFloatMinMax(source.GetChild("rotationspeed"), rotationSpeedMin_, rotationSpeedMax_);
  208. if (source.HasChild("faceCameraMode"))
  209. {
  210. String type = source.GetChild("faceCameraMode").GetAttributeLower("value");
  211. faceCameraMode_ = (FaceCameraMode)GetStringListIndex(type.CString(), faceCameraModeNames, FC_ROTATE_XYZ);
  212. }
  213. if (source.HasChild("sizedelta"))
  214. {
  215. XMLElement deltaElem = source.GetChild("sizedelta");
  216. if (deltaElem.HasAttribute("add"))
  217. sizeAdd_ = deltaElem.GetFloat("add");
  218. if (deltaElem.HasAttribute("mul"))
  219. sizeMul_ = deltaElem.GetFloat("mul");
  220. }
  221. if (source.HasChild("color"))
  222. {
  223. ColorFrame colorFrame(source.GetChild("color").GetColor("value"));
  224. SetColorFrame(0, colorFrame);
  225. }
  226. if (source.HasChild("colorfade"))
  227. {
  228. Vector<ColorFrame> fades;
  229. for (XMLElement colorFadeElem = source.GetChild("colorfade"); colorFadeElem;
  230. colorFadeElem = colorFadeElem.GetNext("colorfade"))
  231. fades.Push(ColorFrame(colorFadeElem.GetColor("color"), colorFadeElem.GetFloat("time")));
  232. SetColorFrames(fades);
  233. }
  234. if (colorFrames_.Empty())
  235. colorFrames_.Push(ColorFrame(Color::WHITE));
  236. if (source.HasChild("texanim"))
  237. {
  238. Vector<TextureFrame> animations;
  239. for (XMLElement animElem = source.GetChild("texanim"); animElem; animElem = animElem.GetNext("texanim"))
  240. {
  241. TextureFrame animation;
  242. animation.uv_ = animElem.GetRect("uv");
  243. animation.time_ = animElem.GetFloat("time");
  244. animations.Push(animation);
  245. }
  246. SetTextureFrames(animations);
  247. }
  248. return true;
  249. }
  250. bool ParticleEffect::Save(Serializer& dest) const
  251. {
  252. SharedPtr<XMLFile> xml(new XMLFile(context_));
  253. XMLElement materialElem = xml->CreateRoot("particleeffect");
  254. Save(materialElem);
  255. return xml->Save(dest);
  256. }
  257. bool ParticleEffect::Save(XMLElement& dest) const
  258. {
  259. if (dest.IsNull())
  260. {
  261. URHO3D_LOGERROR("Can not save particle effect to null XML element");
  262. return false;
  263. }
  264. XMLElement childElem = dest.CreateChild("material");
  265. childElem.SetAttribute("name", GetResourceName(material_));
  266. childElem = dest.CreateChild("numparticles");
  267. childElem.SetI32("value", numParticles_);
  268. childElem = dest.CreateChild("updateinvisible");
  269. childElem.SetBool("enable", updateInvisible_);
  270. childElem = dest.CreateChild("relative");
  271. childElem.SetBool("enable", relative_);
  272. childElem = dest.CreateChild("scaled");
  273. childElem.SetBool("enable", scaled_);
  274. childElem = dest.CreateChild("sorted");
  275. childElem.SetBool("enable", sorted_);
  276. childElem = dest.CreateChild("fixedscreensize");
  277. childElem.SetBool("enable", fixedScreenSize_);
  278. childElem = dest.CreateChild("animlodbias");
  279. childElem.SetFloat("value", animationLodBias_);
  280. childElem = dest.CreateChild("emittertype");
  281. childElem.SetAttribute("value", emitterTypeNames[emitterType_]);
  282. childElem = dest.CreateChild("emittersize");
  283. childElem.SetVector3("value", emitterSize_);
  284. childElem = dest.CreateChild("direction");
  285. childElem.SetVector3("min", directionMin_);
  286. childElem.SetVector3("max", directionMax_);
  287. childElem = dest.CreateChild("constantforce");
  288. childElem.SetVector3("value", constantForce_);
  289. childElem = dest.CreateChild("dampingforce");
  290. childElem.SetFloat("value", dampingForce_);
  291. childElem = dest.CreateChild("activetime");
  292. childElem.SetFloat("value", activeTime_);
  293. childElem = dest.CreateChild("inactivetime");
  294. childElem.SetFloat("value", inactiveTime_);
  295. childElem = dest.CreateChild("emissionrate");
  296. childElem.SetFloat("min", emissionRateMin_);
  297. childElem.SetFloat("max", emissionRateMax_);
  298. childElem = dest.CreateChild("particlesize");
  299. childElem.SetVector2("min", sizeMin_);
  300. childElem.SetVector2("max", sizeMax_);
  301. childElem = dest.CreateChild("timetolive");
  302. childElem.SetFloat("min", timeToLiveMin_);
  303. childElem.SetFloat("max", timeToLiveMax_);
  304. childElem = dest.CreateChild("velocity");
  305. childElem.SetFloat("min", velocityMin_);
  306. childElem.SetFloat("max", velocityMax_);
  307. childElem = dest.CreateChild("rotation");
  308. childElem.SetFloat("min", rotationMin_);
  309. childElem.SetFloat("max", rotationMax_);
  310. childElem = dest.CreateChild("rotationspeed");
  311. childElem.SetFloat("min", rotationSpeedMin_);
  312. childElem.SetFloat("max", rotationSpeedMax_);
  313. childElem = dest.CreateChild("sizedelta");
  314. childElem.SetFloat("add", sizeAdd_);
  315. childElem.SetFloat("mul", sizeMul_);
  316. childElem = dest.CreateChild("faceCameraMode");
  317. childElem.SetAttribute("value", faceCameraModeNames[faceCameraMode_]);
  318. if (colorFrames_.Size() == 1)
  319. {
  320. childElem = dest.CreateChild("color");
  321. childElem.SetColor("value", colorFrames_[0].color_);
  322. }
  323. if (colorFrames_.Size() > 1)
  324. {
  325. for (const ColorFrame& colorFrame : colorFrames_)
  326. {
  327. childElem = dest.CreateChild("colorfade");
  328. childElem.SetColor("color", colorFrame.color_);
  329. childElem.SetFloat("time", colorFrame.time_);
  330. }
  331. }
  332. for (const TextureFrame& textureFrame : textureFrames_)
  333. {
  334. childElem = dest.CreateChild("texanim");
  335. childElem.SetRect("uv", textureFrame.uv_);
  336. childElem.SetFloat("time", textureFrame.time_);
  337. }
  338. return true;
  339. }
  340. void ParticleEffect::SetMaterial(Material* material)
  341. {
  342. material_ = material;
  343. }
  344. void ParticleEffect::SetNumParticles(unsigned num)
  345. {
  346. numParticles_ = Max(0U, num);
  347. }
  348. void ParticleEffect::SetUpdateInvisible(bool enable)
  349. {
  350. updateInvisible_ = enable;
  351. }
  352. void ParticleEffect::SetRelative(bool enable)
  353. {
  354. relative_ = enable;
  355. }
  356. void ParticleEffect::SetScaled(bool enable)
  357. {
  358. scaled_ = enable;
  359. }
  360. void ParticleEffect::SetSorted(bool enable)
  361. {
  362. sorted_ = enable;
  363. }
  364. void ParticleEffect::SetFixedScreenSize(bool enable)
  365. {
  366. fixedScreenSize_ = enable;
  367. }
  368. void ParticleEffect::SetAnimationLodBias(float lodBias)
  369. {
  370. animationLodBias_ = lodBias;
  371. }
  372. void ParticleEffect::SetEmitterType(EmitterType type)
  373. {
  374. emitterType_ = type;
  375. }
  376. void ParticleEffect::SetEmitterSize(const Vector3& size)
  377. {
  378. emitterSize_ = size;
  379. }
  380. void ParticleEffect::SetMinDirection(const Vector3& direction)
  381. {
  382. directionMin_ = direction;
  383. }
  384. void ParticleEffect::SetMaxDirection(const Vector3& direction)
  385. {
  386. directionMax_ = direction;
  387. }
  388. void ParticleEffect::SetConstantForce(const Vector3& force)
  389. {
  390. constantForce_ = force;
  391. }
  392. void ParticleEffect::SetDampingForce(float force)
  393. {
  394. dampingForce_ = force;
  395. }
  396. void ParticleEffect::SetActiveTime(float time)
  397. {
  398. activeTime_ = time;
  399. }
  400. void ParticleEffect::SetInactiveTime(float time)
  401. {
  402. inactiveTime_ = time;
  403. }
  404. void ParticleEffect::SetMinEmissionRate(float rate)
  405. {
  406. emissionRateMin_ = Max(rate, MIN_EMISSION_RATE);
  407. }
  408. void ParticleEffect::SetMaxEmissionRate(float rate)
  409. {
  410. emissionRateMax_ = Max(rate, MIN_EMISSION_RATE);
  411. }
  412. void ParticleEffect::SetMinParticleSize(const Vector2& size)
  413. {
  414. sizeMin_ = size;
  415. }
  416. void ParticleEffect::SetMaxParticleSize(const Vector2& size)
  417. {
  418. sizeMax_ = size;
  419. }
  420. void ParticleEffect::SetMinTimeToLive(float time)
  421. {
  422. timeToLiveMin_ = Max(time, 0.0f);
  423. }
  424. void ParticleEffect::SetMaxTimeToLive(float time)
  425. {
  426. timeToLiveMax_ = Max(time, 0.0f);
  427. }
  428. void ParticleEffect::SetMinVelocity(float velocity)
  429. {
  430. velocityMin_ = velocity;
  431. }
  432. void ParticleEffect::SetMaxVelocity(float velocity)
  433. {
  434. velocityMax_ = velocity;
  435. }
  436. void ParticleEffect::SetMinRotation(float rotation)
  437. {
  438. rotationMin_ = rotation;
  439. }
  440. void ParticleEffect::SetMaxRotation(float rotation)
  441. {
  442. rotationMax_ = rotation;
  443. }
  444. void ParticleEffect::SetMinRotationSpeed(float speed)
  445. {
  446. rotationSpeedMin_ = speed;
  447. }
  448. void ParticleEffect::SetMaxRotationSpeed(float speed)
  449. {
  450. rotationSpeedMax_ = speed;
  451. }
  452. void ParticleEffect::SetSizeAdd(float sizeAdd)
  453. {
  454. sizeAdd_ = sizeAdd;
  455. }
  456. void ParticleEffect::SetSizeMul(float sizeMul)
  457. {
  458. sizeMul_ = sizeMul;
  459. }
  460. void ParticleEffect::AddColorTime(const Color& color, const float time)
  461. {
  462. unsigned s = colorFrames_.Size();
  463. colorFrames_.Resize(s + 1);
  464. for (unsigned i = 0; i < s; i++)
  465. {
  466. if (colorFrames_[i].time_ > time)
  467. {
  468. for (unsigned j = s; j > i; j--)
  469. {
  470. colorFrames_[j].color_ = colorFrames_[j - 1].color_;
  471. colorFrames_[j].time_ = colorFrames_[j - 1].time_;
  472. }
  473. colorFrames_[i].color_ = color;
  474. colorFrames_[i].time_ = time;
  475. return;
  476. }
  477. }
  478. // highest time, add last:
  479. colorFrames_[s].color_ = color;
  480. colorFrames_[s].time_ = time;
  481. }
  482. void ParticleEffect::AddColorFrame(const ColorFrame& colorFrame)
  483. {
  484. AddColorTime(colorFrame.color_, colorFrame.time_);
  485. }
  486. void ParticleEffect::RemoveColorFrame(unsigned index)
  487. {
  488. unsigned s = colorFrames_.Size();
  489. for (unsigned i = index; i < s - 1; i++)
  490. {
  491. colorFrames_[i].color_ = colorFrames_[i + 1].color_;
  492. colorFrames_[i].time_ = colorFrames_[i + 1].time_;
  493. }
  494. colorFrames_.Resize(s - 1);
  495. }
  496. void ParticleEffect::SetColorFrames(const Vector<ColorFrame>& colorFrames)
  497. {
  498. colorFrames_ = colorFrames;
  499. }
  500. void ParticleEffect::SetColorFrame(unsigned index, const ColorFrame& colorFrame)
  501. {
  502. if (colorFrames_.Size() < index + 1)
  503. colorFrames_.Resize(index + 1);
  504. colorFrames_[index] = colorFrame;
  505. }
  506. void ParticleEffect::SetNumColorFrames(unsigned number)
  507. {
  508. unsigned s = colorFrames_.Size();
  509. if (s != number)
  510. colorFrames_.Resize(number);
  511. }
  512. void ParticleEffect::SetFaceCameraMode(FaceCameraMode mode)
  513. {
  514. faceCameraMode_ = mode;
  515. }
  516. void ParticleEffect::SortColorFrames()
  517. {
  518. Vector<ColorFrame> cf = colorFrames_;
  519. colorFrames_.Clear();
  520. for (const ColorFrame& colorFrame : cf)
  521. AddColorFrame(colorFrame);
  522. }
  523. void ParticleEffect::AddTextureTime(const Rect& uv, const float time)
  524. {
  525. unsigned s = textureFrames_.Size();
  526. textureFrames_.Resize(s + 1);
  527. for (unsigned i = 0; i < s; i++)
  528. {
  529. if (textureFrames_[i].time_ > time)
  530. {
  531. for (unsigned j = s; j > i; j--)
  532. {
  533. textureFrames_[j].uv_ = textureFrames_[j - 1].uv_;
  534. textureFrames_[j].time_ = textureFrames_[j - 1].time_;
  535. }
  536. textureFrames_[i].uv_ = uv;
  537. textureFrames_[i].time_ = time;
  538. return;
  539. }
  540. }
  541. // Highest time, add last
  542. textureFrames_[s].uv_ = uv;
  543. textureFrames_[s].time_ = time;
  544. }
  545. void ParticleEffect::AddTextureFrame(const TextureFrame& textureFrame)
  546. {
  547. AddTextureTime(textureFrame.uv_, textureFrame.time_);
  548. }
  549. void ParticleEffect::RemoveTextureFrame(unsigned index)
  550. {
  551. unsigned s = textureFrames_.Size();
  552. for (unsigned i = index; i < s - 1; i++)
  553. {
  554. textureFrames_[i].uv_ = textureFrames_[i + 1].uv_;
  555. textureFrames_[i].time_ = textureFrames_[i + 1].time_;
  556. }
  557. textureFrames_.Resize(s - 1);
  558. }
  559. void ParticleEffect::SetTextureFrames(const Vector<TextureFrame>& textureFrames)
  560. {
  561. textureFrames_ = textureFrames;
  562. }
  563. void ParticleEffect::SetTextureFrame(unsigned index, const TextureFrame& textureFrame)
  564. {
  565. if (textureFrames_.Size() < index + 1)
  566. textureFrames_.Resize(index + 1);
  567. textureFrames_[index] = textureFrame;
  568. }
  569. void ParticleEffect::SetNumTextureFrames(unsigned number)
  570. {
  571. unsigned s = textureFrames_.Size();
  572. if (s != number)
  573. textureFrames_.Resize(number);
  574. }
  575. void ParticleEffect::SortTextureFrames()
  576. {
  577. Vector<TextureFrame> tf = textureFrames_;
  578. textureFrames_.Clear();
  579. for (const TextureFrame& textureFrame : tf)
  580. AddTextureFrame(textureFrame);
  581. }
  582. SharedPtr<ParticleEffect> ParticleEffect::Clone(const String& cloneName) const
  583. {
  584. SharedPtr<ParticleEffect> ret(new ParticleEffect(context_));
  585. ret->SetName(cloneName);
  586. ret->material_ = material_;
  587. ret->numParticles_ = numParticles_;
  588. ret->updateInvisible_ = updateInvisible_;
  589. ret->relative_ = relative_;
  590. ret->scaled_ = scaled_;
  591. ret->sorted_ = sorted_;
  592. ret->fixedScreenSize_ = fixedScreenSize_;
  593. ret->animationLodBias_ = animationLodBias_;
  594. ret->emitterType_ = emitterType_;
  595. ret->emitterSize_ = emitterSize_;
  596. ret->directionMin_ = directionMin_;
  597. ret->directionMax_ = directionMax_;
  598. ret->constantForce_ = constantForce_;
  599. ret->dampingForce_ = dampingForce_;
  600. ret->activeTime_ = activeTime_;
  601. ret->inactiveTime_ = inactiveTime_;
  602. ret->emissionRateMin_ = emissionRateMin_;
  603. ret->emissionRateMax_ = emissionRateMax_;
  604. ret->sizeMin_ = sizeMin_;
  605. ret->sizeMax_ = sizeMax_;
  606. ret->timeToLiveMin_ = timeToLiveMin_;
  607. ret->timeToLiveMax_ = timeToLiveMax_;
  608. ret->velocityMin_ = velocityMin_;
  609. ret->velocityMax_ = velocityMax_;
  610. ret->rotationMin_ = rotationMin_;
  611. ret->rotationMax_ = rotationMax_;
  612. ret->rotationSpeedMin_ = rotationSpeedMin_;
  613. ret->rotationSpeedMax_ = rotationSpeedMax_;
  614. ret->sizeAdd_ = sizeAdd_;
  615. ret->sizeMul_ = sizeMul_;
  616. ret->colorFrames_ = colorFrames_;
  617. ret->textureFrames_ = textureFrames_;
  618. ret->faceCameraMode_ = faceCameraMode_;
  619. /// \todo Zero if source was created programmatically
  620. ret->SetMemoryUse(GetMemoryUse());
  621. return ret;
  622. }
  623. const ColorFrame* ParticleEffect::GetColorFrame(unsigned index) const
  624. {
  625. return index < colorFrames_.Size() ? &colorFrames_[index] : nullptr;
  626. }
  627. const TextureFrame* ParticleEffect::GetTextureFrame(unsigned index) const
  628. {
  629. return index < textureFrames_.Size() ? &textureFrames_[index] : nullptr;
  630. }
  631. Vector3 ParticleEffect::GetRandomDirection() const
  632. {
  633. return Vector3(Lerp(directionMin_.x_, directionMax_.x_, Random(1.0f)), Lerp(directionMin_.y_, directionMax_.y_, Random(1.0f)),
  634. Lerp(directionMin_.z_, directionMax_.z_, Random(1.0f)));
  635. }
  636. Vector2 ParticleEffect::GetRandomSize() const
  637. {
  638. return sizeMin_.Lerp(sizeMax_, Random(1.0f));
  639. }
  640. float ParticleEffect::GetRandomVelocity() const
  641. {
  642. return Lerp(velocityMin_, velocityMax_, Random(1.0f));
  643. }
  644. float ParticleEffect::GetRandomTimeToLive() const
  645. {
  646. return Lerp(timeToLiveMin_, timeToLiveMax_, Random(1.0f));
  647. }
  648. float ParticleEffect::GetRandomRotationSpeed() const
  649. {
  650. return Lerp(rotationSpeedMin_, rotationSpeedMax_, Random(1.0f));
  651. }
  652. float ParticleEffect::GetRandomRotation() const
  653. {
  654. return Lerp(rotationMin_, rotationMax_, Random(1.0f));
  655. }
  656. void ParticleEffect::GetFloatMinMax(const XMLElement& element, float& minValue, float& maxValue)
  657. {
  658. if (element.IsNull())
  659. return;
  660. if (element.HasAttribute("value"))
  661. minValue = maxValue = element.GetFloat("value");
  662. if (element.HasAttribute("min") && element.HasAttribute("max"))
  663. {
  664. minValue = element.GetFloat("min");
  665. maxValue = element.GetFloat("max");
  666. }
  667. }
  668. void ParticleEffect::GetVector2MinMax(const XMLElement& element, Vector2& minValue, Vector2& maxValue)
  669. {
  670. if (element.IsNull())
  671. return;
  672. if (element.HasAttribute("value"))
  673. minValue = maxValue = element.GetVector2("value");
  674. if (element.HasAttribute("min") && element.HasAttribute("max"))
  675. {
  676. minValue = element.GetVector2("min");
  677. maxValue = element.GetVector2("max");
  678. }
  679. }
  680. void ParticleEffect::GetVector3MinMax(const XMLElement& element, Vector3& minValue, Vector3& maxValue)
  681. {
  682. if (element.IsNull())
  683. return;
  684. if (element.HasAttribute("value"))
  685. minValue = maxValue = element.GetVector3("value");
  686. if (element.HasAttribute("min") && element.HasAttribute("max"))
  687. {
  688. minValue = element.GetVector3("min");
  689. maxValue = element.GetVector3("max");
  690. }
  691. }
  692. }