FS2_Open
Open source remastering of the Freespace 2 engine
particle.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Volition, Inc. 1999. All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 #include "bmpman/bmpman.h"
13 #include "cmdline/cmdline.h"
14 #include "debugconsole/console.h"
15 #include "globalincs/systemvars.h"
16 #include "graphics/2d.h"
17 #include "graphics/grbatch.h"
19 #include "object/object.h"
20 #include "particle/particle.h"
21 #include "render/3d.h"
22 
23 #ifndef NDEBUG
24 #include "io/timer.h"
25 #endif
26 
27 int Num_particles = 0;
28 static SCP_vector<particle*> Particles;
29 
32 
35 
38 
39 static int Particles_enabled = 1;
40 
41 uint lastSignature = 0; // 0 is an invalid signature!
42 
45 
46 // Reset everything between levels
48 {
49  int fps;
50 
51  Particles_enabled = (Detail.num_particles > 0);
52 
53  Num_particles = 0;
54 
55  Particles.clear();
56 
57  // FIRE!!!
58  if ( Anim_bitmap_id_fire == -1 ) {
59  Anim_bitmap_id_fire = bm_load_animation( "particleexp01", &Anim_num_frames_fire, &fps, NULL, 0 );
60  }
61 
62  // Cough, cough
63  if ( Anim_bitmap_id_smoke == -1 ) {
64  Anim_bitmap_id_smoke = bm_load_animation( "particlesmoke01", &Anim_num_frames_smoke, &fps, NULL, 0 );
65  }
66 
67  // wheeze
68  if ( Anim_bitmap_id_smoke2 == -1 ) {
69  Anim_bitmap_id_smoke2 = bm_load_animation( "particlesmoke02", &Anim_num_frames_smoke2, &fps, NULL, 0 );
70  }
71 
72  // grab a vertex buffer object
73  if ( Particle_buffer_object < 0 ) {
75  }
76 
79  }
80 }
81 
82 // only call from game_shutdown()!!!
84 {
85  for (SCP_vector<particle*>::iterator p = Particles.begin(); p != Particles.end(); ++p)
86  {
87  (*p)->signature = 0;
88  delete *p;
89  }
90  Particles.clear();
91 }
92 
94 {
98 }
99 
100 DCF_BOOL2(particles, Particles_enabled, "Turns particles on/off", "Usage: particles [bool]\nTurns particle system on/off. If nothing passed, then toggles it.\n");
101 
103 
104 // Creates a single particle. See the PARTICLE_?? defines for types.
106 {
107  if ( !Particles_enabled )
108  {
109  return NULL;
110  }
111 
112  particle* new_particle = new particle();
113  int fps = 1;
114 
115  new_particle->pos = pinfo->pos;
116  new_particle->velocity = pinfo->vel;
117  new_particle->age = 0.0f;
118  new_particle->max_life = pinfo->lifetime;
119  new_particle->radius = pinfo->rad;
120  new_particle->type = pinfo->type;
121  new_particle->optional_data = pinfo->optional_data;
122  new_particle->tracer_length = pinfo->tracer_length;
123  new_particle->attached_objnum = pinfo->attached_objnum;
124  new_particle->attached_sig = pinfo->attached_sig;
125  new_particle->reverse = pinfo->reverse;
126  new_particle->particle_index = (int)Particles.size();
127 
128  switch (pinfo->type) {
129  case PARTICLE_BITMAP:
131  if (pinfo->optional_data < 0) {
132  Int3();
133  delete new_particle;
134  return NULL;
135  }
136 
137  bm_get_info( pinfo->optional_data, NULL, NULL, NULL, &new_particle->nframes, &fps );
138 
139  if ( new_particle->nframes > 1 ) {
140  // Recalculate max life for ani's
141  new_particle->max_life = i2fl(new_particle->nframes) / i2fl(fps);
142  }
143 
144  break;
145  }
146 
147  case PARTICLE_FIRE: {
148  if (Anim_bitmap_id_fire < 0) {
149  delete new_particle;
150  return NULL;
151  }
152 
153  new_particle->optional_data = Anim_bitmap_id_fire;
154  new_particle->nframes = Anim_num_frames_fire;
155 
156  break;
157  }
158 
159  case PARTICLE_SMOKE: {
160  if (Anim_bitmap_id_smoke < 0) {
161  delete new_particle;
162  return NULL;
163  }
164 
165  new_particle->optional_data = Anim_bitmap_id_smoke;
166  new_particle->nframes = Anim_num_frames_smoke;
167 
168  break;
169  }
170 
171  case PARTICLE_SMOKE2: {
172  if (Anim_bitmap_id_smoke2 < 0) {
173  delete new_particle;
174  return NULL;
175  }
176 
177  new_particle->optional_data = Anim_bitmap_id_smoke2;
178  new_particle->nframes = Anim_num_frames_smoke2;
179 
180  break;
181  }
182 
183  default:
184  new_particle->nframes = 1;
185  break;
186  }
187 
188  new_particle->signature = ++lastSignature;
189  Particles.push_back( new_particle );
190 
191 #ifndef NDEBUG
192  if (Particles.size() > (uint)Num_particles_hwm) {
193  Num_particles_hwm = (int)Particles.size();
194 
195  nprintf(("Particles", "Num_particles high water mark = %i\n", Num_particles_hwm));
196  }
197 #endif
198 
199  return Particles.back();
200 }
201 
202 particle *particle_create( vec3d *pos, vec3d *vel, float lifetime, float rad, int type, int optional_data, float tracer_length, object *objp, bool reverse )
203 {
204  particle_info pinfo;
205 
206  if ( (type < 0) || (type >= NUM_PARTICLE_TYPES) ) {
207  Int3();
208  return NULL;
209  }
210 
211  // setup old data
212  pinfo.pos = *pos;
213  pinfo.vel = *vel;
214  pinfo.lifetime = lifetime;
215  pinfo.rad = rad;
216  pinfo.type = type;
217  pinfo.optional_data = optional_data;
218 
219  // setup new data
220  pinfo.tracer_length = -1.0f;
221  if(objp == NULL)
222  {
223  pinfo.attached_objnum = -1;
224  pinfo.attached_sig = -1;
225  }
226  else
227  {
228  pinfo.attached_objnum = OBJ_INDEX(objp);
229  pinfo.attached_sig = objp->signature;
230  }
231  pinfo.reverse = reverse? 1 : 0;
232 
233  // lower level function
234  return particle_create(&pinfo);
235 }
236 
237 MONITOR( NumParticles )
238 
239 void particle_move_all(float frametime)
240 {
241  MONITOR_INC( NumParticles, Num_particles );
242 
243  if ( !Particles_enabled )
244  return;
245 
246  if ( Particles.empty() )
247  return;
248 
249  for (SCP_vector<particle*>::iterator p = Particles.begin(); p != Particles.end(); )
250  {
251  particle* part = *p;
252  if (part->age == 0.0f) {
253  part->age = 0.00001f;
254  } else {
255  part->age += frametime;
256  }
257 
258  bool remove_particle = false;
259 
260  // if its time expired, remove it
261  if (part->age > part->max_life) {
262  // special case, if max_life is 0 then we want it to render at least once
263  if ( (part->age > frametime) || (part->max_life > 0.0f) ) {
264  remove_particle = true;
265  }
266  }
267 
268  // if the particle is attached to an object which has become invalid, kill it
269  if (part->attached_objnum >= 0) {
270  // if the signature has changed, or it's bogus, kill it
271  if ( (part->attached_objnum >= MAX_OBJECTS) || (part->attached_sig != Objects[part->attached_objnum].signature) ) {
272  remove_particle = true;
273  }
274  }
275 
276  if (remove_particle)
277  {
278  part->signature = 0;
279  delete part;
280 
281  // if we're sitting on the very last particle, popping-back will invalidate the iterator!
282  if (p + 1 == Particles.end())
283  {
284  Particles.pop_back();
285  break;
286  }
287  else
288  {
289  *p = Particles.back();
290  Particles.pop_back();
291  continue;
292  }
293  }
294 
295  // move as a regular particle
296  vm_vec_scale_add2( &part->pos, &part->velocity, frametime );
297 
298  // next particle
299  ++p;
300  }
301 }
302 
303 // kill all active particles
305 {
306  // kill all active particles
307  Num_particles = 0;
308  Num_particles_hwm = 0;
309 
310  for (SCP_vector<particle*>::iterator p = Particles.begin(); p != Particles.end(); ++p)
311  {
312  (*p)->signature = 0;
313  delete *p;
314  }
315  Particles.clear();
316 }
317 
318 MONITOR( NumParticlesRend )
319 
320 static float get_current_alpha(vec3d *pos)
321 {
322  float dist;
323  float alpha;
324 
325  const float inner_radius = 30.0f;
326  const float magic_num = 2.75f;
327 
328  // determine what alpha to draw this bitmap with
329  // higher alpha the closer the bitmap gets to the eye
330  dist = vm_vec_dist_quick(&Eye_position, pos);
331 
332  // if the point is inside the inner radius, alpha is based on distance to the player's eye,
333  // becoming more transparent as it gets close
334  if (dist <= inner_radius) {
335  // alpha per meter between the magic # and the inner radius
336  alpha = 0.99999f / (inner_radius - magic_num);
337 
338  // above value times the # of meters away we are
339  alpha *= (dist - magic_num);
340  return (alpha < 0.05f) ? 0.0f : alpha;
341  }
342 
343  return 0.99999f;
344 }
345 
347 {
348  ubyte flags;
349  float pct_complete;
350  float alpha;
351  vertex pos;
352  vec3d ts, te, temp;
353  int rotate = 1;
354  int framenum, cur_frame;
355  bool render_batch = false;
357 
358  if ( !Particles_enabled )
359  return;
360 
361  MONITOR_INC( NumParticlesRend, Num_particles );
362 
363  if ( Particles.empty() )
364  return;
365 
366  for (SCP_vector<particle*>::iterator p = Particles.begin(); p != Particles.end(); ++p) {
367  particle* part = *p;
368  // skip back-facing particles (ripped from fullneb code)
369  // Wanderer - add support for attached particles
370  vec3d p_pos;
371  if (part->attached_objnum >= 0) {
372  vm_vec_unrotate(&p_pos, &part->pos, &Objects[part->attached_objnum].orient);
373  vm_vec_add2(&p_pos, &Objects[part->attached_objnum].pos);
374  } else {
375  p_pos = part->pos;
376  }
377 
378  if ( vm_vec_dot_to_point(&Eye_matrix.vec.fvec, &Eye_position, &p_pos) <= 0.0f ) {
379  continue;
380  }
381 
382  // calculate the alpha to draw at
383  alpha = get_current_alpha(&p_pos);
384 
385  // if it's transparent then just skip it
386  if (alpha <= 0.0f) {
387  continue;
388  }
389 
390  // make sure "rotate" is enabled for this particle
391  rotate = 1;
392 
393  // if this is a tracer style particle, calculate tracer vectors
394  if (part->tracer_length > 0.0f) {
395  ts = p_pos;
396  temp = part->velocity;
397  vm_vec_normalize_quick(&temp);
398  vm_vec_scale_add(&te, &ts, &temp, part->tracer_length);
399 
400  // don't bother rotating
401  rotate = 0;
402  }
403 
404  // rotate the vertex
405  if (rotate) {
406  flags = g3_rotate_vertex( &pos, &p_pos );
407 
408  if ( flags ) {
409  continue;
410  }
411 
412  if (!Cmdline_nohtl)
413  g3_transfer_vertex(&pos, &p_pos);
414  }
415 
416  // pct complete for the particle
417  pct_complete = part->age / part->max_life;
418 
419  // figure out which frame we should be using
420  if (part->nframes > 1) {
421  framenum = fl2i(pct_complete * part->nframes + 0.5);
422  CLAMP(framenum, 0, part->nframes-1);
423 
424  cur_frame = part->reverse ? (part->nframes - framenum - 1) : framenum;
425  } else {
426  cur_frame = 0;
427  }
428 
429  if (part->type == PARTICLE_DEBUG) {
430  gr_set_color( 255, 0, 0 );
431  g3_draw_sphere_ez( &p_pos, part->radius );
432  } else {
433  framenum = part->optional_data;
434 
435  Assert( cur_frame < part->nframes );
436 
437  // if this is a tracer style particle
438  if (part->tracer_length > 0.0f) {
439  batch_add_laser( framenum + cur_frame, &ts, part->radius, &te, part->radius );
440  }
441  // draw as a regular bitmap
442  else {
443  batch_add_bitmap( framenum + cur_frame, tmap_flags | TMAP_FLAG_VERTEX_GEN, &pos, part->particle_index % 8, part->radius, alpha );
444  }
445 
446  render_batch = true;
447  }
448  }
449 
450  profile_begin("Batch Render");
451  if (render_batch) {
454  }
455  profile_end("Batch Render");
456 }
457 
458 
459 
460 
461 //============================================================================
462 //============== HIGH-LEVEL PARTICLE SYSTEM CREATION CODE ====================
463 //============================================================================
464 
465 // Use a structure rather than pass a ton of parameters to particle_emit
466 /*
467 typedef struct particle_emitter {
468  int num_low; // Lowest number of particles to create
469  int num_high; // Highest number of particles to create
470  vec3d pos; // Where the particles emit from
471  vec3d vel; // Initial velocity of all the particles
472  float lifetime; // How long the particles live
473  vec3d normal; // What normal the particle emit arond
474  float normal_variance; // How close they stick to that normal 0=good, 1=360 degree
475  float min_vel; // How fast the slowest particle can move
476  float max_vel; // How fast the fastest particle can move
477  float min_rad; // Min radius
478  float max_rad; // Max radius
479 } particle_emitter;
480 */
481 
482 static inline int get_percent(int count)
483 {
484  if (count == 0)
485  return 0;
486 
487  // this should basically return a scale like:
488  // 50, 75, 100, 125, 150, ...
489  // based on value of 'count' (detail level)
490  return ( 50 + (25 * (count-1)) );
491 }
492 
493 // Creates a bunch of particles. You pass a structure
494 // rather than a bunch of parameters.
495 void particle_emit( particle_emitter *pe, int type, int optional_data, float range )
496 {
497  int i, n;
498 
499  if ( !Particles_enabled )
500  return;
501 
502  int n1, n2;
503 
504  // Account for detail
505  int percent = get_percent(Detail.num_particles);
506 
507  //Particle rendering drops out too soon. Seems to be around 150 m. Is it detail level controllable? I'd like it to be 500-1000
508  float min_dist = 125.0f;
509  float dist = vm_vec_dist_quick( &pe->pos, &Eye_position ) / range;
510  if ( dist > min_dist ) {
511  percent = fl2i( i2fl(percent)*min_dist / dist );
512  if ( percent < 1 ) {
513  return;
514  }
515  }
516  //mprintf(( "Dist = %.1f, percent = %d%%\n", dist, percent ));
517 
518  n1 = (pe->num_low*percent)/100;
519  n2 = (pe->num_high*percent)/100;
520 
521  // How many to emit?
522  n = (rand() % (n2-n1+1)) + n1;
523 
524  if ( n < 1 ) return;
525 
526 
527  for (i=0; i<n; i++ ) {
528  // Create a particle
529  vec3d tmp_vel;
530  vec3d normal; // What normal the particle emit arond
531 
532  float radius = (( pe->max_rad - pe->min_rad ) * frand()) + pe->min_rad;
533 
534  float speed = (( pe->max_vel - pe->min_vel ) * frand()) + pe->min_vel;
535 
536  float life = (( pe->max_life - pe->min_life ) * frand()) + pe->min_life;
537 
538  normal.xyz.x = pe->normal.xyz.x + (frand()*2.0f - 1.0f)*pe->normal_variance;
539  normal.xyz.y = pe->normal.xyz.y + (frand()*2.0f - 1.0f)*pe->normal_variance;
540  normal.xyz.z = pe->normal.xyz.z + (frand()*2.0f - 1.0f)*pe->normal_variance;
541  vm_vec_normalize_safe( &normal );
542  vm_vec_scale_add( &tmp_vel, &pe->vel, &normal, speed );
543 
544  particle_create( &pe->pos, &tmp_vel, life, radius, type, optional_data );
545  }
546 }
int attached_objnum
Definition: particle.h:89
#define OGL_EXT_GEOMETRY_SHADER4
float vm_vec_dot_to_point(const vec3d *dir, const vec3d *p1, const vec3d *p2)
Definition: vecmat.cpp:1348
int i
Definition: multi_pxo.cpp:466
float tracer_length
Definition: particle.h:88
#define TMAP_FLAG_SOFT_QUAD
Definition: tmapper.h:79
int optional_data
Definition: particle.h:84
void vm_vec_scale_add(vec3d *dest, const vec3d *src1, const vec3d *src2, float k)
Definition: vecmat.cpp:266
void particle_close()
Definition: particle.cpp:83
float max_life
Definition: particle.h:81
int Anim_num_frames_smoke
Definition: particle.cpp:34
ubyte g3_transfer_vertex(vertex *dest, const vec3d *src)
Definition: 3dmath.cpp:84
float vm_vec_normalize_quick(vec3d *src)
Definition: vecmat.cpp:529
float age
Definition: particle.h:80
int Particle_buffer_object
Definition: particle.cpp:43
Assert(pm!=NULL)
Definition: pstypes.h:88
ubyte reverse
Definition: particle.h:91
void particle_kill_all()
Definition: particle.cpp:304
int type
Definition: particle.h:83
matrix Eye_matrix
Definition: 3dsetup.cpp:26
int bm_get_info(int handle, int *w, int *h, ubyte *flags, int *nframes, int *fps)
Gets info on the bitmap indexed by handle.
Definition: bmpman.cpp:769
struct vec3d::@225::@227 xyz
#define TMAP_HTL_3D_UNLIT
Definition: tmapper.h:63
GLclampf f
Definition: Glext.h:7097
#define MAX_OBJECTS
Definition: globals.h:83
#define TMAP_FLAG_VERTEX_GEN
Definition: tmapper.h:92
void vm_vec_scale_add2(vec3d *dest, const vec3d *src, float k)
Definition: vecmat.cpp:284
float batch_add_laser(int texture, vec3d *p0, float width1, vec3d *p1, float width2, int r, int g, int b)
Definition: grbatch.cpp:698
vec3d pos
Definition: particle.h:78
object * objp
Definition: lua.cpp:3105
void profile_begin(const char *name)
Definition: profiling.cpp:80
#define PARTICLE_BITMAP
Definition: particle.h:49
#define Int3()
Definition: pstypes.h:292
vec3d pos
Definition: object.h:152
DCF_BOOL2(particles, Particles_enabled,"Turns particles on/off","Usage: particles [bool]\nTurns particle system on/off. If nothing passed, then toggles it.\n")
int Anim_num_frames_smoke2
Definition: particle.cpp:37
void particle_emit(particle_emitter *pe, int type, int optional_data, float range)
Definition: particle.cpp:495
int Num_particles
Definition: particle.cpp:27
int Anim_bitmap_id_fire
Definition: particle.cpp:30
int signature
Definition: object.h:145
GLenum type
Definition: Gl.h:1492
int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *keyframe, int can_drop_frames, int dir_type)
Loads a bitmap sequance so we can draw with it.
Definition: bmpman.cpp:1420
#define CLAMP(x, min, max)
Definition: pstypes.h:488
void gr_set_color(int r, int g, int b)
Definition: 2d.cpp:1188
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
int optional_data
Definition: particle.h:67
GLenum GLint * range
Definition: Glext.h:7096
void batch_render_all(int stream_buffer)
Definition: grbatch.cpp:1124
void vm_vec_add2(vec3d *dest, const vec3d *src)
Definition: vecmat.cpp:178
int Cmdline_nohtl
Definition: cmdline.cpp:438
#define PARTICLE_FIRE
Definition: particle.h:51
int nframes
Definition: particle.h:85
struct matrix::@228::@230 vec
unsigned int uint
Definition: pstypes.h:64
#define nprintf(args)
Definition: pstypes.h:239
detail_levels Detail
Definition: systemvars.cpp:478
int Num_particles_hwm
Definition: particle.cpp:102
#define PARTICLE_SMOKE
Definition: particle.h:52
int Anim_num_frames_fire
Definition: particle.cpp:31
vec3d vel
Definition: particle.h:63
int Geometry_shader_buffer_object
Definition: particle.cpp:44
ubyte g3_rotate_vertex(vertex *dest, const vec3d *src)
Definition: 3dmath.cpp:97
#define MONITOR(function_name)
Definition: pstypes.h:454
vec3d pos
Definition: particle.h:62
void particle_page_in()
Definition: particle.cpp:93
float vm_vec_normalize_safe(vec3d *v)
Definition: vecmat.cpp:471
vec3d * vm_vec_unrotate(vec3d *dest, const vec3d *src, const matrix *m)
Definition: vecmat.cpp:959
#define Is_Extension_Enabled(x)
object Objects[MAX_OBJECTS]
Definition: object.cpp:62
GLclampd n
Definition: Glext.h:7286
unsigned char ubyte
Definition: pstypes.h:62
#define MONITOR_INC(function_name, inc)
Definition: pstypes.h:457
#define OBJ_INDEX(objp)
Definition: object.h:235
matrix orient
Definition: object.h:153
GLbitfield flags
Definition: Glext.h:6722
float tracer_length
Definition: particle.h:70
int Anim_bitmap_id_smoke
Definition: particle.cpp:33
typedef void(APIENTRY *PFNGLARRAYELEMENTEXTPROC)(GLint i)
struct particle particle
void particle_move_all(float frametime)
Definition: particle.cpp:239
vec3d Eye_position
Definition: 3dsetup.cpp:27
void profile_end(const char *name)
Definition: profiling.cpp:130
int attached_sig
Definition: particle.h:72
int Anim_bitmap_id_smoke2
Definition: particle.cpp:36
float vm_vec_dist_quick(const vec3d *v0, const vec3d *v1)
Definition: vecmat.cpp:417
float frand()
Return random value in range 0.0..1.0- (1.0- means the closest number less than 1.0)
Definition: floating.cpp:35
void particle_render_all()
Definition: particle.cpp:346
#define fl2i(fl)
Definition: floating.h:33
void bm_page_in_texture(int bitmapnum, int nframes)
Marks a texture as being used for this level.
Definition: bmpman.cpp:2462
#define PARTICLE_BITMAP_PERSISTENT
Definition: particle.h:54
int batch_add_bitmap(int texture, int tmap_flags, vertex *pnt, int orient, float rad, float alpha, float depth)
Definition: grbatch.cpp:724
float lifetime
Definition: particle.h:64
GLfloat GLfloat p
Definition: Glext.h:8373
An overhauled/updated debug console to allow monitoring, testing, and general debugging of new featur...
int particle_index
Definition: particle.h:92
#define TMAP_FLAG_TEXTURED
Definition: tmapper.h:36
float normal_variance
Definition: particle.h:114
hull_check pos
Definition: lua.cpp:5050
float radius
Definition: particle.h:82
#define i2fl(i)
Definition: floating.h:32
void geometry_batch_render(int stream_buffer)
Definition: grbatch.cpp:1099
GLint GLsizei count
Definition: Gl.h:1491
#define NUM_PARTICLE_TYPES
Definition: particle.h:57
void particle_init()
Definition: particle.cpp:47
#define gr_create_stream_buffer
Definition: 2d.h:886
vec3d velocity
Definition: particle.h:79
int temp
Definition: lua.cpp:4996
int attached_sig
Definition: particle.h:90
float rad
Definition: particle.h:65
bool Cmdline_no_geo_sdr_effects
Definition: cmdline.cpp:451
int g3_draw_sphere_ez(const vec3d *pnt, float rad)
Definition: 3ddraw.cpp:498
uint lastSignature
Definition: particle.cpp:41
particle * particle_create(particle_info *pinfo)
Definition: particle.cpp:105
uint signature
Definition: particle.h:94
GLclampf GLclampf GLclampf alpha
Definition: Glext.h:5177
#define PARTICLE_SMOKE2
Definition: particle.h:53
ubyte reverse
Definition: particle.h:73
int attached_objnum
Definition: particle.h:71
#define PARTICLE_DEBUG
Definition: particle.h:48