summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cgame/cg_local.h7
-rw-r--r--src/cgame/cg_main.c14
-rw-r--r--src/cgame/cg_view.c238
3 files changed, 164 insertions, 95 deletions
diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h
index 55df1873..0169ff77 100644
--- a/src/cgame/cg_local.h
+++ b/src/cgame/cg_local.h
@@ -1484,8 +1484,13 @@ extern vmCvar_t cg_tracerWidth;
extern vmCvar_t cg_tracerLength;
extern vmCvar_t cg_autoswitch;
extern vmCvar_t cg_thirdPerson;
+extern vmCvar_t cg_thirdPersonShoulderViewMode;
+extern vmCvar_t cg_thirdPersonPitchFollow;
extern vmCvar_t cg_thirdPersonRange;
-extern vmCvar_t cg_thirdPersonShoulderView;
+extern vmCvar_t cg_shoulderViewOverride;
+extern vmCvar_t cg_shoulderViewUp;
+extern vmCvar_t cg_shoulderViewRight;
+extern vmCvar_t cg_shoulderViewForward;
extern vmCvar_t cg_stereoSeparation;
extern vmCvar_t cg_lagometer;
extern vmCvar_t cg_synchronousClients;
diff --git a/src/cgame/cg_main.c b/src/cgame/cg_main.c
index 94e7c237..999a582e 100644
--- a/src/cgame/cg_main.c
+++ b/src/cgame/cg_main.c
@@ -142,8 +142,13 @@ vmCvar_t cg_tracerWidth;
vmCvar_t cg_tracerLength;
vmCvar_t cg_autoswitch;
vmCvar_t cg_thirdPerson;
-vmCvar_t cg_thirdPersonShoulderView;
+vmCvar_t cg_thirdPersonShoulderViewMode;
+vmCvar_t cg_thirdPersonPitchFollow;
vmCvar_t cg_thirdPersonRange;
+vmCvar_t cg_shoulderViewOverride;
+vmCvar_t cg_shoulderViewUp;
+vmCvar_t cg_shoulderViewRight;
+vmCvar_t cg_shoulderViewForward;
vmCvar_t cg_stereoSeparation;
vmCvar_t cg_lagometer;
vmCvar_t cg_synchronousClients;
@@ -263,8 +268,13 @@ static cvarTable_t cvarTable[ ] =
{ &cg_tracerWidth, "cg_tracerwidth", "1", CVAR_CHEAT },
{ &cg_tracerLength, "cg_tracerlength", "100", CVAR_CHEAT },
{ &cg_thirdPersonRange, "cg_thirdPersonRange", "75", CVAR_ARCHIVE },
- { &cg_thirdPersonShoulderView, "cg_thirdPersonShoulderView", "0", CVAR_ARCHIVE },
{ &cg_thirdPerson, "cg_thirdPerson", "0", CVAR_CHEAT },
+ { &cg_thirdPersonPitchFollow, "cg_thirdPersonPitchFollow", "0", 0 },
+ { &cg_thirdPersonShoulderViewMode, "cg_thirdPersonShoulderViewMode", "1", CVAR_ARCHIVE },
+ { &cg_shoulderViewOverride, "cg_shoulderViewOverride", "0", 0 },
+ { &cg_shoulderViewUp, "cg_shoulderViewUp", "0", 0 },
+ { &cg_shoulderViewRight, "cg_shoulderViewRight", "0", 0 },
+ { &cg_shoulderViewForward, "cg_shoulderViewForward", "0", 0 },
{ &cg_stats, "cg_stats", "0", 0 },
{ &cg_drawFriend, "cg_drawFriend", "1", CVAR_ARCHIVE },
{ &cg_teamChatsOnly, "cg_teamChatsOnly", "0", CVAR_ARCHIVE },
diff --git a/src/cgame/cg_view.c b/src/cgame/cg_view.c
index be6d492c..28ef9411 100644
--- a/src/cgame/cg_view.c
+++ b/src/cgame/cg_view.c
@@ -282,13 +282,12 @@ void CG_OffsetThirdPersonView( void )
float deltaPitch;
static float pitch;
-
-
- // If cg_thirdpersonShoulderView, run that function instead
- // If we're chasing a wallwalker, do shoulder view regardless
- // because actual third person creates lots of problems otherwise.
- if( ( cg_thirdPersonShoulderView.integer ) ||
- ( ( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) &&
+ // If cg_thirdpersonShoulderViewMode == 2, do shoulder view instead
+ // If cg_thirdpersonShoulderViewMode == 1, do shoulder view when chasing
+ // a wallwalker because it's really erratic to watch
+ if( ( cg_thirdPersonShoulderViewMode.integer == 2 ) ||
+ ( ( cg_thirdPersonShoulderViewMode.integer == 1 ) &&
+ ( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) &&
( cg.snap->ps.stats[ STAT_HEALTH ] > 0 ) ) )
{
CG_OffsetShoulderView( );
@@ -296,96 +295,107 @@ void CG_OffsetThirdPersonView( void )
}
BG_GetClientNormal( &cg.predictedPlayerState, surfNormal );
- // set the view origin to the class's view height
+ // Set the view origin to the class's view height
VectorMA( cg.refdef.vieworg, cg.predictedPlayerState.viewheight, surfNormal, cg.refdef.vieworg );
- // set the focus point where the camera will look (at the player's vieworg)
+ // Set the focus point where the camera will look (at the player's vieworg)
VectorCopy( cg.refdef.vieworg, focusPoint );
- // if player is dead, we want the player to be between us and the killer
- // so pretend that the player was looking at the killer, then place cam behind them
- // FIXME: Still fails to see killer when killer is above/below or killer moves
- // out of view (relative to the dead player)
+ // If player is dead, we want the player to be between us and the killer
+ // so pretend that the player was looking at the killer, then place cam behind them.
+ // FIXME: This still fails to see killer when killer is above/below or killer moves
+ // out of view (relative to the dead player).
if( cg.predictedPlayerState.stats[ STAT_HEALTH ] <= 0 )
cg.refdefViewAngles[ YAW ] = cg.predictedPlayerState.stats[ STAT_VIEWLOCK ];
- // Calculate the angle of the camera's position around the player
- // unless in demo, PLAYING in third person, or in dead-third-person cam, allow the player
- // to control camera position offsets using the mouse position
+ // get and rangecheck cg_thirdPersonRange
+ range = cg_thirdPersonRange.value;
+ if( range > 150.0f ) range = 150.0f;
+ if( range < 30.0f ) range = 30.0f;
+
+ // Calculate the angle of the camera's position around the player.
+ // Unless in demo, PLAYING in third person, or in dead-third-person cam, allow the player
+ // to control camera position offsets using the mouse position.
if( cg.demoPlayback ||
( ( cg.snap->ps.pm_flags & PMF_FOLLOW ) &&
( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 ) ) )
{
- // collect our input values from the mouse
+ // Collect our input values from the mouse.
cmdNum = trap_GetCurrentCmdNumber();
trap_GetUserCmd( cmdNum, &cmd );
trap_GetUserCmd( cmdNum - 1, &oldCmd );
-
- // prevent pitch from wrapping and clamp it within a [-75, 90] range
- // cgame has no access to ps.delta_angles[] here, so we need to reproduce
- // it ourselves
+ // Prevent pitch from wrapping and clamp it within a [-75, 90] range.
+ // Cgame has no access to ps.delta_angles[] here, so we need to reproduce
+ // it ourselves.
deltaPitch = SHORT2ANGLE( cmd.angles[ PITCH ] - oldCmd.angles[ PITCH ] );
if( fabs(deltaPitch) < 200.0f )
{
- if( pitch + deltaPitch > 90.0f ) pitch = 90.0f;
- else if( pitch + deltaPitch < -75.0f ) pitch = -75.0f;
- else pitch += deltaPitch;
+ pitch += deltaPitch;
+ AngleNormalize180( pitch );
}
mouseInputAngles[ PITCH ] = pitch;
- mouseInputAngles[ YAW ] = -1.0f * SHORT2ANGLE( cmd.angles[ YAW ] ); //yaw is inverted
+ mouseInputAngles[ YAW ] = -1.0f * SHORT2ANGLE( cmd.angles[ YAW ] ); // yaw is inverted
mouseInputAngles[ ROLL ] = 0.0f;
+ for( i = 0; i < 3; i++ )
+ AngleNormalize180( mouseInputAngles[ i ] );
+
// Set the rotation angles to be the view angles offset by the mouse input
- // Ignore the original pitch though, it's buggy
- cg.refdefViewAngles[ PITCH ] = 0.0f;
+ // Ignore the original pitch though; it's too jerky otherwise
+ if( !cg_thirdPersonPitchFollow.integer )
+ cg.refdefViewAngles[ PITCH ] = 0.0f;
+
for( i = 0; i < 3; i++ )
{
rotationAngles[ i ] = cg.refdefViewAngles[ i ] + mouseInputAngles[ i ];
AngleNormalize180( rotationAngles[ i ] );
}
+
+ // Don't let pitch go too high/too low or the camera flips around and
+ // that's really annoying.
+ // However, when we're not on the floor or ceiling (wallwalk) pitch
+ // may not be pitch, so just let it go.
+ if( surfNormal[ 2 ] > 0.5 || surfNormal[ 2 ] < -0.5 )
+ {
+ if( rotationAngles[ PITCH ] > 85 )
+ rotationAngles[ PITCH ] = 85;
+ else if( rotationAngles[ PITCH ] < -85 )
+ rotationAngles[ PITCH ] = 85;
+ }
+
+ // Perform the rotations specified by rotationAngles.
+ AnglesToAxis( rotationAngles, axis );
+ if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) ||
+ !BG_RotateAxis( cg.snap->ps.grapplePoint, axis, rotaxis, qfalse,
+ cg.snap->ps.eFlags & EF_WALLCLIMBCEILING ) )
+ AxisCopy( axis, rotaxis );
+
+ // Convert the new axis back to angles.
+ AxisToAngles( rotaxis, rotationAngles );
+
+ for( i = 0; i < 3; i++ )
+ AngleNormalize180( rotationAngles[ i ] );
}
else
{
// If we're playing the game in third person, the viewangles already
- // take care of our mouselook, so just use them
+ // take care of our mouselook, so just use them.
for( i = 0; i < 3; i++ )
rotationAngles[ i ] = cg.refdefViewAngles[ i ];
}
- // get and rangecheck cg_thirdPersonRange
- range = cg_thirdPersonRange.value;
- if( range > 150.0f ) range = 150.0f;
- if( range < 30.0f ) range = 30.0f;
-
-
- // calculate the camera position relative to the player's viewaxis
-
- // perform the rotations specified by rotationAngles
- AnglesToAxis( rotationAngles, axis );
- if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) ||
- !BG_RotateAxis( cg.snap->ps.grapplePoint, axis, rotaxis, qfalse,
- cg.snap->ps.eFlags & EF_WALLCLIMBCEILING ) )
- AxisCopy( axis, rotaxis );
-
- // convert the new axis back to angles
- AxisToAngles( rotaxis, rotationAngles );
-
- // force angles to -180 <= x <= 180
- for( i = 0; i < 3; i++ )
- AngleNormalize180( rotationAngles[ i ] );
-
- // move the camera range distance back
+ // Move the camera range distance back.
AngleVectors( rotationAngles, forward, right, up );
VectorCopy( cg.refdef.vieworg, view );
VectorMA( view, -range, forward, view );
- // ensure that the current camera position isn't out of bounds and that there
- // is nothing between the camera and the player
+ // Ensure that the current camera position isn't out of bounds and that there
+ // is nothing between the camera and the player.
if( !cg_cameraMode.integer )
{
- // trace a ray from the origin to the viewpoint to make sure the view isn't
+ // Trace a ray from the origin to the viewpoint to make sure the view isn't
// in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
@@ -393,18 +403,18 @@ void CG_OffsetThirdPersonView( void )
{
VectorCopy( trace.endpos, view );
view[ 2 ] += ( 1.0f - trace.fraction ) * 32;
- // try another trace to this position, because a tunnel may have the ceiling
- // close enogh that this is poking out
+ // Try another trace to this position, because a tunnel may have the ceiling
+ // close enogh that this is poking out.
CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
VectorCopy( trace.endpos, view );
}
}
- // set the camera position to what we calculated
+ // Set the camera position to what we calculated.
VectorCopy( view, cg.refdef.vieworg );
- // the above checks may have moved the camera such that the existing viewangles
+ // The above checks may have moved the camera such that the existing viewangles
// may not still face the player. Recalculate them to do so.
VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint );
vectoangles( focusPoint, cg.refdefViewAngles );
@@ -420,92 +430,136 @@ CG_OffsetShoulderView
void CG_OffsetShoulderView( void )
{
int i;
- vec3_t surfNormal;
int cmdNum;
usercmd_t cmd, oldCmd;
vec3_t rotationAngles;
vec3_t axis[ 3 ], rotaxis[ 3 ];
- float deltaPitch;
- static float pitch;
+ float deltaMousePitch;
+ static float mousePitch;
+ vec3_t forward, right, up;
+ float classFwdOffset = 0.0f, classUpOffset = 0.0f, classRightOffset = 0.0f;
+ AngleVectors( cg.refdefViewAngles, forward, right, up );
- BG_GetClientNormal( &cg.predictedPlayerState, surfNormal );
-
- // boost the camera up a little bit from the vieworigin so you aren't inside
+ // Set a nice view by offsetting from vieworigin to get to the "shoulder"
+ // for each class.
+ // FIXME: These need to not be hard-coded so hackishly
switch( cg.snap->ps.stats[ STAT_CLASS ] )
{
+ case PCL_ALIEN_BUILDER0:
+ case PCL_ALIEN_BUILDER0_UPG:
+ classFwdOffset = -8.0f;
+ classRightOffset = 15.0f;
+ classUpOffset = 13.0f;
+ break;
case PCL_ALIEN_LEVEL0:
- VectorMA( cg.refdef.vieworg, 15.0f, surfNormal, cg.refdef.vieworg );
+ classFwdOffset = -5.0f;
+ classRightOffset = 0.0f;
+ classUpOffset = 17.0f;
break;
case PCL_ALIEN_LEVEL1:
case PCL_ALIEN_LEVEL1_UPG:
- VectorMA( cg.refdef.vieworg, 20.0f, surfNormal, cg.refdef.vieworg );
+ classFwdOffset = -10.0f;
+ classRightOffset = 0.0f;
+ classUpOffset = 18.0f;
break;
case PCL_ALIEN_LEVEL2:
case PCL_ALIEN_LEVEL2_UPG:
- VectorMA( cg.refdef.vieworg, 10.0f, surfNormal, cg.refdef.vieworg );
+ classFwdOffset = 0.0f;
+ classRightOffset = 12.0f;
+ classUpOffset = 5.0f;
break;
case PCL_ALIEN_LEVEL3:
- VectorMA( cg.refdef.vieworg, 8.0f, surfNormal, cg.refdef.vieworg );
+ classFwdOffset = -10.0f;
+ classRightOffset = 15.0f;
+ classUpOffset = 8.0f;
break;
case PCL_ALIEN_LEVEL3_UPG:
- VectorMA( cg.refdef.vieworg, 15.0f, surfNormal, cg.refdef.vieworg );
+ classFwdOffset = -10.0f;
+ classRightOffset = 17.0f;
+ classUpOffset = 12.0f;
break;
case PCL_ALIEN_LEVEL4:
- VectorMA( cg.refdef.vieworg, 35.0f, surfNormal, cg.refdef.vieworg );
+ classFwdOffset = -20.0f;
+ classRightOffset = -25.0f;
+ classUpOffset = 30.0f;
break;
case PCL_HUMAN:
- VectorMA( cg.refdef.vieworg, 20.0f, surfNormal, cg.refdef.vieworg );
+ classFwdOffset = -10.0f;
+ classRightOffset = 15.0f;
+ classUpOffset = 0.0f;
break;
case PCL_HUMAN_BSUIT:
- VectorMA( cg.refdef.vieworg, 26.0f, surfNormal, cg.refdef.vieworg );
+ classFwdOffset = -30.0f;
+ classRightOffset = 25.0f;
+ classUpOffset = -2.0f;
break;
}
- if( !( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
+ // The override is temporary so that people can help find good offset positions for me.
+ // It will not remain in final code.
+ if( !cg_shoulderViewOverride.integer )
+ {
+ VectorMA( cg.refdef.vieworg, classFwdOffset, forward, cg.refdef.vieworg );
+ VectorMA( cg.refdef.vieworg, classUpOffset, up, cg.refdef.vieworg );
+ VectorMA( cg.refdef.vieworg, classRightOffset, right, cg.refdef.vieworg );
+ }
+ else
+ {
+ VectorMA( cg.refdef.vieworg, cg_shoulderViewForward.value, forward, cg.refdef.vieworg );
+ VectorMA( cg.refdef.vieworg, cg_shoulderViewUp.value, up, cg.refdef.vieworg );
+ VectorMA( cg.refdef.vieworg, cg_shoulderViewRight.value, right, cg.refdef.vieworg );
+ }
+
+ // If someone is playing like this, the rest is already taken care of
+ // so just get the firstperson effects and leave.
+ if( !cg.demoPlayback && !( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
{
CG_OffsetFirstPersonView();
return;
}
+ // Get mouse input for camera rotation.
cmdNum = trap_GetCurrentCmdNumber();
trap_GetUserCmd( cmdNum, &cmd );
trap_GetUserCmd( cmdNum - 1, &oldCmd );
- // prevent pitch from wrapping and clamp it within a [30, -50] range
- // cgame has no access to ps.delta_angles[] here, so we need to reproduce
- // it ourselves
- deltaPitch = SHORT2ANGLE( cmd.angles[ PITCH ] - oldCmd.angles[ PITCH ] );
- if( fabs(deltaPitch) < 200.0f )
- {
- if( pitch + deltaPitch > 30.0f ) pitch = 30.0f;
- else if( pitch + deltaPitch < -50.0f ) pitch = -50.0f;
- else pitch += deltaPitch;
- }
- rotationAngles[ PITCH ] = pitch;
- rotationAngles[ YAW ] = SHORT2ANGLE( cmd.angles[ YAW ] ) + cg.refdefViewAngles[ YAW ];
+ // Prevent pitch from wrapping and clamp it within a [30, -50] range.
+ // Cgame has no access to ps.delta_angles[] here, so we need to reproduce
+ // it ourselves here.
+ deltaMousePitch = SHORT2ANGLE( cmd.angles[ PITCH ] - oldCmd.angles[ PITCH ] );
+ if( fabs(deltaMousePitch) < 200.0f )
+ mousePitch += deltaMousePitch;
+
+ // Handle pitch.
+ rotationAngles[ PITCH ] = mousePitch;
+ // Ignore following pitch; it's too jerky otherwise.
+ if( cg_thirdPersonPitchFollow.integer )
+ mousePitch += cg.refdefViewAngles[ PITCH ];
+ AngleNormalize180( rotationAngles[ PITCH ] );
+ if( rotationAngles [ PITCH ] < -90.0f ) rotationAngles [ PITCH ] = -90.0f;
+ if( rotationAngles [ PITCH ] > 90.0f ) rotationAngles [ PITCH ] = 90.0f;
+
+ // Yaw and Roll are much easier.
+ rotationAngles[ YAW ] = SHORT2ANGLE( cmd.angles[ YAW ] ) + cg.refdefViewAngles[ YAW ];
rotationAngles[ ROLL ] = 0.0f;
- // convert viewangles -> axis
+ // Perform the rotations.
AnglesToAxis( rotationAngles, axis );
-
if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) ||
!BG_RotateAxis( cg.snap->ps.grapplePoint, axis, rotaxis, qfalse,
cg.snap->ps.eFlags & EF_WALLCLIMBCEILING ) )
AxisCopy( axis, rotaxis );
-
- // convert the new axis back to angles
AxisToAngles( rotaxis, rotationAngles );
- // force angles to -180 <= x <= 180
for( i = 0; i < 3; i++ )
AngleNormalize180( rotationAngles[ i ] );
- // actually set the viewangles
+ // Actually set the viewangles.
for( i = 0; i < 3; i++ )
cg.refdefViewAngles[ i ] = rotationAngles[ i ];
- // now run the first person stuff so we get various effects added
+ // Now run the first person stuff so we get various effects added.
CG_OffsetFirstPersonView( );
}