/*==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==*/ ///////////////////////////////////////////////////////////////////////////////////////// // // 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() { }