788 lines
22 KiB
C++
788 lines
22 KiB
C++
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
|
|
|
#include "ParticleEffect.h"
|
|
#include "Particles.h"
|
|
#include "Engine/Core/Types/CommonValue.h"
|
|
#include "Engine/Serialization/JsonTools.h"
|
|
#include "Engine/Serialization/Serialization.h"
|
|
#include "Engine/Level/Scene/SceneRendering.h"
|
|
#include "Engine/Level/Scene/Scene.h"
|
|
#include "Engine/Engine/Time.h"
|
|
#include "Engine/Engine/Engine.h"
|
|
|
|
ParticleEffect::ParticleEffect(const SpawnParams& params)
|
|
: Actor(params)
|
|
, _lastUpdateFrame(0)
|
|
, _lastMinDstSqr(MAX_Real)
|
|
{
|
|
_box = BoundingBox(_transform.Translation);
|
|
BoundingSphere::FromBox(_box, _sphere);
|
|
|
|
ParticleSystem.Changed.Bind<ParticleEffect, &ParticleEffect::OnParticleSystemModified>(this);
|
|
ParticleSystem.Loaded.Bind<ParticleEffect, &ParticleEffect::OnParticleSystemLoaded>(this);
|
|
}
|
|
|
|
void ParticleEffectParameter::Init(ParticleEffect* effect, int32 emitterIndex, int32 paramIndex)
|
|
{
|
|
_effect = effect;
|
|
_emitterIndex = emitterIndex;
|
|
_paramIndex = paramIndex;
|
|
}
|
|
|
|
bool ParticleEffectParameter::IsValid() const
|
|
{
|
|
return _effect->ParticleSystem &&
|
|
_effect->Instance.Emitters.Count() > _emitterIndex &&
|
|
_effect->ParticleSystem->Emitters[_emitterIndex] &&
|
|
_effect->ParticleSystem->Emitters[_emitterIndex]->Graph.Parameters.Count() > _paramIndex;
|
|
}
|
|
|
|
ParticleEmitter* ParticleEffectParameter::GetEmitter() const
|
|
{
|
|
CHECK_RETURN(IsValid(), nullptr);
|
|
return _effect->ParticleSystem->Emitters[_emitterIndex];
|
|
}
|
|
|
|
VariantType ParticleEffectParameter::GetParamType() const
|
|
{
|
|
CHECK_RETURN(IsValid(), VariantType(VariantType::Bool));
|
|
return _effect->ParticleSystem->Emitters[_emitterIndex]->Graph.Parameters[_paramIndex].Type;
|
|
}
|
|
|
|
Guid ParticleEffectParameter::GetParamIdentifier() const
|
|
{
|
|
CHECK_RETURN(IsValid(), Guid::Empty);
|
|
return _effect->ParticleSystem->Emitters[_emitterIndex]->Graph.Parameters[_paramIndex].Identifier;
|
|
}
|
|
|
|
const String& ParticleEffectParameter::GetTrackName() const
|
|
{
|
|
CHECK_RETURN(IsValid(), String::Empty);
|
|
auto system = _effect->ParticleSystem.Get();
|
|
for (int32 i = 0; i < system->Tracks.Count(); i++)
|
|
{
|
|
auto& track = system->Tracks[i];
|
|
if (track.Type == ParticleSystem::Track::Types::Emitter && track.AsEmitter.Index == _emitterIndex)
|
|
{
|
|
return track.Name;
|
|
}
|
|
}
|
|
return String::Empty;
|
|
}
|
|
|
|
const String& ParticleEffectParameter::GetName() const
|
|
{
|
|
CHECK_RETURN(IsValid(), String::Empty);
|
|
return _effect->ParticleSystem->Emitters[_emitterIndex]->Graph.Parameters[_paramIndex].Name;
|
|
}
|
|
|
|
bool ParticleEffectParameter::GetIsPublic() const
|
|
{
|
|
CHECK_RETURN(IsValid(), false);
|
|
return _effect->ParticleSystem->Emitters[_emitterIndex]->Graph.Parameters[_paramIndex].IsPublic;
|
|
}
|
|
|
|
Variant ParticleEffectParameter::GetDefaultValue() const
|
|
{
|
|
CHECK_RETURN(IsValid(), Variant::False);
|
|
const ParticleSystemParameter& param = _effect->ParticleSystem->Emitters[_emitterIndex]->Graph.Parameters[_paramIndex];
|
|
Variant paramValue = param.Value;
|
|
_effect->ParticleSystem->EmittersParametersOverrides.TryGet(ToPair(_emitterIndex, param.Identifier), paramValue);
|
|
return paramValue;
|
|
}
|
|
|
|
Variant ParticleEffectParameter::GetDefaultEmitterValue() const
|
|
{
|
|
CHECK_RETURN(IsValid(), Variant::False);
|
|
const ParticleSystemParameter& param = _effect->ParticleSystem->Emitters[_emitterIndex]->Graph.Parameters[_paramIndex];
|
|
return param.Value;
|
|
}
|
|
|
|
Variant ParticleEffectParameter::GetValue() const
|
|
{
|
|
CHECK_RETURN(IsValid(), Variant::False);
|
|
const Variant& paramValue = _effect->Instance.Emitters[_emitterIndex].Parameters[_paramIndex];
|
|
return paramValue;
|
|
}
|
|
|
|
void ParticleEffectParameter::SetValue(const Variant& value) const
|
|
{
|
|
CHECK(IsValid());
|
|
_effect->Instance.Emitters[_emitterIndex].Parameters[_paramIndex] = value;
|
|
}
|
|
|
|
GraphParameter* ParticleEffectParameter::GetEmitterParameter() const
|
|
{
|
|
CHECK_RETURN(IsValid(), nullptr);
|
|
ParticleSystemParameter& param = _effect->ParticleSystem->Emitters[_emitterIndex]->Graph.Parameters[_paramIndex];
|
|
return ¶m;
|
|
}
|
|
|
|
const Array<ParticleEffectParameter>& ParticleEffect::GetParameters()
|
|
{
|
|
Sync();
|
|
|
|
if (_parametersVersion != Instance.ParametersVersion)
|
|
{
|
|
_parametersVersion = Instance.ParametersVersion;
|
|
|
|
int32 count = 0;
|
|
for (auto& e : Instance.Emitters)
|
|
count += e.Parameters.Count();
|
|
_parameters.Clear();
|
|
_parameters.Resize(count, false);
|
|
|
|
int32 index = 0;
|
|
for (int32 emitterIndex = 0; emitterIndex < Instance.Emitters.Count(); emitterIndex++)
|
|
{
|
|
auto& emitter = Instance.Emitters[emitterIndex];
|
|
for (int32 paramIndex = 0; paramIndex < emitter.Parameters.Count(); paramIndex++)
|
|
{
|
|
_parameters[index++].Init(this, emitterIndex, paramIndex);
|
|
}
|
|
}
|
|
|
|
ApplyModifiedParameters();
|
|
}
|
|
|
|
return _parameters;
|
|
}
|
|
|
|
uint32 ParticleEffect::GetParametersVersion() const
|
|
{
|
|
return Instance.ParametersVersion;
|
|
}
|
|
|
|
ParticleEffectParameter* ParticleEffect::GetParameter(const StringView& emitterTrackName, const StringView& paramName)
|
|
{
|
|
auto& parameters = GetParameters();
|
|
if (parameters.IsEmpty())
|
|
return nullptr;
|
|
|
|
auto system = ParticleSystem.Get();
|
|
for (int32 i = 0; i < system->Tracks.Count(); i++)
|
|
{
|
|
auto& track = system->Tracks[i];
|
|
if (track.Type == ParticleSystem::Track::Types::Emitter && track.Name == emitterTrackName)
|
|
{
|
|
const int32 emitterIndex = track.AsEmitter.Index;
|
|
for (auto& param : parameters)
|
|
{
|
|
if (param.GetEmitterIndex() == emitterIndex && param.GetName() == paramName)
|
|
{
|
|
return (ParticleEffectParameter*)¶m;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
ParticleEffectParameter* ParticleEffect::GetParameter(const StringView& emitterTrackName, const Guid& paramId)
|
|
{
|
|
auto& parameters = GetParameters();
|
|
if (parameters.IsEmpty())
|
|
return nullptr;
|
|
|
|
auto system = ParticleSystem.Get();
|
|
for (int32 i = 0; i < system->Tracks.Count(); i++)
|
|
{
|
|
auto& track = system->Tracks[i];
|
|
if (track.Type == ParticleSystem::Track::Types::Emitter && track.Name == emitterTrackName)
|
|
{
|
|
const int32 emitterIndex = track.AsEmitter.Index;
|
|
for (auto& param : parameters)
|
|
{
|
|
if (param.GetEmitterIndex() == emitterIndex && param.GetParamIdentifier() == paramId)
|
|
{
|
|
return (ParticleEffectParameter*)¶m;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Variant ParticleEffect::GetParameterValue(const StringView& emitterTrackName, const StringView& paramName)
|
|
{
|
|
const auto param = GetParameter(emitterTrackName, paramName);
|
|
CHECK_RETURN(param, Variant::Null);
|
|
return param->GetValue();
|
|
}
|
|
|
|
void ParticleEffect::SetParameterValue(const StringView& emitterTrackName, const StringView& paramName, const Variant& value)
|
|
{
|
|
auto param = GetParameter(emitterTrackName, paramName);
|
|
CHECK(param);
|
|
param->SetValue(value);
|
|
}
|
|
|
|
void ParticleEffect::ResetParameters()
|
|
{
|
|
_parametersOverrides.Clear();
|
|
auto& parameters = GetParameters();
|
|
for (auto& p : parameters)
|
|
{
|
|
p.SetValue(p.GetDefaultValue());
|
|
}
|
|
}
|
|
|
|
float ParticleEffect::GetTime() const
|
|
{
|
|
return Instance.Time;
|
|
}
|
|
|
|
void ParticleEffect::SetTime(float time)
|
|
{
|
|
Instance.Time = time;
|
|
}
|
|
|
|
float ParticleEffect::GetLastUpdateTime() const
|
|
{
|
|
return Instance.LastUpdateTime;
|
|
}
|
|
|
|
void ParticleEffect::SetLastUpdateTime(float time)
|
|
{
|
|
Instance.LastUpdateTime = time;
|
|
}
|
|
|
|
int32 ParticleEffect::GetParticlesCount() const
|
|
{
|
|
return Instance.GetParticlesCount();
|
|
}
|
|
|
|
bool ParticleEffect::GetIsPlaying() const
|
|
{
|
|
return _isPlaying;
|
|
}
|
|
|
|
void ParticleEffect::ResetSimulation()
|
|
{
|
|
Instance.ClearState();
|
|
}
|
|
|
|
void ParticleEffect::UpdateSimulation(bool singleFrame)
|
|
{
|
|
// Skip if need to
|
|
if (!IsActiveInHierarchy()
|
|
|| ParticleSystem == nullptr
|
|
|| !ParticleSystem->IsLoaded()
|
|
|| _lastUpdateFrame == Engine::UpdateCount)
|
|
return;
|
|
|
|
// Request update
|
|
_lastUpdateFrame = Engine::UpdateCount;
|
|
_lastMinDstSqr = MAX_Real;
|
|
if (singleFrame)
|
|
Instance.LastUpdateTime = (UseTimeScale ? Time::Update.Time : Time::Update.UnscaledTime).GetTotalSeconds();
|
|
Particles::UpdateEffect(this);
|
|
}
|
|
|
|
void ParticleEffect::Play()
|
|
{
|
|
_isPlaying = true;
|
|
_isStopped = false;
|
|
}
|
|
|
|
void ParticleEffect::Pause()
|
|
{
|
|
_isPlaying = false;
|
|
}
|
|
|
|
void ParticleEffect::Stop()
|
|
{
|
|
_isStopped = true;
|
|
_isPlaying = false;
|
|
ResetSimulation();
|
|
}
|
|
|
|
void ParticleEffect::UpdateBounds()
|
|
{
|
|
BoundingBox bounds = BoundingBox::Empty;
|
|
const auto particleSystem = ParticleSystem.Get();
|
|
if (particleSystem && Instance.LastUpdateTime >= 0)
|
|
{
|
|
for (int32 j = 0; j < particleSystem->Tracks.Count(); j++)
|
|
{
|
|
const auto& track = particleSystem->Tracks[j];
|
|
if (track.Type != ParticleSystem::Track::Types::Emitter || track.Disabled)
|
|
continue;
|
|
const int32 emitterIndex = track.AsEmitter.Index;
|
|
ParticleEmitter* emitter = particleSystem->Emitters[emitterIndex].Get();
|
|
if (!emitter || emitter->Capacity == 0 || emitter->Graph.Layout.Size == 0 || Instance.Emitters.Count() <= emitterIndex)
|
|
continue;
|
|
auto& data = Instance.Emitters[emitterIndex];
|
|
|
|
BoundingBox emitterBounds;
|
|
if (emitter->GraphExecutorCPU.ComputeBounds(emitter, this, data, emitterBounds))
|
|
{
|
|
ASSERT(!emitterBounds.Minimum.IsNanOrInfinity() && !emitterBounds.Maximum.IsNanOrInfinity());
|
|
|
|
if (emitter->SimulationSpace == ParticlesSimulationSpace::Local)
|
|
{
|
|
BoundingBox::Transform(emitterBounds, _transform, emitterBounds);
|
|
}
|
|
|
|
BoundingBox::Merge(emitterBounds, bounds, bounds);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Empty bounds if there is no particle system to play or it has been never played
|
|
if (bounds == BoundingBox::Empty)
|
|
{
|
|
bounds = BoundingBox(_transform.Translation);
|
|
}
|
|
|
|
_box = bounds;
|
|
BoundingSphere::FromBox(bounds, _sphere);
|
|
if (_sceneRenderingKey != -1)
|
|
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
|
}
|
|
|
|
void ParticleEffect::Sync()
|
|
{
|
|
auto system = ParticleSystem.Get();
|
|
if (!system || system->WaitForLoaded())
|
|
{
|
|
Instance.ClearState();
|
|
return;
|
|
}
|
|
|
|
Instance.Sync(system);
|
|
|
|
for (int32 i = 0; i < system->Tracks.Count(); i++)
|
|
{
|
|
auto& track = system->Tracks[i];
|
|
if (track.Type == ParticleSystem::Track::Types::Emitter)
|
|
{
|
|
const int32 emitterIndex = track.AsEmitter.Index;
|
|
const auto emitter = system->Emitters[emitterIndex].Get();
|
|
if (emitter)
|
|
{
|
|
Instance.Emitters[emitterIndex].Sync(Instance, system, emitterIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SceneRenderTask* ParticleEffect::GetRenderTask() const
|
|
{
|
|
const uint64 minFrame = Engine::UpdateCount - 2;
|
|
|
|
// Custom task
|
|
const auto customViewRenderTask = CustomViewRenderTask.Get();
|
|
if (customViewRenderTask && customViewRenderTask->Enabled && customViewRenderTask->LastUsedFrame >= minFrame)
|
|
return customViewRenderTask;
|
|
|
|
// Main task
|
|
const auto mainRenderTask = MainRenderTask::Instance;
|
|
if (mainRenderTask && mainRenderTask->Enabled && mainRenderTask->LastUsedFrame >= minFrame)
|
|
return mainRenderTask;
|
|
|
|
// Editor viewport
|
|
#if USE_EDITOR
|
|
for (auto task : RenderTask::Tasks)
|
|
{
|
|
if (task->LastUsedFrame >= minFrame && task->Enabled)
|
|
{
|
|
if (const auto sceneRenderTask = Cast<SceneRenderTask>(task))
|
|
{
|
|
if (sceneRenderTask->ActorsSource == ActorsSources::Scenes)
|
|
{
|
|
return sceneRenderTask;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return nullptr;
|
|
}
|
|
|
|
#if USE_EDITOR
|
|
|
|
Array<ParticleEffect::ParameterOverride>& ParticleEffect::GetParametersOverrides()
|
|
{
|
|
CacheModifiedParameters();
|
|
return _parametersOverrides;
|
|
}
|
|
|
|
void ParticleEffect::SetParametersOverrides(const Array<ParameterOverride>& value)
|
|
{
|
|
ResetParameters();
|
|
_parametersOverrides = value;
|
|
ApplyModifiedParameters();
|
|
}
|
|
|
|
#endif
|
|
|
|
void ParticleEffect::Update()
|
|
{
|
|
if (!_isPlaying)
|
|
{
|
|
// Move update timer forward while paused for correct delta time after unpause
|
|
Instance.LastUpdateTime = (UseTimeScale ? Time::Update.Time : Time::Update.UnscaledTime).GetTotalSeconds();
|
|
return;
|
|
}
|
|
|
|
// Skip if off-screen
|
|
if (!UpdateWhenOffscreen && _lastMinDstSqr >= MAX_Real)
|
|
return;
|
|
|
|
if (UpdateMode == SimulationUpdateMode::FixedTimestep)
|
|
{
|
|
// Check if last simulation update was past enough to kick a new on
|
|
const float time = Time::Update.Time.GetTotalSeconds();
|
|
if (time - Instance.LastUpdateTime < FixedTimestep)
|
|
return;
|
|
}
|
|
|
|
UpdateSimulation();
|
|
}
|
|
|
|
#if USE_EDITOR
|
|
|
|
#include "Editor/Editor.h"
|
|
|
|
void ParticleEffect::UpdateExecuteInEditor()
|
|
{
|
|
// Auto-play in Editor
|
|
if (!Editor::IsPlayMode && !_isStopped)
|
|
{
|
|
_isPlaying = true;
|
|
Update();
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
void ParticleEffect::CacheModifiedParameters()
|
|
{
|
|
if (_parameters.IsEmpty())
|
|
return;
|
|
_parametersOverrides.Clear();
|
|
auto& parameters = GetParameters();
|
|
for (auto& param : parameters)
|
|
{
|
|
if (param.GetValue() != param.GetDefaultValue())
|
|
{
|
|
auto& e = _parametersOverrides.AddOne();
|
|
e.Track = param.GetTrackName();
|
|
e.Id = param.GetParamIdentifier();
|
|
e.Value = param.GetValue();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ParticleEffect::ApplyModifiedParameters()
|
|
{
|
|
if (_parametersOverrides.IsEmpty())
|
|
return;
|
|
|
|
// Parameters getter applies the parameters overrides
|
|
if (_parameters.IsEmpty())
|
|
{
|
|
GetParameters();
|
|
return;
|
|
}
|
|
|
|
for (auto& e : _parametersOverrides)
|
|
{
|
|
const auto param = GetParameter(e.Track, e.Id);
|
|
if (param)
|
|
{
|
|
param->SetValue(e.Value);
|
|
}
|
|
else
|
|
{
|
|
LOG(Warning, "Failed to apply the particle effect parameter (id={0} from track={1})", e.Id, e.Track);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ParticleEffect::OnParticleSystemModified()
|
|
{
|
|
Instance.ClearState();
|
|
_parameters.Resize(0);
|
|
_parametersVersion = 0;
|
|
}
|
|
|
|
void ParticleEffect::OnParticleSystemLoaded()
|
|
{
|
|
ApplyModifiedParameters();
|
|
}
|
|
|
|
bool ParticleEffect::HasContentLoaded() const
|
|
{
|
|
if (ParticleSystem == nullptr)
|
|
return true;
|
|
if (!ParticleSystem->IsLoaded())
|
|
return false;
|
|
for (int32 i = 0; i < ParticleSystem->Emitters.Count(); i++)
|
|
{
|
|
const auto emitter = ParticleSystem->Emitters[i].Get();
|
|
if (emitter && !emitter->IsLoaded())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ParticleEffect::Draw(RenderContext& renderContext)
|
|
{
|
|
if (renderContext.View.Pass == DrawPass::GlobalSDF || renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas)
|
|
return;
|
|
_lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(GetPosition(), renderContext.View.Position));
|
|
Particles::DrawParticles(renderContext, this);
|
|
}
|
|
|
|
#if USE_EDITOR
|
|
|
|
#include "Engine/Debug/DebugDraw.h"
|
|
|
|
void ParticleEffect::OnDebugDrawSelected()
|
|
{
|
|
DEBUG_DRAW_WIRE_BOX(_box, Color::Violet * 0.7f, 0, true);
|
|
|
|
// Base
|
|
Actor::OnDebugDrawSelected();
|
|
}
|
|
|
|
#endif
|
|
|
|
void ParticleEffect::OnLayerChanged()
|
|
{
|
|
if (_sceneRenderingKey != -1)
|
|
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
|
}
|
|
|
|
void ParticleEffect::Serialize(SerializeStream& stream, const void* otherObj)
|
|
{
|
|
// Base
|
|
Actor::Serialize(stream, otherObj);
|
|
|
|
SERIALIZE_GET_OTHER_OBJ(ParticleEffect);
|
|
|
|
const auto& params = GetParameters();
|
|
const auto* otherParams = other ? &((ParticleEffect*)other)->GetParameters() : nullptr;
|
|
{
|
|
stream.JKEY("Overrides");
|
|
stream.StartArray();
|
|
for (int32 i = 0; i < params.Count(); i++)
|
|
{
|
|
auto& param = params[i];
|
|
if (otherParams)
|
|
{
|
|
if (otherParams->IsEmpty())
|
|
{
|
|
// Serialize only parameters values overriding the default value (from system)
|
|
if (param.GetValue() == param.GetDefaultValue())
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// Serialize only parameters values overriding the other object value (from diff)
|
|
const auto& otherParam = (*otherParams)[i];
|
|
if (param.GetValue() == otherParam.GetValue())
|
|
continue;
|
|
}
|
|
}
|
|
|
|
stream.StartObject();
|
|
|
|
stream.JKEY("Track");
|
|
stream.String(param.GetTrackName());
|
|
|
|
stream.JKEY("Id");
|
|
stream.Guid(param.GetParamIdentifier());
|
|
|
|
stream.JKEY("Value");
|
|
Serialization::Serialize(stream, param.GetValue(), nullptr);
|
|
|
|
stream.EndObject();
|
|
}
|
|
stream.EndArray();
|
|
}
|
|
|
|
SERIALIZE(ParticleSystem);
|
|
SERIALIZE(UpdateMode);
|
|
SERIALIZE(FixedTimestep);
|
|
SERIALIZE(SimulationSpeed);
|
|
SERIALIZE(UseTimeScale);
|
|
SERIALIZE(IsLooping);
|
|
SERIALIZE(PlayOnStart);
|
|
SERIALIZE(UpdateWhenOffscreen);
|
|
SERIALIZE(DrawModes);
|
|
SERIALIZE(SortOrder);
|
|
}
|
|
|
|
void ParticleEffect::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
|
{
|
|
// Base
|
|
Actor::Deserialize(stream, modifier);
|
|
|
|
const auto overridesMember = stream.FindMember("Overrides");
|
|
if (overridesMember != stream.MemberEnd())
|
|
{
|
|
// [Deprecated on 25.11.2018, expires on 25.11.2022]
|
|
if (modifier->EngineBuild < 6197)
|
|
{
|
|
const auto& overrides = overridesMember->value;
|
|
ASSERT(overrides.IsArray());
|
|
_parametersOverrides.EnsureCapacity(_parametersOverrides.Count() + overrides.Size());
|
|
for (rapidjson::SizeType i = 0; i < overrides.Size(); i++)
|
|
{
|
|
const auto& o = (DeserializeStream&)overrides[i];
|
|
const String trackName = JsonTools::GetString(o, "Track");
|
|
const Guid id = JsonTools::GetGuid(o, "Id");
|
|
ParameterOverride* e = nullptr;
|
|
for (auto& q : _parametersOverrides)
|
|
{
|
|
if (q.Id == id && q.Track == trackName)
|
|
{
|
|
e = &q;
|
|
break;
|
|
}
|
|
}
|
|
if (e)
|
|
{
|
|
// Update overriden parameter value
|
|
CommonValue value;
|
|
auto mValue = SERIALIZE_FIND_MEMBER(o, "Value");
|
|
if (mValue != o.MemberEnd())
|
|
e->Value = Variant(JsonTools::GetCommonValue(mValue->value));
|
|
}
|
|
else
|
|
{
|
|
// Add parameter override
|
|
auto& p = _parametersOverrides.AddOne();
|
|
p.Track = trackName;
|
|
p.Id = id;
|
|
CommonValue value;
|
|
auto mValue = SERIALIZE_FIND_MEMBER(o, "Value");
|
|
if (mValue != o.MemberEnd())
|
|
p.Value = Variant(JsonTools::GetCommonValue(mValue->value));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const auto& overrides = overridesMember->value;
|
|
ASSERT(overrides.IsArray());
|
|
_parametersOverrides.EnsureCapacity(_parametersOverrides.Count() + overrides.Size());
|
|
for (rapidjson::SizeType i = 0; i < overrides.Size(); i++)
|
|
{
|
|
auto& o = (DeserializeStream&)overrides[i];
|
|
const String trackName = JsonTools::GetString(o, "Track");
|
|
const Guid id = JsonTools::GetGuid(o, "Id");
|
|
ParameterOverride* e = nullptr;
|
|
for (auto& q : _parametersOverrides)
|
|
{
|
|
if (q.Id == id && q.Track == trackName)
|
|
{
|
|
e = &q;
|
|
break;
|
|
}
|
|
}
|
|
if (e)
|
|
{
|
|
// Update overriden parameter value
|
|
const auto mValue = SERIALIZE_FIND_MEMBER(o, "Value");
|
|
if (mValue != stream.MemberEnd())
|
|
Serialization::Deserialize(mValue->value, e->Value, modifier);
|
|
}
|
|
else
|
|
{
|
|
// Add parameter override
|
|
auto& p = _parametersOverrides.AddOne();
|
|
p.Track = trackName;
|
|
p.Id = id;
|
|
const auto mValue = SERIALIZE_FIND_MEMBER(o, "Value");
|
|
if (mValue != stream.MemberEnd())
|
|
Serialization::Deserialize(mValue->value, p.Value, modifier);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DESERIALIZE(ParticleSystem);
|
|
DESERIALIZE(UpdateMode);
|
|
DESERIALIZE(FixedTimestep);
|
|
DESERIALIZE(SimulationSpeed);
|
|
DESERIALIZE(UseTimeScale);
|
|
DESERIALIZE(IsLooping);
|
|
DESERIALIZE(PlayOnStart);
|
|
DESERIALIZE(UpdateWhenOffscreen);
|
|
DESERIALIZE(DrawModes);
|
|
DESERIALIZE(SortOrder);
|
|
|
|
if (_parameters.HasItems())
|
|
{
|
|
ApplyModifiedParameters();
|
|
}
|
|
}
|
|
|
|
void ParticleEffect::EndPlay()
|
|
{
|
|
CacheModifiedParameters();
|
|
Particles::OnEffectDestroy(this);
|
|
Instance.ClearState();
|
|
_parameters.Clear();
|
|
_parametersVersion = 0;
|
|
|
|
// Base
|
|
Actor::EndPlay();
|
|
}
|
|
|
|
void ParticleEffect::OnEnable()
|
|
{
|
|
GetScene()->Ticking.Update.AddTick<ParticleEffect, &ParticleEffect::Update>(this);
|
|
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
|
#if USE_EDITOR
|
|
GetSceneRendering()->AddViewportIcon(this);
|
|
GetScene()->Ticking.Update.AddTickExecuteInEditor<ParticleEffect, &ParticleEffect::UpdateExecuteInEditor>(this);
|
|
#endif
|
|
|
|
if (PlayOnStart)
|
|
Play();
|
|
|
|
// Base
|
|
Actor::OnEnable();
|
|
}
|
|
|
|
void ParticleEffect::OnDisable()
|
|
{
|
|
#if USE_EDITOR
|
|
GetScene()->Ticking.Update.RemoveTickExecuteInEditor(this);
|
|
GetSceneRendering()->RemoveViewportIcon(this);
|
|
#endif
|
|
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
|
GetScene()->Ticking.Update.RemoveTick(this);
|
|
|
|
// Base
|
|
Actor::OnDisable();
|
|
}
|
|
|
|
void ParticleEffect::OnActiveInTreeChanged()
|
|
{
|
|
// Base
|
|
Actor::OnActiveInTreeChanged();
|
|
|
|
if (!IsActiveInHierarchy())
|
|
{
|
|
// Invalidate the simulation
|
|
CacheModifiedParameters();
|
|
Instance.ClearState();
|
|
}
|
|
}
|
|
|
|
void ParticleEffect::OnTransformChanged()
|
|
{
|
|
// Base
|
|
Actor::OnTransformChanged();
|
|
|
|
UpdateBounds();
|
|
}
|