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.
 
 
 
 
 

1002 lines
28 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==*/
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
// havok (must be first)
//#include <hkmath/quaternion.h>
//#include <hkmath/quaternion.h>
// 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 ----------------------------
// ------------
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()
{
}