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

/*==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;
}