diff --git a/code/cgame/cg_draw.c b/code/cgame/cg_draw.c index aa22657..3671b78 100644 --- a/code/cgame/cg_draw.c +++ b/code/cgame/cg_draw.c @@ -740,14 +740,17 @@ static float CG_DrawFPS( float y ) { static int previous; int t, frameTime; - // don't use serverTime, because that will be drifting to - // correct for internet lag changes, timescales, timedemos, etc - t = trap_Milliseconds(); - frameTime = t - previous; - previous = t; - - previousTimes[index % FPS_FRAMES] = frameTime; - index++; + if (cg.viewport == 0) { + // don't use serverTime, because that will be drifting to + // correct for internet lag changes, timescales, timedemos, etc + t = trap_Milliseconds(); + frameTime = t - previous; + previous = t; + + previousTimes[index % FPS_FRAMES] = frameTime; + index++; + } + if ( index > FPS_FRAMES ) { // average multiple frames together to smooth changes out a bit total = 0; diff --git a/code/cgame/cg_drawtools.c b/code/cgame/cg_drawtools.c index c0ce1ef..c17b6af 100644 --- a/code/cgame/cg_drawtools.c +++ b/code/cgame/cg_drawtools.c @@ -31,17 +31,55 @@ Adjusted for resolution and screen aspect ratio ================ */ void CG_AdjustFrom640( float *x, float *y, float *w, float *h ) { -#if 0 - // adjust for wide screens - if ( cgs.glconfig.vidWidth * 480 > cgs.glconfig.vidHeight * 640 ) { - *x += 0.5 * ( cgs.glconfig.vidWidth - ( cgs.glconfig.vidHeight * 640 / 480 ) ); + int viewXBias = 0; + + if (cg.numViewports != 1 && cg.snap) { + qboolean right = qfalse; + qboolean down = qfalse; + + if (cg.numViewports == 2) { + if (cg.viewport == 1) { + down = qtrue; + } + } + else if (cg.numViewports == 3 && cg.viewport == 2) { + down = qtrue; + } + else if (cg.numViewports <= 4) { + if (cg.viewport == 1 || cg.viewport == 3) { + right = qtrue; + } + if (cg.viewport == 2 || cg.viewport == 3) { + down = qtrue; + } + } + + if (cg.viewport != 0 && (cg.numViewports == 2 || cg.numViewports == 3) && cg.splitviewVertical) { + right = !right; + down = !down; + } + + if (right) { + viewXBias = 2; + *x += SCREEN_WIDTH; + } + if (down) { + *y += SCREEN_HEIGHT; + } } -#endif + // scale for screen sizes *x *= cgs.screenXScale; - *y *= cgs.screenYScale; *w *= cgs.screenXScale; + + *x += cgs.screenXBias*(viewXBias); + + *y *= cgs.screenYScale; *h *= cgs.screenYScale; + + // Offset for widescreen + *x += cgs.screenXBias; // center X + *y += cgs.screenYBias * 2; // Bottom Y } /* @@ -292,6 +330,10 @@ void CG_TileClear( void ) { int top, bottom, left, right; int w, h; + if ( cg.viewport != 0 ) { + return; + } + w = cgs.glconfig.vidWidth; h = cgs.glconfig.vidHeight; diff --git a/code/cgame/cg_local.h b/code/cgame/cg_local.h index 4c4f6ea..c243a44 100644 --- a/code/cgame/cg_local.h +++ b/code/cgame/cg_local.h @@ -643,6 +643,11 @@ typedef struct { char testModelName[MAX_QPATH]; qboolean testGun; + // + int splitviewVertical; + int numViewports; + int viewport; + int viewportAngleAdd; } cg_t; @@ -999,6 +1004,7 @@ typedef struct { float screenXScale; // derived from glconfig float screenYScale; float screenXBias; + float screenYBias; int serverCommandSequence; // reliable command stream counter int processedSnapshotNum;// the number of snapshots cgame has requested @@ -1193,6 +1199,10 @@ extern vmCvar_t cg_recordSPDemoName; extern vmCvar_t cg_obeliskRespawnDelay; #endif +extern vmCvar_t cg_viewports; +extern vmCvar_t cg_viewportSplitVertical; +extern vmCvar_t cg_viewportAngleAdd; + // // cg_main.c // diff --git a/code/cgame/cg_main.c b/code/cgame/cg_main.c index 3b1f77b..124e51e 100644 --- a/code/cgame/cg_main.c +++ b/code/cgame/cg_main.c @@ -198,6 +198,11 @@ vmCvar_t cg_recordSPDemoName; vmCvar_t cg_obeliskRespawnDelay; #endif +vmCvar_t cg_viewports; +vmCvar_t cg_viewportSplitVertical; +vmCvar_t cg_viewportAngleAdd; + + typedef struct { vmCvar_t *vmCvar; char *cvarName; @@ -307,6 +312,10 @@ static cvarTable_t cvarTable[] = { { &cg_smoothClients, "cg_smoothClients", "0", CVAR_USERINFO | CVAR_ARCHIVE}, { &cg_cameraMode, "com_cameraMode", "0", CVAR_CHEAT}, + { &cg_viewports, "cg_viewports", "4", CVAR_CHEAT}, + { &cg_viewportSplitVertical, "cg_viewportSplitVertical", "0", CVAR_CHEAT}, + { &cg_viewportAngleAdd, "cg_viewportAngleAdd", "90", CVAR_CHEAT}, + { &pmove_fixed, "pmove_fixed", "0", CVAR_SYSTEMINFO}, { &pmove_msec, "pmove_msec", "8", CVAR_SYSTEMINFO}, { &cg_noTaunt, "cg_noTaunt", "0", CVAR_ARCHIVE}, diff --git a/code/cgame/cg_view.c b/code/cgame/cg_view.c index 0fc763a..2dc04b3 100644 --- a/code/cgame/cg_view.c +++ b/code/cgame/cg_view.c @@ -181,8 +181,90 @@ Sets the coordinates of the rendered window */ static void CG_CalcVrect (void) { int size; + float viewWidth, viewHeight; + float viewX, viewY; - // the intermission should allways be full screen + // Viewport size + viewX = viewY = 0; + viewWidth = cgs.glconfig.vidWidth; + viewHeight = cgs.glconfig.vidHeight; + + // Splitscreen viewports + if (cg.numViewports == 2) { + if (cg.splitviewVertical) { + viewWidth *= 0.5f; + + if (cg.viewport == 1) { + viewX += viewWidth; + } + } else { + viewHeight *= 0.5f; + + if (cg.viewport == 1) { + viewY += viewHeight; + } + } + } else if (cg.numViewports == 3) { + if (cg.splitviewVertical) { + if (cg.viewport == 2) { + viewWidth *= 0.5f; + viewX += viewWidth; + } else { + viewWidth *= 0.5f; + viewHeight *= 0.5f; + + if (cg.viewport == 1) { + viewY += viewHeight; + } + } + } else { + if (cg.viewport == 2) { + viewHeight *= 0.5f; + viewY += viewHeight; + } else { + viewWidth *= 0.5f; + viewHeight *= 0.5f; + + if (cg.viewport == 1) { + viewX += viewWidth; + } + } + } + } else if (cg.numViewports > 1 && cg.numViewports <= 4) { + viewWidth *= 0.5f; + viewHeight *= 0.5f; + + if (cg.viewport == 1 || cg.viewport == 3) { + viewX += viewWidth; + } + + if (cg.viewport == 2 || cg.viewport == 3) { + viewY += viewHeight; + } + } + + // Viewport scale and offset + //cgs.screenXScale = viewWidth * (1.0/640.0); + //cgs.screenYScale = viewHeight * (1.0/480.0); + if ( viewWidth * 480 > viewHeight * 640 ) { + cgs.screenXScale = viewWidth * (1.0/640.0); + cgs.screenYScale = viewHeight * (1.0/480.0); + // wide screen + cgs.screenXBias = 0.5 * ( viewWidth - ( viewHeight * (640.0/480.0) ) ); + cgs.screenXScale = cgs.screenYScale; + // no narrow screen + cgs.screenYBias = 0; + } else { + cgs.screenXScale = viewWidth * (1.0/640.0); + cgs.screenYScale = viewHeight * (1.0/480.0); + // narrow screen + cgs.screenYBias = 0.5 * ( viewHeight - ( viewWidth * (480.0/640.0) ) ); + cgs.screenYScale = cgs.screenXScale; + // no wide screen + cgs.screenXBias = 0; + } + + // the intermission should always be full screen if ( cg.snap->ps.pm_type == PM_INTERMISSION ) { size = 100; } else { @@ -196,16 +278,17 @@ static void CG_CalcVrect (void) { } else { size = cg_viewsize.integer; } - } - cg.refdef.width = cgs.glconfig.vidWidth*size/100; + + // Rendered window for drawing world + cg.refdef.width = viewWidth*size/100; cg.refdef.width &= ~1; - cg.refdef.height = cgs.glconfig.vidHeight*size/100; + cg.refdef.height = viewHeight*size/100; cg.refdef.height &= ~1; - cg.refdef.x = (cgs.glconfig.vidWidth - cg.refdef.width)/2; - cg.refdef.y = (cgs.glconfig.vidHeight - cg.refdef.height)/2; + cg.refdef.x = viewX + (viewWidth - cg.refdef.width)/2; + cg.refdef.y = viewY + (viewHeight - cg.refdef.height)/2; } //============================================================================== @@ -254,8 +337,8 @@ static void CG_OffsetThirdPersonView( void ) { AngleVectors( cg.refdefViewAngles, forward, right, up ); - forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI ); - sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI ); + forwardScale = cos( ( cg_thirdPersonAngle.value + cg.viewportAngleAdd ) / 180 * M_PI ); + sideScale = sin( ( cg_thirdPersonAngle.value + cg.viewportAngleAdd ) / 180 * M_PI ); VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view ); VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view ); @@ -286,7 +369,7 @@ static void CG_OffsetThirdPersonView( void ) { focusDist = 1; // should never happen } cg.refdefViewAngles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist ); - cg.refdefViewAngles[YAW] -= cg_thirdPersonAngle.value; + cg.refdefViewAngles[YAW] -= cg_thirdPersonAngle.value + cg.viewportAngleAdd; } @@ -330,11 +413,13 @@ static void CG_OffsetFirstPersonView( void ) { if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) { angles[ROLL] = 40; angles[PITCH] = -15; - angles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW]; + angles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW] + cg.viewportAngleAdd; origin[2] += cg.predictedPlayerState.viewheight; return; } + angles[YAW] += cg.viewportAngleAdd; + // add angles based on damage kick if ( cg.damageTime ) { ratio = cg.time - cg.damageTime; @@ -798,6 +883,12 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo // decide on third person view cg.renderingThirdPerson = cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0); + // + cg.numViewports = Com_Clamp(1, 4, cg_viewports.integer); + cg.splitviewVertical = cg_viewportSplitVertical.integer; + cg.viewport = 0; + cg.viewportAngleAdd = 0; + // build cg.refdef inwater = CG_CalcViewValues(); @@ -864,6 +955,36 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo // actually issue the rendering calls CG_DrawActive( stereoView ); + for ( cg.viewport = 1; cg.viewport < cg.numViewports; cg.viewport++ ) { + cg.viewportAngleAdd = cg.viewport * cg_viewportAngleAdd.integer; + + // decide on third person view + cg.renderingThirdPerson = cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0); + + // build cg.refdef + inwater = CG_CalcViewValues(); + + // first person blend blobs, done after AnglesToAxis + if ( !cg.renderingThirdPerson ) { + CG_DamageBlendBlob(); + } + + // build the render lists + if ( !cg.hyperspace ) { + CG_AddPacketEntities(); // adter calcViewValues, so predicted player state is correct + CG_AddMarks(); + CG_AddParticles (); + CG_AddLocalEntities(); + } + CG_AddViewWeapon( &cg.predictedPlayerState ); + + cg.refdef.time = cg.time; + memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) ); + + // drawn the second world scene + CG_DrawActive( stereoView ); + } + if ( cg_stats.integer ) { CG_Printf( "cg.clientFrame:%i\n", cg.clientFrame ); }