You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1862 lines
48 KiB

/*==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 <http://www.gnu.org/licenses/>.
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"
//#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 (fCurRad<fGoalRad)
{
if (mustWrap)
{
fCurRad-=speed;
hsBool didWrap=false;
while(fCurRad<0)
{
didWrap=true;
fCurRad+=kTwoPI;
}
if (fCurRad<fGoalRad && didWrap)
fCurRad=fGoalRad;
}
else
{
fCurRad+=speed;
if (fCurRad>fGoalRad)
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<fGoalRad)
fCurRad=fGoalRad;
}
}
}
hsAssert(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, &center);
}
else
{
// farthest
v = hsVector3(&center, &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));
}