|
|
|
/*==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 "hsTypes.h"
|
|
|
|
#include "plCoordinateInterface.h"
|
|
|
|
#include "plDrawInterface.h"
|
|
|
|
#include "plSimulationInterface.h"
|
|
|
|
#include "plAudioInterface.h"
|
|
|
|
#include "../pnMessage/plWarpMsg.h"
|
|
|
|
#include "../pnMessage/plTimeMsg.h"
|
|
|
|
#include "../pnMessage/plCorrectionMsg.h"
|
|
|
|
#include "../pnMessage/plIntRefMsg.h"
|
|
|
|
#include "../pnNetCommon/plSDLTypes.h"
|
|
|
|
#include "plSceneObject.h"
|
|
|
|
#include "hsResMgr.h"
|
|
|
|
#include "plgDispatch.h"
|
|
|
|
#include "../pnKeyedObject/plKey.h"
|
|
|
|
#include "hsStream.h"
|
|
|
|
|
|
|
|
#include "plProfile.h"
|
|
|
|
|
|
|
|
UInt8 plCoordinateInterface::fTransformPhase = plCoordinateInterface::kTransformPhaseNormal;
|
|
|
|
hsBool plCoordinateInterface::fDelayedTransformsEnabled = true;
|
|
|
|
|
|
|
|
plCoordinateInterface::plCoordinateInterface()
|
|
|
|
: fParent(nil),
|
|
|
|
fReason(kReasonUnknown)
|
|
|
|
{
|
|
|
|
fLocalToParent.Reset();
|
|
|
|
fParentToLocal.Reset();
|
|
|
|
|
|
|
|
fLocalToWorld.Reset();
|
|
|
|
fWorldToLocal.Reset();
|
|
|
|
|
|
|
|
fState = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
plCoordinateInterface::~plCoordinateInterface()
|
|
|
|
{
|
|
|
|
if( fParent )
|
|
|
|
fParent->IRemoveChild(IGetOwner());
|
|
|
|
int i;
|
|
|
|
for( i = fChildren.GetCount()-1; i >= 0; i-- )
|
|
|
|
IRemoveChild(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::ISetSceneNode(plKey newNode)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fChildren.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( fChildren[i] )
|
|
|
|
fChildren[i]->SetSceneNode(newNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::ISetOwner(plSceneObject* so)
|
|
|
|
{
|
|
|
|
plObjInterface::ISetOwner(so);
|
|
|
|
|
|
|
|
IDirtyTransform();
|
|
|
|
fReason |= kReasonUnknown;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::ISetParent(plCoordinateInterface* par)
|
|
|
|
{
|
|
|
|
fParent = par;
|
|
|
|
|
|
|
|
// This won't have any effect if my owner is NetGroupConstant
|
|
|
|
if( fParent )
|
|
|
|
ISetNetGroupRecur(fParent->GetNetGroup());
|
|
|
|
|
|
|
|
IDirtyTransform();
|
|
|
|
fReason |= kReasonUnknown;
|
|
|
|
}
|
|
|
|
|
|
|
|
plCoordinateInterface* plCoordinateInterface::GetChild(int i) const
|
|
|
|
{
|
|
|
|
return fChildren[i] ? fChildren[i]->GetVolatileCoordinateInterface() : nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::IRemoveChild(int i)
|
|
|
|
{
|
|
|
|
if( fChildren[i] )
|
|
|
|
{
|
|
|
|
plCoordinateInterface* childCI = fChildren[i]->GetVolatileCoordinateInterface();
|
|
|
|
if( childCI )
|
|
|
|
childCI->ISetParent(nil);
|
|
|
|
}
|
|
|
|
fChildren.Remove(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::IRemoveChild(plSceneObject* child)
|
|
|
|
{
|
|
|
|
int idx = fChildren.Find(child);
|
|
|
|
if( idx != fChildren.kMissingIndex )
|
|
|
|
IRemoveChild(idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::ISetChild(plSceneObject* child, int which)
|
|
|
|
{
|
|
|
|
hsAssert(child, "Setting a nil child");
|
|
|
|
plCoordinateInterface* childCI = child->GetVolatileCoordinateInterface();
|
|
|
|
hsAssert(childCI, "Child with no coordinate interface");
|
|
|
|
childCI->ISetParent(this);
|
|
|
|
|
|
|
|
if( which < 0 )
|
|
|
|
which = fChildren.GetCount();
|
|
|
|
fChildren.ExpandAndZero(which+1);
|
|
|
|
fChildren[which] = child;
|
|
|
|
|
|
|
|
// If we can't delay our transform update, neither can any of our parents.
|
|
|
|
if (!childCI->GetProperty(kDelayedTransformEval))
|
|
|
|
{
|
|
|
|
plCoordinateInterface *current = childCI->GetParent();
|
|
|
|
while (current)
|
|
|
|
{
|
|
|
|
current->SetProperty(kDelayedTransformEval, false);
|
|
|
|
current = current->GetParent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::IAddChild(plSceneObject* child)
|
|
|
|
{
|
|
|
|
ISetChild(child, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::IAttachChild(plSceneObject* child, UInt8 flags)
|
|
|
|
{
|
|
|
|
hsAssert(child, "Attaching a nil child");
|
|
|
|
plCoordinateInterface* childCI = child->GetVolatileCoordinateInterface();
|
|
|
|
hsAssert(childCI, "Owner without CoordinateInterface being attached");
|
|
|
|
|
|
|
|
if (childCI->GetParent() == this)
|
|
|
|
return; // We're already attached! Who told us to do this?
|
|
|
|
|
|
|
|
hsMatrix44 l2w = childCI->GetLocalToWorld();
|
|
|
|
hsMatrix44 w2l = childCI->GetWorldToLocal();
|
|
|
|
|
|
|
|
if( childCI->GetParent() )
|
|
|
|
childCI->GetParent()->IDetachChild(child, flags | kAboutToAttach);
|
|
|
|
|
|
|
|
childCI->IUnRegisterForTransformMessage();
|
|
|
|
|
|
|
|
IAddChild(child);
|
|
|
|
|
|
|
|
if( flags & kMaintainWorldPosition )
|
|
|
|
childCI->WarpToWorld(l2w,w2l);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::IDetachChild(plSceneObject* child, UInt8 flags)
|
|
|
|
{
|
|
|
|
hsAssert(child, "Detaching a nil child");
|
|
|
|
plCoordinateInterface* childCI = child->GetVolatileCoordinateInterface();
|
|
|
|
hsAssert(childCI, "Owner without CoordinateInterface being attached");
|
|
|
|
|
|
|
|
hsMatrix44 l2w = childCI->GetLocalToWorld();
|
|
|
|
hsMatrix44 w2l = childCI->GetWorldToLocal();
|
|
|
|
|
|
|
|
GetKey()->Release(child->GetKey());
|
|
|
|
if( IGetOwner() && IGetOwner()->GetKey() )
|
|
|
|
IGetOwner()->GetKey()->Release(child->GetKey());
|
|
|
|
IRemoveChild(child);
|
|
|
|
|
|
|
|
if( flags & kMaintainWorldPosition )
|
|
|
|
childCI->WarpToWorld(l2w,w2l);
|
|
|
|
|
|
|
|
// If the child was keeping us from delaying our transform,
|
|
|
|
// maybe we can, now that it's gone.
|
|
|
|
if (!childCI->GetProperty(kDelayedTransformEval))
|
|
|
|
IUpdateDelayProp();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A few notes on the delay transform properties...
|
|
|
|
*
|
|
|
|
* The kCanEverDelayTransform prop is independent of any parents/children.
|
|
|
|
* It means this particular node must always update its transform in response
|
|
|
|
* to a plTransformMsg. It is intended for objects with physics, because they
|
|
|
|
* need to be up-to-date before the simulationMgr updates the physical world.
|
|
|
|
*
|
|
|
|
* The kDelayedTransformEval prop is for nodes that are free of physics. (So no
|
|
|
|
* physical descendants either). If the property is set, we won't update our
|
|
|
|
* transform until AFTER the simulationMgr does its work.
|
|
|
|
*
|
|
|
|
* When we attach a child that can't delay its eval (at the moment), we recurse
|
|
|
|
* up to the root, turning off the kDelayedTransformEval prop as we go. When we
|
|
|
|
* remove such a child, we check if that child was the only reason we weren't
|
|
|
|
* delaying our transform. If so, we update ourself and tell our parent to check.
|
|
|
|
*
|
|
|
|
* BTW: The POINT of all this is that when we update our l2w transforms because
|
|
|
|
* we're animated, and then we update AGAIN after a parent node of ours involved
|
|
|
|
* in physics gets a slight nudge, the first update becomes pointless. The
|
|
|
|
* delay prop bookkeeping keeps us from doing the wasted calculations. And since
|
|
|
|
* nearly all bones on the avatar are in this exact situation, it's worth doing.
|
|
|
|
*/
|
|
|
|
void plCoordinateInterface::IUpdateDelayProp()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
if (!GetProperty(kCanEverDelayTransform))
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < GetNumChildren(); i++)
|
|
|
|
{
|
|
|
|
// If we still have a child that needs the delay...
|
|
|
|
if (!GetChild(i)->GetProperty(kDelayedTransformEval))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cool, we can delay now, which means maybe our parent can too.
|
|
|
|
SetProperty(kDelayedTransformEval, true);
|
|
|
|
if (GetParent())
|
|
|
|
GetParent()->IUpdateDelayProp();
|
|
|
|
}
|
|
|
|
|
|
|
|
plCoordinateInterface* plCoordinateInterface::IGetRoot()
|
|
|
|
{
|
|
|
|
return fParent ? fParent->IGetRoot() : this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::IRegisterForTransformMessage(hsBool delayed)
|
|
|
|
{
|
|
|
|
if( IGetOwner() )
|
|
|
|
{
|
|
|
|
if ((delayed || fTransformPhase == kTransformPhaseDelayed) && fDelayedTransformsEnabled)
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plDelayedTransformMsg::Index(), IGetOwner()->GetKey());
|
|
|
|
else
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plTransformMsg::Index(), IGetOwner()->GetKey());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::IUnRegisterForTransformMessage()
|
|
|
|
{
|
|
|
|
if( IGetOwner() )
|
|
|
|
plgDispatch::Dispatch()->UnRegisterForExactType(plTransformMsg::Index(), IGetOwner()->GetKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void plCoordinateInterface::IDirtyTransform()
|
|
|
|
{
|
|
|
|
fState |= kTransformDirty;
|
|
|
|
|
|
|
|
IGetRoot()->IRegisterForTransformMessage(GetProperty(kDelayedTransformEval));
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::MultTransformLocal(const hsMatrix44& move, const hsMatrix44& invMove)
|
|
|
|
{
|
|
|
|
fReason |= kReasonUnknown;
|
|
|
|
fLocalToParent = move * fLocalToParent;
|
|
|
|
fParentToLocal = fParentToLocal * invMove;
|
|
|
|
|
|
|
|
IDirtyTransform();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l)
|
|
|
|
{
|
|
|
|
fReason |= kReasonUnknown;
|
|
|
|
|
|
|
|
if( fParent )
|
|
|
|
{
|
|
|
|
SetLocalToParent(fParent->GetWorldToLocal() * l2w, w2l * fParent->GetLocalToWorld());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SetLocalToParent(l2w, w2l);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::SetTransformPhysical(const hsMatrix44& l2w, const hsMatrix44& w2l)
|
|
|
|
{
|
|
|
|
// since we use public interfaces to do the details
|
|
|
|
// AND those public interfaces could be called by anyone
|
|
|
|
// AND those public interfaces therefore have to set their own "reason" for the transform change
|
|
|
|
// THEREFORE: we need to preserve the "reason" flags before we call the public interfaces
|
|
|
|
// so that we don't get reasonPhysics + reasonUnknown, just reasonPhysics
|
|
|
|
UInt16 oldReason = fReason;
|
|
|
|
|
|
|
|
if( fParent )
|
|
|
|
{
|
|
|
|
SetLocalToParent(fParent->GetWorldToLocal() * l2w, w2l * fParent->GetLocalToWorld());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SetLocalToParent(l2w, w2l);
|
|
|
|
}
|
|
|
|
fReason = oldReason | kReasonPhysics;
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt16 plCoordinateInterface::GetReasons()
|
|
|
|
{
|
|
|
|
return fReason;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::ClearReasons()
|
|
|
|
{
|
|
|
|
fReason = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::SetLocalToParent(const hsMatrix44& l2p, const hsMatrix44& p2l)
|
|
|
|
{
|
|
|
|
fReason |= kReasonUnknown;
|
|
|
|
fLocalToParent = l2p;
|
|
|
|
fParentToLocal = p2l;
|
|
|
|
|
|
|
|
IDirtyTransform();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::WarpToLocal(const hsMatrix44& l2p, const hsMatrix44& p2l)
|
|
|
|
{
|
|
|
|
fReason |= kReasonUnknown;
|
|
|
|
SetLocalToParent(l2p, p2l);
|
|
|
|
|
|
|
|
// update physical state when an object is warped
|
|
|
|
if (IGetOwner())
|
|
|
|
IGetOwner()->DirtySynchState(kSDLPhysical, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::WarpToWorld(const hsMatrix44& l2w, const hsMatrix44& w2l)
|
|
|
|
{
|
|
|
|
fReason |= kReasonUnknown;
|
|
|
|
if( fParent )
|
|
|
|
{
|
|
|
|
hsMatrix44 l2p = fParent->GetWorldToLocal() * l2w;
|
|
|
|
hsMatrix44 p2l = w2l * fParent->GetLocalToWorld();
|
|
|
|
WarpToLocal(l2p, p2l);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
WarpToLocal(l2w, w2l);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plProfile_CreateCounter("CITrans", "Object", CITrans);
|
|
|
|
plProfile_CreateCounter(" CIRecalc", "Object", CIRecalc);
|
|
|
|
plProfile_CreateCounter(" CIDirty", "Object", CIDirty);
|
|
|
|
plProfile_CreateCounter(" CISet", "Object", CISet);
|
|
|
|
|
|
|
|
plProfile_CreateTimer("CITransT", "Object", CITransT);
|
|
|
|
plProfile_CreateTimer(" CIRecalcT", "Object", CIRecalcT);
|
|
|
|
plProfile_CreateTimer(" CIDirtyT", "Object", CIDirtyT);
|
|
|
|
plProfile_CreateTimer(" CISetT", "Object", CISetT);
|
|
|
|
|
|
|
|
static inline hsMatrix44 IMatrixMul34(const hsMatrix44& lhs, const hsMatrix44& rhs)
|
|
|
|
{
|
|
|
|
hsMatrix44 ret;
|
|
|
|
ret.NotIdentity();
|
|
|
|
ret.fMap[3][0] = ret.fMap[3][1] = ret.fMap[3][2] = 0;
|
|
|
|
ret.fMap[3][3] = 1.f;
|
|
|
|
|
|
|
|
ret.fMap[0][0] = lhs.fMap[0][0] * rhs.fMap[0][0]
|
|
|
|
+ lhs.fMap[0][1] * rhs.fMap[1][0]
|
|
|
|
+ lhs.fMap[0][2] * rhs.fMap[2][0];
|
|
|
|
|
|
|
|
ret.fMap[0][1] = lhs.fMap[0][0] * rhs.fMap[0][1]
|
|
|
|
+ lhs.fMap[0][1] * rhs.fMap[1][1]
|
|
|
|
+ lhs.fMap[0][2] * rhs.fMap[2][1];
|
|
|
|
|
|
|
|
ret.fMap[0][2] = lhs.fMap[0][0] * rhs.fMap[0][2]
|
|
|
|
+ lhs.fMap[0][1] * rhs.fMap[1][2]
|
|
|
|
+ lhs.fMap[0][2] * rhs.fMap[2][2];
|
|
|
|
|
|
|
|
ret.fMap[0][3] = lhs.fMap[0][0] * rhs.fMap[0][3]
|
|
|
|
+ lhs.fMap[0][1] * rhs.fMap[1][3]
|
|
|
|
+ lhs.fMap[0][2] * rhs.fMap[2][3]
|
|
|
|
+ lhs.fMap[0][3];
|
|
|
|
|
|
|
|
ret.fMap[1][0] = lhs.fMap[1][0] * rhs.fMap[0][0]
|
|
|
|
+ lhs.fMap[1][1] * rhs.fMap[1][0]
|
|
|
|
+ lhs.fMap[1][2] * rhs.fMap[2][0];
|
|
|
|
|
|
|
|
ret.fMap[1][1] = lhs.fMap[1][0] * rhs.fMap[0][1]
|
|
|
|
+ lhs.fMap[1][1] * rhs.fMap[1][1]
|
|
|
|
+ lhs.fMap[1][2] * rhs.fMap[2][1];
|
|
|
|
|
|
|
|
ret.fMap[1][2] = lhs.fMap[1][0] * rhs.fMap[0][2]
|
|
|
|
+ lhs.fMap[1][1] * rhs.fMap[1][2]
|
|
|
|
+ lhs.fMap[1][2] * rhs.fMap[2][2];
|
|
|
|
|
|
|
|
ret.fMap[1][3] = lhs.fMap[1][0] * rhs.fMap[0][3]
|
|
|
|
+ lhs.fMap[1][1] * rhs.fMap[1][3]
|
|
|
|
+ lhs.fMap[1][2] * rhs.fMap[2][3]
|
|
|
|
+ lhs.fMap[1][3];
|
|
|
|
|
|
|
|
ret.fMap[2][0] = lhs.fMap[2][0] * rhs.fMap[0][0]
|
|
|
|
+ lhs.fMap[2][1] * rhs.fMap[1][0]
|
|
|
|
+ lhs.fMap[2][2] * rhs.fMap[2][0];
|
|
|
|
|
|
|
|
ret.fMap[2][1] = lhs.fMap[2][0] * rhs.fMap[0][1]
|
|
|
|
+ lhs.fMap[2][1] * rhs.fMap[1][1]
|
|
|
|
+ lhs.fMap[2][2] * rhs.fMap[2][1];
|
|
|
|
|
|
|
|
ret.fMap[2][2] = lhs.fMap[2][0] * rhs.fMap[0][2]
|
|
|
|
+ lhs.fMap[2][1] * rhs.fMap[1][2]
|
|
|
|
+ lhs.fMap[2][2] * rhs.fMap[2][2];
|
|
|
|
|
|
|
|
ret.fMap[2][3] = lhs.fMap[2][0] * rhs.fMap[0][3]
|
|
|
|
+ lhs.fMap[2][1] * rhs.fMap[1][3]
|
|
|
|
+ lhs.fMap[2][2] * rhs.fMap[2][3]
|
|
|
|
+ lhs.fMap[2][3];
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::IRecalcTransforms()
|
|
|
|
{
|
|
|
|
plProfile_IncCount(CIRecalc, 1);
|
|
|
|
plProfile_BeginTiming(CIRecalcT);
|
|
|
|
if( fParent )
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
fLocalToWorld = fParent->GetLocalToWorld() * fLocalToParent;
|
|
|
|
fWorldToLocal = fParentToLocal * fParent->GetWorldToLocal();
|
|
|
|
#else
|
|
|
|
fLocalToWorld = IMatrixMul34(fParent->GetLocalToWorld(), fLocalToParent);
|
|
|
|
fWorldToLocal = IMatrixMul34(fParentToLocal, fParent->GetWorldToLocal());
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fLocalToWorld = fLocalToParent;
|
|
|
|
fWorldToLocal = fParentToLocal;
|
|
|
|
}
|
|
|
|
plProfile_EndTiming(CIRecalcT);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::ITransformChanged(hsBool force, UInt16 reasons, hsBool checkForDelay)
|
|
|
|
{
|
|
|
|
plProfile_IncCount(CITrans, 1);
|
|
|
|
plProfile_BeginTiming(CITransT);
|
|
|
|
|
|
|
|
// inherit reasons for transform change from our parents
|
|
|
|
fReason |= reasons;
|
|
|
|
|
|
|
|
UInt16 propagateReasons = fReason;
|
|
|
|
|
|
|
|
hsBool process = !(checkForDelay && GetProperty(kDelayedTransformEval)) || !fDelayedTransformsEnabled;
|
|
|
|
|
|
|
|
if (process)
|
|
|
|
{
|
|
|
|
if( fState & kTransformDirty )
|
|
|
|
force = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( force )
|
|
|
|
{
|
|
|
|
IRecalcTransforms();
|
|
|
|
|
|
|
|
plProfile_IncCount(CISet, 1);
|
|
|
|
plProfile_BeginTiming(CISetT);
|
|
|
|
if( IGetOwner() )
|
|
|
|
{
|
|
|
|
IGetOwner()->ISetTransform(fLocalToWorld, fWorldToLocal);
|
|
|
|
}
|
|
|
|
plProfile_EndTiming(CISetT);
|
|
|
|
fState &= ~kTransformDirty;
|
|
|
|
}
|
|
|
|
|
|
|
|
plProfile_EndTiming(CITransT);
|
|
|
|
if (process)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fChildren.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( fChildren[i] && fChildren[i]->GetVolatileCoordinateInterface() )
|
|
|
|
fChildren[i]->GetVolatileCoordinateInterface()->ITransformChanged(force, propagateReasons, checkForDelay);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (force)
|
|
|
|
{
|
|
|
|
plProfile_IncCount(CIDirty, 1);
|
|
|
|
plProfile_BeginTiming(CITransT);
|
|
|
|
// Our parent is dirty and we're bailing out on evaluating right now.
|
|
|
|
// Need to ensure we'll be evaluated in the delay pass
|
|
|
|
plProfile_BeginTiming(CIDirtyT);
|
|
|
|
IDirtyTransform();
|
|
|
|
plProfile_EndTiming(CIDirtyT);
|
|
|
|
plProfile_EndTiming(CITransT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::FlushTransform(hsBool fromRoot)
|
|
|
|
{
|
|
|
|
if( fromRoot )
|
|
|
|
IGetRoot()->ITransformChanged(false, 0, false);
|
|
|
|
else
|
|
|
|
ITransformChanged(false, 0, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::ISetNetGroupRecur(plNetGroupId netGroup)
|
|
|
|
{
|
|
|
|
if( !IGetOwner() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( IGetOwner()->GetSynchFlags() & kHasConstantNetGroup )
|
|
|
|
return;
|
|
|
|
|
|
|
|
IGetOwner()->plSynchedObject::SetNetGroup(netGroup);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < GetNumChildren(); i++ )
|
|
|
|
{
|
|
|
|
if( GetChild(i) )
|
|
|
|
{
|
|
|
|
GetChild(i)->ISetNetGroupRecur(netGroup);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void plCoordinateInterface::Read(hsStream* stream, hsResMgr* mgr)
|
|
|
|
{
|
|
|
|
plObjInterface::Read(stream, mgr);
|
|
|
|
|
|
|
|
fLocalToParent.Read(stream);
|
|
|
|
fParentToLocal.Read(stream);
|
|
|
|
|
|
|
|
fLocalToWorld.Read(stream);
|
|
|
|
fWorldToLocal.Read(stream);
|
|
|
|
|
|
|
|
int n = stream->ReadSwap32();
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < n; i++ )
|
|
|
|
{
|
|
|
|
plIntRefMsg* refMsg = TRACKED_NEW plIntRefMsg(GetKey(), plRefMsg::kOnCreate, -1, plIntRefMsg::kChildObject);
|
|
|
|
mgr->ReadKeyNotifyMe(stream,refMsg, plRefFlags::kPassiveRef);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plCoordinateInterface::Write(hsStream* stream, hsResMgr* mgr)
|
|
|
|
{
|
|
|
|
plObjInterface::Write(stream, mgr);
|
|
|
|
|
|
|
|
fLocalToParent.Write(stream);
|
|
|
|
fParentToLocal.Write(stream);
|
|
|
|
|
|
|
|
fLocalToWorld.Write(stream);
|
|
|
|
fWorldToLocal.Write(stream);
|
|
|
|
|
|
|
|
stream->WriteSwap32(fChildren.GetCount());
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fChildren.GetCount(); i++ )
|
|
|
|
mgr->WriteKey(stream, fChildren[i]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plCoordinateInterface::MsgReceive(plMessage* msg)
|
|
|
|
{
|
|
|
|
hsBool retVal = false;
|
|
|
|
|
|
|
|
plIntRefMsg* intRefMsg;
|
|
|
|
plCorrectionMsg* corrMsg;
|
|
|
|
|
|
|
|
// warp message
|
|
|
|
plWarpMsg* pWarpMsg = plWarpMsg::ConvertNoRef(msg);
|
|
|
|
if (pWarpMsg)
|
|
|
|
{
|
|
|
|
hsMatrix44 l2w = pWarpMsg->GetTransform();
|
|
|
|
hsMatrix44 inv;
|
|
|
|
l2w.GetInverse(&inv);
|
|
|
|
WarpToWorld(l2w,inv);
|
|
|
|
if (pWarpMsg->GetWarpFlags() & plWarpMsg::kFlushTransform)
|
|
|
|
ITransformChanged(false, kReasonUnknown, false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if( intRefMsg = plIntRefMsg::ConvertNoRef(msg) )
|
|
|
|
{
|
|
|
|
switch( intRefMsg->fType )
|
|
|
|
{
|
|
|
|
case plIntRefMsg::kChildObject:
|
|
|
|
case plIntRefMsg::kChild:
|
|
|
|
{
|
|
|
|
plSceneObject* co = nil;
|
|
|
|
if( intRefMsg->fType == plIntRefMsg::kChildObject )
|
|
|
|
{
|
|
|
|
co = plSceneObject::ConvertNoRef(intRefMsg->GetRef());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
plCoordinateInterface* ci = plCoordinateInterface::ConvertNoRef(intRefMsg->GetRef());
|
|
|
|
co = ci ? ci->IGetOwner() : nil;
|
|
|
|
}
|
|
|
|
if( intRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnReplace) )
|
|
|
|
{
|
|
|
|
ISetChild(co, intRefMsg->fWhich);
|
|
|
|
}
|
|
|
|
else if( intRefMsg->GetContext() & plRefMsg::kOnDestroy )
|
|
|
|
{
|
|
|
|
IRemoveChild(co);
|
|
|
|
}
|
|
|
|
else if( intRefMsg->GetContext() & plRefMsg::kOnRequest )
|
|
|
|
{
|
|
|
|
IAttachChild(co, kMaintainWorldPosition|kMaintainSceneNode);
|
|
|
|
}
|
|
|
|
else if( intRefMsg->GetContext() & plRefMsg::kOnRemove )
|
|
|
|
{
|
|
|
|
IDetachChild(co, kMaintainWorldPosition|kMaintainSceneNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( corrMsg = plCorrectionMsg::ConvertNoRef(msg) )
|
|
|
|
{
|
|
|
|
SetTransformPhysical(corrMsg->fLocalToWorld, corrMsg->fWorldToLocal);
|
|
|
|
|
|
|
|
if(corrMsg->fDirtySynch)
|
|
|
|
{
|
|
|
|
if (IGetOwner())
|
|
|
|
IGetOwner()->DirtySynchState(kSDLPhysical, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return plObjInterface::MsgReceive(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|