/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Additional permissions under GNU GPL version 3 section 7 If you modify this Program, or any covered work, by linking or combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK (or a modified version of those libraries), containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the licensors of this Program grant you additional permission to convey the resulting work. Corresponding Source for a non-source form of such a combination shall include the source code for the parts of OpenSSL and IJG JPEG Library used as well as that of the covered work. You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ #include "HeadSpin.h" #include "plCameraBrain.h" #include "hsTimer.h" #include "hsResMgr.h" #include "plRefFlags.h" #include "plCameraModifier.h" #include "plVirtualCamNeu.h" #include "plgDispatch.h" #include "../plInterp/plController.h" #include "../pnSceneObject/plSceneObject.h" #include "../pnSceneObject/plCoordinateInterface.h" #include "../pnSceneObject/plSimulationInterface.h" #include "../pnSceneObject/plDrawInterface.h" #include "../plScene/plSceneNode.h" #include "../pnMessage/plCameraMsg.h" #include "../pnMessage/plPlayerPageMsg.h" #include "../pnMessage/plEnableMsg.h" #include "../pnMessage/plTimeMsg.h" #include "../plMessage/plInputEventMsg.h" #include "../plMessage/plLOSRequestMsg.h" #include "../plMessage/plLOSHitMsg.h" #include "../plMessage/plAvatarMsg.h" #include "../pnKeyedObject/plKey.h" #include "../plInputCore/plInputDevice.h" #include "../plInputCore/plInputManager.h" #include "../pnNetCommon/plNetApp.h" #include "../pfAnimation/plLineFollowMod.h" #include "../plAvatar/plAvatarMgr.h" #include "../plAvatar/plArmatureMod.h" #include "../plAvatar/plAvBrainHuman.h" #include "../plNetClient/plNetClientMgr.h" hsBool plCameraBrain1_FirstPerson::fDontFade = false; hsScalar plCameraBrain1::fFallAccel = 20.0f; hsScalar plCameraBrain1::fFallDecel = 5.0f; hsScalar plCameraBrain1::fFallVelocity = 50.0f; hsScalar plCameraBrain1::fFallPOAAccel = 10.0f; hsScalar plCameraBrain1::fFallPOADecel = 10.0f; hsScalar plCameraBrain1::fFallPOAVelocity = 50.0f; // basic camera brain now is a fixed brain by default. // if it doesn't have a subject (an object) it will just look straight ahead. // if there's a subject it will follow it. // the avatar and drive cameras are subclasses of the basic brain. plCameraBrain1::plCameraBrain1() : fCurCamSpeed(0.0f), fCurViewSpeed(0.0f), fVelocity(30.0f), fAccel(30.0f), fDecel(30.0f), fPOAVelocity(30.0f), fPOAAccel(30.0f), fPOADecel(30.0f), fSubjectKey(nil), fRail(nil), fXPanLimit(0.0f), fZPanLimit(0.0f), fPanSpeed(0.5f), fZoomMin(0.0f), fZoomMax(0.0f), fZoomRate(0.0f), fOffsetLength(0.0f), fOffsetPct(1.0f) { } plCameraBrain1::plCameraBrain1(plCameraModifier1* pMod) : fCurCamSpeed(0.0f), fCurViewSpeed(0.0f), fVelocity(30.0f), fAccel(30.0f), fDecel(30.0f), fPOAVelocity(30.0f), fPOAAccel(30.0f), fPOADecel(30.0f), fSubjectKey(nil), fRail(nil), fXPanLimit(0.0f), fZPanLimit(0.0f), fZoomMin(0.0f), fZoomMax(0.0f), fZoomRate(0.0f), fOffsetLength(0.0f), fOffsetPct(1.0f) { fCamera = pMod; pMod->SetBrain(this); fPOAGoal.Set(0,0,0); fGoal.Set(1,1,1); fPOAOffset.Set(0,0,0); fTargetMatrix.Make(&fGoal, &fPOAGoal, &hsVector3(0,0,1)); fFlags.Clear(); } plCameraBrain1::~plCameraBrain1() { } void plCameraBrain1::AddTarget() { fTargetMatrix = fCamera->GetTarget()->GetCoordinateInterface()->GetLocalToWorld(); hsVector3 view; fTargetMatrix.GetAxis(0, &view, 0); fGoal = fTargetMatrix.GetTranslate(); fPOAGoal = fGoal - view; fCamera->SetTargetPos(fGoal); fCamera->SetTargetPOA(fPOAGoal); } // called when we are pushed on top of the camera stack (or re-activated by another popping off directly above) void plCameraBrain1::Push(hsBool recenter) { if (fFlags.IsBitSet(kRailComponent)) { fRail->Init(); } if (recenter) plInputManager::SetRecenterMouse(false); fOffsetPct = 1.0f; // force update once to pop into position before we render SetFlags(kCutPOAOnce); SetFlags(kCutPosOnce); Update(true); } // called when we pop off the camera stack - only if we are the current camera void plCameraBrain1::Pop() { ClearMovementFlag(S_SET_FREELOOK); } // set the goal to which we want to animate the fov void plCameraBrain1::SetFOVGoal(hsScalar w, hsScalar h, double t) { if (fFOVhGoal == h || h == fCamera->GetFOVh() && fFOVwGoal == w || w == fCamera->GetFOVw()) return; hsScalar dif = h - fCamera->GetFOVh(); fFOVhAnimRate = dif / ((hsScalar)t); fFOVhGoal = h; fFOVStartTime = hsTimer::GetSysSeconds(); fFOVEndTime = fFOVStartTime + t; if (w == 0.f) { fFOVwGoal = IMakeFOVwZoom(h); dif = fFOVwGoal - fCamera->GetFOVw(); fFOVwAnimRate = dif / ((float)t); } else { dif = w - fCamera->GetFOVw(); fFOVwAnimRate = dif / ((float)t); fFOVwGoal = w; } fFlags.SetBit(kAnimateFOV); } // set parameters for how this camera zooms FOV based on user input (mostly for telescopes) void plCameraBrain1::SetZoomParams(hsScalar max, hsScalar min, hsScalar rate) { fZoomRate = rate; fZoomMax = max; fZoomMin = min; fFlags.SetBit(kZoomEnabled); } // periodic update - forced means we are forcing an update at a non-normal time to "prime" the camera // into position before it renders the first time (hence the fake 10 second frame delta) void plCameraBrain1::Update(hsBool forced) { double secs = hsTimer::GetDelSysSeconds(); if (forced) secs = 10.0f; // is there a subject we are following? if (GetSubject()) { fTargetMatrix = fCamera->GetTarget()->GetCoordinateInterface()->GetLocalToWorld(); fGoal = fTargetMatrix.GetTranslate(); fPOAGoal = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); fPOAGoal += fPOAOffset; } else { // get view based on current orientation (we could be animated) if (fCamera->GetTarget()) { fTargetMatrix = fCamera->GetTarget()->GetCoordinateInterface()->GetLocalToWorld(); hsVector3 view; fTargetMatrix.GetAxis(0, &view, 0); fGoal = fTargetMatrix.GetTranslate(); fPOAGoal = fGoal - (view * 10); } } AdjustForInput(secs); IMoveTowardGoal(secs); IPointTowardGoal(secs); if (fFlags.IsBitSet(kAnimateFOV)) IAnimateFOV(secs); } // adjust FOV based on elapsed time void plCameraBrain1::IAnimateFOV(double time) { hsScalar dH = fFOVhAnimRate * hsTimer::GetDelSysSeconds(); hsScalar dW = fFOVwAnimRate * hsTimer::GetDelSysSeconds(); dH += fCamera->GetFOVh(); dW += fCamera->GetFOVw(); if ( (fFOVhAnimRate < 0.0f && dH <= fFOVhGoal) || (fFOVhAnimRate > 0.0f && dH >= fFOVhGoal) ) { dH = fFOVhGoal; } if ( (fFOVwAnimRate < 0.0f && dW <= fFOVwGoal) || (fFOVwAnimRate > 0.0f && dW >= fFOVwGoal) ) { dW = fFOVwGoal; } if (dW == fFOVwGoal && dH == fFOVhGoal) fFlags.ClearBit(kAnimateFOV); fCamera->SetFOV( dW, dH ); } // move the camera's origin point (not where it is looking) toward where it is going void plCameraBrain1::IMoveTowardGoal(double elapsedTime) { hsBool current = plVirtualCam1::Instance()->IsCurrentCamera(GetCamera()); if (fFlags.IsBitSet(kCutPos) || fFlags.IsBitSet(kNonPhys) || !current) { fCamera->SetTargetPos(fGoal); return; } if (fFlags.IsBitSet(kCutPosOnce)) { fCamera->SetTargetPos(fGoal); fFlags.ClearBit(kCutPosOnce); return; } hsVector3 dir(fGoal - fCamera->GetTargetPos()); hsScalar distToGoal=dir.Magnitude(); //smooth out stoppage... hsScalar adjMaxVel = fVelocity; if (distToGoal <= 5.0f && distToGoal > 0.1f) { hsScalar mult = (distToGoal - 5.0f)*0.1f; adjMaxVel = fVelocity - hsABS(fVelocity*mult); } if (distToGoal > 0.0f) dir.Normalize(); hsVector3 vel( dir * fCurCamSpeed ); if (fFlags.IsBitSet(kFalling)) IAdjustVelocity(plCameraBrain1::fFallAccel, plCameraBrain1::fFallDecel, &dir, &vel, plCameraBrain1::fFallVelocity, distToGoal, elapsedTime); else if (plVirtualCam1::Instance()->fUseAccelOverride) IAdjustVelocity(plVirtualCam1::Instance()->fAccel, plVirtualCam1::Instance()->fDecel, &dir, &vel, plVirtualCam1::Instance()->fVel, distToGoal, elapsedTime); else if (fFlags.IsBitSet(kPanicVelocity)) IAdjustVelocity(fAccel, fDecel, &dir, &vel, 1000.0f, distToGoal, elapsedTime); else if (fFlags.IsBitSet(kRunning) && fVelocity < 16.0f) // speed up when we run if necessary IAdjustVelocity(fAccel, fDecel, &dir, &vel, 16.0f, distToGoal, elapsedTime); else IAdjustVelocity(fAccel, fDecel, &dir, &vel, adjMaxVel, distToGoal, elapsedTime); fCurCamSpeed = vel.Magnitude(); hsScalar distMoved; if (fFlags.IsBitSet(kPanicVelocity)) distMoved = IClampVelocity(&vel, 1000.0f, elapsedTime); else if (fFlags.IsBitSet(kFalling)) distMoved = IClampVelocity(&vel, plCameraBrain1::fFallVelocity, elapsedTime); else if (fFlags.IsBitSet(kRunning) && fVelocity < 16.0f) distMoved = IClampVelocity(&vel, 16.0f, elapsedTime); else distMoved = IClampVelocity(&vel, fVelocity, elapsedTime); // compute final pos if (distMoved > distToGoal) fCamera->SetTargetPos(fGoal); else fCamera->SetTargetPos(fCamera->GetTargetPos() + vel); } void plCameraBrain1::SetMovementFlag(int f) { fMoveFlags.SetBit(f); } void plCameraBrain1::IPointTowardGoal(double elapsedTime) { hsBool current = plVirtualCam1::Instance()->IsCurrentCamera(GetCamera()); if (fFlags.IsBitSet(kCutPOA) || fFlags.IsBitSet(kNonPhys) || !current) { fCamera->SetTargetPOA(fPOAGoal); return; } if (fFlags.IsBitSet(kCutPOAOnce)) { fCamera->SetTargetPOA(fPOAGoal); fFlags.ClearBit(kCutPOAOnce); return; } hsVector3 dir(fPOAGoal - fCamera->GetTargetPOA()); hsScalar distToGoal=dir.Magnitude(); if (distToGoal > 0.0f) dir.Normalize(); // smooth out stoppage hsScalar adjMaxVel = fPOAVelocity; if (distToGoal <= 5.0f && distToGoal > 0.1f) { hsScalar mult = (distToGoal - 5.0f)*0.1f; adjMaxVel = fPOAVelocity - hsABS(fPOAVelocity*mult); } hsVector3 vel( dir * fCurViewSpeed ); if (fFlags.IsBitSet(kFalling)) IAdjustVelocity(plCameraBrain1::fFallPOAAccel, plCameraBrain1::fFallPOADecel, &dir, &vel, plCameraBrain1::fFallPOAVelocity, distToGoal, elapsedTime); else if (plVirtualCam1::Instance()->fUseAccelOverride) IAdjustVelocity(plVirtualCam1::Instance()->fAccel, plVirtualCam1::Instance()->fDecel, &dir, &vel, plVirtualCam1::Instance()->fVel, distToGoal, elapsedTime); else if (fFlags.IsBitSet(kPanicVelocity)) IAdjustVelocity(fPOAAccel, fPOADecel, &dir, &vel, 1000.0f, distToGoal, elapsedTime); else if (fFlags.IsBitSet(kRunning) && fPOAVelocity < 16.0f) IAdjustVelocity(fPOAAccel, fPOADecel, &dir, &vel, 16.0f, distToGoal, elapsedTime); else IAdjustVelocity(fPOAAccel, fPOADecel, &dir, &vel, adjMaxVel, distToGoal, elapsedTime); fCurViewSpeed = vel.Magnitude(); hsScalar distMoved; if (fFlags.IsBitSet(kPanicVelocity)) distMoved = IClampVelocity(&vel, 1000.0f, elapsedTime); else if (fFlags.IsBitSet(kFalling)) distMoved = IClampVelocity(&vel, plCameraBrain1::fFallPOAVelocity, elapsedTime); else if (fFlags.IsBitSet(kRunning) && fPOAVelocity < 16.0f) distMoved = IClampVelocity(&vel, 16.0f, elapsedTime); else distMoved = IClampVelocity(&vel, fPOAVelocity, elapsedTime); // compute final pos if (distMoved > distToGoal) fCamera->SetTargetPOA(fPOAGoal); else fCamera->SetTargetPOA(fCamera->GetTargetPOA() + vel); } void plCameraBrain1::IAdjustVelocity(hsScalar adjAccelRate, hsScalar adjDecelRate, hsVector3* dir, hsVector3* vel, hsScalar maxSpeed, hsScalar distToGoal, double elapsedTime) { hsScalar speed = vel->Magnitude(); // save current speed *vel = *dir * speed; // change vel to correct dir // compute accel/decel hsScalar finalAccelRate; if (IShouldDecelerate(adjDecelRate, speed, distToGoal)) { finalAccelRate = -adjDecelRate; } else { finalAccelRate = adjAccelRate; } if (finalAccelRate != 0) { // compute accel vector in the direction of the goal hsVector3 accelVec = *dir * finalAccelRate; accelVec = accelVec * (hsScalar)elapsedTime; // add acceleration to velocity *vel = *vel + accelVec; } else { *vel = *dir * maxSpeed; } } hsScalar plCameraBrain1::IClampVelocity(hsVector3* vel, hsScalar maxSpeed, double elapsedTime) { *vel = *vel * (hsScalar)elapsedTime; maxSpeed *= (hsScalar)elapsedTime; // clamp speed (clamp if going negative?) hsScalar distMoved = vel->Magnitude(); if (distMoved > maxSpeed) { vel->Normalize(); *vel = *vel * maxSpeed; return maxSpeed; } return distMoved; } hsBool plCameraBrain1::IShouldDecelerate(hsScalar decelSpeed, hsScalar curSpeed, hsScalar distToGoal) { if (decelSpeed == 0) // no deceleration return false; // compute distance required to stop, given decel speed (in units/sec sq) hsScalar stopTime = curSpeed / decelSpeed; hsScalar avgSpeed = curSpeed * .5f; hsScalar stopDist = avgSpeed * stopTime; return (hsABS(distToGoal) <= hsABS(stopDist)); // stopDist+avgSpeed? } // // Make adjustments to camera position based on // user input - NOTE this is for mouse-cursor based adjustment // void plCameraBrain1::AdjustForInput(double secs) { // move closer to camera target based on user input if (fOffsetPct < 1.0f) { hsVector3 v(fPOAGoal - fGoal); hsScalar len = v.Magnitude(); len = len - (len * fOffsetPct); v.Normalize(); fGoal = fGoal + (v * len); } } void plCameraBrain1::Read(hsStream* stream, hsResMgr* mgr) { hsKeyedObject::Read(stream, mgr); fPOAOffset.Read(stream); plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kSubject ); // SceneObject mgr->ReadKeyNotifyMe( stream, msg, plRefFlags::kActiveRef ); plGenRefMsg* msg2 = TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnRequest, 0, kRailComponent ); // SceneObject mgr->ReadKeyNotifyMe( stream, msg2, plRefFlags::kActiveRef ); fFlags.Read(stream); if (fFlags.IsBitSet(kFollowLocalAvatar)) { if (plNetClientApp::GetInstance() && plNetClientApp::GetInstance()->GetLocalPlayer()) SetSubject((plSceneObject*)plNetClientApp::GetInstance()->GetLocalPlayer()); plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey()); } fAccel = stream->ReadSwapFloat(); fDecel = stream->ReadSwapFloat(); fVelocity = stream->ReadSwapFloat(); fPOAAccel = stream->ReadSwapFloat(); fPOADecel = stream->ReadSwapFloat(); fPOAVelocity = stream->ReadSwapFloat(); fXPanLimit = stream->ReadSwapFloat(); fZPanLimit = stream->ReadSwapFloat(); fZoomRate = stream->ReadSwapFloat(); fZoomMin = stream->ReadSwapFloat(); fZoomMax = stream->ReadSwapFloat(); } void plCameraBrain1::Write(hsStream* stream, hsResMgr* mgr) { hsKeyedObject::Write(stream, mgr); fPOAOffset.Write(stream); mgr->WriteKey(stream, GetSubject()); mgr->WriteKey(stream, fRail); fFlags.Write(stream); stream->WriteSwapFloat(fAccel); stream->WriteSwapFloat(fDecel); stream->WriteSwapFloat(fVelocity); stream->WriteSwapFloat(fPOAAccel); stream->WriteSwapFloat(fPOADecel); stream->WriteSwapFloat(fPOAVelocity); stream->WriteSwapFloat(fXPanLimit); stream->WriteSwapFloat(fZPanLimit); stream->WriteSwapFloat(fZoomRate); stream->WriteSwapFloat(fZoomMin); stream->WriteSwapFloat(fZoomMax); } hsScalar plCameraBrain1::IMakeFOVwZoom(hsScalar fovH) const { hsScalar num = hsTan(hsScalarDegToRad(fovH / 2)) * hsTan(hsScalarDegToRad(fCamera->GetFOVw() / 2)); hsScalar denom = hsTan(hsScalarDegToRad(fCamera->GetFOVh() / 2)); return (hsScalar)(2 * hsABS(hsScalarRadToDeg(atan(num / denom)))); } hsBool plCameraBrain1::MsgReceive(plMessage* msg) { plCameraMsg* pCamMsg = plCameraMsg::ConvertNoRef(msg); if (pCamMsg) { if (pCamMsg->Cmd(plCameraMsg::kStartZoomIn)) { fFlags.SetBit(kAnimateFOV); fFOVhGoal = fZoomMin; fFOVwGoal = IMakeFOVwZoom(fZoomMin); fFOVwAnimRate = fFOVhAnimRate = -1*fZoomRate; return true; } else if (pCamMsg->Cmd(plCameraMsg::kStartZoomOut)) { fFlags.SetBit(kAnimateFOV); fFOVhGoal = fZoomMax; fFOVwGoal = IMakeFOVwZoom(fZoomMax); fFOVwAnimRate = fFOVhAnimRate = fZoomRate; return true; } else if (pCamMsg->Cmd(plCameraMsg::kStopZoom)) { fFlags.ClearBit(kAnimateFOV); return true; } else if (pCamMsg->Cmd(plCameraMsg::kNonPhysOn)) { fFlags.SetBit(kNonPhys); return true; } else if (pCamMsg->Cmd(plCameraMsg::kNonPhysOff)) { fFlags.ClearBit(kNonPhys); return true; } } plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg); if (pRefMsg) { if (pRefMsg->fType == kSubject) { if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) SetSubject((plSceneObject*)pRefMsg->GetRef()); else SetSubject(nil); return true; } else if (pRefMsg->fType == kRailComponent) { if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) { fFlags.SetBit(kRailComponent); fRail = (plRailCameraMod*)pRefMsg->GetRef(); } else { fRail = nil; fFlags.ClearBit(kRailComponent); } return true; } } plPlayerPageMsg* pPMsg = plPlayerPageMsg::ConvertNoRef(msg); if (pPMsg) { if (pPMsg->fPlayer == plNetClientMgr::GetInstance()->GetLocalPlayerKey()) { if (pPMsg->fUnload) { if (fFlags.IsBitSet(kFollowLocalAvatar)) SetSubject(nil); } else { plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kSubject ); // SceneObject hsgResMgr::ResMgr()->AddViaNotify(pPMsg->fPlayer, msg, plRefFlags::kPassiveRef); fFlags.SetBit(kCutPosOnce); fFlags.SetBit(kCutPOAOnce); } } return true; } plControlEventMsg* pCMsg = plControlEventMsg::ConvertNoRef(msg); if (pCMsg) { if (pCMsg->ControlActivated()) { SetMovementFlag(pCMsg->GetControlCode()); if (pCMsg->GetControlCode() == S_SET_FREELOOK) { plInputManager::SetRecenterMouse(true); plMouseDevice::HideCursor(); } if (pCMsg->GetControlCode() == B_CAMERA_ZOOM_IN && fFlags.IsBitSet(kZoomEnabled)) { fFlags.SetBit(kAnimateFOV); fFOVhGoal = fZoomMin; fFOVwGoal = IMakeFOVwZoom(fZoomMin); fFOVwAnimRate = fFOVhAnimRate = -1*fZoomRate; fFOVEndTime = hsTimer::GetSysSeconds() + 60; return true; } else if (pCMsg->GetControlCode() == B_CAMERA_ZOOM_OUT && fFlags.IsBitSet(kZoomEnabled)) { fFlags.SetBit(kAnimateFOV); fFOVhGoal = fZoomMax; fFOVwGoal = IMakeFOVwZoom(fZoomMax); fFOVwAnimRate = fFOVhAnimRate = fZoomRate; fFOVEndTime = hsTimer::GetSysSeconds() + 60; return true; } else if (pCMsg->GetControlCode() == B_CAMERA_RECENTER) { if (fFlags.IsBitSet(kZoomEnabled)) { fFlags.SetBit(kAnimateFOV); fFOVhGoal = fZoomMin + ((fZoomMax - fZoomMin) / 2); fFOVwGoal = IMakeFOVwZoom(fFOVhGoal); if (fCamera->GetFOVh() >= fFOVhGoal) fFOVwAnimRate = fFOVhAnimRate = -1*fZoomRate; else fFOVwAnimRate = fFOVhAnimRate = fZoomRate; fFOVEndTime = hsTimer::GetSysSeconds() + 60; } return true; } } else { ClearMovementFlag(pCMsg->GetControlCode()); if (pCMsg->GetControlCode() == S_SET_FREELOOK) { plInputManager::SetRecenterMouse(false); plMouseDevice::ShowCursor(); } else if ( (pCMsg->GetControlCode() == B_CAMERA_ZOOM_IN || pCMsg->GetControlCode() == B_CAMERA_ZOOM_OUT) && fFlags.IsBitSet(kZoomEnabled) ) { fFlags.ClearBit(kAnimateFOV); return true; } } return true; } return hsKeyedObject::MsgReceive(msg); } plSceneObject* plCameraBrain1::GetSubject() { if (fSubjectKey) return plSceneObject::ConvertNoRef(fSubjectKey->ObjectIsLoaded()); return nil; } void plCameraBrain1::SetSubject(plSceneObject* sub) { if (sub) fSubjectKey = sub->GetKey(); else fSubjectKey = nil; } // // // new drive mode brain // hsScalar plCameraBrain1_Drive::fTurnRate = 100.0f; hsScalar plCameraBrain1_Drive::fAcceleration = 200.0f; hsScalar plCameraBrain1_Drive::fDeceleration = 200.0f; hsScalar plCameraBrain1_Drive::fMaxVelocity = 100.0f; // constructor plCameraBrain1_Drive::plCameraBrain1_Drive() : plCameraBrain1() { fGoal.Set(100,100,100); fPOAGoal.Set(0,0,0); fUp.Set(0,0,1); fCamera->SetTargetPos(fGoal); fCamera->SetTargetPOA(fPOAGoal); fLastTime = 0.f; } plCameraBrain1_Drive::plCameraBrain1_Drive(plCameraModifier1* pMod) : plCameraBrain1(pMod) { fGoal.Set(100,100,100); fPOAGoal.Set(0,0,0); fUp.Set(0,0,1); fCamera->SetTargetPos(fGoal); fCamera->SetTargetPOA(fPOAGoal); fLastTime = 0.f; } // destructor plCameraBrain1_Drive::~plCameraBrain1_Drive() { } void plCameraBrain1_Drive::Push(hsBool recenter) { plCameraBrain1::Push(recenter); plInputManager::SetRecenterMouse(true); fLastTime = hsTimer::GetSeconds(); } void plCameraBrain1_Drive::Pop() { plInputManager::SetRecenterMouse(false); } // // Update Method // void plCameraBrain1_Drive::Update(hsBool forced) { fTargetMatrix.Make(&fGoal, &fPOAGoal, &(-1*fUp)); // update our desired position: double time = hsTimer::GetSeconds(); hsScalar eTime = (hsScalar)(time-fLastTime); if(eTime > 0.01f) eTime = 0.01f; fLastTime = time; hsPoint3 cameraPos = fCamera->GetTargetPos(); hsVector3 view, up, right; fTargetMatrix.GetAxis(&view,&up,&right); hsScalar delta = 5.0f * eTime; // adjust speed if (HasMovementFlag(B_CAMERA_DRIVE_SPEED_UP)) { plCameraBrain1_Drive::fAcceleration += delta; plCameraBrain1_Drive::fDeceleration += delta; plCameraBrain1_Drive::fMaxVelocity += delta; } if (HasMovementFlag(B_CAMERA_DRIVE_SPEED_DOWN)) { plCameraBrain1_Drive::fAcceleration -= delta; plCameraBrain1_Drive::fDeceleration -= delta; plCameraBrain1_Drive::fMaxVelocity -= delta; if (plCameraBrain1_Drive::fAcceleration <= 0.0f) plCameraBrain1_Drive::fAcceleration = 0.0f; if (plCameraBrain1_Drive::fTurnRate <= 0.0f) plCameraBrain1_Drive::fTurnRate = 0.0f; if (plCameraBrain1_Drive::fDeceleration <= 0.0f) plCameraBrain1_Drive::fDeceleration = 0.0f; if (plCameraBrain1_Drive::fMaxVelocity <= 0.0f) plCameraBrain1_Drive::fMaxVelocity = 0.0f; } if (plVirtualCam1::Instance()->fUseAccelOverride) { fMaxVelocity = plVirtualCam1::Instance()->fVel; } hsScalar speed = fMaxVelocity; hsScalar turn = 1.0f; if (HasMovementFlag(B_CONTROL_MODIFIER_FAST)) { turn *= 0.25; speed *= 10; } if (HasMovementFlag(B_CAMERA_MOVE_FORWARD)) { cameraPos += view * speed * eTime; } if (HasMovementFlag(B_CAMERA_MOVE_BACKWARD)) { cameraPos += view * speed * eTime * -1; } if (HasMovementFlag(B_CAMERA_MOVE_LEFT)) { cameraPos += right * speed * eTime * -1; } if (HasMovementFlag(B_CAMERA_MOVE_RIGHT)) { cameraPos += right * speed * eTime; } if (HasMovementFlag(B_CAMERA_MOVE_DOWN)) { cameraPos += up * speed * eTime * -1; } if (HasMovementFlag(B_CAMERA_MOVE_UP)) { cameraPos += up * speed * eTime; } fGoal = cameraPos; IMoveTowardGoal(eTime); hsMatrix44 rot; // make sure camera is perpindicular to absolute up fTargetMatrix.fMap[0][2] = 0.0f; fTargetMatrix.fMap[1][2] = 0.0f; fTargetMatrix.GetAxis(&view,&up,&right); if ( HasMovementFlag( B_CAMERA_ROTATE_RIGHT ) || HasMovementFlag( B_CAMERA_ROTATE_LEFT ) ) { hsQuat q(turn * fTurnRate * eTime * deltaX, &up); q.NormalizeIfNeeded(); q.MakeMatrix(&rot); ClearMovementFlag( B_CAMERA_ROTATE_RIGHT ); ClearMovementFlag( B_CAMERA_ROTATE_LEFT ); fTargetMatrix = rot * fTargetMatrix; } rot.Reset(); if ( HasMovementFlag( B_CAMERA_ROTATE_UP ) || HasMovementFlag(B_CAMERA_ROTATE_DOWN) ) { hsQuat q(turn * fTurnRate* eTime * deltaY, &right); q.NormalizeIfNeeded(); q.MakeMatrix(&rot); ClearMovementFlag( B_CAMERA_ROTATE_UP ); ClearMovementFlag( B_CAMERA_ROTATE_DOWN ); fTargetMatrix = rot * fTargetMatrix; } fTargetMatrix.GetAxis(&view,&up,&right); hsPoint3 at(fGoal + (view * 2.0f)); // Now passing in Up for up parameter, instead of down. mf_flip_up - mf fTargetMatrix.MakeCamera(&fGoal, &at, &up); fPOAGoal = at; fUp = up; fCamera->SetTargetPos(fGoal); fCamera->SetTargetPOA(fPOAGoal); } hsBool plCameraBrain1_Drive::MsgReceive(plMessage* msg) { plMouseEventMsg* pMouseMsg = plMouseEventMsg::ConvertNoRef(msg); if( pMouseMsg ) { if (pMouseMsg->GetDX() > 0.4 || pMouseMsg->GetDX() < -0.4) { bDisregardY = true; return true; } else if (pMouseMsg->GetDY() > 0.4 || pMouseMsg->GetDY() < -0.4) { bDisregardX = true; return true; } if (bDisregardY && pMouseMsg->GetDY() != 0.0) { bDisregardY = false; return true; } if (bDisregardX && pMouseMsg->GetDX() != 0.0) { bDisregardX = false; return true; } if (pMouseMsg->GetDX() < 0) { SetMovementFlag( B_CAMERA_ROTATE_RIGHT ); deltaX = pMouseMsg->GetDX(); } else if (pMouseMsg->GetDX() > 0) { SetMovementFlag( B_CAMERA_ROTATE_LEFT ); deltaX = pMouseMsg->GetDX(); } else if (pMouseMsg->GetDY() > 0) { SetMovementFlag( B_CAMERA_ROTATE_DOWN ); deltaY = pMouseMsg->GetDY(); } else if (pMouseMsg->GetDY() < 0) { deltaY = pMouseMsg->GetDY(); SetMovementFlag( B_CAMERA_ROTATE_UP ); } return true; } return plCameraBrain1::MsgReceive(msg); } // // // // new simplified avatar camera // constructor plCameraBrain1_Avatar::plCameraBrain1_Avatar() : plCameraBrain1() { bObscured = false; fOffset.Set(0,0,0); fPOAOffset.Set(0,0,0); fFaded = false; } plCameraBrain1_Avatar::plCameraBrain1_Avatar(plCameraModifier1* pMod) : plCameraBrain1(pMod) { bObscured = false; fOffset.Set(0,0,0); fPOAOffset.Set(0,0,0); fFaded = false; } // destructor plCameraBrain1_Avatar::~plCameraBrain1_Avatar() { if (!plNetClientMgr::GetInstance()) return; if (fFaded) { plCameraTargetFadeMsg* pMsg = TRACKED_NEW plCameraTargetFadeMsg; pMsg->SetFadeOut(false); pMsg->SetSubjectKey(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); pMsg->SetBCastFlag(plMessage::kBCastByExactType); pMsg->SetBCastFlag(plMessage::kNetPropagate, FALSE); pMsg->AddReceiver(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); plgDispatch::MsgSend(pMsg); } } void plCameraBrain1_Avatar::Pop() { bObscured = false; if (fFaded) ISendFadeMsg(false); plCameraBrain1::Pop(); } void plCameraBrain1_Avatar::Push(hsBool recenter) { bObscured = false; fFallTimer = 0.0f; plCameraBrain1::Push(recenter); } // // Update Method // void plCameraBrain1_Avatar::Update(hsBool forced) { if (!GetSubject()) return; if (HasFlag(kBeginFalling) && hsTimer::GetSysSeconds() >= fFallTimer) { fFallTimer = 0.0f; fFlags.SetBit(kFalling); fFlags.ClearBit(kBeginFalling); plVirtualCam1::Instance()->SetFlags(plVirtualCam1::kFalling); plVirtualCam1::Instance()->StartUnPan(); fOffsetPct = 1.0f; } // determine elapsed time; double secs = hsTimer::GetDelSysSeconds(); if (forced) secs = 10.0f; if (fFlags.IsBitSet(kIsTransitionCamera)) { if (GetKey()) hsStatusMessageF("%s thinks it's the transition camera\n",GetKeyName()); } else { CalculatePosition(); AdjustForInput(secs); } IMoveTowardGoal(secs); IPointTowardGoal(secs); if (fFlags.IsBitSet(kAnimateFOV)) IAnimateFOV(secs); } // // Calculate the best position for the camera in basic follow mode: // void plCameraBrain1_Avatar::CalculatePosition() { if (!GetSubject() || !GetSubject()->GetCoordinateInterface()) return; // get target pos hsPoint3 targetFrom = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); // get view normal hsVector3 view = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); hsVector3 right = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kRight); // compute camera position and interest if (HasFlag(kWorldspacePos)) { fGoal = targetFrom + GetOffset(); } else { fGoal = targetFrom; if (!fFlags.IsBitSet(kFalling)) { fGoal = fGoal - view * GetOffset().fY; fGoal = fGoal - right * GetOffset().fX; fGoal.fZ += GetOffset().fZ; } else { fGoal = fGoal - view * 0.5; fGoal.fZ += 20; } } if (HasFlag(kWorldspacePOA)) { fPOAGoal = targetFrom + GetPOAOffset(); } else { fPOAGoal = targetFrom; //if (!fFlags.IsBitSet(kFalling)) { fPOAGoal = fPOAGoal - view * GetPOAOffset().fY; fPOAGoal = fPOAGoal - right * GetPOAOffset().fX; fPOAGoal.fZ += GetPOAOffset().fZ; } } // check to see if we've lagged too far behind hsVector3 dist(fCamera->GetTargetPos() - fGoal); if (dist.MagnitudeSquared() > (4*fOffset.MagnitudeSquared())) SetFlags(kPanicVelocity); else ClearFlags(kPanicVelocity); // check LOS if (GetCamera()->GetKey() && fFlags.IsBitSet(kMaintainLOS) && (plVirtualCam1::Instance()->GetCurrentStackCamera() == GetCamera())) { plLOSRequestMsg* pMsg = TRACKED_NEW plLOSRequestMsg( GetCamera()->GetKey(), fPOAGoal, fGoal, plSimDefs::kLOSDBCameraBlockers, plLOSRequestMsg::kTestClosest, plLOSRequestMsg::kReportHitOrMiss); plgDispatch::MsgSend( pMsg ); } if (bObscured) { fGoal = fHitPoint + (fHitNormal * 0.5f); hsVector3 newOff(fGoal - fPOAGoal); hsVector3 actualOff(fCamera->GetTargetPos() - fPOAGoal); if (newOff.MagnitudeSquared() <= 16.0f && actualOff.MagnitudeSquared() <= 16.0f && !fFaded) { ISendFadeMsg(true); fFaded = true; } else if (newOff.MagnitudeSquared() >= 16.0f && fFaded) { ISendFadeMsg(false); fFaded = false; } } else if (fFaded && !bObscured) { ISendFadeMsg(false); fFaded = false; } } void plCameraBrain1_Avatar::IHandleObstacle() { // swing around the 'obstacle' } void plCameraBrain1_Avatar::ISendFadeMsg(hsBool fade) { if (plVirtualCam1::IsCurrentCamera(GetCamera())) { if (fade) plVirtualCam1::AddMsgToLog("current camera sending Fade Out message to Avatar"); else plVirtualCam1::AddMsgToLog("current camera sending Fade In message to Avatar"); plCameraTargetFadeMsg* pMsg = TRACKED_NEW plCameraTargetFadeMsg; pMsg->SetFadeOut(fade); pMsg->SetSubjectKey(GetSubject()->GetKey()); pMsg->SetBCastFlag(plMessage::kBCastByExactType); pMsg->SetBCastFlag(plMessage::kNetPropagate, FALSE); pMsg->AddReceiver(GetSubject()->GetKey()); plgDispatch::MsgSend(pMsg); } } hsBool plCameraBrain1_Avatar::MsgReceive(plMessage* msg) { plLOSHitMsg *pLOSMsg = plLOSHitMsg::ConvertNoRef( msg ); if( pLOSMsg ) { bObscured = !pLOSMsg->fNoHit; fHitPoint = pLOSMsg->fHitPoint; fHitNormal = pLOSMsg->fNormal; if (pLOSMsg->fHitFlags & plSimDefs::kLOS_CameraAvoidObject) fObstacle = (plSceneObject*)pLOSMsg->fObj->GetObjectPtr(); else fObstacle = nil; return true; } plMouseEventMsg* pMouseMsg = plMouseEventMsg::ConvertNoRef(msg); if( pMouseMsg ) { if (pMouseMsg->GetButton() == kWheelPos || pMouseMsg->GetButton() == kWheelNeg) { if (fFlags.IsBitSet(kFalling)) return true; fOffsetPct += -1 * ( (pMouseMsg->GetWheelDelta() / 24) * 0.01f); if (fOffsetPct > 1.0f) fOffsetPct = 1.0f; else if (fOffsetPct < 0.1f) fOffsetPct = 0.1f; return true; } } plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg); if (pRefMsg) { if (pRefMsg->fType == kSubject) { if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) { SetSubject((plSceneObject*)pRefMsg->GetRef()); plSceneObject* avSO = nil; if (plNetClientMgr::GetInstance()) avSO = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer()); if (GetSubject() == avSO) { plArmatureMod* avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); if (avMod) avMod->RegisterForBehaviorNotify(GetKey()); } } else SetSubject(nil); return true; } } plAvatarBehaviorNotifyMsg *behNotifymsg = plAvatarBehaviorNotifyMsg::ConvertNoRef(msg); if (behNotifymsg) { if (behNotifymsg->fType == plHBehavior::kBehaviorTypeFall && fFlags.IsBitSet(kVerticalWhenFalling)) { if (behNotifymsg->state) { SetFlags(kBeginFalling); fFallTimer = hsTimer::GetSysSeconds() + plVirtualCam1::fFallTimerDelay; } else if (!behNotifymsg->state) { if (fFlags.IsBitSet(kFalling)) { plVirtualCam1::Instance()->ClearFlags(plVirtualCam1::kFalling); fFlags.ClearBit(kFalling); } else { ClearFlags(kBeginFalling); fFallTimer = 0.0f; } } } else if (behNotifymsg->fType == plHBehavior::kBehaviorTypeFall) { if (behNotifymsg->state && fFlags.IsBitSet(kSpeedUpWhenRunning)) fFlags.SetBit(kRunning); else fFlags.ClearBit(kRunning); } return true; } return plCameraBrain1::MsgReceive(msg); } void plCameraBrain1_Avatar::Read(hsStream* stream, hsResMgr* mgr) { plCameraBrain1::Read(stream, mgr); fOffset.Read(stream); SetFlags(kCutPOA); if (fFlags.IsBitSet(kFollowLocalAvatar) && GetSubject()) { plSceneObject* avSO = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer()); if (GetSubject() == avSO) { plArmatureMod* avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); if (avMod) avMod->RegisterForBehaviorNotify(GetKey()); } } } void plCameraBrain1_Avatar::Write(hsStream* stream, hsResMgr* mgr) { plCameraBrain1::Write(stream, mgr); fOffset.Write(stream); } // // first person cam, derived from avatar cam - // only difference is push() & pop() fade avatar in/out // plCameraBrain1_FirstPerson::plCameraBrain1_FirstPerson() : plCameraBrain1_Avatar() { fPosNode = nil; } plCameraBrain1_FirstPerson::plCameraBrain1_FirstPerson(plCameraModifier1* pMod) : plCameraBrain1_Avatar(pMod) { } // destructor plCameraBrain1_FirstPerson::~plCameraBrain1_FirstPerson() { } hsBool plCameraBrain1_FirstPerson::MsgReceive(plMessage* msg) { plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg); if (pRefMsg) { if (pRefMsg->fType == kSubject) { if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) { fPosNode = nil; SetSubject((plSceneObject*)pRefMsg->GetRef()); // are we the built-in 1st person camera? If we are, change our subject pointer to FPCameraOrigin node on the avatar plUoid U(kDefaultCameraMod1_KEY); plKey pKey = hsgResMgr::ResMgr()->FindKey(U); if (pKey && pKey == fCamera->GetKey()) { const plCoordinateInterface* ci = GetSubject()->GetCoordinateInterface(); for (int i = 0; i < ci->GetNumChildren(); i++) { plSceneObject* child = (plSceneObject*)ci->GetChild(i)->GetOwner(); if (child) { const char* name = child->GetKeyName(); if (stricmp(name, "FPCameraOrigin") == 0) { fPosNode = child; SetOffset(hsVector3(0,0,0)); SetPOAOffset(hsVector3(0,-10,0)); return true; } } } } } else { fPosNode = nil; SetSubject(nil); } return true; } } plMouseEventMsg* pMouseMsg = plMouseEventMsg::ConvertNoRef(msg); if( pMouseMsg ) { if (pMouseMsg->GetButton() == kWheelPos || pMouseMsg->GetButton() == kWheelNeg) { return true; } } return plCameraBrain1_Avatar::MsgReceive(msg); } void plCameraBrain1_FirstPerson::CalculatePosition() { // get target pos hsPoint3 targetFrom; if (fPosNode) targetFrom = fPosNode->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); else targetFrom = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); // get view normal hsVector3 view = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); hsVector3 right = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kRight); // compute camera position and interest if (HasFlag(kWorldspacePos)) { fGoal = targetFrom + GetOffset(); } else { fGoal = targetFrom; fGoal = fGoal - view * GetOffset().fY; fGoal = fGoal - right * GetOffset().fX; fGoal.fZ += GetOffset().fZ; } if (HasFlag(kWorldspacePOA)) { fPOAGoal = targetFrom + GetPOAOffset(); } else { fPOAGoal = targetFrom; fPOAGoal = fPOAGoal - view * GetPOAOffset().fY; fPOAGoal = fPOAGoal - right * GetPOAOffset().fX; fPOAGoal.fZ += GetPOAOffset().fZ; } // check to see if we've lagged too far behind hsVector3 dist(fCamera->GetTargetPos() - fGoal); if (dist.MagnitudeSquared() > (4*fOffset.MagnitudeSquared())) SetFlags(kPanicVelocity); else ClearFlags(kPanicVelocity); // check LOS if (GetCamera()->GetKey() && fFlags.IsBitSet(kMaintainLOS) && (plVirtualCam1::Instance()->GetCurrentStackCamera() == GetCamera())) { plLOSRequestMsg* pMsg = TRACKED_NEW plLOSRequestMsg( GetCamera()->GetKey(), fPOAGoal, fGoal, plSimDefs::kLOSDBCameraBlockers, plLOSRequestMsg::kTestClosest, plLOSRequestMsg::kReportHitOrMiss); plgDispatch::MsgSend( pMsg ); } if (bObscured) { fGoal = fHitPoint + (fHitNormal * 0.5f); hsVector3 newOff(fGoal - fPOAGoal); hsVector3 actualOff(fCamera->GetTargetPos() - fPOAGoal); if (newOff.MagnitudeSquared() <= 16.0f && actualOff.MagnitudeSquared() <= 16.0f && !fFaded) { ISendFadeMsg(true); fFaded = true; } else if (newOff.MagnitudeSquared() >= 16.0f && fFaded) { ISendFadeMsg(false); fFaded = false; } } else if (fFaded && !bObscured) { ISendFadeMsg(false); fFaded = false; } } void plCameraBrain1_FirstPerson::Push(hsBool recenter) { if (!GetSubject()) return; if (plCameraBrain1_FirstPerson::fDontFade) return; plEnableMsg* pMsg = TRACKED_NEW plEnableMsg; pMsg->SetCmd(plEnableMsg::kDisable); pMsg->AddType(plEnableMsg::kDrawable); pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); pMsg->AddReceiver(GetSubject()->GetKey()); pMsg->SetSender(GetCamera()->GetKey()); plgDispatch::MsgSend(pMsg); plCameraBrain1::Push(recenter); ISendFadeMsg(true); } void plCameraBrain1_FirstPerson::Pop() { plCameraBrain1::Pop(); if (!GetSubject()) return; ISendFadeMsg(false); } // // true fixed cam, derived from basic cam - // Has a separate interest point object it maintains // plCameraBrain1_Fixed::plCameraBrain1_Fixed() : plCameraBrain1() { fTargetPoint= nil; } plCameraBrain1_Fixed::plCameraBrain1_Fixed(plCameraModifier1* pMod) : plCameraBrain1(pMod) { fTargetPoint = nil; } // destructor plCameraBrain1_Fixed::~plCameraBrain1_Fixed() { } void plCameraBrain1_Fixed::Read(hsStream* stream, hsResMgr* mgr) { plCameraBrain1::Read(stream, mgr); mgr->ReadKeyNotifyMe( stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, 99), plRefFlags::kPassiveRef); } void plCameraBrain1_Fixed::Write(hsStream* stream, hsResMgr* mgr) { plCameraBrain1::Write(stream, mgr); mgr->WriteKey(stream, fTargetPoint); } void plCameraBrain1_Fixed::Update(hsBool forced) { double secs = hsTimer::GetDelSysSeconds(); if (forced) secs = 10.0f; if (!fFlags.IsBitSet(kIsTransitionCamera)) { if(fTargetPoint) fTargetPoint->GetBrain()->Update(); if (GetSubject()) { fTargetMatrix = fCamera->GetTarget()->GetCoordinateInterface()->GetLocalToWorld(); fGoal = fTargetMatrix.GetTranslate(); // get target pos hsPoint3 targetFrom = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); // get view normal hsVector3 view = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); hsVector3 right = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kRight); // compute camera position and interest if (HasFlag(kWorldspacePOA)) { fPOAGoal = targetFrom + GetPOAOffset(); } else { fPOAGoal = targetFrom; fPOAGoal = fPOAGoal - view * GetPOAOffset().fY; fPOAGoal = fPOAGoal - right * GetPOAOffset().fX; fPOAGoal.fZ += GetPOAOffset().fZ; } } else { if (fCamera->GetTarget()) { fTargetMatrix = fCamera->GetTarget()->GetCoordinateInterface()->GetLocalToWorld(); hsVector3 view; hsVector3 up; fTargetMatrix.GetAxis(0, &view, &up); fGoal = fTargetMatrix.GetTranslate(); if (fTargetPoint) fPOAGoal = fTargetPoint->GetBrain()->GetGoal(); else fPOAGoal = fGoal - (view * 10); } } if (fFlags.IsBitSet(kRailComponent) && fRail) { if (fCurCamSpeed == 0) fCurCamSpeed = 1.0f; if (forced) fGoal = fRail->GetGoal(10, 10); else fGoal = fRail->GetGoal(secs, fCurCamSpeed); } AdjustForInput(secs); } IMoveTowardGoal(secs); IPointTowardGoal(secs); if (fFlags.IsBitSet(kAnimateFOV)) IAnimateFOV(secs); } void plCameraBrain1_Fixed::CalculatePosition() { } hsBool plCameraBrain1_Fixed::MsgReceive(plMessage* msg) { plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg); if (pRefMsg) { if (pRefMsg->GetContext() == plRefMsg::kOnCreate && pRefMsg->fType == 99) { fTargetPoint = (plCameraModifier1*)(pRefMsg->GetRef()); } } return (plCameraBrain1::MsgReceive(msg)); } // // // // circle camera crap static const hsScalar kTwoPI = 2.0f*hsScalarPI; plCameraBrain1_Circle::plCameraBrain1_Circle() : plCameraBrain1_Fixed() { fCircleFlags = 0; fCenterObject = nil; fCenter.Set(0,0,0); fCurRad = fGoalRad = 1; fPOAObj = nil; fCirPerSec = 0.25f; } plCameraBrain1_Circle::plCameraBrain1_Circle(plCameraModifier1* pMod) : plCameraBrain1_Fixed(pMod) { fCircleFlags = 0; fCenterObject = nil; fCenter.Set(0,0,0); fCurRad = fGoalRad = 1; fPOAObj = nil; fCirPerSec = 0.25f; } plCameraBrain1_Circle::~plCameraBrain1_Circle() { } // // // void plCameraBrain1_Circle::Update(hsBool forced) { double secs = hsTimer::GetDelSysSeconds(); if (forced) secs = 10.0f; if (!GetSubject() || !GetSubject()->GetCoordinateInterface()) return; hsPoint3 sub = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); fGoal = IGetClosestPointOnCircle(&sub); if (fPOAObj && fPOAObj->GetCoordinateInterface()) fPOAGoal = fPOAObj->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); else fPOAGoal = GetCenterPoint(); fPOAGoal += fPOAOffset; if (HasFlag(kCutPosOnce)) { fGoal = MoveTowardsFromGoal(&fCamera->GetTargetPos(), secs, true); fFlags.ClearBit(kCutPos); } else { fGoal = MoveTowardsFromGoal(&fCamera->GetTargetPos(), secs); fFlags.SetBit(kCutPos); } AdjustForInput(secs); IMoveTowardGoal(secs); IPointTowardGoal(secs); if (fFlags.IsBitSet(kAnimateFOV)) IAnimateFOV(secs); } // // keep us on the circle // hsPoint3 plCameraBrain1_Circle::MoveTowardsFromGoal(const hsPoint3* fromGoal, double secs, hsBool warp) { if (fCurRad != fGoalRad) { hsScalar dist = hsABS(fGoalRad-fCurRad); hsAssert(dist>=0 && dist<=kTwoPI, "illegal radian diff"); hsBool mustWrap = (dist > hsScalarPI); // go opposite direction for shortcut and wrap // compute speed hsScalar speed; if (warp) speed = (hsScalar)(kTwoPI * 100 * secs); else speed = (hsScalar)(kTwoPI * fCirPerSec * secs); // move towards goalRad hsAssert(fCurRad>=0 && fCurRad<=kTwoPI, "illegal radian value"); if (fCurRadfGoalRad) fCurRad=fGoalRad; } } else { if (mustWrap) { fCurRad+=speed; hsBool didWrap=false; while(fCurRad>kTwoPI) { didWrap=true; fCurRad-=kTwoPI; } if (fCurRad>fGoalRad && didWrap) fCurRad=fGoalRad; } else { fCurRad-=speed; if (fCurRad=0 && fCurRad<=kTwoPI, "illegal radian value"); hsPoint3 x; x = GetCenterPoint() + hsVector3((hsScalar)hsCosine(fCurRad)*fRadius, (hsScalar)hsSine(fCurRad)*fRadius, 0.0f); x.fZ = fCamera->GetTargetPos().fZ; return x; } hsPoint3 plCameraBrain1_Circle::IGetClosestPointOnCircle(const hsPoint3* toThis) { hsPoint3 center=GetCenterPoint(); hsPoint3 p(toThis->fX, toThis->fY, center.fZ); // Move to plane of circle hsVector3 v; if (!(GetCircleFlags() & kFarthest) ) { v = hsVector3(&p, ¢er); } else { // farthest v = hsVector3(¢er, &p); } v.Normalize(); fGoalRad = (hsScalar)atan2(v.fY, v.fX); // -pi to pi hsAssert(fGoalRad>=-hsScalarPI && fGoalRad<=hsScalarPI, "Illegal atan2 val"); if (fGoalRad<0) fGoalRad = kTwoPI + fGoalRad; // 0 to 2pi hsAssert(fGoalRad>=0 && fGoalRad<=kTwoPI, "Illegal atan2 val"); v = v * fRadius; center = center + v; center.fZ = fCamera->GetTargetPos().fZ; return (center); } hsPoint3 plCameraBrain1_Circle::GetCenterPoint() { if (fCenterObject && fCenterObject->GetCoordinateInterface()) { return fCenterObject->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); } return fCenter; } void plCameraBrain1_Circle::Write(hsStream* stream, hsResMgr* mgr) { plCameraBrain1::Write(stream, mgr); stream->WriteSwap32(GetCircleFlags()); fCenter.Write(stream); stream->WriteSwapScalar(GetRadius()); mgr->WriteKey(stream, fCenterObject); mgr->WriteKey(stream, fPOAObj); stream->WriteSwapScalar(fCirPerSec); } void plCameraBrain1_Circle::Read(hsStream* stream, hsResMgr* mgr) { plCameraBrain1::Read(stream, mgr); SetCircleFlags(stream->ReadSwap32()); if (GetCircleFlags() & kCircleLocalAvatar) { if (plNetClientApp::GetInstance() && plNetClientApp::GetInstance()->GetLocalPlayer()) SetPOAObject((plSceneObject*)plNetClientApp::GetInstance()->GetLocalPlayer()); plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey()); } fCenter.Read(stream); SetRadius(stream->ReadSwapScalar()); plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kCircleTarget ); // SceneObject mgr->ReadKeyNotifyMe( stream, msg, plRefFlags::kActiveRef ); plGenRefMsg* msg2 = TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnRequest, 0, kPOAObject ); // SceneObject mgr->ReadKeyNotifyMe( stream, msg2, plRefFlags::kActiveRef ); fCirPerSec = stream->ReadSwapScalar(); plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); } hsBool plCameraBrain1_Circle::MsgReceive(plMessage* msg) { plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg); if (pRefMsg) { if (pRefMsg->fType == kCircleTarget) { if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) fCenterObject = (plSceneObject*)pRefMsg->GetRef(); else fCenterObject = nil; return true; } else if (pRefMsg->fType == kPOAObject) { if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) fPOAObj = (plSceneObject*)pRefMsg->GetRef(); else fPOAObj = nil; return true; } else if (pRefMsg->fType == kSubject) { if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) SetSubject((plSceneObject*)pRefMsg->GetRef()); else SetSubject(nil); return true; } } plPlayerPageMsg* pPMsg = plPlayerPageMsg::ConvertNoRef(msg); if (pPMsg && pPMsg->fPlayer == plNetClientMgr::GetInstance()->GetLocalPlayerKey()) { if (pPMsg->fUnload) { if (GetCircleFlags() & kCircleLocalAvatar) fPOAObj = nil; if (fFlags.IsBitSet(kFollowLocalAvatar)) SetSubject(nil); } else { if (GetCircleFlags() & kCircleLocalAvatar) { SetPOAObject((plSceneObject*)pPMsg->fPlayer->GetObjectPtr()); fFlags.SetBit(kCutPOA); } if (fFlags.IsBitSet(kFollowLocalAvatar)) { plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kSubject ); // SceneObject hsgResMgr::ResMgr()->AddViaNotify(pPMsg->fPlayer, msg, plRefFlags::kPassiveRef); fFlags.SetBit(kCutPosOnce); fFlags.SetBit(kCutPOA); } } return true; } return (plCameraBrain1_Fixed::MsgReceive(msg)); }