Third-person camera

From Valve Developer Community
Jump to navigation Jump to search
English (en)Translate (Translate)

Do not use this on the Orange Box Engine

This is the old way of doing things. For the OrangeBox, please see Thirdperson-OrangeBox


Overview

This tutorial will teach you how to create a third person camera perspective with the following features:

  • Collision detection
  • Mouse-wheel zooming
  • Transparency when too close

Techniques Used

  • Hiding view model (Gordon's hands) in third person view
  • Hiding weapon in third person (or first) view
  • Hiding crosshair in third person (or first) view
  • Hiding player model when in first person view
  • Adding your own keyboard command

Thank you to the Third Person Camera articles on The Wavelength. This tutorial is a combination of both with a few user friendly modifications.

Step 1: in_camera.cpp

This tutorial assumes you are creating a Half-Life 2 Single Player modification and using Visual Studio .NET 2003.

Open up Game_Sdk.sln in VS.NET.

The first step will be to change the camera code inside in_camera.cpp (located in the client Project).

Comment the line:

#define CAM_MIN_DIST			36.0
// #define CAM_MIN_DIST    36.0

Under the line #define YAW_MIN -135.0

Add:

in_camera.cpp

#define CAM_MIN_DIST		16.0 // Don't let the camera get any closer than ...
#define CAM_MAX_DIST		96.0 // ... or any farther away than ...
#define CAM_SWITCH_DIST		96.0 // the default camera distance when switching 1st to 3rd person
#define CAM_HULL_OFFSET		6.0  // the size of the bounding hull used for collision checking
#define START_TRANS_DIST	40.0 // how close to player when it starts making model translucent
#define TRANS_DELTA	        1.9921875 // Set to 255 / START_TRANS_DIST

static Vector CAM_HULL_MIN(-CAM_HULL_OFFSET,-CAM_HULL_OFFSET,-CAM_HULL_OFFSET);
static Vector CAM_HULL_MAX( CAM_HULL_OFFSET, CAM_HULL_OFFSET, CAM_HULL_OFFSET);

Under the line: static kbutton_t cam_in, cam_out, cam_move;

Add:

float mf_NextSwitch;

Replace:

static ConVar cam_idealyaw( "cam_idealyaw", "90", FCVAR_ARCHIVE ); // thirdperson yaw

With:

static ConVar cam_idealyaw( "cam_idealyaw", "0", FCVAR_ARCHIVE ); /* thirdperson yaw
                                                                     | fix side view bug */

Here, all we are doing is change the CAM_MIN_DIST variable and adding our own variables to use later in the code. Instead of commenting out CAM_MIN_DIST you can just delete it. We also fixed a bug that makes the camera view the player from the side and not from behind (you will need to delete your config.cfg file for this to update and take effect).

Replace this section (under the function CAM_Think):

if( !m_fCameraInThirdPerson )
      return;

With this:

// Toggle first/3rd person, check every 0.5 seconds
if(gpGlobals->curtime >= mf_NextSwitch) {
	if(input->KeyState(&in_camchange))
	{
		mf_NextSwitch = gpGlobals->curtime + 0.5; /* Check if the key has been pressed every
                                                             0.5 seconds */
		if(!m_fCameraInThirdPerson)
		{
			CAM_ToThirdPerson();
		} else {
			CAM_ToFirstPerson();
		}
	}
}

if( !m_fCameraInThirdPerson )
{
	// If in FP mode, transparent
	// This is sort of a hack since when the map first loads you ARE transparent...
	// This is for when you switch from 3rd to 1st.
	if(C_BasePlayer::GetLocalPlayer() ) {
		C_BasePlayer::GetLocalPlayer()->SetRenderMode( kRenderTransColor );
		C_BasePlayer::GetLocalPlayer()->SetRenderColorA(0);
	}

	return;
}

Here we tell the code when to check if the user has pressed the in_camchange button (which we will define later on). We have also put in some code that will turn the player transparent when you switch FROM 3rd person TO 1st person. This is to prevent the player model from getting in the way of the camera.

Note.pngNote:You can change the 0.5 on this line: mf_NextSwitch = gpGlobals->curtime + 0.5 to a smaller number if you want to check more frequently if a user has pressed the "in_camchange" key.

Within this piece of code:

else if( input->KeyState( &cam_out ) ) {
	dist += CAM_DIST_DELTA;

}

Add right after dist += CAM_DIST_DELTA;:

		// Prevent the cam_out from going any further
		// NOTE: Without this, it _appears_ it's not moving, but
		// the transparency changes so it is... this fixes it.
		if( dist > CAM_MAX_DIST )
			dist = CAM_MAX_DIST;

Replace this:

if( cam_snapto.GetInt() )
	{
		camAngles[ YAW ] = cam_idealyaw.GetFloat() + viewangles[ YAW ];
		camAngles[ PITCH ] = cam_idealpitch.GetFloat() + viewangles[ PITCH ];
		camAngles[ 2 ] = cam_idealdist.GetFloat();
	}
	else
        {
         // code already here
}

With:

if( cam_snapto.GetInt() )
{
	camAngles[ YAW ] = cam_idealyaw.GetFloat() + viewangles[ YAW ];
	camAngles[ PITCH ] = cam_idealpitch.GetFloat() + viewangles[ PITCH ];
	camAngles[ 2 ] = cam_idealdist.GetFloat();
}
else
{
	if( camAngles[ YAW ] - viewangles[ YAW ] != cam_idealyaw.GetFloat() )
		camAngles[ YAW ] = MoveToward( camAngles[ YAW ],
					       cam_idealyaw.GetFloat() + viewangles[ YAW ],
                                               CAM_ANGLE_SPEED );
 
        if( camAngles[ PITCH ] - viewangles[ PITCH ] != cam_idealpitch.GetFloat() )
            camAngles[ PITCH ] = MoveToward( camAngles[ PITCH ],
                                             cam_idealpitch.GetFloat() + viewangles[ PITCH ],
                                             CAM_ANGLE_SPEED );
 
        if( !C_BasePlayer::GetLocalPlayer() ) {
            // this code can be hit from the main menu, where it will crash
            camAngles[ 2 ] = dist; // if there's no localplayer to calc from
        }
        else {
            trace_t tr;
            float adjDist = dist;
            C_BasePlayer* localPlayer = C_BasePlayer::GetLocalPlayer();

            Vector origin = localPlayer->GetLocalOrigin(); // find our player's origin
            origin += localPlayer->GetViewOffset(); // and from there, his eye position

            AngleVectors( QAngle(camAngles.x, camAngles.y, camAngles.z),
                         &camForward, NULL, NULL ); // get the forward vector

            UTIL_TraceHull( origin, origin - (camForward * dist),
                            CAM_HULL_MIN, CAM_HULL_MAX,
                            MASK_SOLID, NULL, &tr ); /* use our previously #defined hull to
                                                        collision trace */

            if( tr.fraction < 1.0 ) {
                adjDist = dist * tr.fraction; // move the camera closer if it hit something
            }
            else {
                adjDist = dist; // no trace hit, use cam_idealdist without adjusting it
            }

	    if ( adjDist < START_TRANS_DIST ) {
	        localPlayer->SetRenderMode( kRenderTransColor ); // make him translucent
		localPlayer->SetRenderColorA( (byte)(adjDist * TRANS_DELTA) ); // closer=less opacity
	    }

            if( adjDist < CAM_MIN_DIST )
                adjDist = CAM_MIN_DIST; // clamp up to minimum

            if( adjDist > CAM_MAX_DIST )
                adjDist = CAM_MAX_DIST; // clamp down to maximum

            camAngles[ 2 ] = adjDist;
        }
    }

Just under the above code you should see

	m_vecCameraOffset[ 0 ] = camAngles[ 0 ];
	m_vecCameraOffset[ 1 ] = camAngles[ 1 ];
	m_vecCameraOffset[ 2 ] = dist;

Replace

	m_vecCameraOffset[ 2 ] = dist;

With:

	m_vecCameraOffset[ 2 ] = camAngles[ 2 ];


Here we are checking for collision and handling transparency.

In the function void CInput::CAM_ToThirdPerson(void)

Change:

m_vecCameraOffset[ 2 ] = CAM_MIN_DIST;

To:

m_vecCameraOffset[ 2 ] = CAM_SWITCH_DIST; /* When switching, move camera to our specified switch
                                             distance */

Add underneath that:

		// Set model to 'player' because when a map first loads
		// it doesn't set the model... sort of a hack.
		//C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
		//pPlayer->SetModel("player");

NB: This code actually causes the game to crash when using the current HL2DM code, although the third person camera works fine without it. Uncomment these lines if you're experiencing problems and your player model is not being loaded, otherwise leave them commented.

Step 2: c_basecombatweapon.cpp

Open the file c_basecombatweapon.cpp under the client project.

Underneath this, in C_BaseCombatWeapon::DrawModel( int flags ):

// check if local player chases owner of this weapon in first person
C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer();

Add:

// Get rid of view model, crosshair and weapon in 3rd person
// Once we actually get weapons working, we'll let it draw only the weapon
if (localplayer)
{
	if (input->CAM_IsThirdPerson())
	{
		// make sure the caller is the player to prevent hiding EVERYONE's weapon model.
		if(localplayer == GetOwner())
                {
			SetModel( GetWorldModel() );
			// Hide crosshair, remove this line if you still want to see it.
			localplayer->m_Local.m_iHideHUD |= HIDEHUD_CROSSHAIR;
			return false;
			/* Comment above and uncomment the line underneath if you want
			   to be able to do weapon things (it will display the weapon). */
			//return BaseClass:: DrawModel( flags );
                }
	}
}

Here we have told the code to hide the view model (Gordon's hands) and the crosshair when in third person view. The way the code is written above, the weapon model is also hidden in 3rd person view. If you want your mod to be able to show the weapon in third person view, comment return false; and uncomment return BaseClass:: DrawModel( flags );.

On how to go about positioning weapons and whatnot for 3rd person view, this tutorial does not cover that and assumes you would like to hide the weapon in 3rd person view.

Step 3: in_main.cpp

Open up in_main.cpp in the client project.

Underneath the line kbutton_t in_graph;, add:

kbutton_t	in_camchange;

Underneath:

void IN_ZoomDown(void) {KeyDown(&in_zoom);}
void IN_ZoomUp(void) {KeyUp(&in_zoom);}

Add:

void IN_CamChangeDown(void) {KeyDown(&in_camchange);}
void IN_CamChangeUp(void) {KeyUp(&in_camchange);}

Underneath CalcButtonBits( bits, IN_ZOOM, s_ClearInputState, &in_zoom, bResetState );, add:

CalcButtonBits( bits, IN_CAMCHANGE, s_ClearInputState, &in_camchange, bResetState );

Underneath:

static ConCommand startzoom("+zoom", IN_ZoomDown);
static ConCommand endzoom("-zoom", IN_ZoomUp);

Add:

static ConCommand startcamchange("+camchange", IN_CamChangeDown);
static ConCommand endcamchange("-camchange", IN_CamChangeUp);

Step 4: input.h & in_buttons.h

Open the file in_buttons.h under Header Files under the server project.

Underneath #define IN_BULLRUSH (1 << 22), add:

#define IN_CAMCHANGE	(1 << 23)
Note.pngNote:If you plan on adding more keyboard commands, you can just add:
#define {command} (1 << {int})
Where "{int}" is the next integer (in this case, 24). Note also that {int} cannot go above 31 because the int would break.

Open the file input.h under Header Files under the client project.

Underneath:

extern kbutton_t in_back;

Add:

extern kbutton_t in_camchange;

Step 5: kb_act.lst

Open up kb_act.lst in your SourceMods/{your_mod}/scripts/ folder. Now, anywhere you want the label to appear, add:

"+camchange"			"Toggle 3rd/1st Person View"

You can add a token in your {mod_name}_english.txt also and refer to that (e.g. "#Mod_CameraToggle").

Step 6: Animations

Originally found on VERC Forums:

Posted by: Whisp

This is a follow up to a post that I did awhile back. I was trying to see if any knew how to add animation in thirdperson view to a single player mod. No one knew, but I am happy to annouce that I have found the way. By looking at the multiple player code I found that the CHL2MP_Player class is derived from CHL2_Player of the single player game. If you look in the CHL2MP_Player class you will find that the SetAnimation function is overridden. I copied this function from the MP source and added it to the CHL2_Player class of my single player mod. I then change the player model to one of the Humans (Alyx works also) and modified the MP function to use SP activities. Below is the function as it sits now. I hope this helps anyone trying to add this sort of animation. I have not found a way to tell if the player is running to not so the animation is currently always run (that is the way it is in MP as well). I tried checking for the IN_RUN key but that did not work. I also tried the speed code at the top of the MP function and that also did not work. If anyone knows a way to tell this please let me know.
// Set the activity based on an event or current state
void CHL2_Player::SetAnimation( PLAYER_ANIM playerAnim )
{
	int animDesired;
	
	float speed;
	
	speed = GetAbsVelocity().Length2D();
	
	if ( GetFlags() & ( FL_FROZEN | FL_ATCONTROLS ) )
	{
	speed = 0;
	playerAnim = PLAYER_IDLE;
	}
	
	Activity idealActivity = ACT_RUN;
	
	// This could stand to be redone. Why is playerAnim abstracted from activity? (sjb)
	if ( playerAnim == PLAYER_JUMP )
	{
		idealActivity = ACT_JUMP;
	}
	else if ( playerAnim == PLAYER_DIE )
	{
		if ( m_lifeState == LIFE_ALIVE )
		{
			return;
		}
	}
	else if ( playerAnim == PLAYER_ATTACK1 )
	{
		if ( GetActivity( ) == ACT_HOVER ||
			GetActivity( ) == ACT_SWIM ||
			GetActivity( ) == ACT_HOP ||
			GetActivity( ) == ACT_LEAP ||
			GetActivity( ) == ACT_DIESIMPLE )
		{
			idealActivity = GetActivity( );
		}
		else
		{
			idealActivity = ACT_GESTURE_RANGE_ATTACK1;
		}
	}
	else if ( playerAnim == PLAYER_RELOAD )
	{
		idealActivity = ACT_GESTURE_RELOAD;
	}
	else if ( playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK )
	{
		if ( !( GetFlags() & FL_ONGROUND ) && GetActivity( ) == ACT_JUMP ) // Still jumping
		{
			idealActivity = GetActivity( );
		}
		/*
		else if ( GetWaterLevel() > 1 )
		{
			if ( speed == 0 )
				idealActivity = ACT_HOVER;
			else
				idealActivity = ACT_SWIM;
		}
		*/
		else
		{
			if ( GetFlags() & FL_DUCKING )
			{
				if ( speed > 0 )
				{
					idealActivity = ACT_WALK_CROUCH;
				}
				else
				{
					idealActivity = ACT_COVER_LOW; //ACT_IDLE_CROUCH;
				}
			}
			else
			{
				if ( speed > 0 )
				{
					idealActivity = m_fIsSprinting ? ACT_WALK : ACT_RUN;
				}
				else
				{
					idealActivity = ACT_IDLE;
				}
			}
		}
	
		//idealActivity = TranslateTeamActivity( idealActivity );
	}
	
	if ( idealActivity == ACT_GESTURE_RANGE_ATTACK1 )
	{
		RestartGesture( Weapon_TranslateActivity( idealActivity ) );
	
		// FIXME: this seems a bit wacked
		Weapon_SetActivity( Weapon_TranslateActivity( ACT_RANGE_ATTACK1 ), 0 );
	
		return;
	}
	else if ( idealActivity == ACT_GESTURE_RELOAD )
	{
		RestartGesture( Weapon_TranslateActivity( idealActivity ) );
		return;
	}
	else
	{
		SetActivity( idealActivity );
	
		animDesired = SelectWeightedSequence( Weapon_TranslateActivity ( idealActivity ) );
	
		if (animDesired == -1)
		{
			animDesired = SelectWeightedSequence( idealActivity );
	
			if ( animDesired == -1 )
			{
				animDesired = 0;
			}
		}
	
		// Already using the desired animation?
		if ( GetSequence() == animDesired )
			return;
		
		m_flPlaybackRate = 1.0;
		ResetSequence( animDesired );
		SetCycle( 0 );
		return;
	}
	
	// Already using the desired animation?
	if ( GetSequence() == animDesired )
		return;
	
	//Msg( "Set animation to %d\n", animDesired );
	// Reset to first frame of desired animation
	ResetSequence( animDesired );
	SetCycle( 0 );
}

== the above code needs to go inside hl2_player.cpp. this then needs to be defined in hl2_player.h so at line 174 put:

void SetAnimation ( PLAYER_ANIM playerAnim );

==

Conclusion

You should now have a working third person / first person hybrid. If you want to add a default key for "+camchange", refer to Customizing Options: Keyboard wiki article.

If you didn't hide the player model and the weapon, you will see an ugly model which is supposed to represent Gordon Freeman. Valve didn't attempt to use the 3rd person in HL2 so you will have to found a player model with all weapon animations and install it !

Reference

Reference code

in_camera.cpp

Your finished in_camera.cpp should now look like:

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Handles 3rd and 1st Person Views
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "hud.h"
#include "kbutton.h"
#include "input.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

//-------------------------------------------------- Constants

#define CAM_DIST_DELTA 1.0
#define CAM_ANGLE_DELTA 2.5
#define CAM_ANGLE_SPEED 2.5
#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

#define CAM_MIN_DIST		16.0   // Don't let the camera get any closer than ...
#define CAM_SWITCH_DIST		96.0   // the default distance when switching from 1st to 3rd
#define CAM_MAX_DIST		96.0   // ... or any farther away than ...
#define CAM_HULL_OFFSET		6.0    // the size of the bounding hull used for collision checking
#define START_TRANS_DIST	40.0   // how close to player when it starts making model translucent
#define TRANS_DELTA		1.9921875 // Set to 255 / START_TRANS_DIST

static Vector CAM_HULL_MIN(-CAM_HULL_OFFSET,-CAM_HULL_OFFSET,-CAM_HULL_OFFSET);
static Vector CAM_HULL_MAX( CAM_HULL_OFFSET, CAM_HULL_OFFSET, CAM_HULL_OFFSET);

//-------------------------------------------------- Global Variables

static ConVar cam_command( "cam_command", "0", FCVAR_CHEAT | FCVAR_ARCHIVE ); /* tells camera to go
                                                                                 to thirdperson */
static ConVar cam_snapto( "cam_snapto", "0", FCVAR_ARCHIVE );	      // snap to thirdperson view
static ConVar cam_idealyaw( "cam_idealyaw", "0", FCVAR_ARCHIVE );     // thirdperson yaw
static ConVar cam_idealpitch( "cam_idealpitch", "0", FCVAR_ARCHIVE ); // thirperson pitch
static ConVar cam_idealdist( "cam_idealdist", "64", FCVAR_ARCHIVE );  // thirdperson distance

static ConVar c_maxpitch( "c_maxpitch", "90", FCVAR_ARCHIVE );
static ConVar c_minpitch( "c_minpitch", "0", FCVAR_ARCHIVE );
static ConVar c_maxyaw( "c_maxyaw",   "135", FCVAR_ARCHIVE );
static ConVar c_minyaw( "c_minyaw",   "-135", FCVAR_ARCHIVE );
static ConVar c_maxdistance( "c_maxdistance",   "200", FCVAR_ARCHIVE );
static ConVar c_mindistance( "c_mindistance",   "30", FCVAR_ARCHIVE );
static ConVar c_orthowidth( "c_orthowidth",   "100", FCVAR_ARCHIVE );
static ConVar c_orthoheight( "c_orthoheight",   "100", FCVAR_ARCHIVE );

static kbutton_t cam_pitchup, cam_pitchdown, cam_yawleft, cam_yawright;
static kbutton_t cam_in, cam_out, cam_move;

float mf_NextSwitch;

// API Wrappers

/*
==============================
CAM_ToThirdPerson

==============================
*/
void CAM_ToThirdPerson(void)
{
	input->CAM_ToThirdPerson();
}

/*
==============================
CAM_ToFirstPerson

==============================
*/
void CAM_ToFirstPerson(void) 
{ 
	input->CAM_ToFirstPerson();
}

/*
==============================
CAM_ToOrthographic

==============================
*/
void CAM_ToOrthographic(void) 
{ 
	input->CAM_ToOrthographic();
}

/*
==============================
CAM_StartMouseMove

==============================
*/
void CAM_StartMouseMove( void )
{
	input->CAM_StartMouseMove();
}

/*
==============================
CAM_EndMouseMove

==============================
*/
void CAM_EndMouseMove( void )
{
	input->CAM_EndMouseMove();
}

/*
==============================
CAM_StartDistance

==============================
*/
void CAM_StartDistance( void )
{
	input->CAM_StartDistance();
}

/*
==============================
CAM_EndDistance

==============================
*/
void CAM_EndDistance( void )
{
	input->CAM_EndDistance();
}

/*
==============================
CAM_ToggleSnapto

==============================
*/
void CAM_ToggleSnapto( void )
{ 
	cam_snapto.SetValue( !cam_snapto.GetInt() );
}


/*
==============================
MoveToward

==============================
*/
float MoveToward( float cur, float goal, float maxspeed )
{
	if( cur != goal )
	{
		if( abs( 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;
}

/*
==============================
CAM_Think

==============================
*/
void CInput::CAM_Think( void )
{
	Vector origin;
	Vector ext, pnt, camForward, camRight, camUp;
	float dist;
	Vector camAngles;
	float flSensitivity;
	QAngle viewangles;
	
	switch( cam_command.GetInt() )
	{
	case CAM_COMMAND_TOTHIRDPERSON:
		CAM_ToThirdPerson();
		break;
		
	case CAM_COMMAND_TOFIRSTPERSON:
		CAM_ToFirstPerson();
		break;
		
	case CAM_COMMAND_NONE:
	default:
		break;
	}
	
	// Toggle first/3rd person
	if(gpGlobals->curtime >= mf_NextSwitch) {
		if(input->KeyState(&in_camchange))
		{
			mf_NextSwitch = gpGlobals->curtime + 0.5;
			if(!m_fCameraInThirdPerson)
			{
				CAM_ToThirdPerson();
			} else {
				CAM_ToFirstPerson();
			}
		}
	}

	if( !m_fCameraInThirdPerson )
	{
		// If in FP mode, transparent
		// This is sort of a hack since when the map first loads you ARE transparent...
		// This is for when you switch from 3rd to 1st.
		if(C_BasePlayer::GetLocalPlayer() ) {
			C_BasePlayer::GetLocalPlayer()->SetRenderMode( kRenderTransColor );
			C_BasePlayer::GetLocalPlayer()->SetRenderColorA(0);
		}

		return;
	}		
	
	camAngles[ PITCH ] = cam_idealpitch.GetFloat();
	camAngles[ YAW ] = cam_idealyaw.GetFloat();
	dist = cam_idealdist.GetFloat();
	//
	//movement of the camera with the mouse
	//
	if (m_fCameraMovingWithMouse)
	{
		int cpx, cpy;
		
		//get windows cursor position
		GetMousePos (cpx, cpy);
		
		m_nCameraX = cpx;
		m_nCameraY = cpy;
		
		//check for X delta values and adjust accordingly
		//eventually adjust YAW based on amount of movement
		//don't do any movement of cam using YAW/PITCH if we are zooming in/out the camera	
		if (!m_fCameraDistanceMove)
		{
			int x, y;
			GetWindowCenter( x,  y );
			
			/* keep the camera within certain limits around the player (ie avoid certain
                           bad viewing angles) */
  
			if (m_nCameraX>x)
			{
				//if ((camAngles[YAW]>=225.0)||(camAngles[YAW]<135.0))
				if (camAngles[YAW]<c_maxyaw.GetFloat())
				{
					camAngles[ YAW ] += (CAM_ANGLE_MOVE)*((m_nCameraX-x)/2);
				}
				if (camAngles[YAW]>c_maxyaw.GetFloat())
				{
					
					camAngles[YAW]=c_maxyaw.GetFloat();
				}
			}
			else if (m_nCameraX<x)
			{
				//if ((camAngles[YAW]<=135.0)||(camAngles[YAW]>225.0))
				if (camAngles[YAW]>c_minyaw.GetFloat())
				{
					camAngles[ YAW ] -= (CAM_ANGLE_MOVE)* ((x-m_nCameraX)/2);
					
				}
				if (camAngles[YAW]<c_minyaw.GetFloat())
				{
					camAngles[YAW]=c_minyaw.GetFloat();
					
				}
			}
			
			//check for y delta values and adjust accordingly
			//eventually adjust PITCH based on amount of movement
			//also make sure camera is within bounds
			if (m_nCameraY > y)
			{
				if(camAngles[PITCH]<c_maxpitch.GetFloat())
				{
					camAngles[PITCH] +=(CAM_ANGLE_MOVE)* ((m_nCameraY-y)/2);
				}
				if (camAngles[PITCH]>c_maxpitch.GetFloat())
				{
					camAngles[PITCH]=c_maxpitch.GetFloat();
				}
			}
			else if (m_nCameraY<y)
			{
				if (camAngles[PITCH]>c_minpitch.GetFloat())
				{
					camAngles[PITCH] -= (CAM_ANGLE_MOVE)*((y-m_nCameraY)/2);
				}
				if (camAngles[PITCH]<c_minpitch.GetFloat())
				{
					camAngles[PITCH]=c_minpitch.GetFloat();
				}
			}
			
			//set old mouse coordinates to current mouse coordinates
			//since we are done with the mouse
			
			if ( ( flSensitivity = gHUD.GetSensitivity() ) != 0 )
			{
				m_nCameraOldX=m_nCameraX*flSensitivity;
				m_nCameraOldY=m_nCameraY*flSensitivity;
			}
			else
			{
				m_nCameraOldX=m_nCameraX;
				m_nCameraOldY=m_nCameraY;
			}

			ResetMouse();
		}
	}
	
	//Nathan code here
	if( input->KeyState( &cam_pitchup ) )
		camAngles[ PITCH ] += CAM_ANGLE_DELTA;
	else if( input->KeyState( &cam_pitchdown ) )
		camAngles[ PITCH ] -= CAM_ANGLE_DELTA;
	
	if( input->KeyState( &cam_yawleft ) )
		camAngles[ YAW ] -= CAM_ANGLE_DELTA;
	else if( input->KeyState( &cam_yawright ) )
		camAngles[ YAW ] += CAM_ANGLE_DELTA;
	
	if( input->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( input->KeyState( &cam_out ) ) {
		dist += CAM_DIST_DELTA;

		// Prevent the cam_out from going any further
		// NOTE: Without this, it _appears_ it's not moving, but
		// the transparency changes so it is... this fixes it.
		if( dist > CAM_MAX_DIST )
			dist = CAM_MAX_DIST;
	}

	if (m_fCameraDistanceMove)
	{
		int x, y;
		GetWindowCenter( x, y );

		if (m_nCameraY>y)
		{
			if(dist<c_maxdistance.GetFloat())
			{
				dist +=CAM_DIST_DELTA * ((m_nCameraY-y)/2);
			}
			if (dist>c_maxdistance.GetFloat())
			{
				dist=c_maxdistance.GetFloat();
			}
		}
		else if (m_nCameraY<y)
		{
			if (dist>c_mindistance.GetFloat())
			{
				dist -= (CAM_DIST_DELTA)*((y-m_nCameraY)/2);
			}
			if (dist<c_mindistance.GetFloat())
			{
				dist=c_mindistance.GetFloat();
			}
		}
		//set old mouse coordinates to current mouse coordinates
		//since we are done with the mouse
		m_nCameraOldX=m_nCameraX*gHUD.GetSensitivity();
		m_nCameraOldY=m_nCameraY*gHUD.GetSensitivity();
		ResetMouse();
	}
	
	// update ideal
	cam_idealpitch.SetValue( camAngles[ PITCH ] );
	cam_idealyaw.SetValue( camAngles[ YAW ] );
	cam_idealdist.SetValue( dist );
	
	// Move towards ideal
	VectorCopy( m_vecCameraOffset, camAngles );
	
	engine->GetViewAngles( viewangles );
	
	if( cam_snapto.GetInt() )
	{
		camAngles[ YAW ] = cam_idealyaw.GetFloat() + viewangles[ YAW ];
		camAngles[ PITCH ] = cam_idealpitch.GetFloat() + viewangles[ PITCH ];
		camAngles[ 2 ] = cam_idealdist.GetFloat();
	}
	else
    	{
        	if( camAngles[ YAW ] - viewangles[ YAW ] != cam_idealyaw.GetFloat() )
            		camAngles[ YAW ] = MoveToward( camAngles[ YAW ],
                                                       cam_idealyaw.GetFloat() + viewangles[ YAW ],
                                                       CAM_ANGLE_SPEED );
 
        	if( camAngles[ PITCH ] - viewangles[ PITCH ] != cam_idealpitch.GetFloat() )
            		camAngles[ PITCH ] = MoveToward( camAngles[ PITCH ],
                                                    cam_idealpitch.GetFloat() + viewangles[ PITCH ],
                                                    CAM_ANGLE_SPEED );
 
        	if( !C_BasePlayer::GetLocalPlayer() ) {
            	// this code can be hit from the main menu, where it will crash
            		camAngles[ 2 ] = dist; // if there's no localplayer to calc from
        }
        else {
            trace_t tr;
            float adjDist = dist;
            C_BasePlayer* localPlayer = C_BasePlayer::GetLocalPlayer();

            Vector origin = localPlayer->GetLocalOrigin(); // find our player's origin
            origin += localPlayer->GetViewOffset(); // and from there, his eye position

            AngleVectors( QAngle(camAngles.x, camAngles.y, camAngles.z),
                         &camForward, NULL, NULL ); // get the forward vector

            UTIL_TraceHull( origin, origin - (camForward * dist),
                            CAM_HULL_MIN, CAM_HULL_MAX,
                            MASK_SOLID, NULL, &tr ); /* use our previously #defined hull to
                                                        collision trace */

            if( tr.fraction < 1.0 ) {
                adjDist = dist * tr.fraction; // move the camera closer if it hit something
            }
            else {
                adjDist = dist; // no trace hit, use cam_idealdist without adjusting it
            }

			if ( adjDist < START_TRANS_DIST ) {
                                // make him translucent
				localPlayer->SetRenderMode( kRenderTransColor );
                                // closer = less opacity
				localPlayer->SetRenderColorA( (byte)(adjDist * TRANS_DELTA) );
			}

            if( adjDist < CAM_MIN_DIST )
                adjDist = CAM_MIN_DIST; // clamp up to minimum

            if( adjDist > CAM_MAX_DIST )
                adjDist = CAM_MAX_DIST; // clamp down to maximum

            camAngles[ 2 ] = adjDist;
        }
    }

    m_vecCameraOffset[ 0 ] = camAngles[ 0 ];
    m_vecCameraOffset[ 1 ] = camAngles[ 1 ];
    m_vecCameraOffset[ 2 ] = camAngles[ 2 ];
}

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 ); }

/*
==============================
CAM_ToThirdPerson

==============================
*/
void CInput::CAM_ToThirdPerson(void)
{ 
	QAngle viewangles;

// Do allow third person in TF2 for now
#if !defined( TF2_CLIENT_DLL ) && !defined( CSTRIKE_DLL )

#if !defined( _DEBUG )
	if ( gpGlobals->maxClients > 1 )
	{
		// no thirdperson in multiplayer.
		return;
	}
#endif

#endif

	engine->GetViewAngles( viewangles );

	if( !m_fCameraInThirdPerson )
	{		
		m_fCameraInThirdPerson = true;

		
		m_vecCameraOffset[ YAW ] = viewangles[ YAW ]; 
		m_vecCameraOffset[ PITCH ] = viewangles[ PITCH ]; 
		m_vecCameraOffset[ 2 ] = CAM_SWITCH_DIST; 

		// Set model to player because when a map first loads
		// it doesn't set the model... sort of a hack.
		//C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
		//pPlayer->SetModel("player");
	}

	cam_command.SetValue( 0 );
}

/*
==============================
CAM_ToFirstPerson

==============================
*/
void CInput::CAM_ToFirstPerson(void)
{
	m_fCameraInThirdPerson = false;
	cam_command.SetValue( 0 );
}

/*
==============================
CAM_ToFirstPerson

==============================
*/
bool CInput::CAM_IsOrthographic(void) const
{
	return m_CameraIsOrthographic;
}


/*
==============================
CAM_ToFirstPerson

==============================
*/
void CInput::CAM_OrthographicSize(float& w, float& h) const
{
	w = c_orthowidth.GetFloat(); h = c_orthoheight.GetFloat();
}


/*
==============================
CAM_ToFirstPerson

==============================
*/
void CInput::CAM_ToOrthographic(void)
{	
	m_fCameraInThirdPerson = false;
	m_CameraIsOrthographic = true;
	cam_command.SetValue( 0 );
}

/*
==============================
CAM_StartMouseMove

==============================
*/
void CInput::CAM_StartMouseMove(void)
{
	float flSensitivity;
		
	//only move the cam with mouse if we are in third person.
	if ( m_fCameraInThirdPerson )
	{
		//set appropriate flags and initialize the old mouse position
		//variables for mouse camera movement
		if (!m_fCameraMovingWithMouse)
		{
			int cpx, cpy;

			m_fCameraMovingWithMouse=true;
			m_fCameraInterceptingMouse=true;
			
			GetMousePos(cpx, cpy);

			m_nCameraX = cpx;
			m_nCameraY = cpy;

			if ( ( flSensitivity = gHUD.GetSensitivity() ) != 0 )
			{
				m_nCameraOldX=m_nCameraX*flSensitivity;
				m_nCameraOldY=m_nCameraY*flSensitivity;
			}
			else
			{
				m_nCameraOldX=m_nCameraX;
				m_nCameraOldY=m_nCameraY;
			}
		}
	}
	//we are not in 3rd person view..therefore do not allow camera movement
	else
	{   
		m_fCameraMovingWithMouse=false;
		m_fCameraInterceptingMouse=false;
	}
}

/*
==============================
CAM_EndMouseMove

the key has been released for camera movement
tell the engine that mouse camera movement is off
==============================
*/
void CInput::CAM_EndMouseMove(void)
{
   m_fCameraMovingWithMouse=false;
   m_fCameraInterceptingMouse=false;
}

/*
==============================
CAM_StartDistance

routines to start the process of moving the cam in or out 
using the mouse
==============================
*/
void CInput::CAM_StartDistance(void)
{
	//only move the cam with mouse if we are in third person.
	if ( m_fCameraInThirdPerson )
	{
	  //set appropriate flags and initialize the old mouse position
	  //variables for mouse camera movement
	  if (!m_fCameraDistanceMove)
	  {
		  int cpx, cpy;

		  m_fCameraDistanceMove=true;
		  m_fCameraMovingWithMouse=true;
		  m_fCameraInterceptingMouse=true;
		  GetMousePos(cpx, cpy);

		  m_nCameraX = cpx;
		  m_nCameraY = cpy;

		  m_nCameraOldX=m_nCameraX*gHUD.GetSensitivity();
		  m_nCameraOldY=m_nCameraY*gHUD.GetSensitivity();
	  }
	}
	//we are not in 3rd person view..therefore do not allow camera movement
	else
	{   
		m_fCameraDistanceMove=false;
		m_fCameraMovingWithMouse=false;
		m_fCameraInterceptingMouse=false;
	}
}

/*
==============================
CAM_EndDistance

the key has been released for camera movement
tell the engine that mouse camera movement is off
==============================
*/
void CInput::CAM_EndDistance(void)
{
   m_fCameraDistanceMove=false;
   m_fCameraMovingWithMouse=false;
   m_fCameraInterceptingMouse=false;
}

/*
==============================
CAM_IsThirdPerson

==============================
*/
int CInput::CAM_IsThirdPerson( void )
{
	return m_fCameraInThirdPerson;
}

/*
==============================
CAM_GetCameraOffset

==============================
*/
void CInput::CAM_GetCameraOffset( Vector& ofs )
{
	VectorCopy( m_vecCameraOffset, ofs );
}

/*
==============================
CAM_InterceptingMouse

==============================
*/
int CInput::CAM_InterceptingMouse( void )
{
	return m_fCameraInterceptingMouse;
}

static ConCommand startpitchup( "+campitchup", CAM_PitchUpDown );
static ConCommand endpitcup( "-campitchup", CAM_PitchUpUp );
static ConCommand startpitchdown( "+campitchdown", CAM_PitchDownDown );
static ConCommand endpitchdown( "-campitchdown", CAM_PitchDownUp );
static ConCommand startcamyawleft( "+camyawleft", CAM_YawLeftDown );
static ConCommand endcamyawleft( "-camyawleft", CAM_YawLeftUp );
static ConCommand startcamyawright( "+camyawright", CAM_YawRightDown );
static ConCommand endcamyawright( "-camyawright", CAM_YawRightUp );
static ConCommand startcamin( "+camin", CAM_InDown );
static ConCommand endcamin( "-camin", CAM_InUp );
static ConCommand startcamout( "+camout", CAM_OutDown );
static ConCommand camout( "-camout", CAM_OutUp );
static ConCommand thirdperson( "thirdperson", ::CAM_ToThirdPerson,
                               "Switch to thirdperson camera.", FCVAR_CHEAT );
static ConCommand firstperson( "firstperson", ::CAM_ToFirstPerson, "Switch to firstperson camera." );
static ConCommand camortho( "camortho", ::CAM_ToOrthographic,
                            "Switch to orthographic camera.", FCVAR_CHEAT );
static ConCommand startcammousemove( "+cammousemove",::CAM_StartMouseMove);
static ConCommand endcammousemove( "-cammousemove",::CAM_EndMouseMove);
static ConCommand startcamdistance( "+camdistance", ::CAM_StartDistance );
static ConCommand endcamdistance( "-camdistance", ::CAM_EndDistance );
static ConCommand snapto( "snapto", CAM_ToggleSnapto );
/*
==============================
Init_Camera

==============================
*/
void CInput::Init_Camera( void )
{
	m_CameraIsOrthographic = false;
}

c_basecombatweapon.cpp

The DrawModel function should now look like:

//-----------------------------------------------------------------------------
// Purpose: Render the weapon. Draw the Viewmodel if the weapon's being carried
//			by this player, otherwise draw the worldmodel.
//-----------------------------------------------------------------------------
int C_BaseCombatWeapon::DrawModel( int flags )
{
	VPROF_BUDGET( "C_BaseCombatWeapon::DrawModel", VPROF_BUDGETGROUP_MODEL_RENDERING );
	if ( !m_bReadyToDraw )
		return 0;

	if ( !IsVisible() )
		return 0;

	// check if local player chases owner of this weapon in first person
	C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer();

	// Get rid of view model, crosshair and weapon in 3rd person
	// Once we actually get weapons working, we'll let it draw only the weapon
	if (localplayer)
	{
		if (input->CAM_IsThirdPerson())
		{
                        if(localplayer == GetOwner())
                        {
			SetModel( GetWorldModel() );
			localplayer->m_Local.m_iHideHUD |= HIDEHUD_CROSSHAIR;
			return false;
			//return BaseClass:: DrawModel( flags ); 
                                                                   /* Uncomment this if you want to be
                                                                    able to do weapon things
                                                                    (it will display the weapon). */
                        }
		}
	} 

	if ( localplayer && localplayer->IsObserver() && GetOwner() )
	{
		// don't draw weapon if chasing this guy as spectator
		// we don't check that in ShouldDraw() since this may change
		// without notification 
		
		if ( localplayer->GetObserverMode() == OBS_MODE_IN_EYE &&
			 localplayer->GetObserverTarget() == GetOwner() ) 
			return false;
	}

	return BaseClass::DrawModel( flags );
}

See also