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.
1018 lines
28 KiB
1018 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/>. |
|
|
|
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 <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() |
|
{ |
|
|
|
} |