diff options
Diffstat (limited to 'src/cgame')
| -rw-r--r-- | src/cgame/cg_attachment.c | 344 | ||||
| -rw-r--r-- | src/cgame/cg_buildable.c | 40 | ||||
| -rw-r--r-- | src/cgame/cg_consolecmds.c | 4 | ||||
| -rw-r--r-- | src/cgame/cg_ents.c | 103 | ||||
| -rw-r--r-- | src/cgame/cg_event.c | 44 | ||||
| -rw-r--r-- | src/cgame/cg_local.h | 318 | ||||
| -rw-r--r-- | src/cgame/cg_main.c | 11 | ||||
| -rw-r--r-- | src/cgame/cg_particles.c | 500 | ||||
| -rw-r--r-- | src/cgame/cg_players.c | 7 | ||||
| -rw-r--r-- | src/cgame/cg_servercmds.c | 14 | ||||
| -rw-r--r-- | src/cgame/cg_trails.c | 1746 | ||||
| -rw-r--r-- | src/cgame/cg_view.c | 6 | ||||
| -rw-r--r-- | src/cgame/cg_weapons.c | 87 | 
13 files changed, 2080 insertions, 1144 deletions
diff --git a/src/cgame/cg_attachment.c b/src/cgame/cg_attachment.c new file mode 100644 index 00000000..d8f20d90 --- /dev/null +++ b/src/cgame/cg_attachment.c @@ -0,0 +1,344 @@ +// cg_attachment.c -- an abstract attachment system + +/* + *  Portions Copyright (C) 2000-2001 Tim Angus + * + *  This program is free software; you can redistribute it and/or modify it + *  under the terms of the OSML - Open Source Modification License v1.0 as + *  described in the file COPYING which is distributed with this source + *  code. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "cg_local.h" + +/* +=============== +CG_AttachmentPoint + +Return the attachment point +=============== +*/ +qboolean CG_AttachmentPoint( attachment_t *a, vec3_t v ) +{ +  centity_t   *cent; + +  if( !a ) +    return qfalse; + +  switch( a->type ) +  { +    case AT_STATIC: +      if( !a->staticValid ) +        return qfalse; + +      VectorCopy( a->origin, v ); +      break; + +    case AT_TAG: +      if( !a->tagValid ) +        return qfalse; + +      AxisCopy( axisDefault, a->re.axis ); +      CG_PositionRotatedEntityOnTag( &a->re, &a->parent, +                                     a->model, a->tagName ); +      VectorCopy( a->re.origin, v ); +      break; + +    case AT_CENT: +      if( !a->centValid ) +        return qfalse; + +      if( a->centNum == cg.predictedPlayerState.clientNum ) +      { +        // this is smoother if it's the local client +        VectorCopy( cg.predictedPlayerState.origin, v ); +      } +      else +      { +        cent = &cg_entities[ a->centNum ]; +        VectorCopy( cent->lerpOrigin, v ); +      } +      break; + +    case AT_PARTICLE: +      if( !a->particleValid ) +        return qfalse; + +      if( !a->particle->valid ) +      { +        a->particleValid = qfalse; +        return qfalse; +      } +      else +        VectorCopy( a->particle->origin, v ); +      break; + +    default: +      CG_Printf( S_COLOR_RED "ERROR: Invalid attachmentType_t in attachment\n" ); +      break; +  } + +  if( a->hasOffset ) +    VectorAdd( v, a->offset, v ); + +  return qtrue; +} + +/* +=============== +CG_AttachmentDir + +Return the attachment direction +=============== +*/ +qboolean CG_AttachmentDir( attachment_t *a, vec3_t v ) +{ +  vec3_t      forward; +  centity_t   *cent; + +  if( !a ) +    return qfalse; + +  switch( a->type ) +  { +    case AT_STATIC: +      //FIXME: hmmmmmmm +      return qfalse; +      break; + +    case AT_TAG: +      if( !a->tagValid ) +        return qfalse; + +      VectorCopy( a->re.axis[ 0 ], v ); +      break; + +    case AT_CENT: +      if( !a->centValid ) +        return qfalse; + +      cent = &cg_entities[ a->centNum ]; +      AngleVectors( cent->lerpAngles, forward, NULL, NULL ); +      VectorCopy( forward, v ); +      break; + +    case AT_PARTICLE: +      if( !a->particleValid ) +        return qfalse; + +      if( !a->particle->valid ) +      { +        a->particleValid = qfalse; +        return qfalse; +      } +      else +        VectorCopy( a->particle->velocity, v ); +      break; + +    default: +      CG_Printf( S_COLOR_RED "ERROR: Invalid attachmentType_t in attachment\n" ); +      break; +  } + +  VectorNormalize( v ); +  return qtrue; +} + +/* +=============== +CG_AttachmentVelocity + +If the attachment can have velocity, return it +=============== +*/ +qboolean CG_AttachmentVelocity( attachment_t *a, vec3_t v ) +{ +  if( !a ) +    return qfalse; + +  if( a->particleValid && a->particle->valid ) +  { +    VectorCopy( a->particle->velocity, v ); +    return qtrue; +  } +  else if( a->centValid ) +  { +    centity_t *cent = &cg_entities[ a->centNum ]; + +    VectorCopy( cent->currentState.pos.trDelta, v ); +    return qtrue; +  } + +  return qfalse; +} + +/* +=============== +CG_AttachmentCentNum + +If the attachment has a centNum, return it +=============== +*/ +int CG_AttachmentCentNum( attachment_t *a ) +{ +  if( !a || !a->centValid ) +    return -1; + +  return a->centNum; +} + +/* +=============== +CG_Attached + +If the attachment is valid, return qtrue +=============== +*/ +qboolean CG_Attached( attachment_t *a ) +{ +  if( !a ) +    return qfalse; + +  return a->attached; +} + +/* +=============== +CG_AttachToPoint + +Attach to a point in space +=============== +*/ +void CG_AttachToPoint( attachment_t *a ) +{ +  if( !a || !a->staticValid ) +    return; + +  a->type = AT_STATIC; +  a->attached = qtrue; +} + +/* +=============== +CG_AttachToCent + +Attach to a centity_t +=============== +*/ +void CG_AttachToCent( attachment_t *a ) +{ +  if( !a || !a->centValid ) +    return; + +  a->type = AT_CENT; +  a->attached = qtrue; +} + +/* +=============== +CG_AttachToTag + +Attach to a model tag +=============== +*/ +void CG_AttachToTag( attachment_t *a ) +{ +  if( !a || !a->tagValid ) +    return; + +  a->type = AT_TAG; +  a->attached = qtrue; +} + +/* +=============== +CG_AttachToParticle + +Attach to a particle +=============== +*/ +void CG_AttachToParticle( attachment_t *a ) +{ +  if( !a || !a->particleValid ) +    return; + +  a->type = AT_PARTICLE; +  a->attached = qtrue; +} + +/* +=============== +CG_SetAttachmentPoint +=============== +*/ +void CG_SetAttachmentPoint( attachment_t *a, vec3_t v ) +{ +  if( !a ) +    return; + +  VectorCopy( v, a->origin ); +  a->staticValid = qtrue; +} + +/* +=============== +CG_SetAttachmentCent +=============== +*/ +void CG_SetAttachmentCent( attachment_t *a, centity_t *cent ) +{ +  if( !a || !cent ) +    return; + +  a->centNum = cent->currentState.number; +  a->centValid = qtrue; +} + +/* +=============== +CG_SetAttachmentTag +=============== +*/ +void CG_SetAttachmentTag( attachment_t *a, refEntity_t parent, +    qhandle_t model, char *tagName ) +{ +  if( !a ) +    return; + +  a->parent = parent; +  a->model = model; +  strncpy( a->tagName, tagName, MAX_STRING_CHARS ); +  a->tagValid = qtrue; +} + +/* +=============== +CG_SetAttachmentParticle +=============== +*/ +void CG_SetAttachmentParticle( attachment_t *a, particle_t *p ) +{ +  if( !a ) +    return; + +  a->particle = p; +  a->particleValid = qtrue; +} + +/* +=============== +CG_SetAttachmentOffset +=============== +*/ +void CG_SetAttachmentOffset( attachment_t *a, vec3_t v ) +{ +  if( !a ) +    return; + +  VectorCopy( v, a->offset ); +  a->hasOffset = qtrue; +} diff --git a/src/cgame/cg_buildable.c b/src/cgame/cg_buildable.c index 38259dc6..1daaf464 100644 --- a/src/cgame/cg_buildable.c +++ b/src/cgame/cg_buildable.c @@ -108,9 +108,13 @@ void CG_AlienBuildableExplosion( vec3_t origin, vec3_t dir )    //particle system    ps = CG_SpawnNewParticleSystem( cgs.media.alienBuildableDestroyedPS ); -  CG_SetParticleSystemOrigin( ps, origin ); -  CG_SetParticleSystemNormal( ps, dir ); -  CG_AttachParticleSystemToOrigin( ps ); + +  if( CG_IsParticleSystemValid( &ps ) ) +  { +    CG_SetAttachmentPoint( &ps->attachment, origin ); +    CG_SetParticleSystemNormal( ps, dir ); +    CG_AttachToPoint( &ps->attachment ); +  }  } @@ -205,9 +209,13 @@ void CG_HumanBuildableExplosion( vec3_t origin, vec3_t dir )    //particle system    ps = CG_SpawnNewParticleSystem( cgs.media.humanBuildableDestroyedPS ); -  CG_SetParticleSystemOrigin( ps, origin ); -  CG_SetParticleSystemNormal( ps, dir ); -  CG_AttachParticleSystemToOrigin( ps ); + +  if( CG_IsParticleSystemValid( &ps ) ) +  { +    CG_SetAttachmentPoint( &ps->attachment, origin ); +    CG_SetParticleSystemNormal( ps, dir ); +    CG_AttachToPoint( &ps->attachment ); +  }  } @@ -500,6 +508,8 @@ void CG_InitBuildables( )      cg.buildablesFraction = (float)i / (float)( BA_NUM_BUILDABLES - 1 );      trap_UpdateScreen( );    } + +  cgs.media.teslaZapTS = CG_RegisterTrailSystem( "models/buildables/tesla/zap" );  }  /* @@ -823,8 +833,12 @@ static void CG_BuildableParticleEffects( centity_t *cent )      if( healthFrac < 0.33f && !CG_IsParticleSystemValid( ¢->buildablePS ) )      {        cent->buildablePS = CG_SpawnNewParticleSystem( cgs.media.humanBuildableDamagedPS ); -      CG_SetParticleSystemCent( cent->buildablePS, cent ); -      CG_AttachParticleSystemToCent( cent->buildablePS ); + +      if( CG_IsParticleSystemValid( ¢->buildablePS ) ) +      { +        CG_SetAttachmentCent( ¢->buildablePS->attachment, cent ); +        CG_AttachToCent( ¢->buildablePS->attachment ); +      }      }      else if( healthFrac >= 0.33f && CG_IsParticleSystemValid( ¢->buildablePS ) )        CG_DestroyParticleSystem( ¢->buildablePS ); @@ -834,9 +848,13 @@ static void CG_BuildableParticleEffects( centity_t *cent )      if( healthFrac < 0.33f && !CG_IsParticleSystemValid( ¢->buildablePS ) )      {        cent->buildablePS = CG_SpawnNewParticleSystem( cgs.media.alienBuildableDamagedPS ); -      CG_SetParticleSystemCent( cent->buildablePS, cent ); -      CG_SetParticleSystemNormal( cent->buildablePS, es->origin2 ); -      CG_AttachParticleSystemToCent( cent->buildablePS ); + +      if( CG_IsParticleSystemValid( ¢->buildablePS ) ) +      { +        CG_SetAttachmentCent( ¢->buildablePS->attachment, cent ); +        CG_SetParticleSystemNormal( cent->buildablePS, es->origin2 ); +        CG_AttachToCent( ¢->buildablePS->attachment ); +      }      }      else if( healthFrac >= 0.33f && CG_IsParticleSystemValid( ¢->buildablePS ) )        CG_DestroyParticleSystem( ¢->buildablePS ); diff --git a/src/cgame/cg_consolecmds.c b/src/cgame/cg_consolecmds.c index bf15145c..c875fa48 100644 --- a/src/cgame/cg_consolecmds.c +++ b/src/cgame/cg_consolecmds.c @@ -171,6 +171,10 @@ static consoleCommand_t commands[ ] =    { "tell_target", CG_TellTarget_f },    { "tell_attacker", CG_TellAttacker_f },    { "tcmd", CG_TargetCommand_f }, +  { "testPS", CG_TestPS_f }, +  { "destroyTestPS", CG_DestroyTestPS_f }, +  { "testTS", CG_TestTS_f }, +  { "destroyTestTS", CG_DestroyTestTS_f },  }; diff --git a/src/cgame/cg_ents.c b/src/cgame/cg_ents.c index c4adf46b..8c047e27 100644 --- a/src/cgame/cg_ents.c +++ b/src/cgame/cg_ents.c @@ -240,6 +240,8 @@ static void CG_EntityEffects( centity_t *cent )      trap_R_AddLightToScene( cent->lerpOrigin, i, r, g, b );    } +  if( cg.time > cent->muzzleTSDeathTime && CG_IsTrailSystemValid( ¢->muzzleTS ) ) +    CG_DestroyTrailSystem( ¢->muzzleTS );  } @@ -320,6 +322,7 @@ static void CG_LaunchMissile( centity_t *cent )    entityState_t       *es;    const weaponInfo_t  *wi;    particleSystem_t    *ps; +  trailSystem_t       *ts;    weapon_t            weapon;    weaponMode_t        weaponMode; @@ -335,8 +338,23 @@ static void CG_LaunchMissile( centity_t *cent )    if( wi->wim[ weaponMode ].missileParticleSystem )    {      ps = CG_SpawnNewParticleSystem( wi->wim[ weaponMode ].missileParticleSystem ); -    CG_SetParticleSystemCent( ps, cent ); -    CG_AttachParticleSystemToCent( ps ); + +    if( CG_IsParticleSystemValid( &ps ) ) +    { +      CG_SetAttachmentCent( &ps->attachment, cent ); +      CG_AttachToCent( &ps->attachment ); +    } +  } + +  if( wi->wim[ weaponMode ].missileTrailSystem ) +  { +    ts = CG_SpawnNewTrailSystem( wi->wim[ weaponMode ].missileTrailSystem ); + +    if( CG_IsTrailSystemValid( &ts ) ) +    { +      CG_SetAttachmentCent( &ts->frontAttachment, cent ); +      CG_AttachToCent( &ts->frontAttachment ); +    }    }  } @@ -763,54 +781,51 @@ CG_Lev2ZapChain  */  static void CG_Lev2ZapChain( centity_t *cent )  { -  int           i = 0; +  int           i;    entityState_t *es; -  vec3_t        start, end;    centity_t     *source, *target;    es = ¢->currentState; -  if( es->time > 0 ) +  for( i = 0; i <= 2; i++ )    { -    source = &cg_entities[ es->powerups ]; -    target = &cg_entities[ es->time ]; - -    if( es->powerups == cg.predictedPlayerState.clientNum ) -      VectorCopy( cg.predictedPlayerState.origin, start ); -    else -      VectorCopy( source->currentState.pos.trBase, start ); - -    VectorCopy( target->currentState.pos.trBase, end ); +    switch( i ) +    { +      case 0: +        if( es->time <= 0 ) +          continue; -    CG_DynamicLightningBolt( cgs.media.lightningShader, start, end, -                             1+((cg.time%((i+2)*(i+3)))+i)%2, 7 + (float)(i%3)*5 + 6.0*random(), -                             qtrue, 1.0, 0, i*i*3 ); -  } +        source = &cg_entities[ es->powerups ]; +        target = &cg_entities[ es->time ]; +        break; -  if( es->time2 > 0 ) -  { -    source = &cg_entities[ es->time ]; -    target = &cg_entities[ es->time2 ]; +      case 1: +        if( es->time2 <= 0 ) +          continue; -    VectorCopy( source->currentState.pos.trBase, start ); -    VectorCopy( target->currentState.pos.trBase, end ); +        source = &cg_entities[ es->time ]; +        target = &cg_entities[ es->time2 ]; +        break; -    CG_DynamicLightningBolt( cgs.media.lightningShader, start, end, -                             1+((cg.time%((i+2)*(i+3)))+i)%2, 7 + (float)(i%3)*5 + 6.0*random(), -                             qtrue, 1.0, 0, i*i*3 ); -  } +      case 2: +        if( es->constantLight <= 0 ) +          continue; -  if( es->constantLight > 0 ) -  { -    source = &cg_entities[ es->time2 ]; -    target = &cg_entities[ es->constantLight ]; +        source = &cg_entities[ es->time2 ]; +        target = &cg_entities[ es->constantLight ]; +        break; +    } -    VectorCopy( source->currentState.pos.trBase, start ); -    VectorCopy( target->currentState.pos.trBase, end ); +    if( !CG_IsTrailSystemValid( ¢->level2ZapTS[ i ] ) ) +      cent->level2ZapTS[ i ] = CG_SpawnNewTrailSystem( cgs.media.level2ZapTS ); -    CG_DynamicLightningBolt( cgs.media.lightningShader, start, end, -                             1+((cg.time%((i+2)*(i+3)))+i)%2, 7 + (float)(i%3)*5 + 6.0*random(), -                             qtrue, 1.0, 0, i*i*3 ); +    if( CG_IsTrailSystemValid( ¢->level2ZapTS[ i ] ) ) +    { +      CG_SetAttachmentCent( ¢->level2ZapTS[ i ]->frontAttachment, source ); +      CG_SetAttachmentCent( ¢->level2ZapTS[ i ]->backAttachment, target ); +      CG_AttachToCent( ¢->level2ZapTS[ i ]->frontAttachment ); +      CG_AttachToCent( ¢->level2ZapTS[ i ]->backAttachment ); +    }    }  } @@ -983,8 +998,22 @@ CG_CEntityPVSLeave  */  static void CG_CEntityPVSLeave( centity_t *cent )  { +  int           i; +  entityState_t *es = ¢->currentState; +    if( cg_debugPVS.integer )      CG_Printf( "Entity %d left PVS\n", cent->currentState.number ); + +  switch( es->eType ) +  { +    case ET_LEV2_ZAP_CHAIN: +      for( i = 0; i <= 2; i++ ) +      { +        if( CG_IsTrailSystemValid( ¢->level2ZapTS[ i ] ) ) +          CG_DestroyTrailSystem( ¢->level2ZapTS[ i ] ); +      } +      break; +  }  } diff --git a/src/cgame/cg_event.c b/src/cgame/cg_event.c index 4d176096..9273922f 100644 --- a/src/cgame/cg_event.c +++ b/src/cgame/cg_event.c @@ -740,7 +740,29 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )      case EV_TESLATRAIL:        DEBUGNAME( "EV_TESLATRAIL" );        cent->currentState.weapon = WP_TESLAGEN; -      CG_TeslaTrail( es->origin2, es->pos.trBase, es->generic1, es->clientNum ); +      { +        centity_t *source = &cg_entities[ es->generic1 ]; +        centity_t *target = &cg_entities[ es->clientNum ]; +        vec3_t    sourceOffset = { 0.0f, 0.0f, 28.0f }; +        vec3_t    targetOffset = { 0.0f, 0.0f, -2.0f }; + +        if( !CG_IsTrailSystemValid( &source->muzzleTS ) ) +        { +          source->muzzleTS = CG_SpawnNewTrailSystem( cgs.media.teslaZapTS ); + +          if( CG_IsTrailSystemValid( &source->muzzleTS ) ) +          { +            CG_SetAttachmentCent( &source->muzzleTS->frontAttachment, source ); +            CG_SetAttachmentCent( &source->muzzleTS->backAttachment, target ); +            CG_AttachToCent( &source->muzzleTS->frontAttachment ); +            CG_AttachToCent( &source->muzzleTS->backAttachment ); +            CG_SetAttachmentOffset( &source->muzzleTS->frontAttachment, sourceOffset ); +            CG_SetAttachmentOffset( &source->muzzleTS->backAttachment, targetOffset ); + +            source->muzzleTSDeathTime = cg.time + cg_teslaTrailTime.integer; +          } +        } +      }        break;      case EV_BULLET_HIT_WALL: @@ -880,8 +902,12 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )        trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.alienEvolveSound );        {          particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.alienEvolvePS ); -        CG_SetParticleSystemCent( ps, cent ); -        CG_AttachParticleSystemToCent( ps ); + +        if( CG_IsParticleSystemValid( &ps ) ) +        { +          CG_SetAttachmentCent( &ps->attachment, cent ); +          CG_AttachToCent( &ps->attachment ); +        }        }        if( es->number == cg.clientNum ) @@ -902,10 +928,14 @@ void CG_EntityEvent( centity_t *cent, vec3_t position )        DEBUGNAME( "EV_ALIEN_ACIDTUBE" );        {          particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.alienAcidTubePS ); -        CG_SetParticleSystemCent( ps, cent ); -        ByteToDir( es->eventParm, dir ); -        CG_SetParticleSystemNormal( ps, dir ); -        CG_AttachParticleSystemToCent( ps ); + +        if( CG_IsParticleSystemValid( &ps ) ) +        { +          CG_SetAttachmentCent( &ps->attachment, cent ); +          ByteToDir( es->eventParm, dir ); +          CG_SetParticleSystemNormal( ps, dir ); +          CG_AttachToCent( &ps->attachment ); +        }        }        break; diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h index d6b07714..069dd604 100644 --- a/src/cgame/cg_local.h +++ b/src/cgame/cg_local.h @@ -71,19 +71,6 @@  #define NUM_CROSSHAIRS      10 -//TA: ripped from wolf source -// Ridah, trails -#define STYPE_STRETCH 0 -#define STYPE_REPEAT  1 - -#define TJFL_FADEIN   (1<<0) -#define TJFL_CROSSOVER  (1<<1) -#define TJFL_NOCULL   (1<<2) -#define TJFL_FIXDISTORT (1<<3) -#define TJFL_SPARKHEADFLARE (1<<4) -#define TJFL_NOPOLYMERGE  (1<<5) -// done. -  #define TEAM_OVERLAY_MAXNAME_WIDTH  12  #define TEAM_OVERLAY_MAXLOCATION_WIDTH  16 @@ -124,6 +111,51 @@ typedef enum    JPS_ASCENDING  } jetPackState_t; +//====================================================================== + +//attachment system +typedef enum +{ +  AT_STATIC, +  AT_TAG, +  AT_CENT, +  AT_PARTICLE +} attachmentType_t; + +//forward declaration for particle_t +struct particle_s; + +typedef struct attachment_s +{ +  attachmentType_t  type; +  qboolean          attached; + +  qboolean          staticValid; +  qboolean          tagValid; +  qboolean          centValid; +  qboolean          particleValid; + +  qboolean          hasOffset; +  vec3_t            offset; + +  //AT_STATIC +  vec3_t            origin; + +  //AT_TAG +  refEntity_t       re;     //FIXME: should be pointers? +  refEntity_t       parent; // +  qhandle_t         model; +  char              tagName[ MAX_STRING_CHARS ]; + +  //AT_CENT +  int               centNum; + +  //AT_PARTICLE +  struct particle_s *particle; +} attachment_t; + +//====================================================================== +  //particle system stuff  #define MAX_SHADER_FRAMES         32  #define MAX_EJECTORS_PER_SYSTEM   4 @@ -269,51 +301,18 @@ RUN TIME STRUCTURES  ===============  */ -typedef enum -{ -  PSA_STATIC, -  PSA_TAG, -  PSA_CENT_ORIGIN, -  PSA_PARTICLE -} psAttachmentType_t; - - -typedef struct psAttachment_s -{ -  qboolean staticValid; -  qboolean tagValid; -  qboolean centValid; -  qboolean normalValid; -  qboolean particleValid; - -  //PMT_STATIC -  vec3_t      origin; - -  //PMT_TAG -  refEntity_t re;     //FIXME: should be pointers? -  refEntity_t parent; // -  qhandle_t   model; -  char        tagName[ MAX_STRING_CHARS ]; - -  //PMT_CENT_ANGLES -  int         centNum; - -  //PMT_NORMAL -  vec3_t      normal; -} psAttachment_t; - -  typedef struct particleSystem_s  {    baseParticleSystem_t  *class; -  psAttachmentType_t    attachType; -  psAttachment_t        attachment; -  qboolean              attached;   //is the particle system attached to anything +  attachment_t          attachment;    qboolean              valid;    qboolean              lazyRemove; //mark this system for later removal +  //for PMT_NORMAL +  qboolean              normalValid; +  vec3_t                normal;  } particleSystem_t; @@ -357,14 +356,133 @@ typedef struct particle_s    pLerpValues_t     rotation;    qboolean          valid; +  int               frameWhenInvalidated;    int               sortKey; - -  particleSystem_t  *childSystem;  } particle_t; +//====================================================================== -//================================================= +//trail system stuff +#define MAX_BEAMS_PER_SYSTEM      4 + +#define MAX_BASETRAIL_SYSTEMS     64 +#define MAX_BASETRAIL_BEAMS       MAX_BASETRAIL_SYSTEMS*MAX_BEAMS_PER_SYSTEM + +#define MAX_TRAIL_SYSTEMS         32 +#define MAX_TRAIL_BEAMS           MAX_TRAIL_SYSTEMS*MAX_BEAMS_PER_SYSTEM +#define MAX_TRAIL_BEAM_NODES      128 + +#define MAX_TRAIL_BEAM_JITTERS    4 + +typedef enum +{ +  TBTT_STRETCH, +  TBTT_REPEAT +} trailBeamTextureType_t; + +typedef struct baseTrailJitter_s +{ +  float   magnitude; +  int     period; +} baseTrailJitter_t; + +//beam template +typedef struct baseTrailBeam_s +{ +  int                     numSegments; +  float                   frontWidth; +  float                   backWidth; +  float                   frontAlpha; +  float                   backAlpha; +  byte                    frontColor[ 3 ]; +  byte                    backColor[ 3 ]; + +  // the time it takes for a segment to vanish (single attached only) +  int                     segmentTime; + +  // the time it takes for a beam to fade out (double attached only) +  int                     fadeOutTime; + +  char                    shaderName[ MAX_QPATH ]; +  qhandle_t               shader; + +  trailBeamTextureType_t  textureType; + +  //TBTT_STRETCH +  float                   frontTextureCoord; +  float                   backTextureCoord; + +  //TBTT_REPEAT +  float                   repeatLength; +  qboolean                clampToBack; + +  qboolean                realLight; + +  int                     numJitters; +  baseTrailJitter_t       jitters[ MAX_TRAIL_BEAM_JITTERS ]; +  qboolean                jitterAttachments; +} baseTrailBeam_t; + + +//trail system template +typedef struct baseTrailSystem_s +{ +  char            name[ MAX_QPATH ]; +  baseTrailBeam_t *beams[ MAX_BEAMS_PER_SYSTEM ]; +  int             numBeams; + +  qboolean        registered; //whether or not the assets for this trail have been loaded +} baseTrailSystem_t; + +typedef struct trailSystem_s +{ +  baseTrailSystem_t   *class; + +  attachment_t        frontAttachment; +  attachment_t        backAttachment; + +  int                 destroyTime; +  qboolean            valid; +} trailSystem_t; + +typedef struct trailBeamNode_s +{ +  vec3_t                  refPosition; +  vec3_t                  position; + +  int                     timeLeft; + +  float                   textureCoord; +  float                   halfWidth; +  byte                    alpha; +  byte                    color[ 3 ]; + +  float                   jitters[ MAX_TRAIL_BEAM_JITTERS ]; +  float                   jitter; +   +  struct trailBeamNode_s  *prev; +  struct trailBeamNode_s  *next; + +  qboolean                used; +} trailBeamNode_t; + +typedef struct trailBeam_s +{ +  baseTrailBeam_t   *class; +  trailSystem_t     *parent; + +  trailBeamNode_t   nodePool[ MAX_TRAIL_BEAM_NODES ]; +  trailBeamNode_t   *nodes; + +  int               lastEvalTime; +   +  qboolean          valid; + +  int               nextJitterTimes[ MAX_TRAIL_BEAM_JITTERS ]; +} trailBeam_t; + +//======================================================================  // player entities need to track more information  // than any other type of entity. @@ -435,8 +553,6 @@ typedef struct lightFlareStatus_s  //================================================= -#define MAX_CENTITY_PARTICLE_SYSTEMS  8 -  // centity_t have a direct corespondence with gentity_t in the game, but  // only the entityState_t is directly communicated to the cgame  typedef struct centity_s @@ -495,6 +611,11 @@ typedef struct centity_s    particleSystem_t      *entityPS;    qboolean              entityPSMissing; +  trailSystem_t         *level2ZapTS[ 3 ]; + +  trailSystem_t         *muzzleTS; //used for the tesla and reactor +  int                   muzzleTSDeathTime; +    qboolean              valid;    qboolean              oldValid;  } centity_t; @@ -521,7 +642,6 @@ typedef enum  {    LE_MARK,    LE_EXPLOSION, -  LE_LIGHTNING_BOLT, //wolf trail    LE_SPRITE_EXPLOSION,    LE_FRAGMENT,    LE_MOVE_SCALE_FADE, @@ -700,6 +820,7 @@ typedef struct weaponInfoMode_s    qhandle_t   missileSprite;    int         missileSpriteSize;    qhandle_t   missileParticleSystem; +  qhandle_t   missileTrailSystem;    qboolean    missileRotates;    qboolean    missileAnimates;    int         missileAnimStartFrame; @@ -1086,12 +1207,10 @@ typedef struct    qhandle_t   gibSpark1;    qhandle_t   gibSpark2; -  qhandle_t   smoke2; -    qhandle_t   machinegunBrassModel;    qhandle_t   shotgunBrassModel; -  qhandle_t   lightningShader; +  qhandle_t   level2ZapTS;    qhandle_t   friendShader; @@ -1148,7 +1267,6 @@ typedef struct    qhandle_t   bulletFlashModel;    qhandle_t   ringFlashModel;    qhandle_t   dishFlashModel; -  qhandle_t   lightningExplosionModel;    // weapon effect shaders    qhandle_t   bloodExplosionShader; @@ -1216,9 +1334,6 @@ typedef struct    qhandle_t   selectCursor;    qhandle_t   sizeCursor; -  //TA: for wolf trail effects -  qhandle_t   sparkFlareShader; -    //light armour    qhandle_t larmourHeadSkin;    qhandle_t larmourLegsSkin; @@ -1243,6 +1358,7 @@ typedef struct    qhandle_t   humanBuildableDestroyedPS;    qhandle_t   alienBuildableDamagedPS;    qhandle_t   alienBuildableDestroyedPS; +  qhandle_t   teslaZapTS;    sfxHandle_t lCannonWarningSound; @@ -1463,6 +1579,7 @@ extern  vmCvar_t    cg_depthSortParticles;  extern  vmCvar_t    cg_consoleLatency;  extern  vmCvar_t    cg_lightFlare;  extern  vmCvar_t    cg_debugParticles; +extern  vmCvar_t    cg_debugTrails;  extern  vmCvar_t    cg_debugPVS;  extern  vmCvar_t    cg_disableWarningDialogs;  extern  vmCvar_t    cg_disableScannerPlane; @@ -1659,7 +1776,6 @@ void        CG_MissileHitPlayer( weapon_t weapon, weaponMode_t weaponMode, vec3_  void        CG_Bullet( vec3_t origin, int sourceEntityNum, vec3_t normal, qboolean flesh, int fleshEntityNum );  void        CG_ShotgunFire( entityState_t *es ); -void        CG_TeslaTrail( vec3_t start, vec3_t end, int srcENum, int destENum );  void        CG_AddViewWeapon (playerState_t *ps);  void        CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent );  void        CG_DrawItemSelect( rectDef_t *rect, vec4_t color ); @@ -1707,7 +1823,6 @@ localEntity_t *CG_SmokePuff( const vec3_t p,  void          CG_BubbleTrail( vec3_t start, vec3_t end, float spacing );  void          CG_SpawnEffect( vec3_t org );  void          CG_GibPlayer( vec3_t playerOrigin ); -void          CG_BigExplode( vec3_t playerOrigin );  void          CG_Bleed( vec3_t origin, int entityNum ); @@ -1715,30 +1830,6 @@ localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir,                                   qhandle_t hModel, qhandle_t shader, int msec,                                   qboolean isSprite ); -//TA: wolf tesla effect -void          CG_DynamicLightningBolt( qhandle_t shader, vec3_t start, vec3_t pend, -                                       int numBolts, float maxWidth, qboolean fade, -                                       float startAlpha, int recursion, int randseed ); - -// Ridah, trails -// -// cg_trails.c -// -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 ); -int           CG_AddSparkJunc( int headJuncIndex, qhandle_t shader, vec3_t pos, int trailLife, -                             float alphaStart, float alphaEnd, float startWidth, float endWidth ); -int           CG_AddSmokeJunc( int headJuncIndex, qhandle_t shader, vec3_t pos, int trailLife, -                             float alpha, float startWidth, float endWidth ); -int           CG_AddFireJunc( int headJuncIndex, qhandle_t shader, vec3_t pos, int trailLife, -                            float alpha, float startWidth, float endWidth ); -void          CG_AddTrails( void ); -void          CG_ClearTrails( void ); -// done. - -  //  // cg_snapshot.c  // @@ -1774,6 +1865,28 @@ void          CG_Free( void *ptr );  void          CG_DefragmentMemory( void );  // +// cg_attachment.c +// +qboolean    CG_AttachmentPoint( attachment_t *a, vec3_t v ); +qboolean    CG_AttachmentDir( attachment_t *a, vec3_t v ); +qboolean    CG_AttachmentVelocity( attachment_t *a, vec3_t v ); +int         CG_AttachmentCentNum( attachment_t *a ); + +qboolean    CG_Attached( attachment_t *a ); + +void        CG_AttachToPoint( attachment_t *a ); +void        CG_AttachToCent( attachment_t *a ); +void        CG_AttachToTag( attachment_t *a ); +void        CG_AttachToParticle( attachment_t *a ); +void        CG_SetAttachmentPoint( attachment_t *a, vec3_t v ); +void        CG_SetAttachmentCent( attachment_t *a, centity_t *cent ); +void        CG_SetAttachmentTag( attachment_t *a, refEntity_t parent, +                qhandle_t model, char *tagName ); +void        CG_SetAttachmentParticle( attachment_t *a, particle_t *p ); + +void        CG_SetAttachmentOffset( attachment_t *a, vec3_t v ); + +//  // cg_particles.c  //  void                CG_LoadParticleSystems( void ); @@ -1785,20 +1898,31 @@ void                CG_DestroyParticleSystem( particleSystem_t **ps );  qboolean            CG_IsParticleSystemInfinite( particleSystem_t *ps );  qboolean            CG_IsParticleSystemValid( particleSystem_t **ps ); -void                CG_SetParticleSystemCent( particleSystem_t *ps, centity_t *cent ); -void                CG_AttachParticleSystemToCent( particleSystem_t *ps ); -void                CG_SetParticleSystemTag( particleSystem_t *ps, refEntity_t parent, qhandle_t model, char *tagName ); -void                CG_AttachParticleSystemToTag( particleSystem_t *ps ); -void                CG_SetParticleSystemOrigin( particleSystem_t *ps, vec3_t origin ); -void                CG_AttachParticleSystemToOrigin( particleSystem_t *ps );  void                CG_SetParticleSystemNormal( particleSystem_t *ps, vec3_t normal ); -void                CG_AttachParticleSystemToParticle( particleSystem_t *ps ); -void                CG_SetParticleSystemParentParticle( particleSystem_t *ps, particle_t *p );  void                CG_AddParticles( void );  void                CG_ParticleSystemEntity( centity_t *cent ); +void                CG_TestPS_f( void ); +void                CG_DestroyTestPS_f( void ); + +// +// cg_trails.c +// +void                CG_LoadTrailSystems( void ); +qhandle_t           CG_RegisterTrailSystem( char *name ); + +trailSystem_t       *CG_SpawnNewTrailSystem( qhandle_t psHandle ); +void                CG_DestroyTrailSystem( trailSystem_t **ts ); + +qboolean            CG_IsTrailSystemValid( trailSystem_t **ts ); + +void                CG_AddTrails( void ); + +void                CG_TestTS_f( void ); +void                CG_DestroyTestTS_f( void ); +  //  // cg_ptr.c  // diff --git a/src/cgame/cg_main.c b/src/cgame/cg_main.c index a5db719c..663ace40 100644 --- a/src/cgame/cg_main.c +++ b/src/cgame/cg_main.c @@ -199,6 +199,7 @@ vmCvar_t  cg_depthSortParticles;  vmCvar_t  cg_consoleLatency;  vmCvar_t  cg_lightFlare;  vmCvar_t  cg_debugParticles; +vmCvar_t  cg_debugTrails;  vmCvar_t  cg_debugPVS;  vmCvar_t  cg_disableWarningDialogs;  vmCvar_t  cg_disableScannerPlane; @@ -304,6 +305,7 @@ static cvarTable_t cvarTable[ ] =    { &cg_consoleLatency, "cg_consoleLatency", "3000", CVAR_ARCHIVE },    { &cg_lightFlare, "cg_lightFlare", "3", CVAR_ARCHIVE },    { &cg_debugParticles, "cg_debugParticles", "0", CVAR_CHEAT }, +  { &cg_debugTrails, "cg_debugTrails", "0", CVAR_CHEAT },    { &cg_debugPVS, "cg_debugPVS", "0", CVAR_CHEAT },    { &cg_disableWarningDialogs, "cg_disableWarningDialogs", "0", CVAR_ARCHIVE },    { &cg_disableScannerPlane, "cg_disableScannerPlane", "0", CVAR_ARCHIVE }, @@ -794,8 +796,8 @@ static void CG_RegisterGraphics( void )    cgs.media.upgradeClassIconShader    = trap_R_RegisterShader( "icons/icona_upgrade.tga" ); -  cgs.media.machinegunBrassModel      = trap_R_RegisterModel( "models/weapons2/shells/m_shell.md3" ); -  cgs.media.shotgunBrassModel         = trap_R_RegisterModel( "models/weapons2/shells/s_shell.md3" ); +  cgs.media.machinegunBrassModel      = trap_R_RegisterModel( "models/weapons/shells/rifle_shell.md3" ); +  cgs.media.shotgunBrassModel         = trap_R_RegisterModel( "models/weapons/shells/shotgun_shell.md3" );    cgs.media.gibAbdomen                = trap_R_RegisterModel( "models/gibs/abdomen.md3" );    cgs.media.gibArm                    = trap_R_RegisterModel( "models/gibs/arm.md3" ); @@ -825,8 +827,6 @@ static void CG_RegisterGraphics( void )    cgs.media.alienGib3                 = trap_R_RegisterModel( "models/fx/alien_gibs/a_gib3.md3" );    cgs.media.alienGib4                 = trap_R_RegisterModel( "models/fx/alien_gibs/a_gib4.md3" ); -  cgs.media.smoke2                    = trap_R_RegisterModel( "models/weapons2/shells/s_shell.md3" ); -    cgs.media.balloonShader             = trap_R_RegisterShader( "sprites/balloon3" );    cgs.media.bloodExplosionShader      = trap_R_RegisterShader( "bloodExplosion" ); @@ -1803,6 +1803,9 @@ void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum )    CG_LoadParticleSystems( );    CG_UpdateMediaFraction( 0.05f ); +  CG_LoadTrailSystems( ); +  CG_UpdateMediaFraction( 0.05f ); +    CG_RegisterSounds( );    CG_UpdateMediaFraction( 0.60f ); diff --git a/src/cgame/cg_particles.c b/src/cgame/cg_particles.c index b6a52d90..a6ef99e5 100644 --- a/src/cgame/cg_particles.c +++ b/src/cgame/cg_particles.c @@ -25,7 +25,8 @@ static int                    numBaseParticles = 0;  static particleSystem_t     particleSystems[ MAX_PARTICLE_SYSTEMS ];  static particleEjector_t    particleEjectors[ MAX_PARTICLE_EJECTORS ];  static particle_t           particles[ MAX_PARTICLES ]; -static particle_t           sortParticles[ MAX_PARTICLES ]; +static particle_t           *sortedParticles[ MAX_PARTICLES ]; +static particle_t           *radixBuffer[ MAX_PARTICLES ];  /*  =============== @@ -85,7 +86,7 @@ CG_DestroyParticle  Destroy an individual particle  ===============  */ -static void CG_DestroyParticle( particle_t *p ) +static void CG_DestroyParticle( particle_t *p, vec3_t impactNormal )  {    //this particle has an onDeath particle system attached    if( p->class->onDeathSystemName[ 0 ] != '\0' ) @@ -96,13 +97,19 @@ static void CG_DestroyParticle( particle_t *p )      if( CG_IsParticleSystemValid( &ps ) )      { -      CG_SetParticleSystemOrigin( ps, p->origin ); -      CG_SetParticleSystemNormal( ps, p->velocity ); -      CG_AttachParticleSystemToOrigin( ps ); +      if( impactNormal ) +        CG_SetParticleSystemNormal( ps, impactNormal ); + +      CG_SetAttachmentPoint( &ps->attachment, p->origin ); +      CG_AttachToPoint( &ps->attachment );      }    }    p->valid = qfalse; + +  //this gives other systems a couple of +  //frames to realise the particle is gone +  p->frameWhenInvalidated = cg.clientFrame;  }  /* @@ -118,14 +125,14 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p    particle_t              *p = NULL;    particleEjector_t       *pe = parent;    particleSystem_t        *ps = parent->parent; -  vec3_t                  forward; -  centity_t               *cent = &cg_entities[ ps->attachment.centNum ]; +  vec3_t                  attachmentPoint, attachmentVelocity;    for( i = 0; i < MAX_PARTICLES; i++ )    {      p = &particles[ i ]; -    if( !p->valid ) +    //FIXME: the + 1 may be unnecessary +    if( !p->valid && cg.clientFrame > p->frameWhenInvalidated + 1 )      {        memset( p, 0, sizeof( particle_t ) ); @@ -148,59 +155,10 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p        p->rotation.initial = CG_RandomiseValue( bp->rotation.initial, bp->rotation.initialRandFrac );        p->rotation.final = CG_RandomiseValue( bp->rotation.final, bp->rotation.finalRandFrac ); -      switch( ps->attachType ) -      { -        case PSA_STATIC: -          if( !ps->attachment.staticValid ) -            return NULL; - -          VectorCopy( ps->attachment.origin, p->origin ); -          break; - -        case PSA_TAG: -          if( !ps->attachment.tagValid ) -            return NULL; - -          AxisCopy( axisDefault, ps->attachment.re.axis ); -          CG_PositionRotatedEntityOnTag( &ps->attachment.re, &ps->attachment.parent, -                                         ps->attachment.model, ps->attachment.tagName ); -          VectorCopy( ps->attachment.re.origin, p->origin ); -          break; - -        case PSA_CENT_ORIGIN: -          if( !ps->attachment.centValid ) -            return NULL; - -          VectorCopy( cent->lerpOrigin, p->origin ); -          break; - -        case PSA_PARTICLE: -          if( !ps->attachment.particleValid ) -            return NULL; - -          //find a particle which has ps as a child -          for( j = 0; j < MAX_PARTICLES; j++ ) -          { -            particle_t *parentParticle = &particles[ j ]; - -            if( parentParticle->valid && parentParticle->childSystem == ps ) -            { -              VectorCopy( parentParticle->origin, p->origin ); -              break; -            } -          } - -          if( j == MAX_PARTICLES ) -          { -            //didn't find the parent, so it's probably died already - -            //prevent further (expensive) attempts at particle creation -            ps->attachment.particleValid = qfalse; -            return NULL; -          } -          break; -      } +      if( !CG_AttachmentPoint( &ps->attachment, attachmentPoint ) ) +        return NULL; +      VectorCopy( attachmentPoint, p->origin );        VectorAdd( p->origin, bp->displacement, p->origin );        for( j = 0; j <= 2; j++ ) @@ -209,52 +167,32 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p        switch( bp->velMoveType )        {          case PMT_STATIC: -            if( bp->velMoveValues.dirType == PMD_POINT )              VectorSubtract( bp->velMoveValues.point, p->origin, p->velocity );            else if( bp->velMoveValues.dirType == PMD_LINEAR )              VectorCopy( bp->velMoveValues.dir, p->velocity ); -            break;          case PMT_TAG: - -          if( !ps->attachment.tagValid ) -            return NULL; - -          if( bp->velMoveValues.dirType == PMD_POINT ) -            VectorSubtract( ps->attachment.re.origin, p->origin, p->velocity ); -          else if( bp->velMoveValues.dirType == PMD_LINEAR ) -            VectorCopy( ps->attachment.re.axis[ 0 ], p->velocity ); - -          break; -          case PMT_CENT_ANGLES: - -          if( !ps->attachment.centValid ) -            return NULL; -            if( bp->velMoveValues.dirType == PMD_POINT ) -            VectorSubtract( cent->lerpOrigin, p->origin, p->velocity ); +            VectorSubtract( attachmentPoint, p->origin, p->velocity );            else if( bp->velMoveValues.dirType == PMD_LINEAR )            { -            AngleVectors( cent->lerpAngles, forward, NULL, NULL ); -            VectorCopy( forward, p->velocity ); +            if( !CG_AttachmentDir( &ps->attachment, p->velocity ) ) +              return NULL;            } -            break;          case PMT_NORMAL: - -          if( !ps->attachment.normalValid ) +          if( !ps->normalValid )              return NULL; -          VectorCopy( ps->attachment.normal, p->velocity ); +          VectorCopy( ps->normal, p->velocity );            //normal displacement            VectorNormalize( p->velocity );            VectorMA( p->origin, bp->normalDisplacement, p->velocity, p->origin ); -            break;        } @@ -264,11 +202,11 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p                     CG_RandomiseValue( bp->velMoveValues.mag, bp->velMoveValues.magRandFrac ),                     p->velocity ); -      if( ps->attachment.centValid ) +      if( CG_AttachmentVelocity( &ps->attachment, attachmentVelocity ) )        {          VectorMA( p->velocity, -                  CG_RandomiseValue( bp->velMoveValues.parentVelFrac, bp->velMoveValues.parentVelFracRandFrac ), -                  cent->currentState.pos.trDelta, p->velocity ); +            CG_RandomiseValue( bp->velMoveValues.parentVelFrac, +              bp->velMoveValues.parentVelFracRandFrac ), attachmentVelocity, p->velocity );        }        p->lastEvalTime = cg.time; @@ -278,15 +216,12 @@ static particle_t *CG_SpawnNewParticle( baseParticle_t *bp, particleEjector_t *p        //this particle has a child particle system attached        if( bp->childSystemName[ 0 ] != '\0' )        { -        particleSystem_t  *ps; - -        ps = CG_SpawnNewParticleSystem( bp->childSystemHandle ); +        particleSystem_t  *ps = CG_SpawnNewParticleSystem( bp->childSystemHandle );          if( CG_IsParticleSystemValid( &ps ) )          { -          CG_SetParticleSystemParentParticle( ps, p ); -          CG_SetParticleSystemNormal( ps, p->velocity ); -          CG_AttachParticleSystemToParticle( ps ); +          CG_SetAttachmentParticle( &ps->attachment, p ); +          CG_AttachToParticle( &ps->attachment );          }        } @@ -324,7 +259,7 @@ static void CG_SpawnNewParticles( void )      if( pe->valid )      {        //a non attached particle system can't make particles -      if( !ps->attached ) +      if( !CG_Attached( &ps->attachment ) )          continue;        bpe = particleEjectors[ i ].class; @@ -528,60 +463,13 @@ qhandle_t CG_RegisterParticleSystem( char *name )      }    } -  CG_Printf( S_COLOR_RED "ERROR: failed to load particle system %s\n", name ); +  CG_Printf( S_COLOR_RED "ERROR: failed to register particle system %s\n", name );    return 0;  }  /*  =============== -atof_neg - -atof with an allowance for negative values -=============== -*/ -static float atof_neg( char *token, qboolean allowNegative ) -{ -  float value; - -  value = atof( token ); - -  if( !allowNegative && value < 0.0f ) -  { -    CG_Printf( S_COLOR_YELLOW "WARNING: negative value %f is now allowed here, " -                              "replaced with 1.0f\n", value ); -    value = 1.0f; -  } - -  return value; -} - -/* -=============== -atoi_neg - -atoi with an allowance for negative values -=============== -*/ -static int atoi_neg( char *token, qboolean allowNegative ) -{ -  int value; - -  value = atoi( token ); - -  if( !allowNegative && value < 0 ) -  { -    CG_Printf( S_COLOR_YELLOW "WARNING: negative value %d is now allowed here, " -                              "replaced with 1\n", value ); -    value = 1; -  } - -  return value; -} - - -/* -===============  CG_ParseValueAndVariance  Parse a value and its random variance @@ -1275,7 +1163,8 @@ static qboolean CG_ParseParticleSystem( baseParticleSystem_t *bps, char **text_p        }        else if( numBaseParticleEjectors == MAX_BASEPARTICLE_EJECTORS )        { -        CG_Printf( S_COLOR_RED "ERROR: maximum number of particle ejectors (%d) reached\n", MAX_BASEPARTICLE_EJECTORS ); +        CG_Printf( S_COLOR_RED "ERROR: maximum number of particle ejectors (%d) reached\n", +            MAX_BASEPARTICLE_EJECTORS );          return qfalse;        }        else @@ -1347,11 +1236,8 @@ static qboolean CG_ParseParticleFile( const char *fileName )    {      token = COM_Parse( &text_p ); -    if( !token ) -      break; -      if( !Q_stricmp( token, "" ) ) -      return qfalse; +      break;      if( !Q_stricmp( token, "{" ) )      { @@ -1380,7 +1266,8 @@ static qboolean CG_ParseParticleFile( const char *fileName )          if( numBaseParticleSystems == MAX_BASEPARTICLE_SYSTEMS )          { -          CG_Printf( S_COLOR_RED "ERROR: maximum number of particle systems (%d) reached\n", MAX_BASEPARTICLE_EJECTORS ); +          CG_Printf( S_COLOR_RED "ERROR: maximum number of particle systems (%d) reached\n", +              MAX_BASEPARTICLE_SYSTEMS );            return qfalse;          }          else @@ -1518,167 +1405,9 @@ void CG_LoadParticleSystems( void )    }  } - -/* -=============== -CG_AttachParticleSystemToCent - -Attach a particle system to a centity_t -=============== -*/ -void CG_AttachParticleSystemToCent( particleSystem_t *ps ) -{ -  if( ps == NULL || !ps->valid ) -  { -    CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); -    return; -  } - -  ps->attachType = PSA_CENT_ORIGIN; -  ps->attached = qtrue; -} - -/* -=============== -CG_SetParticleSystemCent - -Set a particle system attachment means -=============== -*/ -void CG_SetParticleSystemCent( particleSystem_t *ps, centity_t *cent ) -{ -  if( ps == NULL || !ps->valid ) -  { -    CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); -    return; -  } - -  ps->attachment.centValid = qtrue; -  ps->attachment.centNum = cent->currentState.number; -} - -/* -=============== -CG_AttachParticleSystemToTag - -Attach a particle system to a model tag -=============== -*/ -void CG_AttachParticleSystemToTag( particleSystem_t *ps ) -{ -  if( ps == NULL || !ps->valid ) -  { -    CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); -    return; -  } - -  ps->attachType = PSA_TAG; -  ps->attached = qtrue; -} - -/* -=============== -CG_SetParticleSystemToTag - -Set a particle system attachment means -=============== -*/ -void CG_SetParticleSystemTag( particleSystem_t *ps, refEntity_t parent, -                              qhandle_t model, char *tagName ) -{ -  if( ps == NULL || !ps->valid ) -  { -    CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); -    return; -  } - -  ps->attachment.tagValid = qtrue; -  ps->attachment.parent = parent; -  ps->attachment.model = model; -  strncpy( ps->attachment.tagName, tagName, MAX_STRING_CHARS ); -} - -/* -=============== -CG_AttachParticleSystemToOrigin - -Attach a particle system to a point in space -=============== -*/ -void CG_AttachParticleSystemToOrigin( particleSystem_t *ps ) -{ -  if( ps == NULL || !ps->valid ) -  { -    CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); -    return; -  } - -  ps->attachType = PSA_STATIC; -  ps->attached = qtrue; -} - -/* -=============== -CG_SetParticleSystemOrigin - -Set a particle system attachment means -=============== -*/ -void CG_SetParticleSystemOrigin( particleSystem_t *ps, vec3_t origin ) -{ -  if( ps == NULL || !ps->valid ) -  { -    CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); -    return; -  } - -  ps->attachment.staticValid = qtrue; -  VectorCopy( origin, ps->attachment.origin ); -} - -/* -=============== -CG_AttachParticleSystemToParticle - -Attach a particle system to a particle -=============== -*/ -void CG_AttachParticleSystemToParticle( particleSystem_t *ps ) -{ -  if( ps == NULL || !ps->valid ) -  { -    CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); -    return; -  } - -  ps->attachType = PSA_PARTICLE; -  ps->attached = qtrue; -} - -/* -=============== -CG_SetParticleSystemParentParticle - -Set a particle system attachment means -=============== -*/ -void CG_SetParticleSystemParentParticle( particleSystem_t *ps, particle_t *p ) -{ -  if( ps == NULL || !ps->valid ) -  { -    CG_Printf( S_COLOR_YELLOW "WARNING: tried to modify a NULL particle system\n" ); -    return; -  } - -  ps->attachment.particleValid = qtrue; -  p->childSystem = ps; -} -  /*  ===============  CG_SetParticleSystemNormal - -Set a particle system attachment means  ===============  */  void CG_SetParticleSystemNormal( particleSystem_t *ps, vec3_t normal ) @@ -1689,9 +1418,9 @@ void CG_SetParticleSystemNormal( particleSystem_t *ps, vec3_t normal )      return;    } -  ps->attachment.normalValid = qtrue; -  VectorCopy( normal, ps->attachment.normal ); -  VectorNormalize( ps->attachment.normal ); +  ps->normalValid = qtrue; +  VectorCopy( normal, ps->normal ); +  VectorNormalize( ps->normal );  } @@ -1807,6 +1536,7 @@ static void CG_GarbageCollectParticleSystems( void )    int               i, j, count;    particleSystem_t  *ps;    particleEjector_t *pe; +  int               centNum;    for( i = 0; i < MAX_PARTICLE_SYSTEMS; i++ )    { @@ -1830,9 +1560,10 @@ static void CG_GarbageCollectParticleSystems( void )      //check systems where the parent cent has left the PVS      //( local player entity is always valid ) -    if( ps->attachment.centValid && ps->attachment.centNum != cg.snap->ps.clientNum ) +    if( ( centNum = CG_AttachmentCentNum( &ps->attachment ) ) >= 0 && +        centNum != cg.snap->ps.clientNum )      { -      if( !cg_entities[ ps->attachment.centNum ].valid ) +      if( !cg_entities[ centNum ].valid )          ps->lazyRemove = qtrue;      } @@ -1874,16 +1605,14 @@ static void CG_EvaluateParticlePhysics( particle_t *p )  {    particleSystem_t  *ps = p->parent->parent;    baseParticle_t    *bp = p->class; -  vec3_t            acceleration, forward, newOrigin; +  vec3_t            acceleration, newOrigin;    vec3_t            mins, maxs;    float             deltaTime, bounce, radius, dot;    trace_t           trace; -  centity_t         *cent;    switch( bp->accMoveType )    {      case PMT_STATIC: -        if( bp->accMoveValues.dirType == PMD_POINT )          VectorSubtract( bp->accMoveValues.point, p->origin, acceleration );        else if( bp->accMoveValues.dirType == PMD_LINEAR ) @@ -1892,40 +1621,28 @@ static void CG_EvaluateParticlePhysics( particle_t *p )        break;      case PMT_TAG: - -      if( !ps->attachment.tagValid ) -        return; - -      if( bp->accMoveValues.dirType == PMD_POINT ) -        VectorSubtract( ps->attachment.re.origin, p->origin, acceleration ); -      else if( bp->accMoveValues.dirType == PMD_LINEAR ) -        VectorCopy( ps->attachment.re.axis[ 0 ], acceleration ); - -      break; -      case PMT_CENT_ANGLES: +      if( bp->accMoveValues.dirType == PMD_POINT ) +      { +        vec3_t point; -      if( !ps->attachment.centValid ) -        return; - -      cent = &cg_entities[ ps->attachment.centNum ]; +        if( !CG_AttachmentPoint( &ps->attachment, point ) ) +          return; -      if( bp->accMoveValues.dirType == PMD_POINT ) -        VectorSubtract( cent->lerpOrigin, p->origin, acceleration ); +        VectorSubtract( point, p->origin, acceleration ); +      }        else if( bp->accMoveValues.dirType == PMD_LINEAR )        { -        AngleVectors( cent->lerpAngles, forward, NULL, NULL ); -        VectorCopy( forward, acceleration ); +        if( !CG_AttachmentDir( &ps->attachment, acceleration ) ) +          return;        } -        break;      case PMT_NORMAL: - -      if( !ps->attachment.normalValid ) +      if( !ps->normalValid )          return; -      VectorCopy( ps->attachment.normal, acceleration ); +      VectorCopy( ps->normal, acceleration );        break;    } @@ -1974,10 +1691,8 @@ static void CG_EvaluateParticlePhysics( particle_t *p )    VectorMA( p->origin, deltaTime, p->velocity, newOrigin );    p->lastEvalTime = cg.time; -  if( !ps->attachment.centValid ) -    CG_Trace( &trace, p->origin, mins, maxs, newOrigin, -1, CONTENTS_SOLID ); -  else -    CG_Trace( &trace, p->origin, mins, maxs, newOrigin, ps->attachment.centNum, CONTENTS_SOLID ); +  CG_Trace( &trace, p->origin, mins, maxs, newOrigin, +      CG_AttachmentCentNum( &ps->attachment ), CONTENTS_SOLID );    //not hit anything or not a collider    if( trace.fraction == 1.0f || bounce == 0.0f ) @@ -1988,9 +1703,14 @@ static void CG_EvaluateParticlePhysics( particle_t *p )    //remove particles that get into a CONTENTS_NODROP brush    if( ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) || -      ( bp->cullOnStartSolid && trace.startsolid ) || bp->bounceCull ) +      ( bp->cullOnStartSolid && trace.startsolid ) )    { -    CG_DestroyParticle( p ); +    CG_DestroyParticle( p, NULL ); +    return; +  } +  else if( bp->bounceCull ) +  { +    CG_DestroyParticle( p, trace.plane.normal );      return;    } @@ -2011,7 +1731,7 @@ static void CG_EvaluateParticlePhysics( particle_t *p )  CG_Radix  ===============  */ -static void CG_Radix( int bits, int size, particle_t *source, particle_t *dest ) +static void CG_Radix( int bits, int size, particle_t **source, particle_t **dest )  {    int count[ 256 ];    int index[ 256 ]; @@ -2020,7 +1740,7 @@ static void CG_Radix( int bits, int size, particle_t *source, particle_t *dest )    memset( count, 0, sizeof( count ) );    for( i = 0; i < size; i++ ) -    count[ GETKEY( source[ i ].sortKey, bits ) ]++; +    count[ GETKEY( source[ i ]->sortKey, bits ) ]++;    index[ 0 ] = 0; @@ -2028,7 +1748,7 @@ static void CG_Radix( int bits, int size, particle_t *source, particle_t *dest )      index[ i ] = index[ i - 1 ] + count[ i - 1 ];    for( i = 0; i < size; i++ ) -    dest[ index[ GETKEY( source[ i ].sortKey, bits ) ]++ ] = source[ i ]; +    dest[ index[ GETKEY( source[ i ]->sortKey, bits ) ]++ ] = source[ i ];  }  /* @@ -2038,7 +1758,7 @@ CG_RadixSort  Radix sort with 4 byte size buckets  ===============  */ -static void CG_RadixSort( particle_t *source, particle_t *temp, int size ) +static void CG_RadixSort( particle_t **source, particle_t **temp, int size )  {    CG_Radix( 0,   size, source, temp );    CG_Radix( 8,   size, temp, source ); @@ -2059,19 +1779,22 @@ static void CG_CompactAndSortParticles( void )    int     numParticles;    vec3_t  delta; +  for( i = 0; i < MAX_PARTICLES; i++ ) +    sortedParticles[ i ] = &particles[ i ]; +    for( i = MAX_PARTICLES - 1; i >= 0; i-- )    { -    if( particles[ i ].valid ) +    if( sortedParticles[ i ]->valid )      { -      while( particles[ j ].valid ) +      //find the first hole +      while( sortedParticles[ j ]->valid )          j++;        //no more holes        if( j >= i )          break; -      particles[ j ] = particles[ i ]; -      memset( &particles[ i ], 0, sizeof( particles[ 0 ] ) ); +      sortedParticles[ j ] = sortedParticles[ i ];      }    } @@ -2080,18 +1803,19 @@ static void CG_CompactAndSortParticles( void )    //set sort keys    for( i = 0; i < numParticles; i++ )    { -    VectorSubtract( particles[ i ].origin, cg.refdef.vieworg, delta ); -    particles[ i ].sortKey = (int)DotProduct( delta, delta ); +    VectorSubtract( sortedParticles[ i ]->origin, cg.refdef.vieworg, delta ); +    sortedParticles[ i ]->sortKey = (int)DotProduct( delta, delta );    } -  CG_RadixSort( particles, sortParticles, numParticles ); +  CG_RadixSort( sortedParticles, radixBuffer, numParticles ); +  //FIXME: wtf?    //reverse order of particles array    for( i = 0; i < numParticles; i++ ) -    sortParticles[ i ] = particles[ numParticles - i - 1 ]; +    radixBuffer[ i ] = sortedParticles[ numParticles - i - 1 ];    for( i = 0; i < numParticles; i++ ) -    particles[ i ] = sortParticles[ i ]; +    sortedParticles[ i ] = radixBuffer[ i ];  }  /* @@ -2198,7 +1922,7 @@ void CG_AddParticles( void )    for( i = 0; i < MAX_PARTICLES; i++ )    { -    p = &particles[ i ]; +    p = sortedParticles[ i ];      if( p->valid )      { @@ -2209,7 +1933,7 @@ void CG_AddParticles( void )          CG_RenderParticle( p );        }        else -        CG_DestroyParticle( p ); +        CG_DestroyParticle( p, NULL );      }    } @@ -2258,11 +1982,63 @@ void CG_ParticleSystemEntity( centity_t *cent )      if( CG_IsParticleSystemValid( ¢->entityPS ) )      { -      CG_SetParticleSystemOrigin( cent->entityPS, cent->lerpOrigin ); -      CG_SetParticleSystemCent( cent->entityPS, cent ); -      CG_AttachParticleSystemToOrigin( cent->entityPS ); +      CG_SetAttachmentPoint( ¢->entityPS->attachment, cent->lerpOrigin ); +      CG_SetAttachmentCent( ¢->entityPS->attachment, cent ); +      CG_AttachToPoint( ¢->entityPS->attachment );      }      else        cent->entityPSMissing = qtrue;    }  } + +static particleSystem_t *testPS; +static qhandle_t        testPSHandle; + +/* +=============== +CG_DestroyTestPS_f + +Destroy the test a particle system +=============== +*/ +void CG_DestroyTestPS_f( void ) +{ +  if( CG_IsParticleSystemValid( &testPS ) ) +    CG_DestroyParticleSystem( &testPS ); +} + +/* +=============== +CG_TestPS_f + +Test a particle system +=============== +*/ +void CG_TestPS_f( void ) +{ +  vec3_t  origin; +  vec3_t  up = { 0.0f, 0.0f, 1.0f }; +  char    psName[ MAX_QPATH ]; + +  if( trap_Argc( ) < 2 ) +    return; + +  Q_strncpyz( psName, CG_Argv( 1 ), MAX_QPATH ); +  testPSHandle = CG_RegisterParticleSystem( psName ); + +  if( testPSHandle ) +  { +    CG_DestroyTestPS_f( ); + +    testPS = CG_SpawnNewParticleSystem( testPSHandle ); + +    VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[ 0 ], origin ); + +    if( CG_IsParticleSystemValid( &testPS ) ) +    { +      CG_SetAttachmentPoint( &testPS->attachment, origin ); +      CG_SetParticleSystemNormal( testPS, up ); +      CG_AttachToPoint( &testPS->attachment ); +    } +  } +} diff --git a/src/cgame/cg_players.c b/src/cgame/cg_players.c index 18a32efc..1ef18f8b 100644 --- a/src/cgame/cg_players.c +++ b/src/cgame/cg_players.c @@ -1631,9 +1631,10 @@ static void CG_PlayerUpgrades( centity_t *cent, refEntity_t *torso )        if( CG_IsParticleSystemValid( ¢->jetPackPS ) )        { -        CG_SetParticleSystemTag( cent->jetPackPS, jetpack, jetpack.hModel, "tag_flash" ); -        CG_SetParticleSystemCent( cent->jetPackPS, cent ); -        CG_AttachParticleSystemToTag( cent->jetPackPS ); +        CG_SetAttachmentTag( ¢->jetPackPS->attachment, +            jetpack, jetpack.hModel, "tag_flash" ); +        CG_SetAttachmentCent( ¢->jetPackPS->attachment, cent ); +        CG_AttachToTag( ¢->jetPackPS->attachment );        }      }      else if( CG_IsParticleSystemValid( ¢->jetPackPS ) ) diff --git a/src/cgame/cg_servercmds.c b/src/cgame/cg_servercmds.c index e2391127..a92fa37e 100644 --- a/src/cgame/cg_servercmds.c +++ b/src/cgame/cg_servercmds.c @@ -484,10 +484,6 @@ static void CG_MapRestart( void )    CG_InitLocalEntities( );    CG_InitMarkPolys( ); -  // Ridah, trails -  CG_ClearTrails( ); -  // done. -    // make sure the "3 frags left" warnings play again    cg.fraglimitWarnings = 0; @@ -1048,9 +1044,13 @@ static void CG_ServerCommand( void )    if( !strcmp( cmd, "poisoncloud" ) )    {      cg.poisonedTime = cg.time; -    cg.poisonCloudPS = CG_SpawnNewParticleSystem( cgs.media.poisonCloudPS ); -    CG_SetParticleSystemCent( cg.poisonCloudPS, &cg.predictedPlayerEntity ); -    CG_AttachParticleSystemToCent( cg.poisonCloudPS ); + +    if( CG_IsParticleSystemValid( &cg.poisonCloudPS ) ) +    { +      cg.poisonCloudPS = CG_SpawnNewParticleSystem( cgs.media.poisonCloudPS ); +      CG_SetAttachmentCent( &cg.poisonCloudPS->attachment, &cg.predictedPlayerEntity ); +      CG_AttachToCent( &cg.poisonCloudPS->attachment ); +    }      return;    } 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 ); +    }    }  } diff --git a/src/cgame/cg_view.c b/src/cgame/cg_view.c index 0e98036d..59016dc4 100644 --- a/src/cgame/cg_view.c +++ b/src/cgame/cg_view.c @@ -1311,7 +1311,7 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo    // build the render lists    if( !cg.hyperspace )    { -    CG_AddPacketEntities( );     // adter calcViewValues, so predicted player state is correct +    CG_AddPacketEntities( );     // after calcViewValues, so predicted player state is correct      CG_AddMarks( );      CG_AddLocalEntities( );    } @@ -1322,9 +1322,7 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo    if( !cg.hyperspace )    {      CG_AddParticles( ); - -    //TA: wolf trails stuff -    CG_AddTrails( );    // this must come last, so the trails dropped this frame get drawn +    CG_AddTrails( );    }    // add buffered sounds diff --git a/src/cgame/cg_weapons.c b/src/cgame/cg_weapons.c index dcef9f82..935342ac 100644 --- a/src/cgame/cg_weapons.c +++ b/src/cgame/cg_weapons.c @@ -157,35 +157,6 @@ static void CG_ShotgunEjectBrass( centity_t *cent )    le->leMarkType = LEMT_NONE;  } -/* -========================== -CG_TeslaTrail -========================== -*/ -void CG_TeslaTrail( vec3_t start, vec3_t end, int srcENum, int destENum ) -{ -  localEntity_t *le; -  refEntity_t   *re; - -  //add a bunch of bolt segments -  le = CG_AllocLocalEntity( ); -  re = &le->refEntity; - -  le->leType = LE_LIGHTNING_BOLT; -  le->startTime = cg.time; -  le->endTime = cg.time + cg_teslaTrailTime.value; -  le->lifeRate = 1.0 / ( le->endTime - le->startTime ); -  re->customShader = cgs.media.lightningShader; - -  le->srcENum = srcENum; -  le->destENum = destENum; -  le->vOffset = 28; -  le->maxRange = BG_FindRangeForBuildable( BA_H_TESLAGEN ) * M_ROOT3; - -  VectorCopy( start, re->origin ); -  VectorCopy( end, re->oldorigin ); -} -  /*  ================= @@ -351,6 +322,19 @@ static qboolean CG_ParseWeaponModeSection( weaponInfoMode_t *wim, char **text_p        continue;      } +    else if( !Q_stricmp( token, "missileTrailSystem" ) ) +    { +      token = COM_Parse( text_p ); +      if( !token ) +        break; + +      wim->missileTrailSystem = CG_RegisterTrailSystem( token ); + +      if( !wim->missileTrailSystem ) +        CG_Printf( S_COLOR_RED "ERROR: missile trail system not found %s\n", token ); + +      continue; +    }      else if( !Q_stricmp( token, "muzzleParticleSystem" ) )      {        token = COM_Parse( text_p ); @@ -856,8 +840,7 @@ void CG_InitWeapons( void )    for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )      CG_RegisterWeapon( i ); -  cgs.media.lightningShader         = trap_R_RegisterShader( "models/ammo/tesla/tesla_bolt"); -  cgs.media.lightningExplosionModel = trap_R_RegisterModel( "models/weaphits/crackle.md3" ); +  cgs.media.level2ZapTS = CG_RegisterTrailSystem( "models/weapons/lev2zap/lightning" );  } @@ -1102,9 +1085,9 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent          cent->currentState.number != cg.predictedPlayerState.clientNum )      {        if( noGunModel ) -        CG_SetParticleSystemTag( cent->muzzlePS, *parent, parent->hModel, "tag_weapon" ); +        CG_SetAttachmentTag( ¢->muzzlePS->attachment, *parent, parent->hModel, "tag_weapon" );        else -        CG_SetParticleSystemTag( cent->muzzlePS, gun, weapon->weaponModel, "tag_flash" ); +        CG_SetAttachmentTag( ¢->muzzlePS->attachment, gun, weapon->weaponModel, "tag_flash" );      }      //if the PS is infinite disable it when not firing @@ -1148,13 +1131,17 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent      {        cent->muzzlePS = CG_SpawnNewParticleSystem( weapon->wim[ weaponMode ].muzzleParticleSystem ); -      if( noGunModel ) -        CG_SetParticleSystemTag( cent->muzzlePS, *parent, parent->hModel, "tag_weapon" ); -      else -        CG_SetParticleSystemTag( cent->muzzlePS, gun, weapon->weaponModel, "tag_flash" ); +      if( CG_IsParticleSystemValid( ¢->muzzlePS ) ) +      { +        if( noGunModel ) +          CG_SetAttachmentTag( ¢->muzzlePS->attachment, *parent, parent->hModel, "tag_weapon" ); +        else +          CG_SetAttachmentTag( ¢->muzzlePS->attachment, gun, weapon->weaponModel, "tag_flash" ); + +        CG_SetAttachmentCent( ¢->muzzlePS->attachment, cent ); +        CG_AttachToTag( ¢->muzzlePS->attachment ); +      } -      CG_SetParticleSystemCent( cent->muzzlePS, cent ); -      CG_AttachParticleSystemToTag( cent->muzzlePS );        cent->muzzlePsTrigger = qfalse;      } @@ -1228,15 +1215,19 @@ void CG_AddViewWeapon( playerState_t *ps )      VectorMA( origin, -8, cg.refdef.viewaxis[ 2 ], origin );      if( cent->muzzlePS ) -      CG_SetParticleSystemOrigin( cent->muzzlePS, origin ); +      CG_SetAttachmentPoint( ¢->muzzlePS->attachment, origin );      //check for particle systems      if( wi->wim[ weaponMode ].muzzleParticleSystem && cent->muzzlePsTrigger )      {        cent->muzzlePS = CG_SpawnNewParticleSystem( wi->wim[ weaponMode ].muzzleParticleSystem ); -      CG_SetParticleSystemOrigin( cent->muzzlePS, origin ); -      CG_SetParticleSystemCent( cent->muzzlePS, cent ); -      CG_AttachParticleSystemToOrigin( cent->muzzlePS ); + +      if( CG_IsParticleSystemValid( ¢->muzzlePS ) ) +      { +        CG_SetAttachmentPoint( ¢->muzzlePS->attachment, origin ); +        CG_SetAttachmentCent( ¢->muzzlePS->attachment, cent ); +        CG_AttachToPoint( ¢->muzzlePS->attachment ); +      }        cent->muzzlePsTrigger = qfalse;      } @@ -1765,9 +1756,13 @@ void CG_MissileHitWall( weapon_t weaponNum, weaponMode_t weaponMode, int clientN    if( ps )    {      particleSystem_t *partSystem = CG_SpawnNewParticleSystem( ps ); -    CG_SetParticleSystemOrigin( partSystem, origin ); -    CG_SetParticleSystemNormal( partSystem, dir ); -    CG_AttachParticleSystemToOrigin( partSystem ); + +    if( CG_IsParticleSystemValid( &partSystem ) ) +    { +      CG_SetAttachmentPoint( &partSystem->attachment, origin ); +      CG_SetParticleSystemNormal( partSystem, dir ); +      CG_AttachToPoint( &partSystem->attachment ); +    }    }    //  | 
