/*==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 . 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==*/ ///////////////////////////////////////////////////////////////////////////////////////// // // INCLUDES // ///////////////////////////////////////////////////////////////////////////////////////// // havok (must be first) //#include //#include // singular #include "plMatrixChannel.h" // local #include "plQuatChannel.h" #include "plPointChannel.h" // global #include "hsResMgr.h" #include "plProfile.h" #include "hsTimer.h" // other #include "../pnSceneObject/plDrawInterface.h" #include "../pnSceneObject/plSimulationInterface.h" #include "../pnSceneObject/plCoordinateInterface.h" #include "../pnSceneObject/plAudioInterface.h" #include "../plInterp/plController.h" #include "../plInterp/plAnimTimeConvert.h" #include "../plInterp/hsInterp.h" #include "../plTransform/hsAffineParts.h" ///////////////////////////////////////////////////////////////////////////////////////// // // PROFILING GIBBLIES // ///////////////////////////////////////////////////////////////////////////////////////// plProfile_Extern(AffineValue); plProfile_Extern(AffineInterp); plProfile_Extern(AffineBlend); plProfile_Extern(AffineCompose); plProfile_Extern(MatrixApplicator); ///////////////////////////////////////////////////////////////////////////////////////// // // plMatrixChannel // ///////////////////////////////////////////////////////////////////////////////////////// // ctor -------------------------- // ----- plMatrixChannel::plMatrixChannel() : plAGChannel() { } // dtor --------------------------- // ----- plMatrixChannel::~plMatrixChannel() { } // Value -------------------------------------------------------- // ------ const hsMatrix44 & plMatrixChannel::Value(double time, bool peek) { return fResult; } // AffineValue ----------------------------------------------------------- // ------------ const hsAffineParts & plMatrixChannel::AffineValue(double time, bool peek) { return fAP; } // Value -------------------------------------------------------------- // ------ void plMatrixChannel::Value(hsMatrix44 &matrix, double time, bool peek) { matrix = Value(time); } // MakeCombine ----------------------------------------------- // ------------ plAGChannel * plMatrixChannel::MakeCombine(plAGChannel *other) { return nil; } // MakeBlend --------------------------------------------------- // ---------- plAGChannel * plMatrixChannel::MakeBlend(plAGChannel * channelB, plScalarChannel * channelBias, int blendPriority) { plMatrixChannel * matChanB = plMatrixChannel::ConvertNoRef(channelB); plAGChannel * result = this; // if the blend fails, we keep our position in the graph if (matChanB) { result = TRACKED_NEW plMatrixBlend(this, matChanB, channelBias, blendPriority); } return result; } // MakeZeroState ----------------------------- // -------------- plAGChannel * plMatrixChannel::MakeZeroState() { return TRACKED_NEW plMatrixConstant(Value(0)); } // MakeTimeScale -------------------------------------------------------- // -------------- plAGChannel * plMatrixChannel::MakeTimeScale(plScalarChannel *timeSource) { return TRACKED_NEW plMatrixTimeScale(this, timeSource); } // Dump ------------------------------------------- // ----- void plMatrixChannel::Dump(int indent, bool optimized, double time) { std::string indentStr; for(int i = 0; i < indent; i++) { indentStr += "- "; } hsStatusMessageF("%s matChan<%s>", indentStr.c_str(), fName); } ///////////////////////////////////////////////////////////////////////////////////////// // // plMatrixConstant // ///////////////////////////////////////////////////////////////////////////////////////// // ctor ---------------------------- // ----- plMatrixConstant::plMatrixConstant() : plMatrixChannel() { } plMatrixConstant::~plMatrixConstant() { } plMatrixConstant::plMatrixConstant(const hsMatrix44 &value) { Set(value); } void plMatrixConstant::Set(const hsMatrix44 &value) { fResult = value; gemAffineParts gemParts1; decomp_affine(value.fMap, &gemParts1); AP_SET(fAP, gemParts1); } void plMatrixConstant::Write(hsStream *stream, hsResMgr *mgr) { plMatrixChannel::Write(stream, mgr); fAP.Write(stream); } void plMatrixConstant::Read(hsStream *stream, hsResMgr *mgr) { plMatrixChannel::Read(stream, mgr); fAP.Read(stream); fAP.ComposeMatrix(&fResult); } ///////////////////////////////////////////////////////////////////////////////////////// // // plMatrixTimeScale // // Insert into the graph when you need to change the speed or direction of time // Also serves as a handy instancing node, since it just passes its data through. // ///////////////////////////////////////////////////////////////////////////////////////// // ctor ------------------------------ // ----- plMatrixTimeScale::plMatrixTimeScale() : plMatrixChannel(), fTimeSource(nil), fChannelIn(nil) { } // ctor ------------------------------------------------------------------------ // ----- plMatrixTimeScale::plMatrixTimeScale(plMatrixChannel *channel, plScalarChannel *timeSource) : fChannelIn(channel), fTimeSource(timeSource) { } // dtor ------------------------------- // ----- plMatrixTimeScale::~plMatrixTimeScale() { } // IsStoppedAt ---------------------------- // ------------ hsBool plMatrixTimeScale::IsStoppedAt(double time) { return fTimeSource->IsStoppedAt(time); } // Value ---------------------------------------------------------- // ------ const hsMatrix44 & plMatrixTimeScale::Value(double time, bool peek) { fResult = fChannelIn->Value(fTimeSource->Value(time, peek), peek); return fResult; } const hsAffineParts & plMatrixTimeScale::AffineValue(double time, bool peek) { fAP = fChannelIn->AffineValue(fTimeSource->Value(time, peek), peek); return fAP; } // Detach ---------------------------------------------------- // ------- plAGChannel * plMatrixTimeScale::Detach(plAGChannel * detach) { plAGChannel *result = this; // HAVE to recurse on the incoming channel in case there are cycles; // even if we're detaching this node it might also be further upstream fChannelIn = plMatrixChannel::ConvertNoRef(fChannelIn->Detach(detach)); // If you delete a timescale, it is not replaced with its upstream node; // it's just gone. if(!fChannelIn || detach == this) result = nil; if(result != this) delete this; return result; } // Dump --------------------------------------------- // ----- void plMatrixTimeScale::Dump(int indent, bool optimized, double time) { std::string indentStr; for(int i = 0; i < indent; i++) { indentStr += "- "; } hsStatusMessageF("%s matTimeScale <%s> at time <%f>", indentStr.c_str(), fName, fTimeSource->Value(time, true)); fChannelIn->Dump(indent + 1, optimized, time); } ///////////////////////////////////////////////////////////////////////////////////////// // // plMatrixBlend // ///////////////////////////////////////////////////////////////////////////////////////// // ctor ---------------------- // ----- plMatrixBlend::plMatrixBlend() : fChannelA(nil), fChannelB(nil), fChannelBias(nil) { } // ctor ---------------------------------------------------------------------------- // ----- plMatrixBlend::plMatrixBlend(plMatrixChannel * channelA, plMatrixChannel * channelB, plScalarChannel * channelBias, int priority) : fChannelA(channelA), fOptimizedA(channelA), fChannelB(channelB), fOptimizedB(channelB), fChannelBias(channelBias), fPriority(priority) { } // dtor ----------------------- // ----- plMatrixBlend::~plMatrixBlend() { fChannelA = nil; fChannelB = nil; fChannelBias = nil; } // MakeBlend -------------------------------------------------- // ---------- plAGChannel * plMatrixBlend::MakeBlend(plAGChannel *newChannel, plScalarChannel *channelBias, int blendPriority) { plMatrixChannel * newMatChan = plMatrixChannel::ConvertNoRef(newChannel); plAGChannel *result = this; int effectiveBlendPriority = (blendPriority == -1 ? fPriority : blendPriority); if(newMatChan) { if(effectiveBlendPriority >= fPriority) { // if the new channel has higher priority, just do it. result = plMatrixChannel::MakeBlend(newMatChan, channelBias, effectiveBlendPriority); } else { // we're higher priority: pass to our upstream channel fChannelA = plMatrixChannel::ConvertNoRef(fChannelA->MakeBlend(newChannel, channelBias, blendPriority)); hsAssert(fChannelA, "MakeBlend returned non-matrix channel."); // ask our upstream channel to do the blend: it can't be atop us // this request will get recursively delegated until the priorities work. } } return result; } UInt16 plMatrixBlend::GetPriority() { return fPriority; } hsBool plMatrixBlend::IsStoppedAt(double time) { hsScalar blend = fChannelBias->Value(time); if (blend == 0) return fChannelA->IsStoppedAt(time); if (blend == 1) return fChannelB->IsStoppedAt(time); return (fChannelA->IsStoppedAt(time) && fChannelB->IsStoppedAt(time)); } // Value ------------------------------------------------------ // ------ const hsMatrix44 & plMatrixBlend::Value(double time, bool peek) { const hsAffineParts &parts = AffineValue(time, peek); plProfile_BeginTiming(AffineCompose); parts.ComposeMatrix(&fResult); plProfile_EndTiming(AffineCompose); return fResult; } // AffineValue --------------------------------------------------------- // ------------ const hsAffineParts & plMatrixBlend::AffineValue(double time, bool peek) { const hsScalar &blend = fChannelBias->Value(time); if(blend == 0) { return fOptimizedA->AffineValue(time, peek); } else { if(blend == 1) { return fOptimizedB->AffineValue(time, peek); } else { const hsAffineParts &apA = fChannelA->AffineValue(time, peek); const hsAffineParts &apB = fChannelB->AffineValue(time, peek); plProfile_BeginTiming(AffineBlend); hsInterp::LinInterp(&apA, &apB, blend, &fAP); plProfile_EndTiming(AffineBlend); } } return fAP; } // Detach ---------------------------------------------- // ------- plAGChannel * plMatrixBlend::Detach(plAGChannel *remove) { plAGChannel *result = this; // it's possible that the incoming channel could reside down *all* of our // branches (it's a graph, not a tree,) so we always pass down all limbs fChannelBias = plScalarChannel::ConvertNoRef(fChannelBias->Detach(remove)); fChannelA = plMatrixChannel::ConvertNoRef(fChannelA->Detach(remove)); fChannelB = plMatrixChannel::ConvertNoRef(fChannelB->Detach(remove)); if(!fChannelBias) result = fChannelA; else if(fChannelA && !fChannelB) result = fChannelA; else if(fChannelB && !fChannelA) result = fChannelB; else if(!fChannelA && !fChannelB) result = nil; if(result != this) delete this; return result; } // Optimize ------------------------------------- // --------- plAGChannel *plMatrixBlend::Optimize(double time) { fOptimizedA = (plMatrixChannel *)fChannelA->Optimize(time); fOptimizedB = (plMatrixChannel *)fChannelB->Optimize(time); hsScalar blend = fChannelBias->Value(time); if(blend == 0.0f) return fOptimizedA; if(blend == 1.0f) return fOptimizedB; else return this; } // Dump ----------------------------------------- // ----- void plMatrixBlend::Dump(int indent, bool optimized, double time) { std::string indentStr; for(int i = 0; i < indent; i++) { indentStr += "- "; } hsStatusMessageF("%s matBlend<%s>, bias:<%f>", indentStr.c_str(), fName, fChannelBias->Value(time, true)); if(optimized) { fOptimizedB->Dump(indent + 1, optimized, time); fOptimizedA->Dump(indent + 1, optimized, time); } else { fChannelB->Dump(indent + 1, optimized, time); fChannelA->Dump(indent + 1, optimized, time); } } ///////////////////////////////////////////////////////////////////////////////////////// // // plMatrixControllerChannel // ///////////////////////////////////////////////////////////////////////////////////////// // ctor ---------------------------------------------- // ----- plMatrixControllerChannel::plMatrixControllerChannel() : plMatrixChannel(), fController(nil) { } // ctor --------------------------------------------------------------- // ----- plMatrixControllerChannel::plMatrixControllerChannel(plController *controller, hsAffineParts *parts) : fController(controller) { fAP = *parts; } // dtor ----------------------------------------------- // ----- plMatrixControllerChannel::~plMatrixControllerChannel() { if(fController) { delete fController; fController = nil; } } // Value ------------------------------------------------------------------ // ------ const hsMatrix44 & plMatrixControllerChannel::Value(double time, bool peek) { return Value(time, peek, nil); } // Value ------------------------------------------------------------------ // ------ const hsMatrix44 & plMatrixControllerChannel::Value(double time, bool peek, plControllerCacheInfo *cache) { plProfile_BeginTiming(AffineInterp); fController->Interp((hsScalar)time, &fAP, cache); plProfile_EndTiming(AffineInterp); plProfile_BeginTiming(AffineCompose); fAP.ComposeMatrix(&fResult); plProfile_EndTiming(AffineCompose); return fResult; } // AffineValue --------------------------------------------------------------------- // ------------ const hsAffineParts & plMatrixControllerChannel::AffineValue(double time, bool peek) { return AffineValue(time, peek, nil); } // AffineValue --------------------------------------------------------------------- // ------------ const hsAffineParts & plMatrixControllerChannel::AffineValue(double time, bool peek, plControllerCacheInfo *cache) { plProfile_BeginTiming(AffineInterp); fController->Interp((hsScalar)time, &fAP, cache); plProfile_EndTiming(AffineInterp); return fAP; } // MakeCacheChannel ------------------------------------------------------------ // ----------------- plAGChannel *plMatrixControllerChannel::MakeCacheChannel(plAnimTimeConvert *atc) { plControllerCacheInfo *cache = fController->CreateCache(); cache->SetATC(atc); return TRACKED_NEW plMatrixControllerCacheChannel(this, cache); } void plMatrixControllerChannel::Dump(int indent, bool optimized, double time) { std::string indentStr; for(int i = 0; i < indent; i++) { indentStr += "- "; } hsStatusMessageF("%s MatController<%s>", indentStr.c_str(), fName); } // Write ------------------------------------------------------------- // ------ void plMatrixControllerChannel::Write(hsStream *stream, hsResMgr *mgr) { plMatrixChannel::Write(stream, mgr); hsAssert(fController, "Trying to write plMatrixControllerChannel with nil controller. File will not be importable."); mgr->WriteCreatable(stream, fController); fAP.Write(stream); } // Read ------------------------------------------------------------- // ------ void plMatrixControllerChannel::Read(hsStream *stream, hsResMgr *mgr) { plMatrixChannel::Read(stream, mgr); fController = plController::ConvertNoRef(mgr->ReadCreatable(stream)); fAP.Read(stream); } ///////////////////////////////// // PLMATRIXCONTROLLERCACHECHANNEL ///////////////////////////////// // CTOR plMatrixControllerCacheChannel::plMatrixControllerCacheChannel() : plMatrixChannel(), fControllerChannel(nil), fCache(nil) { } // CTOR(name, controller) plMatrixControllerCacheChannel::plMatrixControllerCacheChannel(plMatrixControllerChannel *controller, plControllerCacheInfo *cache) : fControllerChannel(controller), fCache(cache) { } // ~DTOR() plMatrixControllerCacheChannel::~plMatrixControllerCacheChannel() { delete fCache; fControllerChannel = nil; } // VALUE(time) const hsMatrix44 & plMatrixControllerCacheChannel::Value(double time, bool peek) { return fControllerChannel->Value(time, peek, fCache); } const hsAffineParts & plMatrixControllerCacheChannel::AffineValue(double time, bool peek) { return fControllerChannel->AffineValue(time, peek, fCache); } // DETACH plAGChannel * plMatrixControllerCacheChannel::Detach(plAGChannel * detach) { plAGChannel *result = this; fControllerChannel = plMatrixControllerChannel::ConvertNoRef(fControllerChannel->Detach(detach)); if(detach == this) result = fControllerChannel; if(!fControllerChannel) result = nil; if(result != this) delete this; return result; } ///////////////////// // PLQUATPOINTCOMBINE ///////////////////// // CTOR plQuatPointCombine::plQuatPointCombine() : fQuatChannel(nil), fPointChannel(nil) { } // CTOR plQuatPointCombine::plQuatPointCombine(plQuatChannel *quatChannel, plPointChannel *pointChannel) : fQuatChannel(quatChannel), fPointChannel(pointChannel) { } // DTOR plQuatPointCombine::~plQuatPointCombine() { if(fQuatChannel) { //XXX delete fQuatChannel; fQuatChannel = nil; } if(fPointChannel) { //XXX delete fPointChannel; fPointChannel = nil; } } // VALUE(time) const hsMatrix44 & plQuatPointCombine::Value(double time) { if(fQuatChannel) { const hsQuat &quat = fQuatChannel->Value(time); quat.MakeMatrix(&fResult); } else { fResult.Reset(); } if(fPointChannel) { const hsPoint3 &point = fPointChannel->Value(time); fResult.SetTranslate(&point); } return fResult; } const hsAffineParts & plQuatPointCombine::AffineValue(double time) { // XXX Lame hack to get things to compile for now. // Will fix when we actually start using this channel type. gemAffineParts gemParts1; decomp_affine(Value(time).fMap, &gemParts1); AP_SET(fAP, gemParts1); return fAP; } // DETACH plAGChannel * plQuatPointCombine::Detach(plAGChannel *channel) { hsAssert(this != channel, "Can't detach combiners or blenders directly. Detach sub-channels instead."); if(this != channel) { // *** check the types on the replacement channels to make sure they're compatible fQuatChannel = (plQuatChannel *)fQuatChannel->Detach(channel); fPointChannel = (plPointChannel *)fPointChannel->Detach(channel); } return this; } /////////////////////////////////////////////////////////////////////////////////////////// // // PLMATRIXCHANNELAPPLICATOR // /////////////////////////////////////////////////////////////////////////////////////////// // IAPPLY void plMatrixChannelApplicator::IApply(const plAGModifier *mod, double time) { if(fChannel) { plMatrixChannel *matChan = plMatrixChannel::ConvertNoRef(fChannel); if(matChan) { hsMatrix44 inverse; plProfile_BeginTiming(AffineValue); const hsAffineParts &ap = matChan->AffineValue(time); plProfile_EndTiming(AffineValue); hsMatrix44 result; plProfile_BeginTiming(AffineCompose); ap.ComposeMatrix(&result); ap.ComposeInverseMatrix(&inverse); //result.GetInverse(&inverse); plProfile_EndTiming(AffineCompose); plProfile_BeginTiming(MatrixApplicator); plCoordinateInterface *CI = IGetCI(mod); CI->SetLocalToParent(result, inverse); plProfile_EndTiming(MatrixApplicator); } } } /////////////////////////////////////////////////////////////////////////////////////////// // // plMatrixDelayedCorrectionApplicator // /////////////////////////////////////////////////////////////////////////////////////////// const hsScalar plMatrixDelayedCorrectionApplicator::fDelayLength = 1.f; // seconds void plMatrixDelayedCorrectionApplicator::SetCorrection(hsMatrix44 &cor) { if (fIgnoreNextCorrection) { // We want the first correction we get from an avatar to be // instantaneous, otherwise they float over from (0, 0, 0). fIgnoreNextCorrection = false; return; } // decomp_affine seems to always give us the smaller angle quaternion, // which looks right visually when we interp. If certain cases become // visually annoying, we can check and adjust things here. gemAffineParts gemParts1; decomp_affine(cor.fMap, &gemParts1); AP_SET(fCorAP, gemParts1); fDelayStart = hsTimer::GetSysSeconds(); } // CANBLEND hsBool plMatrixDelayedCorrectionApplicator::CanBlend(plAGApplicator *app) { plMatrixChannelApplicator *matChannelApp = plMatrixChannelApplicator::ConvertNoRef(app); if( plMatrixChannelApplicator::ConvertNoRef(app) ) { return true; } return false; } // IAPPLY void plMatrixDelayedCorrectionApplicator::IApply(const plAGModifier *mod, double time) { if(fChannel) { if(fEnabled) { plMatrixChannel *matChan = plMatrixChannel::ConvertNoRef(fChannel); hsAssert(matChan, "Invalid channel given to plMatrixChannelApplicator"); plProfile_BeginTiming(MatrixApplicator); plCoordinateInterface *CI = IGetCI(mod); const hsMatrix44 &animResult = matChan->Value(time); hsMatrix44 localResult; hsMatrix44 localInverse; if (time < fDelayStart + fDelayLength) { hsAffineParts identAP; identAP.Reset(); hsAffineParts interpAP; hsMatrix44 interpResult; hsScalar blend = (hsScalar)((time - fDelayStart) / fDelayLength); hsInterp::LinInterp(&fCorAP, &identAP, blend, &interpAP); interpAP.ComposeMatrix(&interpResult); localResult = interpResult * animResult; localResult.GetInverse(&localInverse); CI->SetLocalToParent(localResult, localInverse); } else { animResult.GetInverse(&localInverse); CI->SetLocalToParent(animResult, localInverse); } plProfile_EndTiming(MatrixApplicator); } } } /////////////////////////////////////////////////////////////////////////////////////////// // // PLMATRIXDIFFERENCEAPPLICATOR // /////////////////////////////////////////////////////////////////////////////////////////// // Reset ------------------------------------- // ------ void plMatrixDifferenceApp::Reset(double time) { hsAssert(fChannel,"Missing input channel when resetting."); if(fChannel) { plMatrixChannel *matChan = plMatrixChannel::ConvertNoRef(fChannel); hsAssert(matChan, "Invalid channel given to plMatrixChannelApplicator"); if(matChan) { hsMatrix44 L2A, A2L; const hsAffineParts &ap = matChan->AffineValue(time); ap.ComposeMatrix(&L2A); // what comes out of AffineValue is a local-to-animation ap.ComposeInverseMatrix(&A2L); fLastA2L = A2L; fLastL2A = L2A; } } } // CanBlend ----------------------------------------------- // --------- hsBool plMatrixDifferenceApp::CanBlend(plAGApplicator *app) { plMatrixChannelApplicator *matChannelApp = plMatrixChannelApplicator::ConvertNoRef(app); if( plMatrixChannelApplicator::ConvertNoRef(app) ) { return true; } return false; } // *** move this somewhere real bool CompareMatrices2(const hsMatrix44 &matA, const hsMatrix44 &matB, float tolerance) { bool c00 = fabs(matA.fMap[0][0] - matB.fMap[0][0]) < tolerance; bool c01 = fabs(matA.fMap[0][1] - matB.fMap[0][1]) < tolerance; bool c02 = fabs(matA.fMap[0][2] - matB.fMap[0][2]) < tolerance; bool c03 = fabs(matA.fMap[0][3] - matB.fMap[0][3]) < tolerance; bool c10 = fabs(matA.fMap[1][0] - matB.fMap[1][0]) < tolerance; bool c11 = fabs(matA.fMap[1][1] - matB.fMap[1][1]) < tolerance; bool c12 = fabs(matA.fMap[1][2] - matB.fMap[1][2]) < tolerance; bool c13 = fabs(matA.fMap[1][3] - matB.fMap[1][3]) < tolerance; bool c20 = fabs(matA.fMap[2][0] - matB.fMap[2][0]) < tolerance; bool c21 = fabs(matA.fMap[2][1] - matB.fMap[2][1]) < tolerance; bool c22 = fabs(matA.fMap[2][2] - matB.fMap[2][2]) < tolerance; bool c23 = fabs(matA.fMap[2][3] - matB.fMap[2][3]) < tolerance; bool c30 = fabs(matA.fMap[3][0] - matB.fMap[3][0]) < tolerance; bool c31 = fabs(matA.fMap[3][1] - matB.fMap[3][1]) < tolerance; bool c32 = fabs(matA.fMap[3][2] - matB.fMap[3][2]) < tolerance; bool c33 = fabs(matA.fMap[3][3] - matB.fMap[3][3]) < tolerance; return c00 && c01 && c02 && c03 && c11 && c12 && c13 && c20 && c21 && c22 && c23 && c30 && c31 && c32 && c33; } // IAPPLY void plMatrixDifferenceApp::IApply(const plAGModifier *mod, double time) { plMatrixChannel *matChan = plMatrixChannel::ConvertNoRef(fChannel); hsAssert(matChan, "Invalid channel given to plMatrixChannelApplicator"); hsMatrix44 L2A, A2L; const hsAffineParts &ap = matChan->AffineValue(time); plProfile_BeginTiming(AffineCompose); ap.ComposeMatrix(&L2A); // what comes out of AffineValue is a local-to-animation ap.ComposeInverseMatrix(&A2L); plProfile_EndTiming(AffineCompose); plProfile_BeginTiming(MatrixApplicator); if(fNew) // if it's new, there's no previous frame to diff against; { // cache the current and don't do anything fLastA2L = A2L; fLastL2A = L2A; fNew = false; } else { if( ! CompareMatrices2(fLastA2L, A2L, .0001f) && ! CompareMatrices2(fLastL2A, L2A, .0001f)) { plCoordinateInterface *CI = IGetCI(mod); hsMatrix44 l2p = CI->GetLocalToParent(); hsMatrix44 p2l = CI->GetParentToLocal(); hsMatrix44 prev2Cur = fLastA2L * L2A; // previous to current in local space hsMatrix44 cur2Prev = A2L * fLastL2A; // current to previous in local space hsMatrix44 newL2P = l2p * prev2Cur; hsMatrix44 newP2L = cur2Prev * p2l; CI->SetLocalToParent(newL2P, newP2L); CI->FlushTransform(); fLastL2A = L2A; fLastA2L = A2L; } } plProfile_EndTiming(MatrixApplicator); } /////////////////////////////////////////////////////////////////////////////////////////// // // PLIK2APPLICATOR // /////////////////////////////////////////////////////////////////////////////////////////// /** A two-bone IK applicator. */ class plIK2Applicator : public plMatrixChannelApplicator { public: // The latest time we were asked to evaluate. We won't actually do the evaluation // until the other bone is asked as well. double GetUpdateTime(); void SetIsEndEffector(bool status); bool GetIsEndEffector(); void SetTarget(hsPoint3 &worldPoint); private: virtual void IApply(const plAGModifier *mod, double time); void ISolve(); // The other bone involved in the IK solution plIK2Applicator *fOtherBone; // The latest time we were asked to evaluate. We won't actually run our // process until the other guy is asked to evaluate the same time. double fUpdateTime; hsPoint3 fTarget; bool fIsEndEffector; }; // GetUpdateTime --------------- double plIK2Applicator::GetUpdateTime() { return fUpdateTime; } void plIK2Applicator::SetIsEndEffector(bool status) { fIsEndEffector = status; } bool plIK2Applicator::GetIsEndEffector() { return fIsEndEffector; } void plIK2Applicator::IApply(const plAGModifier *mod, double time) { fUpdateTime = time; if(time == fOtherBone->GetUpdateTime()) { // we're both up-to-date: go ahead and solve ISolve(); } } void plIK2Applicator::ISolve() { }