You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
294 lines
7.1 KiB
294 lines
7.1 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
|
|
|
|
#include <math.h> |
|
|
|
#include "hsTypes.h" |
|
|
|
#include "plStereizer.h" |
|
#include "plLineFollowMod.h" |
|
|
|
#include "../plMessage/plListenerMsg.h" |
|
#include "plgDispatch.h" |
|
|
|
#include "../pnSceneObject/plSceneObject.h" |
|
#include "../pnSceneObject/plCoordinateInterface.h" |
|
|
|
#include "hsFastMath.h" |
|
|
|
#include "hsGeometry3.h" |
|
#include "hsMatrix44.h" |
|
#include "hsStream.h" |
|
|
|
plStereizer::plStereizer() |
|
: fInitPos(0,0,0), |
|
fListPos(0,0,0), |
|
fListDirection(0,1.f,0), |
|
fListUp(0,0,1.f) |
|
{ |
|
} |
|
|
|
plStereizer::~plStereizer() |
|
{ |
|
if( !HasMaster() ) |
|
plgDispatch::Dispatch()->UnRegisterForExactType(plListenerMsg::Index(), GetKey()); |
|
} |
|
|
|
void plStereizer::Read(hsStream* stream, hsResMgr* mgr) |
|
{ |
|
plSingleModifier::Read(stream, mgr); |
|
|
|
fAmbientDist = stream->ReadSwapScalar(); |
|
fTransition = stream->ReadSwapScalar(); |
|
|
|
fMaxSepDist = stream->ReadSwapScalar(); |
|
fMinSepDist = stream->ReadSwapScalar(); |
|
|
|
fTanAng = stream->ReadSwapScalar(); |
|
|
|
fInitPos.Read(stream); |
|
|
|
if( !HasMaster() ) |
|
plgDispatch::Dispatch()->RegisterForExactType(plListenerMsg::Index(), GetKey()); |
|
} |
|
|
|
void plStereizer::Write(hsStream* stream, hsResMgr* mgr) |
|
{ |
|
plSingleModifier::Write(stream, mgr); |
|
|
|
stream->WriteSwapScalar(fAmbientDist); |
|
stream->WriteSwapScalar(fTransition); |
|
|
|
stream->WriteSwapScalar(fMaxSepDist); |
|
stream->WriteSwapScalar(fMinSepDist); |
|
|
|
stream->WriteSwapScalar(fTanAng); |
|
|
|
fInitPos.Write(stream); |
|
} |
|
|
|
hsBool plStereizer::MsgReceive(plMessage* msg) |
|
{ |
|
plListenerMsg* listenMsg = plListenerMsg::ConvertNoRef(msg); |
|
if( listenMsg ) |
|
{ |
|
SetFromListenerMsg(listenMsg); |
|
return Stereize(); |
|
} |
|
|
|
return plSingleModifier::MsgReceive(msg); |
|
} |
|
|
|
hsBool plStereizer::IEval(double secs, hsScalar del, UInt32 dirty) |
|
{ |
|
return false; |
|
} |
|
|
|
hsBool plStereizer::Stereize() |
|
{ |
|
plSceneObject* targ = GetTarget(); |
|
if( !targ ) |
|
return true; |
|
|
|
targ->FlushTransform(); |
|
|
|
// Find distance to listener |
|
hsPoint3 pos = IGetUnStereoPos(); |
|
hsVector3 posToList(&fListPos, &pos); |
|
hsScalar dist = posToList.Magnitude(); |
|
|
|
// If distance less than ambient distance |
|
// setup as pure ambient |
|
|
|
// Else if distance greater than ambient distance + transition |
|
// setup as pure localized |
|
|
|
// Else |
|
// Calc pure ambient position |
|
// Calc pure localized position |
|
// Interpolate between the two. |
|
if( dist <= fAmbientDist ) |
|
{ |
|
ISetNewPos(IGetAmbientPos()); |
|
} |
|
else if( dist >= fAmbientDist + fTransition ) |
|
{ |
|
ISetNewPos(IGetLocalizedPos(posToList, dist)); |
|
} |
|
else |
|
{ |
|
hsPoint3 ambPos = IGetAmbientPos(); |
|
hsPoint3 localizePos = IGetLocalizedPos(posToList, dist); |
|
|
|
hsPoint3 newPos(ambPos); |
|
newPos += (localizePos - ambPos) * ((dist - fAmbientDist) / fTransition); |
|
|
|
ISetNewPos(newPos); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void plStereizer::ISetNewPos(const hsPoint3& newPos) |
|
{ |
|
hsMatrix44 l2w = GetTarget()->GetLocalToWorld(); |
|
hsMatrix44 w2l = GetTarget()->GetWorldToLocal(); |
|
|
|
l2w.NotIdentity(); |
|
l2w.fMap[0][3] = newPos[0]; |
|
l2w.fMap[1][3] = newPos[1]; |
|
l2w.fMap[2][3] = newPos[2]; |
|
|
|
hsPoint3 invPos = -newPos; |
|
|
|
w2l.fMap[0][3] = ((hsVector3*)&w2l.fMap[0][0])->InnerProduct(invPos); |
|
w2l.fMap[1][3] = ((hsVector3*)&w2l.fMap[1][0])->InnerProduct(invPos); |
|
w2l.fMap[2][3] = ((hsVector3*)&w2l.fMap[2][0])->InnerProduct(invPos); |
|
|
|
IGetTargetCoordinateInterface(0)->SetTransform(l2w, w2l); |
|
} |
|
|
|
void plStereizer::SetFromListenerMsg(const plListenerMsg* listMsg) |
|
{ |
|
fListPos = listMsg->GetPosition(); |
|
fListDirection = listMsg->GetDirection(); |
|
fListUp = listMsg->GetUp(); |
|
} |
|
|
|
hsPoint3 plStereizer::IGetAmbientPos() const |
|
{ |
|
hsPoint3 pos = fListPos; |
|
hsVector3 axOut = fListDirection % fListUp; |
|
hsFastMath::NormalizeAppr(axOut); |
|
if( IsLeftChannel() ) |
|
axOut *= -fMinSepDist; |
|
else |
|
axOut *= fMinSepDist; |
|
|
|
pos += axOut; |
|
|
|
return pos; |
|
} |
|
|
|
hsPoint3 plStereizer::IGetLocalizedPos(const hsVector3& posToList, hsScalar distToList) const |
|
{ |
|
hsPoint3 pos = IGetUnStereoPos(); |
|
|
|
hsVector3 axOut(-posToList.fY, posToList.fX, 0); |
|
hsFastMath::NormalizeAppr(axOut); |
|
|
|
hsScalar distOut = distToList * fTanAng; |
|
if( distOut > fMaxSepDist ) |
|
distOut = fMaxSepDist; |
|
else if( distOut < fMinSepDist ) |
|
distOut = fMinSepDist; |
|
if( IsLeftChannel() ) |
|
distOut = -distOut; |
|
|
|
axOut *= distOut; |
|
|
|
pos += axOut; |
|
|
|
return pos; |
|
} |
|
|
|
void plStereizer::SetSepAngle(hsScalar rads) |
|
{ |
|
fTanAng = hsScalar(tan(rads)); |
|
} |
|
|
|
hsScalar plStereizer::GetSepAngle() const |
|
{ |
|
return atan(fTanAng); |
|
} |
|
|
|
hsPoint3 plStereizer::IGetUnStereoPos() const |
|
{ |
|
return GetWorldInitPos(); |
|
} |
|
|
|
void plStereizer::SetWorldInitPos(const hsPoint3& pos) |
|
{ |
|
plCoordinateInterface* parent = IGetParent(); |
|
if( parent ) |
|
fInitPos = parent->GetWorldToLocal() * pos; |
|
else |
|
fInitPos = pos; |
|
} |
|
|
|
hsPoint3 plStereizer::GetWorldInitPos() const |
|
{ |
|
plCoordinateInterface* parent = IGetParent(); |
|
if( parent ) |
|
return parent->GetLocalToWorld() * fInitPos; |
|
|
|
return fInitPos; |
|
} |
|
|
|
plCoordinateInterface* plStereizer::IGetParent() const |
|
{ |
|
plCoordinateInterface* coord = IGetTargetCoordinateInterface(0); |
|
if( coord ) |
|
{ |
|
return coord->GetParent(); |
|
} |
|
return nil; |
|
} |
|
|
|
// Note that (along with it's many other hacky defects), this |
|
// will go down in flames if there are two potential masters. |
|
// Of course, two line follow mods doesn't really make sense |
|
// now anyway, but the point is that this is a simplified placeholder |
|
// to get the job done. If and when a need is shown for sequencing of |
|
// modifiers, this should be updated to follow that protocol. But |
|
// the rationale is that one simple example of a need for sequencing |
|
// doesn't give enough basis to decide what that protocol should be. |
|
// Or in simpler terms, I want to do it one way, Brice wants to do |
|
// it another, and since either would work for this, we're waiting |
|
// for a tie breaker case that gives one way or the other an advantage. |
|
hsBool plStereizer::CheckForMaster() |
|
{ |
|
ISetHasMaster(false); |
|
plSceneObject* targ = GetTarget(); |
|
if( !targ ) |
|
return false; |
|
|
|
int n = targ->GetNumModifiers(); |
|
int i; |
|
for( i = 0; i < n; i++ ) |
|
{ |
|
plLineFollowMod* line = plLineFollowMod::ConvertNoRef(IGetTargetModifier(0, i)); |
|
if( line ) |
|
{ |
|
ISetHasMaster(true); |
|
line->AddStereizer(GetKey()); |
|
|
|
return true; |
|
} |
|
} |
|
return false; |
|
} |