JNR
fxParticleSystem.cpp
1 /*
2 
3 Jump'n'Run Engine
4 http://www.atanaslaskov.com/jnr/
5 
6 BSD LICENSE
7 Copyright (c) 2007-2013, Atanas Laskov
8 All rights reserved.
9 
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are met:
12 1. Redistributions of source code must retain the above copyright notice,
13 this list of conditions and the following disclaimer.
14 2. Redistributions in binary form must reproduce the above copyright notice,
15 this list of conditions and the following disclaimer in the documentation
16 and/or other materials provided with the distribution.
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL ATANAS LASKOV BE LIABLE FOR ANY
21 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 */
29 
30 //
31 // FILE: fxParticleSystem.cpp
32 // Particle System
33 //
34 // Copyright (C) 2007-2013 Atanas Laskov, <latanas@gmail.com>
35 //
36 #include "stdafx.h"
37 #include "Core.h"
38 
39 double fxParticleSystem::_dParticleDensity = 1;
40 
41 laPoint2* fxParticleSystem::_arParticleUV = NULL;
42 unsigned fxParticleSystem::_nCntMax = 0;
43 
44 fxParticleSystem_params::fxParticleSystem_params()
45 {
46  gen_maxcnt = 32;
47  ang = ang_var = 0;
48 
49  gen_percent = 100;
50  gen_htime = 0;
51  gen_loop = M_TRUE;
52  gen_linearEmitter = M_FALSE;
53 
54  p_rgb = laColor(255,255,255);
55  p_alpha_filter = 0.005;
56  p_size = M_UNIT;
57  p_speed = M_UNIT;
58  p_mass = 0;
59  p_ttl = 1;
60  pv_ramble = 0;
61  pv_speed = 0;
62 
63  pv_size = 0;
64  pv_speed = 0;
65  pv_mass = 0;
66  pv_ttl = 0;
67 }
68 
69 fxParticleSystem::fxParticleSystem(void)
70 {
71  _bEnabled = M_TRUE;
72  _bGenEnabled = M_TRUE;
73 
74  _particles = NULL;
75  _arParticlePos = NULL;
76  _arParticleSize = NULL;
77  _arParticleColors = NULL;
78 
79  _nCnt = _nValidParticles = 0;
80 }
81 
82 fxParticleSystem::~fxParticleSystem(void)
83 {
84  if(_particles) delete [] _particles;
85  if(_arParticlePos) delete [] _arParticlePos;
86  if(_arParticleSize) delete [] _arParticleSize;
87  if(_arParticleColors) delete [] _arParticleColors;
88 }
89 
90 // Create particle sysyem
91 //
92 void fxParticleSystem::create(fxParticleSystem_params &p)
93 {
94  // Allocate memory for the particle Fx
95  //
96  _nCnt = (unsigned)(p.gen_maxcnt*_dParticleDensity);
97 
98  _particles = new fxParticleSystem_particle [_nCnt];
99  _arParticlePos = new laPoint3 [_nCnt];
100  _arParticleSize = new double [_nCnt];
101  _arParticleColors = new laColor [_nCnt*4];
102 
103  // Particle UVs are in a shared array fxParticleSystem::_arParticleUV; Re-allocate if size exceeds the max
104  //
105  if( _nCntMax < _nCnt )
106  {
107  if(_arParticleUV) delete [] _arParticleUV;
108  _arParticleUV = new laPoint2 [_nCnt*4];
109  _nCntMax = _nCnt;
110 
111  for(unsigned i=0; i<_nCntMax; i++)
112  {
113  _arParticleUV[i*4 + 0] = laPoint2(0, 0);
114  _arParticleUV[i*4 + 1] = laPoint2(0, 1);
115  _arParticleUV[i*4 + 2] = laPoint2(1, 1);
116  _arParticleUV[i*4 + 3] = laPoint2(1, 0);
117  }
118  }
119 
120  // Misc
121  //
122  if(p.ang_var<1) p.ang_var = 1;
123 
124  _nSimGenCnt = (unsigned)((_nCnt/100.0)*p.gen_percent);
125 
126  _nGenCnt = _nSimGenCnt;
127  _bHold = M_FALSE;
128 
129  parameters = p;
130  strcpy(parameters.p_texture, p.p_texture);
131 
132  lnEmitter.build_vec( *this, laPoint3(parameters.gen_emitter_sz.x(), parameters.gen_emitter_sz.y(), 0) );
133 }
134 
135 // Draw
136 //
137 #include <GL\GL.h>
138 
139 void fxParticleSystem::draw(laRenderer *r)
140 {
141  PROFILE_REN(fxParticleSystem_draw);
142  if(!_bEnabled) return;
143 
144  // Particles have a custom alpha filter, so override the default
145  double dOldFilter = r->modeBlendFilter(parameters.p_alpha_filter);
146 
147  // z-Buffer Setup:
148  // Enable the depth test (i.e. don't draw if there's stuff in front of the FX),
149  // Don't write to the depth buffer (so particles don't obscure each other, which causes artefacts)
150  //
151  // Particles are drawn *after all geometry in the level*, so this setup should be OK in most cases;
152  // ( But if 2 particle FXs overlap, the color won't be mixed correctly )
153  //
154  glEnable(GL_DEPTH_TEST);
155  glDepthMask(GL_FALSE);
156 
157  // Lazy-load the particle texture
158  //
159  if( !texSprite.id() ) texSprite.load(parameters.p_texture, M_TEX_TMAP);
160  texSprite.use();
161 
162  // Render particles
163  // NOTE: Do not translate here, in most cases the partilce Fx can be positioned together with the object
164  // NOTE: Translating the particle Fx moves all existing particles, whereas changing emitter pos - only newly generated
165  //
166  //r->transPush();
167  //r->transTranslate( pos );
168  r->vquadsMakeBillboards(_nValidParticles, _arParticlePos, _arParticleSize);
169  r->vquadsDraw(_nValidParticles, _arParticleUV, _arParticleColors, M_TRUE, M_TRUE);
170  //r->transPop();
171 
172  // Restore stuff
173  //
174  glEnable(GL_DEPTH_TEST);
175  glDepthMask(GL_TRUE);
176  r->modeBlendFilter( dOldFilter );
177 }
178 
179 // Animate
180 //
181 void fxParticleSystem::animate(laTimer &t)
182 {
183  //PROFILE_REN(fxParticleSystem_animate);
184  static fxParticleSystem_particle *p;
185  static laPoint3 *ppos;
186  static double *psz;
187  static laColor *pcol;
188 
189  _nValidParticles = 0;
190 
191  if(!_bEnabled) return;
192 
193  if(_bHold)
194  {
195  if( _dHoldLeft<t.delta() )
196  {
197  _bHold = M_FALSE;
198  _nGenCnt = _nSimGenCnt;
199  }
200  else _dHoldLeft -= t.delta();
201  }
202 
203  //Adaptive generation (compensate for low FPS)
204  /*if((parameters.gen_htime*2)<(t.delta()))
205  {
206  parameters[1]en_percent += 1;
207  parameters.gen_htime += (t.delta()-parameters.gen_htime)*(parameters.gen_htime/t.delta());
208 
209  _nSimGenCnt = (unsigned)((_nCnt/100.0)*parameters[1]en_percent);
210  }*/
211 
212  // Animate each particle
213  //
214  for(unsigned i=0; i<_nCnt; i++)
215  {
216  p = _particles + i;
217  ppos = _arParticlePos + i;
218  psz = _arParticleSize + i;
219  pcol = _arParticleColors + i*4;
220 
221  // Animate an existing particle
222  //
223  if( p->bValid )
224  {
225  // Check if particle expires
226  //
227  if( p->dTTL < t.delta() ) {
228  p->bValid = M_FALSE;
229  continue;
230  }
231  else _nValidParticles++;
232 
233  // Update TTL, color and size
234  //
235  p->dTTL -= t.delta();
236  pcol[0][3] = pcol[1][3] = pcol[2][3] = pcol[3][3] = (char)( (p->dTTL/p->dTTL_Total) * parameters.p_rgb[3] );
237 
238  // Apply velocity
239  //
240  *ppos += t.delta()*p->velocity;
241 
242  // Ramble effect
243  //
244  p->velocity[0] += t.delta()*((rand()%1000)*(parameters.pv_ramble/1000.0) - parameters.pv_ramble/2);
245  p->velocity[1] += t.delta()*((rand()%1000)*(parameters.pv_ramble/1000.0) - parameters.pv_ramble/2);
246 
247  // Gravitation effect
248  //
249  p->velocity[1] += parameters.p_mass*t.delta();
250 
251  }
252 
253  //Create a new particle if there is generator quota
254  //
255  else if( (_nGenCnt>0) && _bGenEnabled )
256  {
257  //Disable recreation if this the emitter is not supposed to loop
258  if( (p->bCreatedOnce) && (!parameters.gen_loop)) continue;
259 
260  // Randomised start position
261  //
262  if(parameters.gen_linearEmitter) lnEmitter.at( (rand()%100)/200.0 + 0.25, ppos );
263  else *ppos = *this;
264 
265  *ppos += ((rand()%100)/100.0 - 0.5) * parameters.gen_emitter_sz * 0.5;
266 
267  //prect->pt[0][0] += ((rand()%100)/100.0 - 0.5) * parameters->gen_emitter_sz.x();
268  //prect->pt[0][1] += ((rand()%100)/100.0 - 0.5) * parameters->gen_emitter_sz.y();
269  //prect->pt[0][2] += ((rand()%100)/100.0 - 0.5) * parameters->gen_emitter_sz.z();
270 
271  //Randomised emission angle
272  //
273  laPhForce f;
274  f.buildFromAngle( parameters.ang + (rand()%1000)*(parameters.ang_var/1000.0)-parameters.ang_var/2);
275 
276  double speed_randomness = (rand()%1000)*(parameters.pv_speed/1000.0);
277 
278  p->velocity[0] = f.compX()*(parameters.p_speed+speed_randomness);
279  p->velocity[1] = f.compY()*(parameters.p_speed+speed_randomness);
280 
281  //Randomised size
282  //
283  *psz = parameters.p_size;
284  if(parameters.pv_size!=0) *psz += (rand()%1000)*(parameters.pv_size/1000);
285 
286  // Color
287  //
288  pcol[0] = pcol[1] = pcol[2] = pcol[3] = parameters.p_rgb;
289 
290  //Randomised TTL
291  //
292  double ttl_randomness = (rand()%1000)*(parameters.pv_ttl/1000.0)-parameters.pv_ttl/2;
293  p->dTTL_Total = p->dTTL = parameters.p_ttl + ttl_randomness;
294 
295  // Spawn it
296  //
297  p->bValid = p->bCreatedOnce = M_TRUE;
298  _nGenCnt--;
299  }
300  }
301 
302  if( (_nGenCnt<=0) && (!_bHold))
303  {
304  _bHold = M_TRUE;
305  _dHoldLeft = parameters.gen_htime;
306  }
307 
308 }
309 
310 void fxParticleSystem::destroy()
311 {
312  ASSERT(M_FALSE, "Not implemented");
313 }
314 
315 void fxParticleSystem::reset()
316 {
317  for(unsigned i=0; i<_nCnt; i++)
318  {
319  _particles[i].bValid = M_FALSE;
320  _particles[i].bCreatedOnce = M_FALSE;
321  }
322 }
323 
324 void fxParticleSystem_params::load(class laFileParser *fp)
325 {
326  //laPropertyList pl;
327  //fp.readObj(&pl, M_FALSE);
328 
329  char junk[128];
330 
331  //Emitter
332  try{ fp->readSectionSeparator(junk); }catch(laError& ){ throw laError("Section [EMITTER] expected but not found (while parsing '%s')", fp->getFileName()); }
333  _strupr(junk);
334  if(strcmp(junk, "EMITTER")) throw laError("Section [EMITTER] expected, [%s] found (while parsing '%s')", junk, fp->getFileName());
335 
336  fp->readDouble(&ang);
337  fp->readDouble(&ang_var);
338 
339  fp->readObj(&gen_emitter_sz);
340 
341  gen_emitter_sz[0] *= M_UNIT;
342  gen_emitter_sz[1] *= M_UNIT;
343  gen_emitter_sz[2] *= M_UNIT;
344 
345  fp->readBool(&gen_loop);
346  fp->readDouble(&gen_maxcnt);
347  fp->readDouble(&gen_percent);
348  fp->readDouble(&gen_htime);
349 
350  //Particle
351  try{ fp->readSectionSeparator(junk); }catch(laError& ){ throw laError("Section [PARTICLE] expected but not found (while parsing '%s')", fp->getFileName()); }
352  _strupr(junk);
353  if(strcmp(junk, "PARTICLE")) throw laError("Section [PARTICLE] expected, [%s] found (while parsing '%s')", junk, fp->getFileName());
354 
355  fp->readText(p_texture);
356  fp->readObj(&p_rgb);
357  fp->readDouble(&p_alpha_filter);
358 
359  fp->readDouble(&p_size);
360  fp->readDouble(&p_speed);
361  fp->readDouble(&p_mass);
362  fp->readDouble(&p_ttl);
363 
364  p_size *= M_UNIT;
365  p_speed *= M_UNIT;
366 
367  //Varation
368  try{ fp->readSectionSeparator(junk); }catch(laError& ){ throw laError("Section [VARATION] expected but not found (while parsing '%s')", fp->getFileName()); }
369  _strupr(junk);
370  if(strcmp(junk, "VARATION")) throw laError("Section [VARATION] expected, [%s] found (while parsing '%s')", junk, fp->getFileName());
371 
372  fp->readDouble(&pv_size);
373  fp->readDouble(&pv_speed);
374  fp->readDouble(&pv_mass);
375  fp->readDouble(&pv_ttl);
376  fp->readDouble(&pv_ramble);
377 
378  if(pv_ttl>=p_ttl) pv_ttl = p_ttl;
379 
380  pv_size *= M_UNIT;
381  pv_speed *= M_UNIT;
382  pv_ramble *= M_UNIT;
383 }
384 
385 
386 void fxParticleSystem::ctlRewind(unsigned nframes, double fps)
387 {
388  laTimer t;
389  t.frameForce(fps);
390  for(unsigned i=0; i<nframes; i++) animate(t);
391 }
392 
393 void fxParticleSystem::load(class laFileParser *fp)
394 {
395  laFileParser fxp;
396  fxParticleSystem_params parameters;
397  char strFilePath[128]=M_DIR_FX;
398  char strFxFile[128];
399  laPoint3 pos, size;
400  double ang;
401 
402  //Read the .fx file name and the positional information
403  try{
404  fp->readText(strFxFile, M_FALSE);
405  fp->readObj(&pos, M_FALSE);
406  fp->readObj(&size, M_FALSE);
407  fp->readDouble(&ang, M_FALSE);
408  }
409  catch(laError& )
410  {
411  throw laError("FX description appears to be corrupted");
412  }
413 
414  //Load the .fx file into a fxParticleSystem_params structure
415  strcat(strFilePath, strFxFile);
416  fxp.fileOpen(strFilePath);
417  fxp.readObj(&parameters, M_FALSE);
418  fxp.fileClose();
419 
420  x( pos.x()*M_UNIT );
421  y( pos.y()*M_UNIT );
422  z( pos.z()*M_UNIT );
423 
424  //Create the fx spray
425  create(parameters);
426 }
427 
428 void fxParticleSystem::copyStyle(fxParticleSystem* pSource)
429 {
430  parameters.p_rgb = pSource->parameters.p_rgb;
431  texSprite = pSource->texSprite;
432 
433 }
#define M_UNIT
Unit of 1 meter.
2D Point
Definition: laPoint_novec.h:41
#define M_DIR_FX
Visual effects directory.
: Force Simulation
Definition: laPhForce.h:42
virtual void vquadsDraw(unsigned nQuads, laPoint2 *ar_uv=NULL, laColor *ar_color=NULL, M_BOOL bBillboards=M_FALSE, M_BOOL bUseColorArrays=M_FALSE)=0
Draw an array of VQ, starting with the psecified pointers (or the first VQ if null) ...
Virtual interface for the Engine graphics renderer.
Definition: laRenderer.h:98
virtual double modeBlendFilter(const double &dMinVisisbleAlpha)=0
Set a "blend filter" (cutting out elements with opacity lower than dMinVisisbleAlpha) ...
#define M_TEX_TMAP
Transparent texture with an alpha channel.
void vquadsMakeBillboards(unsigned nQuads, laPoint3 *ar_pos, double *ar_size)
Use camera data to create an array of camera-facing VQ (handy for particle effects) ...
Definition: laRenderer.h:303
Error handling class.
Definition: laError.h:46
File Parser.
Definition: laFileParser.h:41