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