2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
33 #include "Model_local.h"
35 static const char *parametricParticle_SnapshotName = "_ParametricParticle_Snapshot_";
39 idRenderModelPrt::idRenderModelPrt
42 idRenderModelPrt::idRenderModelPrt() {
43 particleSystem = NULL;
48 idRenderModelPrt::InitFromFile
51 void idRenderModelPrt::InitFromFile( const char *fileName ) {
53 particleSystem = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, fileName ) );
58 idRenderModelPrt::TouchData
61 void idRenderModelPrt::TouchData( void ) {
62 // Ensure our particle system is added to the list of referenced decls
63 particleSystem = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, name ) );
68 idRenderModelPrt::InstantiateDynamicModel
71 idRenderModel *idRenderModelPrt::InstantiateDynamicModel( const struct renderEntity_s *renderEntity, const struct viewDef_s *viewDef, idRenderModel *cachedModel ) {
72 idRenderModelStatic *staticModel;
74 if ( cachedModel && !r_useCachedDynamicModels.GetBool() ) {
79 // this may be triggered by a model trace or other non-view related source, to which we should look like an empty model
80 if ( renderEntity == NULL || viewDef == NULL ) {
85 if ( r_skipParticles.GetBool() ) {
91 // if the entire system has faded out
92 if ( renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] && viewDef->renderView.time * 0.001f >= renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] ) {
98 if ( cachedModel != NULL ) {
100 assert( dynamic_cast<idRenderModelStatic *>(cachedModel) != NULL );
101 assert( idStr::Icmp( cachedModel->Name(), parametricParticle_SnapshotName ) == 0 );
103 staticModel = static_cast<idRenderModelStatic *>(cachedModel);
107 staticModel = new idRenderModelStatic;
108 staticModel->InitEmpty( parametricParticle_SnapshotName );
113 g.renderEnt = renderEntity;
114 g.renderView = &viewDef->renderView;
118 for ( int stageNum = 0; stageNum < particleSystem->stages.Num(); stageNum++ ) {
119 idParticleStage *stage = particleSystem->stages[stageNum];
121 if ( !stage->material ) {
124 if ( !stage->cycleMsec ) {
127 if ( stage->hidden ) { // just for gui particle editor use
128 staticModel->DeleteSurfaceWithId( stageNum );
132 idRandom steppingRandom, steppingRandom2;
134 int stageAge = g.renderView->time + renderEntity->shaderParms[SHADERPARM_TIMEOFFSET] * 1000 - stage->timeOffset * 1000;
135 int stageCycle = stageAge / stage->cycleMsec;
136 int inCycleTime = stageAge - stageCycle * stage->cycleMsec;
138 // some particles will be in this cycle, some will be in the previous cycle
139 steppingRandom.SetSeed( (( stageCycle << 10 ) & idRandom::MAX_RAND) ^ (int)( renderEntity->shaderParms[SHADERPARM_DIVERSITY] * idRandom::MAX_RAND ) );
140 steppingRandom2.SetSeed( (( (stageCycle-1) << 10 ) & idRandom::MAX_RAND) ^ (int)( renderEntity->shaderParms[SHADERPARM_DIVERSITY] * idRandom::MAX_RAND ) );
142 int count = stage->totalParticles * stage->NumQuadsPerParticle();
145 modelSurface_t *surf;
147 if ( staticModel->FindSurfaceWithId( stageNum, surfaceNum ) ) {
148 surf = &staticModel->surfaces[surfaceNum];
149 R_FreeStaticTriSurfVertexCaches( surf->geometry );
151 surf = &staticModel->surfaces.Alloc();
153 surf->shader = stage->material;
154 surf->geometry = R_AllocStaticTriSurf();
155 R_AllocStaticTriSurfVerts( surf->geometry, 4 * count );
156 R_AllocStaticTriSurfIndexes( surf->geometry, 6 * count );
157 R_AllocStaticTriSurfPlanes( surf->geometry, 6 * count );
161 idDrawVert *verts = surf->geometry->verts;
163 for ( int index = 0; index < stage->totalParticles; index++ ) {
167 steppingRandom.RandomInt();
168 steppingRandom2.RandomInt();
170 // calculate local age for this index
171 int bunchOffset = stage->particleLife * 1000 * stage->spawnBunching * index / stage->totalParticles;
173 int particleAge = stageAge - bunchOffset;
174 int particleCycle = particleAge / stage->cycleMsec;
175 if ( particleCycle < 0 ) {
176 // before the particleSystem spawned
179 if ( stage->cycles && particleCycle >= stage->cycles ) {
180 // cycled systems will only run cycle times
184 if ( particleCycle == stageCycle ) {
185 g.random = steppingRandom;
187 g.random = steppingRandom2;
190 int inCycleTime = particleAge - particleCycle * stage->cycleMsec;
192 if ( renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] &&
193 g.renderView->time - inCycleTime >= renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME]*1000 ) {
194 // don't fire any more particles
198 // supress particles before or after the age clamp
199 g.frac = (float)inCycleTime / ( stage->particleLife * 1000 );
200 if ( g.frac < 0.0f ) {
204 if ( g.frac > 1.0f ) {
205 // this particle is in the deadTime band
209 // this is needed so aimed particles can calculate origins at different times
210 g.originalRandom = g.random;
212 g.age = g.frac * stage->particleLife;
214 // if the particle doesn't get drawn because it is faded out or beyond a kill region, don't increment the verts
215 numVerts += stage->CreateParticle( &g, verts + numVerts );
218 // numVerts must be a multiple of 4
219 assert( ( numVerts & 3 ) == 0 && numVerts <= 4 * count );
223 glIndex_t *indexes = surf->geometry->indexes;
224 for ( int i = 0; i < numVerts; i += 4 ) {
225 indexes[numIndexes+0] = i;
226 indexes[numIndexes+1] = i+2;
227 indexes[numIndexes+2] = i+3;
228 indexes[numIndexes+3] = i;
229 indexes[numIndexes+4] = i+3;
230 indexes[numIndexes+5] = i+1;
234 surf->geometry->tangentsCalculated = false;
235 surf->geometry->facePlanesCalculated = false;
236 surf->geometry->numVerts = numVerts;
237 surf->geometry->numIndexes = numIndexes;
238 surf->geometry->bounds = stage->bounds; // just always draw the particles
246 idRenderModelPrt::IsDynamicModel
249 dynamicModel_t idRenderModelPrt::IsDynamicModel() const {
250 return DM_CONTINUOUS;
255 idRenderModelPrt::Bounds
258 idBounds idRenderModelPrt::Bounds( const struct renderEntity_s *ent ) const {
259 return particleSystem->bounds;
264 idRenderModelPrt::DepthHack
267 float idRenderModelPrt::DepthHack() const {
268 return particleSystem->depthHack;
273 idRenderModelPrt::Memory
276 int idRenderModelPrt::Memory() const {
279 total += idRenderModelStatic::Memory();
281 if ( particleSystem ) {
282 total += sizeof( *particleSystem );
284 for ( int i = 0; i < particleSystem->stages.Num(); i++ ) {
285 total += sizeof( particleSystem->stages[i] );