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