Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ParticleEmitter.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "ParticleEmitter.h"
21 
22 #include "graphics/LightEnv.h"
23 #include "graphics/LOSTexture.h"
27 
28 #include "renderer/Renderer.h"
29 
31  m_Type(type), m_Active(true), m_NextParticleIdx(0), m_EmissionRoundingError(0.f),
32  m_LastUpdateTime(type->m_Manager.GetCurrentTime()),
33  m_IndexArray(GL_DYNAMIC_DRAW),
34  m_VertexArray(GL_DYNAMIC_DRAW)
35 {
36  // If we should start with particles fully emitted, pretend that we
37  // were created in the past so the first update will produce lots of
38  // particles.
39  // TODO: instead of this, maybe it would make more sense to do a full
40  // lifetime-length update of all emitters when the game first starts
41  // (so that e.g. buildings constructed later on won't have fully-started
42  // emitters, but those at the start will)?
43  if (m_Type->m_StartFull)
44  m_LastUpdateTime -= m_Type->m_MaxLifetime;
45 
46  m_Particles.reserve(m_Type->m_MaxParticles);
47 
48  m_AttributePos.type = GL_FLOAT;
51 
52  m_AttributeAxis.type = GL_FLOAT;
55 
56  m_AttributeUV.type = GL_FLOAT;
57  m_AttributeUV.elems = 2;
59 
60  m_AttributeColor.type = GL_UNSIGNED_BYTE;
63 
64  m_VertexArray.SetNumVertices(m_Type->m_MaxParticles * 4);
66 
67  m_IndexArray.SetNumVertices(m_Type->m_MaxParticles * 6);
70  for (size_t i = 0; i < m_Type->m_MaxParticles; ++i)
71  {
72  *index++ = i*4 + 0;
73  *index++ = i*4 + 1;
74  *index++ = i*4 + 2;
75  *index++ = i*4 + 2;
76  *index++ = i*4 + 3;
77  *index++ = i*4 + 0;
78  }
81 }
82 
84 {
85  // Update m_Particles
86  m_Type->UpdateEmitter(*this, m_Type->m_Manager.GetCurrentTime() - m_LastUpdateTime);
87  m_LastUpdateTime = m_Type->m_Manager.GetCurrentTime();
88 
89  // Regenerate the vertex array data:
90 
95 
96  ENSURE(m_Particles.size() <= m_Type->m_MaxParticles);
97 
98  CBoundingBoxAligned bounds;
99 
100  for (size_t i = 0; i < m_Particles.size(); ++i)
101  {
102  // TODO: for more efficient rendering, maybe we should replace this with
103  // a degenerate quad if alpha is 0
104 
105  bounds += m_Particles[i].pos;
106 
107  *attrPos++ = m_Particles[i].pos;
108  *attrPos++ = m_Particles[i].pos;
109  *attrPos++ = m_Particles[i].pos;
110  *attrPos++ = m_Particles[i].pos;
111 
112  // Compute corner offsets, split into sin/cos components so the vertex
113  // shader can multiply by the camera-right (or left?) and camera-up vectors
114  // to get rotating billboards:
115 
116  float s = sin(m_Particles[i].angle) * m_Particles[i].size/2.f;
117  float c = cos(m_Particles[i].angle) * m_Particles[i].size/2.f;
118 
119  (*attrAxis)[0] = c;
120  (*attrAxis)[1] = s;
121  ++attrAxis;
122  (*attrAxis)[0] = s;
123  (*attrAxis)[1] = -c;
124  ++attrAxis;
125  (*attrAxis)[0] = -c;
126  (*attrAxis)[1] = -s;
127  ++attrAxis;
128  (*attrAxis)[0] = -s;
129  (*attrAxis)[1] = c;
130  ++attrAxis;
131 
132  (*attrUV)[0] = 1;
133  (*attrUV)[1] = 0;
134  ++attrUV;
135  (*attrUV)[0] = 0;
136  (*attrUV)[1] = 0;
137  ++attrUV;
138  (*attrUV)[0] = 0;
139  (*attrUV)[1] = 1;
140  ++attrUV;
141  (*attrUV)[0] = 1;
142  (*attrUV)[1] = 1;
143  ++attrUV;
144 
145  SColor4ub color = m_Particles[i].color;
146 
147  // Special case: If the blending depends on the source colour, not the source alpha,
148  // then pre-multiply by the alpha. (This is kind of a hack.)
149  if (m_Type->m_BlendFuncDst == GL_ONE_MINUS_SRC_COLOR)
150  {
151  color.R = (color.R * color.A) / 255;
152  color.G = (color.G * color.A) / 255;
153  color.B = (color.B * color.A) / 255;
154  }
155 
156  *attrColor++ = color;
157  *attrColor++ = color;
158  *attrColor++ = color;
159  *attrColor++ = color;
160  }
161 
162  m_ParticleBounds = bounds;
163 
165 }
166 
168 {
169  CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture();
170  shader->BindTexture(str_losTex, los.GetTextureSmooth());
171  shader->Uniform(str_losTransform, los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f);
172 
173  const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
174  shader->Uniform(str_sunColor, lightEnv.m_SunColor);
175  shader->Uniform(str_fogColor, lightEnv.m_FogColor);
176  shader->Uniform(str_fogParams, lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f);
177 
178  shader->BindTexture(str_baseTex, m_Type->m_Texture);
179  pglBlendEquationEXT(m_Type->m_BlendEquation);
180  glBlendFunc(m_Type->m_BlendFuncSrc, m_Type->m_BlendFuncDst);
181 }
182 
184 {
185  // Some drivers apparently don't like count=0 in glDrawArrays here,
186  // so skip all drawing in that case
187  if (m_Particles.empty())
188  return;
189 
190  u8* indexBase = m_IndexArray.Bind();
191  u8* base = m_VertexArray.Bind();
192 
193  GLsizei stride = (GLsizei)m_VertexArray.GetStride();
194 
195  shader->VertexPointer(3, GL_FLOAT, stride, base + m_AttributePos.offset);
196 
197  // Pass the sin/cos axis components as texcoords for no particular reason
198  // other than that they fit. (Maybe this should be glVertexAttrib* instead?)
199  shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, stride, base + m_AttributeUV.offset);
200  shader->TexCoordPointer(GL_TEXTURE1, 2, GL_FLOAT, stride, base + m_AttributeAxis.offset);
201 
202  shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, base + m_AttributeColor.offset);
203 
204  shader->AssertPointersBound();
205  glDrawElements(GL_TRIANGLES, (GLsizei)(m_Particles.size() * 6), GL_UNSIGNED_SHORT, indexBase);
206 
207  g_Renderer.GetStats().m_DrawCalls++;
208  g_Renderer.GetStats().m_Particles += m_Particles.size();
209 }
210 
212 {
213  m_Active = false;
214  m_Type->m_Manager.AddUnattachedEmitter(self);
215 }
216 
218 {
219  if (m_NextParticleIdx >= m_Particles.size())
220  m_Particles.push_back(particle);
221  else
222  m_Particles[m_NextParticleIdx] = particle;
223 
224  m_NextParticleIdx = (m_NextParticleIdx + 1) % m_Type->m_MaxParticles;
225 }
226 
227 void CParticleEmitter::SetEntityVariable(const std::string& name, float value)
228 {
229  m_EntityVariables[name] = value;
230 }
231 
232 
233 
235  m_Type(type)
236 {
238 }
239 
241 {
242  m_Emitter->Unattach(m_Emitter);
243 }
244 
245 void CModelParticleEmitter::SetEntityVariable(const std::string& name, float value)
246 {
247  m_Emitter->SetEntityVariable(name, value);
248 }
249 
251 {
252  return new CModelParticleEmitter(m_Type);
253 }
254 
256 {
257  // TODO: we ought to compute sensible bounds here, probably based on the
258  // current computed particle positions plus the emitter type's largest
259  // potential bounding box at the current position
260 
261  m_WorldBounds = m_Type->CalculateBounds(m_Emitter->GetPosition(), m_Emitter->GetParticleBounds());
262 }
263 
265 {
266  // TODO: do we need to do anything here?
267 
268  // This is a convenient (though possibly not particularly appropriate) place
269  // to invalidate bounds so they'll be recomputed from the recent particle data
271 }
272 
274 {
275 }
276 
278 {
279  m_Emitter->SetPosition(transform.GetTranslation());
280 }
#define u8
Definition: types.h:39
void Unattach(const CParticleEmitterPtr &self)
Stop this emitter emitting new particles, and pass responsibility for rendering to the CParticleManag...
u8 G
Definition: SColor.h:33
virtual void SetTransform(const CMatrix3D &transform)
CParticleEmitterPtr m_Emitter
bool m_Active
Whether this emitter is still emitting new particles.
Particle emitter.
void UpdateArrayData()
Update particle and vertex array data.
VertexArrayIterator< u16 > GetIterator() const
Gets the iterator over the (only) attribute in this array, i.e. a u16.
shared_ptr< CParticleEmitterType > CParticleEmitterTypePtr
CModelParticleEmitter(const CParticleEmitterTypePtr &type)
GLuint GetTextureSmooth()
Definition: LOSTexture.cpp:106
void AddParticle(const SParticle &particle)
Push a new particle onto the ring buffer.
virtual CModelAbstract * Clone() const
void AddAttribute(Attribute *attr)
Definition: VertexArray.cpp:75
void FreeBackingStore()
std::vector< SParticle > m_Particles
RGBColor m_SunColor
Definition: LightEnv.h:69
u8 B
Definition: SColor.h:34
void RenderArray(const CShaderProgramPtr &shader)
Draw the vertex array.
shared_ptr< CParticleEmitter > CParticleEmitterPtr
#define g_Renderer
Definition: Renderer.h:61
u8 A
Definition: SColor.h:35
RGBColor m_FogColor
Definition: LightEnv.h:72
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
CBoundingBoxAligned m_WorldBounds
World-space bounds of this object.
virtual void InvalidatePosition()
Mark this model&#39;s position and bone matrices, and all props&#39; positions as invalid.
void Bind(const CShaderProgramPtr &shader)
Bind rendering state (textures and blend modes).
float m_FogMax
Definition: LightEnv.h:75
VertexArray m_VertexArray
VertexArray::Attribute m_AttributeAxis
VertexArray::Attribute m_AttributeUV
VertexArray::Attribute m_AttributeColor
void SetNumVertices(size_t num)
Definition: VertexArray.cpp:64
u8 R
Definition: SColor.h:32
virtual void SetEntityVariable(const std::string &name, float value)
Called when the entity tries to set some variable to affect the display of this model and/or its chil...
VertexArrayIterator< T > GetIterator() const
VertexIndexArray m_IndexArray
virtual void ValidatePosition()
Ensure that both the transformation and the bone matrices are correct for this model and all its prop...
Abstract base class for graphical objects that are used by units, or as props attached to other CMode...
Definition: ModelAbstract.h:36
float m_FogFactor
Definition: LightEnv.h:74
virtual void InvalidateBounds()
Marks the bounds as invalid.
Maintains the LOS (fog-of-war / shroud-of-darkness) texture, used for rendering and for the minimap...
Definition: LOSTexture.h:32
CBoundingBoxAligned m_ParticleBounds
Bounding box of the current particle center points.
std::map< std::string, float > m_EntityVariables
const CMatrix3D & GetTextureMatrix()
Returns a matrix to map (x,y,z) world coordinates onto (u,v) LOS texture coordinates, in the form expected by glLoadMatrixf.
Definition: LOSTexture.cpp:184
virtual void CalcBounds()
(Re)calculates and stores any bounds or bound-dependent data for this object.
CVector3D GetTranslation() const
Definition: Matrix3D.cpp:195
Class CLightEnv: description of a lighting environment - contains all the necessary parameters for re...
Definition: LightEnv.h:36
shared_ptr< CShaderProgram > CShaderProgramPtr
size_t GetStride() const
Definition: VertexArray.h:169
VertexArray::Attribute m_AttributePos
Simulation state for a single particle.
CParticleEmitter(const CParticleEmitterTypePtr &type)
CParticleEmitterTypePtr m_Type
CParticleEmitterTypePtr m_Type
void SetEntityVariable(const std::string &name, float value)