/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see .
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.
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.
===========================================================================
*/
#include "../idlib/precompiled.h"
#pragma hdrstop
#include "tr_local.h"
#include "Model_local.h"
static const char *parametricParticle_SnapshotName = "_ParametricParticle_Snapshot_";
/*
====================
idRenderModelPrt::idRenderModelPrt
====================
*/
idRenderModelPrt::idRenderModelPrt() {
particleSystem = NULL;
}
/*
====================
idRenderModelPrt::InitFromFile
====================
*/
void idRenderModelPrt::InitFromFile( const char *fileName ) {
name = fileName;
particleSystem = static_cast( declManager->FindType( DECL_PARTICLE, fileName ) );
}
/*
=================
idRenderModelPrt::TouchData
=================
*/
void idRenderModelPrt::TouchData( void ) {
// Ensure our particle system is added to the list of referenced decls
particleSystem = static_cast( declManager->FindType( DECL_PARTICLE, name ) );
}
/*
====================
idRenderModelPrt::InstantiateDynamicModel
====================
*/
idRenderModel *idRenderModelPrt::InstantiateDynamicModel( const struct renderEntity_s *renderEntity, const struct viewDef_s *viewDef, idRenderModel *cachedModel ) {
idRenderModelStatic *staticModel;
if ( cachedModel && !r_useCachedDynamicModels.GetBool() ) {
delete cachedModel;
cachedModel = NULL;
}
// this may be triggered by a model trace or other non-view related source, to which we should look like an empty model
if ( renderEntity == NULL || viewDef == NULL ) {
delete cachedModel;
return NULL;
}
if ( r_skipParticles.GetBool() ) {
delete cachedModel;
return NULL;
}
/*
// if the entire system has faded out
if ( renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] && viewDef->renderView.time * 0.001f >= renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] ) {
delete cachedModel;
return NULL;
}
*/
if ( cachedModel != NULL ) {
assert( dynamic_cast(cachedModel) != NULL );
assert( idStr::Icmp( cachedModel->Name(), parametricParticle_SnapshotName ) == 0 );
staticModel = static_cast(cachedModel);
} else {
staticModel = new idRenderModelStatic;
staticModel->InitEmpty( parametricParticle_SnapshotName );
}
particleGen_t g;
g.renderEnt = renderEntity;
g.renderView = &viewDef->renderView;
g.origin.Zero();
g.axis.Identity();
for ( int stageNum = 0; stageNum < particleSystem->stages.Num(); stageNum++ ) {
idParticleStage *stage = particleSystem->stages[stageNum];
if ( !stage->material ) {
continue;
}
if ( !stage->cycleMsec ) {
continue;
}
if ( stage->hidden ) { // just for gui particle editor use
staticModel->DeleteSurfaceWithId( stageNum );
continue;
}
idRandom steppingRandom, steppingRandom2;
int stageAge = g.renderView->time + renderEntity->shaderParms[SHADERPARM_TIMEOFFSET] * 1000 - stage->timeOffset * 1000;
int stageCycle = stageAge / stage->cycleMsec;
// some particles will be in this cycle, some will be in the previous cycle
steppingRandom.SetSeed( (( stageCycle << 10 ) & idRandom::MAX_RAND) ^ (int)( renderEntity->shaderParms[SHADERPARM_DIVERSITY] * idRandom::MAX_RAND ) );
steppingRandom2.SetSeed( (( (stageCycle-1) << 10 ) & idRandom::MAX_RAND) ^ (int)( renderEntity->shaderParms[SHADERPARM_DIVERSITY] * idRandom::MAX_RAND ) );
int count = stage->totalParticles * stage->NumQuadsPerParticle();
int surfaceNum;
modelSurface_t *surf;
if ( staticModel->FindSurfaceWithId( stageNum, surfaceNum ) ) {
surf = &staticModel->surfaces[surfaceNum];
R_FreeStaticTriSurfVertexCaches( surf->geometry );
} else {
surf = &staticModel->surfaces.Alloc();
surf->id = stageNum;
surf->shader = stage->material;
surf->geometry = R_AllocStaticTriSurf();
R_AllocStaticTriSurfVerts( surf->geometry, 4 * count );
R_AllocStaticTriSurfIndexes( surf->geometry, 6 * count );
R_AllocStaticTriSurfPlanes( surf->geometry, 6 * count );
}
int numVerts = 0;
idDrawVert *verts = surf->geometry->verts;
for ( int index = 0; index < stage->totalParticles; index++ ) {
g.index = index;
// bump the random
steppingRandom.RandomInt();
steppingRandom2.RandomInt();
// calculate local age for this index
int bunchOffset = stage->particleLife * 1000 * stage->spawnBunching * index / stage->totalParticles;
int particleAge = stageAge - bunchOffset;
int particleCycle = particleAge / stage->cycleMsec;
if ( particleCycle < 0 ) {
// before the particleSystem spawned
continue;
}
if ( stage->cycles && particleCycle >= stage->cycles ) {
// cycled systems will only run cycle times
continue;
}
if ( particleCycle == stageCycle ) {
g.random = steppingRandom;
} else {
g.random = steppingRandom2;
}
int inCycleTime = particleAge - particleCycle * stage->cycleMsec;
if ( renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME] &&
g.renderView->time - inCycleTime >= renderEntity->shaderParms[SHADERPARM_PARTICLE_STOPTIME]*1000 ) {
// don't fire any more particles
continue;
}
// supress particles before or after the age clamp
g.frac = (float)inCycleTime / ( stage->particleLife * 1000 );
if ( g.frac < 0.0f ) {
// yet to be spawned
continue;
}
if ( g.frac > 1.0f ) {
// this particle is in the deadTime band
continue;
}
// this is needed so aimed particles can calculate origins at different times
g.originalRandom = g.random;
g.age = g.frac * stage->particleLife;
// if the particle doesn't get drawn because it is faded out or beyond a kill region, don't increment the verts
numVerts += stage->CreateParticle( &g, verts + numVerts );
}
// numVerts must be a multiple of 4
assert( ( numVerts & 3 ) == 0 && numVerts <= 4 * count );
// build the indexes
int numIndexes = 0;
glIndex_t *indexes = surf->geometry->indexes;
for ( int i = 0; i < numVerts; i += 4 ) {
indexes[numIndexes+0] = i;
indexes[numIndexes+1] = i+2;
indexes[numIndexes+2] = i+3;
indexes[numIndexes+3] = i;
indexes[numIndexes+4] = i+3;
indexes[numIndexes+5] = i+1;
numIndexes += 6;
}
surf->geometry->tangentsCalculated = false;
surf->geometry->facePlanesCalculated = false;
surf->geometry->numVerts = numVerts;
surf->geometry->numIndexes = numIndexes;
surf->geometry->bounds = stage->bounds; // just always draw the particles
}
return staticModel;
}
/*
====================
idRenderModelPrt::IsDynamicModel
====================
*/
dynamicModel_t idRenderModelPrt::IsDynamicModel() const {
return DM_CONTINUOUS;
}
/*
====================
idRenderModelPrt::Bounds
====================
*/
idBounds idRenderModelPrt::Bounds( const struct renderEntity_s *ent ) const {
return particleSystem->bounds;
}
/*
====================
idRenderModelPrt::DepthHack
====================
*/
float idRenderModelPrt::DepthHack() const {
return particleSystem->depthHack;
}
/*
====================
idRenderModelPrt::Memory
====================
*/
int idRenderModelPrt::Memory() const {
int total = 0;
total += idRenderModelStatic::Memory();
if ( particleSystem ) {
total += sizeof( *particleSystem );
for ( int i = 0; i < particleSystem->stages.Num(); i++ ) {
total += sizeof( particleSystem->stages[i] );
}
}
return total;
}