diff options
Diffstat (limited to 'src/cgame/cg_trails.c')
-rw-r--r-- | src/cgame/cg_trails.c | 1746 |
1 files changed, 1180 insertions, 566 deletions
diff --git a/src/cgame/cg_trails.c b/src/cgame/cg_trails.c index 30347e61..e80225e4 100644 --- a/src/cgame/cg_trails.c +++ b/src/cgame/cg_trails.c @@ -1,816 +1,1430 @@ -// Ridah, cg_trails.c - draws a trail using multiple junction points +// cg_trails.c -- the trail system -#include "cg_local.h" - -typedef struct trailJunc_s -{ - struct trailJunc_s *nextGlobal, *prevGlobal; // next junction in the global list it is in (free or used) - struct trailJunc_s *nextJunc; // next junction in the trail - struct trailJunc_s *nextHead, *prevHead; // next head junc in the world - - qboolean inuse, freed; - int ownerIndex; - qhandle_t shader; - - int sType; - int flags; - float sTex; - vec3_t pos; - int spawnTime, endTime; - float alphaStart, alphaEnd; - vec3_t colorStart, colorEnd; - float widthStart, widthEnd; - - // current settings - float alpha; - float width; - vec3_t color; - -} trailJunc_t; - -#define MAX_TRAILJUNCS 4096 +/* + * 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. + */ -trailJunc_t trailJuncs[ MAX_TRAILJUNCS ]; -trailJunc_t *freeTrails, *activeTrails; -trailJunc_t *headTrails; +#include "cg_local.h" -qboolean initTrails = qfalse; +static baseTrailSystem_t baseTrailSystems[ MAX_BASETRAIL_SYSTEMS ]; +static baseTrailBeam_t baseTrailBeams[ MAX_BASETRAIL_BEAMS ]; +static int numBaseTrailSystems = 0; +static int numBaseTrailBeams = 0; -int numTrailsInuse; +static trailSystem_t trailSystems[ MAX_TRAIL_SYSTEMS ]; +static trailBeam_t trailBeams[ MAX_TRAIL_BEAMS ]; /* =============== -CG_ClearTrails +CG_CalculateBeamTextureCoordinates + +Fills in trailBeamNode_t.textureCoord =============== */ -void CG_ClearTrails( void ) +static void CG_CalculateBeamTextureCoordinates( trailBeam_t *tb ) { - int i; - - memset( trailJuncs, 0, sizeof( trailJunc_t ) * MAX_TRAILJUNCS ); + trailBeamNode_t *i = NULL; + trailSystem_t *ts; + baseTrailBeam_t *btb; + float nodeDistances[ MAX_TRAIL_BEAM_NODES ]; + float totalDistance = 0.0f, position = 0.0f; + int j, numNodes = 0; + float TCRange, widthRange, alphaRange; + vec3_t colorRange; + float fadeAlpha = 1.0f; + + if( !tb || !tb->nodes ) + return; - freeTrails = trailJuncs; - activeTrails = NULL; - headTrails = NULL; + ts = tb->parent; + btb = tb->class; - for( i = 0; i < MAX_TRAILJUNCS; i++ ) + if( ts->destroyTime > 0 && btb->fadeOutTime ) { - trailJuncs[ i ].nextGlobal = &trailJuncs[ i + 1 ]; + fadeAlpha -= ( cg.time - ts->destroyTime ) / btb->fadeOutTime; - if( i > 0 ) - trailJuncs[ i ].prevGlobal = &trailJuncs[ i - 1 ]; - else - trailJuncs[ i ].prevGlobal = NULL; + if( fadeAlpha < 0.0f ) + fadeAlpha = 0.0f; + } - trailJuncs[ i ].inuse = qfalse; + TCRange = tb->class->backTextureCoord - + tb->class->frontTextureCoord; + widthRange = tb->class->backWidth - + tb->class->frontWidth; + alphaRange = tb->class->backAlpha - + tb->class->frontAlpha; + VectorSubtract( tb->class->backColor, + tb->class->frontColor, colorRange ); + + for( i = tb->nodes; i && i->next; i = i->next ) + { + nodeDistances[ numNodes++ ] = + Distance( i->position, i->next->position ); } - trailJuncs[ MAX_TRAILJUNCS - 1 ].nextGlobal = NULL; + for( j = 0; j < numNodes; j++ ) + totalDistance += nodeDistances[ j ]; - initTrails = qtrue; - numTrailsInuse = 0; + for( j = 0, i = tb->nodes; i; i = i->next, j++ ) + { + if( tb->class->textureType == TBTT_STRETCH ) + { + i->textureCoord = tb->class->frontTextureCoord + + ( ( position / totalDistance ) * TCRange ); + } + else if( tb->class->textureType == TBTT_REPEAT ) + { + if( tb->class->clampToBack ) + i->textureCoord = ( totalDistance - position ) / + tb->class->repeatLength; + else + i->textureCoord = position / tb->class->repeatLength; + } + + i->halfWidth = ( tb->class->frontWidth + + ( ( position / totalDistance ) * widthRange ) ) / 2.0f; + i->alpha = (byte)( (float)0xFF * ( tb->class->frontAlpha + + ( ( position / totalDistance ) * alphaRange ) ) * fadeAlpha ); + VectorMA( tb->class->frontColor, ( position / totalDistance ), + colorRange, i->color ); + + position += nodeDistances[ j ]; + } } /* =============== -CG_SpawnTrailJunc +CG_LightVertex + +Lights a particular vertex =============== */ -trailJunc_t *CG_SpawnTrailJunc( trailJunc_t *headJunc ) +static void CG_LightVertex( vec3_t point, byte alpha, byte *rgba ) { - trailJunc_t *j; + int i; + vec3_t alight, dlight, lightdir; - if( !freeTrails ) - return NULL; + trap_R_LightForPoint( point, alight, dlight, lightdir ); + for( i = 0; i <= 2; i++ ) + rgba[ i ] = (int)alight[ i ]; - if( cg_paused.integer ) - return NULL; + rgba[ 3 ] = alpha; +} - // select the first free trail, and remove it from the list - j = freeTrails; - freeTrails = j->nextGlobal; +/* +=============== +CG_RenderBeam - if( freeTrails ) - freeTrails->prevGlobal = NULL; +Renders a beam +=============== +*/ +static void CG_RenderBeam( trailBeam_t *tb ) +{ + trailBeamNode_t *i = NULL; + trailBeamNode_t *prev = NULL; + trailBeamNode_t *next = NULL; + vec3_t up; + polyVert_t verts[ ( MAX_TRAIL_BEAM_NODES - 1 ) * 4 ]; + int numVerts = 0; + baseTrailBeam_t *btb; + + if( !tb || !tb->nodes ) + return; - j->nextGlobal = activeTrails; + btb = tb->class; - if( activeTrails ) - activeTrails->prevGlobal = j; + CG_CalculateBeamTextureCoordinates( tb ); - activeTrails = j; - j->prevGlobal = NULL; - j->inuse = qtrue; - j->freed = qfalse; + i = tb->nodes; - // if this owner has a headJunc, add us to the start - if( headJunc ) + do { - // remove the headJunc from the list of heads - if( headJunc == headTrails ) - { - headTrails = headJunc->nextHead; + prev = i->prev; + next = i->next; - if( headTrails ) - headTrails->prevHead = NULL; + if( prev && next ) + { + //this node has two neighbours + GetPerpendicularViewVector( cg.refdef.vieworg, next->position, prev->position, up ); + } + else if( !prev && next ) + { + //this is the front + GetPerpendicularViewVector( cg.refdef.vieworg, next->position, i->position, up ); + } + else if( prev && !next ) + { + //this is the back + GetPerpendicularViewVector( cg.refdef.vieworg, i->position, prev->position, up ); } else + break; + + if( prev ) { - if( headJunc->nextHead ) - headJunc->nextHead->prevHead = headJunc->prevHead; + VectorMA( i->position, i->halfWidth + i->jitter, up, verts[ numVerts ].xyz ); + verts[ numVerts ].st[ 0 ] = i->textureCoord; + verts[ numVerts ].st[ 1 ] = 1.0f; + + if( btb->realLight ) + CG_LightVertex( verts[ numVerts ].xyz, i->alpha, verts[ numVerts ].modulate ); + else + { + VectorCopy( i->color, verts[ numVerts ].modulate ); + verts[ numVerts ].modulate[ 3 ] = i->alpha; + } + + numVerts++; - if( headJunc->prevHead ) - headJunc->prevHead->nextHead = headJunc->nextHead; + VectorMA( i->position, -i->halfWidth + i->jitter, up, verts[ numVerts ].xyz ); + verts[ numVerts ].st[ 0 ] = i->textureCoord; + verts[ numVerts ].st[ 1 ] = 0.0f; + + if( btb->realLight ) + CG_LightVertex( verts[ numVerts ].xyz, i->alpha, verts[ numVerts ].modulate ); + else + { + VectorCopy( i->color, verts[ numVerts ].modulate ); + verts[ numVerts ].modulate[ 3 ] = i->alpha; + } + + numVerts++; } - headJunc->prevHead = NULL; - headJunc->nextHead = NULL; - } - // make us the headTrail - if( headTrails ) - headTrails->prevHead = j; + if( next ) + { + VectorMA( i->position, -i->halfWidth + i->jitter, up, verts[ numVerts ].xyz ); + verts[ numVerts ].st[ 0 ] = i->textureCoord; + verts[ numVerts ].st[ 1 ] = 0.0f; - j->nextHead = headTrails; - j->prevHead = NULL; - headTrails = j; + if( btb->realLight ) + CG_LightVertex( verts[ numVerts ].xyz, i->alpha, verts[ numVerts ].modulate ); + else + { + VectorCopy( i->color, verts[ numVerts ].modulate ); + verts[ numVerts ].modulate[ 3 ] = i->alpha; + } - j->nextJunc = headJunc; // if headJunc is NULL, then we'll just be the end of the list + numVerts++; - numTrailsInuse++; + VectorMA( i->position, i->halfWidth + i->jitter, up, verts[ numVerts ].xyz ); + verts[ numVerts ].st[ 0 ] = i->textureCoord; + verts[ numVerts ].st[ 1 ] = 1.0f; - // debugging -// CG_Printf( "NumTrails: %i\n", numTrailsInuse ); + if( btb->realLight ) + CG_LightVertex( verts[ numVerts ].xyz, i->alpha, verts[ numVerts ].modulate ); + else + { + VectorCopy( i->color, verts[ numVerts ].modulate ); + verts[ numVerts ].modulate[ 3 ] = i->alpha; + } - return j; -} + numVerts++; + } + + i = i->next; + } while( i ); + trap_R_AddPolysToScene( tb->class->shader, 4, &verts[ 0 ], numVerts / 4 ); +} /* =============== -CG_AddTrailJunc - - returns the index of the trail junction created +CG_AllocateBeamNode - Used for generic trails +Allocates a trailBeamNode_t from a trailBeam_t's nodePool =============== */ -int CG_AddTrailJunc( int headJuncIndex, qhandle_t shader, int spawnTime, int sType, vec3_t pos, - int trailLife, float alphaStart, float alphaEnd, float startWidth, - float endWidth, int flags, vec3_t colorStart, vec3_t colorEnd, - float sRatio, float animSpeed ) +static trailBeamNode_t *CG_AllocateBeamNode( trailBeam_t *tb ) { - trailJunc_t *j, *headJunc; + baseTrailBeam_t *btb = tb->class; + int i; + trailBeamNode_t *tbn; - if( headJuncIndex > 0 ) + for( i = 0; i < MAX_TRAIL_BEAM_NODES; i++ ) { - headJunc = &trailJuncs[ headJuncIndex - 1 ]; - - if( !headJunc->inuse ) - headJunc = NULL; + tbn = &tb->nodePool[ i ]; + if( !tbn->used ) + { + tbn->timeLeft = btb->segmentTime; + tbn->prev = NULL; + tbn->next = NULL; + tbn->used = qtrue; + return tbn; + } } - else - headJunc = NULL; - j = CG_SpawnTrailJunc( headJunc ); + // no space left + return NULL; +} - if( !j ) +/* +=============== +CG_DestroyBeamNode + +Removes a node from a beam +Returns the new head +=============== +*/ +static trailBeamNode_t *CG_DestroyBeamNode( trailBeamNode_t *tbn ) +{ + trailBeamNode_t *newHead = NULL; + + if( tbn->prev ) { -// CG_Printf("couldnt spawn trail junc\n"); - return 0; - } + if( tbn->next ) + { + // node is in the middle + tbn->prev->next = tbn->next; + tbn->next->prev = tbn->prev; + } + else // node is at the back + tbn->prev->next = NULL; - if( alphaStart > 1.0 ) - alphaStart = 1.0; + // find the new head (shouldn't have changed) + newHead = tbn->prev; - if( alphaStart < 0.0 ) - alphaStart = 0.0; + while( newHead->prev ) + newHead = newHead->prev; + } + else if( tbn->next ) + { + //node is at the front + tbn->next->prev = NULL; + newHead = tbn->next; + } - if( alphaEnd > 1.0 ) - alphaEnd = 1.0; + tbn->prev = NULL; + tbn->next = NULL; + tbn->used = qfalse; - if( alphaEnd < 0.0 ) - alphaEnd = 0.0; + return newHead; +} - // setup the trail junction - j->shader = shader; - j->sType = sType; - VectorCopy( pos, j->pos ); - j->flags = flags; +/* +=============== +CG_FindLastBeamNode - j->spawnTime = spawnTime; - j->endTime = spawnTime + trailLife; +Returns the last beam node in a beam +=============== +*/ +static trailBeamNode_t *CG_FindLastBeamNode( trailBeam_t *tb ) +{ + trailBeamNode_t *i = tb->nodes; - VectorCopy( colorStart, j->colorStart ); - VectorCopy( colorEnd, j->colorEnd ); + while( i && i->next ) + i = i->next; - j->alphaStart = alphaStart; - j->alphaEnd = alphaEnd; + return i; +} - j->widthStart = startWidth; - j->widthEnd = endWidth; +/* +=============== +CG_CountBeamNodes + +Returns the number of nodes in a beam +=============== +*/ +static int CG_CountBeamNodes( trailBeam_t *tb ) +{ + trailBeamNode_t *i = tb->nodes; + int numNodes = 0; - if( sType == STYPE_REPEAT ) + while( i ) { - if( headJunc ) - j->sTex = headJunc->sTex + ( ( Distance( headJunc->pos, pos ) / sRatio) / j->widthEnd ); - else - { - // FIXME: need a way to specify offset timing - j->sTex = ( animSpeed * ( 1.0 - ( (float)( cg.time % 1000 ) / 1000.0 ) ) ) / ( sRatio ); -// j->sTex = 0; - } + numNodes++; + i = i->next; } - return ( (int)( j - trailJuncs ) + 1 ); + return numNodes; } /* =============== -CG_AddSparkJunc +CG_PrependBeamNode - returns the index of the trail junction created +Prepend a new beam node to the front of a beam +Returns the new node =============== */ -int CG_AddSparkJunc( int headJuncIndex, qhandle_t shader, vec3_t pos, int trailLife, - float alphaStart, float alphaEnd, float startWidth, float endWidth ) +static trailBeamNode_t *CG_PrependBeamNode( trailBeam_t *tb ) { - trailJunc_t *j, *headJunc; + trailBeamNode_t *i; - if( headJuncIndex > 0 ) + if( tb->nodes ) { - headJunc = &trailJuncs[ headJuncIndex - 1 ]; + // prepend another node + i = CG_AllocateBeamNode( tb ); - if( !headJunc->inuse ) - headJunc = NULL; + if( i ) + { + i->next = tb->nodes; + tb->nodes->prev = i; + tb->nodes = i; + } } - else - headJunc = NULL; + else //add first node + { + i = CG_AllocateBeamNode( tb ); - j = CG_SpawnTrailJunc( headJunc ); + if( i ) + tb->nodes = i; + } - if( !j ) - return 0; + return i; +} - // setup the trail junction - j->shader = shader; - j->sType = STYPE_STRETCH; - VectorCopy( pos, j->pos ); - j->flags = TJFL_NOCULL; // don't worry about fading up close +/* +=============== +CG_AppendBeamNode - j->spawnTime = cg.time; - j->endTime = cg.time + trailLife; +Append a new beam node to the back of a beam +Returns the new node +=============== +*/ +static trailBeamNode_t *CG_AppendBeamNode( trailBeam_t *tb ) +{ + trailBeamNode_t *last, *i; - VectorSet( j->colorStart, 1.0, 0.8 + 0.2 * alphaStart, 0.4 + 0.4 * alphaStart ); - VectorSet( j->colorEnd, 1.0, 0.8 + 0.2 * alphaEnd, 0.4 + 0.4 * alphaEnd ); -// VectorScale( j->colorStart, alphaStart, j->colorStart ); -// VectorScale( j->colorEnd, alphaEnd, j->colorEnd ); + if( tb->nodes ) + { + // append another node + last = CG_FindLastBeamNode( tb ); + i = CG_AllocateBeamNode( tb ); - j->alphaStart = alphaStart*2; - j->alphaEnd = alphaEnd*2; -// j->alphaStart = 1.0; -// j->alphaEnd = 1.0; + if( i ) + { + last->next = i; + i->prev = last; + i->next = NULL; + } + } + else //add first node + { + i = CG_AllocateBeamNode( tb ); - j->widthStart = startWidth; - j->widthEnd = endWidth; + if( i ) + tb->nodes = i; + } - return ( (int)( j - trailJuncs ) + 1 ); + return i; } /* =============== -CG_AddSmokeJunc - - returns the index of the trail junction created +CG_ApplyJitters =============== */ -int CG_AddSmokeJunc( int headJuncIndex, qhandle_t shader, vec3_t pos, int trailLife, - float alpha, float startWidth, float endWidth ) +static void CG_ApplyJitters( trailBeam_t *tb ) { -#define ST_RATIO 4.0 // sprite image: width / height - trailJunc_t *j, *headJunc; + trailBeamNode_t *i = NULL; + int j; + baseTrailBeam_t *btb; + trailSystem_t *ts; + trailBeamNode_t *start; + trailBeamNode_t *end; + + if( !tb || !tb->nodes ) + return; - if( headJuncIndex > 0 ) + btb = tb->class; + ts = tb->parent; + + for( j = 0; j < btb->numJitters; j++ ) { - headJunc = &trailJuncs[ headJuncIndex - 1 ]; + if( tb->nextJitterTimes[ j ] <= cg.time ) + { + for( i = tb->nodes; i; i = i->next ) + i->jitters[ j ] = ( crandom( ) * btb->jitters[ j ].magnitude ); - if( !headJunc->inuse ) - headJunc = NULL; + tb->nextJitterTimes[ j ] = cg.time + btb->jitters[ j ].period; + } } - else - headJunc = NULL; - j = CG_SpawnTrailJunc( headJunc ); + start = tb->nodes; + end = CG_FindLastBeamNode( tb ); - if( !j ) - return 0; + if( !btb->jitterAttachments ) + { + if( CG_Attached( &ts->frontAttachment ) && start->next ) + start = start->next; - // setup the trail junction - j->shader = shader; - j->sType = STYPE_REPEAT; - VectorCopy( pos, j->pos ); - j->flags = TJFL_FADEIN; + if( CG_Attached( &ts->backAttachment ) && end->prev ) + end = end->prev; + } - j->spawnTime = cg.time; - j->endTime = cg.time + trailLife; + for( i = start; i; i = i->next ) + { + i->jitter = 0.0f; - // VectorSet(j->colorStart, 0.2, 0.2, 0.2); - VectorSet(j->colorStart, 0.0, 0.0, 0.0); - // VectorSet(j->colorEnd, 0.1, 0.1, 0.1); - VectorSet(j->colorEnd, 0.0, 0.0, 0.0); + for( j = 0; j < btb->numJitters; j++ ) + i->jitter += i->jitters[ j ]; - j->alphaStart = alpha; - j->alphaEnd = 0.0; + //mmmm... nice + if( i == end ) + break; + } +} - j->widthStart = startWidth; - j->widthEnd = endWidth; +/* +=============== +CG_UpdateBeam - if( headJunc ) - j->sTex = headJunc->sTex + ( ( Distance( headJunc->pos, pos ) / ST_RATIO ) / j->widthEnd ); - else +Updates a beam +=============== +*/ +static void CG_UpdateBeam( trailBeam_t *tb ) +{ + baseTrailBeam_t *btb; + trailSystem_t *ts; + trailBeamNode_t *i; + int deltaTime; + int nodesToAdd; + int j; + int numNodes; + + if( !tb ) + return; + + btb = tb->class; + ts = tb->parent; + + deltaTime = cg.time - tb->lastEvalTime; + tb->lastEvalTime = cg.time; + + // first make sure this beam has enough nodes + if( ts->destroyTime <= 0 ) { - // first junction, so this will become the "tail" very soon, make it fade out - j->sTex = 0; - j->alphaStart = 0.0; - j->alphaEnd = 0.0; + nodesToAdd = btb->numSegments - CG_CountBeamNodes( tb ) + 1; + + while( nodesToAdd-- ) + { + i = CG_AppendBeamNode( tb ); + + if( !tb->nodes->next && CG_Attached( &ts->frontAttachment ) ) + { + // this is the first node to be added + CG_AttachmentPoint( &ts->frontAttachment, i->refPosition ); + } + else + VectorCopy( i->prev->refPosition, i->refPosition ); + } } - return ( (int)( j - trailJuncs ) + 1 ); -} + numNodes = CG_CountBeamNodes( tb ); -void CG_KillTrail( trailJunc_t *t ); + for( i = tb->nodes; i; i = i->next ) + VectorCopy( i->refPosition, i->position ); -/* -=========== -CG_FreeTrailJunc -=========== -*/ -void CG_FreeTrailJunc( trailJunc_t *junc ) -{ - // kill any juncs after us, so they aren't left hanging - if( junc->nextJunc ) - CG_KillTrail( junc ); + if( CG_Attached( &ts->frontAttachment ) && CG_Attached( &ts->backAttachment ) ) + { + // beam between two attachments + vec3_t dir, front, back; + + if( ts->destroyTime > 0 && ( cg.time - ts->destroyTime ) >= btb->fadeOutTime ) + { + tb->valid = qfalse; + return; + } - // make it non-active - junc->inuse = qfalse; - junc->freed = qtrue; + CG_AttachmentPoint( &ts->frontAttachment, front ); + CG_AttachmentPoint( &ts->backAttachment, back ); + VectorSubtract( back, front, dir ); - if( junc->nextGlobal ) - junc->nextGlobal->prevGlobal = junc->prevGlobal; + for( j = 0, i = tb->nodes; i; i = i->next, j++ ) + { + float scale = (float)j / (float)( numNodes - 1 ); - if( junc->prevGlobal ) - junc->prevGlobal->nextGlobal = junc->nextGlobal; + VectorMA( front, scale, dir, i->position ); + } + } + else if( CG_Attached( &ts->frontAttachment ) ) + { + // beam from one attachment - if( junc == activeTrails ) - activeTrails = junc->nextGlobal; + // cull the trail tail + i = CG_FindLastBeamNode( tb ); - // if it's a head, remove it - if( junc == headTrails ) - headTrails = junc->nextHead; + if( i && i->timeLeft >= 0 ) + { + i->timeLeft -= deltaTime; - if( junc->nextHead ) - junc->nextHead->prevHead = junc->prevHead; + if( i->timeLeft < 0 ) + { + tb->nodes = CG_DestroyBeamNode( i ); - if( junc->prevHead ) - junc->prevHead->nextHead = junc->nextHead; + if( !tb->nodes ) + { + tb->valid = qfalse; + return; + } - junc->nextHead = NULL; - junc->prevHead = NULL; + // if the ts has been destroyed, stop creating new nodes + if( ts->destroyTime <= 0 ) + CG_PrependBeamNode( tb ); + } + else if( i->timeLeft >= 0 && i->prev ) + { + vec3_t dir; + float length; - // stick it in the free list - junc->prevGlobal = NULL; - junc->nextGlobal = freeTrails; + VectorSubtract( i->refPosition, i->prev->refPosition, dir ); + length = VectorNormalize( dir ) * + ( (float)i->timeLeft / (float)tb->class->segmentTime ); - if( freeTrails ) - freeTrails->prevGlobal = junc; + VectorMA( i->prev->refPosition, length, dir, i->position ); + } + } - freeTrails = junc; + CG_AttachmentPoint( &ts->frontAttachment, tb->nodes->refPosition ); + VectorCopy( tb->nodes->refPosition, tb->nodes->position ); + } - numTrailsInuse--; + CG_ApplyJitters( tb ); } /* -=========== -CG_KillTrail -=========== +=============== +CG_ParseTrailBeamColor +=============== */ -void CG_KillTrail( trailJunc_t *t ) +static qboolean CG_ParseTrailBeamColor( byte *c, char **text_p ) { - trailJunc_t *next; + char *token; + int i; - next = t->nextJunc; + for( i = 0; i <= 2; i++ ) + { + token = COM_Parse( text_p ); + + if( !Q_stricmp( token, "" ) ) + return qfalse; - // kill the trail here - t->nextJunc = NULL; + c[ i ] = (int)( (float)0xFF * atof_neg( token, qfalse ) ); + } - if( next ) - CG_FreeTrailJunc( next ); + return qtrue; } /* -============== -CG_AddTrailToScene +=============== +CG_ParseTrailBeam - TODO: this can do with some major optimization -============== +Parse a trail beam +=============== */ -static vec3_t vforward, vright, vup; +static qboolean CG_ParseTrailBeam( baseTrailBeam_t *btb, char **text_p ) +{ + char *token; + + // read optional parameters + while( 1 ) + { + token = COM_Parse( text_p ); -//TA: staticised to please QVM - #define MAX_TRAIL_VERTS 2048 -static polyVert_t verts[ MAX_TRAIL_VERTS ]; -static polyVert_t outVerts[ MAX_TRAIL_VERTS * 3 ]; + if( !Q_stricmp( token, "" ) ) + return qfalse; -void CG_AddTrailToScene( trailJunc_t *trail, int iteration, int numJuncs ) -{ - int k, i, n, l, numOutVerts; - polyVert_t mid; - float mod[ 4 ]; - float sInc = 0.0f, s = 0.0f; // TTimo: init - trailJunc_t *j, *jNext; - vec3_t fwd, up, p, v; - - // clipping vars - #define TRAIL_FADE_CLOSE_DIST 64.0 - #define TRAIL_FADE_FAR_SCALE 4.0 - vec3_t viewProj; - float viewDist, fadeAlpha; - - // add spark shader at head position - if( trail->flags & TJFL_SPARKHEADFLARE ) - { - j = trail; - VectorCopy( j->pos, p ); - VectorMA( p, -j->width * 2, vup, p ); - VectorMA( p, -j->width * 2, vright, p ); - VectorCopy( p, verts[ 0 ].xyz ); - verts[ 0 ].st[ 0 ] = 0; - verts[ 0 ].st[ 1 ] = 0; - verts[ 0 ].modulate[ 0 ] = 255; - verts[ 0 ].modulate[ 1 ] = 255; - verts[ 0 ].modulate[ 2 ] = 255; - verts[ 0 ].modulate[ 3 ] = (unsigned char)( j->alpha * 255.0 ); - - VectorCopy( j->pos, p ); - VectorMA( p, -j->width * 2, vup, p ); - VectorMA( p, j->width * 2, vright, p ); - VectorCopy( p, verts[ 1 ].xyz ); - verts[ 1 ].st[ 0 ] = 0; - verts[ 1 ].st[ 1 ] = 1; - verts[ 1 ].modulate[ 0 ] = 255; - verts[ 1 ].modulate[ 1 ] = 255; - verts[ 1 ].modulate[ 2 ] = 255; - verts[ 1 ].modulate[ 3 ] = (unsigned char)( j->alpha * 255.0 ); - - VectorCopy( j->pos, p ); - VectorMA( p, j->width * 2, vup, p ); - VectorMA( p, j->width * 2, vright, p ); - VectorCopy( p, verts[ 2 ].xyz ); - verts[ 2 ].st[ 0 ] = 1; - verts[ 2 ].st[ 1 ] = 1; - verts[ 2 ].modulate[ 0 ] = 255; - verts[ 2 ].modulate[ 1 ] = 255; - verts[ 2 ].modulate[ 2 ] = 255; - verts[ 2 ].modulate[ 3 ] = (unsigned char)( j->alpha * 255.0 ); - - VectorCopy( j->pos, p ); - VectorMA( p, j->width * 2, vup, p ); - VectorMA( p, -j->width * 2, vright, p ); - VectorCopy( p, verts[ 3 ].xyz ); - verts[ 3 ].st[ 0 ] = 1; - verts[ 3 ].st[ 1 ] = 0; - verts[ 3 ].modulate[ 0 ] = 255; - verts[ 3 ].modulate[ 1 ] = 255; - verts[ 3 ].modulate[ 2 ] = 255; - verts[ 3 ].modulate[ 3 ] = (unsigned char)( j->alpha * 255.0 ); - - trap_R_AddPolyToScene( cgs.media.sparkFlareShader, 4, verts ); - } - -// if (trail->flags & TJFL_CROSSOVER && iteration < 1) { -// iteration = 1; -// } - - if( !numJuncs ) - { - // first count the number of juncs in the trail - j = trail; - numJuncs = 0; - sInc = 0; - - while( j ) - { - numJuncs++; - - // check for a dead next junc - if( !j->inuse && j->nextJunc && !j->nextJunc->inuse ) - CG_KillTrail( j ); - else if( j->nextJunc && j->nextJunc->freed ) + if( !Q_stricmp( token, "segments" ) ) + { + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; + + btb->numSegments = atoi_neg( token, qfalse ); + + if( btb->numSegments >= MAX_TRAIL_BEAM_NODES ) { - // not sure how this can happen, but it does, and causes infinite loops - j->nextJunc = NULL; + btb->numSegments = MAX_TRAIL_BEAM_NODES - 1; + CG_Printf( S_COLOR_YELLOW "WARNING: too many segments in trail beam\n" ); } + continue; + } + else if( !Q_stricmp( token, "width" ) ) + { + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; - if( j->nextJunc ) - sInc += VectorDistance( j->nextJunc->pos, j->pos ); + btb->frontWidth = atof_neg( token, qfalse ); - j = j->nextJunc; + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; + + if( !Q_stricmp( token, "-" ) ) + btb->backWidth = btb->frontWidth; + else + btb->backWidth = atof_neg( token, qfalse ); + continue; } - } + else if( !Q_stricmp( token, "alpha" ) ) + { + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; - if( numJuncs < 2 ) - return; + btb->frontAlpha = atof_neg( token, qfalse ); - if( trail->sType == STYPE_STRETCH ) - { - //sInc = ((1.0 - 0.1) / (float)(numJuncs)); // hack, the end of funnel shows a bit of the start (looping) - s = 0.05; - //s = 0.05; - } - else if( trail->sType == STYPE_REPEAT ) - s = trail->sTex; + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; - // now traverse the list - j = trail; - jNext = j->nextJunc; - i = 0; - while( jNext ) - { - // first get the directional vectors to the next junc - VectorSubtract( jNext->pos, j->pos, fwd ); - GetPerpendicularViewVector( cg.refdef.vieworg, j->pos, jNext->pos, up ); - - // if it's a crossover, draw it twice - if( j->flags & TJFL_CROSSOVER ) + if( !Q_stricmp( token, "-" ) ) + btb->backAlpha = btb->frontAlpha; + else + btb->backAlpha = atof_neg( token, qfalse ); + continue; + } + else if( !Q_stricmp( token, "color" ) ) { - if( iteration > 0 ) - { - ProjectPointOntoVector( cg.refdef.vieworg, j->pos, jNext->pos, viewProj ); - VectorSubtract( cg.refdef.vieworg, viewProj, v ); - VectorNormalize( v ); + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; - if( iteration == 1 ) - VectorMA( up, 0.3, v, up ); + if( !Q_stricmp( token, "{" ) ) + { + if( !CG_ParseTrailBeamColor( btb->frontColor, 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( !Q_stricmp( token, "" ) ) + break; + + if( !Q_stricmp( token, "-" ) ) + { + btb->backColor[ 0 ] = btb->frontColor[ 0 ]; + btb->backColor[ 1 ] = btb->frontColor[ 1 ]; + btb->backColor[ 2 ] = btb->frontColor[ 2 ]; + } + else if( !Q_stricmp( token, "{" ) ) + { + if( !CG_ParseTrailBeamColor( btb->backColor, text_p ) ) + break; + + token = COM_Parse( text_p ); + if( Q_stricmp( token, "}" ) ) + { + CG_Printf( S_COLOR_RED "ERROR: missing '}'\n" ); + break; + } + } else - VectorMA( up, -0.3, v, up ); - - VectorNormalize( up ); + { + CG_Printf( S_COLOR_RED "ERROR: missing '{'\n" ); + break; + } } + else + { + CG_Printf( S_COLOR_RED "ERROR: missing '{'\n" ); + break; + } + + continue; } - // do fading when moving towards the projection point onto the trail segment vector - else if( !( j->flags & TJFL_NOCULL ) && ( j->widthEnd > 4 || jNext->widthEnd > 4 ) ) + else if( !Q_stricmp( token, "segmentTime" ) ) { - ProjectPointOntoVector( cg.refdef.vieworg, j->pos, jNext->pos, viewProj ); - viewDist = Distance( viewProj, cg.refdef.vieworg ); + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; + + btb->segmentTime = atoi_neg( token, qfalse ); + continue; + } + else if( !Q_stricmp( token, "fadeOutTime" ) ) + { + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; + + btb->fadeOutTime = atoi_neg( token, qfalse ); + continue; + } + else if( !Q_stricmp( token, "shader" ) ) + { + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; + + Q_strncpyz( btb->shaderName, token, MAX_QPATH ); + + continue; + } + else if( !Q_stricmp( token, "textureType" ) ) + { + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; + + if( !Q_stricmp( token, "stretch" ) ) + { + btb->textureType = TBTT_STRETCH; + + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; + + btb->frontTextureCoord = atof_neg( token, qfalse ); + + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; - if( viewDist < ( TRAIL_FADE_CLOSE_DIST * TRAIL_FADE_FAR_SCALE ) ) + btb->backTextureCoord = atof_neg( token, qfalse ); + } + else if( !Q_stricmp( token, "repeat" ) ) { - if( viewDist < TRAIL_FADE_CLOSE_DIST ) - fadeAlpha = 0.0; + btb->textureType = TBTT_REPEAT; + + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; + + if( !Q_stricmp( token, "front" ) ) + btb->clampToBack = qfalse; + else if( !Q_stricmp( token, "back" ) ) + btb->clampToBack = qtrue; else - fadeAlpha = ( viewDist - TRAIL_FADE_CLOSE_DIST ) / ( TRAIL_FADE_CLOSE_DIST * TRAIL_FADE_FAR_SCALE ); + { + CG_Printf( S_COLOR_RED "ERROR: unknown textureType clamp \"%s\"\n", token ); + break; + } - if( fadeAlpha < j->alpha ) - j->alpha = fadeAlpha; + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; - if( fadeAlpha < jNext->alpha ) - jNext->alpha = fadeAlpha; + btb->repeatLength = atof_neg( token, qfalse ); } + else + { + CG_Printf( S_COLOR_RED "ERROR: unknown textureType \"%s\"\n", token ); + break; + } + + continue; } + else if( !Q_stricmp( token, "realLight" ) ) + { + btb->realLight = qtrue; - // now output the QUAD for this segment + continue; + } + else if( !Q_stricmp( token, "jitter" ) ) + { + if( btb->numJitters == MAX_TRAIL_BEAM_JITTERS ) + { + CG_Printf( S_COLOR_RED "ERROR: too many jitters\n", token ); + break; + } + + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; + + btb->jitters[ btb->numJitters ].magnitude = atof_neg( token, qfalse ); - // 1 ---- - VectorMA( j->pos, 0.5 * j->width, up, p ); - VectorCopy( p, verts[ i ].xyz ); - verts[ i ].st[ 0 ] = s; - verts[ i ].st[ 1 ] = 1.0; + token = COM_Parse( text_p ); + if( !Q_stricmp( token, "" ) ) + break; - for( k = 0; k < 3; k++ ) - verts[ i ].modulate[ k ] = (unsigned char)( j->color[ k ] * 255.0 ); + btb->jitters[ btb->numJitters ].period = atoi_neg( token, qfalse ); - verts[ i ].modulate[ 3 ] = (unsigned char)( j->alpha * 255.0 ); + btb->numJitters++; + + continue; + } + else if( !Q_stricmp( token, "jitterAttachments" ) ) + { + btb->jitterAttachments = qtrue; - // blend this with the previous junc - if( j != trail ) + continue; + } + else if( !Q_stricmp( token, "}" ) ) + return qtrue; //reached the end of this trail beam + else { - VectorAdd( verts[ i ].xyz, verts[ i - 1 ].xyz, verts[ i ].xyz ); - VectorScale( verts[ i ].xyz, 0.5, verts[ i ].xyz ); - VectorCopy( verts[ i ].xyz, verts[ i - 1 ].xyz ); + CG_Printf( S_COLOR_RED "ERROR: unknown token '%s' in trail beam\n", token ); + return qfalse; } - else if( j->flags & TJFL_FADEIN ) - verts[ i ].modulate[ 3 ] = 0; // fade in + } - i++; + return qfalse; +} - // 2 ---- - VectorMA( p, -1 * j->width, up, p ); - VectorCopy( p, verts[ i ].xyz ); - verts[ i ].st[ 0 ] = s; - verts[ i ].st[ 1 ] = 0.0; +/* +=============== +CG_InitialiseTrailBeam +=============== +*/ +static void CG_InitialiseTrailBeam( baseTrailBeam_t *btb ) +{ + memset( btb, 0, sizeof( baseTrailBeam_t ) ); - for( k = 0; k < 3; k++ ) - verts[ i ].modulate[ k ] = (unsigned char)( j->color[ k ] * 255.0 ); + btb->numSegments = 1; + btb->frontWidth = btb->backWidth = 8.0f; + btb->frontAlpha = btb->backAlpha = 1.0f; + memset( btb->frontColor, 0xFF, sizeof( btb->frontColor ) ); + memset( btb->backColor, 0xFF, sizeof( btb->backColor ) ); - verts[ i ].modulate[ 3 ] = (unsigned char)( j->alpha * 255.0 ); + btb->segmentTime = 250; + + btb->textureType = TBTT_STRETCH; + btb->frontTextureCoord = 0.0f; + btb->backTextureCoord = 1.0f; +} + +/* +=============== +CG_ParseTrailSystem + +Parse a trail system section +=============== +*/ +static qboolean CG_ParseTrailSystem( baseTrailSystem_t *bts, char **text_p, const char *name ) +{ + char *token; + + // read optional parameters + while( 1 ) + { + token = COM_Parse( text_p ); - // blend this with the previous junc - if( j != trail ) + if( !Q_stricmp( token, "" ) ) + return qfalse; + + if( !Q_stricmp( token, "{" ) ) { - VectorAdd( verts[ i ].xyz, verts[ i - 3 ].xyz, verts[ i ].xyz ); - VectorScale( verts[ i ].xyz, 0.5, verts[ i ].xyz ); - VectorCopy( verts[ i ].xyz, verts[ i - 3 ].xyz ); - } - else if( j->flags & TJFL_FADEIN ) - verts[ i ].modulate[ 3 ] = 0; // fade in + CG_InitialiseTrailBeam( &baseTrailBeams[ numBaseTrailBeams ] ); - i++; + if( !CG_ParseTrailBeam( &baseTrailBeams[ numBaseTrailBeams ], text_p ) ) + { + CG_Printf( S_COLOR_RED "ERROR: failed to parse trail beam\n" ); + return qfalse; + } + + if( bts->numBeams == MAX_BEAMS_PER_SYSTEM ) + { + CG_Printf( S_COLOR_RED "ERROR: trail system has > %d beams\n", MAX_BEAMS_PER_SYSTEM ); + return qfalse; + } + else if( numBaseTrailBeams == MAX_BASETRAIL_BEAMS ) + { + CG_Printf( S_COLOR_RED "ERROR: maximum number of trail beams (%d) reached\n", + MAX_BASETRAIL_BEAMS ); + return qfalse; + } + else + { + //start parsing beams again + bts->beams[ bts->numBeams ] = &baseTrailBeams[ numBaseTrailBeams ]; + bts->numBeams++; + numBaseTrailBeams++; + } + continue; + } + else if( !Q_stricmp( token, "beam" ) ) //acceptable text + continue; + else if( !Q_stricmp( token, "}" ) ) + { + if( cg_debugTrails.integer >= 1 ) + CG_Printf( "Parsed trail system %s\n", name ); - if( trail->sType == STYPE_REPEAT ) - s = jNext->sTex; + return qtrue; //reached the end of this trail system + } else { - //s += sInc; - s += VectorDistance( j->pos, jNext->pos ) / sInc; - if( s > 1.0 ) - s = 1.0; + CG_Printf( S_COLOR_RED "ERROR: unknown token '%s' in trail system %s\n", token, bts->name ); + return qfalse; } + } - // 3 ---- - VectorMA( jNext->pos, -0.5 * jNext->width, up, p ); - VectorCopy( p, verts[ i ].xyz ); - verts[ i ].st[ 0 ] = s; - verts[ i ].st[ 1 ] = 0.0; + return qfalse; +} - for( k = 0; k < 3; k++ ) - verts[ i ].modulate[ k ] = (unsigned char)( jNext->color[ k ] * 255.0 ); +/* +=============== +CG_ParseTrailFile - verts[ i ].modulate[ 3 ] = (unsigned char)( jNext->alpha * 255.0 ); - i++; +Load the trail systems from a trail file +=============== +*/ +static qboolean CG_ParseTrailFile( const char *fileName ) +{ + char *text_p; + int i; + int len; + char *token; + char text[ 32000 ]; + char tsName[ MAX_QPATH ]; + qboolean tsNameSet = 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: trail file %s too long\n", fileName ); + return qfalse; + } - // 4 ---- - VectorMA( p, jNext->width, up, p ); - VectorCopy( p, verts[ i ].xyz ); - verts[ i ].st[ 0 ] = s; - verts[ i ].st[ 1 ] = 1.0; + trap_FS_Read( text, len, f ); + text[ len ] = 0; + trap_FS_FCloseFile( f ); - for( k = 0; k < 3; k++ ) - verts[ i ].modulate[ k ] = (unsigned char)( jNext->color[ k ] * 255.0 ); + // parse the text + text_p = text; - verts[ i ].modulate[ 3 ] = (unsigned char)( jNext->alpha * 255.0 ); - i++; + // read optional parameters + while( 1 ) + { + token = COM_Parse( &text_p ); - if( i + 4 > MAX_TRAIL_VERTS ) + if( !Q_stricmp( token, "" ) ) break; - j = jNext; - jNext = j->nextJunc; + if( !Q_stricmp( token, "{" ) ) + { + if( tsNameSet ) + { + //check for name space clashes + for( i = 0; i < numBaseTrailSystems; i++ ) + { + if( !Q_stricmp( baseTrailSystems[ i ].name, tsName ) ) + { + CG_Printf( S_COLOR_RED "ERROR: a trail system is already named %s\n", tsName ); + return qfalse; + } + } + + Q_strncpyz( baseTrailSystems[ numBaseTrailSystems ].name, tsName, MAX_QPATH ); + + if( !CG_ParseTrailSystem( &baseTrailSystems[ numBaseTrailSystems ], &text_p, tsName ) ) + { + CG_Printf( S_COLOR_RED "ERROR: %s: failed to parse trail system %s\n", fileName, tsName ); + return qfalse; + } + + //start parsing trail systems again + tsNameSet = qfalse; + + if( numBaseTrailSystems == MAX_BASETRAIL_SYSTEMS ) + { + CG_Printf( S_COLOR_RED "ERROR: maximum number of trail systems (%d) reached\n", + MAX_BASETRAIL_SYSTEMS ); + return qfalse; + } + else + numBaseTrailSystems++; + + continue; + } + else + { + CG_Printf( S_COLOR_RED "ERROR: unamed trail system\n" ); + return qfalse; + } + } + + if( !tsNameSet ) + { + Q_strncpyz( tsName, token, sizeof( tsName ) ); + tsNameSet = qtrue; + } + else + { + CG_Printf( S_COLOR_RED "ERROR: trail system already named\n" ); + return qfalse; + } } - if( trail->flags & TJFL_FIXDISTORT ) + return qtrue; +} + +/* +=============== +CG_LoadTrailSystems + +Load trail system templates +=============== +*/ +void CG_LoadTrailSystems( void ) +{ + int i; + /*const char *s[ MAX_TRAIL_FILES ];*/ + + //clear out the old + numBaseTrailSystems = 0; + numBaseTrailBeams = 0; + + for( i = 0; i < MAX_BASETRAIL_SYSTEMS; i++ ) { - // build the list of outVerts, by dividing up the QUAD's into 4 Tri's each, so as to allow - // any shaped (convex) Quad without bilinear distortion - for( k = 0, numOutVerts = 0; k < i; k += 4 ) - { - VectorCopy( verts[ k ].xyz, mid.xyz ); - mid.st[ 0 ] = verts[ k ].st[ 0 ]; - mid.st[ 1 ] = verts[ k ].st[ 1 ]; + baseTrailSystem_t *bts = &baseTrailSystems[ i ]; + memset( bts, 0, sizeof( baseTrailSystem_t ) ); + } - for( l = 0; l < 4; l++ ) - mod[ l ] = (float)verts[ k ].modulate[ l ]; + for( i = 0; i < MAX_BASETRAIL_BEAMS; i++ ) + { + baseTrailBeam_t *btb = &baseTrailBeams[ i ]; + memset( btb, 0, sizeof( baseTrailBeam_t ) ); + } - for( n = 1; n < 4; n++ ) - { - VectorAdd( verts[ k + n ].xyz, mid.xyz, mid.xyz ); - mid.st[ 0 ] += verts[ k + n ].st[ 0 ]; - mid.st[ 1 ] += verts[ k + n ].st[ 1 ]; + //and bring in the new +/* for( i = 0; i < MAX_TRAIL_FILES; i++ ) + { + s[ i ] = CG_ConfigString( CS_TRAIL_FILES + i ); - for( l = 0; l < 4; l++ ) - mod[ l ] += (float)verts[ k + n ].modulate[ l ]; - } + if( strlen( s[ i ] ) > 0 ) + { + CG_Printf( "...loading '%s'\n", s[ i ] ); + CG_ParseTrailFile( s[ i ] ); + } + else + break; + }*/ + CG_Printf( "trail.trail: %d\n", CG_ParseTrailFile( "scripts/trail.trail" ) ); +} + +/* +=============== +CG_RegisterTrailSystem + +Load the media that a trail system needs +=============== +*/ +qhandle_t CG_RegisterTrailSystem( char *name ) +{ + int i, j; + baseTrailSystem_t *bts; + baseTrailBeam_t *btb; - VectorScale( mid.xyz, 0.25, mid.xyz ); - mid.st[ 0 ] *= 0.25; - mid.st[ 1 ] *= 0.25; + for( i = 0; i < MAX_TRAIL_SYSTEMS; i++ ) + { + bts = &baseTrailSystems[ i ]; - for( l = 0; l < 4; l++ ) - mid.modulate[ l ] = (unsigned char)( mod[ l ] / 4.0 ); + if( !Q_stricmp( bts->name, name ) ) + { + //already registered + if( bts->registered ) + return i + 1; - // now output the tri's - for( n = 0; n < 4; n++ ) + for( j = 0; j < bts->numBeams; j++ ) { - outVerts[ numOutVerts++ ] = verts[ k + n ]; - outVerts[ numOutVerts++ ] = mid; + btb = bts->beams[ j ]; - if( n < 3 ) - outVerts[ numOutVerts++ ] = verts[ k + n + 1 ]; - else - outVerts[ numOutVerts++ ] = verts[ k ]; + btb->shader = trap_R_RegisterShader( btb->shaderName ); } + if( cg_debugTrails.integer >= 1 ) + CG_Printf( "Registered trail system %s\n", name ); + + bts->registered = qtrue; + + //avoid returning 0 + return i + 1; } + } - if( !( trail->flags & TJFL_NOPOLYMERGE ) ) - trap_R_AddPolysToScene( trail->shader, 3, &outVerts[ 0 ], numOutVerts / 3 ); - else + CG_Printf( S_COLOR_RED "ERROR: failed to register trail system %s\n", name ); + return 0; +} + + +/* +=============== +CG_SpawnNewTrailBeam + +Allocate a new trail beam +=============== +*/ +static trailBeam_t *CG_SpawnNewTrailBeam( baseTrailBeam_t *btb, + trailSystem_t *parent ) +{ + int i; + trailBeam_t *tb = NULL; + trailSystem_t *ts = parent; + + for( i = 0; i < MAX_TRAIL_BEAMS; i++ ) + { + tb = &trailBeams[ i ]; + + if( !tb->valid ) { - int k; + memset( tb, 0, sizeof( trailBeam_t ) ); - for( k = 0; k < numOutVerts / 3; k++ ) - trap_R_AddPolyToScene( trail->shader, 3, &outVerts[ k * 3 ] ); + //found a free slot + tb->class = btb; + tb->parent = ts; + + tb->valid = qtrue; + + if( cg_debugTrails.integer >= 1 ) + CG_Printf( "TB %s created\n", ts->class->name ); + + break; } } - else + + return tb; +} + + +/* +=============== +CG_SpawnNewTrailSystem + +Spawns a new trail system +=============== +*/ +trailSystem_t *CG_SpawnNewTrailSystem( qhandle_t psHandle ) +{ + int i, j; + trailSystem_t *ts = NULL; + baseTrailSystem_t *bts = &baseTrailSystems[ psHandle - 1 ]; + + if( !bts->registered ) { - // send the polygons - // FIXME: is it possible to send a GL_STRIP here? We are actually sending 2x the verts we really need to - if( !( trail->flags & TJFL_NOPOLYMERGE ) ) - trap_R_AddPolysToScene( trail->shader, 4, &verts[ 0 ], i / 4 ); - else + CG_Printf( S_COLOR_RED "ERROR: a trail system has not been registered yet\n" ); + return NULL; + } + + for( i = 0; i < MAX_TRAIL_SYSTEMS; i++ ) + { + ts = &trailSystems[ i ]; + + if( !ts->valid ) { - int k; + memset( ts, 0, sizeof( trailSystem_t ) ); + + //found a free slot + ts->class = bts; + + ts->valid = qtrue; + ts->destroyTime = -1; + + for( j = 0; j < bts->numBeams; j++ ) + CG_SpawnNewTrailBeam( bts->beams[ j ], ts ); - for( k = 0; k < i / 4; k++ ) - trap_R_AddPolyToScene( trail->shader, 4, &verts[ k * 4 ] ); + if( cg_debugTrails.integer >= 1 ) + CG_Printf( "TS %s created\n", bts->name ); + + break; } } - // do we need to make another pass? - if( trail->flags & TJFL_CROSSOVER ) + return ts; +} + +/* +=============== +CG_DestroyTrailSystem + +Destroy a trail system +=============== +*/ +void CG_DestroyTrailSystem( trailSystem_t **ts ) +{ + (*ts)->destroyTime = cg.time; + + if( CG_Attached( &(*ts)->frontAttachment ) && + !CG_Attached( &(*ts)->backAttachment ) ) { - if( iteration < 2 ) - CG_AddTrailToScene( trail, iteration + 1, numJuncs ); + vec3_t v; + + // attach the trail head to a static point + CG_AttachmentPoint( &(*ts)->frontAttachment, v ); + CG_SetAttachmentPoint( &(*ts)->frontAttachment, v ); + CG_AttachToPoint( &(*ts)->frontAttachment ); + + (*ts)->frontAttachment.centValid = qfalse; // a bit naughty } + ts = NULL; } /* =============== -CG_AddTrails +CG_IsTrailSystemValid + +Test a trail system for validity =============== */ -void CG_AddTrails( void ) +qboolean CG_IsTrailSystemValid( trailSystem_t **ts ) { - float lifeFrac; - trailJunc_t *j, *jNext; + if( *ts == NULL || ( *ts && !(*ts)->valid ) ) + { + if( *ts && !(*ts)->valid ) + *ts = NULL; + + return qfalse; + } - if( !initTrails ) - CG_ClearTrails( ); + return qtrue; +} - //AngleVectors( cg.snap->ps.viewangles, vforward, vright, vup ); - VectorCopy( cg.refdef.viewaxis[ 0 ], vforward ); - VectorCopy( cg.refdef.viewaxis[ 1 ], vright ); - VectorCopy( cg.refdef.viewaxis[ 2 ], vup ); +/* +=============== +CG_GarbageCollectTrailSystems - // update the settings for each junc - j = activeTrails; +Destroy inactive trail systems +=============== +*/ +static void CG_GarbageCollectTrailSystems( void ) +{ + int i, j, count; + trailSystem_t *ts; + trailBeam_t *tb; + int centNum; - while( j ) + for( i = 0; i < MAX_TRAIL_SYSTEMS; i++ ) { - lifeFrac = (float)( cg.time - j->spawnTime ) / (float)( j->endTime - j->spawnTime ); + ts = &trailSystems[ i ]; + count = 0; + + //don't bother checking already invalid systems + if( !ts->valid ) + continue; - if( lifeFrac >= 1.0 ) + for( j = 0; j < MAX_TRAIL_BEAMS; j++ ) { - j->inuse = qfalse; // flag it as dead - j->width = j->widthEnd; - j->alpha = j->alphaEnd; + tb = &trailBeams[ j ]; + + if( tb->valid && tb->parent == ts ) + count++; + } + + if( !count ) + ts->valid = qfalse; - if( j->alpha > 1.0 ) - j->alpha = 1.0; - else if( j->alpha < 0.0 ) - j->alpha = 0.0; + //check systems where the parent cent has left the PVS + //( local player entity is always valid ) + if( ( centNum = CG_AttachmentCentNum( &ts->frontAttachment ) ) >= 0 && + centNum != cg.snap->ps.clientNum ) + { + trailSystem_t *tempTS = ts; - VectorCopy( j->colorEnd, j->color ); + if( !cg_entities[ centNum ].valid ) + CG_DestroyTrailSystem( &tempTS ); } - else + + if( ( centNum = CG_AttachmentCentNum( &ts->backAttachment ) ) >= 0 && + centNum != cg.snap->ps.clientNum ) { - j->width = j->widthStart + ( j->widthEnd - j->widthStart ) * lifeFrac; - j->alpha = j->alphaStart + ( j->alphaEnd - j->alphaStart ) * lifeFrac; + trailSystem_t *tempTS = ts; - if( j->alpha > 1.0 ) - j->alpha = 1.0; - else if( j->alpha < 0.0 ) - j->alpha = 0.0; + if( !cg_entities[ centNum ].valid ) + CG_DestroyTrailSystem( &tempTS ); + } + + if( cg_debugTrails.integer >= 1 && !ts->valid ) + CG_Printf( "TS %s garbage collected\n", ts->class->name ); + } +} - VectorSubtract( j->colorEnd, j->colorStart, j->color ); - VectorMA( j->colorStart, lifeFrac, j->color, j->color ); +/* +=============== +CG_AddTrails + +Add trails to the scene +=============== +*/ +void CG_AddTrails( void ) +{ + int i; + trailBeam_t *tb; + int numTS = 0, numTB = 0; + + //remove expired trail systems + CG_GarbageCollectTrailSystems( ); + + for( i = 0; i < MAX_TRAIL_BEAMS; i++ ) + { + tb = &trailBeams[ i ]; + + if( tb->valid ) + { + CG_UpdateBeam( tb ); + CG_RenderBeam( tb ); } + } + + if( cg_debugTrails.integer >= 2 ) + { + for( i = 0; i < MAX_TRAIL_SYSTEMS; i++ ) + if( trailSystems[ i ].valid ) + numTS++; + + for( i = 0; i < MAX_TRAIL_BEAMS; i++ ) + if( trailBeams[ i ].valid ) + numTB++; - j = j->nextGlobal; + CG_Printf( "TS: %d TB: %d\n", numTS, numTB ); } +} + +static trailSystem_t *testTS; +static qhandle_t testTSHandle; + +/* +=============== +CG_DestroyTestTS_f + +Destroy the test a trail system +=============== +*/ +void CG_DestroyTestTS_f( void ) +{ + if( CG_IsTrailSystemValid( &testTS ) ) + CG_DestroyTrailSystem( &testTS ); +} + +/* +=============== +CG_TestTS_f - // draw the trailHeads - j = headTrails; +Test a trail system +=============== +*/ +void CG_TestTS_f( void ) +{ + char tsName[ MAX_QPATH ]; - while( j ) + if( trap_Argc( ) < 2 ) + return; + + Q_strncpyz( tsName, CG_Argv( 1 ), MAX_QPATH ); + testTSHandle = CG_RegisterTrailSystem( tsName ); + + if( testTSHandle ) { - jNext = j->nextHead; // in case it gets removed + CG_DestroyTestTS_f( ); - if( !j->inuse ) - CG_FreeTrailJunc( j ); - else - CG_AddTrailToScene( j, 0, 0 ); + testTS = CG_SpawnNewTrailSystem( testTSHandle ); - j = jNext; + if( CG_IsTrailSystemValid( &testTS ) ) + { + CG_SetAttachmentCent( &testTS->frontAttachment, &cg_entities[ 0 ] ); + CG_AttachToCent( &testTS->frontAttachment ); + } } } |