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.

1847 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/>.
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));
}