//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============ // // Purpose: // // $NoKeywords: $ //============================================================================= #include "hud.h" #include "cl_util.h" #include "camera.h" #include "kbutton.h" #include "cvardef.h" #include "usercmd.h" #include "const.h" #include "camera.h" #include "in_defs.h" #include "eiface.h" #include "pmtrace.h" float CL_KeyState( kbutton_t *key ); extern "C" { void DLLEXPORT CAM_Think( void ); int DLLEXPORT CL_IsThirdPerson( void ); void DLLEXPORT CL_CameraOffset( float *ofs ); } extern cl_enginefunc_t gEngfuncs; extern vec3_t realOrigin; extern float realViewOrg[3]; //-------------------------------------------------- Constants #define CAM_DIST_DELTA 1.0 #define CAM_ANGLE_DELTA 2.5 #define CAM_ANGLE_SPEED 2.5 #define CAM_MIN_DIST 30.0 #define CAM_ANGLE_MOVE .5 #define MAX_ANGLE_DIFF 10.0 #define PITCH_MAX 90.0 #define PITCH_MIN 0 #define YAW_MAX 135.0 #define YAW_MIN -135.0 enum ECAM_Command { CAM_COMMAND_NONE = 0, CAM_COMMAND_TOTHIRDPERSON = 1, CAM_COMMAND_TOFIRSTPERSON = 2 }; //-------------------------------------------------- Global Variables cvar_t *cam_command; cvar_t *cam_snapto; cvar_t *cam_idealyaw; cvar_t *cam_idealpitch; cvar_t *cam_idealdist; cvar_t *cam_contain; cvar_t *c_maxpitch; cvar_t *c_minpitch; cvar_t *c_maxyaw; cvar_t *c_minyaw; cvar_t *c_maxdistance; cvar_t *c_mindistance; cvar_t *cam_minDist; cvar_t *cam_maxDist; cvar_t *cam_smooth; cvar_t *cam_lookahead; static float camYaw; // pitch, yaw, dist vec3_t cam_ofs; // In third person int cam_thirdperson; int cam_mousemove; //true if we are moving the cam with the mouse, False if not int iMouseInUse = 0; int cam_distancemove; extern int mouse_x, mouse_y; //used to determine what the current x and y values are int cam_old_mouse_x, cam_old_mouse_y; //holds the last ticks mouse movement POINT cam_mouse; //-------------------------------------------------- Local Variables static kbutton_t cam_pitchup, cam_pitchdown, cam_yawleft, cam_yawright; static kbutton_t cam_in, cam_out, cam_move; //-------------------------------------------------- Prototypes void CAM_ToThirdPerson(void); void CAM_ToFirstPerson(void); void CAM_StartDistance(void); void CAM_EndDistance(void); //-------------------------------------------------- Local Functions float MoveToward( float cur, float goal, float maxspeed ) { if( cur != goal ) { if( fabs( cur - goal ) > 180.0 ) { if( cur < goal ) cur += 360.0; else cur -= 360.0; } if( cur < goal ) { if( cur < goal - 1.0 ) cur += ( goal - cur ) / 4.0; else cur = goal; } else { if( cur > goal + 1.0 ) cur -= ( cur - goal ) / 4.0; else cur = goal; } } // bring cur back into range if( cur < 0 ) cur += 360.0; else if( cur >= 360 ) cur -= 360; return cur; } //-------------------------------------------------- Gobal Functions typedef struct { vec3_t boxmins, boxmaxs;// enclose the test object along entire move float *mins, *maxs; // size of the moving object vec3_t mins2, maxs2; // size when clipping against mosnters float *start, *end; trace_t trace; int type; edict_t *passedict; qboolean monsterclip; } moveclip_t; extern trace_t SV_ClipMoveToEntity( edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end ); void DLLEXPORT CAM_Think( void ) { vec3_t origin; vec3_t ext, pnt, camForward, camRight, camUp; moveclip_t clip; float dist; vec3_t camAngles; float flSensitivity; #ifdef LATER int i; #endif vec3_t viewangles; switch( (int)cam_command->value ) { case CAM_COMMAND_TOTHIRDPERSON: CAM_ToThirdPerson(); break; case CAM_COMMAND_TOFIRSTPERSON: CAM_ToFirstPerson(); break; case CAM_COMMAND_NONE: default: break; } if( !cam_thirdperson ) return; #ifdef LATER if( cam_contain->value ) { gEngfuncs.GetClientOrigin( origin ); ext[0] = ext[1] = ext[2] = 0.0; } #endif if( cam_contain->value == 0 ) { cam_idealdist->value = cam_maxDist->value; } else { pmtrace_t *trace; Vector startPos = realOrigin + Vector( realViewOrg[0], realViewOrg[1], cam_minDist->value ); Vector endPos = realOrigin + Vector( realViewOrg[0], realViewOrg[1], cam_maxDist->value ); // Vector(realOrigin.x + realViewOrg[0], realOrigin.y + realViewOrg[1], realOrigin.z + c trace = gEngfuncs.PM_TraceLine( startPos, endPos, 1, 2, -1 ); float traceDist = trace->endpos.z - ( realOrigin.z + 25 ); // Have to add 25 because for whatever reason the trace puts the camera a bit above the object if( cam_smooth->value != 0 ) { cam_idealdist->value -= ( cam_idealdist->value - traceDist ) * ( 0.25f ); } else { cam_idealdist->value = traceDist; } } camAngles[PITCH] = cam_idealpitch->value; camAngles[YAW] = cam_idealyaw->value; dist = cam_idealdist->value; // //movement of the camera with the mouse // if( cam_mousemove ) { //get windows cursor position GetCursorPos( &cam_mouse ); //check for X delta values and adjust accordingly //eventually adjust YAW based on amount of movement //don't do any movement of the cam using YAW/PITCH if we are zooming in/out the camera if( !cam_distancemove ) { //keep the camera within certain limits around the player (ie avoid certain bad viewing angles) if( cam_mouse.x>gEngfuncs.GetWindowCenterX() ) { //if( ( camAngles[YAW] >= 225.0 ) || ( camAngles[YAW] < 135.0 ) ) if( camAngles[YAW] < c_maxyaw->value ) { camAngles[YAW] += CAM_ANGLE_MOVE * ( ( cam_mouse.x - gEngfuncs.GetWindowCenterX() ) / 2 ); } if( camAngles[YAW] > c_maxyaw->value ) { camAngles[YAW] = c_maxyaw->value; } } else if( cam_mouse.x 225.0 ) ) if( camAngles[YAW] > c_minyaw->value ) { camAngles[YAW] -= CAM_ANGLE_MOVE * ( ( gEngfuncs.GetWindowCenterX() - cam_mouse.x ) / 2 ); } if( camAngles[YAW] < c_minyaw->value ) { camAngles[YAW] = c_minyaw->value; } } //check for y delta values and adjust accordingly //eventually adjust PITCH based on amount of movement //also make sure camera is within bounds if( cam_mouse.y>gEngfuncs.GetWindowCenterY() ) { if( camAngles[PITCH] < c_maxpitch->value ) { camAngles[PITCH] += CAM_ANGLE_MOVE * ( ( cam_mouse.y - gEngfuncs.GetWindowCenterY() ) / 2 ); } if( camAngles[PITCH] > c_maxpitch->value ) { camAngles[PITCH] = c_maxpitch->value; } } else if( cam_mouse.y c_minpitch->value ) { camAngles[PITCH] -= CAM_ANGLE_MOVE * ( ( gEngfuncs.GetWindowCenterY() - cam_mouse.y ) / 2 ); } if( camAngles[PITCH] < c_minpitch->value ) { camAngles[PITCH] = c_minpitch->value; } } //set old mouse coordinates to current mouse coordinates //since we are done with the mouse if( ( flSensitivity = gHUD.GetSensitivity() ) != 0 ) { cam_old_mouse_x = cam_mouse.x * flSensitivity; cam_old_mouse_y = cam_mouse.y * flSensitivity; } else { cam_old_mouse_x = cam_mouse.x; cam_old_mouse_y = cam_mouse.y; } SetCursorPos( gEngfuncs.GetWindowCenterX(), gEngfuncs.GetWindowCenterY() ); } } //Nathan code here if( CL_KeyState( &cam_pitchup ) ) camAngles[PITCH] += CAM_ANGLE_DELTA; else if( CL_KeyState( &cam_pitchdown ) ) camAngles[PITCH] -= CAM_ANGLE_DELTA; if( CL_KeyState( &cam_yawleft ) ) camAngles[YAW] -= CAM_ANGLE_DELTA; else if( CL_KeyState( &cam_yawright ) ) camAngles[YAW] += CAM_ANGLE_DELTA; if( CL_KeyState( &cam_in ) ) { dist -= CAM_DIST_DELTA; if( dist < CAM_MIN_DIST ) { // If we go back into first person, reset the angle camAngles[PITCH] = 0; camAngles[YAW] = 0; dist = CAM_MIN_DIST; } } else if( CL_KeyState( &cam_out ) ) dist += CAM_DIST_DELTA; if( cam_distancemove ) { if( cam_mouse.y > gEngfuncs.GetWindowCenterY() ) { if( dist < c_maxdistance->value ) { dist += CAM_DIST_DELTA * ( ( cam_mouse.y - gEngfuncs.GetWindowCenterY() ) / 2); } if( dist > c_maxdistance->value ) { dist = c_maxdistance->value; } } else if( cam_mouse.y < gEngfuncs.GetWindowCenterY() ) { if( dist > c_mindistance->value ) { dist -= CAM_DIST_DELTA * ( ( gEngfuncs.GetWindowCenterY() - cam_mouse.y ) / 2 ); } if ( dist < c_mindistance->value ) { dist = c_mindistance->value; } } //set old mouse coordinates to current mouse coordinates //since we are done with the mouse cam_old_mouse_x = cam_mouse.x * gHUD.GetSensitivity(); cam_old_mouse_y = cam_mouse.y * gHUD.GetSensitivity(); SetCursorPos( gEngfuncs.GetWindowCenterX(), gEngfuncs.GetWindowCenterY() ); } #ifdef LATER if( cam_contain->value ) { // check new ideal VectorCopy( origin, pnt ); AngleVectors( camAngles, camForward, camRight, camUp ); for( i = 0; i < 3; i++ ) pnt[i] += -dist * camForward[i]; // check line from r_refdef.vieworg to pnt memset( &clip, 0, sizeof(moveclip_t) ); clip.trace = SV_ClipMoveToEntity( sv.edicts, r_refdef.vieworg, ext, ext, pnt ); if( clip.trace.fraction == 1.0 ) { // update ideal cam_idealpitch->value = camAngles[PITCH]; cam_idealyaw->value = camAngles[YAW]; cam_idealdist->value = dist; } } else #endif { // update ideal cam_idealpitch->value = camAngles[PITCH]; cam_idealyaw->value = camAngles[YAW]; cam_idealdist->value = dist; } // Move towards ideal VectorCopy( cam_ofs, camAngles ); gEngfuncs.GetViewAngles( (float *)viewangles ); if( cam_snapto->value ) { camAngles[YAW] = cam_idealyaw->value + camYaw; // + viewangles[YAW]; camAngles[PITCH] = cam_idealpitch->value + viewangles[PITCH]; camAngles[2] = cam_idealdist->value; } else { if( camAngles[YAW] - camYaw != cam_idealyaw->value ) camAngles[YAW] = MoveToward( camAngles[YAW], cam_idealyaw->value + camYaw, CAM_ANGLE_SPEED ); if( camAngles[PITCH] - viewangles[PITCH] != cam_idealpitch->value ) camAngles[PITCH] = MoveToward( camAngles[PITCH], cam_idealpitch->value + viewangles[PITCH], CAM_ANGLE_SPEED ); if( fabs( camAngles[2] - cam_idealdist->value ) < 2.0 ) camAngles[2] = cam_idealdist->value; else camAngles[2] += ( cam_idealdist->value - camAngles[2] ) / 4.0; } #ifdef LATER if( cam_contain->value ) { // Test new position dist = camAngles[ROLL]; camAngles[ROLL] = 0; VectorCopy( origin, pnt ); AngleVectors( camAngles, camForward, camRight, camUp ); for( i = 0; i < 3; i++ ) pnt[i] += -dist * camForward[i]; // check line from r_refdef.vieworg to pnt memset( &clip, 0, sizeof(moveclip_t) ); ext[0] = ext[1] = ext[2] = 0.0; clip.trace = SV_ClipMoveToEntity( sv.edicts, r_refdef.vieworg, ext, ext, pnt ); if( clip.trace.fraction != 1.0 ) return; } #endif cam_ofs[0] = camAngles[0]; cam_ofs[1] = camAngles[1]; cam_ofs[2] = dist; } extern void KeyDown( kbutton_t *b ); // HACK extern void KeyUp( kbutton_t *b ); // HACK void CAM_PitchUpDown( void ) { KeyDown( &cam_pitchup ); } void CAM_PitchUpUp( void ) { KeyUp( &cam_pitchup ); } void CAM_PitchDownDown( void ) { KeyDown( &cam_pitchdown ); } void CAM_PitchDownUp( void ) { KeyUp( &cam_pitchdown ); } void CAM_YawLeftDown( void ) { KeyDown( &cam_yawleft ); } void CAM_YawLeftUp( void ) { KeyUp( &cam_yawleft ); } void CAM_YawRightDown( void ) { KeyDown( &cam_yawright ); } void CAM_YawRightUp( void ) { KeyUp( &cam_yawright ); } void CAM_InDown( void ) { KeyDown( &cam_in ); } void CAM_InUp( void ) { KeyUp( &cam_in ); } void CAM_OutDown( void ) { KeyDown( &cam_out ); } void CAM_OutUp( void ) { KeyUp( &cam_out ); } void CAM_ToThirdPerson( void ) { vec3_t viewangles; gEngfuncs.GetViewAngles( (float *)viewangles ); if( !cam_thirdperson ) { cam_thirdperson = 1; cam_ofs[YAW] = viewangles[YAW]; cam_ofs[PITCH] = viewangles[PITCH]; cam_ofs[2] = CAM_MIN_DIST; } gEngfuncs.Cvar_SetValue( "cam_command", 0 ); } void CAM_ToFirstPerson( void ) { cam_thirdperson = 0; gEngfuncs.Cvar_SetValue( "cam_command", 0 ); } void CAM_ToggleSnapto( void ) { cam_snapto->value = !cam_snapto->value; } void CAM_Init( void ) { gEngfuncs.pfnAddCommand( "+campitchup", CAM_PitchUpDown ); gEngfuncs.pfnAddCommand( "-campitchup", CAM_PitchUpUp ); gEngfuncs.pfnAddCommand( "+campitchdown", CAM_PitchDownDown ); gEngfuncs.pfnAddCommand( "-campitchdown", CAM_PitchDownUp ); gEngfuncs.pfnAddCommand( "+camyawleft", CAM_YawLeftDown ); gEngfuncs.pfnAddCommand( "-camyawleft", CAM_YawLeftUp ); gEngfuncs.pfnAddCommand( "+camyawright", CAM_YawRightDown ); gEngfuncs.pfnAddCommand( "-camyawright", CAM_YawRightUp ); gEngfuncs.pfnAddCommand( "+camin", CAM_InDown ); gEngfuncs.pfnAddCommand( "-camin", CAM_InUp ); gEngfuncs.pfnAddCommand( "+camout", CAM_OutDown ); gEngfuncs.pfnAddCommand( "-camout", CAM_OutUp ); gEngfuncs.pfnAddCommand( "thirdperson", CAM_ToThirdPerson ); gEngfuncs.pfnAddCommand( "firstperson", CAM_ToFirstPerson ); gEngfuncs.pfnAddCommand( "+cammousemove",CAM_StartMouseMove); gEngfuncs.pfnAddCommand( "-cammousemove",CAM_EndMouseMove); gEngfuncs.pfnAddCommand( "+camdistance", CAM_StartDistance ); gEngfuncs.pfnAddCommand( "-camdistance", CAM_EndDistance ); gEngfuncs.pfnAddCommand( "snapto", CAM_ToggleSnapto ); cam_command = gEngfuncs.pfnRegisterVariable( "cam_command", "1", 0 ); // tells camera to go to thirdperson cam_snapto = gEngfuncs.pfnRegisterVariable( "cam_snapto", "0", 0 ); // snap to thirdperson view cam_idealyaw = gEngfuncs.pfnRegisterVariable( "cam_idealyaw", "0", 0 ); // thirdperson yaw cam_idealpitch = gEngfuncs.pfnRegisterVariable( "cam_idealpitch", "90", 0 ); // thirperson pitch cam_idealdist = gEngfuncs.pfnRegisterVariable( "cam_idealdist", "100", 0 ); // thirdperson distance cam_contain = gEngfuncs.pfnRegisterVariable( "cam_contain", "1", FCVAR_ARCHIVE ); // contain camera to world(previously unused/broken, repurposed for top-down stuff) c_maxpitch = gEngfuncs.pfnRegisterVariable( "c_maxpitch", "90.0", 0 ); c_minpitch = gEngfuncs.pfnRegisterVariable( "c_minpitch", "0.0", 0 ); c_maxyaw = gEngfuncs.pfnRegisterVariable( "c_maxyaw", "135.0", 0 ); c_minyaw = gEngfuncs.pfnRegisterVariable( "c_minyaw", "-135.0", 0 ); c_maxdistance = gEngfuncs.pfnRegisterVariable( "c_maxdistance", "200.0", 0 ); c_mindistance = gEngfuncs.pfnRegisterVariable( "c_mindistance", "30.0", 0 ); cam_minDist = gEngfuncs.pfnRegisterVariable( "cam_minDist", "150", FCVAR_ARCHIVE ); cam_maxDist = gEngfuncs.pfnRegisterVariable( "cam_maxDist", "500", FCVAR_ARCHIVE ); // Max dist if dynamic, normal distance if fixed cam_smooth = gEngfuncs.pfnRegisterVariable( "cam_smooth", "1", FCVAR_ARCHIVE ); // smooth camera movement cam_lookahead = gEngfuncs.pfnRegisterVariable( "cam_lookahead", "2", FCVAR_ARCHIVE ); // amount to "look ahead" towards the mouse } void CAM_ClearStates( void ) { vec3_t viewangles; gEngfuncs.GetViewAngles( (float *)viewangles ); cam_pitchup.state = 0; cam_pitchdown.state = 0; cam_yawleft.state = 0; cam_yawright.state = 0; cam_in.state = 0; cam_out.state = 0; cam_thirdperson = 1; cam_command->value = 0; cam_mousemove=0; cam_snapto->value = 0; cam_distancemove = 0; cam_ofs[0] = 0.0; cam_ofs[1] = 0.0; cam_ofs[2] = CAM_MIN_DIST; cam_idealpitch->value = viewangles[PITCH]; cam_idealyaw->value = viewangles[YAW]; cam_idealdist->value = CAM_MIN_DIST; } void CAM_StartMouseMove( void ) { float flSensitivity; //only move the cam with mouse if we are in third person. if( cam_thirdperson ) { //set appropriate flags and initialize the old mouse position //variables for mouse camera movement if( !cam_mousemove ) { cam_mousemove = 1; iMouseInUse = 1; GetCursorPos( &cam_mouse ); if( ( flSensitivity = gHUD.GetSensitivity() ) != 0 ) { cam_old_mouse_x = cam_mouse.x * flSensitivity; cam_old_mouse_y = cam_mouse.y * flSensitivity; } else { cam_old_mouse_x = cam_mouse.x; cam_old_mouse_y = cam_mouse.y; } } } //we are not in 3rd person view..therefore do not allow camera movement else { cam_mousemove = 0; iMouseInUse = 0; } } //the key has been released for camera movement //tell the engine that mouse camera movement is off void CAM_EndMouseMove( void ) { cam_mousemove = 0; iMouseInUse = 0; } //---------------------------------------------------------- //routines to start the process of moving the cam in or out //using the mouse //---------------------------------------------------------- void CAM_StartDistance( void ) { //only move the cam with mouse if we are in third person. if( cam_thirdperson ) { //set appropriate flags and initialize the old mouse position //variables for mouse camera movement if( !cam_distancemove ) { cam_distancemove = 1; cam_mousemove = 1; iMouseInUse = 1; GetCursorPos( &cam_mouse ); cam_old_mouse_x = cam_mouse.x * gHUD.GetSensitivity(); cam_old_mouse_y = cam_mouse.y * gHUD.GetSensitivity(); } } //we are not in 3rd person view..therefore do not allow camera movement else { cam_distancemove = 0; cam_mousemove = 0; iMouseInUse = 0; } } //the key has been released for camera movement //tell the engine that mouse camera movement is off void CAM_EndDistance( void ) { cam_distancemove = 0; cam_mousemove = 0; iMouseInUse = 0; } int DLLEXPORT CL_IsThirdPerson( void ) { return ( cam_thirdperson ? 1 : 0 ) || ( g_iUser1 && ( g_iUser2 == gEngfuncs.GetLocalPlayer()->index ) ); } void DLLEXPORT CL_CameraOffset( float *ofs ) { VectorCopy( cam_ofs, ofs ); }