summaryrefslogtreecommitdiff
path: root/src/cgame/cg_trails.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cgame/cg_trails.c')
-rw-r--r--src/cgame/cg_trails.c1746
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 );
+ }
}
}