|
|
@@ -27,6 +27,7 @@
|
|
|
#include "ParticleEmitter.h"
|
|
|
#include "Profiler.h"
|
|
|
#include "ResourceCache.h"
|
|
|
+#include "ResourceEvents.h"
|
|
|
#include "Scene.h"
|
|
|
#include "SceneEvents.h"
|
|
|
#include "XMLFile.h"
|
|
|
@@ -36,7 +37,9 @@
|
|
|
namespace Urho3D
|
|
|
{
|
|
|
|
|
|
-extern const char* GEOMETRY_CATEGORY;
|
|
|
+const char* EFFECT_CATEGORY = "Effect";
|
|
|
+
|
|
|
+static const unsigned MAX_PARTICLES_IN_FRAME = 100;
|
|
|
|
|
|
OBJECTTYPESTATIC(ParticleEmitter);
|
|
|
|
|
|
@@ -81,7 +84,7 @@ ParticleEmitter::~ParticleEmitter()
|
|
|
|
|
|
void ParticleEmitter::RegisterObject(Context* context)
|
|
|
{
|
|
|
- context->RegisterFactory<ParticleEmitter>(GEOMETRY_CATEGORY);
|
|
|
+ context->RegisterFactory<ParticleEmitter>(EFFECT_CATEGORY);
|
|
|
|
|
|
ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
|
|
|
ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_RESOURCEREF, "Parameter Source", GetParameterSourceAttr, SetParameterSourceAttr, ResourceRef, ResourceRef(XMLFile::GetTypeStatic()), AM_DEFAULT);
|
|
|
@@ -136,17 +139,26 @@ void ParticleEmitter::Update(const FrameInfo& frame)
|
|
|
emitting_ = true;
|
|
|
periodTimer_ -= inactiveTime_;
|
|
|
}
|
|
|
+ // If emitter has an indefinite stop interval, keep period timer reset to allow restarting emission in the editor
|
|
|
+ if (inactiveTime_ == 0.0f)
|
|
|
+ periodTimer_ = 0.0f;
|
|
|
}
|
|
|
|
|
|
- // Check for emitting a new particle
|
|
|
+ // Check for emitting new particles
|
|
|
if (emitting_)
|
|
|
{
|
|
|
emissionTimer_ += lastTimeStep_;
|
|
|
- if (emissionTimer_ > 0.0f)
|
|
|
+ unsigned counter = MAX_PARTICLES_IN_FRAME;
|
|
|
+ while (emissionTimer_ > 0.0f && counter)
|
|
|
{
|
|
|
emissionTimer_ -= Lerp(intervalMin_, intervalMax_, Random(1.0f));
|
|
|
if (EmitNewParticle())
|
|
|
+ {
|
|
|
+ --counter;
|
|
|
needCommit = true;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -233,20 +245,107 @@ void ParticleEmitter::Update(const FrameInfo& frame)
|
|
|
Commit();
|
|
|
}
|
|
|
|
|
|
-bool ParticleEmitter::SetParameters(XMLFile* file)
|
|
|
+void ParticleEmitter::SetParameters(XMLFile* file)
|
|
|
{
|
|
|
- ResourceCache* cache = GetSubsystem<ResourceCache>();
|
|
|
- if (!file || !cache)
|
|
|
- return false;
|
|
|
+ if (file == parameterSource_)
|
|
|
+ return;
|
|
|
|
|
|
- XMLElement rootElem = file->GetRoot();
|
|
|
- if (!rootElem)
|
|
|
- return false;
|
|
|
+ if (parameterSource_)
|
|
|
+ UnsubscribeFromEvent(parameterSource_, E_RELOADFINISHED);
|
|
|
+
|
|
|
+ if (file && !file->GetRoot())
|
|
|
+ {
|
|
|
+ LOGERROR("Particle emitter parameter file does not have a valid root element");
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
parameterSource_ = file;
|
|
|
|
|
|
+ if (parameterSource_)
|
|
|
+ SubscribeToEvent(parameterSource_, E_RELOADFINISHED, HANDLER(ParticleEmitter, HandleParametersReloadFinished));
|
|
|
+
|
|
|
+ ApplyParameters();
|
|
|
+ MarkNetworkUpdate();
|
|
|
+}
|
|
|
+
|
|
|
+void ParticleEmitter::SetEmitting(bool enable, bool resetPeriod)
|
|
|
+{
|
|
|
+ if (enable != emitting_ || resetPeriod)
|
|
|
+ {
|
|
|
+ emitting_ = enable;
|
|
|
+ periodTimer_ = 0.0f;
|
|
|
+ MarkNetworkUpdate();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void ParticleEmitter::SetParameterSourceAttr(ResourceRef value)
|
|
|
+{
|
|
|
+ ResourceCache* cache = GetSubsystem<ResourceCache>();
|
|
|
+ SetParameters(cache->GetResource<XMLFile>(value.id_));
|
|
|
+}
|
|
|
+
|
|
|
+void ParticleEmitter::SetParticlesAttr(VariantVector value)
|
|
|
+{
|
|
|
+ unsigned index = 0;
|
|
|
+ SetNumParticles(value[index++].GetInt());
|
|
|
+ for (PODVector<Particle>::Iterator i = particles_.Begin(); i != particles_.End() && index < value.Size(); ++i)
|
|
|
+ {
|
|
|
+ i->velocity_ = value[index++].GetVector3();
|
|
|
+ i->size_ = value[index++].GetVector2();
|
|
|
+ i->timer_ = value[index++].GetFloat();
|
|
|
+ i->timeToLive_ = value[index++].GetFloat();
|
|
|
+ i->scale_ = value[index++].GetFloat();
|
|
|
+ i->rotationSpeed_ = value[index++].GetFloat();
|
|
|
+ i->colorIndex_ = value[index++].GetInt();
|
|
|
+ i->texIndex_ = value[index++].GetInt();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+ResourceRef ParticleEmitter::GetParameterSourceAttr() const
|
|
|
+{
|
|
|
+ return GetResourceRef(parameterSource_, XMLFile::GetTypeStatic());
|
|
|
+}
|
|
|
+
|
|
|
+VariantVector ParticleEmitter::GetParticlesAttr() const
|
|
|
+{
|
|
|
+ VariantVector ret;
|
|
|
+ ret.Reserve(particles_.Size() * 8 + 1);
|
|
|
+ ret.Push(particles_.Size());
|
|
|
+ for (PODVector<Particle>::ConstIterator i = particles_.Begin(); i != particles_.End(); ++i)
|
|
|
+ {
|
|
|
+ ret.Push(i->velocity_);
|
|
|
+ ret.Push(i->size_);
|
|
|
+ ret.Push(i->timer_);
|
|
|
+ ret.Push(i->timeToLive_);
|
|
|
+ ret.Push(i->scale_);
|
|
|
+ ret.Push(i->rotationSpeed_);
|
|
|
+ ret.Push(i->colorIndex_);
|
|
|
+ ret.Push(i->texIndex_);
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+void ParticleEmitter::OnNodeSet(Node* node)
|
|
|
+{
|
|
|
+ BillboardSet::OnNodeSet(node);
|
|
|
+
|
|
|
+ if (node)
|
|
|
+ {
|
|
|
+ Scene* scene = GetScene();
|
|
|
+ if (scene && IsEnabledEffective())
|
|
|
+ SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(ParticleEmitter, HandleScenePostUpdate));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void ParticleEmitter::ApplyParameters()
|
|
|
+{
|
|
|
+ if (!parameterSource_)
|
|
|
+ return;
|
|
|
+
|
|
|
+ XMLElement rootElem = parameterSource_->GetRoot();
|
|
|
+
|
|
|
if (rootElem.HasChild("material"))
|
|
|
- SetMaterial(cache->GetResource<Material>(rootElem.GetChild("material").GetAttribute("name")));
|
|
|
+ SetMaterial(GetSubsystem<ResourceCache>()->GetResource<Material>(rootElem.GetChild("material").GetAttribute("name")));
|
|
|
|
|
|
if (rootElem.HasChild("numparticles"))
|
|
|
SetNumParticles(rootElem.GetChild("numparticles").GetInt("value"));
|
|
|
@@ -307,6 +406,15 @@ bool ParticleEmitter::SetParameters(XMLFile* file)
|
|
|
if (rootElem.HasChild("interval"))
|
|
|
GetFloatMinMax(rootElem.GetChild("interval"), intervalMin_, intervalMax_);
|
|
|
|
|
|
+ if (rootElem.HasChild("emissionrate"))
|
|
|
+ {
|
|
|
+ float emissionRateMin = 0.0f;
|
|
|
+ float emissionRateMax = 0.0f;
|
|
|
+ GetFloatMinMax(rootElem.GetChild("emissionrate"), emissionRateMin, emissionRateMax);
|
|
|
+ intervalMax_ = 1.0f / emissionRateMin;
|
|
|
+ intervalMin_ = 1.0f / emissionRateMax;
|
|
|
+ }
|
|
|
+
|
|
|
if (rootElem.HasChild("particlesize"))
|
|
|
GetVector2MinMax(rootElem.GetChild("particlesize"), sizeMin_, sizeMax_);
|
|
|
|
|
|
@@ -361,78 +469,6 @@ bool ParticleEmitter::SetParameters(XMLFile* file)
|
|
|
}
|
|
|
textureAnimation_ = animations;
|
|
|
}
|
|
|
-
|
|
|
- MarkNetworkUpdate();
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-void ParticleEmitter::SetEmitting(bool enable, bool resetPeriod)
|
|
|
-{
|
|
|
- if (enable != emitting_ || resetPeriod)
|
|
|
- {
|
|
|
- emitting_ = enable;
|
|
|
- periodTimer_ = 0.0f;
|
|
|
- MarkNetworkUpdate();
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void ParticleEmitter::SetParameterSourceAttr(ResourceRef value)
|
|
|
-{
|
|
|
- ResourceCache* cache = GetSubsystem<ResourceCache>();
|
|
|
- SetParameters(cache->GetResource<XMLFile>(value.id_));
|
|
|
-}
|
|
|
-
|
|
|
-void ParticleEmitter::SetParticlesAttr(VariantVector value)
|
|
|
-{
|
|
|
- unsigned index = 0;
|
|
|
- SetNumParticles(value[index++].GetInt());
|
|
|
- for (PODVector<Particle>::Iterator i = particles_.Begin(); i != particles_.End() && index < value.Size(); ++i)
|
|
|
- {
|
|
|
- i->velocity_ = value[index++].GetVector3();
|
|
|
- i->size_ = value[index++].GetVector2();
|
|
|
- i->timer_ = value[index++].GetFloat();
|
|
|
- i->timeToLive_ = value[index++].GetFloat();
|
|
|
- i->scale_ = value[index++].GetFloat();
|
|
|
- i->rotationSpeed_ = value[index++].GetFloat();
|
|
|
- i->colorIndex_ = value[index++].GetInt();
|
|
|
- i->texIndex_ = value[index++].GetInt();
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-ResourceRef ParticleEmitter::GetParameterSourceAttr() const
|
|
|
-{
|
|
|
- return GetResourceRef(parameterSource_, XMLFile::GetTypeStatic());
|
|
|
-}
|
|
|
-
|
|
|
-VariantVector ParticleEmitter::GetParticlesAttr() const
|
|
|
-{
|
|
|
- VariantVector ret;
|
|
|
- ret.Reserve(particles_.Size() * 8 + 1);
|
|
|
- ret.Push(particles_.Size());
|
|
|
- for (PODVector<Particle>::ConstIterator i = particles_.Begin(); i != particles_.End(); ++i)
|
|
|
- {
|
|
|
- ret.Push(i->velocity_);
|
|
|
- ret.Push(i->size_);
|
|
|
- ret.Push(i->timer_);
|
|
|
- ret.Push(i->timeToLive_);
|
|
|
- ret.Push(i->scale_);
|
|
|
- ret.Push(i->rotationSpeed_);
|
|
|
- ret.Push(i->colorIndex_);
|
|
|
- ret.Push(i->texIndex_);
|
|
|
- }
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-void ParticleEmitter::OnNodeSet(Node* node)
|
|
|
-{
|
|
|
- BillboardSet::OnNodeSet(node);
|
|
|
-
|
|
|
- if (node)
|
|
|
- {
|
|
|
- Scene* scene = GetScene();
|
|
|
- if (scene && IsEnabledEffective())
|
|
|
- SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(ParticleEmitter, HandleScenePostUpdate));
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
void ParticleEmitter::SetNumParticles(int num)
|
|
|
@@ -598,4 +634,9 @@ void ParticleEmitter::HandleScenePostUpdate(StringHash eventType, VariantMap& ev
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void ParticleEmitter::HandleParametersReloadFinished(StringHash eventType, VariantMap& eventData)
|
|
|
+{
|
|
|
+ ApplyParameters();
|
|
|
+}
|
|
|
+
|
|
|
}
|