diff options
Diffstat (limited to 'src/cgame/cg_particles.c')
-rw-r--r-- | src/cgame/cg_particles.c | 500 |
1 files changed, 138 insertions, 362 deletions
diff --git a/src/cgame/cg_particles.c b/src/cgame/cg_particles.c index b6a52d90..a6ef99e5 100644 --- a/src/cgame/cg_particles.c +++ b/src/cgame/cg_particles.c @@ -25,7 +25,8 @@ 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 sortParticles[ MAX_PARTICLES ]; +static particle_t *sortedParticles[ MAX_PARTICLES ]; +static particle_t *radixBuffer[ MAX_PARTICLES ]; /* =============== @@ -85,7 +86,7 @@ CG_DestroyParticle Destroy an individual particle =============== */ -static void CG_DestroyParticle( particle_t *p ) +static void CG_DestroyParticle( particle_t *p, vec3_t impactNormal ) { //this particle has an onDeath particle system attached if( p->class->onDeathSystemName[ 0 ] != '\0' ) @@ -96,13 +97,19 @@ static void CG_DestroyParticle( particle_t *p ) if( CG_IsParticleSystemValid( &ps ) ) { - CG_SetParticleSystemOrigin( ps, p->origin ); - CG_SetParticleSystemNormal( ps, p->velocity ); - CG_AttachParticleSystemToOrigin( 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; } /* @@ -118,14 +125,14 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p particle_t *p = NULL; particleEjector_t *pe = parent; particleSystem_t *ps = parent->parent; - vec3_t forward; - centity_t *cent = &cg_entities[ ps->attachment.centNum ]; + vec3_t attachmentPoint, attachmentVelocity; for( i = 0; i < MAX_PARTICLES; i++ ) { p = &particles[ i ]; - if( !p->valid ) + //FIXME: the + 1 may be unnecessary + if( !p->valid && cg.clientFrame > p->frameWhenInvalidated + 1 ) { memset( p, 0, sizeof( particle_t ) ); @@ -148,59 +155,10 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p p->rotation.initial = CG_RandomiseValue( bp->rotation.initial, bp->rotation.initialRandFrac ); p->rotation.final = CG_RandomiseValue( bp->rotation.final, bp->rotation.finalRandFrac ); - switch( ps->attachType ) - { - case PSA_STATIC: - if( !ps->attachment.staticValid ) - return NULL; - - VectorCopy( ps->attachment.origin, p->origin ); - break; - - case PSA_TAG: - if( !ps->attachment.tagValid ) - return NULL; - - AxisCopy( axisDefault, ps->attachment.re.axis ); - CG_PositionRotatedEntityOnTag( &ps->attachment.re, &ps->attachment.parent, - ps->attachment.model, ps->attachment.tagName ); - VectorCopy( ps->attachment.re.origin, p->origin ); - break; - - case PSA_CENT_ORIGIN: - if( !ps->attachment.centValid ) - return NULL; - - VectorCopy( cent->lerpOrigin, p->origin ); - break; - - case PSA_PARTICLE: - if( !ps->attachment.particleValid ) - return NULL; - - //find a particle which has ps as a child - for( j = 0; j < MAX_PARTICLES; j++ ) - { - particle_t *parentParticle = &particles[ j ]; - - if( parentParticle->valid && parentParticle->childSystem == ps ) - { - VectorCopy( parentParticle->origin, p->origin ); - break; - } - } - - if( j == MAX_PARTICLES ) - { - //didn't find the parent, so it's probably died already - - //prevent further (expensive) attempts at particle creation - ps->attachment.particleValid = qfalse; - return NULL; - } - break; - } + if( !CG_AttachmentPoint( &ps->attachment, attachmentPoint ) ) + return NULL; + VectorCopy( attachmentPoint, p->origin ); VectorAdd( p->origin, bp->displacement, p->origin ); for( j = 0; j <= 2; j++ ) @@ -209,52 +167,32 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p 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_TAG: - - if( !ps->attachment.tagValid ) - return NULL; - - if( bp->velMoveValues.dirType == PMD_POINT ) - VectorSubtract( ps->attachment.re.origin, p->origin, p->velocity ); - else if( bp->velMoveValues.dirType == PMD_LINEAR ) - VectorCopy( ps->attachment.re.axis[ 0 ], p->velocity ); - - break; - case PMT_CENT_ANGLES: - - if( !ps->attachment.centValid ) - return NULL; - if( bp->velMoveValues.dirType == PMD_POINT ) - VectorSubtract( cent->lerpOrigin, p->origin, p->velocity ); + VectorSubtract( attachmentPoint, p->origin, p->velocity ); else if( bp->velMoveValues.dirType == PMD_LINEAR ) { - AngleVectors( cent->lerpAngles, forward, NULL, NULL ); - VectorCopy( forward, p->velocity ); + if( !CG_AttachmentDir( &ps->attachment, p->velocity ) ) + return NULL; } - break; case PMT_NORMAL: - - if( !ps->attachment.normalValid ) + if( !ps->normalValid ) return NULL; - VectorCopy( ps->attachment.normal, p->velocity ); + VectorCopy( ps->normal, p->velocity ); //normal displacement VectorNormalize( p->velocity ); VectorMA( p->origin, bp->normalDisplacement, p->velocity, p->origin ); - break; } @@ -264,11 +202,11 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p CG_RandomiseValue( bp->velMoveValues.mag, bp->velMoveValues.magRandFrac ), p->velocity ); - if( ps->attachment.centValid ) + if( CG_AttachmentVelocity( &ps->attachment, attachmentVelocity ) ) { VectorMA( p->velocity, - CG_RandomiseValue( bp->velMoveValues.parentVelFrac, bp->velMoveValues.parentVelFracRandFrac ), - cent->currentState.pos.trDelta, p->velocity ); + CG_RandomiseValue( bp->velMoveValues.parentVelFrac, + bp->velMoveValues.parentVelFracRandFrac ), attachmentVelocity, p->velocity ); } p->lastEvalTime = cg.time; @@ -278,15 +216,12 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p //this particle has a child particle system attached if( bp->childSystemName[ 0 ] != '\0' ) { - particleSystem_t *ps; - - ps = CG_SpawnNewParticleSystem( bp->childSystemHandle ); + particleSystem_t *ps = CG_SpawnNewParticleSystem( bp->childSystemHandle ); if( CG_IsParticleSystemValid( &ps ) ) { - CG_SetParticleSystemParentParticle( ps, p ); - CG_SetParticleSystemNormal( ps, p->velocity ); - CG_AttachParticleSystemToParticle( ps ); + CG_SetAttachmentParticle( &ps->attachment, p ); + CG_AttachToParticle( &ps->attachment ); } } @@ -324,7 +259,7 @@ static void CG_SpawnNewParticles( void ) if( pe->valid ) { //a non attached particle system can't make particles - if( !ps->attached ) + if( !CG_Attached( &ps->attachment ) ) continue; bpe = particleEjectors[ i ].class; @@ -528,60 +463,13 @@ qhandle_t CG_RegisterParticleSystem( char *name ) } } - CG_Printf( S_COLOR_RED "ERROR: failed to load particle system %s\n", name ); + CG_Printf( S_COLOR_RED "ERROR: failed to register particle system %s\n", name ); return 0; } /* =============== -atof_neg - -atof with an allowance for negative values -=============== -*/ -static float atof_neg( char *token, qboolean allowNegative ) -{ - float value; - - value = atof( token ); - - if( !allowNegative && value < 0.0f ) - { - CG_Printf( S_COLOR_YELLOW "WARNING: negative value %f is now allowed here, " - "replaced with 1.0f\n", value ); - value = 1.0f; - } - - return value; -} - -/* -=============== -atoi_neg - -atoi with an allowance for negative values -=============== -*/ -static int atoi_neg( char *token, qboolean allowNegative ) -{ - int value; - - value = atoi( token ); - - if( !allowNegative && value < 0 ) - { - CG_Printf( S_COLOR_YELLOW "WARNING: negative value %d is now allowed here, " - "replaced with 1\n", value ); - value = 1; - } - - return value; -} - - -/* -=============== CG_ParseValueAndVariance Parse a value and its random variance @@ -1275,7 +1163,8 @@ static qboolean CG_ParseParticleSystem( baseParticleSystem_t *bps, char **text_p } else if( numBaseParticleEjectors == MAX_BASEPARTICLE_EJECTORS ) { - CG_Printf( S_COLOR_RED "ERROR: maximum number of particle ejectors (%d) reached\n", MAX_BASEPARTICLE_EJECTORS ); + CG_Printf( S_COLOR_RED "ERROR: maximum number of particle ejectors (%d) reached\n", + MAX_BASEPARTICLE_EJECTORS ); return qfalse; } else @@ -1347,11 +1236,8 @@ static qboolean CG_ParseParticleFile( const char *fileName ) { token = COM_Parse( &text_p ); - if( !token ) - break; - if( !Q_stricmp( token, "" ) ) - return qfalse; + break; if( !Q_stricmp( token, "{" ) ) { @@ -1380,7 +1266,8 @@ static qboolean CG_ParseParticleFile( const char *fileName ) if( numBaseParticleSystems == MAX_BASEPARTICLE_SYSTEMS ) { - CG_Printf( S_COLOR_RED "ERROR: maximum number of particle systems (%d) reached\n", MAX_BASEPARTICLE_EJECTORS ); + CG_Printf( S_COLOR_RED "ERROR: maximum number of particle systems (%d) reached\n", + MAX_BASEPARTICLE_SYSTEMS ); return qfalse; } else @@ -1518,167 +1405,9 @@ void CG_LoadParticleSystems( void ) } } - -/* -=============== -CG_AttachParticleSystemToCent - -Attach a particle system to a centity_t -=============== -*/ -void CG_AttachParticleSystemToCent( particleSystem_t *ps ) -{ - if( ps == NULL || !ps->valid ) - { - CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); - return; - } - - ps->attachType = PSA_CENT_ORIGIN; - ps->attached = qtrue; -} - -/* -=============== -CG_SetParticleSystemCent - -Set a particle system attachment means -=============== -*/ -void CG_SetParticleSystemCent( particleSystem_t *ps, centity_t *cent ) -{ - if( ps == NULL || !ps->valid ) - { - CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); - return; - } - - ps->attachment.centValid = qtrue; - ps->attachment.centNum = cent->currentState.number; -} - -/* -=============== -CG_AttachParticleSystemToTag - -Attach a particle system to a model tag -=============== -*/ -void CG_AttachParticleSystemToTag( particleSystem_t *ps ) -{ - if( ps == NULL || !ps->valid ) - { - CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); - return; - } - - ps->attachType = PSA_TAG; - ps->attached = qtrue; -} - -/* -=============== -CG_SetParticleSystemToTag - -Set a particle system attachment means -=============== -*/ -void CG_SetParticleSystemTag( particleSystem_t *ps, refEntity_t parent, - qhandle_t model, char *tagName ) -{ - if( ps == NULL || !ps->valid ) - { - CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); - return; - } - - ps->attachment.tagValid = qtrue; - ps->attachment.parent = parent; - ps->attachment.model = model; - strncpy( ps->attachment.tagName, tagName, MAX_STRING_CHARS ); -} - -/* -=============== -CG_AttachParticleSystemToOrigin - -Attach a particle system to a point in space -=============== -*/ -void CG_AttachParticleSystemToOrigin( particleSystem_t *ps ) -{ - if( ps == NULL || !ps->valid ) - { - CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); - return; - } - - ps->attachType = PSA_STATIC; - ps->attached = qtrue; -} - -/* -=============== -CG_SetParticleSystemOrigin - -Set a particle system attachment means -=============== -*/ -void CG_SetParticleSystemOrigin( particleSystem_t *ps, vec3_t origin ) -{ - if( ps == NULL || !ps->valid ) - { - CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); - return; - } - - ps->attachment.staticValid = qtrue; - VectorCopy( origin, ps->attachment.origin ); -} - -/* -=============== -CG_AttachParticleSystemToParticle - -Attach a particle system to a particle -=============== -*/ -void CG_AttachParticleSystemToParticle( particleSystem_t *ps ) -{ - if( ps == NULL || !ps->valid ) - { - CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); - return; - } - - ps->attachType = PSA_PARTICLE; - ps->attached = qtrue; -} - -/* -=============== -CG_SetParticleSystemParentParticle - -Set a particle system attachment means -=============== -*/ -void CG_SetParticleSystemParentParticle( particleSystem_t *ps, particle_t *p ) -{ - if( ps == NULL || !ps->valid ) - { - CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); - return; - } - - ps->attachment.particleValid = qtrue; - p->childSystem = ps; -} - /* =============== CG_SetParticleSystemNormal - -Set a particle system attachment means =============== */ void CG_SetParticleSystemNormal( particleSystem_t *ps, vec3_t normal ) @@ -1689,9 +1418,9 @@ void CG_SetParticleSystemNormal( particleSystem_t *ps, vec3_t normal ) return; } - ps->attachment.normalValid = qtrue; - VectorCopy( normal, ps->attachment.normal ); - VectorNormalize( ps->attachment.normal ); + ps->normalValid = qtrue; + VectorCopy( normal, ps->normal ); + VectorNormalize( ps->normal ); } @@ -1807,6 +1536,7 @@ 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++ ) { @@ -1830,9 +1560,10 @@ static void CG_GarbageCollectParticleSystems( void ) //check systems where the parent cent has left the PVS //( local player entity is always valid ) - if( ps->attachment.centValid && ps->attachment.centNum != cg.snap->ps.clientNum ) + if( ( centNum = CG_AttachmentCentNum( &ps->attachment ) ) >= 0 && + centNum != cg.snap->ps.clientNum ) { - if( !cg_entities[ ps->attachment.centNum ].valid ) + if( !cg_entities[ centNum ].valid ) ps->lazyRemove = qtrue; } @@ -1874,16 +1605,14 @@ static void CG_EvaluateParticlePhysics( particle_t *p ) { particleSystem_t *ps = p->parent->parent; baseParticle_t *bp = p->class; - vec3_t acceleration, forward, newOrigin; + vec3_t acceleration, newOrigin; vec3_t mins, maxs; float deltaTime, bounce, radius, dot; trace_t trace; - centity_t *cent; 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 ) @@ -1892,40 +1621,28 @@ static void CG_EvaluateParticlePhysics( particle_t *p ) break; case PMT_TAG: - - if( !ps->attachment.tagValid ) - return; - - if( bp->accMoveValues.dirType == PMD_POINT ) - VectorSubtract( ps->attachment.re.origin, p->origin, acceleration ); - else if( bp->accMoveValues.dirType == PMD_LINEAR ) - VectorCopy( ps->attachment.re.axis[ 0 ], acceleration ); - - break; - case PMT_CENT_ANGLES: + if( bp->accMoveValues.dirType == PMD_POINT ) + { + vec3_t point; - if( !ps->attachment.centValid ) - return; - - cent = &cg_entities[ ps->attachment.centNum ]; + if( !CG_AttachmentPoint( &ps->attachment, point ) ) + return; - if( bp->accMoveValues.dirType == PMD_POINT ) - VectorSubtract( cent->lerpOrigin, p->origin, acceleration ); + VectorSubtract( point, p->origin, acceleration ); + } else if( bp->accMoveValues.dirType == PMD_LINEAR ) { - AngleVectors( cent->lerpAngles, forward, NULL, NULL ); - VectorCopy( forward, acceleration ); + if( !CG_AttachmentDir( &ps->attachment, acceleration ) ) + return; } - break; case PMT_NORMAL: - - if( !ps->attachment.normalValid ) + if( !ps->normalValid ) return; - VectorCopy( ps->attachment.normal, acceleration ); + VectorCopy( ps->normal, acceleration ); break; } @@ -1974,10 +1691,8 @@ static void CG_EvaluateParticlePhysics( particle_t *p ) VectorMA( p->origin, deltaTime, p->velocity, newOrigin ); p->lastEvalTime = cg.time; - if( !ps->attachment.centValid ) - CG_Trace( &trace, p->origin, mins, maxs, newOrigin, -1, CONTENTS_SOLID ); - else - CG_Trace( &trace, p->origin, mins, maxs, newOrigin, ps->attachment.centNum, CONTENTS_SOLID ); + 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 ) @@ -1988,9 +1703,14 @@ static void CG_EvaluateParticlePhysics( particle_t *p ) //remove particles that get into a CONTENTS_NODROP brush if( ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) || - ( bp->cullOnStartSolid && trace.startsolid ) || bp->bounceCull ) + ( bp->cullOnStartSolid && trace.startsolid ) ) { - CG_DestroyParticle( p ); + CG_DestroyParticle( p, NULL ); + return; + } + else if( bp->bounceCull ) + { + CG_DestroyParticle( p, trace.plane.normal ); return; } @@ -2011,7 +1731,7 @@ static void CG_EvaluateParticlePhysics( particle_t *p ) CG_Radix =============== */ -static void CG_Radix( int bits, int size, particle_t *source, particle_t *dest ) +static void CG_Radix( int bits, int size, particle_t **source, particle_t **dest ) { int count[ 256 ]; int index[ 256 ]; @@ -2020,7 +1740,7 @@ static void CG_Radix( int bits, int size, particle_t *source, particle_t *dest ) memset( count, 0, sizeof( count ) ); for( i = 0; i < size; i++ ) - count[ GETKEY( source[ i ].sortKey, bits ) ]++; + count[ GETKEY( source[ i ]->sortKey, bits ) ]++; index[ 0 ] = 0; @@ -2028,7 +1748,7 @@ static void CG_Radix( int bits, int size, particle_t *source, particle_t *dest ) index[ i ] = index[ i - 1 ] + count[ i - 1 ]; for( i = 0; i < size; i++ ) - dest[ index[ GETKEY( source[ i ].sortKey, bits ) ]++ ] = source[ i ]; + dest[ index[ GETKEY( source[ i ]->sortKey, bits ) ]++ ] = source[ i ]; } /* @@ -2038,7 +1758,7 @@ CG_RadixSort Radix sort with 4 byte size buckets =============== */ -static void CG_RadixSort( particle_t *source, particle_t *temp, int size ) +static void CG_RadixSort( particle_t **source, particle_t **temp, int size ) { CG_Radix( 0, size, source, temp ); CG_Radix( 8, size, temp, source ); @@ -2059,19 +1779,22 @@ static void CG_CompactAndSortParticles( void ) 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( particles[ i ].valid ) + if( sortedParticles[ i ]->valid ) { - while( particles[ j ].valid ) + //find the first hole + while( sortedParticles[ j ]->valid ) j++; //no more holes if( j >= i ) break; - particles[ j ] = particles[ i ]; - memset( &particles[ i ], 0, sizeof( particles[ 0 ] ) ); + sortedParticles[ j ] = sortedParticles[ i ]; } } @@ -2080,18 +1803,19 @@ static void CG_CompactAndSortParticles( void ) //set sort keys for( i = 0; i < numParticles; i++ ) { - VectorSubtract( particles[ i ].origin, cg.refdef.vieworg, delta ); - particles[ i ].sortKey = (int)DotProduct( delta, delta ); + VectorSubtract( sortedParticles[ i ]->origin, cg.refdef.vieworg, delta ); + sortedParticles[ i ]->sortKey = (int)DotProduct( delta, delta ); } - CG_RadixSort( particles, sortParticles, numParticles ); + CG_RadixSort( sortedParticles, radixBuffer, numParticles ); + //FIXME: wtf? //reverse order of particles array for( i = 0; i < numParticles; i++ ) - sortParticles[ i ] = particles[ numParticles - i - 1 ]; + radixBuffer[ i ] = sortedParticles[ numParticles - i - 1 ]; for( i = 0; i < numParticles; i++ ) - particles[ i ] = sortParticles[ i ]; + sortedParticles[ i ] = radixBuffer[ i ]; } /* @@ -2198,7 +1922,7 @@ void CG_AddParticles( void ) for( i = 0; i < MAX_PARTICLES; i++ ) { - p = &particles[ i ]; + p = sortedParticles[ i ]; if( p->valid ) { @@ -2209,7 +1933,7 @@ void CG_AddParticles( void ) CG_RenderParticle( p ); } else - CG_DestroyParticle( p ); + CG_DestroyParticle( p, NULL ); } } @@ -2258,11 +1982,63 @@ void CG_ParticleSystemEntity( centity_t *cent ) if( CG_IsParticleSystemValid( ¢->entityPS ) ) { - CG_SetParticleSystemOrigin( cent->entityPS, cent->lerpOrigin ); - CG_SetParticleSystemCent( cent->entityPS, cent ); - CG_AttachParticleSystemToOrigin( cent->entityPS ); + CG_SetAttachmentPoint( ¢->entityPS->attachment, cent->lerpOrigin ); + CG_SetAttachmentCent( ¢->entityPS->attachment, cent ); + CG_AttachToPoint( ¢->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 ); + } + } +} |