|
|
|
/*==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 "plLineFollowMod.h"
|
|
|
|
#include "plStereizer.h"
|
|
|
|
#include "plInterp/plAnimPath.h"
|
|
|
|
#include "hsResMgr.h"
|
|
|
|
#include "pnMessage/plRefMsg.h"
|
|
|
|
#include "pnSceneObject/plSceneObject.h"
|
|
|
|
#include "pnSceneObject/plCoordinateInterface.h"
|
|
|
|
#include "pnSceneObject/plDrawInterface.h"
|
|
|
|
#include "plgDispatch.h"
|
|
|
|
#include "plMessage/plListenerMsg.h"
|
|
|
|
#include "plMessage/plRenderMsg.h"
|
|
|
|
#include "pnMessage/plTimeMsg.h"
|
|
|
|
#include "hsBounds.h"
|
|
|
|
#include "plPipeline.h"
|
|
|
|
#include "hsFastMath.h"
|
|
|
|
#include "pnMessage/plPlayerPageMsg.h"
|
|
|
|
#include "pnNetCommon/plNetApp.h"
|
|
|
|
#include "plNetClient/plNetClientMgr.h"
|
|
|
|
#include "hsTimer.h"
|
|
|
|
|
|
|
|
plLineFollowMod::plLineFollowMod()
|
|
|
|
: fPath(nil),
|
|
|
|
fPathParent(nil),
|
|
|
|
fRefObj(nil),
|
|
|
|
fFollowMode(kFollowListener),
|
|
|
|
fFollowFlags(kNone),
|
|
|
|
fOffset(0),
|
|
|
|
fOffsetClamp(0),
|
|
|
|
fSpeedClamp(0)
|
|
|
|
{
|
|
|
|
fSearchPos.Set(0,0,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
plLineFollowMod::~plLineFollowMod()
|
|
|
|
{
|
|
|
|
delete fPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::SetSpeedClamp(float fps)
|
|
|
|
{
|
|
|
|
fSpeedClamp = fps;
|
|
|
|
if( fSpeedClamp > 0 )
|
|
|
|
{
|
|
|
|
fFollowFlags |= kSpeedClamp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fFollowFlags &= ~kSpeedClamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::SetOffsetFeet(float f)
|
|
|
|
{
|
|
|
|
fOffset = f;
|
|
|
|
if( fOffset != 0 )
|
|
|
|
{
|
|
|
|
fFollowFlags &= ~kOffsetAng;
|
|
|
|
fFollowFlags |= kOffsetFeet;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fFollowFlags &= ~kOffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::SetForceToLine(bool on)
|
|
|
|
{
|
|
|
|
if( on )
|
|
|
|
fFollowFlags |= kForceToLine;
|
|
|
|
else
|
|
|
|
fFollowFlags &= ~kForceToLine;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::SetOffsetDegrees(float f)
|
|
|
|
{
|
|
|
|
fOffset = hsDegreesToRadians(f);
|
|
|
|
if( fOffset != 0 )
|
|
|
|
{
|
|
|
|
fFollowFlags &= ~kOffsetFeet;
|
|
|
|
fFollowFlags |= kOffsetAng;
|
|
|
|
fTanOffset = tanf(f);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fFollowFlags &= ~kOffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::SetOffsetClamp(float f)
|
|
|
|
{
|
|
|
|
fOffsetClamp = f;
|
|
|
|
if( fOffsetClamp > 0 )
|
|
|
|
{
|
|
|
|
fFollowFlags |= kOffsetClamp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fFollowFlags &= ~kOffsetClamp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::SetPath(plAnimPath* path)
|
|
|
|
{
|
|
|
|
delete fPath;
|
|
|
|
fPath = path;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::Read(hsStream* stream, hsResMgr* mgr)
|
|
|
|
{
|
|
|
|
plMultiModifier::Read(stream, mgr);
|
|
|
|
|
|
|
|
fPath = plAnimPath::ConvertNoRef(mgr->ReadCreatable(stream));
|
|
|
|
|
|
|
|
mgr->ReadKeyNotifyMe(stream, new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefParent), plRefFlags::kPassiveRef);
|
|
|
|
|
|
|
|
mgr->ReadKeyNotifyMe(stream, new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefObject), plRefFlags::kPassiveRef);
|
|
|
|
|
|
|
|
int n = stream->ReadLE32();
|
|
|
|
while(n--)
|
|
|
|
{
|
|
|
|
mgr->ReadKeyNotifyMe(stream, new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefStereizer), plRefFlags::kPassiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t f = stream->ReadLE32();
|
|
|
|
SetFollowMode(FollowMode(f & 0xffff));
|
|
|
|
|
|
|
|
fFollowFlags = (uint16_t)((f >> 16) & 0xffff);
|
|
|
|
|
|
|
|
if( fFollowFlags & kOffset )
|
|
|
|
{
|
|
|
|
fOffset = stream->ReadLEScalar();
|
|
|
|
}
|
|
|
|
if( fFollowFlags & kOffsetAng )
|
|
|
|
{
|
|
|
|
fTanOffset = tanf(fOffset);
|
|
|
|
}
|
|
|
|
if( fFollowFlags & kOffsetClamp )
|
|
|
|
{
|
|
|
|
fOffsetClamp = stream->ReadLEScalar();
|
|
|
|
}
|
|
|
|
if( fFollowFlags & kSpeedClamp )
|
|
|
|
{
|
|
|
|
fSpeedClamp = stream->ReadLEScalar();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::Write(hsStream* stream, hsResMgr* mgr)
|
|
|
|
{
|
|
|
|
plMultiModifier::Write(stream, mgr);
|
|
|
|
|
|
|
|
mgr->WriteCreatable(stream, fPath);
|
|
|
|
|
|
|
|
mgr->WriteKey(stream, fPathParent);
|
|
|
|
|
|
|
|
mgr->WriteKey(stream, fRefObj);
|
|
|
|
|
|
|
|
stream->WriteLE32(fStereizers.GetCount());
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fStereizers.GetCount(); i++ )
|
|
|
|
mgr->WriteKey(stream, fStereizers[i]->GetKey());
|
|
|
|
|
|
|
|
uint32_t f = uint32_t(fFollowMode) | (uint32_t(fFollowFlags) << 16);
|
|
|
|
stream->WriteLE32(f);
|
|
|
|
|
|
|
|
if( fFollowFlags & kOffset )
|
|
|
|
stream->WriteLEScalar(fOffset);
|
|
|
|
if( fFollowFlags & kOffsetClamp )
|
|
|
|
stream->WriteLEScalar(fOffsetClamp);
|
|
|
|
if( fFollowFlags & kSpeedClamp )
|
|
|
|
stream->WriteLEScalar(fSpeedClamp);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#include "plProfile.h"
|
|
|
|
plProfile_CreateTimer("LineFollow", "RenderSetup", LineFollow);
|
|
|
|
|
|
|
|
bool plLineFollowMod::MsgReceive(plMessage* msg)
|
|
|
|
{
|
|
|
|
plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg);
|
|
|
|
if( refMsg )
|
|
|
|
{
|
|
|
|
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
|
|
|
|
{
|
|
|
|
plSceneObject* obj = plSceneObject::ConvertNoRef(refMsg->GetRef());
|
|
|
|
if( kRefParent == refMsg->fType )
|
|
|
|
fPathParent = obj;
|
|
|
|
else if( kRefObject == refMsg->fType )
|
|
|
|
fRefObj = obj;
|
|
|
|
else if( kRefStereizer == refMsg->fType )
|
|
|
|
{
|
|
|
|
plStereizer* ster = plStereizer::ConvertNoRef(refMsg->GetRef());
|
|
|
|
int idx = fStereizers.Find(ster);
|
|
|
|
if( idx == fStereizers.kMissingIndex )
|
|
|
|
fStereizers.Append(ster);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) )
|
|
|
|
{
|
|
|
|
if( kRefParent == refMsg->fType )
|
|
|
|
fPathParent = nil;
|
|
|
|
else if( kRefObject == refMsg->fType )
|
|
|
|
fRefObj = nil;
|
|
|
|
else if( kRefStereizer == refMsg->fType )
|
|
|
|
{
|
|
|
|
plStereizer* ster = (plStereizer*)(refMsg->GetRef());
|
|
|
|
int idx = fStereizers.Find(ster);
|
|
|
|
if( idx != fStereizers.kMissingIndex )
|
|
|
|
fStereizers.Remove(idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
plRenderMsg* rend = plRenderMsg::ConvertNoRef(msg);
|
|
|
|
if( rend )
|
|
|
|
{
|
|
|
|
plProfile_BeginLap(LineFollow, this->GetKey()->GetUoid().GetObjectName().c_str());
|
|
|
|
hsPoint3 oldPos = fSearchPos;
|
|
|
|
fSearchPos = rend->Pipeline()->GetViewPositionWorld();
|
|
|
|
ICheckForPop(oldPos, fSearchPos);
|
|
|
|
plProfile_EndLap(LineFollow, this->GetKey()->GetUoid().GetObjectName().c_str());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
plListenerMsg* list = plListenerMsg::ConvertNoRef(msg);
|
|
|
|
if( list )
|
|
|
|
{
|
|
|
|
hsPoint3 oldPos = fSearchPos;
|
|
|
|
fSearchPos = list->GetPosition();
|
|
|
|
ICheckForPop(oldPos, fSearchPos);
|
|
|
|
|
|
|
|
ISetupStereizers(list);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
plPlayerPageMsg* pPMsg = plPlayerPageMsg::ConvertNoRef(msg);
|
|
|
|
if (pPMsg)
|
|
|
|
{
|
|
|
|
if (pPMsg->fPlayer == plNetClientMgr::GetInstance()->GetLocalPlayerKey() && !pPMsg->fUnload)
|
|
|
|
{
|
|
|
|
fRefObj = (plSceneObject*)pPMsg->fPlayer->GetObjectPtr();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return plMultiModifier::MsgReceive(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::SetFollowMode(FollowMode f)
|
|
|
|
{
|
|
|
|
IUnRegister();
|
|
|
|
fFollowMode = f;
|
|
|
|
IRegister();
|
|
|
|
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::IUnRegister()
|
|
|
|
{
|
|
|
|
switch( fFollowMode )
|
|
|
|
{
|
|
|
|
case kFollowObject:
|
|
|
|
break;
|
|
|
|
case kFollowListener:
|
|
|
|
plgDispatch::Dispatch()->UnRegisterForExactType(plListenerMsg::Index(), GetKey());
|
|
|
|
break;
|
|
|
|
case kFollowCamera:
|
|
|
|
plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey());
|
|
|
|
break;
|
|
|
|
case kFollowLocalAvatar:
|
|
|
|
plgDispatch::Dispatch()->UnRegisterForExactType(plPlayerPageMsg::Index(), GetKey());
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::IRegister()
|
|
|
|
{
|
|
|
|
switch( fFollowMode )
|
|
|
|
{
|
|
|
|
case kFollowObject:
|
|
|
|
break;
|
|
|
|
case kFollowListener:
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plListenerMsg::Index(), GetKey());
|
|
|
|
break;
|
|
|
|
case kFollowCamera:
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey());
|
|
|
|
break;
|
|
|
|
case kFollowLocalAvatar:
|
|
|
|
{
|
|
|
|
if (plNetClientApp::GetInstance() && plNetClientApp::GetInstance()->GetLocalPlayer())
|
|
|
|
fRefObj = ((plSceneObject*)plNetClientApp::GetInstance()->GetLocalPlayer());
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plLineFollowMod::IEval(double secs, float del, uint32_t dirty)
|
|
|
|
{
|
|
|
|
if( !fPath )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ISetPathTransform();
|
|
|
|
|
|
|
|
if( !IGetSearchPos() )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
hsMatrix44 tgtXfm;
|
|
|
|
IGetTargetTransform(fSearchPos, tgtXfm);
|
|
|
|
|
|
|
|
if( fFollowFlags & kOffset )
|
|
|
|
IOffsetTargetTransform(tgtXfm);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < GetNumTargets(); i++ )
|
|
|
|
{
|
|
|
|
ISetTargetTransform(i, tgtXfm);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plLineFollowMod::IOffsetTargetTransform(hsMatrix44& tgtXfm)
|
|
|
|
{
|
|
|
|
hsPoint3 tgtPos = tgtXfm.GetTranslate();
|
|
|
|
|
|
|
|
hsVector3 tgt2src(&fSearchPos, &tgtPos);
|
|
|
|
float t2sLen = tgt2src.Magnitude();
|
|
|
|
hsFastMath::NormalizeAppr(tgt2src);
|
|
|
|
|
|
|
|
hsVector3 out;
|
|
|
|
out.Set(-tgt2src.fY, tgt2src.fX, 0); // (0,0,1) X (tgt2src)
|
|
|
|
|
|
|
|
if( fFollowFlags & kOffsetAng )
|
|
|
|
{
|
|
|
|
float del = t2sLen * fTanOffset;
|
|
|
|
if( fFollowFlags & kOffsetClamp )
|
|
|
|
{
|
|
|
|
if( del > fOffsetClamp )
|
|
|
|
del = fOffsetClamp;
|
|
|
|
else if( del < -fOffsetClamp )
|
|
|
|
del = -fOffsetClamp;
|
|
|
|
}
|
|
|
|
out *= del;
|
|
|
|
}
|
|
|
|
else if( fFollowFlags & kOffsetFeet )
|
|
|
|
{
|
|
|
|
out *= fOffset;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
out.Set(0,0,0);
|
|
|
|
|
|
|
|
if( fFollowFlags & kForceToLine )
|
|
|
|
{
|
|
|
|
hsPoint3 newSearch = tgtPos;
|
|
|
|
newSearch += out;
|
|
|
|
IGetTargetTransform(newSearch, tgtXfm);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tgtXfm.fMap[0][3] += out[0];
|
|
|
|
tgtXfm.fMap[1][3] += out[1];
|
|
|
|
tgtXfm.fMap[2][3] += out[2];
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plLineFollowMod::IGetTargetTransform(hsPoint3& searchPos, hsMatrix44& tgtXfm)
|
|
|
|
{
|
|
|
|
float t = fPath->GetExtremePoint(searchPos);
|
|
|
|
if( fFollowFlags & kFullMatrix )
|
|
|
|
{
|
|
|
|
fPath->SetCurTime(t, plAnimPath::kNone);
|
|
|
|
fPath->GetMatrix44(&tgtXfm);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fPath->SetCurTime(t, plAnimPath::kCalcPosOnly);
|
|
|
|
hsPoint3 pos;
|
|
|
|
fPath->GetPosition(&pos);
|
|
|
|
tgtXfm.MakeTranslateMat((hsVector3*)&pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::ISetPathTransform()
|
|
|
|
{
|
|
|
|
if( fPathParent && fPathParent->GetCoordinateInterface() )
|
|
|
|
{
|
|
|
|
hsMatrix44 l2w = fPathParent->GetCoordinateInterface()->GetLocalToWorld();
|
|
|
|
hsMatrix44 w2l = fPathParent->GetCoordinateInterface()->GetWorldToLocal();
|
|
|
|
|
|
|
|
fPath->SetTransform(l2w, w2l);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::ICheckForPop(const hsPoint3& oldPos, const hsPoint3& newPos)
|
|
|
|
{
|
|
|
|
hsVector3 del(&oldPos, &newPos);
|
|
|
|
|
|
|
|
float elapsed = hsTimer::GetDelSysSeconds();
|
|
|
|
float speedSq = 0.f;
|
|
|
|
if (elapsed > 0.f)
|
|
|
|
speedSq = del.MagnitudeSquared() / elapsed;
|
|
|
|
|
|
|
|
const float kMaxSpeedSq = 30.f * 30.f; // (feet per sec)^2
|
|
|
|
if( speedSq > kMaxSpeedSq )
|
|
|
|
fFollowFlags |= kSearchPosPop;
|
|
|
|
else
|
|
|
|
fFollowFlags &= ~kSearchPosPop;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plLineFollowMod::IGetSearchPos()
|
|
|
|
{
|
|
|
|
hsPoint3 oldPos = fSearchPos;
|
|
|
|
if( kFollowObject == fFollowMode )
|
|
|
|
{
|
|
|
|
if( !fRefObj )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if( fRefObj->GetCoordinateInterface() )
|
|
|
|
{
|
|
|
|
fSearchPos = fRefObj->GetCoordinateInterface()->GetWorldPos();
|
|
|
|
ICheckForPop(oldPos, fSearchPos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if( fRefObj->GetDrawInterface() )
|
|
|
|
{
|
|
|
|
fSearchPos = fRefObj->GetDrawInterface()->GetWorldBounds().GetCenter();
|
|
|
|
ICheckForPop(oldPos, fSearchPos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (fFollowMode == kFollowLocalAvatar)
|
|
|
|
{
|
|
|
|
if (!fRefObj)
|
|
|
|
return false;
|
|
|
|
if( fRefObj->GetCoordinateInterface() )
|
|
|
|
{
|
|
|
|
fSearchPos = fRefObj->GetCoordinateInterface()->GetWorldPos();
|
|
|
|
ICheckForPop(oldPos, fSearchPos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if( fRefObj->GetDrawInterface() )
|
|
|
|
{
|
|
|
|
fSearchPos = fRefObj->GetDrawInterface()->GetWorldBounds().GetCenter();
|
|
|
|
ICheckForPop(oldPos, fSearchPos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsMatrix44 plLineFollowMod::IInterpMatrices(const hsMatrix44& m0, const hsMatrix44& m1, float parm)
|
|
|
|
{
|
|
|
|
hsMatrix44 retVal;
|
|
|
|
int i, j;
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
|
|
{
|
|
|
|
for( j = 0; j < 4; j++ )
|
|
|
|
{
|
|
|
|
retVal.fMap[i][j] = m0.fMap[i][j] * (1.f - parm) + m1.fMap[i][j] * parm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
retVal.fMap[3][0] = retVal.fMap[3][1] = retVal.fMap[3][2] = 0;
|
|
|
|
retVal.fMap[3][3] = 1.f;
|
|
|
|
retVal.NotIdentity();
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsMatrix44 plLineFollowMod::ISpeedClamp(plCoordinateInterface* ci, const hsMatrix44& unclTgtXfm)
|
|
|
|
{
|
|
|
|
// If our search position has popped, or delsysseconds is zero, just return as is.
|
|
|
|
if( (fFollowFlags & kSearchPosPop) || !(hsTimer::GetDelSysSeconds() > 0) )
|
|
|
|
return unclTgtXfm;
|
|
|
|
|
|
|
|
const hsMatrix44 currL2W = ci->GetLocalToWorld();
|
|
|
|
const hsPoint3 oldPos = currL2W.GetTranslate();
|
|
|
|
const hsPoint3 newPos = unclTgtXfm.GetTranslate();
|
|
|
|
const hsVector3 del(&newPos, &oldPos);
|
|
|
|
float elapsed = hsTimer::GetDelSysSeconds();
|
|
|
|
float speed = 0.f;
|
|
|
|
if (elapsed > 0.f)
|
|
|
|
speed = del.Magnitude() / elapsed;
|
|
|
|
|
|
|
|
if( speed > fSpeedClamp )
|
|
|
|
{
|
|
|
|
float parm = fSpeedClamp / speed;
|
|
|
|
|
|
|
|
hsMatrix44 clTgtXfm = IInterpMatrices(currL2W, unclTgtXfm, parm);
|
|
|
|
|
|
|
|
return clTgtXfm;
|
|
|
|
}
|
|
|
|
|
|
|
|
return unclTgtXfm;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::ISetTargetTransform(int iTarg, const hsMatrix44& unclTgtXfm)
|
|
|
|
{
|
|
|
|
plCoordinateInterface* ci = IGetTargetCoordinateInterface(iTarg);
|
|
|
|
if( ci )
|
|
|
|
{
|
|
|
|
hsMatrix44 tgtXfm = fFollowFlags & kSpeedClamp ? ISpeedClamp(ci, unclTgtXfm) : unclTgtXfm;
|
|
|
|
if( fFollowFlags & kFullMatrix )
|
|
|
|
{
|
|
|
|
// This branch currently never gets taken. If it ever does,
|
|
|
|
// we should probably optimize out this GetInverse() (depending
|
|
|
|
// on how often it gets taken).
|
|
|
|
const hsMatrix44& l2w = tgtXfm;
|
|
|
|
hsMatrix44 w2l;
|
|
|
|
l2w.GetInverse(&w2l);
|
|
|
|
|
|
|
|
ci->SetTransform(l2w, w2l);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hsMatrix44 l2w = ci->GetLocalToWorld();
|
|
|
|
hsMatrix44 w2l = ci->GetWorldToLocal();
|
|
|
|
|
|
|
|
hsPoint3 pos = tgtXfm.GetTranslate();
|
|
|
|
hsPoint3 oldPos = l2w.GetTranslate();
|
|
|
|
|
|
|
|
l2w.SetTranslate(&pos);
|
|
|
|
|
|
|
|
hsMatrix44 xlate;
|
|
|
|
xlate.Reset();
|
|
|
|
xlate.SetTranslate(&oldPos);
|
|
|
|
w2l = w2l * xlate;
|
|
|
|
hsPoint3 neg = -pos;
|
|
|
|
xlate.SetTranslate(&neg);
|
|
|
|
w2l = w2l * xlate;
|
|
|
|
|
|
|
|
ci->SetTransform(l2w, w2l);
|
|
|
|
}
|
|
|
|
hsPoint3 newPos = tgtXfm.GetTranslate();
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fStereizers.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( fStereizers[i] )
|
|
|
|
{
|
|
|
|
fStereizers[i]->SetWorldInitPos(newPos);
|
|
|
|
fStereizers[i]->Stereize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::ISetupStereizers(const plListenerMsg* listMsg)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fStereizers.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( fStereizers[i] )
|
|
|
|
fStereizers[i]->SetFromListenerMsg(listMsg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::AddTarget(plSceneObject* so)
|
|
|
|
{
|
|
|
|
plMultiModifier::AddTarget(so);
|
|
|
|
|
|
|
|
if( so )
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::RemoveTarget(plSceneObject* so)
|
|
|
|
{
|
|
|
|
plMultiModifier::RemoveTarget(so);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::AddStereizer(const plKey& key)
|
|
|
|
{
|
|
|
|
hsgResMgr::ResMgr()->SendRef(plKey(key), new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefStereizer), plRefFlags::kPassiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plLineFollowMod::RemoveStereizer(const plKey& key)
|
|
|
|
{
|
|
|
|
hsgResMgr::ResMgr()->SendRef(plKey(key), new plGenRefMsg(GetKey(), plRefMsg::kOnRemove, 0, kRefStereizer), plRefFlags::kPassiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
// derived version of this class for rail cameras
|
|
|
|
// the difference is the rail camera just calculates
|
|
|
|
// the desired position but does not move the target to
|
|
|
|
// it.
|
|
|
|
|
|
|
|
plRailCameraMod::plRailCameraMod() :
|
|
|
|
plLineFollowMod(),
|
|
|
|
fCurrentTime(0.0f),
|
|
|
|
fTargetTime(0.0f),
|
|
|
|
fFarthest(false)
|
|
|
|
{
|
|
|
|
fGoal.Set(0,0,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
plRailCameraMod::~plRailCameraMod()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plRailCameraMod::IGetTargetTransform(hsPoint3& searchPos, hsMatrix44& tgtXfm)
|
|
|
|
{
|
|
|
|
if (fPath->GetFarthest())
|
|
|
|
{
|
|
|
|
fFarthest = true;
|
|
|
|
fPath->SetFarthest(false);
|
|
|
|
}
|
|
|
|
fTargetTime = fPath->GetExtremePoint(searchPos);
|
|
|
|
fPath->SetCurTime(fTargetTime, plAnimPath::kCalcPosOnly);
|
|
|
|
hsPoint3 pos;
|
|
|
|
fPath->GetPosition(&pos);
|
|
|
|
tgtXfm.MakeTranslateMat((hsVector3*)&pos);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsPoint3 plRailCameraMod::GetGoal(double secs, float speed)
|
|
|
|
{
|
|
|
|
float delTime;
|
|
|
|
int dir;
|
|
|
|
if (fTargetTime == fCurrentTime)
|
|
|
|
return fGoal;
|
|
|
|
|
|
|
|
if (fTargetTime > fCurrentTime)
|
|
|
|
{
|
|
|
|
dir = 1;
|
|
|
|
delTime = fTargetTime - fCurrentTime;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dir = -1;
|
|
|
|
delTime = fCurrentTime - fTargetTime;
|
|
|
|
}
|
|
|
|
if (fPath->GetWrap() && delTime > fPath->GetLength() * 0.5f)
|
|
|
|
dir *= -1;
|
|
|
|
|
|
|
|
if (delTime <= (secs * speed))
|
|
|
|
fCurrentTime = fTargetTime;
|
|
|
|
else
|
|
|
|
fCurrentTime += (float)((secs * speed) * dir);
|
|
|
|
|
|
|
|
if (fPath->GetWrap())
|
|
|
|
{
|
|
|
|
if (fCurrentTime > fPath->GetLength())
|
|
|
|
fCurrentTime = (fCurrentTime - fPath->GetLength());
|
|
|
|
else
|
|
|
|
if (fCurrentTime < 0.0f)
|
|
|
|
fCurrentTime = fPath->GetLength() - fCurrentTime;
|
|
|
|
}
|
|
|
|
if (fFarthest)
|
|
|
|
fPath->SetCurTime((fPath->GetLength() - fCurrentTime), plAnimPath::kCalcPosOnly);
|
|
|
|
else
|
|
|
|
fPath->SetCurTime(fCurrentTime, plAnimPath::kCalcPosOnly);
|
|
|
|
|
|
|
|
fPath->GetPosition(&fGoal);
|
|
|
|
|
|
|
|
fPath->SetCurTime(fTargetTime, plAnimPath::kCalcPosOnly);
|
|
|
|
|
|
|
|
return fGoal;
|
|
|
|
}
|