// // Copyright (c) 2008-2017 the Urho3D project. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // #include "../Precompiled.h" #include "../Core/Context.h" #include "../IO/FileSystem.h" #include "../IO/Log.h" #include "../Resource/ResourceCache.h" #include "../Resource/XMLFile.h" #include "../Atomic2D/ParticleEffect2D.h" #include "../Atomic2D/Sprite2D.h" #include "../DebugNew.h" namespace Atomic { static const int srcBlendFuncs[] = { 1, // GL_ONE 1, // GL_ONE 0x0306, // GL_DST_COLOR 0x0302, // GL_SRC_ALPHA 0x0302, // GL_SRC_ALPHA 1, // GL_ONE 0x0305, // GL_ONE_MINUS_DST_ALPHA 1, // GL_ONE 0x0302 // GL_SRC_ALPHA }; static const int destBlendFuncs[] = { 0, // GL_ZERO 1, // GL_ONE 0, // GL_ZERO 0x0303, // GL_ONE_MINUS_SRC_ALPHA 1, // GL_ONE 0x0303, // GL_ONE_MINUS_SRC_ALPHA 0x0304, // GL_DST_ALPHA 1, // GL_ONE 1 // GL_ONE }; #if ATOMIC_CXX11 // Make sure that there are are as many blend functions as we have blend modes. static_assert(sizeof(srcBlendFuncs) / sizeof(srcBlendFuncs[0]) == MAX_BLENDMODES, ""); static_assert(sizeof(destBlendFuncs) / sizeof(destBlendFuncs[0]) == MAX_BLENDMODES, ""); #endif ParticleEffect2D::ParticleEffect2D(Context* context) : Resource(context), sourcePositionVariance_(7.0f, 7.0f), speed_(260.0f), speedVariance_(10.0f), particleLifeSpan_(1.000f), particleLifespanVariance_(0.700f), angle_(0.0f), angleVariance_(360.0f), gravity_(0.0f, 0.0f), radialAcceleration_(-380.0f), tangentialAcceleration_(-140.0f), radialAccelVariance_(0.0f), tangentialAccelVariance_(0.0f), startColor_(1.0f, 0.0f, 0.0f, 1.0f), startColorVariance_(0.0f, 0.0f, 0.0f, 0.0f), finishColor_(1.0f, 1.0f, 0.0f, 1.0f), finishColorVariance_(0.0f, 0.0f, 0.0f, 0.0f), maxParticles_(600), startParticleSize_(60.0f), startParticleSizeVariance_(40.0f), finishParticleSize_(5.0f), finishParticleSizeVariance_(5.0f), duration_(-1.0f), emitterType_(EMITTER_TYPE_GRAVITY), maxRadius_(100.0f), maxRadiusVariance_(0.0f), minRadius_(0.0f), minRadiusVariance_(0.0f), rotatePerSecond_(0.0f), rotatePerSecondVariance_(0.0f), blendMode_(BLEND_ALPHA), rotationStart_(0.0f), rotationStartVariance_(0.0f), rotationEnd_(0.0f), rotationEndVariance_(0.0f) { } ParticleEffect2D::~ParticleEffect2D() { } void ParticleEffect2D::RegisterObject(Context* context) { context->RegisterFactory(); } bool ParticleEffect2D::BeginLoad(Deserializer& source) { if (GetName().Empty()) SetName(source.GetName()); loadSpriteName_.Clear(); XMLFile xmlFile(context_); if (!xmlFile.Load(source)) return false; XMLElement rootElem = xmlFile.GetRoot("particleEmitterConfig"); if (!rootElem) return false; String texture = rootElem.GetChild("texture").GetAttribute("name"); loadSpriteName_ = GetParentPath(GetName()) + texture; // If async loading, request the sprite beforehand if (GetAsyncLoadState() == ASYNC_LOADING) GetSubsystem()->BackgroundLoadResource(loadSpriteName_, true, this); sourcePositionVariance_ = ReadVector2(rootElem, "sourcePositionVariance"); speed_ = ReadFloat(rootElem, "speed"); speedVariance_ = ReadFloat(rootElem, "speedVariance"); particleLifeSpan_ = Max(0.01f, ReadFloat(rootElem, "particleLifeSpan")); particleLifespanVariance_ = ReadFloat(rootElem, "particleLifespanVariance"); angle_ = ReadFloat(rootElem, "angle"); angleVariance_ = ReadFloat(rootElem, "angleVariance"); gravity_ = ReadVector2(rootElem, "gravity"); radialAcceleration_ = ReadFloat(rootElem, "radialAcceleration"); tangentialAcceleration_ = ReadFloat(rootElem, "tangentialAcceleration"); radialAccelVariance_ = ReadFloat(rootElem, "radialAccelVariance"); tangentialAccelVariance_ = ReadFloat(rootElem, "tangentialAccelVariance"); startColor_ = ReadColor(rootElem, "startColor"); startColorVariance_ = ReadColor(rootElem, "startColorVariance"); finishColor_ = ReadColor(rootElem, "finishColor"); finishColorVariance_ = ReadColor(rootElem, "finishColorVariance"); maxParticles_ = ReadInt(rootElem, "maxParticles"); startParticleSize_ = ReadFloat(rootElem, "startParticleSize"); startParticleSizeVariance_ = ReadFloat(rootElem, "startParticleSizeVariance"); finishParticleSize_ = ReadFloat(rootElem, "finishParticleSize"); // Typo in pex file finishParticleSizeVariance_ = ReadFloat(rootElem, "FinishParticleSizeVariance"); duration_ = M_INFINITY; if (rootElem.HasChild("duration")) { float duration = ReadFloat(rootElem, "duration"); if (duration > 0.0f) duration_ = duration; } emitterType_ = (EmitterType2D)ReadInt(rootElem, "emitterType"); maxRadius_ = ReadFloat(rootElem, "maxRadius"); maxRadiusVariance_ = ReadFloat(rootElem, "maxRadiusVariance"); minRadius_ = ReadFloat(rootElem, "minRadius"); minRadiusVariance_ = ReadFloat(rootElem, "minRadiusVariance"); rotatePerSecond_ = ReadFloat(rootElem, "rotatePerSecond"); rotatePerSecondVariance_ = ReadFloat(rootElem, "rotatePerSecondVariance"); int blendFuncSource = ReadInt(rootElem, "blendFuncSource"); int blendFuncDestination = ReadInt(rootElem, "blendFuncDestination"); blendMode_ = BLEND_ALPHA; for (int i = 0; i < MAX_BLENDMODES; ++i) { if (blendFuncSource == srcBlendFuncs[i] && blendFuncDestination == destBlendFuncs[i]) { blendMode_ = (BlendMode)i; break; } } rotationStart_ = ReadFloat(rootElem, "rotationStart"); rotationStartVariance_ = ReadFloat(rootElem, "rotationStartVariance"); rotationEnd_ = ReadFloat(rootElem, "rotationEnd"); rotationEndVariance_ = ReadFloat(rootElem, "rotationEndVariance"); // Note: not accurate SetMemoryUse(source.GetSize()); return true; } bool ParticleEffect2D::EndLoad() { // Apply the sprite now if (!loadSpriteName_.Empty()) { ResourceCache* cache = GetSubsystem(); sprite_ = cache->GetResource(loadSpriteName_); if (!sprite_) ATOMIC_LOGERROR("Could not load sprite " + loadSpriteName_ + " for particle effect"); loadSpriteName_.Clear(); } return true; } bool ParticleEffect2D::Save(Serializer& dest) const { if (!sprite_) return false; XMLFile xmlFile(context_); XMLElement rootElem = xmlFile.CreateRoot("particleEmitterConfig"); String fileName = GetFileNameAndExtension(sprite_->GetName()); rootElem.CreateChild("texture").SetAttribute("name", fileName); WriteVector2(rootElem, "sourcePosition", Vector2::ZERO); WriteVector2(rootElem, "sourcePositionVariance", sourcePositionVariance_); WriteFloat(rootElem, "speed", speed_); WriteFloat(rootElem, "speedVariance", speedVariance_); WriteFloat(rootElem, "particleLifeSpan", particleLifeSpan_); WriteFloat(rootElem, "particleLifespanVariance", particleLifespanVariance_); WriteFloat(rootElem, "angle", angle_); WriteFloat(rootElem, "angleVariance", angleVariance_); WriteVector2(rootElem, "gravity", gravity_); WriteFloat(rootElem, "radialAcceleration", radialAcceleration_); WriteFloat(rootElem, "tangentialAcceleration", tangentialAcceleration_); WriteFloat(rootElem, "radialAccelVariance", radialAccelVariance_); WriteFloat(rootElem, "tangentialAccelVariance", tangentialAccelVariance_); WriteColor(rootElem, "startColor", startColor_); WriteColor(rootElem, "startColorVariance", startColorVariance_); WriteColor(rootElem, "finishColor", finishColor_); WriteColor(rootElem, "finishColorVariance", finishColorVariance_); WriteInt(rootElem, "maxParticles", maxParticles_); WriteFloat(rootElem, "startParticleSize", startParticleSize_); WriteFloat(rootElem, "startParticleSizeVariance", startParticleSizeVariance_); WriteFloat(rootElem, "finishParticleSize", finishParticleSize_); // Typo in pex file WriteFloat(rootElem, "FinishParticleSizeVariance", finishParticleSizeVariance_); float duration = duration_; if (duration == M_INFINITY) duration = -1.0f; WriteFloat(rootElem, "duration", duration); WriteInt(rootElem, "emitterType", (int)emitterType_); WriteFloat(rootElem, "maxRadius", maxRadius_); WriteFloat(rootElem, "maxRadiusVariance", maxRadiusVariance_); WriteFloat(rootElem, "minRadius", minRadius_); WriteFloat(rootElem, "minRadiusVariance", minRadiusVariance_); WriteFloat(rootElem, "rotatePerSecond", rotatePerSecond_); WriteFloat(rootElem, "rotatePerSecondVariance", rotatePerSecondVariance_); WriteInt(rootElem, "blendFuncSource", srcBlendFuncs[blendMode_]); WriteInt(rootElem, "blendFuncDestination", destBlendFuncs[blendMode_]); WriteFloat(rootElem, "rotationStart", rotationStart_); WriteFloat(rootElem, "rotationStartVariance", rotationStartVariance_); WriteFloat(rootElem, "rotationEnd", rotationEnd_); WriteFloat(rootElem, "rotationEndVariance", rotationEndVariance_); return xmlFile.Save(dest); } void ParticleEffect2D::SetSprite(Sprite2D* sprite) { sprite_ = sprite; } void ParticleEffect2D::SetSourcePositionVariance(const Vector2& sourcePositionVariance) { sourcePositionVariance_ = sourcePositionVariance; } void ParticleEffect2D::SetSpeed(float speed) { speed_ = speed; } void ParticleEffect2D::SetSpeedVariance(float speedVariance) { speedVariance_ = speedVariance; } void ParticleEffect2D::SetParticleLifeSpan(float particleLifeSpan) { particleLifeSpan_ = particleLifeSpan; } void ParticleEffect2D::SetParticleLifespanVariance(float particleLifespanVariance) { particleLifespanVariance_ = particleLifespanVariance; } void ParticleEffect2D::SetAngle(float angle) { angle_ = angle; } void ParticleEffect2D::SetAngleVariance(float angleVariance) { angleVariance_ = angleVariance; } void ParticleEffect2D::SetGravity(const Vector2& gravity) { gravity_ = gravity; } void ParticleEffect2D::SetRadialAcceleration(float radialAcceleration) { radialAcceleration_ = radialAcceleration; } void ParticleEffect2D::SetTangentialAcceleration(float tangentialAcceleration) { tangentialAcceleration_ = tangentialAcceleration; } void ParticleEffect2D::SetRadialAccelVariance(float radialAccelVariance) { radialAccelVariance_ = radialAccelVariance; } void ParticleEffect2D::SetTangentialAccelVariance(float tangentialAccelVariance) { tangentialAccelVariance_ = tangentialAccelVariance; } void ParticleEffect2D::SetStartColor(const Color& startColor) { startColor_ = startColor; } void ParticleEffect2D::SetStartColorVariance(const Color& startColorVariance) { startColorVariance_ = startColorVariance; } void ParticleEffect2D::SetFinishColor(const Color& finishColor) { finishColor_ = finishColor; } void ParticleEffect2D::SetFinishColorVariance(const Color& finishColorVariance) { finishColorVariance_ = finishColorVariance; } void ParticleEffect2D::SetMaxParticles(int maxParticles) { maxParticles_ = maxParticles; } void ParticleEffect2D::SetStartParticleSize(float startParticleSize) { startParticleSize_ = startParticleSize; } void ParticleEffect2D::SetStartParticleSizeVariance(float startParticleSizeVariance) { startParticleSizeVariance_ = startParticleSizeVariance; } void ParticleEffect2D::SetFinishParticleSize(float finishParticleSize) { finishParticleSize_ = finishParticleSize; } void ParticleEffect2D::SetFinishParticleSizeVariance(float finishParticleSizeVariance) { finishParticleSizeVariance_ = finishParticleSizeVariance; } void ParticleEffect2D::SetDuration(float duration) { duration_ = duration; } void ParticleEffect2D::SetEmitterType(EmitterType2D emitterType) { emitterType_ = emitterType; } void ParticleEffect2D::SetMaxRadius(float maxRadius) { maxRadius_ = maxRadius; } void ParticleEffect2D::SetMaxRadiusVariance(float maxRadiusVariance) { maxRadiusVariance_ = maxRadiusVariance; } void ParticleEffect2D::SetMinRadius(float minRadius) { minRadius_ = minRadius; } void ParticleEffect2D::SetMinRadiusVariance(float minRadiusVariance) { minRadiusVariance_ = minRadiusVariance; } void ParticleEffect2D::SetRotatePerSecond(float rotatePerSecond) { rotatePerSecond_ = rotatePerSecond; } void ParticleEffect2D::SetRotatePerSecondVariance(float rotatePerSecondVariance) { rotatePerSecondVariance_ = rotatePerSecondVariance; } void ParticleEffect2D::SetBlendMode(BlendMode blendMode) { blendMode_ = blendMode; } void ParticleEffect2D::SetRotationStart(float rotationStart) { rotationStart_ = rotationStart; } void ParticleEffect2D::SetRotationStartVariance(float rotationStartVariance) { rotationStartVariance_ = rotationStartVariance; } void ParticleEffect2D::SetRotationEnd(float rotationEnd) { rotationEnd_ = rotationEnd; } void ParticleEffect2D::SetRotationEndVariance(float rotationEndVariance) { rotationEndVariance_ = rotationEndVariance; } SharedPtr ParticleEffect2D::Clone(const String& cloneName) const { SharedPtr ret(new ParticleEffect2D(context_)); ret->SetName(cloneName); ret->sprite_ = sprite_; ret->sourcePositionVariance_ = sourcePositionVariance_; ret->speed_ = speed_; ret->speedVariance_ = speedVariance_; ret->particleLifeSpan_ = particleLifeSpan_; ret->particleLifespanVariance_ = particleLifespanVariance_; ret->angle_ = angle_; ret->angleVariance_ = angleVariance_; ret->gravity_ = gravity_; ret->radialAcceleration_ = radialAcceleration_; ret->tangentialAcceleration_ = tangentialAcceleration_; ret->radialAccelVariance_ = radialAccelVariance_; ret->tangentialAccelVariance_ = tangentialAccelVariance_; ret->startColor_ = startColor_; ret->startColorVariance_ = startColorVariance_; ret->finishColor_ = finishColor_; ret->finishColorVariance_ = finishColorVariance_; ret->maxParticles_ = maxParticles_; ret->startParticleSize_ = startParticleSize_; ret->startParticleSizeVariance_ = startParticleSizeVariance_; ret->finishParticleSize_ = finishParticleSize_; ret->finishParticleSizeVariance_ = finishParticleSizeVariance_; ret->duration_ = duration_; ret->emitterType_ = emitterType_; ret->maxRadius_ = maxRadius_; ret->maxRadiusVariance_ = maxRadiusVariance_; ret->minRadius_ = minRadius_; ret->minRadiusVariance_ = minRadiusVariance_; ret->rotatePerSecond_ = rotatePerSecond_; ret->rotatePerSecondVariance_ = rotatePerSecondVariance_; ret->blendMode_ = blendMode_; ret->rotationStart_ = rotationStart_; ret->rotationStartVariance_ = rotationStartVariance_; ret->rotationEnd_ = rotationEnd_; ret->rotationEndVariance_ = rotationEndVariance_; /// \todo Zero if source was created programmatically ret->SetMemoryUse(GetMemoryUse()); return ret; } int ParticleEffect2D::ReadInt(const XMLElement& element, const String& name) const { return element.GetChild(name).GetInt("value"); } float ParticleEffect2D::ReadFloat(const XMLElement& element, const String& name) const { return element.GetChild(name).GetFloat("value"); } Color ParticleEffect2D::ReadColor(const XMLElement& element, const String& name) const { XMLElement child = element.GetChild(name); return Color(child.GetFloat("red"), child.GetFloat("green"), child.GetFloat("blue"), child.GetFloat("alpha")); } Vector2 ParticleEffect2D::ReadVector2(const XMLElement& element, const String& name) const { XMLElement child = element.GetChild(name); return Vector2(child.GetFloat("x"), child.GetFloat("y")); } void ParticleEffect2D::WriteInt(XMLElement& element, const String& name, int value) const { XMLElement child = element.CreateChild(name); child.SetInt("value", value); } void ParticleEffect2D::WriteFloat(XMLElement& element, const String& name, float value) const { XMLElement child = element.CreateChild(name); child.SetFloat("value", value); } void ParticleEffect2D::WriteColor(XMLElement& element, const String& name, const Color& color) const { XMLElement child = element.CreateChild(name); child.SetFloat("red", color.r_); child.SetFloat("green", color.g_); child.SetFloat("blue", color.b_); child.SetFloat("alpha", color.a_); } void ParticleEffect2D::WriteVector2(XMLElement& element, const String& name, const Vector2& value) const { XMLElement child = element.CreateChild(name); child.SetFloat("x", value.x_); child.SetFloat("y", value.y_); } }