Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

cg_ents.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 //
00023 // cg_ents.c -- present snapshot entities, happens every single frame
00024 
00025 #include "cg_local.h"
00026 
00027 
00028 /*
00029 ======================
00030 CG_PositionEntityOnTag
00031 
00032 Modifies the entities position and axis by the given
00033 tag location
00034 ======================
00035 */
00036 void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, 
00037                             qhandle_t parentModel, char *tagName ) {
00038     int             i;
00039     orientation_t   lerped;
00040     
00041     // lerp the tag
00042     trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
00043         1.0 - parent->backlerp, tagName );
00044 
00045     // FIXME: allow origin offsets along tag?
00046     VectorCopy( parent->origin, entity->origin );
00047     for ( i = 0 ; i < 3 ; i++ ) {
00048         VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
00049     }
00050 
00051     // had to cast away the const to avoid compiler problems...
00052     MatrixMultiply( lerped.axis, ((refEntity_t *)parent)->axis, entity->axis );
00053     entity->backlerp = parent->backlerp;
00054 }
00055 
00056 
00057 /*
00058 ======================
00059 CG_PositionRotatedEntityOnTag
00060 
00061 Modifies the entities position and axis by the given
00062 tag location
00063 ======================
00064 */
00065 void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, 
00066                             qhandle_t parentModel, char *tagName ) {
00067     int             i;
00068     orientation_t   lerped;
00069     vec3_t          tempAxis[3];
00070 
00071 //AxisClear( entity->axis );
00072     // lerp the tag
00073     trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
00074         1.0 - parent->backlerp, tagName );
00075 
00076     // FIXME: allow origin offsets along tag?
00077     VectorCopy( parent->origin, entity->origin );
00078     for ( i = 0 ; i < 3 ; i++ ) {
00079         VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
00080     }
00081 
00082     // had to cast away the const to avoid compiler problems...
00083     MatrixMultiply( entity->axis, lerped.axis, tempAxis );
00084     MatrixMultiply( tempAxis, ((refEntity_t *)parent)->axis, entity->axis );
00085 }
00086 
00087 
00088 
00089 /*
00090 ==========================================================================
00091 
00092 FUNCTIONS CALLED EACH FRAME
00093 
00094 ==========================================================================
00095 */
00096 
00097 /*
00098 ======================
00099 CG_SetEntitySoundPosition
00100 
00101 Also called by event processing code
00102 ======================
00103 */
00104 void CG_SetEntitySoundPosition( centity_t *cent ) {
00105     if ( cent->currentState.solid == SOLID_BMODEL ) {
00106         vec3_t  origin;
00107         float   *v;
00108 
00109         v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
00110         VectorAdd( cent->lerpOrigin, v, origin );
00111         trap_S_UpdateEntityPosition( cent->currentState.number, origin );
00112     } else {
00113         trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin );
00114     }
00115 }
00116 
00117 /*
00118 ==================
00119 CG_EntityEffects
00120 
00121 Add continuous entity effects, like local entity emission and lighting
00122 ==================
00123 */
00124 static void CG_EntityEffects( centity_t *cent ) {
00125 
00126     // update sound origins
00127     CG_SetEntitySoundPosition( cent );
00128 
00129     // add loop sound
00130     if ( cent->currentState.loopSound ) {
00131         if (cent->currentState.eType != ET_SPEAKER) {
00132             trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, 
00133                 cgs.gameSounds[ cent->currentState.loopSound ] );
00134         } else {
00135             trap_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, 
00136                 cgs.gameSounds[ cent->currentState.loopSound ] );
00137         }
00138     }
00139 
00140 
00141     // constant light glow
00142     if ( cent->currentState.constantLight ) {
00143         int     cl;
00144         int     i, r, g, b;
00145 
00146         cl = cent->currentState.constantLight;
00147         r = cl & 255;
00148         g = ( cl >> 8 ) & 255;
00149         b = ( cl >> 16 ) & 255;
00150         i = ( ( cl >> 24 ) & 255 ) * 4;
00151         trap_R_AddLightToScene( cent->lerpOrigin, i, r, g, b );
00152     }
00153 
00154 }
00155 
00156 
00157 /*
00158 ==================
00159 CG_General
00160 ==================
00161 */
00162 static void CG_General( centity_t *cent ) {
00163     refEntity_t         ent;
00164     entityState_t       *s1;
00165 
00166     s1 = &cent->currentState;
00167 
00168     // if set to invisible, skip
00169     if (!s1->modelindex) {
00170         return;
00171     }
00172 
00173     memset (&ent, 0, sizeof(ent));
00174 
00175     // set frame
00176 
00177     ent.frame = s1->frame;
00178     ent.oldframe = ent.frame;
00179     ent.backlerp = 0;
00180 
00181     VectorCopy( cent->lerpOrigin, ent.origin);
00182     VectorCopy( cent->lerpOrigin, ent.oldorigin);
00183 
00184     ent.hModel = cgs.gameModels[s1->modelindex];
00185 
00186     // player model
00187     if (s1->number == cg.snap->ps.clientNum) {
00188         ent.renderfx |= RF_THIRD_PERSON;    // only draw from mirrors
00189     }
00190 
00191     // convert angles to axis
00192     AnglesToAxis( cent->lerpAngles, ent.axis );
00193 
00194     // add to refresh list
00195     trap_R_AddRefEntityToScene (&ent);
00196 }
00197 
00198 /*
00199 ==================
00200 CG_Speaker
00201 
00202 Speaker entities can automatically play sounds
00203 ==================
00204 */
00205 static void CG_Speaker( centity_t *cent ) {
00206     if ( ! cent->currentState.clientNum ) { // FIXME: use something other than clientNum...
00207         return;     // not auto triggering
00208     }
00209 
00210     if ( cg.time < cent->miscTime ) {
00211         return;
00212     }
00213 
00214     trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] );
00215 
00216     //  ent->s.frame = ent->wait * 10;
00217     //  ent->s.clientNum = ent->random * 10;
00218     cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom();
00219 }
00220 
00221 /*
00222 ==================
00223 CG_Item
00224 ==================
00225 */
00226 static void CG_Item( centity_t *cent ) {
00227     refEntity_t     ent;
00228     entityState_t   *es;
00229     gitem_t         *item;
00230     int             msec;
00231     float           frac;
00232     float           scale;
00233     weaponInfo_t    *wi;
00234 
00235     es = &cent->currentState;
00236     if ( es->modelindex >= bg_numItems ) {
00237         CG_Error( "Bad item index %i on entity", es->modelindex );
00238     }
00239 
00240     // if set to invisible, skip
00241     if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) {
00242         return;
00243     }
00244 
00245     item = &bg_itemlist[ es->modelindex ];
00246     if ( cg_simpleItems.integer && item->giType != IT_TEAM ) {
00247         memset( &ent, 0, sizeof( ent ) );
00248         ent.reType = RT_SPRITE;
00249         VectorCopy( cent->lerpOrigin, ent.origin );
00250         ent.radius = 14;
00251         ent.customShader = cg_items[es->modelindex].icon;
00252         ent.shaderRGBA[0] = 255;
00253         ent.shaderRGBA[1] = 255;
00254         ent.shaderRGBA[2] = 255;
00255         ent.shaderRGBA[3] = 255;
00256         trap_R_AddRefEntityToScene(&ent);
00257         return;
00258     }
00259 
00260     // items bob up and down continuously
00261     scale = 0.005 + cent->currentState.number * 0.00001;
00262     cent->lerpOrigin[2] += 4 + cos( ( cg.time + 1000 ) *  scale ) * 4;
00263 
00264     memset (&ent, 0, sizeof(ent));
00265 
00266     // autorotate at one of two speeds
00267     if ( item->giType == IT_HEALTH ) {
00268         VectorCopy( cg.autoAnglesFast, cent->lerpAngles );
00269         AxisCopy( cg.autoAxisFast, ent.axis );
00270     } else {
00271         VectorCopy( cg.autoAngles, cent->lerpAngles );
00272         AxisCopy( cg.autoAxis, ent.axis );
00273     }
00274 
00275     wi = NULL;
00276     // the weapons have their origin where they attatch to player
00277     // models, so we need to offset them or they will rotate
00278     // eccentricly
00279     if ( item->giType == IT_WEAPON ) {
00280         wi = &cg_weapons[item->giTag];
00281         cent->lerpOrigin[0] -= 
00282             wi->weaponMidpoint[0] * ent.axis[0][0] +
00283             wi->weaponMidpoint[1] * ent.axis[1][0] +
00284             wi->weaponMidpoint[2] * ent.axis[2][0];
00285         cent->lerpOrigin[1] -= 
00286             wi->weaponMidpoint[0] * ent.axis[0][1] +
00287             wi->weaponMidpoint[1] * ent.axis[1][1] +
00288             wi->weaponMidpoint[2] * ent.axis[2][1];
00289         cent->lerpOrigin[2] -= 
00290             wi->weaponMidpoint[0] * ent.axis[0][2] +
00291             wi->weaponMidpoint[1] * ent.axis[1][2] +
00292             wi->weaponMidpoint[2] * ent.axis[2][2];
00293 
00294         cent->lerpOrigin[2] += 8;   // an extra height boost
00295     }
00296 
00297     ent.hModel = cg_items[es->modelindex].models[0];
00298 
00299     VectorCopy( cent->lerpOrigin, ent.origin);
00300     VectorCopy( cent->lerpOrigin, ent.oldorigin);
00301 
00302     ent.nonNormalizedAxes = qfalse;
00303 
00304     // if just respawned, slowly scale up
00305     msec = cg.time - cent->miscTime;
00306     if ( msec >= 0 && msec < ITEM_SCALEUP_TIME ) {
00307         frac = (float)msec / ITEM_SCALEUP_TIME;
00308         VectorScale( ent.axis[0], frac, ent.axis[0] );
00309         VectorScale( ent.axis[1], frac, ent.axis[1] );
00310         VectorScale( ent.axis[2], frac, ent.axis[2] );
00311         ent.nonNormalizedAxes = qtrue;
00312     } else {
00313         frac = 1.0;
00314     }
00315 
00316     // items without glow textures need to keep a minimum light value
00317     // so they are always visible
00318     if ( ( item->giType == IT_WEAPON ) ||
00319          ( item->giType == IT_ARMOR ) ) {
00320         ent.renderfx |= RF_MINLIGHT;
00321     }
00322 
00323     // increase the size of the weapons when they are presented as items
00324     if ( item->giType == IT_WEAPON ) {
00325         VectorScale( ent.axis[0], 1.5, ent.axis[0] );
00326         VectorScale( ent.axis[1], 1.5, ent.axis[1] );
00327         VectorScale( ent.axis[2], 1.5, ent.axis[2] );
00328         ent.nonNormalizedAxes = qtrue;
00329 #ifdef MISSIONPACK
00330         trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.weaponHoverSound );
00331 #endif
00332     }
00333 
00334 #ifdef MISSIONPACK
00335     if ( item->giType == IT_HOLDABLE && item->giTag == HI_KAMIKAZE ) {
00336         VectorScale( ent.axis[0], 2, ent.axis[0] );
00337         VectorScale( ent.axis[1], 2, ent.axis[1] );
00338         VectorScale( ent.axis[2], 2, ent.axis[2] );
00339         ent.nonNormalizedAxes = qtrue;
00340     }
00341 #endif
00342 
00343     // add to refresh list
00344     trap_R_AddRefEntityToScene(&ent);
00345 
00346 #ifdef MISSIONPACK
00347     if ( item->giType == IT_WEAPON && wi->barrelModel ) {
00348         refEntity_t barrel;
00349 
00350         memset( &barrel, 0, sizeof( barrel ) );
00351 
00352         barrel.hModel = wi->barrelModel;
00353 
00354         VectorCopy( ent.lightingOrigin, barrel.lightingOrigin );
00355         barrel.shadowPlane = ent.shadowPlane;
00356         barrel.renderfx = ent.renderfx;
00357 
00358         CG_PositionRotatedEntityOnTag( &barrel, &ent, wi->weaponModel, "tag_barrel" );
00359 
00360         AxisCopy( ent.axis, barrel.axis );
00361         barrel.nonNormalizedAxes = ent.nonNormalizedAxes;
00362 
00363         trap_R_AddRefEntityToScene( &barrel );
00364     }
00365 #endif
00366 
00367     // accompanying rings / spheres for powerups
00368     if ( !cg_simpleItems.integer ) 
00369     {
00370         vec3_t spinAngles;
00371 
00372         VectorClear( spinAngles );
00373 
00374         if ( item->giType == IT_HEALTH || item->giType == IT_POWERUP )
00375         {
00376             if ( ( ent.hModel = cg_items[es->modelindex].models[1] ) != 0 )
00377             {
00378                 if ( item->giType == IT_POWERUP )
00379                 {
00380                     ent.origin[2] += 12;
00381                     spinAngles[1] = ( cg.time & 1023 ) * 360 / -1024.0f;
00382                 }
00383                 AnglesToAxis( spinAngles, ent.axis );
00384                 
00385                 // scale up if respawning
00386                 if ( frac != 1.0 ) {
00387                     VectorScale( ent.axis[0], frac, ent.axis[0] );
00388                     VectorScale( ent.axis[1], frac, ent.axis[1] );
00389                     VectorScale( ent.axis[2], frac, ent.axis[2] );
00390                     ent.nonNormalizedAxes = qtrue;
00391                 }
00392                 trap_R_AddRefEntityToScene( &ent );
00393             }
00394         }
00395     }
00396 }
00397 
00398 //============================================================================
00399 
00400 /*
00401 ===============
00402 CG_Missile
00403 ===============
00404 */
00405 static void CG_Missile( centity_t *cent ) {
00406     refEntity_t         ent;
00407     entityState_t       *s1;
00408     const weaponInfo_t      *weapon;
00409 //  int col;
00410 
00411     s1 = &cent->currentState;
00412     if ( s1->weapon > WP_NUM_WEAPONS ) {
00413         s1->weapon = 0;
00414     }
00415     weapon = &cg_weapons[s1->weapon];
00416 
00417     // calculate the axis
00418     VectorCopy( s1->angles, cent->lerpAngles);
00419 
00420     // add trails
00421     if ( weapon->missileTrailFunc ) 
00422     {
00423         weapon->missileTrailFunc( cent, weapon );
00424     }
00425 /*
00426     if ( cent->currentState.modelindex == TEAM_RED ) {
00427         col = 1;
00428     }
00429     else if ( cent->currentState.modelindex == TEAM_BLUE ) {
00430         col = 2;
00431     }
00432     else {
00433         col = 0;
00434     }
00435 
00436     // add dynamic light
00437     if ( weapon->missileDlight ) {
00438         trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, 
00439             weapon->missileDlightColor[col][0], weapon->missileDlightColor[col][1], weapon->missileDlightColor[col][2] );
00440     }
00441 */
00442     // add dynamic light
00443     if ( weapon->missileDlight ) {
00444         trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, 
00445             weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] );
00446     }
00447 
00448     // add missile sound
00449     if ( weapon->missileSound ) {
00450         vec3_t  velocity;
00451 
00452         BG_EvaluateTrajectoryDelta( &cent->currentState.pos, cg.time, velocity );
00453 
00454         trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound );
00455     }
00456 
00457     // create the render entity
00458     memset (&ent, 0, sizeof(ent));
00459     VectorCopy( cent->lerpOrigin, ent.origin);
00460     VectorCopy( cent->lerpOrigin, ent.oldorigin);
00461 
00462     if ( cent->currentState.weapon == WP_PLASMAGUN ) {
00463         ent.reType = RT_SPRITE;
00464         ent.radius = 16;
00465         ent.rotation = 0;
00466         ent.customShader = cgs.media.plasmaBallShader;
00467         trap_R_AddRefEntityToScene( &ent );
00468         return;
00469     }
00470 
00471     // flicker between two skins
00472     ent.skinNum = cg.clientFrame & 1;
00473     ent.hModel = weapon->missileModel;
00474     ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
00475 
00476 #ifdef MISSIONPACK
00477     if ( cent->currentState.weapon == WP_PROX_LAUNCHER ) {
00478         if (s1->generic1 == TEAM_BLUE) {
00479             ent.hModel = cgs.media.blueProxMine;
00480         }
00481     }
00482 #endif
00483 
00484     // convert direction of travel into axis
00485     if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
00486         ent.axis[0][2] = 1;
00487     }
00488 
00489     // spin as it moves
00490     if ( s1->pos.trType != TR_STATIONARY ) {
00491         RotateAroundDirection( ent.axis, cg.time / 4 );
00492     } else {
00493 #ifdef MISSIONPACK
00494         if ( s1->weapon == WP_PROX_LAUNCHER ) {
00495             AnglesToAxis( cent->lerpAngles, ent.axis );
00496         }
00497         else
00498 #endif
00499         {
00500             RotateAroundDirection( ent.axis, s1->time );
00501         }
00502     }
00503 
00504     // add to refresh list, possibly with quad glow
00505     CG_AddRefEntityWithPowerups( &ent, s1, TEAM_FREE );
00506 }
00507 
00508 /*
00509 ===============
00510 CG_Grapple
00511 
00512 This is called when the grapple is sitting up against the wall
00513 ===============
00514 */
00515 static void CG_Grapple( centity_t *cent ) {
00516     refEntity_t         ent;
00517     entityState_t       *s1;
00518     const weaponInfo_t      *weapon;
00519 
00520     s1 = &cent->currentState;
00521     if ( s1->weapon > WP_NUM_WEAPONS ) {
00522         s1->weapon = 0;
00523     }
00524     weapon = &cg_weapons[s1->weapon];
00525 
00526     // calculate the axis
00527     VectorCopy( s1->angles, cent->lerpAngles);
00528 
00529 #if 0 // FIXME add grapple pull sound here..?
00530     // add missile sound
00531     if ( weapon->missileSound ) {
00532         trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->missileSound );
00533     }
00534 #endif
00535 
00536     // Will draw cable if needed
00537     CG_GrappleTrail ( cent, weapon );
00538 
00539     // create the render entity
00540     memset (&ent, 0, sizeof(ent));
00541     VectorCopy( cent->lerpOrigin, ent.origin);
00542     VectorCopy( cent->lerpOrigin, ent.oldorigin);
00543 
00544     // flicker between two skins
00545     ent.skinNum = cg.clientFrame & 1;
00546     ent.hModel = weapon->missileModel;
00547     ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
00548 
00549     // convert direction of travel into axis
00550     if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
00551         ent.axis[0][2] = 1;
00552     }
00553 
00554     trap_R_AddRefEntityToScene( &ent );
00555 }
00556 
00557 /*
00558 ===============
00559 CG_Mover
00560 ===============
00561 */
00562 static void CG_Mover( centity_t *cent ) {
00563     refEntity_t         ent;
00564     entityState_t       *s1;
00565 
00566     s1 = &cent->currentState;
00567 
00568     // create the render entity
00569     memset (&ent, 0, sizeof(ent));
00570     VectorCopy( cent->lerpOrigin, ent.origin);
00571     VectorCopy( cent->lerpOrigin, ent.oldorigin);
00572     AnglesToAxis( cent->lerpAngles, ent.axis );
00573 
00574     ent.renderfx = RF_NOSHADOW;
00575 
00576     // flicker between two skins (FIXME?)
00577     ent.skinNum = ( cg.time >> 6 ) & 1;
00578 
00579     // get the model, either as a bmodel or a modelindex
00580     if ( s1->solid == SOLID_BMODEL ) {
00581         ent.hModel = cgs.inlineDrawModel[s1->modelindex];
00582     } else {
00583         ent.hModel = cgs.gameModels[s1->modelindex];
00584     }
00585 
00586     // add to refresh list
00587     trap_R_AddRefEntityToScene(&ent);
00588 
00589     // add the secondary model
00590     if ( s1->modelindex2 ) {
00591         ent.skinNum = 0;
00592         ent.hModel = cgs.gameModels[s1->modelindex2];
00593         trap_R_AddRefEntityToScene(&ent);
00594     }
00595 
00596 }
00597 
00598 /*
00599 ===============
00600 CG_Beam
00601 
00602 Also called as an event
00603 ===============
00604 */
00605 void CG_Beam( centity_t *cent ) {
00606     refEntity_t         ent;
00607     entityState_t       *s1;
00608 
00609     s1 = &cent->currentState;
00610 
00611     // create the render entity
00612     memset (&ent, 0, sizeof(ent));
00613     VectorCopy( s1->pos.trBase, ent.origin );
00614     VectorCopy( s1->origin2, ent.oldorigin );
00615     AxisClear( ent.axis );
00616     ent.reType = RT_BEAM;
00617 
00618     ent.renderfx = RF_NOSHADOW;
00619 
00620     // add to refresh list
00621     trap_R_AddRefEntityToScene(&ent);
00622 }
00623 
00624 
00625 /*
00626 ===============
00627 CG_Portal
00628 ===============
00629 */
00630 static void CG_Portal( centity_t *cent ) {
00631     refEntity_t         ent;
00632     entityState_t       *s1;
00633 
00634     s1 = &cent->currentState;
00635 
00636     // create the render entity
00637     memset (&ent, 0, sizeof(ent));
00638     VectorCopy( cent->lerpOrigin, ent.origin );
00639     VectorCopy( s1->origin2, ent.oldorigin );
00640     ByteToDir( s1->eventParm, ent.axis[0] );
00641     PerpendicularVector( ent.axis[1], ent.axis[0] );
00642 
00643     // negating this tends to get the directions like they want
00644     // we really should have a camera roll value
00645     VectorSubtract( vec3_origin, ent.axis[1], ent.axis[1] );
00646 
00647     CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] );
00648     ent.reType = RT_PORTALSURFACE;
00649     ent.oldframe = s1->powerups;
00650     ent.frame = s1->frame;      // rotation speed
00651     ent.skinNum = s1->clientNum/256.0 * 360;    // roll offset
00652 
00653     // add to refresh list
00654     trap_R_AddRefEntityToScene(&ent);
00655 }
00656 
00657 
00658 /*
00659 =========================
00660 CG_AdjustPositionForMover
00661 
00662 Also called by client movement prediction code
00663 =========================
00664 */
00665 void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ) {
00666     centity_t   *cent;
00667     vec3_t  oldOrigin, origin, deltaOrigin;
00668     vec3_t  oldAngles, angles, deltaAngles;
00669 
00670     if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) {
00671         VectorCopy( in, out );
00672         return;
00673     }
00674 
00675     cent = &cg_entities[ moverNum ];
00676     if ( cent->currentState.eType != ET_MOVER ) {
00677         VectorCopy( in, out );
00678         return;
00679     }
00680 
00681     BG_EvaluateTrajectory( &cent->currentState.pos, fromTime, oldOrigin );
00682     BG_EvaluateTrajectory( &cent->currentState.apos, fromTime, oldAngles );
00683 
00684     BG_EvaluateTrajectory( &cent->currentState.pos, toTime, origin );
00685     BG_EvaluateTrajectory( &cent->currentState.apos, toTime, angles );
00686 
00687     VectorSubtract( origin, oldOrigin, deltaOrigin );
00688     VectorSubtract( angles, oldAngles, deltaAngles );
00689 
00690     VectorAdd( in, deltaOrigin, out );
00691 
00692     // FIXME: origin change when on a rotating object
00693 }
00694 
00695 
00696 /*
00697 =============================
00698 CG_InterpolateEntityPosition
00699 =============================
00700 */
00701 static void CG_InterpolateEntityPosition( centity_t *cent ) {
00702     vec3_t      current, next;
00703     float       f;
00704 
00705     // it would be an internal error to find an entity that interpolates without
00706     // a snapshot ahead of the current one
00707     if ( cg.nextSnap == NULL ) {
00708         CG_Error( "CG_InterpoateEntityPosition: cg.nextSnap == NULL" );
00709     }
00710 
00711     f = cg.frameInterpolation;
00712 
00713     // this will linearize a sine or parabolic curve, but it is important
00714     // to not extrapolate player positions if more recent data is available
00715     BG_EvaluateTrajectory( &cent->currentState.pos, cg.snap->serverTime, current );
00716     BG_EvaluateTrajectory( &cent->nextState.pos, cg.nextSnap->serverTime, next );
00717 
00718     cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] );
00719     cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] );
00720     cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] );
00721 
00722     BG_EvaluateTrajectory( &cent->currentState.apos, cg.snap->serverTime, current );
00723     BG_EvaluateTrajectory( &cent->nextState.apos, cg.nextSnap->serverTime, next );
00724 
00725     cent->lerpAngles[0] = LerpAngle( current[0], next[0], f );
00726     cent->lerpAngles[1] = LerpAngle( current[1], next[1], f );
00727     cent->lerpAngles[2] = LerpAngle( current[2], next[2], f );
00728 
00729 }
00730 
00731 /*
00732 ===============
00733 CG_CalcEntityLerpPositions
00734 
00735 ===============
00736 */
00737 static void CG_CalcEntityLerpPositions( centity_t *cent ) {
00738 
00739     // if this player does not want to see extrapolated players
00740     if ( !cg_smoothClients.integer ) {
00741         // make sure the clients use TR_INTERPOLATE
00742         if ( cent->currentState.number < MAX_CLIENTS ) {
00743             cent->currentState.pos.trType = TR_INTERPOLATE;
00744             cent->nextState.pos.trType = TR_INTERPOLATE;
00745         }
00746     }
00747 
00748     if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) {
00749         CG_InterpolateEntityPosition( cent );
00750         return;
00751     }
00752 
00753     // first see if we can interpolate between two snaps for
00754     // linear extrapolated clients
00755     if ( cent->interpolate && cent->currentState.pos.trType == TR_LINEAR_STOP &&
00756                                             cent->currentState.number < MAX_CLIENTS) {
00757         CG_InterpolateEntityPosition( cent );
00758         return;
00759     }
00760 
00761     // just use the current frame and evaluate as best we can
00762     BG_EvaluateTrajectory( &cent->currentState.pos, cg.time, cent->lerpOrigin );
00763     BG_EvaluateTrajectory( &cent->currentState.apos, cg.time, cent->lerpAngles );
00764 
00765     // adjust for riding a mover if it wasn't rolled into the predicted
00766     // player state
00767     if ( cent != &cg.predictedPlayerEntity ) {
00768         CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum, 
00769         cg.snap->serverTime, cg.time, cent->lerpOrigin );
00770     }
00771 }
00772 
00773 /*
00774 ===============
00775 CG_TeamBase
00776 ===============
00777 */
00778 static void CG_TeamBase( centity_t *cent ) {
00779     refEntity_t model;
00780 #ifdef MISSIONPACK
00781     vec3_t angles;
00782     int t, h;
00783     float c;
00784 
00785     if ( cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF ) {
00786 #else
00787     if ( cgs.gametype == GT_CTF) {
00788 #endif
00789         // show the flag base
00790         memset(&model, 0, sizeof(model));
00791         model.reType = RT_MODEL;
00792         VectorCopy( cent->lerpOrigin, model.lightingOrigin );
00793         VectorCopy( cent->lerpOrigin, model.origin );
00794         AnglesToAxis( cent->currentState.angles, model.axis );
00795         if ( cent->currentState.modelindex == TEAM_RED ) {
00796             model.hModel = cgs.media.redFlagBaseModel;
00797         }
00798         else if ( cent->currentState.modelindex == TEAM_BLUE ) {
00799             model.hModel = cgs.media.blueFlagBaseModel;
00800         }
00801         else {
00802             model.hModel = cgs.media.neutralFlagBaseModel;
00803         }
00804         trap_R_AddRefEntityToScene( &model );
00805     }
00806 #ifdef MISSIONPACK
00807     else if ( cgs.gametype == GT_OBELISK ) {
00808         // show the obelisk
00809         memset(&model, 0, sizeof(model));
00810         model.reType = RT_MODEL;
00811         VectorCopy( cent->lerpOrigin, model.lightingOrigin );
00812         VectorCopy( cent->lerpOrigin, model.origin );
00813         AnglesToAxis( cent->currentState.angles, model.axis );
00814 
00815         model.hModel = cgs.media.overloadBaseModel;
00816         trap_R_AddRefEntityToScene( &model );
00817         // if hit
00818         if ( cent->currentState.frame == 1) {
00819             // show hit model
00820             // modelindex2 is the health value of the obelisk
00821             c = cent->currentState.modelindex2;
00822             model.shaderRGBA[0] = 0xff;
00823             model.shaderRGBA[1] = c;
00824             model.shaderRGBA[2] = c;
00825             model.shaderRGBA[3] = 0xff;
00826             //
00827             model.hModel = cgs.media.overloadEnergyModel;
00828             trap_R_AddRefEntityToScene( &model );
00829         }
00830         // if respawning
00831         if ( cent->currentState.frame == 2) {
00832             if ( !cent->miscTime ) {
00833                 cent->miscTime = cg.time;
00834             }
00835             t = cg.time - cent->miscTime;
00836             h = (cg_obeliskRespawnDelay.integer - 5) * 1000;
00837             //
00838             if (t > h) {
00839                 c = (float) (t - h) / h;
00840                 if (c > 1)
00841                     c = 1;
00842             }
00843             else {
00844                 c = 0;
00845             }
00846             // show the lights
00847             AnglesToAxis( cent->currentState.angles, model.axis );
00848             //
00849             model.shaderRGBA[0] = c * 0xff;
00850             model.shaderRGBA[1] = c * 0xff;
00851             model.shaderRGBA[2] = c * 0xff;
00852             model.shaderRGBA[3] = c * 0xff;
00853 
00854             model.hModel = cgs.media.overloadLightsModel;
00855             trap_R_AddRefEntityToScene( &model );
00856             // show the target
00857             if (t > h) {
00858                 if ( !cent->muzzleFlashTime ) {
00859                     trap_S_StartSound (cent->lerpOrigin, ENTITYNUM_NONE, CHAN_BODY,  cgs.media.obeliskRespawnSound);
00860                     cent->muzzleFlashTime = 1;
00861                 }
00862                 VectorCopy(cent->currentState.angles, angles);
00863                 angles[YAW] += (float) 16 * acos(1-c) * 180 / M_PI;
00864                 AnglesToAxis( angles, model.axis );
00865 
00866                 VectorScale( model.axis[0], c, model.axis[0]);
00867                 VectorScale( model.axis[1], c, model.axis[1]);
00868                 VectorScale( model.axis[2], c, model.axis[2]);
00869 
00870                 model.shaderRGBA[0] = 0xff;
00871                 model.shaderRGBA[1] = 0xff;
00872                 model.shaderRGBA[2] = 0xff;
00873                 model.shaderRGBA[3] = 0xff;
00874                 //
00875                 model.origin[2] += 56;
00876                 model.hModel = cgs.media.overloadTargetModel;
00877                 trap_R_AddRefEntityToScene( &model );
00878             }
00879             else {
00880                 //FIXME: show animated smoke
00881             }
00882         }
00883         else {
00884             cent->miscTime = 0;
00885             cent->muzzleFlashTime = 0;
00886             // modelindex2 is the health value of the obelisk
00887             c = cent->currentState.modelindex2;
00888             model.shaderRGBA[0] = 0xff;
00889             model.shaderRGBA[1] = c;
00890             model.shaderRGBA[2] = c;
00891             model.shaderRGBA[3] = 0xff;
00892             // show the lights
00893             model.hModel = cgs.media.overloadLightsModel;
00894             trap_R_AddRefEntityToScene( &model );
00895             // show the target
00896             model.origin[2] += 56;
00897             model.hModel = cgs.media.overloadTargetModel;
00898             trap_R_AddRefEntityToScene( &model );
00899         }
00900     }
00901     else if ( cgs.gametype == GT_HARVESTER ) {
00902         // show harvester model
00903         memset(&model, 0, sizeof(model));
00904         model.reType = RT_MODEL;
00905         VectorCopy( cent->lerpOrigin, model.lightingOrigin );
00906         VectorCopy( cent->lerpOrigin, model.origin );
00907         AnglesToAxis( cent->currentState.angles, model.axis );
00908 
00909         if ( cent->currentState.modelindex == TEAM_RED ) {
00910             model.hModel = cgs.media.harvesterModel;
00911             model.customSkin = cgs.media.harvesterRedSkin;
00912         }
00913         else if ( cent->currentState.modelindex == TEAM_BLUE ) {
00914             model.hModel = cgs.media.harvesterModel;
00915             model.customSkin = cgs.media.harvesterBlueSkin;
00916         }
00917         else {
00918             model.hModel = cgs.media.harvesterNeutralModel;
00919             model.customSkin = 0;
00920         }
00921         trap_R_AddRefEntityToScene( &model );
00922     }
00923 #endif
00924 }
00925 
00926 /*
00927 ===============
00928 CG_AddCEntity
00929 
00930 ===============
00931 */
00932 static void CG_AddCEntity( centity_t *cent ) {
00933     // event-only entities will have been dealt with already
00934     if ( cent->currentState.eType >= ET_EVENTS ) {
00935         return;
00936     }
00937 
00938     // calculate the current origin
00939     CG_CalcEntityLerpPositions( cent );
00940 
00941     // add automatic effects
00942     CG_EntityEffects( cent );
00943 
00944     switch ( cent->currentState.eType ) {
00945     default:
00946         CG_Error( "Bad entity type: %i\n", cent->currentState.eType );
00947         break;
00948     case ET_INVISIBLE:
00949     case ET_PUSH_TRIGGER:
00950     case ET_TELEPORT_TRIGGER:
00951         break;
00952     case ET_GENERAL:
00953         CG_General( cent );
00954         break;
00955     case ET_PLAYER:
00956         CG_Player( cent );
00957         break;
00958     case ET_ITEM:
00959         CG_Item( cent );
00960         break;
00961     case ET_MISSILE:
00962         CG_Missile( cent );
00963         break;
00964     case ET_MOVER:
00965         CG_Mover( cent );
00966         break;
00967     case ET_BEAM:
00968         CG_Beam( cent );
00969         break;
00970     case ET_PORTAL:
00971         CG_Portal( cent );
00972         break;
00973     case ET_SPEAKER:
00974         CG_Speaker( cent );
00975         break;
00976     case ET_GRAPPLE:
00977         CG_Grapple( cent );
00978         break;
00979     case ET_TEAM:
00980         CG_TeamBase( cent );
00981         break;
00982     }
00983 }
00984 
00985 /*
00986 ===============
00987 CG_AddPacketEntities
00988 
00989 ===============
00990 */
00991 void CG_AddPacketEntities( void ) {
00992     int                 num;
00993     centity_t           *cent;
00994     playerState_t       *ps;
00995 
00996     // set cg.frameInterpolation
00997     if ( cg.nextSnap ) {
00998         int     delta;
00999 
01000         delta = (cg.nextSnap->serverTime - cg.snap->serverTime);
01001         if ( delta == 0 ) {
01002             cg.frameInterpolation = 0;
01003         } else {
01004             cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta;
01005         }
01006     } else {
01007         cg.frameInterpolation = 0;  // actually, it should never be used, because 
01008                                     // no entities should be marked as interpolating
01009     }
01010 
01011     // the auto-rotating items will all have the same axis
01012     cg.autoAngles[0] = 0;
01013     cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0;
01014     cg.autoAngles[2] = 0;
01015 
01016     cg.autoAnglesFast[0] = 0;
01017     cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f;
01018     cg.autoAnglesFast[2] = 0;
01019 
01020     AnglesToAxis( cg.autoAngles, cg.autoAxis );
01021     AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast );
01022 
01023     // generate and add the entity from the playerstate
01024     ps = &cg.predictedPlayerState;
01025     BG_PlayerStateToEntityState( ps, &cg.predictedPlayerEntity.currentState, qfalse );
01026     CG_AddCEntity( &cg.predictedPlayerEntity );
01027 
01028     // lerp the non-predicted value for lightning gun origins
01029     CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] );
01030 
01031     // add each entity sent over by the server
01032     for ( num = 0 ; num < cg.snap->numEntities ; num++ ) {
01033         cent = &cg_entities[ cg.snap->entities[ num ].number ];
01034         CG_AddCEntity( cent );
01035     }
01036 }
01037 

Generated on Thu Aug 25 12:37:21 2005 for Quake III Arena by  doxygen 1.3.9.1