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