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