mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-12-14 14:31:19 +00:00
320 lines
8.7 KiB
C++
320 lines
8.7 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
Doom 3 BFG Edition 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 BFG Edition 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 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 BFG Edition 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 BFG Edition 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.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#pragma hdrstop
|
|
#include "precompiled.h"
|
|
|
|
#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<const idDeclParticle*>( declManager->FindType( DECL_PARTICLE, fileName ) );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idRenderModelPrt::TouchData
|
|
=================
|
|
*/
|
|
void idRenderModelPrt::TouchData()
|
|
{
|
|
// Ensure our particle system is added to the list of referenced decls
|
|
particleSystem = static_cast<const idDeclParticle*>( declManager->FindType( DECL_PARTICLE, name ) );
|
|
}
|
|
|
|
/*
|
|
====================
|
|
idRenderModelPrt::InstantiateDynamicModel
|
|
====================
|
|
*/
|
|
idRenderModel* idRenderModelPrt::InstantiateDynamicModel( const struct renderEntity_s* renderEntity, const viewDef_t* 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<idRenderModelStatic*>( cachedModel ) != NULL );
|
|
assert( idStr::Icmp( cachedModel->Name(), parametricParticle_SnapshotName ) == 0 );
|
|
|
|
staticModel = static_cast<idRenderModelStatic*>( cachedModel );
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
staticModel = new( TAG_MODEL ) 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->timeGroup] + 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 );
|
|
}
|
|
|
|
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[renderEntity->timeGroup] - 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;
|
|
triIndex_t* indexes = surf->geometry->indexes;
|
|
for( int i = 0; i < numVerts; i += 4 )
|
|
{
|
|
indexes[numIndexes + 0] = i + 0;
|
|
indexes[numIndexes + 1] = i + 2;
|
|
indexes[numIndexes + 2] = i + 3;
|
|
indexes[numIndexes + 3] = i + 0;
|
|
indexes[numIndexes + 4] = i + 3;
|
|
indexes[numIndexes + 5] = i + 1;
|
|
numIndexes += 6;
|
|
}
|
|
|
|
surf->geometry->tangentsCalculated = 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;
|
|
}
|