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.
807 lines
20 KiB
807 lines
20 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 "hsTypes.h" |
|
#include "plMorphSequence.h" |
|
#include "plMorphSequenceSDLMod.h" |
|
|
|
#include "plAccessGeometry.h" |
|
#include "plAccessVtxSpan.h" |
|
|
|
#include "../pnSceneObject/plSceneObject.h" |
|
#include "../pnSceneObject/plDrawInterface.h" |
|
#include "../pnSceneObject/plCoordinateInterface.h" |
|
|
|
#include "plDrawableSpans.h" |
|
#include "plInstanceDrawInterface.h" |
|
|
|
#include "hsResMgr.h" |
|
#include "plgDispatch.h" |
|
#include "../plMessage/plRenderMsg.h" |
|
|
|
#include "plSharedMesh.h" |
|
|
|
#include "plTweak.h" |
|
#include "hsTimer.h" |
|
#include "hsFastMath.h" |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
|
|
void plMorphDataSet::Read(hsStream* s, hsResMgr* mgr) |
|
{ |
|
hsKeyedObject::Read(s, mgr); |
|
|
|
int n = s->ReadSwap32(); |
|
fMorphs.SetCount(n); |
|
int i; |
|
for( i = 0; i < n; i++ ) |
|
fMorphs[i].Read(s, mgr); |
|
} |
|
|
|
void plMorphDataSet::Write(hsStream* s, hsResMgr* mgr) |
|
{ |
|
hsKeyedObject::Write(s, mgr); |
|
|
|
s->WriteSwap32(fMorphs.GetCount()); |
|
int i; |
|
for( i = 0; i < fMorphs.GetCount(); i++ ) |
|
fMorphs[i].Write(s, mgr); |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////// |
|
|
|
const UInt32 kChanMask = plAccessVtxSpan::kPositionMask |
|
| plAccessVtxSpan::kNormalMask |
|
| plAccessVtxSpan::kUVWMask; |
|
|
|
// Probably want another type which is just initialize/override |
|
// so we can set the mesh to whatever base set we want, as if |
|
// that's what got loaded from disk. |
|
// Use Access RW. That might already be handled in the customization stuff. |
|
|
|
plConst(hsScalar) kMorphTime(0.5); |
|
|
|
class plMorphTarget |
|
{ |
|
public: |
|
UInt16 fLayer; |
|
UInt16 fDelta; |
|
hsScalar fWeight; |
|
}; |
|
|
|
hsTArray<plMorphTarget> fTgtWgts; |
|
|
|
plMorphSequence::plMorphSequence() |
|
: fMorphFlags(0), |
|
fMorphSDLMod(nil), |
|
fGlobalLayerRef(-1) |
|
{ |
|
} |
|
|
|
plMorphSequence::~plMorphSequence() |
|
{ |
|
DeInit(); |
|
} |
|
|
|
hsBool plMorphSequence::MsgReceive(plMessage* msg) |
|
{ |
|
plRenderMsg* rend = plRenderMsg::ConvertNoRef(msg); |
|
if( rend ) |
|
{ |
|
// For now, I'm ignoring the target weight stuff for shared meshes. |
|
// Can always add it in later if desired. |
|
if( fTgtWgts.GetCount() ) |
|
{ |
|
hsScalar delWgt = hsTimer::GetDelSysSeconds() / (kMorphTime > 0 ? kMorphTime : 1.e-3f); |
|
int i; |
|
for( i = 0; i < fTgtWgts.GetCount(); i++ ) |
|
{ |
|
hsScalar currWgt = GetWeight(fTgtWgts[i].fLayer, fTgtWgts[i].fDelta); |
|
if( fTgtWgts[i].fWeight < currWgt ) |
|
{ |
|
if( fTgtWgts[i].fWeight >= (currWgt -= delWgt) ) |
|
currWgt = fTgtWgts[i].fWeight; |
|
} |
|
else if( fTgtWgts[i].fWeight > currWgt ) |
|
{ |
|
if( fTgtWgts[i].fWeight <= (currWgt += delWgt) ) |
|
currWgt = fTgtWgts[i].fWeight; |
|
} |
|
|
|
fMorphs[fTgtWgts[i].fLayer].SetWeight(fTgtWgts[i].fDelta, currWgt); |
|
|
|
if( fTgtWgts[i].fWeight == currWgt ) |
|
{ |
|
fTgtWgts.Remove(i); |
|
i--; |
|
} |
|
} |
|
ISetDirty(true); |
|
} |
|
|
|
if( !(fMorphFlags & kDirty) ) |
|
{ |
|
// We went a whole frame without getting dirty, |
|
// we can stop refreshing now. |
|
plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); |
|
|
|
return true; |
|
} |
|
ISetDirty(false); |
|
if( fMorphFlags & kDirtyIndices ) |
|
IFindIndices(); |
|
|
|
if( fMorphFlags & kHaveShared ) |
|
{ |
|
IApplyShared(); |
|
} |
|
else |
|
{ |
|
Apply(); |
|
} |
|
return true; |
|
} |
|
|
|
plSharedMeshBCMsg *smMsg = plSharedMeshBCMsg::ConvertNoRef(msg); |
|
if (smMsg) |
|
{ |
|
if (IGetDrawInterface()->GetKey() == smMsg->GetSender() || IIsUsingDrawable(smMsg->fDraw)) |
|
fMorphFlags |= kDirtyIndices; |
|
} |
|
|
|
plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg); |
|
if (refMsg) |
|
{ |
|
plSharedMesh *mesh = plSharedMesh::ConvertNoRef(refMsg->GetRef()); |
|
if (mesh) |
|
{ |
|
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest) ) |
|
{ |
|
AddSharedMesh(mesh); |
|
} |
|
else if( refMsg->GetContext() & plRefMsg::kOnReplace) |
|
{ |
|
plSharedMesh *oldMesh = plSharedMesh::ConvertNoRef(refMsg->GetOldRef()); |
|
if (oldMesh) |
|
RemoveSharedMesh(oldMesh); |
|
AddSharedMesh(mesh); |
|
} |
|
else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) |
|
RemoveSharedMesh(mesh); |
|
|
|
return true; |
|
} |
|
} |
|
|
|
return plSingleModifier::MsgReceive(msg); |
|
} |
|
|
|
int plMorphSequence::GetNumLayers(plKey meshKey /* = nil */) const |
|
{ |
|
int index = IFindSharedMeshIndex(meshKey); |
|
if (index < 0) |
|
return fMorphs.GetCount(); |
|
else |
|
return fSharedMeshes[index].fMesh->fMorphSet->fMorphs.GetCount(); |
|
} |
|
|
|
int plMorphSequence::GetNumDeltas(int iLay, plKey meshKey /* = nil */) const |
|
{ |
|
int index = IFindSharedMeshIndex(meshKey); |
|
if (index < 0) |
|
return fMorphs[iLay].GetNumDeltas(); |
|
else |
|
return fSharedMeshes[index].fMesh->fMorphSet->fMorphs[iLay].GetNumDeltas(); |
|
} |
|
|
|
hsScalar plMorphSequence::GetWeight(int iLay, int iDel, plKey meshKey /* = nil */) const |
|
{ |
|
int index = IFindSharedMeshIndex(meshKey); |
|
if (index == -1) |
|
return fMorphs[iLay].GetWeight(iDel); |
|
else |
|
return fSharedMeshes[index].fArrayWeights[iLay].fDeltaWeights[iDel]; |
|
} |
|
|
|
void plMorphSequence::SetWeight(int iLay, int iDel, hsScalar w, plKey meshKey /* = nil */) |
|
{ |
|
int index = IFindSharedMeshIndex(meshKey); |
|
|
|
// Only dirty if the weight isn't for a pending mesh |
|
if(meshKey == nil || index >= 0) |
|
ISetDirty(true); |
|
|
|
if (meshKey == nil) |
|
{ |
|
if( iLay < fMorphs.GetCount() ) |
|
{ |
|
if( kMorphTime > 0 ) |
|
{ |
|
plMorphTarget tgt; |
|
tgt.fLayer = iLay; |
|
tgt.fDelta = iDel; |
|
tgt.fWeight = w; |
|
|
|
fTgtWgts.Append(tgt); |
|
} |
|
else |
|
{ |
|
fMorphs[iLay].SetWeight(iDel, w); |
|
} |
|
} |
|
} |
|
else if (index >= 0) |
|
{ |
|
fSharedMeshes[index].fArrayWeights[iLay].fDeltaWeights[iDel] = w; |
|
fSharedMeshes[index].fFlags |= plSharedMeshInfo::kInfoDirtyMesh; |
|
if (index == fGlobalLayerRef) |
|
ISetAllSharedToGlobal(); |
|
} |
|
else |
|
{ |
|
// Setting weight for a mesh we haven't added yet (loading state) |
|
index = IFindPendingStateIndex(meshKey); |
|
if (index < 0) |
|
{ |
|
fPendingStates.Push(); |
|
index = fPendingStates.GetCount() - 1; |
|
fPendingStates[index].fSharedMeshKey = meshKey; |
|
} |
|
if (fPendingStates[index].fArrayWeights.GetCount() <= iLay) |
|
{ |
|
int had = fPendingStates[index].fArrayWeights.GetCount(); |
|
hsTArray<plMorphArrayWeights> temp(iLay + 1); |
|
temp = fPendingStates[index].fArrayWeights; |
|
temp.SetCount(iLay + 1); |
|
|
|
fPendingStates[index].fArrayWeights.Swap(temp); |
|
int i; |
|
for( i = had; i < iLay+1; i++ ) |
|
fPendingStates[index].fArrayWeights[i].fDeltaWeights.Reset(); |
|
} |
|
if (fPendingStates[index].fArrayWeights[iLay].fDeltaWeights.GetCount() <= iDel) |
|
fPendingStates[index].fArrayWeights[iLay].fDeltaWeights.ExpandAndZero(iDel + 1); |
|
|
|
fPendingStates[index].fArrayWeights[iLay].fDeltaWeights[iDel] = w; |
|
} |
|
} |
|
|
|
void plMorphSequence::ISetDirty(hsBool on) |
|
{ |
|
if( on ) |
|
{ |
|
if( !(fMorphFlags & kDirty) ) |
|
plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); |
|
fMorphFlags |= kDirty; |
|
|
|
// Even if we know we're already dirty, there could be new info this frame. |
|
// Need to tell our scene object |
|
// |
|
// Actually, in the case of the avatar, this sends an insane flurry of messages to the server |
|
// when we drag (or even just hold the mouse button down on) the morph scrollbars. |
|
// These messages are completely unneccessary. We broadcast our state when we join a new age, and that's enough. |
|
// Non-avatar related morphs (if they ever exist) will need to call DirtySynchState some special |
|
// way. Not here. |
|
// |
|
//if (GetTarget(0)) |
|
// GetTarget(0)->DirtySynchState(kSDLMorphSequence, 0); |
|
} |
|
else |
|
{ |
|
fMorphFlags &= ~kDirty; |
|
} |
|
} |
|
|
|
// MorphSequence - Init |
|
void plMorphSequence::Init() |
|
{ |
|
if( fMorphFlags & kHaveShared ) |
|
{ |
|
IFindIndices(); |
|
} |
|
else |
|
{ |
|
if( !GetHaveSnap() ) |
|
{ |
|
// Take an access snapshot of all our spans |
|
const plDrawInterface* di = IGetDrawInterface(); |
|
|
|
plAccessGeometry::Instance()->TakeSnapShot(di, kChanMask); |
|
|
|
ISetHaveSnap(true); |
|
} |
|
} |
|
} |
|
|
|
void plMorphSequence::Activate() |
|
{ |
|
Init(); |
|
ISetDirty(true); |
|
} |
|
|
|
void plMorphSequence::DeActivate() |
|
{ |
|
if( fMorphFlags & kHaveShared ) |
|
{ |
|
IReleaseIndices(); |
|
} |
|
plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); |
|
} |
|
|
|
// MorphSequence - DeInit |
|
void plMorphSequence::DeInit() |
|
{ |
|
if( fMorphFlags & kHaveShared ) |
|
{ |
|
IReleaseIndices(); |
|
} |
|
else |
|
{ |
|
if( GetHaveSnap() ) |
|
{ |
|
// Release all our snapshot data |
|
const plDrawInterface* di = IGetDrawInterface(); |
|
|
|
if( di ) |
|
plAccessGeometry::Instance()->ReleaseSnapShot(di); |
|
|
|
ISetHaveSnap(false); |
|
} |
|
} |
|
} |
|
|
|
void plMorphSequence::IRenormalize(hsTArray<plAccessSpan>& dst) const |
|
{ |
|
int i; |
|
for( i = 0; i < dst.GetCount(); i++ ) |
|
{ |
|
hsAssert(dst[i].HasAccessVtx(), "Come on, everyone has vertices"); |
|
plAccessVtxSpan& accVtx = dst[i].AccessVtx(); |
|
int j; |
|
for( j = 0; j < accVtx.VertCount(); j++ ) |
|
{ |
|
hsFastMath::Normalize(accVtx.Normal(j)); |
|
} |
|
} |
|
} |
|
|
|
// MorphSequence - Apply |
|
void plMorphSequence::Apply() const |
|
{ |
|
const plDrawInterface* di = IGetDrawInterface(); |
|
if( !di ) |
|
return; |
|
|
|
// Reset to initial. |
|
Reset(di); |
|
|
|
// We'll be accumulating into the buffer, so open RW |
|
hsTArray<plAccessSpan> dst; |
|
plAccessGeometry::Instance()->OpenRW(di, dst); |
|
|
|
// For each MorphArray |
|
int i; |
|
for( i = 0; i < fMorphs.GetCount(); i++ ) |
|
{ |
|
// Apply Delta |
|
fMorphs[i].Apply(dst); |
|
} |
|
|
|
IRenormalize(dst); |
|
|
|
// Close up the access spans |
|
plAccessGeometry::Instance()->Close(dst); |
|
} |
|
|
|
// MorphSequence - Reset to initial |
|
void plMorphSequence::Reset(const plDrawInterface* di) const |
|
{ |
|
if( !di ) |
|
{ |
|
di = IGetDrawInterface(); |
|
} |
|
|
|
// Use access span RestoreSnapshot |
|
if( di ) |
|
plAccessGeometry::Instance()->RestoreSnapShot(di, kChanMask); |
|
} |
|
|
|
const plDrawInterface* plMorphSequence::IGetDrawInterface() const |
|
{ |
|
plSceneObject* so = GetTarget(); |
|
if( !so ) |
|
return nil; |
|
|
|
const plDrawInterface* di = so->GetDrawInterface(); |
|
|
|
return di; |
|
} |
|
|
|
void plMorphSequence::AddTarget(plSceneObject* so) |
|
{ |
|
plSingleModifier::AddTarget(so); |
|
|
|
if (!fMorphSDLMod) |
|
{ |
|
fMorphSDLMod = TRACKED_NEW plMorphSequenceSDLMod; |
|
so->AddModifier(fMorphSDLMod); |
|
} |
|
} |
|
|
|
void plMorphSequence::RemoveTarget(plSceneObject *so) |
|
{ |
|
plSingleModifier::RemoveTarget(so); |
|
|
|
if (so) |
|
if (fMorphSDLMod) |
|
so->RemoveModifier(fMorphSDLMod); |
|
|
|
delete fMorphSDLMod; |
|
fMorphSDLMod = nil; |
|
} |
|
|
|
void plMorphSequence::Read(hsStream* s, hsResMgr* mgr) |
|
{ |
|
plSingleModifier::Read(s, mgr); |
|
|
|
fMorphFlags = 0; |
|
|
|
int n = s->ReadSwap32(); |
|
fMorphs.SetCount(n); |
|
int i; |
|
for( i = 0; i < n; i++ ) |
|
fMorphs[i].Read(s, mgr); |
|
|
|
n = s->ReadSwap32(); |
|
for( i = 0; i < n; i++) |
|
mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); |
|
} |
|
|
|
void plMorphSequence::Write(hsStream* s, hsResMgr* mgr) |
|
{ |
|
plSingleModifier::Write(s, mgr); |
|
|
|
s->WriteSwap32(fMorphs.GetCount()); |
|
int i; |
|
for( i = 0; i < fMorphs.GetCount(); i++ ) |
|
fMorphs[i].Write(s, mgr); |
|
|
|
s->WriteSwap32(fSharedMeshes.GetCount()); |
|
for( i = 0; i < fSharedMeshes.GetCount(); i++ ) |
|
mgr->WriteKey(s, fSharedMeshes[i].fMesh); |
|
} |
|
|
|
// Normal sequence of calls: |
|
// 1) on notification that meshes have changed (or activate) |
|
// IFindIndices() - Sets up indices |
|
// IApplyShared() - Find which mesh is active and |
|
// IResetShared(iActive); |
|
// IApplyShared(iActive); |
|
// go dormant |
|
// 2) on weight change |
|
// SetWeight() - Register for render message and set the new weight |
|
// 3) on render msg: |
|
// if dirty |
|
// IApplyShared() - Find which mesh is active and |
|
// IResetShared(iActive); |
|
// IApplyShared(iActive); |
|
// else |
|
// Unregister for render message. |
|
// 4) on deinit |
|
// IReleaseIndices() - Just let's us know there's nothing much to be done. |
|
// |
|
|
|
void plMorphSequence::IResetShared() |
|
{ |
|
int i; |
|
for (i = 0; i < fSharedMeshes.GetCount(); i++) |
|
IResetShared(i); |
|
} |
|
|
|
void plMorphSequence::IApplyShared() |
|
{ |
|
int i; |
|
for (i = 0; i < fSharedMeshes.GetCount(); i++) |
|
{ |
|
IResetShared(i); |
|
IApplyShared(i); |
|
} |
|
} |
|
|
|
void plMorphSequence::IFindIndices() |
|
{ |
|
fMorphFlags &= ~kDirtyIndices; |
|
|
|
int i; |
|
for( i = 0; i < fSharedMeshes.GetCount(); i++ ) |
|
IFindIndices(i); |
|
} |
|
|
|
void plMorphSequence::IReleaseIndices() |
|
{ |
|
int i; |
|
for( i = 0; i < fSharedMeshes.GetCount(); i++ ) |
|
IReleaseIndices(i); |
|
} |
|
|
|
|
|
void plMorphSequence::IApplyShared(int iShare) |
|
{ |
|
if( iShare >= fSharedMeshes.GetCount() || fSharedMeshes[iShare].fCurrDraw == nil) |
|
return; |
|
|
|
plSharedMeshInfo& mInfo = fSharedMeshes[iShare]; |
|
|
|
hsTArray<plAccessSpan> dst; |
|
// Now copy each shared mesh geometryspan into the drawable |
|
// to get it back to it's pristine condition. |
|
int i; |
|
for( i = 0; i < mInfo.fMesh->fSpans.GetCount(); i++ ) |
|
{ |
|
plAccessSpan dstAcc; |
|
plAccessGeometry::Instance()->OpenRW(mInfo.fCurrDraw, mInfo.fCurrIdx[i], dstAcc); |
|
|
|
dst.Append(dstAcc); |
|
} |
|
|
|
// For each MorphArray |
|
for( i = 0; i < mInfo.fMesh->fMorphSet->fMorphs.GetCount(); i++ ) |
|
{ |
|
// Apply Delta |
|
mInfo.fMesh->fMorphSet->fMorphs[i].Apply(dst, &mInfo.fArrayWeights[i].fDeltaWeights); |
|
} |
|
|
|
IRenormalize(dst); |
|
|
|
// Close up the access spans |
|
plAccessGeometry::Instance()->Close(dst); |
|
mInfo.fFlags &= ~plSharedMeshInfo::kInfoDirtyMesh; |
|
} |
|
|
|
hsBool plMorphSequence::IResetShared(int iShare) |
|
{ |
|
if( iShare >= fSharedMeshes.GetCount() || fSharedMeshes[iShare].fCurrDraw == nil) |
|
return false; |
|
|
|
plSharedMeshInfo& mInfo = fSharedMeshes[iShare]; |
|
|
|
// Now copy each shared mesh geometryspan into the drawable |
|
// to get it back to it's pristine condition. |
|
int i; |
|
for( i = 0; i < mInfo.fMesh->fSpans.GetCount(); i++ ) |
|
{ |
|
plAccessSpan srcAcc; |
|
plAccessGeometry::Instance()->AccessSpanFromGeometrySpan(srcAcc, mInfo.fMesh->fSpans[i]); |
|
plAccessSpan dstAcc; |
|
plAccessGeometry::Instance()->OpenRW(mInfo.fCurrDraw, mInfo.fCurrIdx[i], dstAcc); |
|
|
|
plAccPosNormUVWIterator srcIter(&srcAcc.AccessVtx()); |
|
plAccPosNormUVWIterator dstIter(&dstAcc.AccessVtx()); |
|
|
|
const int numUVWs = srcAcc.AccessVtx().NumUVWs(); |
|
|
|
for( srcIter.Begin(), dstIter.Begin(); srcIter.More(); srcIter.Advance(), dstIter.Advance() ) |
|
{ |
|
*dstIter.Position() = *srcIter.Position(); |
|
*dstIter.Normal() = *srcIter.Normal(); |
|
|
|
int j; |
|
for( j = 0; j < numUVWs; j++ ) |
|
*dstIter.UVW(j) = *srcIter.UVW(j); |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
hsBool plMorphSequence::IFindIndices(int iShare) |
|
{ |
|
plSharedMeshInfo& mInfo = fSharedMeshes[iShare]; |
|
mInfo.fCurrDraw = nil; // In case we fail. |
|
|
|
const plInstanceDrawInterface* di = plInstanceDrawInterface::ConvertNoRef(IGetDrawInterface()); |
|
if( !di ) |
|
return false; |
|
|
|
Int32 meshIdx = di->GetSharedMeshIndex(mInfo.fMesh); |
|
if( meshIdx < 0 ) |
|
return false; |
|
|
|
plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable((UInt8)meshIdx)); |
|
if( !dr ) |
|
return false; |
|
|
|
mInfo.fCurrDraw = dr; |
|
|
|
plDISpanIndex& diIndex = dr->GetDISpans(di->GetDrawableMeshIndex((UInt8)meshIdx)); |
|
|
|
hsAssert(mInfo.fMesh->fSpans.GetCount() == diIndex.GetCount(), "Mismatch between geometry and indices"); |
|
|
|
mInfo.fCurrIdx.SetCount(diIndex.GetCount()); |
|
|
|
int i; |
|
for( i = 0; i < diIndex.GetCount(); i++ ) |
|
mInfo.fCurrIdx[i] = diIndex[i]; |
|
|
|
return true; |
|
} |
|
|
|
void plMorphSequence::IReleaseIndices(int iShare) |
|
{ |
|
plSharedMeshInfo& mInfo = fSharedMeshes[iShare]; |
|
mInfo.fCurrDraw = nil; |
|
} |
|
|
|
Int32 plMorphSequence::IFindSharedMeshIndex(plKey meshKey) const |
|
{ |
|
int i; |
|
for( i = 0; i < fSharedMeshes.GetCount(); i++ ) |
|
{ |
|
if (fSharedMeshes[i].fMesh->GetKey() == meshKey) |
|
return i; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
Int32 plMorphSequence::IFindPendingStateIndex(plKey meshKey) const |
|
{ |
|
int i; |
|
for( i = 0; i < fPendingStates.GetCount(); i++ ) |
|
{ |
|
if (fPendingStates[i].fSharedMeshKey == meshKey) |
|
return i; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
hsBool plMorphSequence::IIsUsingDrawable(plDrawable *draw) |
|
{ |
|
int i; |
|
for (i = 0; i < fSharedMeshes.GetCount(); i++) |
|
{ |
|
if (fSharedMeshes[i].fCurrDraw == draw) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void plMorphSequence::AddSharedMesh(plSharedMesh* mesh) |
|
{ |
|
// This happens if SDL state tells us to use a mesh without morph info |
|
// (Either because the object used to but no longer does, or because |
|
// the SDL is giving us the wrong key.) |
|
if (!mesh->fMorphSet) |
|
return; |
|
|
|
if (fSharedMeshes.GetCount() == 0) |
|
plgDispatch::Dispatch()->RegisterForExactType(plSharedMeshBCMsg::Index(), GetKey()); |
|
|
|
if (IFindSharedMeshIndex(mesh->GetKey()) >= 0) |
|
return; // We already have it. |
|
|
|
hsAssert(fSharedMeshes.GetCount() < 127, "Too many meshes for one morph sequence."); |
|
SetUseSharedMesh(true); |
|
int pendingIndex = IFindPendingStateIndex(mesh->GetKey()); |
|
|
|
plSharedMeshInfo mInfo; |
|
mInfo.fMesh = mesh; |
|
mInfo.fCurrDraw = nil; |
|
|
|
// Intialize our weights to zero. |
|
mInfo.fArrayWeights.Reset(); |
|
mInfo.fArrayWeights.SetCount(mesh->fMorphSet->fMorphs.GetCount()); |
|
int i, j; |
|
for (i = 0; i < mesh->fMorphSet->fMorphs.GetCount(); i++) |
|
mInfo.fArrayWeights[i].fDeltaWeights.ExpandAndZero(mesh->fMorphSet->fMorphs[i].GetNumDeltas()); |
|
|
|
// Aha, we have some pending weights. Copy them in! |
|
if (pendingIndex >= 0) |
|
{ |
|
// Filter in any data that's valid |
|
for (i = 0; i < mInfo.fArrayWeights.GetCount() && i < fPendingStates[pendingIndex].fArrayWeights.GetCount(); i++) |
|
{ |
|
for (j = 0; j < mInfo.fArrayWeights[i].fDeltaWeights.GetCount() && |
|
j < fPendingStates[pendingIndex].fArrayWeights[i].fDeltaWeights.GetCount(); j++) |
|
{ |
|
mInfo.fArrayWeights[i].fDeltaWeights[j] = fPendingStates[pendingIndex].fArrayWeights[i].fDeltaWeights[j]; |
|
} |
|
} |
|
|
|
fPendingStates.Remove(pendingIndex); |
|
|
|
mInfo.fFlags |= plSharedMeshInfo::kInfoDirtyMesh; |
|
ISetDirty(true); |
|
} |
|
|
|
fSharedMeshes.Append(mInfo); |
|
IFindIndices(fSharedMeshes.GetCount() - 1); |
|
|
|
if (mesh->fFlags & plSharedMesh::kLayer0GlobalToMod) |
|
{ |
|
fGlobalLayerRef = fSharedMeshes.GetCount() - 1; |
|
ISetAllSharedToGlobal(); |
|
} |
|
else |
|
ISetSingleSharedToGlobal(fSharedMeshes.GetCount() - 1); |
|
} |
|
|
|
void plMorphSequence::RemoveSharedMesh(plSharedMesh* mesh) |
|
{ |
|
int idx = IFindSharedMeshIndex(mesh->GetKey()); |
|
if (idx < 0) |
|
return; // Don't have it... can't remove it can we? |
|
|
|
fSharedMeshes.Remove(idx); |
|
|
|
fGlobalLayerRef = -1; |
|
int i; |
|
for (i = 0; i < fSharedMeshes.GetCount(); i++) |
|
{ |
|
if (fSharedMeshes[i].fMesh->fFlags & plSharedMesh::kLayer0GlobalToMod) |
|
{ |
|
fGlobalLayerRef = i; |
|
break; |
|
} |
|
} |
|
|
|
if (fSharedMeshes.GetCount() == 0) |
|
plgDispatch::Dispatch()->UnRegisterForExactType(plSharedMeshBCMsg::Index(), GetKey()); |
|
} |
|
|
|
void plMorphSequence::FindMorphMods(const plSceneObject *so, hsTArray<const plMorphSequence*> &mods) |
|
{ |
|
const plMorphSequence *morph = plMorphSequence::ConvertNoRef(so->GetModifierByType(plMorphSequence::Index())); |
|
if (morph) |
|
mods.Append(morph); |
|
|
|
const plCoordinateInterface *ci = so->GetCoordinateInterface(); |
|
int i; |
|
for (i = 0; i < ci->GetNumChildren(); i++) |
|
FindMorphMods(ci->GetChild(i)->GetOwner(), mods); |
|
} |
|
|
|
void plMorphSequence::ISetAllSharedToGlobal() |
|
{ |
|
int i; |
|
for (i = 0; i < fSharedMeshes.GetCount(); i++) |
|
{ |
|
if (i != fGlobalLayerRef) |
|
ISetSingleSharedToGlobal(i); |
|
} |
|
} |
|
|
|
void plMorphSequence::ISetSingleSharedToGlobal(int idx) |
|
{ |
|
if (fGlobalLayerRef < 0) |
|
return; |
|
|
|
int i; |
|
for (i = 0; i < fSharedMeshes[fGlobalLayerRef].fArrayWeights[0].fDeltaWeights.GetCount(); i++) |
|
SetWeight(0, i, fSharedMeshes[fGlobalLayerRef].fArrayWeights[0].fDeltaWeights[i], fSharedMeshes[idx].fMesh->GetKey()); |
|
} |