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

cg_predict.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_predict.c -- this file generates cg.predictedPlayerState by either
00024 // interpolating between snapshots from the server or locally predicting
00025 // ahead the client's movement.
00026 // It also handles local physics interaction, like fragments bouncing off walls
00027 
00028 #include "cg_local.h"
00029 
00030 static  pmove_t     cg_pmove;
00031 
00032 static  int         cg_numSolidEntities;
00033 static  centity_t   *cg_solidEntities[MAX_ENTITIES_IN_SNAPSHOT];
00034 static  int         cg_numTriggerEntities;
00035 static  centity_t   *cg_triggerEntities[MAX_ENTITIES_IN_SNAPSHOT];
00036 
00037 /*
00038 ====================
00039 CG_BuildSolidList
00040 
00041 When a new cg.snap has been set, this function builds a sublist
00042 of the entities that are actually solid, to make for more
00043 efficient collision detection
00044 ====================
00045 */
00046 void CG_BuildSolidList( void ) {
00047     int         i;
00048     centity_t   *cent;
00049     snapshot_t  *snap;
00050     entityState_t   *ent;
00051 
00052     cg_numSolidEntities = 0;
00053     cg_numTriggerEntities = 0;
00054 
00055     if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) {
00056         snap = cg.nextSnap;
00057     } else {
00058         snap = cg.snap;
00059     }
00060 
00061     for ( i = 0 ; i < snap->numEntities ; i++ ) {
00062         cent = &cg_entities[ snap->entities[ i ].number ];
00063         ent = &cent->currentState;
00064 
00065         if ( ent->eType == ET_ITEM || ent->eType == ET_PUSH_TRIGGER || ent->eType == ET_TELEPORT_TRIGGER ) {
00066             cg_triggerEntities[cg_numTriggerEntities] = cent;
00067             cg_numTriggerEntities++;
00068             continue;
00069         }
00070 
00071         if ( cent->nextState.solid ) {
00072             cg_solidEntities[cg_numSolidEntities] = cent;
00073             cg_numSolidEntities++;
00074             continue;
00075         }
00076     }
00077 }
00078 
00079 /*
00080 ====================
00081 CG_ClipMoveToEntities
00082 
00083 ====================
00084 */
00085 static void CG_ClipMoveToEntities ( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end,
00086                             int skipNumber, int mask, trace_t *tr ) {
00087     int         i, x, zd, zu;
00088     trace_t     trace;
00089     entityState_t   *ent;
00090     clipHandle_t    cmodel;
00091     vec3_t      bmins, bmaxs;
00092     vec3_t      origin, angles;
00093     centity_t   *cent;
00094 
00095     for ( i = 0 ; i < cg_numSolidEntities ; i++ ) {
00096         cent = cg_solidEntities[ i ];
00097         ent = &cent->currentState;
00098 
00099         if ( ent->number == skipNumber ) {
00100             continue;
00101         }
00102 
00103         if ( ent->solid == SOLID_BMODEL ) {
00104             // special value for bmodel
00105             cmodel = trap_CM_InlineModel( ent->modelindex );
00106             VectorCopy( cent->lerpAngles, angles );
00107             BG_EvaluateTrajectory( &cent->currentState.pos, cg.physicsTime, origin );
00108         } else {
00109             // encoded bbox
00110             x = (ent->solid & 255);
00111             zd = ((ent->solid>>8) & 255);
00112             zu = ((ent->solid>>16) & 255) - 32;
00113 
00114             bmins[0] = bmins[1] = -x;
00115             bmaxs[0] = bmaxs[1] = x;
00116             bmins[2] = -zd;
00117             bmaxs[2] = zu;
00118 
00119             cmodel = trap_CM_TempBoxModel( bmins, bmaxs );
00120             VectorCopy( vec3_origin, angles );
00121             VectorCopy( cent->lerpOrigin, origin );
00122         }
00123 
00124 
00125         trap_CM_TransformedBoxTrace ( &trace, start, end,
00126             mins, maxs, cmodel,  mask, origin, angles);
00127 
00128         if (trace.allsolid || trace.fraction < tr->fraction) {
00129             trace.entityNum = ent->number;
00130             *tr = trace;
00131         } else if (trace.startsolid) {
00132             tr->startsolid = qtrue;
00133         }
00134         if ( tr->allsolid ) {
00135             return;
00136         }
00137     }
00138 }
00139 
00140 /*
00141 ================
00142 CG_Trace
00143 ================
00144 */
00145 void    CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, 
00146                      int skipNumber, int mask ) {
00147     trace_t t;
00148 
00149     trap_CM_BoxTrace ( &t, start, end, mins, maxs, 0, mask);
00150     t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
00151     // check all other solid models
00152     CG_ClipMoveToEntities (start, mins, maxs, end, skipNumber, mask, &t);
00153 
00154     *result = t;
00155 }
00156 
00157 /*
00158 ================
00159 CG_PointContents
00160 ================
00161 */
00162 int     CG_PointContents( const vec3_t point, int passEntityNum ) {
00163     int         i;
00164     entityState_t   *ent;
00165     centity_t   *cent;
00166     clipHandle_t cmodel;
00167     int         contents;
00168 
00169     contents = trap_CM_PointContents (point, 0);
00170 
00171     for ( i = 0 ; i < cg_numSolidEntities ; i++ ) {
00172         cent = cg_solidEntities[ i ];
00173 
00174         ent = &cent->currentState;
00175 
00176         if ( ent->number == passEntityNum ) {
00177             continue;
00178         }
00179 
00180         if (ent->solid != SOLID_BMODEL) { // special value for bmodel
00181             continue;
00182         }
00183 
00184         cmodel = trap_CM_InlineModel( ent->modelindex );
00185         if ( !cmodel ) {
00186             continue;
00187         }
00188 
00189         contents |= trap_CM_TransformedPointContents( point, cmodel, ent->origin, ent->angles );
00190     }
00191 
00192     return contents;
00193 }
00194 
00195 
00196 /*
00197 ========================
00198 CG_InterpolatePlayerState
00199 
00200 Generates cg.predictedPlayerState by interpolating between
00201 cg.snap->player_state and cg.nextFrame->player_state
00202 ========================
00203 */
00204 static void CG_InterpolatePlayerState( qboolean grabAngles ) {
00205     float           f;
00206     int             i;
00207     playerState_t   *out;
00208     snapshot_t      *prev, *next;
00209 
00210     out = &cg.predictedPlayerState;
00211     prev = cg.snap;
00212     next = cg.nextSnap;
00213 
00214     *out = cg.snap->ps;
00215 
00216     // if we are still allowing local input, short circuit the view angles
00217     if ( grabAngles ) {
00218         usercmd_t   cmd;
00219         int         cmdNum;
00220 
00221         cmdNum = trap_GetCurrentCmdNumber();
00222         trap_GetUserCmd( cmdNum, &cmd );
00223 
00224         PM_UpdateViewAngles( out, &cmd );
00225     }
00226 
00227     // if the next frame is a teleport, we can't lerp to it
00228     if ( cg.nextFrameTeleport ) {
00229         return;
00230     }
00231 
00232     if ( !next || next->serverTime <= prev->serverTime ) {
00233         return;
00234     }
00235 
00236     f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime );
00237 
00238     i = next->ps.bobCycle;
00239     if ( i < prev->ps.bobCycle ) {
00240         i += 256;       // handle wraparound
00241     }
00242     out->bobCycle = prev->ps.bobCycle + f * ( i - prev->ps.bobCycle );
00243 
00244     for ( i = 0 ; i < 3 ; i++ ) {
00245         out->origin[i] = prev->ps.origin[i] + f * (next->ps.origin[i] - prev->ps.origin[i] );
00246         if ( !grabAngles ) {
00247             out->viewangles[i] = LerpAngle( 
00248                 prev->ps.viewangles[i], next->ps.viewangles[i], f );
00249         }
00250         out->velocity[i] = prev->ps.velocity[i] + 
00251             f * (next->ps.velocity[i] - prev->ps.velocity[i] );
00252     }
00253 
00254 }
00255 
00256 /*
00257 ===================
00258 CG_TouchItem
00259 ===================
00260 */
00261 static void CG_TouchItem( centity_t *cent ) {
00262     gitem_t     *item;
00263 
00264     if ( !cg_predictItems.integer ) {
00265         return;
00266     }
00267     if ( !BG_PlayerTouchesItem( &cg.predictedPlayerState, &cent->currentState, cg.time ) ) {
00268         return;
00269     }
00270 
00271     // never pick an item up twice in a prediction
00272     if ( cent->miscTime == cg.time ) {
00273         return;
00274     }
00275 
00276     if ( !BG_CanItemBeGrabbed( cgs.gametype, &cent->currentState, &cg.predictedPlayerState ) ) {
00277         return;     // can't hold it
00278     }
00279 
00280     item = &bg_itemlist[ cent->currentState.modelindex ];
00281 
00282     // Special case for flags.  
00283     // We don't predict touching our own flag
00284 #ifdef MISSIONPACK
00285     if( cgs.gametype == GT_1FCTF ) {
00286         if( item->giTag != PW_NEUTRALFLAG ) {
00287             return;
00288         }
00289     }
00290     if( cgs.gametype == GT_CTF || cgs.gametype == GT_HARVESTER ) {
00291 #else
00292     if( cgs.gametype == GT_CTF ) {
00293 #endif
00294         if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_RED &&
00295             item->giTag == PW_REDFLAG)
00296             return;
00297         if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_BLUE &&
00298             item->giTag == PW_BLUEFLAG)
00299             return;
00300     }
00301 
00302     // grab it
00303     BG_AddPredictableEventToPlayerstate( EV_ITEM_PICKUP, cent->currentState.modelindex , &cg.predictedPlayerState);
00304 
00305     // remove it from the frame so it won't be drawn
00306     cent->currentState.eFlags |= EF_NODRAW;
00307 
00308     // don't touch it again this prediction
00309     cent->miscTime = cg.time;
00310 
00311     // if its a weapon, give them some predicted ammo so the autoswitch will work
00312     if ( item->giType == IT_WEAPON ) {
00313         cg.predictedPlayerState.stats[ STAT_WEAPONS ] |= 1 << item->giTag;
00314         if ( !cg.predictedPlayerState.ammo[ item->giTag ] ) {
00315             cg.predictedPlayerState.ammo[ item->giTag ] = 1;
00316         }
00317     }
00318 }
00319 
00320 
00321 /*
00322 =========================
00323 CG_TouchTriggerPrediction
00324 
00325 Predict push triggers and items
00326 =========================
00327 */
00328 static void CG_TouchTriggerPrediction( void ) {
00329     int         i;
00330     trace_t     trace;
00331     entityState_t   *ent;
00332     clipHandle_t cmodel;
00333     centity_t   *cent;
00334     qboolean    spectator;
00335 
00336     // dead clients don't activate triggers
00337     if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
00338         return;
00339     }
00340 
00341     spectator = ( cg.predictedPlayerState.pm_type == PM_SPECTATOR );
00342 
00343     if ( cg.predictedPlayerState.pm_type != PM_NORMAL && !spectator ) {
00344         return;
00345     }
00346 
00347     for ( i = 0 ; i < cg_numTriggerEntities ; i++ ) {
00348         cent = cg_triggerEntities[ i ];
00349         ent = &cent->currentState;
00350 
00351         if ( ent->eType == ET_ITEM && !spectator ) {
00352             CG_TouchItem( cent );
00353             continue;
00354         }
00355 
00356         if ( ent->solid != SOLID_BMODEL ) {
00357             continue;
00358         }
00359 
00360         cmodel = trap_CM_InlineModel( ent->modelindex );
00361         if ( !cmodel ) {
00362             continue;
00363         }
00364 
00365         trap_CM_BoxTrace( &trace, cg.predictedPlayerState.origin, cg.predictedPlayerState.origin, 
00366             cg_pmove.mins, cg_pmove.maxs, cmodel, -1 );
00367 
00368         if ( !trace.startsolid ) {
00369             continue;
00370         }
00371 
00372         if ( ent->eType == ET_TELEPORT_TRIGGER ) {
00373             cg.hyperspace = qtrue;
00374         } else if ( ent->eType == ET_PUSH_TRIGGER ) {
00375             BG_TouchJumpPad( &cg.predictedPlayerState, ent );
00376         }
00377     }
00378 
00379     // if we didn't touch a jump pad this pmove frame
00380     if ( cg.predictedPlayerState.jumppad_frame != cg.predictedPlayerState.pmove_framecount ) {
00381         cg.predictedPlayerState.jumppad_frame = 0;
00382         cg.predictedPlayerState.jumppad_ent = 0;
00383     }
00384 }
00385 
00386 
00387 
00388 /*
00389 =================
00390 CG_PredictPlayerState
00391 
00392 Generates cg.predictedPlayerState for the current cg.time
00393 cg.predictedPlayerState is guaranteed to be valid after exiting.
00394 
00395 For demo playback, this will be an interpolation between two valid
00396 playerState_t.
00397 
00398 For normal gameplay, it will be the result of predicted usercmd_t on
00399 top of the most recent playerState_t received from the server.
00400 
00401 Each new snapshot will usually have one or more new usercmd over the last,
00402 but we simulate all unacknowledged commands each time, not just the new ones.
00403 This means that on an internet connection, quite a few pmoves may be issued
00404 each frame.
00405 
00406 OPTIMIZE: don't re-simulate unless the newly arrived snapshot playerState_t
00407 differs from the predicted one.  Would require saving all intermediate
00408 playerState_t during prediction.
00409 
00410 We detect prediction errors and allow them to be decayed off over several frames
00411 to ease the jerk.
00412 =================
00413 */
00414 void CG_PredictPlayerState( void ) {
00415     int         cmdNum, current;
00416     playerState_t   oldPlayerState;
00417     qboolean    moved;
00418     usercmd_t   oldestCmd;
00419     usercmd_t   latestCmd;
00420 
00421     cg.hyperspace = qfalse; // will be set if touching a trigger_teleport
00422 
00423     // if this is the first frame we must guarantee
00424     // predictedPlayerState is valid even if there is some
00425     // other error condition
00426     if ( !cg.validPPS ) {
00427         cg.validPPS = qtrue;
00428         cg.predictedPlayerState = cg.snap->ps;
00429     }
00430 
00431 
00432     // demo playback just copies the moves
00433     if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) ) {
00434         CG_InterpolatePlayerState( qfalse );
00435         return;
00436     }
00437 
00438     // non-predicting local movement will grab the latest angles
00439     if ( cg_nopredict.integer || cg_synchronousClients.integer ) {
00440         CG_InterpolatePlayerState( qtrue );
00441         return;
00442     }
00443 
00444     // prepare for pmove
00445     cg_pmove.ps = &cg.predictedPlayerState;
00446     cg_pmove.trace = CG_Trace;
00447     cg_pmove.pointcontents = CG_PointContents;
00448     if ( cg_pmove.ps->pm_type == PM_DEAD ) {
00449         cg_pmove.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
00450     }
00451     else {
00452         cg_pmove.tracemask = MASK_PLAYERSOLID;
00453     }
00454     if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
00455         cg_pmove.tracemask &= ~CONTENTS_BODY;   // spectators can fly through bodies
00456     }
00457     cg_pmove.noFootsteps = ( cgs.dmflags & DF_NO_FOOTSTEPS ) > 0;
00458 
00459     // save the state before the pmove so we can detect transitions
00460     oldPlayerState = cg.predictedPlayerState;
00461 
00462     current = trap_GetCurrentCmdNumber();
00463 
00464     // if we don't have the commands right after the snapshot, we
00465     // can't accurately predict a current position, so just freeze at
00466     // the last good position we had
00467     cmdNum = current - CMD_BACKUP + 1;
00468     trap_GetUserCmd( cmdNum, &oldestCmd );
00469     if ( oldestCmd.serverTime > cg.snap->ps.commandTime 
00470         && oldestCmd.serverTime < cg.time ) {   // special check for map_restart
00471         if ( cg_showmiss.integer ) {
00472             CG_Printf ("exceeded PACKET_BACKUP on commands\n");
00473         }
00474         return;
00475     }
00476 
00477     // get the latest command so we can know which commands are from previous map_restarts
00478     trap_GetUserCmd( current, &latestCmd );
00479 
00480     // get the most recent information we have, even if
00481     // the server time is beyond our current cg.time,
00482     // because predicted player positions are going to 
00483     // be ahead of everything else anyway
00484     if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) {
00485         cg.predictedPlayerState = cg.nextSnap->ps;
00486         cg.physicsTime = cg.nextSnap->serverTime;
00487     } else {
00488         cg.predictedPlayerState = cg.snap->ps;
00489         cg.physicsTime = cg.snap->serverTime;
00490     }
00491 
00492     if ( pmove_msec.integer < 8 ) {
00493         trap_Cvar_Set("pmove_msec", "8");
00494     }
00495     else if (pmove_msec.integer > 33) {
00496         trap_Cvar_Set("pmove_msec", "33");
00497     }
00498 
00499     cg_pmove.pmove_fixed = pmove_fixed.integer;// | cg_pmove_fixed.integer;
00500     cg_pmove.pmove_msec = pmove_msec.integer;
00501 
00502     // run cmds
00503     moved = qfalse;
00504     for ( cmdNum = current - CMD_BACKUP + 1 ; cmdNum <= current ; cmdNum++ ) {
00505         // get the command
00506         trap_GetUserCmd( cmdNum, &cg_pmove.cmd );
00507 
00508         if ( cg_pmove.pmove_fixed ) {
00509             PM_UpdateViewAngles( cg_pmove.ps, &cg_pmove.cmd );
00510         }
00511 
00512         // don't do anything if the time is before the snapshot player time
00513         if ( cg_pmove.cmd.serverTime <= cg.predictedPlayerState.commandTime ) {
00514             continue;
00515         }
00516 
00517         // don't do anything if the command was from a previous map_restart
00518         if ( cg_pmove.cmd.serverTime > latestCmd.serverTime ) {
00519             continue;
00520         }
00521 
00522         // check for a prediction error from last frame
00523         // on a lan, this will often be the exact value
00524         // from the snapshot, but on a wan we will have
00525         // to predict several commands to get to the point
00526         // we want to compare
00527         if ( cg.predictedPlayerState.commandTime == oldPlayerState.commandTime ) {
00528             vec3_t  delta;
00529             float   len;
00530 
00531             if ( cg.thisFrameTeleport ) {
00532                 // a teleport will not cause an error decay
00533                 VectorClear( cg.predictedError );
00534                 if ( cg_showmiss.integer ) {
00535                     CG_Printf( "PredictionTeleport\n" );
00536                 }
00537                 cg.thisFrameTeleport = qfalse;
00538             } else {
00539                 vec3_t  adjusted;
00540                 CG_AdjustPositionForMover( cg.predictedPlayerState.origin, 
00541                     cg.predictedPlayerState.groundEntityNum, cg.physicsTime, cg.oldTime, adjusted );
00542 
00543                 if ( cg_showmiss.integer ) {
00544                     if (!VectorCompare( oldPlayerState.origin, adjusted )) {
00545                         CG_Printf("prediction error\n");
00546                     }
00547                 }
00548                 VectorSubtract( oldPlayerState.origin, adjusted, delta );
00549                 len = VectorLength( delta );
00550                 if ( len > 0.1 ) {
00551                     if ( cg_showmiss.integer ) {
00552                         CG_Printf("Prediction miss: %f\n", len);
00553                     }
00554                     if ( cg_errorDecay.integer ) {
00555                         int     t;
00556                         float   f;
00557 
00558                         t = cg.time - cg.predictedErrorTime;
00559                         f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
00560                         if ( f < 0 ) {
00561                             f = 0;
00562                         }
00563                         if ( f > 0 && cg_showmiss.integer ) {
00564                             CG_Printf("Double prediction decay: %f\n", f);
00565                         }
00566                         VectorScale( cg.predictedError, f, cg.predictedError );
00567                     } else {
00568                         VectorClear( cg.predictedError );
00569                     }
00570                     VectorAdd( delta, cg.predictedError, cg.predictedError );
00571                     cg.predictedErrorTime = cg.oldTime;
00572                 }
00573             }
00574         }
00575 
00576         // don't predict gauntlet firing, which is only supposed to happen
00577         // when it actually inflicts damage
00578         cg_pmove.gauntletHit = qfalse;
00579 
00580         if ( cg_pmove.pmove_fixed ) {
00581             cg_pmove.cmd.serverTime = ((cg_pmove.cmd.serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer;
00582         }
00583 
00584         Pmove (&cg_pmove);
00585 
00586         moved = qtrue;
00587 
00588         // add push trigger movement effects
00589         CG_TouchTriggerPrediction();
00590 
00591         // check for predictable events that changed from previous predictions
00592         //CG_CheckChangedPredictableEvents(&cg.predictedPlayerState);
00593     }
00594 
00595     if ( cg_showmiss.integer > 1 ) {
00596         CG_Printf( "[%i : %i] ", cg_pmove.cmd.serverTime, cg.time );
00597     }
00598 
00599     if ( !moved ) {
00600         if ( cg_showmiss.integer ) {
00601             CG_Printf( "not moved\n" );
00602         }
00603         return;
00604     }
00605 
00606     // adjust for the movement of the groundentity
00607     CG_AdjustPositionForMover( cg.predictedPlayerState.origin, 
00608         cg.predictedPlayerState.groundEntityNum, 
00609         cg.physicsTime, cg.time, cg.predictedPlayerState.origin );
00610 
00611     if ( cg_showmiss.integer ) {
00612         if (cg.predictedPlayerState.eventSequence > oldPlayerState.eventSequence + MAX_PS_EVENTS) {
00613             CG_Printf("WARNING: dropped event\n");
00614         }
00615     }
00616 
00617     // fire events and other transition triggered things
00618     CG_TransitionPlayerState( &cg.predictedPlayerState, &oldPlayerState );
00619 
00620     if ( cg_showmiss.integer ) {
00621         if (cg.eventSequence > cg.predictedPlayerState.eventSequence) {
00622             CG_Printf("WARNING: double event\n");
00623             cg.eventSequence = cg.predictedPlayerState.eventSequence;
00624         }
00625     }
00626 }
00627 
00628 

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