/*==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 . 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" //#define aspect_HDTV // maybe someday we'll be on the xbox... #ifdef aspect_HDTV #define FOV_RATIO 1.78 #else #define FOV_RATIO 1.33333333 #endif 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 h, double t) { if (fFOVGoal == h || h == fCamera->GetFOVh()) return; hsScalar dif = h - fCamera->GetFOVh(); fFOVAnimRate = dif / ((hsScalar)t); fFOVGoal = h; fFOVStartTime = hsTimer::GetSysSeconds(); fFOVEndTime = fFOVStartTime + t; 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 = fFOVAnimRate * hsTimer::GetDelSysSeconds(); dH += fCamera->GetFOVh(); if ( (fFOVAnimRate < 0.0f && dH <= fFOVGoal) || (fFOVAnimRate > 0.0f && dH >= fFOVGoal) ) { fFlags.ClearBit(kAnimateFOV); dH = fFOVGoal; } fCamera->SetFOVw( (hsScalar)(dH * FOV_RATIO) ); fCamera->SetFOVh( 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); } hsBool plCameraBrain1::MsgReceive(plMessage* msg) { plCameraMsg* pCamMsg = plCameraMsg::ConvertNoRef(msg); if (pCamMsg) { if (pCamMsg->Cmd(plCameraMsg::kStartZoomIn)) { fFlags.SetBit(kAnimateFOV); fFOVGoal = fZoomMin; fFOVAnimRate = -1*fZoomRate; return true; } else if (pCamMsg->Cmd(plCameraMsg::kStartZoomOut)) { fFlags.SetBit(kAnimateFOV); fFOVGoal = fZoomMax; fFOVAnimRate = 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); fFOVGoal = fZoomMin; fFOVAnimRate = -1*fZoomRate; fFOVEndTime = hsTimer::GetSysSeconds() + 60; return true; } else if (pCMsg->GetControlCode() == B_CAMERA_ZOOM_OUT && fFlags.IsBitSet(kZoomEnabled)) { fFlags.SetBit(kAnimateFOV); fFOVGoal = fZoomMax; fFOVAnimRate = fZoomRate; fFOVEndTime = hsTimer::GetSysSeconds() + 60; return true; } else if (pCMsg->GetControlCode() == B_CAMERA_RECENTER) { if (fFlags.IsBitSet(kZoomEnabled)) { fFlags.SetBit(kAnimateFOV); fFOVGoal = fZoomMin + ((fZoomMax - fZoomMin) / 2); if (fCamera->GetFOVw() >= fFOVGoal) fFOVAnimRate = -1*fZoomRate; else fFOVAnimRate = 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)); }