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.
 
 
 
 
 

1890 lines
55 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 <cmath>
#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"
bool plCameraBrain1_FirstPerson::fDontFade = false;
float plCameraBrain1::fFallAccel = 20.0f;
float plCameraBrain1::fFallDecel = 5.0f;
float plCameraBrain1::fFallVelocity = 50.0f;
float plCameraBrain1::fFallPOAAccel = 10.0f;
float plCameraBrain1::fFallPOADecel = 10.0f;
float 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);
hsVector3 up(0, 0, 1);
fTargetMatrix.Make(&fGoal, &fPOAGoal, &up);
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(bool 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(float w, float h, double t)
{
if (fFOVhGoal == h || h == fCamera->GetFOVh() &&
fFOVwGoal == w || w == fCamera->GetFOVw())
return;
float dif = h - fCamera->GetFOVh();
fFOVhAnimRate = dif / ((float)t);
fFOVhGoal = h;
fFOVStartTime = hsTimer::GetSysSeconds();
fFOVEndTime = fFOVStartTime + t;
if (w == 0.f)
{
fFOVwGoal = IMakeFOVwZoom(h);
dif = fFOVwGoal - fCamera->GetFOVw();
fFOVwAnimRate = dif / ((float)t);
}
else
{
dif = w - fCamera->GetFOVw();
fFOVwAnimRate = dif / ((float)t);
fFOVwGoal = w;
}
fFlags.SetBit(kAnimateFOV);
}
// set parameters for how this camera zooms FOV based on user input (mostly for telescopes)
void plCameraBrain1::SetZoomParams(float max, float min, float 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(bool 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)
{
float dH = fFOVhAnimRate * hsTimer::GetDelSysSeconds();
float dW = fFOVwAnimRate * hsTimer::GetDelSysSeconds();
dH += fCamera->GetFOVh();
dW += fCamera->GetFOVw();
if ( (fFOVhAnimRate < 0.0f && dH <= fFOVhGoal) ||
(fFOVhAnimRate > 0.0f && dH >= fFOVhGoal) )
{
dH = fFOVhGoal;
}
if ( (fFOVwAnimRate < 0.0f && dW <= fFOVwGoal) ||
(fFOVwAnimRate > 0.0f && dW >= fFOVwGoal) )
{
dW = fFOVwGoal;
}
if (dW == fFOVwGoal && dH == fFOVhGoal)
fFlags.ClearBit(kAnimateFOV);
fCamera->SetFOV( dW, dH );
}
// move the camera's origin point (not where it is looking) toward where it is going
void plCameraBrain1::IMoveTowardGoal(double elapsedTime)
{
bool 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());
float distToGoal=dir.Magnitude();
//smooth out stoppage...
float adjMaxVel = fVelocity;
if (distToGoal <= 5.0f && distToGoal > 0.1f)
{
float 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();
float 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)
{
bool 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());
float distToGoal=dir.Magnitude();
if (distToGoal > 0.0f)
dir.Normalize();
// smooth out stoppage
float adjMaxVel = fPOAVelocity;
if (distToGoal <= 5.0f && distToGoal > 0.1f)
{
float 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();
float 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(float adjAccelRate, float adjDecelRate,
hsVector3* dir, hsVector3* vel, float maxSpeed,
float distToGoal, double elapsedTime)
{
float speed = vel->Magnitude(); // save current speed
*vel = *dir * speed; // change vel to correct dir
// compute accel/decel
float 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 * (float)elapsedTime;
// add acceleration to velocity
*vel = *vel + accelVec;
}
else
{
*vel = *dir * maxSpeed;
}
}
float plCameraBrain1::IClampVelocity(hsVector3* vel, float maxSpeed, double elapsedTime)
{
*vel = *vel * (float)elapsedTime;
maxSpeed *= (float)elapsedTime;
// clamp speed (clamp if going negative?)
float distMoved = vel->Magnitude();
if (distMoved > maxSpeed)
{
vel->Normalize();
*vel = *vel * maxSpeed;
return maxSpeed;
}
return distMoved;
}
bool plCameraBrain1::IShouldDecelerate(float decelSpeed, float curSpeed, float distToGoal)
{
if (decelSpeed == 0)
// no deceleration
return false;
// compute distance required to stop, given decel speed (in units/sec sq)
float stopTime = curSpeed / decelSpeed;
float avgSpeed = curSpeed * .5f;
float 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);
float 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 = new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kSubject ); // SceneObject
mgr->ReadKeyNotifyMe( stream, msg, plRefFlags::kActiveRef );
plGenRefMsg* msg2 = 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->ReadLEFloat();
fDecel = stream->ReadLEFloat();
fVelocity = stream->ReadLEFloat();
fPOAAccel = stream->ReadLEFloat();
fPOADecel = stream->ReadLEFloat();
fPOAVelocity = stream->ReadLEFloat();
fXPanLimit = stream->ReadLEFloat();
fZPanLimit = stream->ReadLEFloat();
fZoomRate = stream->ReadLEFloat();
fZoomMin = stream->ReadLEFloat();
fZoomMax = stream->ReadLEFloat();
}
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->WriteLEFloat(fAccel);
stream->WriteLEFloat(fDecel);
stream->WriteLEFloat(fVelocity);
stream->WriteLEFloat(fPOAAccel);
stream->WriteLEFloat(fPOADecel);
stream->WriteLEFloat(fPOAVelocity);
stream->WriteLEFloat(fXPanLimit);
stream->WriteLEFloat(fZPanLimit);
stream->WriteLEFloat(fZoomRate);
stream->WriteLEFloat(fZoomMin);
stream->WriteLEFloat(fZoomMax);
}
float plCameraBrain1::IMakeFOVwZoom(float fovH) const
{
float num = tan(hsDegreesToRadians(fovH / 2)) * tan(hsDegreesToRadians(fCamera->GetFOVw() / 2));
float denom = tan(hsDegreesToRadians(fCamera->GetFOVh() / 2));
return 2 * hsABS(hsRadiansToDegrees(atan(num / denom)));
}
bool plCameraBrain1::MsgReceive(plMessage* msg)
{
plCameraMsg* pCamMsg = plCameraMsg::ConvertNoRef(msg);
if (pCamMsg)
{
if (pCamMsg->Cmd(plCameraMsg::kStartZoomIn))
{
fFlags.SetBit(kAnimateFOV);
fFOVhGoal = fZoomMin;
fFOVwGoal = IMakeFOVwZoom(fZoomMin);
fFOVwAnimRate = fFOVhAnimRate = -1*fZoomRate;
return true;
}
else
if (pCamMsg->Cmd(plCameraMsg::kStartZoomOut))
{
fFlags.SetBit(kAnimateFOV);
fFOVhGoal = fZoomMax;
fFOVwGoal = IMakeFOVwZoom(fZoomMax);
fFOVwAnimRate = fFOVhAnimRate = fZoomRate;
return true;
}
else
if (pCamMsg->Cmd(plCameraMsg::kStopZoom))
{
fFlags.ClearBit(kAnimateFOV);
return true;
}
else
if (pCamMsg->Cmd(plCameraMsg::kNonPhysOn))
{
fFlags.SetBit(kNonPhys);
return true;
}
else
if (pCamMsg->Cmd(plCameraMsg::kNonPhysOff))
{
fFlags.ClearBit(kNonPhys);
return true;
}
}
plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg);
if (pRefMsg)
{
if (pRefMsg->fType == kSubject)
{
if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) )
SetSubject((plSceneObject*)pRefMsg->GetRef());
else
SetSubject(nil);
return true;
}
else
if (pRefMsg->fType == kRailComponent)
{
if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) )
{
fFlags.SetBit(kRailComponent);
fRail = (plRailCameraMod*)pRefMsg->GetRef();
}
else
{
fRail = nil;
fFlags.ClearBit(kRailComponent);
}
return true;
}
}
plPlayerPageMsg* pPMsg = plPlayerPageMsg::ConvertNoRef(msg);
if (pPMsg)
{
if (pPMsg->fPlayer == plNetClientMgr::GetInstance()->GetLocalPlayerKey())
{
if (pPMsg->fUnload)
{
if (fFlags.IsBitSet(kFollowLocalAvatar))
SetSubject(nil);
}
else
{
plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kSubject ); // SceneObject
hsgResMgr::ResMgr()->AddViaNotify(pPMsg->fPlayer, msg, plRefFlags::kPassiveRef);
fFlags.SetBit(kCutPosOnce);
fFlags.SetBit(kCutPOAOnce);
}
}
return true;
}
plControlEventMsg* pCMsg = plControlEventMsg::ConvertNoRef(msg);
if (pCMsg)
{
if (pCMsg->ControlActivated())
{
SetMovementFlag(pCMsg->GetControlCode());
if (pCMsg->GetControlCode() == S_SET_FREELOOK)
{
plInputManager::SetRecenterMouse(true);
plMouseDevice::HideCursor();
}
if (pCMsg->GetControlCode() == B_CAMERA_ZOOM_IN && fFlags.IsBitSet(kZoomEnabled))
{
fFlags.SetBit(kAnimateFOV);
fFOVhGoal = fZoomMin;
fFOVwGoal = IMakeFOVwZoom(fZoomMin);
fFOVwAnimRate = fFOVhAnimRate = -1*fZoomRate;
fFOVEndTime = hsTimer::GetSysSeconds() + 60;
return true;
}
else
if (pCMsg->GetControlCode() == B_CAMERA_ZOOM_OUT && fFlags.IsBitSet(kZoomEnabled))
{
fFlags.SetBit(kAnimateFOV);
fFOVhGoal = fZoomMax;
fFOVwGoal = IMakeFOVwZoom(fZoomMax);
fFOVwAnimRate = fFOVhAnimRate = fZoomRate;
fFOVEndTime = hsTimer::GetSysSeconds() + 60;
return true;
}
else
if (pCMsg->GetControlCode() == B_CAMERA_RECENTER)
{
if (fFlags.IsBitSet(kZoomEnabled))
{
fFlags.SetBit(kAnimateFOV);
fFOVhGoal = fZoomMin + ((fZoomMax - fZoomMin) / 2);
fFOVwGoal = IMakeFOVwZoom(fFOVhGoal);
if (fCamera->GetFOVh() >= fFOVhGoal)
fFOVwAnimRate = fFOVhAnimRate = -1*fZoomRate;
else
fFOVwAnimRate = fFOVhAnimRate = fZoomRate;
fFOVEndTime = hsTimer::GetSysSeconds() + 60;
}
return true;
}
}
else
{
ClearMovementFlag(pCMsg->GetControlCode());
if (pCMsg->GetControlCode() == S_SET_FREELOOK)
{
plInputManager::SetRecenterMouse(false);
plMouseDevice::ShowCursor();
}
else
if ( (pCMsg->GetControlCode() == B_CAMERA_ZOOM_IN || pCMsg->GetControlCode() == B_CAMERA_ZOOM_OUT)
&& fFlags.IsBitSet(kZoomEnabled) )
{
fFlags.ClearBit(kAnimateFOV);
return true;
}
}
return true;
}
return hsKeyedObject::MsgReceive(msg);
}
plSceneObject* plCameraBrain1::GetSubject()
{
if (fSubjectKey)
return plSceneObject::ConvertNoRef(fSubjectKey->ObjectIsLoaded());
return nil;
}
void plCameraBrain1::SetSubject(plSceneObject* sub)
{
if (sub)
fSubjectKey = sub->GetKey();
else
fSubjectKey = nil;
}
//
//
// new drive mode brain
//
float plCameraBrain1_Drive::fTurnRate = 100.0f;
float plCameraBrain1_Drive::fAcceleration = 200.0f;
float plCameraBrain1_Drive::fDeceleration = 200.0f;
float 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(bool recenter)
{
plCameraBrain1::Push(recenter);
plInputManager::SetRecenterMouse(true);
fLastTime = hsTimer::GetSeconds();
}
void plCameraBrain1_Drive::Pop()
{
plInputManager::SetRecenterMouse(false);
}
//
// Update Method
//
void plCameraBrain1_Drive::Update(bool forced)
{
hsVector3 neg_up = -1 * fUp;
fTargetMatrix.Make(&fGoal, &fPOAGoal, &neg_up);
// update our desired position:
double time = hsTimer::GetSeconds();
float eTime = (float)(time-fLastTime);
if(eTime > 0.01f)
eTime = 0.01f;
fLastTime = time;
hsPoint3 cameraPos = fCamera->GetTargetPos();
hsVector3 view, up, right;
fTargetMatrix.GetAxis(&view,&up,&right);
float 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;
}
float speed = fMaxVelocity;
float 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);
}
bool 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 = 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(bool recenter)
{
bObscured = false;
fFallTimer = 0.0f;
plCameraBrain1::Push(recenter);
}
//
// Update Method
//
void plCameraBrain1_Avatar::Update(bool 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().c_str());
}
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 = 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(bool 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 = 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);
}
}
bool 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()
{
}
bool 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 plString& name = child->GetKeyName();
if (name.Compare("FPCameraOrigin", plString::kCaseInsensitive) == 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 = 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(bool recenter)
{
if (!GetSubject())
return;
if (plCameraBrain1_FirstPerson::fDontFade)
return;
plEnableMsg* pMsg = 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, 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(bool 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()
{
}
bool 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 float kTwoPI = 2.0f*M_PI;
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(bool 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;
hsPoint3 goalpos = fCamera->GetTargetPos();
if (HasFlag(kCutPosOnce))
{
fGoal = MoveTowardsFromGoal(&goalpos, secs, true);
fFlags.ClearBit(kCutPos);
}
else
{
fGoal = MoveTowardsFromGoal(&goalpos, 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, bool warp)
{
if (fCurRad != fGoalRad)
{
float dist = hsABS(fGoalRad-fCurRad);
hsAssert(dist>=0 && dist<=kTwoPI, "illegal radian diff");
bool mustWrap = (dist > M_PI); // go opposite direction for shortcut and wrap
// compute speed
float speed;
if (warp)
speed = (float)(kTwoPI * 100 * secs);
else
speed = (float)(kTwoPI * fCirPerSec * secs);
// move towards goalRad
hsAssert(fCurRad>=0 && fCurRad<=kTwoPI, "illegal radian value");
if (fCurRad<fGoalRad)
{
if (mustWrap)
{
fCurRad-=speed;
bool 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;
bool 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((float)cos(fCurRad)*fRadius, (float)sin(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 = (float)atan2(v.fY, v.fX); // -pi to pi
hsAssert(fGoalRad>=-M_PI && fGoalRad<=M_PI, "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->WriteLE32(GetCircleFlags());
fCenter.Write(stream);
stream->WriteLEScalar(GetRadius());
mgr->WriteKey(stream, fCenterObject);
mgr->WriteKey(stream, fPOAObj);
stream->WriteLEScalar(fCirPerSec);
}
void plCameraBrain1_Circle::Read(hsStream* stream, hsResMgr* mgr)
{
plCameraBrain1::Read(stream, mgr);
SetCircleFlags(stream->ReadLE32());
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->ReadLEScalar());
plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kCircleTarget ); // SceneObject
mgr->ReadKeyNotifyMe( stream, msg, plRefFlags::kActiveRef );
plGenRefMsg* msg2 = new plGenRefMsg( GetKey(), plRefMsg::kOnRequest, 0, kPOAObject ); // SceneObject
mgr->ReadKeyNotifyMe( stream, msg2, plRefFlags::kActiveRef );
fCirPerSec = stream->ReadLEScalar();
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
}
bool 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 = 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));
}