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.c351
1 files changed, 299 insertions, 52 deletions
diff --git a/src/cgame/cg_particles.c b/src/cgame/cg_particles.c
index a6ef99e5..02d4b5f8 100644
--- a/src/cgame/cg_particles.c
+++ b/src/cgame/cg_particles.c
@@ -93,7 +93,7 @@ static void CG_DestroyParticle( particle_t *p, vec3_t impactNormal )
{
particleSystem_t *ps;
- ps = CG_SpawnNewParticleSystem( p->class->childSystemHandle );
+ ps = CG_SpawnNewParticleSystem( p->class->onDeathSystemHandle );
if( CG_IsParticleSystemValid( &ps ) )
{
@@ -126,6 +126,7 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p
particleEjector_t *pe = parent;
particleSystem_t *ps = parent->parent;
vec3_t attachmentPoint, attachmentVelocity;
+ vec3_t transform[ 3 ];
for( i = 0; i < MAX_PARTICLES; i++ )
{
@@ -155,11 +156,31 @@ 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 );
+ 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 );
- VectorAdd( p->origin, bp->displacement, 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 );
@@ -173,6 +194,26 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p
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 )
@@ -186,7 +227,11 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p
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 );
@@ -225,6 +270,18 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p
}
}
+ //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;
}
}
@@ -415,11 +472,11 @@ qhandle_t CG_RegisterParticleSystem( char *name )
baseParticleEjector_t *bpe;
baseParticle_t *bp;
- for( i = 0; i < MAX_PARTICLE_SYSTEMS; i++ )
+ for( i = 0; i < MAX_BASEPARTICLE_SYSTEMS; i++ )
{
bps = &baseParticleSystems[ i ];
- if( !strcmp( bps->name, name ) )
+ if( !Q_stricmpn( bps->name, name, MAX_QPATH ) )
{
//already registered
if( bps->registered )
@@ -436,6 +493,9 @@ qhandle_t CG_RegisterParticleSystem( char *name )
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 ] );
+
//recursively register any children
if( bp->childSystemName[ 0 ] != '\0' )
{
@@ -450,6 +510,9 @@ qhandle_t CG_RegisterParticleSystem( char *name )
//the system deals with it
CG_RegisterParticleSystem( bp->onDeathSystemName );
}
+
+ if( bp->childTrailSystemName[ 0 ] != '\0' )
+ bp->childTrailSystemHandle = CG_RegisterTrailSystem( bp->childTrailSystemName );
}
}
@@ -571,6 +634,13 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
}
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;
@@ -581,15 +651,86 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
bp->framerate = atof_neg( token, qfalse );
token = COM_ParseExt( text_p, qfalse );
+ if( !*token )
+ break;
- while( token && token[ 0 ] != 0 )
+ while( *token && bp->numFrames < MAX_PS_SHADER_FRAMES )
{
Q_strncpyz( bp->shaderNames[ bp->numFrames++ ], token, MAX_QPATH );
token = COM_ParseExt( text_p, qfalse );
}
- if( !token )
+ 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;
}
@@ -602,6 +743,8 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
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" ) )
@@ -701,6 +844,8 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
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" ) )
@@ -978,6 +1123,16 @@ static qboolean CG_ParseParticle( baseParticle_t *bp, char **text_p )
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
@@ -1176,6 +1331,8 @@ static qboolean CG_ParseParticleSystem( baseParticleSystem_t *bps, char **text_p
}
continue;
}
+ else if( !Q_stricmp( token, "thirdPersonOnly" ) )
+ bps->thirdPersonOnly = qtrue;
else if( !Q_stricmp( token, "ejector" ) ) //acceptable text
continue;
else if( !Q_stricmp( token, "}" ) )
@@ -1609,6 +1766,13 @@ static void CG_EvaluateParticlePhysics( particle_t *p )
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 )
{
@@ -1620,6 +1784,26 @@ static void CG_EvaluateParticlePhysics( particle_t *p )
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 )
@@ -1720,6 +1904,11 @@ static void CG_EvaluateParticlePhysics( particle_t *p )
VectorScale( p->velocity, bounce, p->velocity );
+ if( trace.plane.normal[ 2 ] > 0.0f &&
+ ( p->velocity[ 2 ] < 40.0f ||
+ p->velocity[ 2 ] < -cg.frametime * p->velocity[ 2 ] ) )
+ p->atRest = qtrue;
+
VectorCopy( trace.endpos, p->origin );
}
@@ -1827,71 +2016,129 @@ Actually render a particle
*/
static void CG_RenderParticle( particle_t *p )
{
- refEntity_t re;
- float timeFrac;
- int index;
- baseParticle_t *bp = p->class;
- vec3_t alight, dlight, lightdir;
- int i;
+ 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 ) );
- for( i = 0; i <= 3; re.shaderRGBA[ i++ ] = 0xFF );
-
timeFrac = CG_CalculateTimeFrac( p->birthTime, p->lifeTime, 0 );
- re.reType = RT_SPRITE;
- re.shaderTime = p->birthTime / 1000.0f; //FIXME: allow user to change?
-
- 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 = CG_LerpValues( p->radius.initial,
+ scale = CG_LerpValues( p->radius.initial,
p->radius.final,
CG_CalculateTimeFrac( p->birthTime,
p->lifeTime,
p->radius.delay ) );
- re.rotation = CG_LerpValues( p->rotation.initial,
- p->rotation.final,
- CG_CalculateTimeFrac( p->birthTime,
- p->lifeTime,
- p->rotation.delay ) );
+ if( bp->numFrames ) //shader based
+ {
+ re.reType = RT_SPRITE;
- // 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;
+ re.shaderTime = p->birthTime / 1000.0f; //FIXME: allow user to change?
- //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 ] = (int)alight[ i ];
- }
+ //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 ] = (int)alight[ i ];
+ }
+ else
+ for( i = 0; i <= 3; re.shaderRGBA[ i++ ] = 0xFF );
- if( bp->framerate == 0.0f )
- {
- //sync animation time to lifeTime of particle
- index = (int)( timeFrac * ( bp->numFrames + 1 ) );
+ 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( index >= bp->numFrames )
- index = bp->numFrames - 1;
+ // 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 ];
+ }
- re.customShader = bp->shaders[ index ];
}
- else
+ else if( bp->numModels ) //model based
{
- //looping animation
- index = (int)( bp->framerate * timeFrac * p->lifeTime * 0.001 ) % bp->numFrames;
- re.customShader = bp->shaders[ index ];
+ 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 )
+ re.renderfx |= RF_THIRD_PERSON;
+
VectorCopy( p->origin, re.origin );
trap_R_AddRefEntityToScene( &re );