|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
#include "HeadSpin.h"
|
|
|
|
|
|
|
|
#include "plCameraModifier.h"
|
|
|
|
#include "plCameraBrain.h"
|
|
|
|
#include "plVirtualCamNeu.h"
|
|
|
|
#include "hsTimer.h"
|
|
|
|
#include "plgDispatch.h"
|
|
|
|
#include "pnSceneObject/plSceneObject.h"
|
|
|
|
#include "pnSceneObject/plCoordinateInterface.h"
|
|
|
|
#include "plMessage/plInputEventMsg.h"
|
|
|
|
#include "plMessage/plAnimCmdMsg.h"
|
|
|
|
#include "pnMessage/plTimeMsg.h"
|
|
|
|
#include "pnKeyedObject/plKey.h"
|
|
|
|
#include "pnKeyedObject/plFixedKey.h"
|
|
|
|
#include "plInputCore/plInputDevice.h"
|
|
|
|
#include "plInputCore/plInputManager.h"
|
|
|
|
#include "hsResMgr.h"
|
|
|
|
#include "pnMessage/plCameraMsg.h"
|
|
|
|
#include "plPhysical/plSimDefs.h"
|
|
|
|
|
|
|
|
#include "plPhysical.h"
|
|
|
|
#include "pnSceneObject/plSimulationInterface.h"
|
|
|
|
#include "plAvatar/plAvatarMgr.h"
|
|
|
|
#include "plAvatar/plArmatureMod.h"
|
|
|
|
#include "plAvatar/plAvCallbackAction.h"
|
|
|
|
|
|
|
|
// new stuff
|
|
|
|
|
|
|
|
plCameraModifier1::plCameraModifier1() :
|
|
|
|
fBrain(nil),
|
|
|
|
fSubObj(nil),
|
|
|
|
fFOVw(45.0f),
|
|
|
|
fFOVh(33.75f),
|
|
|
|
fAnimated(false),
|
|
|
|
fStartAnimOnPush(false),
|
|
|
|
fStopAnimOnPop(false),
|
|
|
|
fResetAnimOnPop(false),
|
|
|
|
fInSubLastUpdate(false),
|
|
|
|
fUpdateBrainTarget(false)
|
|
|
|
{
|
|
|
|
fFrom.Set(0,0,0);
|
|
|
|
fAt.Set(0,1,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
plCameraModifier1::~plCameraModifier1()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < GetNumTrans(); i++)
|
|
|
|
delete(GetTrans(i));
|
|
|
|
fTrans.SetCountAndZero(0);
|
|
|
|
|
|
|
|
for (i = 0; i < fMessageQueue.Count(); i++)
|
|
|
|
hsRefCnt_SafeUnRef(fMessageQueue[i]);
|
|
|
|
fMessageQueue.SetCountAndZero(0);
|
|
|
|
|
|
|
|
for (i = 0; i < fFOVInstructions.Count(); i++)
|
|
|
|
hsRefCnt_SafeUnRef(fFOVInstructions[i]);
|
|
|
|
fFOVInstructions.SetCountAndZero(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void plCameraModifier1::AddTarget(plSceneObject* so)
|
|
|
|
{
|
|
|
|
fTarget = so;
|
|
|
|
if( plVirtualCam1::Instance() )
|
|
|
|
plVirtualCam1::Instance()->AddCameraLoaded(so);
|
|
|
|
fFrom = (so->GetWorldToLocal().GetTranslate());
|
|
|
|
if (GetBrain())
|
|
|
|
{
|
|
|
|
if (fTarget->GetCoordinateInterface())
|
|
|
|
GetBrain()->AddTarget();
|
|
|
|
else
|
|
|
|
fUpdateBrainTarget = true; // update the brain later
|
|
|
|
}
|
|
|
|
if (GetKey())
|
|
|
|
{
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCameraModifier1::SetSubject(plSceneObject* pObj)
|
|
|
|
{
|
|
|
|
if (GetBrain())
|
|
|
|
GetBrain()->SetSubject(pObj);
|
|
|
|
else
|
|
|
|
fSubObj = pObj;
|
|
|
|
}
|
|
|
|
|
|
|
|
plSceneObject* plCameraModifier1::GetSubject()
|
|
|
|
{
|
|
|
|
if (GetBrain())
|
|
|
|
return GetBrain()->GetSubject();
|
|
|
|
else
|
|
|
|
return fSubObj;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCameraModifier1::SetFOVw(hsScalar f, hsBool fUpdateVCam)
|
|
|
|
{
|
|
|
|
fFOVw = f;
|
|
|
|
if (plVirtualCam1::Instance() && fUpdateVCam)
|
|
|
|
plVirtualCam1::SetFOV(fFOVw, fFOVh, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCameraModifier1::SetFOVh(hsScalar f, hsBool fUpdateVCam)
|
|
|
|
{
|
|
|
|
fFOVh = f;
|
|
|
|
if (plVirtualCam1::Instance() && fUpdateVCam)
|
|
|
|
plVirtualCam1::SetFOV(fFOVw, fFOVh, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plCameraModifier1::SetFaded(hsBool b)
|
|
|
|
{
|
|
|
|
if (GetBrain())
|
|
|
|
return GetBrain()->SetFaded(b);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plCameraModifier1::GetFaded()
|
|
|
|
{
|
|
|
|
if (GetBrain())
|
|
|
|
return GetBrain()->GetFaded();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
hsBool plCameraModifier1::MsgReceive(plMessage* msg)
|
|
|
|
{
|
|
|
|
if (GetBrain())
|
|
|
|
GetBrain()->MsgReceive(msg);
|
|
|
|
|
|
|
|
plCameraMsg* pCamMsg = plCameraMsg::ConvertNoRef(msg);
|
|
|
|
if (pCamMsg)
|
|
|
|
{
|
|
|
|
if (pCamMsg->Cmd(plCameraMsg::kAddFOVKeyframe))
|
|
|
|
{
|
|
|
|
hsRefCnt_SafeRef(msg);
|
|
|
|
fFOVInstructions.Append(pCamMsg);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (pCamMsg->Cmd(plCameraMsg::kSetAnimated))
|
|
|
|
{
|
|
|
|
fAnimated = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
plEventCallbackMsg* pEventMsg = plEventCallbackMsg::ConvertNoRef(msg);
|
|
|
|
if (pEventMsg)
|
|
|
|
{
|
|
|
|
double time = (double)fFOVInstructions[pEventMsg->fIndex]->GetConfig()->fAccel;
|
|
|
|
double time2 = (double)pEventMsg->fEventTime;
|
|
|
|
time = hsABS(time - time2);
|
|
|
|
hsScalar h = fFOVInstructions[pEventMsg->fIndex]->GetConfig()->fFOVh;
|
|
|
|
if (GetBrain())
|
|
|
|
GetBrain()->SetFOVGoal(h, time);
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimCmdMsg* pAnimMsg = plAnimCmdMsg::ConvertNoRef(msg);
|
|
|
|
if (pAnimMsg)
|
|
|
|
{
|
|
|
|
hsRefCnt_SafeRef(msg);
|
|
|
|
msg->ClearReceivers();
|
|
|
|
msg->AddReceiver(msg->GetSender());
|
|
|
|
fMessageQueue.Append(msg);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg);
|
|
|
|
if (pRefMsg )
|
|
|
|
{
|
|
|
|
if( pRefMsg->GetContext() & (plRefMsg::kOnCreate | plRefMsg::kOnRequest) )
|
|
|
|
{
|
|
|
|
if (pRefMsg->fType == kRefBrain)
|
|
|
|
{
|
|
|
|
plCameraBrain1* pBrain = plCameraBrain1::ConvertNoRef(pRefMsg->GetRef());
|
|
|
|
if (pBrain)
|
|
|
|
{
|
|
|
|
pBrain->SetCamera(this);
|
|
|
|
fBrain = pBrain;
|
|
|
|
if (fSubObj)
|
|
|
|
fBrain->SetSubject(fSubObj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (pRefMsg->fType == kRefCallbackMsg && fMessageQueue[pRefMsg->fWhich] != nil)
|
|
|
|
{
|
|
|
|
|
|
|
|
plgDispatch::MsgSend(fMessageQueue[pRefMsg->fWhich]);
|
|
|
|
fMessageQueue[pRefMsg->fWhich] = nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( pRefMsg->GetContext() & (plRefMsg::kOnDestroy | plRefMsg::kOnRemove) )
|
|
|
|
{
|
|
|
|
plCameraBrain1* pBrain = (plCameraBrain1*)(pRefMsg->GetRef());
|
|
|
|
if (fBrain == pBrain)
|
|
|
|
fBrain = nil;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return plSingleModifier::MsgReceive(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void plCameraModifier1::Update()
|
|
|
|
{
|
|
|
|
// update the brain
|
|
|
|
|
|
|
|
// this freeze thing is a useful debugging tool...
|
|
|
|
if (plVirtualCam1::Instance()->freeze)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (GetBrain())
|
|
|
|
{
|
|
|
|
if (fUpdateBrainTarget && fTarget->GetCoordinateInterface()) // if we need to update the brain and the target is loaded
|
|
|
|
{
|
|
|
|
fUpdateBrainTarget = false;
|
|
|
|
GetBrain()->AddTarget(); // update the brain's target
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool moveInSub = !(GetBrain()->HasFlag(plCameraBrain1::kIgnoreSubworldMovement));
|
|
|
|
|
|
|
|
if (moveInSub && GetBrain()->GetSubject())
|
|
|
|
{
|
|
|
|
plKey worldKey = nil;
|
|
|
|
|
|
|
|
// First check if this is a physical. If so, grab the subworld from that
|
|
|
|
if (GetBrain()->GetSubject()->GetSimulationInterface())
|
|
|
|
{
|
|
|
|
plPhysical* phys = GetBrain()->GetSubject()->GetSimulationInterface()->GetPhysical();
|
|
|
|
if (phys)
|
|
|
|
worldKey = phys->GetWorldKey();
|
|
|
|
}
|
|
|
|
// Also, check if this is an avatar. They don't have physicals, you
|
|
|
|
// have to ask the avatar controller for the subworld key.
|
|
|
|
if (!worldKey)
|
|
|
|
{
|
|
|
|
plArmatureMod* armMod = plAvatarMgr::FindAvatar(plKey(GetBrain()->GetSubject()->GetKey()));
|
|
|
|
if (armMod && armMod->GetController() )
|
|
|
|
worldKey = armMod->GetController()->GetSubworld();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (worldKey)
|
|
|
|
{
|
|
|
|
// this picks up and moves the camera to it's previous subworld coordinate (so the subworld isn't moving out from underneath us)
|
|
|
|
hsMatrix44 l2w, w2l;
|
|
|
|
plSceneObject* so = plSceneObject::ConvertNoRef(worldKey->ObjectIsLoaded());
|
|
|
|
if (so)
|
|
|
|
{
|
|
|
|
l2w = so->GetLocalToWorld();
|
|
|
|
w2l = so->GetWorldToLocal();
|
|
|
|
|
|
|
|
if (fInSubLastUpdate)
|
|
|
|
{
|
|
|
|
if (!(fLastSubPos == fFrom && fLastSubPOA == fAt))
|
|
|
|
{
|
|
|
|
SetTargetPos(l2w * fLastSubPos);
|
|
|
|
SetTargetPOA(l2w * fLastSubPOA);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fInSubLastUpdate = true;
|
|
|
|
}
|
|
|
|
GetBrain()->Update();
|
|
|
|
fLastSubPos = w2l * GetTargetPos();
|
|
|
|
fLastSubPOA = w2l * GetTargetPOA();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fInSubLastUpdate = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
GetBrain()->Update();
|
|
|
|
fLastSubPos = GetTargetPos();
|
|
|
|
fLastSubPOA = GetTargetPOA();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCameraModifier1::Read(hsStream* stream, hsResMgr* mgr)
|
|
|
|
{
|
|
|
|
hsKeyedObject::Read(stream, mgr);
|
|
|
|
fBrain = nil;
|
|
|
|
mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefBrain), plRefFlags::kActiveRef);
|
|
|
|
int count = stream->ReadSwap32();
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
|
|
|
|
plKey key = mgr->ReadKey(stream);
|
|
|
|
hsBool cutpos = stream->ReadBool();
|
|
|
|
hsBool cutpoa = stream->ReadBool();
|
|
|
|
hsBool ignore = stream->ReadBool();
|
|
|
|
hsScalar v = stream->ReadSwapScalar();
|
|
|
|
hsScalar a = stream->ReadSwapScalar();
|
|
|
|
hsScalar d = stream->ReadSwapScalar();
|
|
|
|
hsScalar pV = stream->ReadSwapScalar();
|
|
|
|
hsScalar pA = stream->ReadSwapScalar();
|
|
|
|
hsScalar pD = stream->ReadSwapScalar();
|
|
|
|
|
|
|
|
CamTrans* camTrans = TRACKED_NEW CamTrans(key);
|
|
|
|
camTrans->fAccel = a;
|
|
|
|
camTrans->fDecel = d;
|
|
|
|
camTrans->fVelocity = v;
|
|
|
|
camTrans->fPOAAccel = pA;
|
|
|
|
camTrans->fPOADecel = pD;
|
|
|
|
camTrans->fPOAVelocity = pV;
|
|
|
|
camTrans->fCutPos = cutpos;
|
|
|
|
camTrans->fCutPOA = cutpoa;
|
|
|
|
camTrans->fIgnore = ignore;
|
|
|
|
|
|
|
|
fTrans.Append(camTrans);
|
|
|
|
}
|
|
|
|
fFOVw = stream->ReadSwapFloat();
|
|
|
|
fFOVh = stream->ReadSwapFloat();
|
|
|
|
int n = stream->ReadSwap32();
|
|
|
|
fMessageQueue.SetCountAndZero(n);
|
|
|
|
for(i = 0; i < n; i++ )
|
|
|
|
{
|
|
|
|
plMessage* pMsg = plMessage::ConvertNoRef(mgr->ReadCreatable(stream));
|
|
|
|
fMessageQueue[i] = pMsg;
|
|
|
|
}
|
|
|
|
for(i = 0; i < n; i++ )
|
|
|
|
{
|
|
|
|
mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, i, kRefCallbackMsg), plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
n = stream->ReadSwap32();
|
|
|
|
fFOVInstructions.SetCountAndZero(n);
|
|
|
|
for(i = 0; i < n; i++ )
|
|
|
|
{
|
|
|
|
plCameraMsg* pMsg = plCameraMsg::ConvertNoRef(mgr->ReadCreatable(stream));
|
|
|
|
fFOVInstructions[i] = pMsg;
|
|
|
|
}
|
|
|
|
fAnimated = stream->ReadBool();
|
|
|
|
fStartAnimOnPush = stream->ReadBool();
|
|
|
|
fStopAnimOnPop = stream->ReadBool();
|
|
|
|
fResetAnimOnPop = stream->ReadBool();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCameraModifier1::Write(hsStream* stream, hsResMgr* mgr)
|
|
|
|
{
|
|
|
|
hsKeyedObject::Write(stream, mgr);
|
|
|
|
if (fBrain)
|
|
|
|
mgr->WriteKey(stream, fBrain );
|
|
|
|
|
|
|
|
int i = fTrans.Count();
|
|
|
|
stream->WriteSwap32(i);
|
|
|
|
for (i = 0; i < fTrans.Count(); i++)
|
|
|
|
{
|
|
|
|
mgr->WriteKey(stream, fTrans[i]->fTransTo);
|
|
|
|
stream->WriteBool(fTrans[i]->fCutPos);
|
|
|
|
stream->WriteBool(fTrans[i]->fCutPOA);
|
|
|
|
stream->WriteBool(fTrans[i]->fIgnore);
|
|
|
|
stream->WriteSwapScalar(fTrans[i]->fVelocity);
|
|
|
|
stream->WriteSwapScalar(fTrans[i]->fAccel);
|
|
|
|
stream->WriteSwapScalar(fTrans[i]->fDecel);
|
|
|
|
stream->WriteSwapScalar(fTrans[i]->fPOAVelocity);
|
|
|
|
stream->WriteSwapScalar(fTrans[i]->fPOAAccel);
|
|
|
|
stream->WriteSwapScalar(fTrans[i]->fPOADecel);
|
|
|
|
}
|
|
|
|
stream->WriteSwapFloat(fFOVw);
|
|
|
|
stream->WriteSwapFloat(fFOVh);
|
|
|
|
stream->WriteSwap32(fMessageQueue.Count());
|
|
|
|
for (i = 0; i < fMessageQueue.Count(); i++)
|
|
|
|
{
|
|
|
|
mgr->WriteCreatable(stream, fMessageQueue[i]);
|
|
|
|
}
|
|
|
|
for (i = 0; i < fMessageQueue.Count(); i++)
|
|
|
|
{
|
|
|
|
mgr->WriteKey(stream, fMessageQueue[i]->GetSender());
|
|
|
|
}
|
|
|
|
stream->WriteSwap32(fFOVInstructions.Count());
|
|
|
|
for (i = 0; i < fFOVInstructions.Count(); i++)
|
|
|
|
{
|
|
|
|
mgr->WriteCreatable(stream, fFOVInstructions[i]);
|
|
|
|
}
|
|
|
|
stream->WriteBool(fAnimated);
|
|
|
|
stream->WriteBool(fStartAnimOnPush);
|
|
|
|
stream->WriteBool(fStopAnimOnPop);
|
|
|
|
stream->WriteBool(fResetAnimOnPop);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCameraModifier1::Push(hsBool recenter)
|
|
|
|
{
|
|
|
|
if (fAnimated)
|
|
|
|
{
|
|
|
|
if (fStartAnimOnPush)
|
|
|
|
{
|
|
|
|
plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg;
|
|
|
|
pMsg->SetCmd(plAnimCmdMsg::kRunForward);
|
|
|
|
pMsg->SetBCastFlag(plMessage::kPropagateToModifiers);
|
|
|
|
pMsg->AddReceiver(GetTarget()->GetKey());
|
|
|
|
if (GetBrain() && GetBrain()->GetSubject())
|
|
|
|
pMsg->AddReceiver(GetBrain()->GetSubject()->GetKey());
|
|
|
|
pMsg->Send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fBrain)
|
|
|
|
fBrain->Push(recenter);
|
|
|
|
|
|
|
|
|
|
|
|
if (GetKey())
|
|
|
|
{
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plMouseEventMsg::Index(), GetKey());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCameraModifier1::Pop()
|
|
|
|
{
|
|
|
|
if (fAnimated)
|
|
|
|
{
|
|
|
|
if (fStopAnimOnPop)
|
|
|
|
{
|
|
|
|
plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg;
|
|
|
|
pMsg->SetCmd(plAnimCmdMsg::kStop);
|
|
|
|
pMsg->SetBCastFlag(plMessage::kPropagateToModifiers);
|
|
|
|
pMsg->AddReceiver(GetTarget()->GetKey());
|
|
|
|
if (GetBrain() && GetBrain()->GetSubject())
|
|
|
|
pMsg->AddReceiver(GetBrain()->GetSubject()->GetKey());
|
|
|
|
pMsg->Send();
|
|
|
|
}
|
|
|
|
if (fResetAnimOnPop)
|
|
|
|
{
|
|
|
|
plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg;
|
|
|
|
pMsg->SetCmd(plAnimCmdMsg::kGoToBegin);
|
|
|
|
pMsg->SetBCastFlag(plMessage::kPropagateToModifiers);
|
|
|
|
pMsg->AddReceiver(GetTarget()->GetKey());
|
|
|
|
if (GetBrain() && GetBrain()->GetSubject())
|
|
|
|
pMsg->AddReceiver(GetBrain()->GetSubject()->GetKey());
|
|
|
|
pMsg->Send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fBrain)
|
|
|
|
fBrain->Pop();
|
|
|
|
if (GetKey()) // the reason we might not have a key is a special run-time POA which doesn't need to receive messages...
|
|
|
|
{
|
|
|
|
plgDispatch::Dispatch()->UnRegisterForExactType(plMouseEventMsg::Index(), GetKey());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCameraModifier1::SetTransform(hsPoint3 at)
|
|
|
|
{
|
|
|
|
if (!GetTarget())
|
|
|
|
return;
|
|
|
|
hsMatrix44 l2w;
|
|
|
|
hsMatrix44 w2l;
|
|
|
|
hsVector3 up(0,0,1);
|
|
|
|
l2w.Make(&fFrom, &at, &up);
|
|
|
|
l2w.GetInverse(&w2l);
|
|
|
|
IGetTargetCoordinateInterface(0)->SetTransform( l2w, w2l );
|
|
|
|
}
|