doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
SmokeParticles.cpp
Go to the documentation of this file.
1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31 
32 #include "Game_local.h"
33 
34 static const char *smokeParticle_SnapshotName = "_SmokeParticle_Snapshot_";
35 
36 /*
37 ================
38 idSmokeParticles::idSmokeParticles
39 ================
40 */
42  initialized = false;
43  memset( &renderEntity, 0, sizeof( renderEntity ) );
44  renderEntityHandle = -1;
45  memset( smokes, 0, sizeof( smokes ) );
46  freeSmokes = NULL;
47  numActiveSmokes = 0;
49 }
50 
51 /*
52 ================
53 idSmokeParticles::Init
54 ================
55 */
56 void idSmokeParticles::Init( void ) {
57  if ( initialized ) {
58  Shutdown();
59  }
60 
61  // set up the free list
62  for ( int i = 0; i < MAX_SMOKE_PARTICLES-1; i++ ) {
63  smokes[i].next = &smokes[i+1];
64  }
65  smokes[MAX_SMOKE_PARTICLES-1].next = NULL;
66  freeSmokes = &smokes[0];
67  numActiveSmokes = 0;
68 
70 
71  memset( &renderEntity, 0, sizeof( renderEntity ) );
72 
79 
81  renderEntity.hModel->InitEmpty( smokeParticle_SnapshotName );
82 
83  // we certainly don't want particle shadows
85 
86  // huge bounds, so it will be present in every world area
87  renderEntity.bounds.AddPoint( idVec3(-100000, -100000, -100000) );
88  renderEntity.bounds.AddPoint( idVec3( 100000, 100000, 100000) );
89 
91  // add to renderer list
93 
95 
96  initialized = true;
97 }
98 
99 /*
100 ================
101 idSmokeParticles::Shutdown
102 ================
103 */
105  // make sure the render entity is freed before the model is freed
106  if ( renderEntityHandle != -1 ) {
108  renderEntityHandle = -1;
109  }
110  if ( renderEntity.hModel != NULL ) {
113  }
114  initialized = false;
115 }
116 
117 /*
118 ================
119 idSmokeParticles::FreeSmokes
120 ================
121 */
123  for ( int activeStageNum = 0; activeStageNum < activeStages.Num(); activeStageNum++ ) {
124  singleSmoke_t *smoke, *next, *last;
125 
126  activeSmokeStage_t *active = &activeStages[activeStageNum];
127  const idParticleStage *stage = active->stage;
128 
129  for ( last = NULL, smoke = active->smokes; smoke; smoke = next ) {
130  next = smoke->next;
131 
132 #ifdef _D3XP
133  float frac;
134 
135  if ( smoke->timeGroup ) {
136  frac = (float)( gameLocal.fast.time - smoke->privateStartTime ) / ( stage->particleLife * 1000 );
137  }
138  else {
139  frac = (float)( gameLocal.slow.time - smoke->privateStartTime ) / ( stage->particleLife * 1000 );
140  }
141 #else
142  float frac = (float)( gameLocal.time - smoke->privateStartTime ) / ( stage->particleLife * 1000 );
143 #endif
144  if ( frac >= 1.0f ) {
145  // remove the particle from the stage list
146  if ( last != NULL ) {
147  last->next = smoke->next;
148  } else {
149  active->smokes = smoke->next;
150  }
151  // put the particle on the free list
152  smoke->next = freeSmokes;
153  freeSmokes = smoke;
154  numActiveSmokes--;
155  continue;
156  }
157 
158  last = smoke;
159  }
160 
161  if ( !active->smokes ) {
162  // remove this from the activeStages list
163  activeStages.RemoveIndex( activeStageNum );
164  activeStageNum--;
165  }
166  }
167 }
168 
169 /*
170 ================
171 idSmokeParticles::EmitSmoke
172 
173 Called by game code to drop another particle into the list
174 ================
175 */
176 bool idSmokeParticles::EmitSmoke( const idDeclParticle *smoke, const int systemStartTime, const float diversity, const idVec3 &origin, const idMat3 &axis, int timeGroup /*_D3XP*/ ) {
177  bool continues = false;
178 #ifdef _D3XP
179  SetTimeState ts( timeGroup );
180 #endif
181 
182  if ( !smoke ) {
183  return false;
184  }
185 
186  if ( !gameLocal.isNewFrame ) {
187  return false;
188  }
189 
190  // dedicated doesn't smoke. No UpdateRenderEntity, so they would not be freed
191  if ( gameLocal.localClientNum < 0 ) {
192  return false;
193  }
194 
195  assert( gameLocal.time == 0 || systemStartTime <= gameLocal.time );
196  if ( systemStartTime > gameLocal.time ) {
197  return false;
198  }
199 
200  idRandom steppingRandom( 0xffff * diversity );
201 
202  // for each stage in the smoke that is still emitting particles, emit a new singleSmoke_t
203  for ( int stageNum = 0; stageNum < smoke->stages.Num(); stageNum++ ) {
204  const idParticleStage *stage = smoke->stages[stageNum];
205 
206  if ( !stage->cycleMsec ) {
207  continue;
208  }
209 
210  if ( !stage->material ) {
211  continue;
212  }
213 
214  if ( stage->particleLife <= 0 ) {
215  continue;
216  }
217 
218  // see how many particles we should emit this tic
219  // FIXME: smoke.privateStartTime += stage->timeOffset;
220  int finalParticleTime = stage->cycleMsec * stage->spawnBunching;
221  int deltaMsec = gameLocal.time - systemStartTime;
222 
223  int nowCount, prevCount;
224  if ( finalParticleTime == 0 ) {
225  // if spawnBunching is 0, they will all come out at once
226  if ( gameLocal.time == systemStartTime ) {
227  prevCount = -1;
228  nowCount = stage->totalParticles-1;
229  } else {
230  prevCount = stage->totalParticles;
231  }
232  } else {
233  nowCount = floor( ( (float)deltaMsec / finalParticleTime ) * stage->totalParticles );
234  if ( nowCount >= stage->totalParticles ) {
235  nowCount = stage->totalParticles-1;
236  }
237  prevCount = floor( ((float)( deltaMsec - gameLocal.msec /*_D3XP - FIX - was USERCMD_MSEC*/ ) / finalParticleTime) * stage->totalParticles );
238  if ( prevCount < -1 ) {
239  prevCount = -1;
240  }
241  }
242 
243  if ( prevCount >= stage->totalParticles ) {
244  // no more particles from this stage
245  continue;
246  }
247 
248  if ( nowCount < stage->totalParticles-1 ) {
249  // the system will need to emit particles next frame as well
250  continues = true;
251  }
252 
253  // find an activeSmokeStage that matches this
254  activeSmokeStage_t *active;
255  int i;
256  for ( i = 0 ; i < activeStages.Num() ; i++ ) {
257  active = &activeStages[i];
258  if ( active->stage == stage ) {
259  break;
260  }
261  }
262  if ( i == activeStages.Num() ) {
263  // add a new one
264  activeSmokeStage_t newActive;
265 
266  newActive.smokes = NULL;
267  newActive.stage = stage;
268  i = activeStages.Append( newActive );
269  active = &activeStages[i];
270  }
271 
272  // add all the required particles
273  for ( prevCount++ ; prevCount <= nowCount ; prevCount++ ) {
274  if ( !freeSmokes ) {
275  gameLocal.Printf( "idSmokeParticles::EmitSmoke: no free smokes with %d active stages\n", activeStages.Num() );
276  return true;
277  }
278  singleSmoke_t *newSmoke = freeSmokes;
280  numActiveSmokes++;
281 
282 #ifdef _D3XP
283  newSmoke->timeGroup = timeGroup;
284 #endif
285  newSmoke->index = prevCount;
286  newSmoke->axis = axis;
287  newSmoke->origin = origin;
288  newSmoke->random = steppingRandom;
289  newSmoke->privateStartTime = systemStartTime + prevCount * finalParticleTime / stage->totalParticles;
290  newSmoke->next = active->smokes;
291  active->smokes = newSmoke;
292 
293  steppingRandom.RandomInt(); // advance the random
294  }
295  }
296 
297  return continues;
298 }
299 
300 /*
301 ================
302 idSmokeParticles::UpdateRenderEntity
303 ================
304 */
305 bool idSmokeParticles::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) {
306 
307  // FIXME: re-use model surfaces
308  renderEntity->hModel->InitEmpty( smokeParticle_SnapshotName );
309 
310  // this may be triggered by a model trace or other non-view related source,
311  // to which we should look like an empty model
312  if ( !renderView ) {
313  return false;
314  }
315 
316  // don't regenerate it if it is current
317  if ( renderView->time == currentParticleTime && !renderView->forceUpdate ) {
318  return false;
319  }
320  currentParticleTime = renderView->time;
321 
323 
325  g.renderView = renderView;
326 
327  for ( int activeStageNum = 0; activeStageNum < activeStages.Num(); activeStageNum++ ) {
328  singleSmoke_t *smoke, *next, *last;
329 
330  activeSmokeStage_t *active = &activeStages[activeStageNum];
331  const idParticleStage *stage = active->stage;
332 
333  if ( !stage->material ) {
334  continue;
335  }
336 
337  // allocate a srfTriangles that can hold all the particles
338  int count = 0;
339  for ( smoke = active->smokes; smoke; smoke = smoke->next ) {
340  count++;
341  }
342  int quads = count * stage->NumQuadsPerParticle();
343  srfTriangles_t *tri = renderEntity->hModel->AllocSurfaceTriangles( quads * 4, quads * 6 );
344  tri->numIndexes = quads * 6;
345  tri->numVerts = quads * 4;
346 
347  // just always draw the particles
348  tri->bounds[0][0] =
349  tri->bounds[0][1] =
350  tri->bounds[0][2] = -99999;
351  tri->bounds[1][0] =
352  tri->bounds[1][1] =
353  tri->bounds[1][2] = 99999;
354 
355  tri->numVerts = 0;
356  for ( last = NULL, smoke = active->smokes; smoke; smoke = next ) {
357  next = smoke->next;
358 
359 #ifdef _D3XP
360  if ( smoke->timeGroup ) {
361  g.frac = (float)( gameLocal.fast.time - smoke->privateStartTime ) / (stage->particleLife * 1000);
362  }
363  else {
364  g.frac = (float)( gameLocal.time - smoke->privateStartTime ) / (stage->particleLife * 1000);
365  }
366 #else
367  g.frac = (float)( gameLocal.time - smoke->privateStartTime ) / (stage->particleLife * 1000);
368 #endif
369  if ( g.frac >= 1.0f ) {
370  // remove the particle from the stage list
371  if ( last != NULL ) {
372  last->next = smoke->next;
373  } else {
374  active->smokes = smoke->next;
375  }
376  // put the particle on the free list
377  smoke->next = freeSmokes;
378  freeSmokes = smoke;
379  numActiveSmokes--;
380  continue;
381  }
382 
383  g.index = smoke->index;
384  g.random = smoke->random;
385 
386  g.origin = smoke->origin;
387  g.axis = smoke->axis;
388 
389  g.originalRandom = g.random;
390  g.age = g.frac * stage->particleLife;
391 
392  tri->numVerts += stage->CreateParticle( &g, tri->verts + tri->numVerts );
393 
394  last = smoke;
395  }
396  if ( tri->numVerts > quads * 4 ) {
397  gameLocal.Error( "idSmokeParticles::UpdateRenderEntity: miscounted verts" );
398  }
399 
400  if ( tri->numVerts == 0 ) {
401 
402  // they were all removed
403  renderEntity->hModel->FreeSurfaceTriangles( tri );
404 
405  if ( !active->smokes ) {
406  // remove this from the activeStages list
407  activeStages.RemoveIndex( activeStageNum );
408  activeStageNum--;
409  }
410  } else {
411  // build the index list
412  int indexes = 0;
413  for ( int i = 0 ; i < tri->numVerts ; i += 4 ) {
414  tri->indexes[indexes+0] = i;
415  tri->indexes[indexes+1] = i+2;
416  tri->indexes[indexes+2] = i+3;
417  tri->indexes[indexes+3] = i;
418  tri->indexes[indexes+4] = i+3;
419  tri->indexes[indexes+5] = i+1;
420  indexes += 6;
421  }
422  tri->numIndexes = indexes;
423 
424  modelSurface_t surf;
425  surf.geometry = tri;
426  surf.shader = stage->material;
427  surf.id = 0;
428 
429  renderEntity->hModel->AddSurface( surf );
430  }
431  }
432  return true;
433 }
434 
435 /*
436 ================
437 idSmokeParticles::ModelCallback
438 ================
439 */
440 bool idSmokeParticles::ModelCallback( renderEntity_s *renderEntity, const renderView_t *renderView ) {
441  // update the particles
442  if ( gameLocal.smokeParticles ) {
443  return gameLocal.smokeParticles->UpdateRenderEntity( renderEntity, renderView );
444  }
445 
446  return true;
447 }
GLubyte g
Definition: glext.h:4662
virtual srfTriangles_t * AllocSurfaceTriangles(int numVerts, int numIndexes) const =0
bool EmitSmoke(const idDeclParticle *smoke, const int startTime, const float diversity, const idVec3 &origin, const idMat3 &axis, int timeGroup)
assert(prefInfo.fullscreenBtn)
idMat3 mat3_identity(idVec3(1, 0, 0), idVec3(0, 1, 0), idVec3(0, 0, 1))
int numVerts
Definition: Model.h:98
void Printf(const char *fmt,...) const id_attribute((format(printf
Definition: Game_local.cpp:699
idSmokeParticles * smokeParticles
Definition: Game_local.h:307
bool isNewFrame
Definition: Game_local.h:333
virtual qhandle_t AddEntityDef(const renderEntity_t *re)=0
void void void void void Error(const char *fmt,...) const id_attribute((format(printf
Definition: Game_local.cpp:783
const idMaterial * shader
Definition: Model.h:146
virtual void FreeSurfaceTriangles(srfTriangles_t *tris) const =0
virtual void AddSurface(modelSurface_t surface)=0
Definition: Vector.h:316
case const float
Definition: Callbacks.cpp:62
const renderEntity_t * renderEnt
Definition: DeclParticle.h:86
bool forceUpdate
Definition: RenderWorld.h:219
const int SHADERPARM_GREEN
Definition: RenderWorld.h:47
void Clear(void)
Definition: Bounds.h:201
renderEntity_t renderEntity
singleSmoke_t * smokes
int i
Definition: process.py:33
deferredEntityCallback_t callback
Definition: RenderWorld.h:96
struct singleSmoke_s * next
idRandom originalRandom
Definition: DeclParticle.h:96
virtual void InitEmpty(const char *name)=0
const int SHADERPARM_BLUE
Definition: RenderWorld.h:48
bool AddPoint(const idVec3 &v)
Definition: Bounds.h:226
int RandomInt(void)
Definition: Random.h:70
virtual void FreeEntityDef(qhandle_t entityHandle)=0
GLuint GLuint GLsizei count
Definition: glext.h:2845
singleSmoke_t smokes[MAX_SMOKE_PARTICLES]
static bool ModelCallback(renderEntity_s *renderEntity, const renderView_t *renderView)
idBounds bounds
Definition: Model.h:87
#define NULL
Definition: Lib.h:88
srfTriangles_t * geometry
Definition: Model.h:147
virtual int CreateParticle(particleGen_t *g, idDrawVert *verts) const
const renderView_t * renderView
Definition: DeclParticle.h:87
idRandom random
const idMaterial * material
Definition: DeclParticle.h:128
idGameLocal gameLocal
Definition: Game_local.cpp:64
idRenderModel * hModel
Definition: RenderWorld.h:81
idList< idParticleStage * > stages
Definition: DeclParticle.h:204
virtual void FreeModel(idRenderModel *model)=0
idRandom random
Definition: DeclParticle.h:90
singleSmoke_t * freeSmokes
bool UpdateRenderEntity(renderEntity_s *renderEntity, const renderView_t *renderView)
int localClientNum
Definition: Game_local.h:330
int Append(const type &obj)
Definition: List.h:646
idRenderModelManager * renderModelManager
Definition: Matrix.h:333
const int SHADERPARM_RED
Definition: RenderWorld.h:46
static const int MAX_SMOKE_PARTICLES
tuple f
Definition: idal.py:89
int Num(void) const
Definition: List.h:265
bool RemoveIndex(int index)
Definition: List.h:849
idBounds bounds
Definition: RenderWorld.h:95
glIndex_t * indexes
Definition: Model.h:102
int numIndexes
Definition: Model.h:101
idRenderWorld * gameRenderWorld
Definition: Game_local.cpp:55
const idParticleStage * stage
float shaderParms[MAX_ENTITY_SHADER_PARMS]
Definition: RenderWorld.h:127
virtual int NumQuadsPerParticle() const
idDrawVert * verts
Definition: Model.h:99
virtual idRenderModel * AllocModel()=0
void Clear(void)
Definition: List.h:184
idList< activeSmokeStage_t > activeStages