/* Copyright (C) 2011 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #ifndef INCLUDED_PARTICLEEMITTER #define INCLUDED_PARTICLEEMITTER #include "graphics/ModelAbstract.h" #include "graphics/ParticleEmitterType.h" #include "graphics/Texture.h" #include "maths/Quaternion.h" #include "renderer/VertexArray.h" #include /** * Simulation state for a single particle. */ struct SParticle { CVector3D pos; CVector3D velocity; float angle; float angleSpeed; float size; float sizeGrowthRate; SColor4ub color; float age; float maxAge; }; typedef shared_ptr CParticleEmitterPtr; /** * Particle emitter. * * Emitters store particle data in two forms: * * m_Particles contains the raw data used for the CPU particle simulation. * * m_VertexArray contains the data required for rendering. * Particles are rendered as billboard quads, so the vertex array contains four vertices * per particle with different UV coordinates. The billboard position computation is * performed by a vertex shader. * * The number of particles is a constant for the entire life of the emitter, * to simplify the updating and rendering. * m_Particles acts like a ring buffer, so we don't have to worry about dynamically * allocating particles. If particles have variable lifetimes, they'll exist in the * array with alpha=0 until they're overwritten by a new particle after the maximum * lifetime. * * (It's quite likely this could be made more efficient, if the overhead of any added * complexity is not high.) */ class CParticleEmitter { public: CParticleEmitter(const CParticleEmitterTypePtr& type); /** * Set the position to be used for emission of new particles. */ void SetPosition(const CVector3D& pos) { m_Pos = pos; } CVector3D GetPosition() const { return m_Pos; } /** * Set the rotation to be used for emission of new particles (note: depends on particles). */ void SetRotation(const CQuaternion& rot) { m_Rot = rot; } CQuaternion GetRotation() const { return m_Rot; } /** * Get the bounding box of the center points of particles at their current positions. */ CBoundingBoxAligned GetParticleBounds() { return m_ParticleBounds; } /** * Push a new particle onto the ring buffer. (May overwrite an old particle.) */ void AddParticle(const SParticle& particle); /** * Update particle and vertex array data. Must be called before RenderArray. * * If frameNumber is the same as the previous call to UpdateArrayData, * then the function will do no work and return immediately. */ void UpdateArrayData(int frameNumber); /** * Make the vertex data available for subsequent binding and rendering. */ void PrepareForRendering(); /** * Bind rendering state (textures and blend modes). */ void Bind(const CShaderProgramPtr& shader); /** * Draw the vertex array. */ void RenderArray(const CShaderProgramPtr& shader); /** * Stop this emitter emitting new particles, and pass responsibility for rendering * to the CParticleManager. This should be called before dropping the last shared_ptr * to this object so that it will carry on rendering (until all particles have dissipated) * even when it's no longer attached to a model. * @param self the shared_ptr you're about to drop */ void Unattach(const CParticleEmitterPtr& self); void SetEntityVariable(const std::string& name, float value); CParticleEmitterTypePtr m_Type; /// Whether this emitter is still emitting new particles bool m_Active; CVector3D m_Pos; CQuaternion m_Rot; std::map m_EntityVariables; std::vector m_Particles; size_t m_NextParticleIdx; float m_LastUpdateTime; float m_EmissionRoundingError; private: /// Bounding box of the current particle center points CBoundingBoxAligned m_ParticleBounds; VertexIndexArray m_IndexArray; VertexArray m_VertexArray; VertexArray::Attribute m_AttributePos; VertexArray::Attribute m_AttributeAxis; VertexArray::Attribute m_AttributeUV; VertexArray::Attribute m_AttributeColor; int m_LastFrameNumber; }; /** * Particle emitter model, for attaching emitters as props on other models. */ class CModelParticleEmitter : public CModelAbstract { public: CModelParticleEmitter(const CParticleEmitterTypePtr& type); ~CModelParticleEmitter(); /// Dynamic cast virtual CModelParticleEmitter* ToCModelParticleEmitter() { return this; } virtual CModelAbstract* Clone() const; virtual void SetDirtyRec(int dirtyflags) { SetDirty(dirtyflags); } virtual void SetTerrainDirty(ssize_t UNUSED(i0), ssize_t UNUSED(j0), ssize_t UNUSED(i1), ssize_t UNUSED(j1)) { } virtual void SetEntityVariable(const std::string& name, float value); virtual void CalcBounds(); virtual void ValidatePosition(); virtual void InvalidatePosition(); virtual void SetTransform(const CMatrix3D& transform); CParticleEmitterTypePtr m_Type; CParticleEmitterPtr m_Emitter; }; #endif // INCLUDED_PARTICLEEMITTER