/* ===================== CG_DrawActive Perform all drawing needed to completely fill the screen ===================== */ void CG_DrawActive() { // optionally draw the info screen instead if ( !cg.snap ) { return; } // clear around the rendered view if sized down CG_TileClear(); CG_DrawBinaryShadersFinalPhases(); // draw 3D view trap_R_RenderScene( &cg.refdef ); // first person blend blobs, done after AnglesToAxis if ( !cg.renderingThirdPerson ) { CG_PainBlend(); } // draw status bar and other floating elements CG_Draw2D(); }
/* ===================== CG_DrawActive Perform all drawing needed to completely fill the screen ===================== */ void CG_DrawActive( stereoFrame_t stereoView ) { float separation; vec3_t baseOrg; // optionally draw the info screen instead if ( !cg.snap ) { return; } switch ( stereoView ) { case STEREO_CENTER: separation = 0; break; default: separation = 0; CG_Error( "CG_DrawActive: Undefined stereoView" ); } // clear around the rendered view if sized down CG_TileClear(); // offset vieworg appropriately if we're doing stereo separation VectorCopy( cg.refdef.vieworg, baseOrg ); if ( separation != 0 ) { VectorMA( cg.refdef.vieworg, -separation, cg.refdef.viewaxis[ 1 ], cg.refdef.vieworg ); } CG_DrawBinaryShadersFinalPhases(); // draw 3D view trap_R_RenderScene( &cg.refdef ); // restore original viewpoint if running stereo if ( separation != 0 ) { VectorCopy( baseOrg, cg.refdef.vieworg ); } // first person blend blobs, done after AnglesToAxis if ( !cg.renderingThirdPerson ) { CG_PainBlend(); } // draw status bar and other floating elements CG_Draw2D(); }
/* ================ vmMain This is the only way control passes into the module. This must be the very first function compiled into the .q3vm file ================ */ intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) { switch ( command ) { case CG_INIT: CG_Init( arg0, arg1, arg2, arg3 ); return 0; case CG_SHUTDOWN: CG_Shutdown(); return 0; case CG_CONSOLE_COMMAND: return CG_ConsoleCommand(); case CG_DRAW_ACTIVE_FRAME: CG_DrawActiveFrame( arg0, arg1, arg2 ); return 0; case CG_DRAW_2D: CG_Draw2D( arg0, arg1, arg2 ); return 0; case CG_CROSSHAIR_PLAYER: return CG_CrosshairPlayer(); case CG_LAST_ATTACKER: return CG_LastAttacker(); case CG_KEY_EVENT: CG_KeyEvent(arg0, arg1); return 0; case CG_MOUSE_EVENT: CG_MouseEvent(arg0, arg1); return 0; case CG_EVENT_HANDLING: CG_EventHandling(arg0); return 0; case CG_CENTERPRINT: CG_CenterPrint( (const char*)arg0, 200, 10 ); return 0; case CG_LOCATIONPRINT: CG_LocationPrint( (const char*)arg0, arg1, arg2, 10 ); return 0; case CG_PARSEMSG: CG_ParseCGMessage(); return 0; default: CG_Error( "vmMain: unknown command %i", command ); break; } return -1; }
void CG_DemosDrawActiveFrame( int serverTime, stereoFrame_t stereoView ) { int deltaTime; qboolean hadSkip; qboolean captureFrame; float captureFPS; float frameSpeed; int blurTotal, blurIndex; float blurFraction; float stereoSep = CG_Cvar_Get( "r_stereoSeparation" ); int inwater, entityNum; if (!demo.initDone) { if ( !cg.snap ) { demoProcessSnapShots( qtrue ); } if ( !cg.snap ) { CG_Error( "No Initial demo snapshot found" ); } demoPlaybackInit(); } cg.demoPlayback = 2; // update cvars CG_UpdateCvars(); // if we are only updating the screen as a loading // pacifier, don't even try to read snapshots if ( cg.loading ) { CG_DrawInformation(); return; } captureFrame = demo.capture.active && !demo.play.paused; if ( captureFrame ) { trap_MME_BlurInfo( &blurTotal, &blurIndex ); captureFPS = mov_captureFPS.value; if ( blurTotal > 0) { captureFPS *= blurTotal; blurFraction = blurIndex / (float)blurTotal; } else { blurFraction = 0; } } else { } /* Forward the demo */ deltaTime = serverTime - demo.serverTime; if (deltaTime > 50) deltaTime = 50; demo.serverTime = serverTime; demo.serverDeltaTime = 0.001 * deltaTime; cg.oldTime = cg.time; cg.oldTimeFraction = cg.timeFraction; if (demo.play.time < 0) { demo.play.time = demo.play.fraction = 0; } demo.play.oldTime = demo.play.time; /* Handle the music */ if ( demo.play.paused ) { if ( lastMusicStart >= 0) demoSynchMusic( -1, 0 ); } else { int musicStart = (demo.play.time - mov_musicStart.value * 1000 ); if ( musicStart <= 0 ) { if (lastMusicStart >= 0 ) demoSynchMusic( -1, 0 ); } else { if ( demo.play.time != demo.play.lastTime || lastMusicStart < 0) demoSynchMusic( musicStart, 0 ); } } /* forward the time a bit till the moment of capture */ if ( captureFrame && demo.capture.locked && demo.play.time < demo.capture.start ) { int left = demo.capture.start - demo.play.time; if ( left > 2000) { left -= 1000; captureFrame = qfalse; } else if (left > 5) { captureFrame = qfalse; left = 5; } demo.play.time += left; } else if ( captureFrame && demo.loop.total && blurTotal ) { float loopFraction = demo.loop.index / (float)demo.loop.total; demo.play.time = demo.loop.start; demo.play.fraction = demo.loop.range * loopFraction; demo.play.time += (int)demo.play.fraction; demo.play.fraction -= (int)demo.play.fraction; } else if (captureFrame) { float frameDelay = 1000.0f / captureFPS; demo.play.fraction += frameDelay * demo.play.speed; demo.play.time += (int)demo.play.fraction; demo.play.fraction -= (int)demo.play.fraction; } else if ( demo.find ) { demo.play.time = demo.play.oldTime + 20; demo.play.fraction = 0; if ( demo.play.paused ) demo.find = findNone; } else if (!demo.play.paused) { float delta = demo.play.fraction + deltaTime * demo.play.speed; demo.play.time += (int)delta; demo.play.fraction = delta - (int)delta; } demo.play.lastTime = demo.play.time; if ( demo.loop.total && captureFrame && blurTotal ) { //Delay till we hit the right part at the start int time; float timeFraction; if ( demo.loop.lineDelay && !blurIndex ) { time = demo.loop.start - demo.loop.lineDelay; timeFraction = 0; if ( demo.loop.lineDelay > 8 ) demo.loop.lineDelay -= 8; else demo.loop.lineDelay = 0; captureFrame = qfalse; } else { if ( blurIndex == blurTotal - 1 ) { //We'll restart back to the start again demo.loop.lineDelay = 2000; if ( ++demo.loop.index >= demo.loop.total ) { demo.loop.total = 0; } } time = demo.loop.start; timeFraction = demo.loop.range * blurFraction; } time += (int)timeFraction; timeFraction -= (int)timeFraction; lineAt( time, timeFraction, &demo.line.time, &cg.timeFraction, &frameSpeed ); } else { lineAt( demo.play.time, demo.play.fraction, &demo.line.time, &cg.timeFraction, &frameSpeed ); } /* Set the correct time */ cg.time = trap_MME_SeekTime( demo.line.time ); /* cg.time is shifted ahead a bit to correct some issues.. */ frameSpeed *= demo.play.speed; cg.frametime = (cg.time - cg.oldTime) + (cg.timeFraction - cg.oldTimeFraction); if (cg.frametime < 0) { int i; cg.frametime = 0; hadSkip = qtrue; cg.oldTime = cg.time; cg.oldTimeFraction = cg.timeFraction; CG_InitLocalEntities(); CG_InitMarkPolys(); CG_ClearParticles (); trap_FX_Reset( ); trap_R_DecalReset(); cg.centerPrintTime = 0; cg.damageTime = 0; cg.powerupTime = 0; cg.rewardTime = 0; cg.scoreFadeTime = 0; cg.lastKillTime = 0; cg.attackerTime = 0; cg.soundTime = 0; cg.itemPickupTime = 0; cg.itemPickupBlendTime = 0; cg.weaponSelectTime = 0; cg.headEndTime = 0; cg.headStartTime = 0; cg.v_dmg_time = 0; cg.rewardCount[0] = 0; cg.rewardStack = 0; cg.rewardTime = 0; trap_S_ClearLoopingSounds(qtrue); for (i = 0; i < MAX_CHATBOX_ITEMS; i++) cg.chatItems[i].time = 0; } else if (cg.frametime > 100) { hadSkip = qtrue; } else { hadSkip = qfalse; } /* Make sure the random seed is the same each time we hit this frame */ srand( (cg.time % 10000000) + cg.timeFraction * 1000); /* Prepare to render the screen */ trap_S_ClearLoopingSounds(qfalse); trap_R_ClearScene(); /* Update demo related information */ trap_SetUserCmdValue( cg.weaponSelect, 1 ); demoProcessSnapShots( hadSkip ); if ( !cg.snap ) { CG_DrawInformation(); return; } CG_PreparePacketEntities( ); CG_DemosUpdatePlayer( ); chaseUpdate( demo.play.time, demo.play.fraction ); cameraUpdate( demo.play.time, demo.play.fraction ); dofUpdate( demo.play.time, demo.play.fraction ); demoEffectUpdate( demo.play.time, demo.play.fraction ); cg.clientFrame++; // update cg.predictedPlayerState CG_InterpolatePlayerState( qfalse ); BG_PlayerStateToEntityState( &cg.predictedPlayerState, &cg.predictedPlayerEntity.currentState, qfalse ); if ( cg.cpma.detected ) { if ( cg.predictedPlayerState.pm_type >= 4 ) cg.predictedPlayerEntity.currentState.eType = ET_INVISIBLE; } cg.predictedPlayerEntity.currentValid = qtrue; VectorCopy( cg.predictedPlayerEntity.currentState.pos.trBase, cg.predictedPlayerEntity.lerpOrigin ); VectorCopy( cg.predictedPlayerEntity.currentState.apos.trBase, cg.predictedPlayerEntity.lerpAngles ); inwater = demoSetupView(); CG_TileClear(); trap_FX_Begin( cg.time, cg.timeFraction ); scriptRun( hadSkip ); CG_AddPacketEntities(); CG_AddMarks(); CG_AddParticles (); CG_AddLocalEntities(); if ( cg.playerCent == &cg.predictedPlayerEntity ) { // warning sounds when powerup is wearing off CG_PowerupTimerSounds(); CG_AddViewWeapon( &cg.predictedPlayerState ); } else if ( cg.playerCent && cg.playerCent->currentState.number < MAX_CLIENTS ) { CG_AddViewWeaponDirect( cg.playerCent ); } trap_S_UpdateEntityPosition(ENTITYNUM_NONE, cg.refdef.vieworg); CG_PlayBufferedSounds(); CG_PlayBufferedVoiceChats(); cg.refdef.time = cg.time; memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) ); /* Render some extra demo related stuff */ if (!captureFrame) { switch (demo.editType) { case editCamera: cameraDraw( demo.play.time, demo.play.fraction ); break; case editChase: chaseDraw( demo.play.time, demo.play.fraction ); break; case editDof: dofDraw( demo.play.time, demo.play.fraction ); break; case editEffect: demoEffectDraw( demo.play.time, demo.play.fraction ); break; } /* Add bounding boxes for easy aiming */ if ( demo.editType && ( demo.cmd.buttons & BUTTON_ATTACK) && ( demo.cmd.buttons & BUTTON_AFFIRMATIVE) ) { int i; centity_t *targetCent; for (i = 0;i<MAX_GENTITIES;i++) { targetCent = demoTargetEntity( i ); if (targetCent) { vec3_t container, traceStart, traceImpact, forward; const float *color; demoCentityBoxSize( targetCent, container ); VectorSubtract( demo.viewOrigin, targetCent->lerpOrigin, traceStart ); AngleVectors( demo.viewAngles, forward, 0, 0 ); if (BoxTraceImpact( traceStart, forward, container, traceImpact )) { color = colorRed; } else { color = colorYellow; } demoDrawBox( targetCent->lerpOrigin, container, color ); } } } if ( mov_gridStep.value > 0 && mov_gridRange.value > 0) { vec4_t color; vec3_t offset; qhandle_t shader = trap_R_RegisterShader( "mme/gridline" ); color[0] = color[1] = color[2] = 1; color[3] = 0; offset[0] = offset[1] = offset[2] = 0; Q_parseColor( mov_gridColor.string, ospColors, color ); demoDrawGrid( demo.viewOrigin, color, offset, mov_gridWidth.value, mov_gridStep.value, mov_gridRange.value, shader ); } } if (frameSpeed > 5) frameSpeed = 5; trap_S_UpdateScale( frameSpeed ); if (cg.playerCent && cg.predictedPlayerState.pm_type == PM_INTERMISSION) { entityNum = cg.snap->ps.clientNum; } else if (cg.playerCent) { entityNum = cg.playerCent->currentState.number; } else { entityNum = ENTITYNUM_NONE; } trap_S_Respatialize( entityNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater); trap_FX_End(); if (captureFrame && stereoSep > 0.0f) trap_Cvar_Set("r_stereoSeparation", va("%f", -stereoSep)); trap_MME_TimeFraction(cg.timeFraction); trap_R_RenderScene( &cg.refdef ); if ( demo.viewType == viewChase && cg.playerCent && ( cg.playerCent->currentState.number < MAX_CLIENTS ) ) CG_Draw2D(); // CG_DrawSmallString( 0, 0, va( "height %d", cg.playerCent->pe.viewHeight ), 1 ); if (captureFrame) { char fileName[MAX_OSPATH]; Com_sprintf( fileName, sizeof( fileName ), "capture/%s/%s", mme_demoFileName.string, mov_captureName.string ); trap_MME_Capture( fileName, captureFPS, demo.viewFocus, demo.viewRadius ); if ( mov_captureCamera.integer ) demoAddViewPos( fileName, demo.viewOrigin, demo.viewAngles, demo.viewFov ); } else { if (demo.editType && !cg.playerCent) demoDrawCrosshair(); hudDraw(); if (demo.editType) { demoDrawProgress(trap_MME_ProgressTime()); } } //checkCaptureEnd: if ( demo.capture.active && demo.capture.locked && demo.play.time > demo.capture.end ) { Com_Printf( "Capturing ended\n" ); if (demo.autoLoad) { trap_SendConsoleCommand( "disconnect\n" ); } demo.capture.active = qfalse; } }
void CG_RenderView( float frameTime, float realFrameTime, int realTime, unsigned int serverTime, float stereo_separation, unsigned int extrapolationTime, bool flipped ) { refdef_t *rd = &cg.view.refdef; // update time cg.realTime = realTime; cg.frameTime = frameTime; cg.realFrameTime = realFrameTime; cg.frameCount++; cg.time = serverTime; if( !cgs.precacheDone || !cg.frame.valid ) { CG_Precache(); CG_DrawLoading(); return; } { float snapTime = ( cg.frame.serverTime - cg.oldFrame.serverTime ); if( !snapTime ) snapTime = cgs.snapFrameTime; // moved this from CG_Init here cgs.extrapolationTime = extrapolationTime; if( cg.oldFrame.serverTime == cg.frame.serverTime ) cg.lerpfrac = 1.0f; else cg.lerpfrac = ( (double)( cg.time - cgs.extrapolationTime ) - (double)cg.oldFrame.serverTime ) / (double)snapTime; if( cgs.extrapolationTime ) { cg.xerpTime = 0.001f * ( (double)cg.time - (double)cg.frame.serverTime ); cg.oldXerpTime = 0.001f * ( (double)cg.time - (double)cg.oldFrame.serverTime ); if( cg.time >= cg.frame.serverTime ) { cg.xerpSmoothFrac = (double)( cg.time - cg.frame.serverTime ) / (double)( cgs.extrapolationTime ); clamp( cg.xerpSmoothFrac, 0.0f, 1.0f ); } else { cg.xerpSmoothFrac = (double)( cg.frame.serverTime - cg.time ) / (double)( cgs.extrapolationTime ); clamp( cg.xerpSmoothFrac, -1.0f, 0.0f ); cg.xerpSmoothFrac = 1.0f - cg.xerpSmoothFrac; } clamp_low( cg.xerpTime, -( cgs.extrapolationTime * 0.001f ) ); //clamp( cg.xerpTime, -( cgs.extrapolationTime * 0.001f ), ( cgs.extrapolationTime * 0.001f ) ); //clamp( cg.oldXerpTime, 0, ( ( snapTime + cgs.extrapolationTime ) * 0.001f ) ); } else { cg.xerpTime = 0.0f; cg.xerpSmoothFrac = 0.0f; } } if( cg_showClamp->integer ) { if( cg.lerpfrac > 1.0f ) CG_Printf( "high clamp %f\n", cg.lerpfrac ); else if( cg.lerpfrac < 0.0f ) CG_Printf( "low clamp %f\n", cg.lerpfrac ); } clamp( cg.lerpfrac, 0.0f, 1.0f ); if( !cgs.configStrings[CS_WORLDMODEL][0] ) { CG_AddLocalSounds(); trap_R_DrawStretchPic( 0, 0, cgs.vidWidth, cgs.vidHeight, 0, 0, 1, 1, colorBlack, cgs.shaderWhite ); trap_S_Update( vec3_origin, vec3_origin, axis_identity, cgs.clientInfo[cgs.playerNum].name ); return; } // bring up the game menu after reconnecting if( !cgs.tv && !cgs.demoPlaying ) { if( ISREALSPECTATOR() && !cg.firstFrame ) { if( !cgs.gameMenuRequested ) { trap_Cmd_ExecuteText( EXEC_NOW, "gamemenu\n" ); } cgs.gameMenuRequested = true; } } if( !cg.viewFrameCount ) cg.firstViewRealTime = cg.realTime; CG_FlashGameWindow(); // notify player of important game events CG_CalcVrect(); // find sizes of the 3d drawing screen CG_TileClear(); // clear any dirty part of the background CG_ChaseCamButtons(); CG_RunLightStyles(); CG_ClearFragmentedDecals(); trap_R_ClearScene(); if( CG_DemoCam_Update() ) CG_SetupViewDef( &cg.view, CG_DemoCam_GetViewType(), flipped ); else CG_SetupViewDef( &cg.view, VIEWDEF_PLAYERVIEW, flipped ); CG_LerpEntities(); // interpolate packet entities positions CG_CalcViewWeapon( &cg.weapon ); CG_FireEvents( false ); CG_AddEntities(); CG_AddViewWeapon( &cg.weapon ); CG_AddLocalEntities(); CG_AddParticles(); CG_AddDlights(); CG_AddShadeBoxes(); CG_AddDecals(); CG_AddPolys(); CG_AddLightStyles(); #ifndef PUBLIC_BUILD CG_AddTest(); #endif // offset vieworg appropriately if we're doing stereo separation VectorMA( cg.view.origin, stereo_separation, &cg.view.axis[AXIS_RIGHT], rd->vieworg ); // never let it sit exactly on a node line, because a water plane can // disappear when viewed with the eye exactly on it. // the server protocol only specifies to 1/16 pixel, so add 1/16 in each axis rd->vieworg[0] += 1.0/PM_VECTOR_SNAP; rd->vieworg[1] += 1.0/PM_VECTOR_SNAP; rd->vieworg[2] += 1.0/PM_VECTOR_SNAP; AnglesToAxis( cg.view.angles, rd->viewaxis ); rd->rdflags = CG_RenderFlags(); // warp if underwater if( rd->rdflags & RDF_UNDERWATER ) { float phase = rd->time * 0.001 * WAVE_FREQUENCY * M_TWOPI; float v = WAVE_AMPLITUDE * ( sin( phase ) - 1.0 ) + 1; rd->fov_x *= v; rd->fov_y *= v; } CG_AddLocalSounds(); CG_SetSceneTeamColors(); // update the team colors in the renderer trap_R_RenderScene( &cg.view.refdef ); cg.oldAreabits = true; trap_S_Update( cg.view.origin, cg.view.velocity, cg.view.axis, cgs.clientInfo[cgs.playerNum].name ); CG_Draw2D(); CG_ResetTemporaryBoneposesCache(); // clear for next frame cg.viewFrameCount++; }