summaryrefslogtreecommitdiff
path: root/src/cgame/cg_particles.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cgame/cg_particles.c')
-rw-r--r--src/cgame/cg_particles.c2551
1 files changed, 0 insertions, 2551 deletions
diff --git a/src/cgame/cg_particles.c b/src/cgame/cg_particles.c
deleted file mode 100644
index 64587360..00000000
--- a/src/cgame/cg_particles.c
+++ /dev/null
@@ -1,2551 +0,0 @@
-// cg_particles.c -- the particle system
-
-/*
- * Portions Copyright (C) 2000-2001 Tim Angus
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the OSML - Open Source Modification License v1.0 as
- * described in the file COPYING which is distributed with this source
- * code.
- *
- * This program 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.
- */
-
-#include "cg_local.h"
-
-static baseParticleSystem_t baseParticleSystems[ MAX_BASEPARTICLE_SYSTEMS ];
-static baseParticleEjector_t baseParticleEjectors[ MAX_BASEPARTICLE_EJECTORS ];
-static baseParticle_t baseParticles[ MAX_BASEPARTICLES ];
-static int numBaseParticleSystems = 0;
-static int numBaseParticleEjectors = 0;
-static int numBaseParticles = 0;
-
-static particleSystem_t particleSystems[ MAX_PARTICLE_SYSTEMS ];
-static particleEjector_t particleEjectors[ MAX_PARTICLE_EJECTORS ];
-static particle_t particles[ MAX_PARTICLES ];
-static particle_t *sortedParticles[ MAX_PARTICLES ];
-static particle_t *radixBuffer[ MAX_PARTICLES ];
-
-/*
-===============
-CG_LerpValues
-
-Lerp between two values
-===============
-*/
-static float CG_LerpValues( float a, float b, float f )
-{
- if( b == PARTICLES_SAME_AS_INITIAL )
- return a;
- else
- return ( (a) + (f) * ( (b) - (a) ) );
-}
-
-/*
-===============
-CG_RandomiseValue
-
-Randomise some value by some variance
-===============
-*/
-static float CG_RandomiseValue( float value, float variance )
-{
- if( value != 0.0f )
- return value * ( 1.0f + ( random( ) * variance ) );
- else
- return random( ) * variance;
-}
-
-/*
-===============
-CG_SpreadVector
-
-Randomly spread a vector by some amount
-===============
-*/
-static void CG_SpreadVector( vec3_t v, float spread )
-{
- vec3_t p, r1, r2;
- float randomSpread = crandom( ) * spread;
- float randomRotation = random( ) * 360.0f;
-
- PerpendicularVector( p, v );
-
- RotatePointAroundVector( r1, p, v, randomSpread );
- RotatePointAroundVector( r2, v, r1, randomRotation );
-
- VectorCopy( r2, v );
-}
-
-/*
-===============
-CG_DestroyParticle
-
-Destroy an individual particle
-===============
-*/
-static void CG_DestroyParticle( particle_t *p, vec3_t impactNormal )
-{
- //this particle has an onDeath particle system attached
- if( p->class->onDeathSystemName[ 0 ] != '\0' )
- {
- particleSystem_t *ps;
-
- ps = CG_SpawnNewParticleSystem( p->class->onDeathSystemHandle );
-
- if( CG_IsParticleSystemValid( &ps ) )
- {
- if( impactNormal )
- CG_SetParticleSystemNormal( ps, impactNormal );
-
- CG_SetAttachmentPoint( &ps->attachment, p->origin );
- CG_AttachToPoint( &ps->attachment );
- }
- }
-
- p->valid = qfalse;
-
- //this gives other systems a couple of
- //frames to realise the particle is gone
- p->frameWhenInvalidated = cg.clientFrame;
-}
-
-/*
-===============
-CG_SpawnNewParticle
-
-Introduce a new particle into the world
-===============
-*/
-static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *parent )
-{
- int i, j;
- particle_t *p = NULL;
- particleEjector_t *pe = parent;
- particleSystem_t *ps = parent->parent;
- vec3_t attachmentPoint, attachmentVelocity;
- vec3_t transform[ 3 ];
-
- for( i = 0; i < MAX_PARTICLES; i++ )
- {
- p = &particles[ i ];
-
- //FIXME: the + 1 may be unnecessary
- if( !p->valid && cg.clientFrame > p->frameWhenInvalidated + 1 )
- {
- memset( p, 0, sizeof( particle_t ) );
-
- //found a free slot
- p->class = bp;
- p->parent = pe;
-
- p->birthTime = cg.time;
- p->lifeTime = (int)CG_RandomiseValue( (float)bp->lifeTime, bp->lifeTimeRandFrac );
-
- p->radius.delay = (int)CG_RandomiseValue( (float)bp->radius.delay, bp->radius.delayRandFrac );
- p->radius.initial = CG_RandomiseValue( bp->radius.initial, bp->radius.initialRandFrac );
- p->radius.final = CG_RandomiseValue( bp->radius.final, bp->radius.finalRandFrac );
-
- p->alpha.delay = (int)CG_RandomiseValue( (float)bp->alpha.delay, bp->alpha.delayRandFrac );
- p->alpha.initial = CG_RandomiseValue( bp->alpha.initial, bp->alpha.initialRandFrac );
- p->alpha.final = CG_RandomiseValue( bp->alpha.final, bp->alpha.finalRandFrac );
-
- p->rotation.delay = (int)CG_RandomiseValue( (float)bp->rotation.delay, bp->rotation.delayRandFrac );
- p->rotation.initial = CG_RandomiseValue( bp->rotation.initial, bp->rotation.initialRandFrac );
- p->rotation.final = CG_RandomiseValue( bp->rotation.final, bp->rotation.finalRandFrac );
-
- p->dLightRadius.delay =
- (int)CG_RandomiseValue( (float)bp->dLightRadius.delay, bp->dLightRadius.delayRandFrac );
- p->dLightRadius.initial =
- CG_RandomiseValue( bp->dLightRadius.initial, bp->dLightRadius.initialRandFrac );
- p->dLightRadius.final =
- CG_RandomiseValue( bp->dLightRadius.final, bp->dLightRadius.finalRandFrac );
-
- p->colorDelay = CG_RandomiseValue( bp->colorDelay, bp->colorDelayRandFrac );
-
- p->bounceMarkRadius = CG_RandomiseValue( bp->bounceMarkRadius, bp->bounceMarkRadiusRandFrac );
- p->bounceMarkCount =
- round( CG_RandomiseValue( (float)bp->bounceMarkCount, bp->bounceMarkCountRandFrac ) );
- p->bounceSoundCount =
- round( CG_RandomiseValue( (float)bp->bounceSoundCount, bp->bounceSoundCountRandFrac ) );
-
- if( bp->numModels )
- {
- p->model = bp->models[ rand( ) % bp->numModels ];
-
- if( bp->modelAnimation.frameLerp < 0 )
- {
- bp->modelAnimation.frameLerp = p->lifeTime / bp->modelAnimation.numFrames;
- bp->modelAnimation.initialLerp = p->lifeTime / bp->modelAnimation.numFrames;
- }
- }
-
- if( !CG_AttachmentPoint( &ps->attachment, attachmentPoint ) )
- return NULL;
-
- VectorCopy( attachmentPoint, p->origin );
-
- if( CG_AttachmentAxis( &ps->attachment, transform ) )
- {
- vec3_t transDisplacement;
-
- VectorMatrixMultiply( bp->displacement, transform, transDisplacement );
- VectorAdd( p->origin, transDisplacement, p->origin );
- }
- else
- VectorAdd( p->origin, bp->displacement, p->origin );
-
- for( j = 0; j <= 2; j++ )
- p->origin[ j ] += ( crandom( ) * bp->randDisplacement );
-
- switch( bp->velMoveType )
- {
- case PMT_STATIC:
- if( bp->velMoveValues.dirType == PMD_POINT )
- VectorSubtract( bp->velMoveValues.point, p->origin, p->velocity );
- else if( bp->velMoveValues.dirType == PMD_LINEAR )
- VectorCopy( bp->velMoveValues.dir, p->velocity );
- break;
-
- case PMT_STATIC_TRANSFORM:
- if( !CG_AttachmentAxis( &ps->attachment, transform ) )
- {
- CG_Printf( S_COLOR_RED "ERROR: a particle with velocityType "
- "static_transform is not attached to something which can "
- "provide a transformation\n" );
- return NULL;
- }
-
- if( bp->velMoveValues.dirType == PMD_POINT )
- {
- vec3_t transPoint;
-
- VectorMatrixMultiply( bp->velMoveValues.point, transform, transPoint );
- VectorSubtract( transPoint, p->origin, p->velocity );
- }
- else if( bp->velMoveValues.dirType == PMD_LINEAR )
- VectorMatrixMultiply( bp->velMoveValues.dir, transform, p->velocity );
- break;
-
- case PMT_TAG:
- case PMT_CENT_ANGLES:
- if( bp->velMoveValues.dirType == PMD_POINT )
- VectorSubtract( attachmentPoint, p->origin, p->velocity );
- else if( bp->velMoveValues.dirType == PMD_LINEAR )
- {
- if( !CG_AttachmentDir( &ps->attachment, p->velocity ) )
- return NULL;
- }
- break;
-
- case PMT_NORMAL:
- if( !ps->normalValid )
- {
- CG_Printf( S_COLOR_RED "ERROR: a particle with velocityType "
- "normal has no normal\n" );
- return NULL;
- }
-
- VectorCopy( ps->normal, p->velocity );
-
- //normal displacement
- VectorNormalize( p->velocity );
- VectorMA( p->origin, bp->normalDisplacement, p->velocity, p->origin );
- break;
- }
-
- VectorNormalize( p->velocity );
- CG_SpreadVector( p->velocity, bp->velMoveValues.dirRandAngle );
- VectorScale( p->velocity,
- CG_RandomiseValue( bp->velMoveValues.mag, bp->velMoveValues.magRandFrac ),
- p->velocity );
-
- if( CG_AttachmentVelocity( &ps->attachment, attachmentVelocity ) )
- {
- VectorMA( p->velocity,
- CG_RandomiseValue( bp->velMoveValues.parentVelFrac,
- bp->velMoveValues.parentVelFracRandFrac ), attachmentVelocity, p->velocity );
- }
-
- p->lastEvalTime = cg.time;
-
- p->valid = qtrue;
-
- //this particle has a child particle system attached
- if( bp->childSystemName[ 0 ] != '\0' )
- {
- particleSystem_t *ps = CG_SpawnNewParticleSystem( bp->childSystemHandle );
-
- if( CG_IsParticleSystemValid( &ps ) )
- {
- CG_SetAttachmentParticle( &ps->attachment, p );
- CG_AttachToParticle( &ps->attachment );
- }
- }
-
- //this particle has a child trail system attached
- if( bp->childTrailSystemName[ 0 ] != '\0' )
- {
- trailSystem_t *ts = CG_SpawnNewTrailSystem( bp->childTrailSystemHandle );
-
- if( CG_IsTrailSystemValid( &ts ) )
- {
- CG_SetAttachmentParticle( &ts->frontAttachment, p );
- CG_AttachToParticle( &ts->frontAttachment );
- }
- }
-
- break;
- }
- }
-
- return p;
-}
-
-
-/*
-===============
-CG_SpawnNewParticles
-
-Check if there are any ejectors that should be
-introducing new particles
-===============
-*/
-static void CG_SpawnNewParticles( void )
-{
- int i, j;
- particle_t *p;
- particleSystem_t *ps;
- particleEjector_t *pe;
- baseParticleEjector_t *bpe;
- float lerpFrac;
- int count;
-
- for( i = 0; i < MAX_PARTICLE_EJECTORS; i++ )
- {
- pe = &particleEjectors[ i ];
- ps = pe->parent;
-
- if( pe->valid )
- {
- //a non attached particle system can't make particles
- if( !CG_Attached( &ps->attachment ) )
- continue;
-
- bpe = particleEjectors[ i ].class;
-
- //if this system is scheduled for removal don't make any new particles
- if( !ps->lazyRemove )
- {
- while( pe->nextEjectionTime <= cg.time &&
- ( pe->count > 0 || pe->totalParticles == PARTICLES_INFINITE ) )
- {
- for( j = 0; j < bpe->numParticles; j++ )
- CG_SpawnNewParticle( bpe->particles[ j ], pe );
-
- if( pe->count > 0 )
- pe->count--;
-
- //calculate next ejection time
- lerpFrac = 1.0 - ( (float)pe->count / (float)pe->totalParticles );
- pe->nextEjectionTime = cg.time + (int)CG_RandomiseValue(
- CG_LerpValues( pe->ejectPeriod.initial,
- pe->ejectPeriod.final,
- lerpFrac ),
- pe->ejectPeriod.randFrac );
- }
- }
-
- if( pe->count == 0 || ps->lazyRemove )
- {
- count = 0;
-
- //wait for child particles to die before declaring this pe invalid
- for( j = 0; j < MAX_PARTICLES; j++ )
- {
- p = &particles[ j ];
-
- if( p->valid && p->parent == pe )
- count++;
- }
-
- if( !count )
- pe->valid = qfalse;
- }
- }
- }
-}
-
-
-/*
-===============
-CG_SpawnNewParticleEjector
-
-Allocate a new particle ejector
-===============
-*/
-static particleEjector_t *CG_SpawnNewParticleEjector( baseParticleEjector_t *bpe,
- particleSystem_t *parent )
-{
- int i;
- particleEjector_t *pe = NULL;
- particleSystem_t *ps = parent;
-
- for( i = 0; i < MAX_PARTICLE_EJECTORS; i++ )
- {
- pe = &particleEjectors[ i ];
-
- if( !pe->valid )
- {
- memset( pe, 0, sizeof( particleEjector_t ) );
-
- //found a free slot
- pe->class = bpe;
- pe->parent = ps;
-
- pe->ejectPeriod.initial = bpe->eject.initial;
- pe->ejectPeriod.final = bpe->eject.final;
- pe->ejectPeriod.randFrac = bpe->eject.randFrac;
-
- pe->nextEjectionTime = cg.time +
- (int)CG_RandomiseValue( (float)bpe->eject.delay, bpe->eject.delayRandFrac );
- pe->count = pe->totalParticles =
- (int)round( CG_RandomiseValue( (float)bpe->totalParticles, bpe->totalParticlesRandFrac ) );
-
- pe->valid = qtrue;
-
- if( cg_debugParticles.integer >= 1 )
- CG_Printf( "PE %s created\n", ps->class->name );
-
- break;
- }
- }
-
- return pe;
-}
-
-
-/*
-===============
-CG_SpawnNewParticleSystem
-
-Allocate a new particle system
-===============
-*/
-particleSystem_t *CG_SpawnNewParticleSystem( qhandle_t psHandle )
-{
- int i, j;
- particleSystem_t *ps = NULL;
- baseParticleSystem_t *bps = &baseParticleSystems[ psHandle - 1 ];
-
- if( !bps->registered )
- {
- CG_Printf( S_COLOR_RED "ERROR: a particle system has not been registered yet\n" );
- return NULL;
- }
-
- for( i = 0; i < MAX_PARTICLE_SYSTEMS; i++ )
- {
- ps = &particleSystems[ i ];
-
- if( !ps->valid )
- {
- memset( ps, 0, sizeof( particleSystem_t ) );
-
- //found a free slot
- ps->class = bps;
-
- ps->valid = qtrue;
- ps->lazyRemove = qfalse;
-
- for( j = 0; j < bps->numEjectors; j++ )
- CG_SpawnNewParticleEjector( bps->ejectors[ j ], ps );
-
- if( cg_debugParticles.integer >= 1 )
- CG_Printf( "PS %s created\n", bps->name );
-
- break;
- }
- }
-
- return ps;
-}
-
-/*
-===============
-CG_RegisterParticleSystem
-
-Load the shaders required for a particle system
-===============
-*/
-qhandle_t CG_RegisterParticleSystem( char *name )
-{
- int i, j, k, l;
- baseParticleSystem_t *bps;
- baseParticleEjector_t *bpe;
- baseParticle_t *bp;
-
- for( i = 0; i < MAX_BASEPARTICLE_SYSTEMS; i++ )
- {
- bps = &baseParticleSystems[ i ];
-
- if( !Q_stricmpn( bps->name, name, MAX_QPATH ) )
- {
- //already registered
- if( bps->registered )
- return i + 1;
-
- for( j = 0; j < bps->numEjectors; j++ )
- {
- bpe = bps->ejectors[ j ];
-
- for( l = 0; l < bpe->numParticles; l++ )
- {
- bp = bpe->particles[ l ];
-
- for( k = 0; k < bp->numFrames; k++ )
- bp->shaders[ k ] = trap_R_RegisterShader( bp->shaderNames[ k ] );
-
- for( k = 0; k < bp->numModels; k++ )
- bp->models[ k ] = trap_R_RegisterModel( bp->modelNames[ k ] );
-
- if( bp->bounceMarkName[ 0 ] != '\0' )
- bp->bounceMark = trap_R_RegisterShader( bp->bounceMarkName );
-
- if( bp->bounceSoundName[ 0 ] != '\0' )
- bp->bounceSound = trap_S_RegisterSound( bp->bounceSoundName, qfalse );
-
- //recursively register any children
- if( bp->childSystemName[ 0 ] != '\0' )
- {
- //don't care about a handle for children since
- //the system deals with it
- CG_RegisterParticleSystem( bp->childSystemName );
- }
-
- if( bp->onDeathSystemName[ 0 ] != '\0' )
- {
- //don't care about a handle for children since
- //the system deals with it
- CG_RegisterParticleSystem( bp->onDeathSystemName );
- }
-
- if( bp->childTrailSystemName[ 0 ] != '\0' )
- bp->childTrailSystemHandle = CG_RegisterTrailSystem( bp->childTrailSystemName );
- }
- }
-
- if( cg_debugParticles.integer >= 1 )
- CG_Printf( "Registered particle system %s\n", name );
-
- bps->registered = qtrue;
-
- //avoid returning 0
- return i + 1;
- }
- }
-
- CG_Printf( S_COLOR_RED "ERROR: failed to register particle system %s\n", name );
- return 0;
-}
-
-
-/*
-===============
-CG_ParseValueAndVariance
-
-Parse a value and its random variance
-===============
-*/
-static void CG_ParseValueAndVariance( char *token, float *value, float *variance, qboolean allowNegative )
-{
- char valueBuffer[ 16 ];
- char varianceBuffer[ 16 ];
- char *variancePtr = NULL, *varEndPointer = NULL;
- float localValue = 0.0f;
- float localVariance = 0.0f;
-
- Q_strncpyz( valueBuffer, token, sizeof( valueBuffer ) );
- Q_strncpyz( varianceBuffer, token, sizeof( varianceBuffer ) );
-
- variancePtr = strchr( valueBuffer, '~' );
-
- //variance included
- if( variancePtr )
- {
- variancePtr[ 0 ] = '\0';
- variancePtr++;
-
- localValue = atof_neg( valueBuffer, allowNegative );
-
- varEndPointer = strchr( variancePtr, '%' );
-
- if( varEndPointer )
- {
- varEndPointer[ 0 ] = '\0';
- localVariance = atof_neg( variancePtr, qfalse ) / 100.0f;
- }
- else
- {
- if( localValue != 0.0f )
- localVariance = atof_neg( variancePtr, qfalse ) / localValue;
- else
- localVariance = atof_neg( variancePtr, qfalse );
- }
- }
- else
- localValue = atof_neg( valueBuffer, allowNegative );
-
- if( value != NULL )
- *value = localValue;
-
- if( variance != NULL )
- *variance = localVariance;
-}
-
-/*
-===============
-CG_ParseColor
-===============
-*/
-static qboolean CG_ParseColor( byte *c, char **text_p )
-{
- char *token;
- int i;
-
- for( i = 0; i <= 2; i++ )
- {
- token = COM_Parse( text_p );
-
- if( !Q_stricmp( token, "" ) )
- return qfalse;
-
- c[ i ] = (int)( (float)0xFF * atof_neg( token, qfalse ) );
- }
-
- return qtrue;
-}
-
-/*
-===============
-CG_ParseParticle
-
-Parse a particle section
-===============
-*/
-static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
-{
- char *token;
- float number, randFrac;
- int i;
-
- // read optional parameters
- while( 1 )
- {
- token = COM_Parse( text_p );
-
- if( !token )
- break;
-
- if( !Q_stricmp( token, "" ) )
- return qfalse;
-
- if( !Q_stricmp( token, "bounce" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- if( !Q_stricmp( token, "cull" ) )
- {
- bp->bounceCull = qtrue;
-
- bp->bounceFrac = -1.0f;
- bp->bounceFracRandFrac = 0.0f;
- }
- else
- {
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->bounceFrac = number;
- bp->bounceFracRandFrac = randFrac;
- }
-
- continue;
- }
- else if( !Q_stricmp( token, "bounceMark" ) )
- {
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->bounceMarkCount = number;
- bp->bounceMarkCountRandFrac = randFrac;
-
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->bounceMarkRadius = number;
- bp->bounceMarkRadiusRandFrac = randFrac;
-
- token = COM_ParseExt( text_p, qfalse );
- if( !*token )
- break;
-
- Q_strncpyz( bp->bounceMarkName, token, MAX_QPATH );
-
- continue;
- }
- else if( !Q_stricmp( token, "bounceSound" ) )
- {
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->bounceSoundCount = number;
- bp->bounceSoundCountRandFrac = randFrac;
-
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- Q_strncpyz( bp->bounceSoundName, token, MAX_QPATH );
-
- continue;
- }
- else if( !Q_stricmp( token, "shader" ) )
- {
- if( bp->numModels > 0 )
- {
- CG_Printf( S_COLOR_RED "ERROR: 'shader' not allowed in "
- "conjunction with 'model'\n", token );
- break;
- }
-
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- if( !Q_stricmp( token, "sync" ) )
- bp->framerate = 0.0f;
- else
- bp->framerate = atof_neg( token, qfalse );
-
- token = COM_ParseExt( text_p, qfalse );
- if( !*token )
- break;
-
- while( *token && bp->numFrames < MAX_PS_SHADER_FRAMES )
- {
- Q_strncpyz( bp->shaderNames[ bp->numFrames++ ], token, MAX_QPATH );
- token = COM_ParseExt( text_p, qfalse );
- }
-
- continue;
- }
- else if( !Q_stricmp( token, "model" ) )
- {
- if( bp->numFrames > 0 )
- {
- CG_Printf( S_COLOR_RED "ERROR: 'model' not allowed in "
- "conjunction with 'shader'\n", token );
- break;
- }
-
- token = COM_ParseExt( text_p, qfalse );
- if( !*token )
- break;
-
- while( *token && bp->numModels < MAX_PS_MODELS )
- {
- Q_strncpyz( bp->modelNames[ bp->numModels++ ], token, MAX_QPATH );
- token = COM_ParseExt( text_p, qfalse );
- }
-
- continue;
- }
- else if( !Q_stricmp( token, "modelAnimation" ) )
- {
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- bp->modelAnimation.firstFrame = atoi_neg( token, qfalse );
-
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- bp->modelAnimation.numFrames = atoi( token );
- bp->modelAnimation.reversed = qfalse;
- bp->modelAnimation.flipflop = qfalse;
-
- // if numFrames is negative the animation is reversed
- if( bp->modelAnimation.numFrames < 0 )
- {
- bp->modelAnimation.numFrames = -bp->modelAnimation.numFrames;
- bp->modelAnimation.reversed = qtrue;
- }
-
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- bp->modelAnimation.loopFrames = atoi( token );
-
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- if( !Q_stricmp( token, "sync" ) )
- {
- bp->modelAnimation.frameLerp = -1;
- bp->modelAnimation.initialLerp = -1;
- }
- else
- {
- float fps = atof_neg( token, qfalse );
-
- if( fps == 0.0f )
- fps = 1.0f;
-
- bp->modelAnimation.frameLerp = 1000 / fps;
- bp->modelAnimation.initialLerp = 1000 / fps;
- }
-
- continue;
- }
- ///
- else if( !Q_stricmp( token, "velocityType" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- if( !Q_stricmp( token, "static" ) )
- bp->velMoveType = PMT_STATIC;
- else if( !Q_stricmp( token, "static_transform" ) )
- bp->velMoveType = PMT_STATIC_TRANSFORM;
- else if( !Q_stricmp( token, "tag" ) )
- bp->velMoveType = PMT_TAG;
- else if( !Q_stricmp( token, "cent" ) )
- bp->velMoveType = PMT_CENT_ANGLES;
- else if( !Q_stricmp( token, "normal" ) )
- bp->velMoveType = PMT_NORMAL;
-
- continue;
- }
- else if( !Q_stricmp( token, "velocityDir" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- if( !Q_stricmp( token, "linear" ) )
- bp->velMoveValues.dirType = PMD_LINEAR;
- else if( !Q_stricmp( token, "point" ) )
- bp->velMoveValues.dirType = PMD_POINT;
-
- continue;
- }
- else if( !Q_stricmp( token, "velocityMagnitude" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->velMoveValues.mag = number;
- bp->velMoveValues.magRandFrac = randFrac;
-
- continue;
- }
- else if( !Q_stricmp( token, "parentVelocityFraction" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->velMoveValues.parentVelFrac = number;
- bp->velMoveValues.parentVelFracRandFrac = randFrac;
-
- continue;
- }
- else if( !Q_stricmp( token, "velocity" ) )
- {
- for( i = 0; i <= 2; i++ )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- bp->velMoveValues.dir[ i ] = atof_neg( token, qtrue );
- }
-
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, NULL, &randFrac, qfalse );
-
- bp->velMoveValues.dirRandAngle = randFrac;
-
- continue;
- }
- else if( !Q_stricmp( token, "velocityPoint" ) )
- {
- for( i = 0; i <= 2; i++ )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- bp->velMoveValues.point[ i ] = atof_neg( token, qtrue );
- }
-
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, NULL, &randFrac, qfalse );
-
- bp->velMoveValues.pointRandAngle = randFrac;
-
- continue;
- }
- ///
- else if( !Q_stricmp( token, "accelerationType" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- if( !Q_stricmp( token, "static" ) )
- bp->accMoveType = PMT_STATIC;
- else if( !Q_stricmp( token, "static_transform" ) )
- bp->accMoveType = PMT_STATIC_TRANSFORM;
- else if( !Q_stricmp( token, "tag" ) )
- bp->accMoveType = PMT_TAG;
- else if( !Q_stricmp( token, "cent" ) )
- bp->accMoveType = PMT_CENT_ANGLES;
- else if( !Q_stricmp( token, "normal" ) )
- bp->accMoveType = PMT_NORMAL;
-
- continue;
- }
- else if( !Q_stricmp( token, "accelerationDir" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- if( !Q_stricmp( token, "linear" ) )
- bp->accMoveValues.dirType = PMD_LINEAR;
- else if( !Q_stricmp( token, "point" ) )
- bp->accMoveValues.dirType = PMD_POINT;
-
- continue;
- }
- else if( !Q_stricmp( token, "accelerationMagnitude" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->accMoveValues.mag = number;
- bp->accMoveValues.magRandFrac = randFrac;
-
- continue;
- }
- else if( !Q_stricmp( token, "acceleration" ) )
- {
- for( i = 0; i <= 2; i++ )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- bp->accMoveValues.dir[ i ] = atof_neg( token, qtrue );
- }
-
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, NULL, &randFrac, qfalse );
-
- bp->accMoveValues.dirRandAngle = randFrac;
-
- continue;
- }
- else if( !Q_stricmp( token, "accelerationPoint" ) )
- {
- for( i = 0; i <= 2; i++ )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- bp->accMoveValues.point[ i ] = atof_neg( token, qtrue );
- }
-
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, NULL, &randFrac, qfalse );
-
- bp->accMoveValues.pointRandAngle = randFrac;
-
- continue;
- }
- ///
- else if( !Q_stricmp( token, "displacement" ) )
- {
- for( i = 0; i <= 2; i++ )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- bp->displacement[ i ] = atof_neg( token, qtrue );
- }
-
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, NULL, &randFrac, qfalse );
-
- bp->randDisplacement = randFrac;
-
- continue;
- }
- else if( !Q_stricmp( token, "normalDisplacement" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- bp->normalDisplacement = atof_neg( token, qtrue );
-
- continue;
- }
- else if( !Q_stricmp( token, "overdrawProtection" ) )
- {
- bp->overdrawProtection = qtrue;
-
- continue;
- }
- else if( !Q_stricmp( token, "realLight" ) )
- {
- bp->realLight = qtrue;
-
- continue;
- }
- else if( !Q_stricmp( token, "dynamicLight" ) )
- {
- bp->dynamicLight = qtrue;
-
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->dLightRadius.delay = (int)number;
- bp->dLightRadius.delayRandFrac = randFrac;
-
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->dLightRadius.initial = number;
- bp->dLightRadius.initialRandFrac = randFrac;
-
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- if( !Q_stricmp( token, "-" ) )
- {
- bp->dLightRadius.final = PARTICLES_SAME_AS_INITIAL;
- bp->dLightRadius.finalRandFrac = 0.0f;
- }
- else
- {
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->dLightRadius.final = number;
- bp->dLightRadius.finalRandFrac = randFrac;
- }
-
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- if( !Q_stricmp( token, "{" ) )
- {
- if( !CG_ParseColor( bp->dLightColor, text_p ) )
- break;
-
- token = COM_Parse( text_p );
- if( Q_stricmp( token, "}" ) )
- {
- CG_Printf( S_COLOR_RED "ERROR: missing '}'\n" );
- break;
- }
- }
-
- continue;
- }
- else if( !Q_stricmp( token, "cullOnStartSolid" ) )
- {
- bp->cullOnStartSolid = qtrue;
-
- continue;
- }
- else if( !Q_stricmp( token, "radius" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->radius.delay = (int)number;
- bp->radius.delayRandFrac = randFrac;
-
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->radius.initial = number;
- bp->radius.initialRandFrac = randFrac;
-
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- if( !Q_stricmp( token, "-" ) )
- {
- bp->radius.final = PARTICLES_SAME_AS_INITIAL;
- bp->radius.finalRandFrac = 0.0f;
- }
- else
- {
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->radius.final = number;
- bp->radius.finalRandFrac = randFrac;
- }
-
- continue;
- }
- else if( !Q_stricmp( token, "alpha" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->alpha.delay = (int)number;
- bp->alpha.delayRandFrac = randFrac;
-
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->alpha.initial = number;
- bp->alpha.initialRandFrac = randFrac;
-
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- if( !Q_stricmp( token, "-" ) )
- {
- bp->alpha.final = PARTICLES_SAME_AS_INITIAL;
- bp->alpha.finalRandFrac = 0.0f;
- }
- else
- {
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->alpha.final = number;
- bp->alpha.finalRandFrac = randFrac;
- }
-
- continue;
- }
- else if( !Q_stricmp( token, "color" ) )
- {
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->colorDelay = (int)number;
- bp->colorDelayRandFrac = randFrac;
-
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- if( !Q_stricmp( token, "{" ) )
- {
- if( !CG_ParseColor( bp->initialColor, text_p ) )
- break;
-
- token = COM_Parse( text_p );
- if( Q_stricmp( token, "}" ) )
- {
- CG_Printf( S_COLOR_RED "ERROR: missing '}'\n" );
- break;
- }
-
- token = COM_Parse( text_p );
- if( !*token )
- break;
-
- if( !Q_stricmp( token, "-" ) )
- {
- bp->finalColor[ 0 ] = bp->initialColor[ 0 ];
- bp->finalColor[ 1 ] = bp->initialColor[ 1 ];
- bp->finalColor[ 2 ] = bp->initialColor[ 2 ];
- }
- else if( !Q_stricmp( token, "{" ) )
- {
- if( !CG_ParseColor( bp->finalColor, text_p ) )
- break;
-
- token = COM_Parse( text_p );
- if( Q_stricmp( token, "}" ) )
- {
- CG_Printf( S_COLOR_RED "ERROR: missing '}'\n" );
- break;
- }
- }
- else
- {
- CG_Printf( S_COLOR_RED "ERROR: missing '{'\n" );
- break;
- }
- }
- else
- {
- CG_Printf( S_COLOR_RED "ERROR: missing '{'\n" );
- break;
- }
-
- continue;
- }
- else if( !Q_stricmp( token, "rotation" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->rotation.delay = (int)number;
- bp->rotation.delayRandFrac = randFrac;
-
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qtrue );
-
- bp->rotation.initial = number;
- bp->rotation.initialRandFrac = randFrac;
-
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- if( !Q_stricmp( token, "-" ) )
- {
- bp->rotation.final = PARTICLES_SAME_AS_INITIAL;
- bp->rotation.finalRandFrac = 0.0f;
- }
- else
- {
- CG_ParseValueAndVariance( token, &number, &randFrac, qtrue );
-
- bp->rotation.final = number;
- bp->rotation.finalRandFrac = randFrac;
- }
-
- continue;
- }
- else if( !Q_stricmp( token, "lifeTime" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bp->lifeTime = (int)number;
- bp->lifeTimeRandFrac = randFrac;
-
- continue;
- }
- else if( !Q_stricmp( token, "childSystem" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- Q_strncpyz( bp->childSystemName, token, MAX_QPATH );
-
- continue;
- }
- else if( !Q_stricmp( token, "onDeathSystem" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- Q_strncpyz( bp->onDeathSystemName, token, MAX_QPATH );
-
- continue;
- }
- else if( !Q_stricmp( token, "childTrailSystem" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- Q_strncpyz( bp->childTrailSystemName, token, MAX_QPATH );
-
- continue;
- }
- else if( !Q_stricmp( token, "}" ) )
- return qtrue; //reached the end of this particle
- else
- {
- CG_Printf( S_COLOR_RED "ERROR: unknown token '%s' in particle\n", token );
- return qfalse;
- }
- }
-
- return qfalse;
-}
-
-/*
-===============
-CG_InitialiseBaseParticle
-===============
-*/
-static void CG_InitialiseBaseParticle( baseParticle_t *bp )
-{
- memset( bp, 0, sizeof( baseParticle_t ) );
-
- memset( bp->initialColor, 0xFF, sizeof( bp->initialColor ) );
- memset( bp->finalColor, 0xFF, sizeof( bp->finalColor ) );
-}
-
-/*
-===============
-CG_ParseParticleEjector
-
-Parse a particle ejector section
-===============
-*/
-static qboolean CG_ParseParticleEjector( baseParticleEjector_t *bpe, char **text_p )
-{
- char *token;
- float number, randFrac;
-
- // read optional parameters
- while( 1 )
- {
- token = COM_Parse( text_p );
-
- if( !token )
- break;
-
- if( !Q_stricmp( token, "" ) )
- return qfalse;
-
- if( !Q_stricmp( token, "{" ) )
- {
- CG_InitialiseBaseParticle( &baseParticles[ numBaseParticles ] );
-
- if( !CG_ParseParticle( &baseParticles[ numBaseParticles ], text_p ) )
- {
- CG_Printf( S_COLOR_RED "ERROR: failed to parse particle\n" );
- return qfalse;
- }
-
- if( bpe->numParticles == MAX_PARTICLES_PER_EJECTOR )
- {
- CG_Printf( S_COLOR_RED "ERROR: ejector has > %d particles\n", MAX_PARTICLES_PER_EJECTOR );
- return qfalse;
- }
- else if( numBaseParticles == MAX_BASEPARTICLES )
- {
- CG_Printf( S_COLOR_RED "ERROR: maximum number of particles (%d) reached\n", MAX_BASEPARTICLES );
- return qfalse;
- }
- else
- {
- //start parsing particles again
- bpe->particles[ bpe->numParticles ] = &baseParticles[ numBaseParticles ];
- bpe->numParticles++;
- numBaseParticles++;
- }
- continue;
- }
- else if( !Q_stricmp( token, "delay" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bpe->eject.delay = (int)number;
- bpe->eject.delayRandFrac = randFrac;
-
- continue;
- }
- else if( !Q_stricmp( token, "period" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- bpe->eject.initial = atoi_neg( token, qfalse );
-
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- if( !Q_stricmp( token, "-" ) )
- bpe->eject.final = PARTICLES_SAME_AS_INITIAL;
- else
- bpe->eject.final = atoi_neg( token, qfalse );
-
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- CG_ParseValueAndVariance( token, NULL, &bpe->eject.randFrac, qfalse );
-
- continue;
- }
- else if( !Q_stricmp( token, "count" ) )
- {
- token = COM_Parse( text_p );
- if( !token )
- break;
-
- if( !Q_stricmp( token, "infinite" ) )
- {
- bpe->totalParticles = PARTICLES_INFINITE;
- bpe->totalParticlesRandFrac = 0.0f;
- }
- else
- {
- CG_ParseValueAndVariance( token, &number, &randFrac, qfalse );
-
- bpe->totalParticles = (int)number;
- bpe->totalParticlesRandFrac = randFrac;
- }
-
- continue;
- }
- else if( !Q_stricmp( token, "particle" ) ) //acceptable text
- continue;
- else if( !Q_stricmp( token, "}" ) )
- return qtrue; //reached the end of this particle ejector
- else
- {
- CG_Printf( S_COLOR_RED "ERROR: unknown token '%s' in particle ejector\n", token );
- return qfalse;
- }
- }
-
- return qfalse;
-}
-
-
-/*
-===============
-CG_ParseParticleSystem
-
-Parse a particle system section
-===============
-*/
-static qboolean CG_ParseParticleSystem( baseParticleSystem_t *bps, char **text_p, const char *name )
-{
- char *token;
- baseParticleEjector_t *bpe;
-
- // read optional parameters
- while( 1 )
- {
- token = COM_Parse( text_p );
-
- if( !token )
- break;
-
- if( !Q_stricmp( token, "" ) )
- return qfalse;
-
- if( !Q_stricmp( token, "{" ) )
- {
- if( !CG_ParseParticleEjector( &baseParticleEjectors[ numBaseParticleEjectors ], text_p ) )
- {
- CG_Printf( S_COLOR_RED "ERROR: failed to parse particle ejector\n" );
- return qfalse;
- }
-
- bpe = &baseParticleEjectors[ numBaseParticleEjectors ];
-
- //check for infinite count + zero period
- if( bpe->totalParticles == PARTICLES_INFINITE &&
- ( bpe->eject.initial == 0.0f || bpe->eject.final == 0.0f ) )
- {
- CG_Printf( S_COLOR_RED "ERROR: ejector with 'count infinite' potentially has zero period\n" );
- return qfalse;
- }
-
- if( bps->numEjectors == MAX_EJECTORS_PER_SYSTEM )
- {
- CG_Printf( S_COLOR_RED "ERROR: particle system has > %d ejectors\n", MAX_EJECTORS_PER_SYSTEM );
- return qfalse;
- }
- else if( numBaseParticleEjectors == MAX_BASEPARTICLE_EJECTORS )
- {
- CG_Printf( S_COLOR_RED "ERROR: maximum number of particle ejectors (%d) reached\n",
- MAX_BASEPARTICLE_EJECTORS );
- return qfalse;
- }
- else
- {
- //start parsing ejectors again
- bps->ejectors[ bps->numEjectors ] = &baseParticleEjectors[ numBaseParticleEjectors ];
- bps->numEjectors++;
- numBaseParticleEjectors++;
- }
- continue;
- }
- else if( !Q_stricmp( token, "thirdPersonOnly" ) )
- bps->thirdPersonOnly = qtrue;
- else if( !Q_stricmp( token, "ejector" ) ) //acceptable text
- continue;
- else if( !Q_stricmp( token, "}" ) )
- {
- if( cg_debugParticles.integer >= 1 )
- CG_Printf( "Parsed particle system %s\n", name );
-
- return qtrue; //reached the end of this particle system
- }
- else
- {
- CG_Printf( S_COLOR_RED "ERROR: unknown token '%s' in particle system %s\n", token, bps->name );
- return qfalse;
- }
- }
-
- return qfalse;
-}
-
-/*
-===============
-CG_ParseParticleFile
-
-Load the particle systems from a particle file
-===============
-*/
-static qboolean CG_ParseParticleFile( const char *fileName )
-{
- char *text_p;
- int i;
- int len;
- char *token;
- char text[ 32000 ];
- char psName[ MAX_QPATH ];
- qboolean psNameSet = qfalse;
- fileHandle_t f;
-
- // load the file
- len = trap_FS_FOpenFile( fileName, &f, FS_READ );
- if( len <= 0 )
- return qfalse;
-
- if( len >= sizeof( text ) - 1 )
- {
- CG_Printf( S_COLOR_RED "ERROR: particle file %s too long\n", fileName );
- return qfalse;
- }
-
- trap_FS_Read( text, len, f );
- text[ len ] = 0;
- trap_FS_FCloseFile( f );
-
- // parse the text
- text_p = text;
-
- // read optional parameters
- while( 1 )
- {
- token = COM_Parse( &text_p );
-
- if( !Q_stricmp( token, "" ) )
- break;
-
- if( !Q_stricmp( token, "{" ) )
- {
- if( psNameSet )
- {
- //check for name space clashes
- for( i = 0; i < numBaseParticleSystems; i++ )
- {
- if( !Q_stricmp( baseParticleSystems[ i ].name, psName ) )
- {
- CG_Printf( S_COLOR_RED "ERROR: a particle system is already named %s\n", psName );
- return qfalse;
- }
- }
-
- Q_strncpyz( baseParticleSystems[ numBaseParticleSystems ].name, psName, MAX_QPATH );
-
- if( !CG_ParseParticleSystem( &baseParticleSystems[ numBaseParticleSystems ], &text_p, psName ) )
- {
- CG_Printf( S_COLOR_RED "ERROR: %s: failed to parse particle system %s\n", fileName, psName );
- return qfalse;
- }
-
- //start parsing particle systems again
- psNameSet = qfalse;
-
- if( numBaseParticleSystems == MAX_BASEPARTICLE_SYSTEMS )
- {
- CG_Printf( S_COLOR_RED "ERROR: maximum number of particle systems (%d) reached\n",
- MAX_BASEPARTICLE_SYSTEMS );
- return qfalse;
- }
- else
- numBaseParticleSystems++;
-
- continue;
- }
- else
- {
- CG_Printf( S_COLOR_RED "ERROR: unamed particle system\n" );
- return qfalse;
- }
- }
-
- if( !psNameSet )
- {
- Q_strncpyz( psName, token, sizeof( psName ) );
- psNameSet = qtrue;
- }
- else
- {
- CG_Printf( S_COLOR_RED "ERROR: particle system already named\n" );
- return qfalse;
- }
- }
-
- return qtrue;
-}
-
-
-/*
-===============
-CG_LoadParticleSystems
-
-Load particle systems from .particle files
-===============
-*/
-void CG_LoadParticleSystems( void )
-{
- int i, j;
- const char *s[ MAX_PARTICLE_FILES ];
-
- //clear out the old
- numBaseParticleSystems = 0;
- numBaseParticleEjectors = 0;
- numBaseParticles = 0;
-
- for( i = 0; i < MAX_BASEPARTICLE_SYSTEMS; i++ )
- {
- baseParticleSystem_t *bps = &baseParticleSystems[ i ];
- memset( bps, 0, sizeof( baseParticleSystem_t ) );
- }
-
- for( i = 0; i < MAX_BASEPARTICLE_EJECTORS; i++ )
- {
- baseParticleEjector_t *bpe = &baseParticleEjectors[ i ];
- memset( bpe, 0, sizeof( baseParticleEjector_t ) );
- }
-
- for( i = 0; i < MAX_BASEPARTICLES; i++ )
- {
- baseParticle_t *bp = &baseParticles[ i ];
- memset( bp, 0, sizeof( baseParticle_t ) );
- }
-
-
- //and bring in the new
- for( i = 0; i < MAX_PARTICLE_FILES; i++ )
- {
- s[ i ] = CG_ConfigString( CS_PARTICLE_FILES + i );
-
- if( strlen( s[ i ] ) > 0 )
- {
- CG_Printf( "...loading '%s'\n", s[ i ] );
- CG_ParseParticleFile( s[ i ] );
- }
- else
- break;
- }
-
- //connect any child systems to their psHandle
- for( i = 0; i < numBaseParticles; i++ )
- {
- baseParticle_t *bp = &baseParticles[ i ];
-
- if( bp->childSystemName[ 0 ] )
- {
- //particle class has a child, resolve the name
- for( j = 0; j < numBaseParticleSystems; j++ )
- {
- baseParticleSystem_t *bps = &baseParticleSystems[ j ];
-
- if( !Q_stricmp( bps->name, bp->childSystemName ) )
- {
- //FIXME: add checks for cycles and infinite children
-
- bp->childSystemHandle = j + 1;
-
- break;
- }
- }
-
- if( j == numBaseParticleSystems )
- {
- //couldn't find named particle system
- CG_Printf( S_COLOR_YELLOW "WARNING: failed to find child %s\n", bp->childSystemName );
- bp->childSystemName[ 0 ] = '\0';
- }
- }
-
- if( bp->onDeathSystemName[ 0 ] )
- {
- //particle class has a child, resolve the name
- for( j = 0; j < numBaseParticleSystems; j++ )
- {
- baseParticleSystem_t *bps = &baseParticleSystems[ j ];
-
- if( !Q_stricmp( bps->name, bp->onDeathSystemName ) )
- {
- //FIXME: add checks for cycles and infinite children
-
- bp->onDeathSystemHandle = j + 1;
-
- break;
- }
- }
-
- if( j == numBaseParticleSystems )
- {
- //couldn't find named particle system
- CG_Printf( S_COLOR_YELLOW "WARNING: failed to find onDeath system %s\n", bp->onDeathSystemName );
- bp->onDeathSystemName[ 0 ] = '\0';
- }
- }
- }
-}
-
-/*
-===============
-CG_SetParticleSystemNormal
-===============
-*/
-void CG_SetParticleSystemNormal( particleSystem_t *ps, vec3_t normal )
-{
- if( ps == NULL || !ps->valid )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" );
- return;
- }
-
- ps->normalValid = qtrue;
- VectorCopy( normal, ps->normal );
- VectorNormalize( ps->normal );
-}
-
-
-/*
-===============
-CG_DestroyParticleSystem
-
-Destroy a particle system
-
-This doesn't actually invalidate anything, it just stops
-particle ejectors from producing new particles so the
-garbage collector will eventually remove this system.
-However is does set the pointer to NULL so the user is
-unable to manipulate this particle system any longer.
-===============
-*/
-void CG_DestroyParticleSystem( particleSystem_t **ps )
-{
- int i;
- particleEjector_t *pe;
-
- if( *ps == NULL || !(*ps)->valid )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: tried to destroy a NULL particle system\n" );
- return;
- }
-
- if( cg_debugParticles.integer >= 1 )
- CG_Printf( "PS destroyed\n" );
-
- for( i = 0; i < MAX_PARTICLE_EJECTORS; i++ )
- {
- pe = &particleEjectors[ i ];
-
- if( pe->valid && pe->parent == *ps )
- pe->totalParticles = pe->count = 0;
- }
-
- *ps = NULL;
-}
-
-/*
-===============
-CG_IsParticleSystemInfinite
-
-Test a particle system for 'count infinite' ejectors
-===============
-*/
-qboolean CG_IsParticleSystemInfinite( particleSystem_t *ps )
-{
- int i;
- particleEjector_t *pe;
-
- if( ps == NULL )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: tried to test a NULL particle system\n" );
- return qfalse;
- }
-
- if( !ps->valid )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: tried to test an invalid particle system\n" );
- return qfalse;
- }
-
- //don't bother checking already invalid systems
- if( !ps->valid )
- return qfalse;
-
- for( i = 0; i < MAX_PARTICLE_EJECTORS; i++ )
- {
- pe = &particleEjectors[ i ];
-
- if( pe->valid && pe->parent == ps )
- {
- if( pe->totalParticles == PARTICLES_INFINITE )
- return qtrue;
- }
- }
-
- return qfalse;
-}
-
-/*
-===============
-CG_IsParticleSystemValid
-
-Test a particle system for validity
-===============
-*/
-qboolean CG_IsParticleSystemValid( particleSystem_t **ps )
-{
- if( *ps == NULL || ( *ps && !(*ps)->valid ) )
- {
- if( *ps && !(*ps)->valid )
- *ps = NULL;
-
- return qfalse;
- }
-
- return qtrue;
-}
-
-/*
-===============
-CG_GarbageCollectParticleSystems
-
-Destroy inactive particle systems
-===============
-*/
-static void CG_GarbageCollectParticleSystems( void )
-{
- int i, j, count;
- particleSystem_t *ps;
- particleEjector_t *pe;
- int centNum;
-
- for( i = 0; i < MAX_PARTICLE_SYSTEMS; i++ )
- {
- ps = &particleSystems[ i ];
- count = 0;
-
- //don't bother checking already invalid systems
- if( !ps->valid )
- continue;
-
- for( j = 0; j < MAX_PARTICLE_EJECTORS; j++ )
- {
- pe = &particleEjectors[ j ];
-
- if( pe->valid && pe->parent == ps )
- count++;
- }
-
- if( !count )
- ps->valid = qfalse;
-
- //check systems where the parent cent has left the PVS
- //( local player entity is always valid )
- if( ( centNum = CG_AttachmentCentNum( &ps->attachment ) ) >= 0 &&
- centNum != cg.snap->ps.clientNum )
- {
- if( !cg_entities[ centNum ].valid )
- ps->lazyRemove = qtrue;
- }
-
- if( cg_debugParticles.integer >= 1 && !ps->valid )
- CG_Printf( "PS %s garbage collected\n", ps->class->name );
- }
-}
-
-
-/*
-===============
-CG_CalculateTimeFrac
-
-Calculate the fraction of time passed
-===============
-*/
-static float CG_CalculateTimeFrac( int birth, int life, int delay )
-{
- float frac;
-
- frac = ( (float)cg.time - (float)( birth + delay ) ) / (float)( life - delay );
-
- if( frac < 0.0f )
- frac = 0.0f;
- else if( frac > 1.0f )
- frac = 1.0f;
-
- return frac;
-}
-
-/*
-===============
-CG_EvaluateParticlePhysics
-
-Compute the physics on a specific particle
-===============
-*/
-static void CG_EvaluateParticlePhysics( particle_t *p )
-{
- particleSystem_t *ps = p->parent->parent;
- baseParticle_t *bp = p->class;
- vec3_t acceleration, newOrigin;
- vec3_t mins, maxs;
- float deltaTime, bounce, radius, dot;
- trace_t trace;
- vec3_t transform[ 3 ];
-
- if( p->atRest )
- {
- VectorClear( p->velocity );
- return;
- }
-
- switch( bp->accMoveType )
- {
- case PMT_STATIC:
- if( bp->accMoveValues.dirType == PMD_POINT )
- VectorSubtract( bp->accMoveValues.point, p->origin, acceleration );
- else if( bp->accMoveValues.dirType == PMD_LINEAR )
- VectorCopy( bp->accMoveValues.dir, acceleration );
-
- break;
-
- case PMT_STATIC_TRANSFORM:
- if( !CG_AttachmentAxis( &ps->attachment, transform ) )
- {
- CG_Printf( S_COLOR_RED "ERROR: a particle with accelerationType "
- "static_transform is not attached to something which can "
- "provide a transformation\n" );
- return;
- }
-
- if( bp->accMoveValues.dirType == PMD_POINT )
- {
- vec3_t transPoint;
-
- VectorMatrixMultiply( bp->accMoveValues.point, transform, transPoint );
- VectorSubtract( transPoint, p->origin, acceleration );
- }
- else if( bp->accMoveValues.dirType == PMD_LINEAR )
- VectorMatrixMultiply( bp->accMoveValues.dir, transform, acceleration );
- break;
-
- case PMT_TAG:
- case PMT_CENT_ANGLES:
- if( bp->accMoveValues.dirType == PMD_POINT )
- {
- vec3_t point;
-
- if( !CG_AttachmentPoint( &ps->attachment, point ) )
- return;
-
- VectorSubtract( point, p->origin, acceleration );
- }
- else if( bp->accMoveValues.dirType == PMD_LINEAR )
- {
- if( !CG_AttachmentDir( &ps->attachment, acceleration ) )
- return;
- }
- break;
-
- case PMT_NORMAL:
- if( !ps->normalValid )
- return;
-
- VectorCopy( ps->normal, acceleration );
-
- break;
- }
-
-#define MAX_ACC_RADIUS 1000.0f
-
- if( bp->accMoveValues.dirType == PMD_POINT )
- {
- //FIXME: so this fall off is a bit... odd -- it works..
- float r2 = DotProduct( acceleration, acceleration ); // = radius^2
- float scale = ( MAX_ACC_RADIUS - r2 ) / MAX_ACC_RADIUS;
-
- if( scale > 1.0f )
- scale = 1.0f;
- else if( scale < 0.1f )
- scale = 0.1f;
-
- scale *= CG_RandomiseValue( bp->accMoveValues.mag, bp->accMoveValues.magRandFrac );
-
- VectorNormalize( acceleration );
- CG_SpreadVector( acceleration, bp->accMoveValues.dirRandAngle );
- VectorScale( acceleration, scale, acceleration );
- }
- else if( bp->accMoveValues.dirType == PMD_LINEAR )
- {
- VectorNormalize( acceleration );
- CG_SpreadVector( acceleration, bp->accMoveValues.dirRandAngle );
- VectorScale( acceleration,
- CG_RandomiseValue( bp->accMoveValues.mag, bp->accMoveValues.magRandFrac ),
- acceleration );
- }
-
- radius = CG_LerpValues( p->radius.initial,
- p->radius.final,
- CG_CalculateTimeFrac( p->birthTime,
- p->lifeTime,
- p->radius.delay ) );
-
- VectorSet( mins, -radius, -radius, -radius );
- VectorSet( maxs, radius, radius, radius );
-
- bounce = CG_RandomiseValue( bp->bounceFrac, bp->bounceFracRandFrac );
-
- deltaTime = (float)( cg.time - p->lastEvalTime ) * 0.001;
- VectorMA( p->velocity, deltaTime, acceleration, p->velocity );
- VectorMA( p->origin, deltaTime, p->velocity, newOrigin );
- p->lastEvalTime = cg.time;
-
- CG_Trace( &trace, p->origin, mins, maxs, newOrigin,
- CG_AttachmentCentNum( &ps->attachment ), CONTENTS_SOLID );
-
- //not hit anything or not a collider
- if( trace.fraction == 1.0f || bounce == 0.0f )
- {
- VectorCopy( newOrigin, p->origin );
- return;
- }
-
- //remove particles that get into a CONTENTS_NODROP brush
- if( ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) ||
- ( bp->cullOnStartSolid && trace.startsolid ) )
- {
- CG_DestroyParticle( p, NULL );
- return;
- }
- else if( bp->bounceCull )
- {
- CG_DestroyParticle( p, trace.plane.normal );
- return;
- }
-
- //reflect the velocity on the trace plane
- dot = DotProduct( p->velocity, trace.plane.normal );
- VectorMA( p->velocity, -2.0f * dot, trace.plane.normal, p->velocity );
-
- VectorScale( p->velocity, bounce, p->velocity );
-
- if( trace.plane.normal[ 2 ] > 0.5f &&
- ( p->velocity[ 2 ] < 40.0f ||
- p->velocity[ 2 ] < -cg.frametime * p->velocity[ 2 ] ) )
- p->atRest = qtrue;
-
- if( bp->bounceMarkName[ 0 ] && p->bounceMarkCount > 0 )
- {
- CG_ImpactMark( bp->bounceMark, trace.endpos, trace.plane.normal,
- random( ) * 360, 1, 1, 1, 1, qtrue, bp->bounceMarkRadius, qfalse );
- p->bounceMarkCount--;
- }
-
- if( bp->bounceSoundName[ 0 ] && p->bounceSoundCount > 0 )
- {
- trap_S_StartSound( trace.endpos, ENTITYNUM_WORLD, CHAN_AUTO, bp->bounceSound );
- p->bounceSoundCount--;
- }
-
- VectorCopy( trace.endpos, p->origin );
-}
-
-
-#define GETKEY(x,y) (((x)>>y)&0xFF)
-
-/*
-===============
-CG_Radix
-===============
-*/
-static void CG_Radix( int bits, int size, particle_t **source, particle_t **dest )
-{
- int count[ 256 ];
- int index[ 256 ];
- int i;
-
- memset( count, 0, sizeof( count ) );
-
- for( i = 0; i < size; i++ )
- count[ GETKEY( source[ i ]->sortKey, bits ) ]++;
-
- index[ 0 ] = 0;
-
- for( i = 1; i < 256; i++ )
- index[ i ] = index[ i - 1 ] + count[ i - 1 ];
-
- for( i = 0; i < size; i++ )
- dest[ index[ GETKEY( source[ i ]->sortKey, bits ) ]++ ] = source[ i ];
-}
-
-/*
-===============
-CG_RadixSort
-
-Radix sort with 4 byte size buckets
-===============
-*/
-static void CG_RadixSort( particle_t **source, particle_t **temp, int size )
-{
- CG_Radix( 0, size, source, temp );
- CG_Radix( 8, size, temp, source );
- CG_Radix( 16, size, source, temp );
- CG_Radix( 24, size, temp, source );
-}
-
-/*
-===============
-CG_CompactAndSortParticles
-
-Depth sort the particles
-===============
-*/
-static void CG_CompactAndSortParticles( void )
-{
- int i, j = 0;
- int numParticles;
- vec3_t delta;
-
- for( i = 0; i < MAX_PARTICLES; i++ )
- sortedParticles[ i ] = &particles[ i ];
-
- for( i = MAX_PARTICLES - 1; i >= 0; i-- )
- {
- if( sortedParticles[ i ]->valid )
- {
- //find the first hole
- while( sortedParticles[ j ]->valid )
- j++;
-
- //no more holes
- if( j >= i )
- break;
-
- sortedParticles[ j ] = sortedParticles[ i ];
- }
- }
-
- numParticles = i;
-
- //set sort keys
- for( i = 0; i < numParticles; i++ )
- {
- VectorSubtract( sortedParticles[ i ]->origin, cg.refdef.vieworg, delta );
- sortedParticles[ i ]->sortKey = (int)DotProduct( delta, delta );
- }
-
- CG_RadixSort( sortedParticles, radixBuffer, numParticles );
-
- //FIXME: wtf?
- //reverse order of particles array
- for( i = 0; i < numParticles; i++ )
- radixBuffer[ i ] = sortedParticles[ numParticles - i - 1 ];
-
- for( i = 0; i < numParticles; i++ )
- sortedParticles[ i ] = radixBuffer[ i ];
-}
-
-/*
-===============
-CG_RenderParticle
-
-Actually render a particle
-===============
-*/
-static void CG_RenderParticle( particle_t *p )
-{
- refEntity_t re;
- float timeFrac, scale;
- int index;
- baseParticle_t *bp = p->class;
- particleSystem_t *ps = p->parent->parent;
- baseParticleSystem_t *bps = ps->class;
- vec3_t alight, dlight, lightdir;
- int i;
- vec3_t up = { 0.0f, 0.0f, 1.0f };
-
- memset( &re, 0, sizeof( refEntity_t ) );
-
- timeFrac = CG_CalculateTimeFrac( p->birthTime, p->lifeTime, 0 );
-
- scale = CG_LerpValues( p->radius.initial,
- p->radius.final,
- CG_CalculateTimeFrac( p->birthTime,
- p->lifeTime,
- p->radius.delay ) );
-
- re.shaderTime = p->birthTime / 1000.0f;
-
- if( bp->numFrames ) //shader based
- {
- re.reType = RT_SPRITE;
-
- //apply environmental lighting to the particle
- if( bp->realLight )
- {
- trap_R_LightForPoint( p->origin, alight, dlight, lightdir );
- for( i = 0; i <= 2; i++ )
- re.shaderRGBA[ i ] = (byte)alight[ i ];
- }
- else
- {
- vec3_t colorRange;
-
- VectorSubtract( bp->finalColor,
- bp->initialColor, colorRange );
-
- VectorMA( bp->initialColor,
- CG_CalculateTimeFrac( p->birthTime,
- p->lifeTime,
- p->colorDelay ),
- colorRange, re.shaderRGBA );
- }
-
- re.shaderRGBA[ 3 ] = (byte)( (float)0xFF *
- CG_LerpValues( p->alpha.initial,
- p->alpha.final,
- CG_CalculateTimeFrac( p->birthTime,
- p->lifeTime,
- p->alpha.delay ) ) );
-
- re.radius = scale;
-
- re.rotation = CG_LerpValues( p->rotation.initial,
- p->rotation.final,
- CG_CalculateTimeFrac( p->birthTime,
- p->lifeTime,
- p->rotation.delay ) );
-
- // if the view would be "inside" the sprite, kill the sprite
- // so it doesn't add too much overdraw
- if( Distance( p->origin, cg.refdef.vieworg ) < re.radius && bp->overdrawProtection )
- return;
-
- if( bp->framerate == 0.0f )
- {
- //sync animation time to lifeTime of particle
- index = (int)( timeFrac * ( bp->numFrames + 1 ) );
-
- if( index >= bp->numFrames )
- index = bp->numFrames - 1;
-
- re.customShader = bp->shaders[ index ];
- }
- else
- {
- //looping animation
- index = (int)( bp->framerate * timeFrac * p->lifeTime * 0.001 ) % bp->numFrames;
- re.customShader = bp->shaders[ index ];
- }
-
- }
- else if( bp->numModels ) //model based
- {
- re.reType = RT_MODEL;
-
- re.hModel = p->model;
-
- if( p->atRest )
- AxisCopy( p->lastAxis, re.axis );
- else
- {
- // convert direction of travel into axis
- VectorNormalize2( p->velocity, re.axis[ 0 ] );
-
- if( re.axis[ 0 ][ 0 ] == 0.0f && re.axis[ 0 ][ 1 ] == 0.0f )
- AxisCopy( axisDefault, re.axis );
- else
- {
- ProjectPointOnPlane( re.axis[ 2 ], up, re.axis[ 0 ] );
- VectorNormalize( re.axis[ 2 ] );
- CrossProduct( re.axis[ 2 ], re.axis[ 0 ], re.axis[ 1 ] );
- }
-
- AxisCopy( re.axis, p->lastAxis );
- }
-
- if( scale != 1.0f )
- {
- VectorScale( re.axis[ 0 ], scale, re.axis[ 0 ] );
- VectorScale( re.axis[ 1 ], scale, re.axis[ 1 ] );
- VectorScale( re.axis[ 2 ], scale, re.axis[ 2 ] );
- re.nonNormalizedAxes = qtrue;
- }
- else
- re.nonNormalizedAxes = qfalse;
-
- p->lf.animation = &bp->modelAnimation;
-
- //run animation
- CG_RunLerpFrame( &p->lf );
-
- re.oldframe = p->lf.oldFrame;
- re.frame = p->lf.frame;
- re.backlerp = p->lf.backlerp;
- }
-
- if( bps->thirdPersonOnly &&
- CG_AttachmentCentNum( &ps->attachment ) == cg.snap->ps.clientNum &&
- !cg.renderingThirdPerson )
- re.renderfx |= RF_THIRD_PERSON;
-
- if( bp->dynamicLight && !( re.renderfx & RF_THIRD_PERSON ) )
- {
- trap_R_AddLightToScene( p->origin,
- CG_LerpValues( p->dLightRadius.initial, p->dLightRadius.final,
- CG_CalculateTimeFrac( p->birthTime, p->lifeTime, p->dLightRadius.delay ) ),
- (float)bp->dLightColor[ 0 ] / (float)0xFF,
- (float)bp->dLightColor[ 1 ] / (float)0xFF,
- (float)bp->dLightColor[ 2 ] / (float)0xFF );
- }
-
- VectorCopy( p->origin, re.origin );
-
- trap_R_AddRefEntityToScene( &re );
-}
-
-/*
-===============
-CG_AddParticles
-
-Add particles to the scene
-===============
-*/
-void CG_AddParticles( void )
-{
- int i;
- particle_t *p;
- int numPS = 0, numPE = 0, numP = 0;
-
- //remove expired particle systems
- CG_GarbageCollectParticleSystems( );
-
- //check each ejector and introduce any new particles
- CG_SpawnNewParticles( );
-
- //sorting
- if( cg_depthSortParticles.integer )
- CG_CompactAndSortParticles( );
-
- for( i = 0; i < MAX_PARTICLES; i++ )
- {
- p = sortedParticles[ i ];
-
- if( p->valid )
- {
- if( p->birthTime + p->lifeTime > cg.time )
- {
- //particle is active
- CG_EvaluateParticlePhysics( p );
- CG_RenderParticle( p );
- }
- else
- CG_DestroyParticle( p, NULL );
- }
- }
-
- if( cg_debugParticles.integer >= 2 )
- {
- for( i = 0; i < MAX_PARTICLE_SYSTEMS; i++ )
- if( particleSystems[ i ].valid )
- numPS++;
-
- for( i = 0; i < MAX_PARTICLE_EJECTORS; i++ )
- if( particleEjectors[ i ].valid )
- numPE++;
-
- for( i = 0; i < MAX_PARTICLES; i++ )
- if( particles[ i ].valid )
- numP++;
-
- CG_Printf( "PS: %d PE: %d P: %d\n", numPS, numPE, numP );
- }
-}
-
-/*
-===============
-CG_ParticleSystemEntity
-
-Particle system entity client code
-===============
-*/
-void CG_ParticleSystemEntity( centity_t *cent )
-{
- entityState_t *es;
-
- es = &cent->currentState;
-
- if( es->eFlags & EF_NODRAW )
- {
- if( CG_IsParticleSystemValid( &cent->entityPS ) && CG_IsParticleSystemInfinite( cent->entityPS ) )
- CG_DestroyParticleSystem( &cent->entityPS );
-
- return;
- }
-
- if( !CG_IsParticleSystemValid( &cent->entityPS ) && !cent->entityPSMissing )
- {
- cent->entityPS = CG_SpawnNewParticleSystem( cgs.gameParticleSystems[ es->modelindex ] );
-
- if( CG_IsParticleSystemValid( &cent->entityPS ) )
- {
- CG_SetAttachmentPoint( &cent->entityPS->attachment, cent->lerpOrigin );
- CG_SetAttachmentCent( &cent->entityPS->attachment, cent );
- CG_AttachToPoint( &cent->entityPS->attachment );
- }
- else
- cent->entityPSMissing = qtrue;
- }
-}
-
-static particleSystem_t *testPS;
-static qhandle_t testPSHandle;
-
-/*
-===============
-CG_DestroyTestPS_f
-
-Destroy the test a particle system
-===============
-*/
-void CG_DestroyTestPS_f( void )
-{
- if( CG_IsParticleSystemValid( &testPS ) )
- CG_DestroyParticleSystem( &testPS );
-}
-
-/*
-===============
-CG_TestPS_f
-
-Test a particle system
-===============
-*/
-void CG_TestPS_f( void )
-{
- vec3_t origin;
- vec3_t up = { 0.0f, 0.0f, 1.0f };
- char psName[ MAX_QPATH ];
-
- if( trap_Argc( ) < 2 )
- return;
-
- Q_strncpyz( psName, CG_Argv( 1 ), MAX_QPATH );
- testPSHandle = CG_RegisterParticleSystem( psName );
-
- if( testPSHandle )
- {
- CG_DestroyTestPS_f( );
-
- testPS = CG_SpawnNewParticleSystem( testPSHandle );
-
- VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[ 0 ], origin );
-
- if( CG_IsParticleSystemValid( &testPS ) )
- {
- CG_SetAttachmentPoint( &testPS->attachment, origin );
- CG_SetParticleSystemNormal( testPS, up );
- CG_AttachToPoint( &testPS->attachment );
- }
- }
-}