www.pudn.com > RCApp-src.zip > Camera.cpp


/* 
	RedEye Project (http://members.ozemail.com.au/~ndmcevoy/) 
	Copyright (C) 2003  Nick McEvoy 
 
	This library is free software; you can redistribute it and/or 
	modify it under the terms of the GNU Library General Public 
	License as published by the Free Software Foundation; either 
	version 2 of the License, or (at your option) any later version. 
 
	This library is distributed in the hope that it will be useful, 
	but WITHOUT ANY WARRANTY; without even the implied warranty of 
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
	Library General Public License for more details. 
 
	You should have received a copy of the GNU Library General Public 
	License along with this library; if not, write to the Free 
	Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 
	----------------------------------------------------------------- 
 
		Commented for use with Doxygen (http://www.doxygen.org) 
 
	----------------------------------------------------------------- 
*/ 
 
/*! \file Camera.cpp 
 *	\brief Game camera. 
 * 
 *		This file contains the game camera. 
 */ 
 
// GLUT includes 
#include  
 
// RedEye includes 
#include "Camera.h" 
#include "Engine.h" 
#include "Entity.h" 
#include "Collision.h" 
 
float 
reBlendAngle(float current, float desired, float rate) 
{ 
	// 
	// Code adapted from Steve Baker (TuxKart & TuxAQFH) 
	// 
 
	// Adjust heading from -180->180 to 0->360 
	float a1 = desired + 180.0f; 
	float a2 = current + 180.0f; 
 
	// Saftey check 
	while (a1 >= 360.0f) a1 -= 360.0f; 
	while (a2 >= 360.0f) a2 -= 360.0f; 
	while (a1 <    0.0f) a1 += 360.0f; 
	while (a2 <    0.0f) a2 += 360.0f; 
 
	// Return desired if small diff 
	//if (fabs(a1 - a2) <= 1.0) 
	//	return desired; 
 
	if (a1 >= 270.0f && a2 <   90.0f) a1 -= 360.0f; 
	if (a1 <   90.0f && a2 >= 270.0f) a1 += 360.0f; 
 
	float delta = a1 - a2; 
 
	// Quickest path adjust 
	if (delta < -180) delta += 360; 
	if (delta > 180)  delta -= 360; 
 
	// Slow down 
	//if (fabs(delta) > 30.0f) 
	//	rate /= 2.0f; 
 
	//return current + delta/rate; 
	return desired;//(current + delta*0.000001f); 
} 
/*float 
reBlendAngle(float a2, float a1, float rate) 
{ 
	// 
	// Code adapted from Steve Baker (TuxKart & TuxAQFH) 
	// 
	while (a1 >= 360.0f) a1 -= 360.0f; 
	while (a2 >= 360.0f) a2 -= 360.0f; 
	while (a1 <    0.0f) a1 += 360.0f; 
	while (a2 <    0.0f) a2 += 360.0f; 
 
	if (fabs(a1 - a2) <= 1.0) 
		return a1; 
 
	if (a1 >= 270.0f && a2 <   90.0f) a1 -= 360.0f; 
	if (a1 <   90.0f && a2 >= 270.0f) a1 += 360.0f; 
 
	float delta = a1 - a2; 
 
	if (fabs(delta) > 30.0f) 
		rate /= 2.0f; 
 
	return a2 + delta/rate; 
}*/ 
 
 
 
 
reGameCamera::reGameCamera() 
{ 
	meCameraMode = eFixedView; 
	sgZeroCoord(&mCameraPos); 
	sgZeroCoord(&mBlendCameraPos); 
	sgZeroCoord(&mViewOffset); 
	sgZeroCoord(&mExternalViewOffset); 
	sgZeroCoord(&mAttachedViewOffset); 
	mpEntityRef = NULL; 
	mpTrackEntityRef = NULL; 
	mbViewZoomIn = false; 
	mbViewZoomOut = false; 
	mbViewUp = false; 
	mbViewDown = false; 
	mbViewLeft = false; 
	mbViewRight = false; 
} 
 
 
 
 
void 
reGameCamera::Update(float fDeltaSec) 
{ 
	if (meCameraMode == eExternalDetachedView) 
	{ 
		// Move external camera (TODO - Add better limit checks) 
		if (mbViewZoomIn) 
		{ 
			mExternalViewOffset.xyz[SG_Y] += fDeltaSec*20.0f; 
 
			float fNear, fFar; 
			ssgGetNearFar(&fNear, &fFar); 
			fNear += 1.0f; 
 
			float fY = 0; 
			if (mpEntityRef) 
				fY = mpEntityRef->getBSphere()->getRadius(); 
 
			if (fY <= fNear) fY = fNear; // don't get too close (else NEAR clipping) 
 
			if (mExternalViewOffset.xyz[SG_Y] > -fY) 
				mExternalViewOffset.xyz[SG_Y] = -fY; 
		} 
		if (mbViewZoomOut) 
		{ 
			mExternalViewOffset.xyz[SG_Y] -= fDeltaSec*20.0f; 
 
			float fNear, fFar; 
			ssgGetNearFar(&fNear, &fFar); 
 
			if (mExternalViewOffset.xyz[SG_Y] < -fFar) 
				mExternalViewOffset.xyz[SG_Y] = -fFar; 
		} 
		if (mbViewUp) 
			mExternalViewOffset.hpr[SG_Y] += fDeltaSec*20.0f; 
		if (mbViewDown) 
			mExternalViewOffset.hpr[SG_Y] -= fDeltaSec*20.0f; 
		if (mbViewLeft) 
			mExternalViewOffset.hpr[SG_X] += fDeltaSec*20.0f; 
		if (mbViewRight) 
			mExternalViewOffset.hpr[SG_X] -= fDeltaSec*20.0f; 
	} 
	else if (meCameraMode == eExternalAttachedView) 
	{ 
		// Move external camera (TODO - Add better limit checks) 
		if (mbViewZoomIn) 
		{ 
			mAttachedViewOffset.xyz[SG_Y] += fDeltaSec*20.0f; 
 
			float fNear, fFar; 
			ssgGetNearFar(&fNear, &fFar); 
			fNear += 1.0f; 
 
			float fY = 0; 
			if (mpEntityRef) 
				fY = mpEntityRef->getBSphere()->getRadius(); 
 
			if (fY <= fNear) fY = fNear; // don't get too close (else NEAR clipping) 
 
			if (mAttachedViewOffset.xyz[SG_Y] > -fY) 
				mAttachedViewOffset.xyz[SG_Y] = -fY; 
		} 
		if (mbViewZoomOut) 
		{ 
			mAttachedViewOffset.xyz[SG_Y] -= fDeltaSec*20.0f; 
 
			float fNear, fFar; 
			ssgGetNearFar(&fNear, &fFar); 
 
			if (mAttachedViewOffset.xyz[SG_Y] < -fFar) 
				mAttachedViewOffset.xyz[SG_Y] = -fFar; 
		} 
		/*if (mbViewUp) 
			mAttachedViewOffset.hpr[SG_Y] += fDeltaSec*20.0f; 
		if (mbViewDown) 
			mAttachedViewOffset.hpr[SG_Y] -= fDeltaSec*20.0f; 
		if (mbViewLeft) 
			mAttachedViewOffset.hpr[SG_X] += fDeltaSec*20.0f; 
		if (mbViewRight) 
			mAttachedViewOffset.hpr[SG_X] -= fDeltaSec*20.0f;*/ 
	} 
} 
 
 
 
 
void 
reGameCamera::Display() 
{ 
	sgCoord EntityPos; 
	sgCoord TrackEntityPos; 
 
	if (mpEntityRef) 
		mpEntityRef->GetPosition(&EntityPos); 
	else 
		sgZeroCoord(&EntityPos); 
 
	if (mpTrackEntityRef) 
		 mpTrackEntityRef->GetPosition(&TrackEntityPos); 
	else 
		sgZeroCoord(&TrackEntityPos); 
 
	switch (meCameraMode) 
	{ 
	case eFixedView: 
		// Set camera 
		ssgSetCamera(&mCameraPos); 
		break; 
 
	case eFirstPersonView: 
		{ 
			// Start with camera at entity position 
			sgCopyCoord(&mCameraPos, &EntityPos); 
 
			// Translate camera by offset 
			sgMat4 CAM, T, R; 
			sgMakeCoordMat4(CAM, &mCameraPos); 
			sgMakeTransMat4(T, mViewOffset.xyz); 
			sgMakeRotMat4(R, mViewOffset.hpr); 
			sgPreMultMat4(CAM, R); 
			sgPreMultMat4(CAM, T); 
			sgSetCoord(&mCameraPos, CAM); 
 
			// Looking at a target ? 
			if (mpTrackEntityRef) 
			{ 
				sgVec3 DPos; 
				sgSubVec3(DPos, TrackEntityPos.xyz, EntityPos.xyz); 
				mCameraPos.hpr[0] = -atan2(DPos[0], DPos[1]) * SG_RADIANS_TO_DEGREES; 
				mCameraPos.hpr[1] = atan2(DPos[2], sqrt(DPos[0]*DPos[0] + DPos[1]*DPos[1])) * SG_RADIANS_TO_DEGREES; 
				mCameraPos.hpr[2] = 0; 
			} 
 
			// Set camera 
			ssgSetCamera(&mCameraPos); 
		} 
		break; 
 
	case eExternalDetachedView: 
		{ 
			// Looking at a target ? 
			if (mpTrackEntityRef) 
			{ 
				// Start with camera at entity position 
				sgCopyCoord(&mCameraPos, &EntityPos); 
 
				// Look at the target 
				sgVec3 DPos; 
				sgSubVec3(DPos, TrackEntityPos.xyz, EntityPos.xyz); 
				mCameraPos.hpr[0] = -atan2(DPos[0], DPos[1]) * SG_RADIANS_TO_DEGREES; 
				mCameraPos.hpr[1] = atan2(DPos[2], sqrt(DPos[0]*DPos[0] + DPos[1]*DPos[1])) * SG_RADIANS_TO_DEGREES; 
				mCameraPos.hpr[2] = 0; 
 
				// Slight offset so we can see the target 
				mCameraPos.hpr[1] -= 10.0f; 
 
				// Translate camera by offset 
				sgMat4 CAM, T; 
				sgMakeCoordMat4(CAM, &mCameraPos); 
				sgMakeTransMat4(T, mExternalViewOffset.xyz); 
				sgPreMultMat4(CAM, T); 
				sgSetCoord(&mCameraPos, CAM); 
 
				// Set camera 
				ssgSetCamera(&mCameraPos); 
			} 
			else // looking at entity 
			{ 
				// Start with camera at entity position 
				sgCopyCoord(&mCameraPos, &EntityPos); 
 
				// Translate camera by offset 
				sgMat4 CAM, T, R; 
				sgMakeTransMat4(CAM, mCameraPos.xyz); 
				sgMakeTransMat4(T, mExternalViewOffset.xyz); 
				sgMakeRotMat4(R, mExternalViewOffset.hpr); 
				sgPreMultMat4(CAM, R); 
				sgPreMultMat4(CAM, T); 
				sgSetCoord(&mCameraPos, CAM); 
 
				// Set camera 
				ssgSetCamera(&mCameraPos); 
			} 
		} 
		break; 
 
		case eExternalAttachedView: 
		{ 
			// Looking at a target ? 
			if (mpTrackEntityRef) 
			{ 
				// Start with camera at entity position 
				sgCopyCoord(&mCameraPos, &EntityPos); 
 
				// Look at the target 
				sgVec3 DPos; 
				sgSubVec3(DPos, TrackEntityPos.xyz, EntityPos.xyz); 
				mCameraPos.hpr[0] = -atan2(DPos[0], DPos[1]) * SG_RADIANS_TO_DEGREES; 
				mCameraPos.hpr[1] = atan2(DPos[2], sqrt(DPos[0]*DPos[0] + DPos[1]*DPos[1])) * SG_RADIANS_TO_DEGREES; 
				mCameraPos.hpr[2] = 0; 
 
				// Slight offset so we can see the target 
				mCameraPos.hpr[1] -= 10.0f; 
 
				// Translate camera by offset 
				sgMat4 CAM, T; 
				sgMakeCoordMat4(CAM, &mCameraPos); 
				sgMakeTransMat4(T, mAttachedViewOffset.xyz); 
				sgPreMultMat4(CAM, T); 
				sgSetCoord(&mCameraPos, CAM); 
 
				// Set camera 
				ssgSetCamera(&mCameraPos); 
			} 
			else // looking at entity 
			{ 
				// 
				// todo: fix camera hacks ... still looks crappy !!! :( 
				// 
				sgCoord NewCameraPos; 
 
				if (EntityPos.hpr[1] < 85 && EntityPos.hpr[1] > -85) 
				{ 
					// Start with camera at entity position 
					sgCopyCoord(&NewCameraPos, &EntityPos); 
				} 
				else // heading becomes undefined 
				{ 
					// Start with camera at entity position (but don't change camera hpr) 
					sgCopyVec3(NewCameraPos.xyz, EntityPos.xyz); 
				} 
 
				// Translate camera by offset ... 
				// but don't take pitch & roll into account (yet) 
				sgMat4 CAM, T; 
				NewCameraPos.hpr[1] = 0; 
				NewCameraPos.hpr[2] = 0; 
				sgMakeCoordMat4(CAM, &NewCameraPos); 
				sgMakeTransMat4(T, mAttachedViewOffset.xyz); 
				sgPreMultMat4(CAM, T); 
				sgSetCoord(&NewCameraPos, CAM); 
 
				// Now calculate pitch 
				sgVec3 DPos; 
				sgSubVec3(DPos, NewCameraPos.xyz, EntityPos.xyz); 
				// todo: need to use reBlendAngle() here 
				NewCameraPos.hpr[1] = -atan2(DPos[2], sqrt(DPos[0]*DPos[0] + DPos[1]*DPos[1])) * SG_RADIANS_TO_DEGREES + mAttachedViewOffset.hpr[1]; 
				NewCameraPos.hpr[2] = mAttachedViewOffset.hpr[2]; 
 
				// Don't allow camera under terrain 
				float fAltitude = reGetAltitude(reGetWorld(), NewCameraPos.xyz); 
				if (fAltitude < 0) 
					NewCameraPos.xyz[2] += -fAltitude; 
 
				sgLerpAnglesVec3(NewCameraPos.hpr, mCameraPos.hpr, NewCameraPos.hpr, 0.1f); 
				sgLerpVec3(NewCameraPos.xyz, mCameraPos.xyz, NewCameraPos.xyz, 0.1f); 
				sgCopyCoord(&mCameraPos, &NewCameraPos); 
				ssgSetCamera(&mCameraPos); 
 
/*				if (EntityPos.hpr[1] < 85 && EntityPos.hpr[1] > -85) 
				{ 
					// Start with camera at entity position 
					sgCopyCoord(&mCameraPos, &EntityPos); 
				} 
				else // heading becomes undefined 
				{ 
					// Start with camera at entity position (but don't change camera hpr) 
					sgCopyVec3(mCameraPos.xyz, EntityPos.xyz); 
				} 
 
				// Translate camera by offset ... 
				// but don't take pitch & roll into account (yet) 
				sgMat4 CAM, T; 
				mCameraPos.hpr[1] = 0; 
				mCameraPos.hpr[2] = 0; 
				sgMakeCoordMat4(CAM, &mCameraPos); 
				sgMakeTransMat4(T, mAttachedViewOffset.xyz); 
				sgPreMultMat4(CAM, T); 
				sgSetCoord(&mCameraPos, CAM); 
 
				// Now calculate pitch 
				sgVec3 DPos; 
				sgSubVec3(DPos, mCameraPos.xyz, EntityPos.xyz); 
				// todo: need to use reBlendAngle() here 
				mCameraPos.hpr[1] = -atan2(DPos[2], sqrt(DPos[0]*DPos[0] + DPos[1]*DPos[1])) * SG_RADIANS_TO_DEGREES + mAttachedViewOffset.hpr[1]; 
				mCameraPos.hpr[2] = mAttachedViewOffset.hpr[2]; 
 
				// Don't allow camera under terrain 
				float fAltitude = reGetAltitude(reGetWorld(), mCameraPos.xyz); 
				if (fAltitude < 0) 
					mCameraPos.xyz[2] += -fAltitude; 
 
				ssgSetCamera(&mCameraPos);*/ 
 
 
				/*// Start with camera at entity position 
				sgCopyCoord(&mBlendCameraPos, &EntityPos); 
 
				// Translate camera by offset ... 
				// but don't take pitch & roll into account (yet) 
				sgMat4 CAM, T; 
				mBlendCameraPos.hpr[1] = 0; 
				mBlendCameraPos.hpr[2] = 0; 
				sgMakeCoordMat4(CAM, &mBlendCameraPos); 
				sgMakeTransMat4(T, mAttachedViewOffset.xyz); 
				sgPreMultMat4(CAM, T); 
				sgSetCoord(&mBlendCameraPos, CAM); 
 
				// Used to calculate pitch 
				sgVec3 DPos; 
				sgSubVec3(DPos, mBlendCameraPos.xyz, EntityPos.xyz); 
 
				// Set blend position 
				sgCopyVec3(mCameraPos.xyz, mBlendCameraPos.xyz); 
				float fH1 = mCameraPos.hpr[0]; 
				float fH2 = EntityPos.hpr[0] + mAttachedViewOffset.hpr[0]; 
				printf("b4 EntityPos       hpr <%.2f:%.2f:%.2f>\n", EntityPos.hpr[0], EntityPos.hpr[1], EntityPos.hpr[2]); 
				//printf("b4camhead %.2f fH1 %.2f fH2 %.2f\n", mCameraPos.hpr[0], fH1, fH2); 
				mCameraPos.hpr[0] = reBlendAngle(fH1, fH2, 1.0f); 
				mCameraPos.hpr[1] = -atan2(DPos[2], sqrt(DPos[0]*DPos[0] + DPos[1]*DPos[1])) * SG_RADIANS_TO_DEGREES + mAttachedViewOffset.hpr[1]; 
				mCameraPos.hpr[2] = mAttachedViewOffset.hpr[2]; 
				//printf("afcamhead %.2f fH1 %.2f fH2 %.2f\n", mCameraPos.hpr[0], fH1, fH2); 
				printf("af BlendCameraPos  hpr <%.2f:%.2f:%.2f>\n", mCameraPos.hpr[0], mCameraPos.hpr[1], mCameraPos.hpr[2]); 
 
				// Don't allow camera under terrain 
				float fAltitude = reGetAltitude(reGetWorld(), mCameraPos.xyz); 
				if (fAltitude < 0) 
					mCameraPos.xyz[2] += -fAltitude; 
 
				// Set camera 
				ssgSetCamera(&mCameraPos);*/ 
			} 
		} 
		break; 
 
		/*// KEEP OLD METHOD - FOR FUTURE USE??? 
		case eExternalAttachedView: 
		{ 
			// Looking at a target ? 
			if (mpTrackEntityRef) 
			{ 
				// Start with camera at entity position 
				sgCopyCoord(&mCameraPos, &EntityPos); 
 
				// Look at the target 
				sgVec3 DPos; 
				sgSubVec3(DPos, TrackEntityPos.xyz, EntityPos.xyz); 
				mCameraPos.hpr[0] = -atan2(DPos[0], DPos[1]) * SG_RADIANS_TO_DEGREES; 
				mCameraPos.hpr[1] = atan2(DPos[2], sqrt(DPos[0]*DPos[0] + DPos[1]*DPos[1])) * SG_RADIANS_TO_DEGREES; 
				mCameraPos.hpr[2] = 0; 
 
				// Slight offset so we can see the target 
				mCameraPos.hpr[1] -= 10.0f; 
 
				// Translate camera by offset 
				sgMat4 CAM, T; 
				sgMakeCoordMat4(CAM, &mCameraPos); 
				sgMakeTransMat4(T, mExternalViewOffset.xyz); 
				sgPreMultMat4(CAM, T); 
				sgSetCoord(&mCameraPos, CAM); 
 
				// Set camera 
				ssgSetCamera(&mCameraPos); 
			} 
			else // looking at entity 
			{ 
				// Start with camera at entity position 
				sgCopyCoord(&mCameraPos, &EntityPos); 
 
				// Translate camera by offset 
				sgMat4 CAM, T, R; 
				sgMakeCoordMat4(CAM, &mCameraPos); 
				sgMakeTransMat4(T, mExternalViewOffset.xyz); 
				sgMakeRotMat4(R, mExternalViewOffset.hpr); 
				sgPreMultMat4(CAM, R); 
				sgPreMultMat4(CAM, T); 
				sgSetCoord(&mCameraPos, CAM); 
 
				// Set camera 
				ssgSetCamera(&mCameraPos); 
			} 
		} 
		break;*/ 
 
	case eExternalTrackingView: 
		{ 
			// Looking at a target ? 
			if (mpTrackEntityRef) 
			{ 
				// Look at the target 
				sgVec3 DPos; 
				sgSubVec3(DPos, TrackEntityPos.xyz, mCameraPos.xyz); 
				mCameraPos.hpr[0] = -atan2(DPos[0], DPos[1]) * SG_RADIANS_TO_DEGREES; 
				mCameraPos.hpr[1] = atan2(DPos[2], sqrt(DPos[0]*DPos[0] + DPos[1]*DPos[1])) * SG_RADIANS_TO_DEGREES; 
				mCameraPos.hpr[2] = 0; 
 
				// Set camera 
				ssgSetCamera(&mCameraPos); 
			} 
			else // looking at entity 
			{ 
				// Look at the entity 
				sgVec3 DPos; 
				sgSubVec3(DPos, EntityPos.xyz, mCameraPos.xyz); 
				mCameraPos.hpr[0] = -atan2(DPos[0], DPos[1]) * SG_RADIANS_TO_DEGREES; 
				mCameraPos.hpr[1] = atan2(DPos[2], sqrt(DPos[0]*DPos[0] + DPos[1]*DPos[1])) * SG_RADIANS_TO_DEGREES; 
				mCameraPos.hpr[2] = 0; 
 
				// Set camera 
				ssgSetCamera(&mCameraPos); 
			} 
		} 
		break; 
 
	default: 
		// Set camera 
		ssgSetCamera(&mCameraPos); 
		break; 
	} 
}