|
|
|
/*==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 - fabs(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 - fabs(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 (fabs(distToGoal) <= fabs(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 * fabs(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, &up, &view);
|
|
|
|
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 = fabs(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, ¢er);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// farthest
|
|
|
|
v = hsVector3(¢er, &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));
|
|
|
|
}
|
|
|
|
|
|
|
|
|