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.
2172 lines
64 KiB
2172 lines
64 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==*/ |
|
|
|
#include <functional> |
|
#include <math.h> |
|
#include <memory> |
|
|
|
#include "HeadSpin.h" |
|
#include "hsExceptionStack.h" |
|
#include "hsTemplates.h" |
|
#include "hsWindows.h" |
|
#include <commdlg.h> |
|
#include <cmath> |
|
#include <stdmat.h> |
|
#include <bmmlib.h> |
|
#include <istdplug.h> |
|
#include <iparamb2.h> |
|
#include <modstack.h> |
|
#include <keyreduc.h> |
|
#pragma hdrstop |
|
|
|
#include "MaxMain/plMaxNode.h" |
|
|
|
#include "hsMaxLayerBase.h" |
|
#include "plInterp/plAnimEaseTypes.h" |
|
#include "plInterp/plController.h" |
|
#include "plInterp/hsInterp.h" |
|
#include "MaxExport/plErrorMsg.h" |
|
#include "UserPropMgr.h" |
|
#include "hsConverterUtils.h" |
|
#include "hsControlConverter.h" |
|
#include "hsMaterialConverter.h" |
|
#include "MaxExport/plErrorMsg.h" |
|
#include "MaxComponent/plNoteTrackAnim.h" |
|
#include "MaxComponent/plCameraComponents.h" |
|
#include "MaxComponent/plAnimComponent.h" |
|
#include "pnSceneObject/plSceneObject.h" |
|
#include "pnSceneObject/plCoordinateInterface.h" |
|
|
|
typedef std::unique_ptr<IKey, std::function<void(IKey*)>> ikey_ptr; |
|
|
|
/** Allocates a managed buffer to store derivatives of IKey */ |
|
static ikey_ptr IAllocKey(size_t size) |
|
{ |
|
return ikey_ptr( |
|
reinterpret_cast<IKey*>(new uint8_t[size]), |
|
[](IKey* ptr) { delete[] reinterpret_cast<uint8_t*>(ptr); }); |
|
} |
|
|
|
////////////////////////////////////////////////////////////////////////// |
|
|
|
extern UserPropMgr gUserPropMgr; |
|
|
|
hsControlConverter& hsControlConverter::Instance() |
|
{ |
|
static hsControlConverter the_instance; |
|
|
|
return the_instance; |
|
} |
|
|
|
hsControlConverter::hsControlConverter() : fConverterUtils(hsConverterUtils::Instance()) |
|
{ |
|
} |
|
|
|
void hsControlConverter::Init(plErrorMsg* msg) |
|
{ |
|
hsGuardBegin("hsControlConverter::Init"); |
|
|
|
fInterface = GetCOREInterface(); |
|
fErrorMsg = msg; |
|
|
|
fTicksPerFrame = ::GetTicksPerFrame(); /*160*/ |
|
fFrameRate = ::GetFrameRate(); /*30*/ |
|
fTicksPerSec = fTicksPerFrame*fFrameRate; |
|
|
|
Interval interval = fInterface->GetAnimRange(); |
|
fStartFrame = interval.Start()/fTicksPerFrame; |
|
fEndFrame = interval.End()/fTicksPerFrame; |
|
fNumFrames = fEndFrame-fStartFrame+1; |
|
fAnimLength = (float)(fNumFrames-1)/fFrameRate; |
|
|
|
fWarned = false; |
|
|
|
fForceLocal = false; |
|
|
|
fSegStart = fSegEnd = -1; |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
void hsControlConverter::DeInit() |
|
{ |
|
} |
|
|
|
// dummy class that ApplyKeyReduction needs |
|
class KRStatus : public KeyReduceStatus |
|
{ |
|
void Init(int total) {} |
|
int Progress(int p) { return KEYREDUCE_CONTINUE; } |
|
}; |
|
|
|
void hsControlConverter::ReduceKeys(Control *control, float threshold) |
|
{ |
|
if (control == nil || threshold <= 0) |
|
return; |
|
|
|
KRStatus status; |
|
if (control->IsLeaf()) |
|
{ |
|
if (control->IsKeyable()) |
|
{ |
|
IKeyControl *keyCont = GetKeyControlInterface(control); |
|
if (keyCont->GetNumKeys() > 2) |
|
{ |
|
IKey *key1 = (IKey*)new uint8_t[keyCont->GetKeySize()]; |
|
IKey *key2 = (IKey*)new uint8_t[keyCont->GetKeySize()]; |
|
keyCont->GetKey(0, key1); |
|
keyCont->GetKey(keyCont->GetNumKeys() - 1, key2); |
|
|
|
// We want the interval to be one frame past the start and one frame |
|
// before the end, to guarantee we leave the first and last keys |
|
// alone. This will make sure a looping anim still lines up, and |
|
// also prevents us from removing the controller entirely and thinking |
|
// this channel just isn't animated at all. |
|
// |
|
// Also, I think this is a Max bug (since we're using Max's key reduce |
|
// function, and the same error happens without our plugins), but if |
|
// your range is only one frame short of the end of the anim, some |
|
// bones get flipped on that 2nd-to-last frame. So you get a single |
|
// frame with something like your arm pointing in the opposite |
|
// direction at the elbow. |
|
TimeValue start = key1->time + GetTicksPerFrame(); |
|
TimeValue end = key2->time - 2 * GetTicksPerFrame(); |
|
if (start < end) |
|
{ |
|
Interval interval(start, end); |
|
ApplyKeyReduction(control, interval, threshold, GetTicksPerFrame(), &status); |
|
} |
|
delete [] (uint8_t*)key1; |
|
delete [] (uint8_t*)key2; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
int i; |
|
for (i = 0; i < control->NumSubs(); i++) |
|
ReduceKeys((Control*)control->SubAnim(i), threshold); |
|
} |
|
} |
|
|
|
plController *hsControlConverter::ConvertTMAnim(plSceneObject *obj, plMaxNode *node, hsAffineParts *parts, |
|
float start /* = -1 */, float end /* = -1 */) |
|
{ |
|
Control* maxTm = node->GetTMController(); |
|
plController *tmc = hsControlConverter::Instance().MakeTransformController(maxTm, node, start, end); |
|
|
|
if (tmc) |
|
{ |
|
const plCoordinateInterface *ci = obj->GetCoordinateInterface(); |
|
if(ci) |
|
{ |
|
const hsMatrix44& loc2Par = ci->GetLocalToParent(); |
|
|
|
gemAffineParts ap; |
|
decomp_affine(loc2Par.fMap, &ap); |
|
|
|
AP_SET((*parts), ap); |
|
} |
|
} |
|
|
|
return tmc; |
|
} |
|
|
|
bool hsControlConverter::HasKeyTimes(Control* ctl) |
|
{ |
|
hsGuardBegin("hsControlConverter::HasKeyTimes"); |
|
|
|
if( !ctl ) |
|
{ |
|
return false; |
|
} |
|
|
|
return (ctl->NumKeys() > 1); |
|
hsGuardEnd; |
|
} |
|
|
|
|
|
plLeafController* hsControlConverter::MakeMatrix44Controller(StdUVGen* uvGen, const char* nodeName) |
|
{ |
|
hsGuardBegin("hsControlConverter::MakeMatrix44Controller"); |
|
|
|
if( !uvGen ) |
|
return nil; |
|
|
|
ISetSegRange(-1, -1); |
|
|
|
Tab<TimeValue> kTimes; |
|
kTimes.ZeroCount(); |
|
Control* uScaleCtl = nil; |
|
Control* vScaleCtl = nil; |
|
Control* uOffCtl= nil; |
|
Control* vOffCtl = nil; |
|
Control* rotCtl = nil; |
|
GetControllerByName(uvGen, TSTR("U Offset"), uOffCtl); |
|
GetControllerByName(uvGen, TSTR("V Offset"), vOffCtl); |
|
GetControllerByName(uvGen, TSTR("U Tiling"), uScaleCtl); |
|
GetControllerByName(uvGen, TSTR("V Tiling"), vScaleCtl); |
|
GetControllerByName(uvGen, TSTR("Angle"), rotCtl); |
|
|
|
// new with Max R2, replacing "Angle", but it doesn't hurt to look... |
|
Control* uAngCtl = nil; |
|
Control* vAngCtl = nil; |
|
Control* wAngCtl = nil; |
|
GetControllerByName(uvGen, TSTR("U Angle"), uAngCtl); |
|
GetControllerByName(uvGen, TSTR("V Angle"), vAngCtl); |
|
GetControllerByName(uvGen, TSTR("W Angle"), wAngCtl); |
|
|
|
int i; |
|
|
|
CompositeKeyTimes(uOffCtl, kTimes); |
|
CompositeKeyTimes(vOffCtl, kTimes); |
|
CompositeKeyTimes(uScaleCtl, kTimes); |
|
CompositeKeyTimes(vScaleCtl, kTimes); |
|
CompositeKeyTimes(rotCtl, kTimes); |
|
CompositeKeyTimes(uAngCtl, kTimes); |
|
CompositeKeyTimes(vAngCtl, kTimes); |
|
CompositeKeyTimes(wAngCtl, kTimes); |
|
|
|
const float kMaxRads = 30.f * M_PI / 180.f; |
|
MaxSampleAngles(nodeName, uAngCtl, kTimes, kMaxRads); |
|
MaxSampleAngles(nodeName, vAngCtl, kTimes, kMaxRads); |
|
MaxSampleAngles(nodeName, wAngCtl, kTimes, kMaxRads); |
|
|
|
if( kTimes.Count()<2 ) |
|
{ |
|
return nil; |
|
} |
|
|
|
plLeafController* ctrl = new plLeafController; |
|
ctrl->AllocKeys(kTimes.Count(), hsKeyFrame::kMatrix44KeyFrame); |
|
TimeValue resetTime = fConverterUtils.GetTime(fInterface); |
|
for( i=0; i < kTimes.Count(); i++) |
|
{ |
|
Interval v; |
|
uvGen->Update(kTimes[i], v); |
|
|
|
// Get key |
|
float secs = (float)kTimes[i]/fTicksPerSec; |
|
int frameNum= kTimes[i]/fTicksPerFrame; |
|
hsAssert(frameNum <= hsKeyFrame::kMaxFrameNumber, "Anim is too long."); |
|
|
|
fErrorMsg->Set((frameNum < fStartFrame || frameNum > fEndFrame), nodeName, |
|
"Warning: Skipping keyframes outside of animation interval").CheckAndAsk(); |
|
|
|
hsMatrix44Key *key = ctrl->GetMatrix44Key(i); |
|
StdUVGenToHsMatrix44(&key->fValue, uvGen, true); |
|
key->fFrame = frameNum; |
|
} |
|
|
|
return ctrl; |
|
hsGuardEnd; |
|
} |
|
|
|
plLeafController* hsControlConverter::MakeMatrix44Controller(Control* prsControl) |
|
{ |
|
hsGuardBegin("hsControlConverter::MakeMatrix44Controller"); |
|
|
|
ISetSegRange(-1, -1); |
|
|
|
Tab<TimeValue> kTimes; |
|
kTimes.ZeroCount(); |
|
|
|
Control* posCtl = nil; |
|
Control* scaleCtl = nil; |
|
Control* rotCtl = nil; |
|
posCtl = prsControl->GetPositionController(); |
|
rotCtl = prsControl->GetRotationController(); |
|
scaleCtl = prsControl->GetScaleController(); |
|
int i; |
|
|
|
CompositeKeyTimes(posCtl, kTimes); |
|
CompositeKeyTimes(scaleCtl, kTimes); |
|
CompositeKeyTimes(rotCtl, kTimes); |
|
|
|
if( kTimes.Count()<2 ) |
|
{ |
|
return nil; |
|
} |
|
|
|
plLeafController* ctrl = new plLeafController; |
|
ctrl->AllocKeys(kTimes.Count(), hsKeyFrame::kMatrix44KeyFrame); |
|
TimeValue resetTime = fConverterUtils.GetTime(fInterface);; |
|
for( i=0; i < kTimes.Count(); i++) |
|
{ |
|
// Get key |
|
float secs = (float)kTimes[i]/fTicksPerSec; |
|
int frameNum= kTimes[i]/fTicksPerFrame; |
|
hsAssert(frameNum <= hsKeyFrame::kMaxFrameNumber, "Anim is too long."); |
|
|
|
Matrix3 maxXform; |
|
maxXform.IdentityMatrix(); |
|
Interval valid = FOREVER; |
|
prsControl->GetValue(fConverterUtils.GetTime(fInterface), &maxXform, valid, CTRL_RELATIVE); |
|
|
|
hsMatrix44Key *key = ctrl->GetMatrix44Key(i); |
|
Matrix3ToHsMatrix44(&maxXform, &key->fValue); |
|
key->fFrame = frameNum; |
|
} |
|
|
|
return ctrl; |
|
hsGuardEnd; |
|
} |
|
|
|
|
|
// |
|
// Create a plScalarController and store the nodes parm behavior in it. |
|
// |
|
plLeafController* hsControlConverter::MakeScalarController(Control* control, plMaxNode* node, |
|
float start /* = -1 */, float end /* = -1 */) |
|
{ |
|
hsGuardBegin("hsControlConverter::MakeScalarController"); |
|
|
|
if (control == NULL) |
|
return NULL; |
|
|
|
ISetSegRange(start, end); |
|
|
|
return ICreateScalarController(node, control); |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
plController* hsControlConverter::MakeColorController(Control* control, plMaxNode* node, |
|
float start /* = -1 */, float end /* = -1 */) |
|
{ |
|
return MakePosController(control, node, start, end); |
|
} |
|
// |
|
// Create a plPosController and store the nodes parm behavior in it. |
|
// |
|
plController* hsControlConverter::MakePosController(Control* control, plMaxNode* node, |
|
float start /* = -1 */, float end /* = -1 */) |
|
{ |
|
hsGuardBegin("hsControlConverter::MakePosController"); |
|
|
|
if (control == NULL) |
|
return NULL; |
|
|
|
ISetSegRange(start, end); |
|
|
|
plController* hsCont; |
|
|
|
if (control->IsLeaf()) |
|
{ |
|
hsCont = ICreateSimplePosController(node, control); |
|
} |
|
else |
|
{ |
|
hsCont = new plCompoundController; |
|
fErrorMsg->Set(control->NumSubs()!=3, node->GetName(), "compound should have 3 subs").Check(); |
|
|
|
if (control->ClassID() == Class_ID(POSITIONNOISE_CONTROL_CLASS_ID,0) ) |
|
{ |
|
MessageBox(GetActiveWindow(), node->GetName(), |
|
"Warning: Noise position controller not supported. Ignoring.", MB_OK); |
|
return hsCont; |
|
} |
|
|
|
bool keep = false; |
|
for (int i=0; i<3; i++) |
|
{ |
|
Control* sub = (Control*)control->SubAnim(i); |
|
plLeafController* sc = ICreateScalarController(node, sub); |
|
((plCompoundController*)hsCont)->SetController(i, sc); |
|
if (sc) |
|
{ |
|
keep = true; |
|
} |
|
} |
|
if (!keep) |
|
{ |
|
delete hsCont; |
|
hsCont = nil; |
|
} |
|
} |
|
|
|
return hsCont; |
|
hsGuardEnd; |
|
} |
|
|
|
plController *hsControlConverter::MakeScaleController(Control *control, plMaxNode* node, |
|
float start /* = -1 */, float end /* = -1 */) |
|
{ |
|
ISetSegRange(start, end); |
|
|
|
if (control->IsLeaf()) |
|
{ |
|
// Simple scale: linear, bezier, tcb |
|
plLeafController* sc = ICreateSimpleScaleController(node, control); |
|
return sc; |
|
} |
|
else |
|
{ |
|
// compound scale: noise |
|
if (control->ClassID() == Class_ID(SCALENOISE_CONTROL_CLASS_ID,0) ) |
|
{ |
|
MessageBox(GetActiveWindow(), node->GetName(), |
|
"Warning: Noise scale controller not supported. Ignoring.", MB_OK); |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
plController *hsControlConverter::MakeRotController(Control *control, plMaxNode *node, bool camRot /* = false */, |
|
float start /* = -1 */, float end /* = -1 */) |
|
{ |
|
ISetSegRange(start, end); |
|
|
|
if (control->IsLeaf()) |
|
{ |
|
// simple rot: linear, smooth, tcb |
|
plLeafController* rc = ICreateSimpleRotController(node, control, camRot); |
|
return rc; |
|
} |
|
else |
|
{ |
|
// compound rot: euler or noise |
|
if (control->NumSubs()) |
|
{ |
|
if (control->ClassID() == Class_ID(ROTATIONNOISE_CONTROL_CLASS_ID,0) ) |
|
{ |
|
MessageBox(GetActiveWindow(), node->GetName(), |
|
"Warning: Noise rotation controller not supported. Ignoring.", MB_OK); |
|
return nil; |
|
} |
|
if (fErrorMsg->Set(control->ClassID() != Class_ID(EULER_CONTROL_CLASS_ID,0), |
|
node->GetName(), "Expecting euler rot ctrler").CheckAndAsk()) |
|
return nil; |
|
|
|
if (fErrorMsg->Set(control->NumSubs() != 3, node->GetName(), "Rot compound controller should have 3 subcontrollers").CheckAndAsk()) |
|
return nil; |
|
|
|
plCompoundController* rc = new plCompoundController; |
|
int i; |
|
for (i=0; i<3; i++) |
|
{ |
|
Control* sub = (Control*)control->SubAnim(i); |
|
plLeafController* sc = ICreateScalarController(node, sub); |
|
rc->SetController(i, sc); |
|
} |
|
|
|
// |
|
// Check if we need to fixup euler due to missing subcontrollers |
|
// |
|
int numRotConts; |
|
for(numRotConts=0,i=0; i<3; i++) |
|
if (rc->GetController(i)) |
|
numRotConts++; |
|
if (numRotConts>0 && numRotConts<3) |
|
{ |
|
// Someone has deleted 1 or 2 of the subcontrollers |
|
// Add a key at the start and end of the missing tracks |
|
Interval interval = fInterface->GetAnimRange(); |
|
TimeValue startTime = interval.Start(); // in ticks |
|
TimeValue endTime = interval.End(); // in ticks |
|
|
|
hsStatusMessage("Fixing up euler controller due to missing subcontrollers\n"); |
|
for(i=0; i<3; i++) |
|
{ |
|
if (!rc->GetController(i)) |
|
{ |
|
Control* sub = (Control*)control->SubAnim(i); |
|
if (!sub) |
|
continue; |
|
sub->AddNewKey(startTime, ADDKEY_INTERP); |
|
sub->AddNewKey(endTime, ADDKEY_INTERP); |
|
plLeafController* sc = ICreateScalarController(node, sub); |
|
if (sc) |
|
rc->SetController(i, sc); |
|
else |
|
{ |
|
fErrorMsg->Set(true, "Scalar Controller Error", "nil plScalar controller").Show(); |
|
fErrorMsg->Set(); |
|
return rc; |
|
} |
|
|
|
} |
|
} |
|
for(numRotConts=0,i=0; i<3; i++) |
|
if (rc->GetController(i)) |
|
numRotConts++; |
|
|
|
if(numRotConts != 3) |
|
{ |
|
fErrorMsg->Set(true, "Euler Fixup Error", "Euler fixup failed.").Show(); |
|
fErrorMsg->Set(); |
|
return rc; |
|
} |
|
} |
|
else if (numRotConts == 0) // No sub controllers, no point in having the compound controller then |
|
{ |
|
delete rc; |
|
rc = nil; |
|
} |
|
|
|
return rc; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
void hsControlConverter::ScalePositionController(plController* ctl, float scale) |
|
{ |
|
plLeafController* simp = plLeafController::ConvertNoRef(ctl); |
|
plCompoundController* comp; |
|
int i; |
|
if( simp ) |
|
{ |
|
for( i = 0; i < simp->GetNumKeys(); i++ ) |
|
{ |
|
hsPoint3Key* key = simp->GetPoint3Key(i); |
|
if (key) |
|
{ |
|
key->fValue *= scale; |
|
} |
|
|
|
hsBezPoint3Key* bezKey = simp->GetBezPoint3Key(i); |
|
if (bezKey) |
|
{ |
|
bezKey->fInTan *= scale; |
|
bezKey->fOutTan *= scale; |
|
bezKey->fValue *= scale; |
|
} |
|
} |
|
} |
|
else if( comp = plCompoundController::ConvertNoRef(ctl) ) |
|
{ |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
ScalePositionController(comp->GetController(i), scale); |
|
} |
|
} |
|
} |
|
|
|
void hsControlConverter::MaxSampleAngles(const char* nodeName, Control* ctl, Tab<TimeValue>& kTimes, float maxRads) |
|
{ |
|
hsGuardBegin("hsControlConverter::MaxSampleAngles"); |
|
|
|
if( !ctl ) |
|
{ |
|
return; |
|
} |
|
|
|
Tab<TimeValue> rTimes; |
|
Tab<TimeValue> sTimes; |
|
rTimes.ZeroCount(); |
|
IGetControlSampleTimes(ctl, 0, ctl->NumKeys(), rTimes, maxRads); |
|
|
|
int iR; |
|
for( iR = 0; iR < rTimes.Count(); iR++ ) |
|
{ |
|
int iK; |
|
for( iK = 0; iK < kTimes.Count(); iK++ ) |
|
{ |
|
if( kTimes[iK] >= rTimes[iR] ) |
|
break; |
|
} |
|
if( kTimes[iK] != rTimes[iR] ) |
|
kTimes.Insert(iK, 1, rTimes.Addr(iR)); |
|
} |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
plCompoundController *hsControlConverter::MakeTransformController(Control *control, plMaxNode *node, |
|
float start /* = -1 */, float end /* = -1 */) |
|
{ |
|
hsGuardBegin("hsControlConverter::MakeTransformController"); |
|
|
|
if (!control) |
|
return NULL; |
|
|
|
ISetSegRange(start, end); |
|
|
|
Class_ID cid = control->ClassID(); |
|
if (cid == Class_ID(PRS_CONTROL_CLASS_ID,0) || |
|
cid == Class_ID(LOOKAT_CONTROL_CLASS_ID,0)) |
|
{ |
|
int n = control->NumSubs(); |
|
if(n != 3) |
|
{ |
|
fErrorMsg->Set(true, "Transform Controller Error", "Transform controller doesn't have 3 sub controllers").Show(); |
|
fErrorMsg->Set(); |
|
return NULL; |
|
} |
|
|
|
plCompoundController *tmc = new plCompoundController; |
|
for (int i=0; i<n; i++) |
|
{ |
|
Control* sub = (Control*)control->SubAnim(i); |
|
if (sub) |
|
{ |
|
IConvertSubTransform(sub, control->SubAnimName(i), node, tmc, start, end); |
|
} |
|
} |
|
|
|
if (cid == Class_ID(LOOKAT_CONTROL_CLASS_ID,0)) |
|
{ |
|
hsTArray<hsG3DSMaxKeyFrame> kfArray; |
|
IAddPartsKeys(control, &kfArray, node); |
|
bool ignoreFOV = false; |
|
for (int i = 0; i < node->NumAttachedComponents(); i++) |
|
{ |
|
if (node->GetAttachedComponent(i)->ClassID() == ANIMCAM_CMD_CID) |
|
{ |
|
plCameraAnimCmdComponent* pAnimComp = (plCameraAnimCmdComponent*)node->GetAttachedComponent(i); |
|
ignoreFOV = pAnimComp->IgnoreFOV(); |
|
break; |
|
} |
|
} |
|
if (!ignoreFOV) |
|
IExportAnimatedCameraFOV(node, &kfArray); |
|
} |
|
|
|
if (tmc->GetPosController() || tmc->GetRotController() || tmc->GetScaleController()) |
|
return tmc; |
|
else |
|
{ |
|
delete tmc; |
|
return NULL; |
|
} |
|
} |
|
return NULL; |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
void hsControlConverter::ISetSegRange(float start, float end) |
|
{ |
|
fSegStart = (start >= 0 ? fTicksPerSec * start : fInterface->GetAnimRange().Start()); |
|
fSegEnd = (end >= 0 ? fTicksPerSec * end : fInterface->GetAnimRange().End()); |
|
} |
|
|
|
|
|
void hsControlConverter::IConvertSubTransform(Control *control, char *ctlName, plMaxNode *node, plCompoundController *tmc, |
|
float start, float end) |
|
{ |
|
if (control) |
|
{ |
|
ControllerType ct = IGetControlType(ctlName); |
|
|
|
switch(ct) |
|
{ |
|
case ctrlTypePosition: |
|
{ |
|
if(tmc->GetPosController() != nil) |
|
{ |
|
fErrorMsg->Set(true, "Position Controller Error", "Non-nil position controller").Show(); |
|
fErrorMsg->Set(); |
|
return; |
|
} |
|
tmc->SetPosController(MakePosController(control, node, start, end)); |
|
} |
|
break; |
|
case ctrlTypeRollAngle: |
|
case ctrlTypeRotation: |
|
{ |
|
if(tmc->GetRotController() != nil) |
|
{ |
|
fErrorMsg->Set(true, "Position Controller Error", "Non-nil Rotation controller").Show(); |
|
fErrorMsg->Set(); |
|
return; |
|
} |
|
bool camRot = (ct == ctrlTypeRollAngle); |
|
tmc->SetRotController(MakeRotController(control, node, camRot, start, end)); |
|
} |
|
break; |
|
case ctrlTypeScale: |
|
{ |
|
if(tmc->GetScaleController() != nil) |
|
{ |
|
fErrorMsg->Set(true, "Scale Controller Error", "Non-nil Scale Controller").Show(); |
|
fErrorMsg->Set(); |
|
return; |
|
} |
|
tmc->SetScaleController(MakeScaleController(control, node, start, end)); |
|
} |
|
break; |
|
default: |
|
/* |
|
if (plExp.GetLogFile()) |
|
fprintf(plExp.GetLogFile(),"%s unknown ctrl type=%d\n", node->GetName(), (int)ct); |
|
*/ |
|
break; |
|
} |
|
} |
|
} |
|
|
|
// |
|
// |
|
// |
|
plLeafController* hsControlConverter::ICreateSimpleRotController(plMaxNode* node, Control* control, bool camRot) |
|
{ |
|
hsGuardBegin("hsControlConverter::ICreateSimpleRotController"); |
|
|
|
return ICreateQuatController(node, control, true, camRot); |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
plLeafController* hsControlConverter::ICreateSimpleScaleController(plMaxNode* node, Control* control) |
|
{ |
|
hsGuardBegin("hsControlConverter::ICreateSimpleScaleController"); |
|
|
|
return ICreateScaleValueController(node, control); |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
|
|
// |
|
// |
|
// |
|
plLeafController* hsControlConverter::ICreateQuatController(plMaxNode* node, Control* control, bool rotation, bool camRot) |
|
{ |
|
hsGuardBegin("hsControlConverter::ICreateQuatController"); |
|
|
|
int32_t startIdx, endIdx; |
|
IKeyControl* ikeys = GetKeyControlInterface(control); |
|
if ( ikeys && IGetRangeCoverKeyIndices(node ? node->GetName() : nil, control, startIdx, endIdx)>1 ) |
|
{ |
|
if(!(control->IsKeyable())) |
|
{ |
|
fErrorMsg->Set(true, "Quat Controller Creation Error", "Control is not keyable.").Show(); |
|
fErrorMsg->Set(); |
|
return NULL; |
|
} |
|
|
|
ikey_ptr key = IAllocKey(ikeys->GetKeySize()); |
|
plLeafController* pc = new plLeafController; |
|
|
|
uint8_t compressLevel = node->GetAnimCompress(); |
|
uint8_t keyType; |
|
if (compressLevel == plAnimCompressComp::kCompressionHigh) |
|
keyType = hsKeyFrame::kCompressedQuatKeyFrame32; |
|
else if (compressLevel == plAnimCompressComp::kCompressionLow) |
|
keyType = hsKeyFrame::kCompressedQuatKeyFrame64; |
|
else |
|
keyType = hsKeyFrame::kQuatKeyFrame; |
|
|
|
pc->AllocKeys(endIdx - startIdx + 1, keyType); |
|
for(int i = startIdx; i <= endIdx; i++) |
|
{ |
|
// Get key |
|
ikeys->GetKey(i, key.get()); |
|
const float kMaxRads = M_PI* 0.5f; |
|
Tab<TimeValue> kTimes; |
|
kTimes.ZeroCount(); |
|
if( rotation ) |
|
IGetControlSampleTimes(control, i, i, kTimes, kMaxRads); |
|
else |
|
kTimes.Append(1, &key->time); |
|
|
|
int k; |
|
for( k = 0; k < kTimes.Count(); k++ ) |
|
{ |
|
if (keyType == hsKeyFrame::kQuatKeyFrame) |
|
{ |
|
hsQuatKey *hsKey = pc->GetQuatKey(i - startIdx); |
|
ICreateHSInterpKey(control, key.get(), kTimes[k], hsKey, node, camRot); |
|
} |
|
else if (keyType == hsKeyFrame::kCompressedQuatKeyFrame64) |
|
{ |
|
hsQuatKey tempKey; |
|
ICreateHSInterpKey(control, key.get(), kTimes[k], &tempKey, node, camRot); |
|
hsCompressedQuatKey64 *compKey = pc->GetCompressedQuatKey64(i - startIdx); |
|
compKey->fFrame = tempKey.fFrame; |
|
compKey->SetQuat(tempKey.fValue); |
|
} |
|
else |
|
{ |
|
hsQuatKey tempKey; |
|
ICreateHSInterpKey(control, key.get(), kTimes[k], &tempKey, node, camRot); |
|
hsCompressedQuatKey32 *compKey = pc->GetCompressedQuatKey32(i - startIdx); |
|
compKey->fFrame = tempKey.fFrame; |
|
compKey->SetQuat(tempKey.fValue); |
|
} |
|
} |
|
} |
|
|
|
return pc; |
|
} |
|
|
|
return nil; |
|
hsGuardEnd; |
|
} |
|
|
|
|
|
// |
|
// |
|
// |
|
plLeafController* hsControlConverter::ICreateScaleValueController(plMaxNode* node, Control* control) |
|
{ |
|
hsGuardBegin("hsControlConverter::ICreateScaleValueController"); |
|
|
|
//plMaxNode* xformParent = GetXformParent(node); |
|
int32_t startIdx, endIdx; |
|
IKeyControl* ikeys = GetKeyControlInterface(control); |
|
if ( ikeys && IGetRangeCoverKeyIndices(node ? node->GetName() : nil, control, startIdx, endIdx)>1 ) |
|
{ |
|
if(!(control->IsKeyable())) |
|
{ |
|
fErrorMsg->Set(true, "Scale Value Controller Creation Error", "Control is not keyable").Show(); |
|
fErrorMsg->Set(); |
|
return NULL; |
|
} |
|
|
|
ikey_ptr key = IAllocKey(ikeys->GetKeySize()); |
|
plLeafController* pc = new plLeafController; |
|
pc->AllocKeys(endIdx - startIdx + 1, GetKeyType(control)); |
|
for(int i = startIdx; i <= endIdx; i++) |
|
{ |
|
// Get key |
|
ikeys->GetKey(i, key.get()); |
|
hsScaleKey *hsKey = pc->GetScaleKey(i - startIdx); |
|
if (hsKey) |
|
ICreateHSInterpKey(control, key.get(), key->time, hsKey, node); |
|
|
|
hsBezScaleKey *bezKey = pc->GetBezScaleKey(i - startIdx); |
|
if (bezKey) |
|
ICreateHSInterpKey(control, key.get(), key->time, bezKey, node); |
|
} |
|
return pc; |
|
} |
|
|
|
return nil; |
|
hsGuardEnd; |
|
} |
|
|
|
// |
|
// |
|
// |
|
plLeafController* hsControlConverter::ICreateScalarController(plMaxNode* node, Control* control) |
|
{ |
|
hsGuardBegin("hsControlConverter::ICreateScalarController"); |
|
|
|
int32_t startIdx, endIdx; |
|
IKeyControl* ikeys = GetKeyControlInterface(control); |
|
if ( ikeys && IGetRangeCoverKeyIndices(node ? node->GetName() : nil, control, startIdx, endIdx)>1 ) |
|
{ |
|
if(!(control->IsKeyable())) |
|
{ |
|
fErrorMsg->Set(true, "Scale Value Controller Creation Error", "Control is not keyable").Show(); |
|
fErrorMsg->Set(); |
|
return NULL; |
|
} |
|
|
|
ikey_ptr key = IAllocKey(ikeys->GetKeySize()); |
|
plLeafController* pc = new plLeafController; |
|
pc->AllocKeys(endIdx - startIdx + 1, GetKeyType(control)); |
|
for(int i = startIdx; i <= endIdx; i++) |
|
{ |
|
// Get key |
|
ikeys->GetKey(i, key.get()); |
|
hsScalarKey *hsKey = pc->GetScalarKey(i - startIdx); |
|
if (hsKey) |
|
ICreateHSInterpKey(control, key.get(), key->time, hsKey); |
|
|
|
hsBezScalarKey *bezKey = pc->GetBezScalarKey(i - startIdx); |
|
if (bezKey) |
|
ICreateHSInterpKey(control, key.get(), key->time, bezKey); |
|
} |
|
|
|
return pc; |
|
} |
|
return nil; |
|
hsGuardEnd; |
|
} |
|
|
|
|
|
// |
|
// |
|
// |
|
plLeafController* hsControlConverter::ICreateSimplePosController(plMaxNode* node, Control* control) |
|
{ |
|
hsGuardBegin("hsControlConverter::ICreateSimplePosController"); |
|
|
|
IKeyControl* ikeys = GetKeyControlInterface(control); |
|
int32_t startIdx, endIdx; |
|
if ( ikeys && IGetRangeCoverKeyIndices(node ? node->GetName() : nil, control, startIdx, endIdx)>1 ) |
|
{ |
|
if(!(control->IsKeyable())) |
|
{ |
|
fErrorMsg->Set(true, "Simple Position Controller Creation Error", "Control is not keyable").Show(); |
|
fErrorMsg->Set(); |
|
return NULL; |
|
} |
|
|
|
ikey_ptr key = IAllocKey(ikeys->GetKeySize()); |
|
plLeafController* pc = new plLeafController; |
|
pc->AllocKeys(endIdx - startIdx + 1, GetKeyType(control)); |
|
for(int i = startIdx; i <= endIdx; i++) |
|
{ |
|
// Get key |
|
ikeys->GetKey(i, key.get()); |
|
hsPoint3Key *hsKey = pc->GetPoint3Key(i - startIdx); |
|
if (hsKey) |
|
ICreateHSInterpKey(control, key.get(), key->time, hsKey); |
|
|
|
hsBezPoint3Key *bezKey = pc->GetBezPoint3Key(i - startIdx); |
|
if (bezKey) |
|
ICreateHSInterpKey(control, key.get(), key->time, bezKey); |
|
} |
|
return pc; |
|
} |
|
|
|
return nil; |
|
hsGuardEnd; |
|
} |
|
|
|
// |
|
// Create a hsKey and store the nodes LTM in it. |
|
// Recurses along all subcontrollers. |
|
// |
|
int hsControlConverter::IAddPartsKeys(Control* control, |
|
hsTArray <hsG3DSMaxKeyFrame>* kfArray, |
|
plMaxNode* node) |
|
{ |
|
hsGuardBegin("hsControlConverter::IAddPartsKeys"); |
|
|
|
int32_t startIdx, endIdx; |
|
if (control->IsLeaf()) |
|
{ |
|
IKeyControl* ikeys = GetKeyControlInterface(control); |
|
int num = ikeys ? IGetRangeCoverKeyIndices(node ? node->GetName() : nil, control, startIdx, endIdx) : 0; |
|
|
|
if (num<2) |
|
{ |
|
return 0; |
|
} |
|
|
|
if(!control->IsKeyable()) |
|
{ |
|
fErrorMsg->Set(true, "Add Parts Keys Creation Error", "Control is not keyable").Show(); |
|
fErrorMsg->Set(); |
|
return 0; |
|
} |
|
|
|
int i,j; |
|
|
|
// |
|
// Traverse all keys of controller |
|
// |
|
ikey_ptr key = IAllocKey(ikeys->GetKeySize()); |
|
bool mb=false; |
|
plMaxNode* xformParent = GetXformParent(node); |
|
for(i = startIdx; i <= endIdx; i++) |
|
{ |
|
// Get key |
|
ikeys->GetKey(i, key.get()); |
|
float frameTime = key->time / GetTicksPerSec(); |
|
int frameNum = key->time / GetTicksPerFrame(); |
|
hsAssert(frameNum <= hsKeyFrame::kMaxFrameNumber, "Anim is too long."); |
|
|
|
// Check if we already have a hsG3dsMaxKey at this frameNum |
|
int found=FALSE; |
|
for(j=0; j<kfArray->GetCount(); j++) |
|
{ |
|
hsG3DSMaxKeyFrame* k = &(*kfArray)[j]; |
|
if (k->fFrame == frameNum) |
|
{ |
|
found = TRUE; |
|
break; |
|
} |
|
} |
|
if (found==TRUE) |
|
// Skip this key, there's one already there |
|
continue; |
|
|
|
// |
|
// Compute AffineParts |
|
// |
|
hsMatrix44 tXform = node->GetLocalToParent44(key->time); |
|
|
|
gemAffineParts ap; |
|
decomp_affine(tXform.fMap, &ap); |
|
hsAffineParts parts; |
|
AP_SET(parts, ap); |
|
|
|
// Init new keyframe |
|
hsG3DSMaxKeyFrame hKey; |
|
hKey.fParts = parts; |
|
hKey.fFrame = frameNum; |
|
|
|
// Add key to list |
|
kfArray->Append(hKey); |
|
} |
|
} |
|
else |
|
{ |
|
int i; |
|
for (i = 0; i < control->NumSubs(); i++) |
|
IAddPartsKeys((Control *)control->SubAnim(i), kfArray, node); |
|
} |
|
|
|
return kfArray->GetCount(); |
|
hsGuardEnd; |
|
} |
|
|
|
Matrix3 hsControlConverter::StdUVGenToMatrix3(StdUVGen* uvGen) |
|
{ |
|
Matrix3 retVal(true); |
|
if( uvGen ) |
|
uvGen->GetUVTransform(retVal); |
|
|
|
retVal = Inverse(IFlipY()) * retVal * IFlipY(); |
|
|
|
return retVal; |
|
} |
|
|
|
// |
|
// |
|
// returns 0 if identity, 1 otherwise |
|
// takes into account the implicit transform of v -> 1-v in meshconvert:setuvs() |
|
// |
|
bool hsControlConverter::StdUVGenToHsMatrix44(hsMatrix44* hsMat, StdUVGen* uvGen, bool preserveOffset) |
|
{ |
|
hsGuardBegin("hsControlConverter::StdUVGenToHsMatrix44"); |
|
|
|
Matrix3 uvXform; |
|
uvGen->GetUVTransform(uvXform); |
|
|
|
uvXform = Inverse(IFlipY()) * uvXform * IFlipY(); |
|
Matrix3ToHsMatrix44(&uvXform, hsMat); |
|
|
|
if( !preserveOffset ) |
|
{ |
|
int i; |
|
for( i = 0; i < 2; i++ ) |
|
{ |
|
if( fabsf(hsMat->fMap[i][3]) > 1.f ) |
|
hsMat->fMap[i][3] -= float(int(hsMat->fMap[i][3])); |
|
} |
|
} |
|
|
|
return ( !hsMat->IsIdentity() ); |
|
hsGuardEnd; |
|
} |
|
|
|
void hsControlConverter::IGetControlSampleTimes(Control* control, int iLo, int iHi, Tab<TimeValue>& kTimes, float maxRads) |
|
{ |
|
hsGuardBegin("hsControlConverter::IGetControlSampleTimes"); |
|
|
|
kTimes.ZeroCount(); |
|
|
|
if( !control ) |
|
{ |
|
return; |
|
} |
|
|
|
Class_ID cID = control->ClassID(); |
|
SClass_ID sID = control->SuperClassID(); |
|
|
|
if( iLo < 0 ) |
|
iLo = 0; |
|
int num = control->NumKeys(); |
|
iHi++; |
|
if( iHi > num ) |
|
iHi = num; |
|
|
|
|
|
IKeyControl* ikeys = GetKeyControlInterface(control); |
|
ikey_ptr key = IAllocKey(ikeys->GetKeySize()); |
|
ikey_ptr lastKey = IAllocKey(ikeys->GetKeySize()); |
|
|
|
int i; |
|
for( i = iLo; i < iHi; i++ ) |
|
{ |
|
TimeValue t = control->GetKeyTime(i); |
|
|
|
if( !i ) |
|
{ |
|
kTimes.Append(1, &t); |
|
continue; |
|
} |
|
int nSamp = 1; |
|
float rads = 0; |
|
// following code will work, except that rotations are stored |
|
// relative to previous key, so we'd need to end off with something |
|
// like for i = 1; i < n; i++ ) |
|
// key[i] = key[i-1] * key[i] |
|
// or pass in the previous key and do it here. |
|
/////////////////////////////////////// |
|
ikeys->GetKey(i-1, lastKey.get()); |
|
ikeys->GetKey(i, key.get()); |
|
if( cID == Class_ID(TCBINTERP_ROTATION_CLASS_ID, 0) ) |
|
{ |
|
ITCBRotKey* tcbRotKey = (ITCBRotKey*)key.get(); |
|
rads = tcbRotKey->val.angle; |
|
} |
|
else |
|
if( cID == Class_ID(LININTERP_ROTATION_CLASS_ID, 0) ) |
|
{ |
|
ILinRotKey* linRotKey = (ILinRotKey*)key.get(); |
|
|
|
Point3 axis; |
|
AngAxisFromQ(linRotKey->val, &rads, axis); |
|
} |
|
else |
|
if( cID == Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0) ) |
|
{ |
|
IBezQuatKey* bezRotKey = (IBezQuatKey*)key.get(); |
|
|
|
Point3 axis; |
|
AngAxisFromQ(bezRotKey->val, &rads, axis); |
|
} |
|
else |
|
if( cID == Class_ID(TCBINTERP_FLOAT_CLASS_ID, 0) ) |
|
{ |
|
ITCBFloatKey* fKey = (ITCBFloatKey*)key.get(); |
|
|
|
rads = fKey->val; |
|
fKey = (ITCBFloatKey*)lastKey.get(); |
|
rads -= fKey->val; |
|
} |
|
else |
|
if( cID == Class_ID(LININTERP_FLOAT_CLASS_ID, 0) ) |
|
{ |
|
ILinFloatKey* fKey = (ILinFloatKey*)key.get(); |
|
rads = fKey->val; |
|
fKey = (ILinFloatKey*)lastKey.get(); |
|
rads -= fKey->val; |
|
} |
|
else |
|
if( cID == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID, 0) ) |
|
{ |
|
IBezFloatKey* fKey = (IBezFloatKey*)key.get(); |
|
rads = fKey->val; |
|
fKey = (IBezFloatKey*)lastKey.get(); |
|
rads -= fKey->val; |
|
} |
|
|
|
nSamp = int(fabs(rads / maxRads) + 0.9f); |
|
if( nSamp < 2 ) |
|
{ |
|
kTimes.Append(1, &t); |
|
continue; |
|
} |
|
|
|
|
|
TimeValue t0 = control->GetKeyTime(i-1); |
|
int j; |
|
for( j = 0; j < nSamp; j++ ) |
|
{ |
|
float p = float(j+1) / float(nSamp); |
|
TimeValue ti = t0 + TimeValue(p* (t - t0)); |
|
kTimes.Append(1, &ti); |
|
} |
|
/////////////////////////////////////// |
|
} |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
#if 0 |
|
// following code will work, except that TCB (but not Euler) rotations are stored |
|
// relative to previous key, so we'd need to end off with something |
|
// like for i = 1; i < n; i++ ) |
|
// key[i] = key[i-1]* key[i] |
|
// or pass in the previous key and do it here. |
|
Quat quat; |
|
/////////////////////////////////////// |
|
if( cID == Class_ID(TCBINTERP_ROTATION_CLASS_ID, 0) ) |
|
{ |
|
ITCBRotKey* tcbRotKey = (ITCBRotKey*)mKey; |
|
quat = QFromAngAxis(tcbRotKey->val.angle, tcbRotKey->val.axis); |
|
} |
|
else if( cID == Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0) ) |
|
{ |
|
IBezQuatKey* bezRotKey = (IBezQuatKey*)mKey; |
|
quat = bezRotKey->val; |
|
} |
|
else if( cID == Class_ID(LININTERP_ROTATION_CLASS_ID, 0) ) |
|
{ |
|
ILinRotKey* linRotKey = (ILinRotKey*)mKey; |
|
quat = linRotKey->val; |
|
} |
|
else if( cID == Class_ID(EULER_CONTROL_CLASS_ID, 0) ) |
|
{ |
|
float eul[3]; |
|
|
|
int i; |
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
Control* subCntl = (Control*)control->SubAnim(i); |
|
if( fErrorMsg->Set(!(subCntl && (subCntl->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID, 0))), node->GetName(), "Bad sub-controller type for animation").CheckAndAsk() ) |
|
{ |
|
eul[i] = 0.f; |
|
continue; |
|
} |
|
|
|
ITCBFloatKey* fKey = (ITCBFloatKey*)mKey; |
|
eul[i] = fKey->val; |
|
} |
|
|
|
EulerToQuat(eul, quat); |
|
} |
|
hbKey->fValue.Set(-quat.x, -quat.y, -quat.z, quat.w); |
|
|
|
/////////////////////////////////////// |
|
#endif // try getting from key |
|
|
|
// |
|
// Create an hsKeyFrame from a 3DSMax key |
|
// |
|
int32_t hsControlConverter::ICreateHSInterpKey(Control* control, IKey* mKey, TimeValue keyTime, hsKeyFrame* baseKey, plMaxNode* node, bool rotQuat) |
|
{ |
|
hsGuardBegin("hsControlConverter::ICreateHSInterpKey"); |
|
|
|
Class_ID cID = control->ClassID(); |
|
SClass_ID sID = control->SuperClassID(); |
|
char* nodeName = node ? node->GetName() : nil; |
|
|
|
// BEZ |
|
if (cID == Class_ID(HYBRIDINTERP_POSITION_CLASS_ID,0) || |
|
cID == Class_ID(HYBRIDINTERP_COLOR_CLASS_ID,0) || |
|
cID == Class_ID(HYBRIDINTERP_POINT3_CLASS_ID,0) ) |
|
{ |
|
IBezPoint3Key*bKey = (IBezPoint3Key*)mKey; |
|
hsBezPoint3Key* hbKey = (hsBezPoint3Key*)baseKey; |
|
hbKey->fValue.Set(bKey->val.x, bKey->val.y, bKey->val.z); // color should be 0 to 1 |
|
hbKey->fInTan.Set(bKey->intan.x, bKey->intan.y, bKey->intan.z); |
|
hbKey->fOutTan.Set(bKey->outtan.x, bKey->outtan.y, bKey->outtan.z); |
|
} |
|
else if (cID == Class_ID(HYBRIDINTERP_SCALE_CLASS_ID,0)) |
|
{ |
|
IBezScaleKey*bKey = (IBezScaleKey*)mKey; |
|
hsBezScaleKey* hbKey = (hsBezScaleKey*)baseKey; |
|
hsMatrix44 tXform; |
|
IGetUnEasedLocalTM(node, control, &tXform, keyTime); |
|
gemAffineParts ap; |
|
decomp_affine(tXform.fMap, &ap); |
|
|
|
hbKey->fValue.fS.Set(ap.k.x, ap.k.y, ap.k.z); |
|
hbKey->fValue.fQ.Set(ap.u.x, ap.u.y, ap.u.z, ap.u.w); |
|
hbKey->fInTan.Set(bKey->intan.x, bKey->intan.y, bKey->intan.z); |
|
hbKey->fOutTan.Set(bKey->outtan.x, bKey->outtan.y, bKey->outtan.z); |
|
} |
|
|
|
else if (cID == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0) && !rotQuat) |
|
{ |
|
IBezFloatKey* bKey = (IBezFloatKey*)mKey; |
|
hsBezScalarKey* hbKey = (hsBezScalarKey*)baseKey; |
|
hbKey->fValue = bKey->val; |
|
hbKey->fInTan = bKey->intan; |
|
hbKey->fOutTan= bKey->outtan; |
|
} |
|
|
|
else |
|
// LIN |
|
if (cID == Class_ID(LININTERP_POSITION_CLASS_ID,0)) |
|
{ |
|
ILinPoint3Key*bKey = (ILinPoint3Key*)mKey; |
|
hsPoint3Key* hbKey = (hsPoint3Key*)baseKey; |
|
hbKey->fValue.Set(bKey->val.x, bKey->val.y, bKey->val.z); |
|
} |
|
else if (sID == SClass_ID(CTRL_ROTATION_CLASS_ID) || (cID == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0) && rotQuat)) // all rotations |
|
{ |
|
hsQuatKey* hbKey = (hsQuatKey*)baseKey; |
|
|
|
// get rot values from Matrix and use quat slerp. |
|
// could try getting rot values from key |
|
hsMatrix44 tXform; |
|
IGetUnEasedLocalTM(node, control, &tXform, keyTime); |
|
gemAffineParts ap; |
|
decomp_affine(tXform.fMap, &ap); |
|
|
|
hbKey->fValue.Set(ap.q.x, ap.q.y, ap.q.z, ap.q.w); |
|
|
|
IEnableEaseCurves(control, true); // re-enable |
|
} |
|
else if (cID == Class_ID(LININTERP_SCALE_CLASS_ID,0) ) |
|
{ |
|
ILinScaleKey*bKey = (ILinScaleKey*)mKey; |
|
hsScaleKey* hbKey = (hsScaleKey*)baseKey; |
|
hsMatrix44 tXform; |
|
IGetUnEasedLocalTM(node, control, &tXform, keyTime); |
|
gemAffineParts ap; |
|
decomp_affine(tXform.fMap, &ap); |
|
|
|
hbKey->fValue.fS.Set(ap.k.x, ap.k.y, ap.k.z); |
|
hbKey->fValue.fQ.Set(ap.u.x, ap.u.y, ap.u.z, ap.u.w); |
|
} |
|
else |
|
if (cID == Class_ID(LININTERP_FLOAT_CLASS_ID,0) ) |
|
{ |
|
ILinFloatKey* bKey = (ILinFloatKey*)mKey; |
|
hsScalarKey* hbKey = (hsScalarKey*)baseKey; |
|
hbKey->fValue = bKey->val; |
|
} |
|
else |
|
// TCB |
|
if (cID == Class_ID(TCBINTERP_POSITION_CLASS_ID,0) || |
|
cID == Class_ID(TCBINTERP_POINT3_CLASS_ID, 0) ) |
|
{ |
|
ITCBPoint3Key*bKey = (ITCBPoint3Key*)mKey; |
|
hsPoint3Key* hbKey = (hsPoint3Key*)baseKey; |
|
hbKey->fValue.Set(bKey->val.x, bKey->val.y, bKey->val.z); |
|
} |
|
else |
|
if (cID == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0) ) |
|
{ |
|
ITCBFloatKey* bKey = (ITCBFloatKey*)mKey; |
|
hsScalarKey* hbKey = (hsScalarKey*)baseKey; |
|
hbKey->fValue = bKey->val; |
|
} |
|
else if (cID == Class_ID(TCBINTERP_SCALE_CLASS_ID,0) ) |
|
{ |
|
ITCBScaleKey*bKey = (ITCBScaleKey*)mKey; |
|
hsScaleKey* hbKey = (hsScaleKey*)baseKey; |
|
hsMatrix44 tXform; |
|
IGetUnEasedLocalTM(node, control, &tXform, keyTime); |
|
gemAffineParts ap; |
|
decomp_affine(tXform.fMap, &ap); |
|
|
|
hbKey->fValue.fS.Set(ap.k.x, ap.k.y, ap.k.z); |
|
hbKey->fValue.fQ.Set(ap.u.x, ap.u.y, ap.u.z, ap.u.w); |
|
} |
|
else |
|
{ |
|
fErrorMsg->Set(true, nodeName, "unknown controller type?").Check(); |
|
return 0; // failed |
|
} |
|
|
|
int frameNum = keyTime / GetTicksPerFrame(); |
|
hsAssert(frameNum <= hsKeyFrame::kMaxFrameNumber, "Anim is too long."); |
|
baseKey->fFrame = frameNum; |
|
|
|
return 1; // did it |
|
hsGuardEnd; |
|
} |
|
|
|
uint8_t hsControlConverter::GetKeyType(Control* control, bool rotQuat) |
|
{ |
|
Class_ID cID = control->ClassID(); |
|
SClass_ID sID = control->SuperClassID(); |
|
|
|
if (cID == Class_ID(HYBRIDINTERP_POSITION_CLASS_ID,0) || |
|
cID == Class_ID(HYBRIDINTERP_COLOR_CLASS_ID,0) || |
|
cID == Class_ID(HYBRIDINTERP_POINT3_CLASS_ID,0) ) |
|
{ |
|
return hsKeyFrame::kBezPoint3KeyFrame; |
|
} |
|
else if (cID == Class_ID(HYBRIDINTERP_SCALE_CLASS_ID,0)) |
|
{ |
|
return hsKeyFrame::kBezScaleKeyFrame; |
|
} |
|
else if (cID == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0) && !rotQuat) |
|
{ |
|
return hsKeyFrame::kBezScalarKeyFrame; |
|
} |
|
else if (cID == Class_ID(LININTERP_POSITION_CLASS_ID,0)) |
|
{ |
|
return hsKeyFrame::kPoint3KeyFrame; |
|
} |
|
else if (sID == SClass_ID(CTRL_ROTATION_CLASS_ID) || (cID == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0) && rotQuat)) // all rotations |
|
{ |
|
return hsKeyFrame::kQuatKeyFrame; |
|
} |
|
else if (cID == Class_ID(LININTERP_SCALE_CLASS_ID,0) ) |
|
{ |
|
return hsKeyFrame::kScaleKeyFrame; |
|
} |
|
else if (cID == Class_ID(LININTERP_FLOAT_CLASS_ID,0) ) |
|
{ |
|
return hsKeyFrame::kScalarKeyFrame; |
|
} |
|
else if (cID == Class_ID(TCBINTERP_POSITION_CLASS_ID,0) || |
|
cID == Class_ID(TCBINTERP_POINT3_CLASS_ID, 0)) |
|
{ |
|
return hsKeyFrame::kPoint3KeyFrame; |
|
} |
|
else if (cID == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0) ) |
|
{ |
|
return hsKeyFrame::kScalarKeyFrame; |
|
} |
|
else if (cID == Class_ID(TCBINTERP_SCALE_CLASS_ID,0) ) |
|
{ |
|
return hsKeyFrame::kScaleKeyFrame; |
|
} |
|
else |
|
{ |
|
return hsKeyFrame::kUnknownKeyFrame; |
|
} |
|
} |
|
|
|
// |
|
// |
|
// |
|
int32_t hsControlConverter::IGetRangeCoverKeyIndices(char* nodeName, Control* cont, int32_t &start, int32_t &end) |
|
{ |
|
hsGuardBegin("hsControlConverter::IGetRangeCoverKeyIndices"); |
|
|
|
if (!cont) |
|
{ |
|
return 0; |
|
} |
|
|
|
IKeyControl* keys = GetKeyControlInterface(cont); |
|
int numKeys=keys->GetNumKeys(); |
|
if (numKeys == 0) |
|
return 0; |
|
|
|
ikey_ptr key = IAllocKey(keys->GetKeySize()); |
|
|
|
start = numKeys; |
|
for (int i=0; i<numKeys; i++) |
|
{ |
|
keys->GetKey(i, key.get()); |
|
if (IIsKeyInRange(key.get())) |
|
{ |
|
if (start > i) |
|
start = i; |
|
end = i; |
|
} |
|
} |
|
|
|
// If the keys aren't on the exact endpoints of our range, we need to include the next or previous |
|
// one so that we have data for any time within our range. |
|
|
|
if (start == numKeys) // No keys inside the range |
|
{ |
|
for (int i = 0; i < numKeys; i++) |
|
{ |
|
keys->GetKey(i, key.get()); |
|
if (key->time < fSegStart) |
|
start = i; |
|
} |
|
|
|
if ((start == numKeys) || // no keys before the start time |
|
(start == numKeys - 1)) // no keys after end (since the latest key is before start) |
|
{ |
|
return 0; |
|
} |
|
|
|
end = start + 1; |
|
} |
|
else |
|
{ |
|
keys->GetKey(start, key.get()); |
|
if (key->time > fSegStart && start > 0) |
|
start -= 1; |
|
|
|
keys->GetKey(end, key.get()); |
|
if (key->time < fSegEnd && end < numKeys - 1) |
|
end += 1; |
|
} |
|
|
|
//fErrorMsg->Set(numInRange>1 && numInRange!=numKeys, nodeName ? nodeName : "?", |
|
// "Warning: Object has controller with keyframes outside of animation interval").CheckAndAsk(); |
|
|
|
|
|
return end - start + 1; |
|
hsGuardEnd; |
|
} |
|
|
|
// |
|
// find the closest ancestor (if any) that is animated. |
|
// this node's space will be our local space. |
|
// |
|
plMaxNode* hsControlConverter::GetXformParent(plMaxNode* node) |
|
{ |
|
hsGuardBegin("hsControlConverter::GetXformParent"); |
|
|
|
while( node && (node = (plMaxNode *)node->GetParentNode()) && |
|
!(ForceOrigin(node) || ForceLocal(node) || IsAnimated(node)) ); |
|
|
|
return node; |
|
hsGuardEnd; |
|
} |
|
|
|
// ########################################################################### |
|
// Note that ForceWorldSpace Overrides ForceOrigin which Overrides ForceLocal |
|
// ########################################################################### |
|
bool hsControlConverter::ForceWorldSpace(plMaxNode* node) |
|
{ |
|
hsGuardBegin("hsControlConverter::ForceWorldSpace"); |
|
|
|
return false; |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
// ########################################################################### |
|
// Note that ForceWorldSpace Overrides ForceOrigin which Overrides ForceLocal |
|
// ########################################################################### |
|
bool hsControlConverter::ForceOrigin(plMaxNode* node) |
|
{ |
|
hsGuardBegin("hsControlConverter::ForceOrigin"); |
|
|
|
char* nn = node->GetName(); |
|
|
|
if (node->IsRootNode()) |
|
{ |
|
return false; |
|
} |
|
|
|
if (ForceWorldSpace(node)) |
|
{ |
|
return false; |
|
} |
|
|
|
return false; |
|
hsGuardEnd; |
|
} |
|
|
|
// ########################################################################### |
|
// Note that ForceWorldSpace Overrides ForceOrigin which Overrides ForceLocal |
|
// This is significant because things that require ForceLocal because they are |
|
// animated or what-not, are still okay with ForceOrigin, but not v.v. |
|
// ########################################################################### |
|
bool hsControlConverter::ForceLocal(plMaxNode* node) |
|
{ |
|
hsGuardBegin("hsControlConverter::ForceLocal"); |
|
|
|
|
|
const char* nn = node->GetName(); |
|
|
|
if( !node->CanConvert() ) |
|
return false; |
|
|
|
if (node->IsRootNode()) |
|
{ |
|
return false; |
|
} |
|
|
|
if( node->GetForceLocal() ) |
|
return true; |
|
|
|
if( ISkinNode((plMaxNode*)node->GetParentNode()) ) |
|
{ |
|
node->SetForceLocal(true); |
|
return true; |
|
} |
|
|
|
Object* objectRef = node->GetObjectRef(); |
|
if (fConverterUtils.IsInstanced(objectRef) && |
|
gUserPropMgr.UserPropExists(node,"AllowInstancing")) |
|
{ |
|
node->SetForceLocal(true); |
|
return true; |
|
} |
|
|
|
|
|
return false; |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
|
|
bool hsControlConverter::IsAnimated(plMaxNode* node) |
|
{ |
|
hsGuardBegin("hsControlConverter::IsAnimated"); |
|
|
|
return node->IsAnimated(); |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
bool hsControlConverter::OwnsMaterialCopy(plMaxNode* node) |
|
{ |
|
hsGuardBegin("hsControlConverter::OwnsMaterialCopy"); |
|
|
|
return false; |
|
hsGuardEnd; |
|
} |
|
|
|
bool hsControlConverter::HasFrameEvents(plMaxNode *node) |
|
{ |
|
hsGuardBegin("hsSceneConverter::HasFrameEvents"); |
|
|
|
if (!node) |
|
{ |
|
return false; |
|
} |
|
|
|
TSTR sdata; |
|
if (gUserPropMgr.GetUserPropString(node,"FESound",sdata) || |
|
gUserPropMgr.GetUserPropString(node,"FESoundEmitter",sdata) || |
|
gUserPropMgr.GetUserPropString(node,"FEGrab",sdata) || |
|
gUserPropMgr.GetUserPropString(node,"FEDrop",sdata) || |
|
gUserPropMgr.GetUserPropString(node,"FEEventOn",sdata) || |
|
gUserPropMgr.GetUserPropString(node,"FEEventOnPermanent",sdata) || |
|
gUserPropMgr.GetUserPropString(node,"FEEventOff",sdata) || |
|
gUserPropMgr.GetUserPropString(node,"FEActor",sdata)) |
|
{ |
|
return false; |
|
} |
|
|
|
return false; |
|
hsGuardEnd; |
|
} |
|
|
|
bool hsControlConverter::GetControllerByName(Animatable* anim, TSTR &name, Control* &ctl) |
|
{ |
|
hsGuardBegin("hsControlConverter::GetControllerByName"); |
|
|
|
if( anim ) |
|
{ |
|
int nSub = anim->NumSubs(); |
|
int i; |
|
for( i = 0; i < nSub; i++ ) |
|
{ |
|
if (anim->SubAnim(i)==nil) |
|
continue; |
|
TSTR subName = anim->SubAnimName(i); |
|
if( subName == name ) |
|
{ |
|
fErrorMsg->Set(!anim->SubAnim(i), name, "Found controller by name, but nobody home").Check(); |
|
ctl = GetControlInterface(anim->SubAnim(i)); |
|
return true; |
|
} |
|
else if( GetControllerByName(anim->SubAnim(i), name, ctl) ) |
|
{ |
|
return true; |
|
} |
|
} |
|
} |
|
ctl = nil; |
|
|
|
return false; |
|
hsGuardEnd; |
|
} |
|
|
|
Control *hsControlConverter::GetControllerByID(IParamBlock2 *pblock, int paramID) |
|
{ |
|
hsGuardBegin("hsControlConverter::GetControllerByID"); |
|
|
|
if (pblock) |
|
{ |
|
int animIdx = pblock->GetAnimNum(paramID); |
|
if (animIdx != -1) |
|
{ |
|
Animatable* anim = pblock->SubAnim(animIdx); |
|
if (anim) |
|
return GetControlInterface(anim); |
|
} |
|
} |
|
|
|
return NULL; |
|
hsGuardEnd; |
|
} |
|
|
|
void hsControlConverter::CompositeKeyTimes(Control* ctl, Tab<TimeValue> &time) |
|
{ |
|
hsGuardBegin("hsControlConverter::CompositeKeyTimes"); |
|
|
|
if( !ctl ) |
|
{ |
|
return; |
|
} |
|
|
|
int curTime = 0; |
|
int i; |
|
for( i = 0; i < ctl->NumKeys(); i++ ) |
|
{ |
|
TimeValue t = ctl->GetKeyTime(i); |
|
// advance times |
|
while( (curTime < time.Count())&&(t > time[curTime]) ) |
|
curTime++; |
|
// if past end, append it |
|
if( curTime >= time.Count() ) |
|
time.Append(1, &t); |
|
else // if less |
|
if( t < time[curTime] ) |
|
time.Insert(curTime++, 1, &t); |
|
// already there, skip |
|
} |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
// |
|
// |
|
// |
|
ControllerType hsControlConverter::IGetControlType(TSTR ctrlName) |
|
{ |
|
hsGuardBegin("hsControlConverter::IGetControlType"); |
|
|
|
ControllerType ct = ctrlTypeUnknown; |
|
if (ctrlName && !strcmp(ctrlName, "Ease Curve")) |
|
{ |
|
ct = ctrlTypeEase; |
|
} |
|
else if (ctrlName && !strcmp(ctrlName, "Mult Curve")) |
|
{ |
|
ct = ctrlTypeMult; |
|
} |
|
else if (ctrlName && !strcmp(ctrlName, "Position")) |
|
{ |
|
ct = ctrlTypePosition; |
|
} |
|
else if (ctrlName && !strcmp(ctrlName, "Rotation")) |
|
{ |
|
ct = ctrlTypeRotation; |
|
} |
|
else if (ctrlName && !strcmp(ctrlName, "Scale")) |
|
{ |
|
ct = ctrlTypeScale; |
|
} |
|
else if (ctrlName && !strcmp(ctrlName, "Transform")) |
|
{ |
|
ct = ctrlTypeTransform; |
|
} |
|
else if (ctrlName && !strcmp(ctrlName, "Roll Angle")) |
|
{ |
|
ct = ctrlTypeRollAngle; |
|
} |
|
#if 0 |
|
// biped controllers are good for nothing |
|
else if (ctrlName && !strcmp(ctrlName, "Vertical")) |
|
{ |
|
ct = ctrlTypeVert; |
|
} |
|
else if (ctrlName && !strcmp(ctrlName, "Horizontal")) |
|
{ |
|
ct = ctrlTypeHoriz; |
|
} |
|
else if (ctrlName && !strcmp(ctrlName, "Turning")) |
|
{ |
|
ct = ctrlTypeTurn; |
|
} |
|
#endif |
|
|
|
return ct; |
|
hsGuardEnd; |
|
} |
|
|
|
bool hsControlConverter::IIsKeyTimeInRange(TimeValue time) |
|
{ |
|
hsGuardBegin("hsControlConverter::IIsKeyTimeInRange"); |
|
|
|
Interval interval = fInterface->GetAnimRange(); |
|
TimeValue startTime = interval.Start(); // in ticks |
|
TimeValue endTime = interval.End(); // in ticks |
|
|
|
|
|
return (time >= startTime && time <= endTime) && // Max's range |
|
(time >= fSegStart && time <= fSegEnd); |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
bool hsControlConverter::IIsKeyInRange(IKey* key) |
|
{ |
|
hsGuardBegin("hsControlConverter::IIsKeyInRange"); |
|
|
|
return IIsKeyTimeInRange(key->time); |
|
hsGuardEnd; |
|
} |
|
|
|
void hsControlConverter::IGetUnEasedLocalTM(plMaxNode* node, Control* control, hsMatrix44* out, TimeValue time) |
|
{ |
|
hsGuardBegin("hsControlConverter::IGetUnEasedLocalTM"); |
|
|
|
// disable easing so that GetTM won't give us an eased answer. |
|
// we want the uneased "key" value, so that we can do the easing ourselves |
|
IEnableEaseCurves(control, false); |
|
|
|
// Make scale key match nodeTM |
|
fErrorMsg->Set(!node, "ICreateHSInterpKey", "nil node").Check(); |
|
*out = node->GetLocalToParent44(time); |
|
|
|
IEnableEaseCurves(control, true); // re-enable |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
// |
|
// |
|
// |
|
void hsControlConverter::IEnableEaseCurves(Animatable* control, bool enable) |
|
{ |
|
hsGuardBegin("hsControlConverter::IEnableEaseCurves"); |
|
|
|
if (control) |
|
{ |
|
int n = control->NumSubs(); |
|
for (int i=0; i<n; i++) |
|
IEnableEaseCurves(control->SubAnim(i), enable); |
|
|
|
EaseCurveList* el = GetEaseListInterface(control); |
|
if (el) |
|
{ |
|
for(int i=0; i<el->NumEaseCurves(); i++) |
|
{ |
|
if (enable) |
|
el->EnableEaseCurve(i); |
|
else |
|
el->DisableEaseCurve(i); |
|
} |
|
} |
|
} |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
// We don't actually use this ID for a plugin, just to keep track of our AppData chunks |
|
#define CONTROL_CONVERTER_CID Class_ID(0xae807d2, 0x523808c7) |
|
|
|
Matrix3 hsControlConverter::IFlipY() |
|
{ |
|
hsGuardBegin("hsControlConverter::IFlipY"); |
|
|
|
Matrix3 xfm = ScaleMatrix(Point3(1.0, -1.0, 1.0)) * TransMatrix(Point3(0.0, 1.0, 0.0)); |
|
|
|
return xfm; |
|
hsGuardEnd; |
|
} |
|
|
|
bool hsControlConverter::ISkinNode(plMaxNode* node) |
|
{ |
|
hsGuardBegin("hsControlConverter::ISkinNode"); |
|
|
|
/* |
|
if( fForceSkinning ) |
|
return true; |
|
if( fForceNoSkinning ) |
|
return false; |
|
*/ |
|
if (gUserPropMgr.UserPropExists(node,"MATSkin")) |
|
{ |
|
return true; |
|
} |
|
|
|
if (gUserPropMgr.UserPropExists(node,"MATSkinColor")) |
|
{ |
|
return true; |
|
} |
|
|
|
if( node && node->GetName() && strstr(node->GetName(), "%skin") ) |
|
{ |
|
return true; |
|
} |
|
|
|
return false; |
|
hsGuardEnd; |
|
} |
|
|
|
void hsControlConverter::Matrix3ToHsMatrix44(Matrix3* m3, hsMatrix44* hsM) |
|
{ |
|
hsGuardBegin("hsControlConverter::Matrix3ToHsMatrix44"); |
|
|
|
MRow* m = m3->GetAddr(); |
|
|
|
hsM->Reset(); |
|
hsM->fMap[0][0] = m[0][0]; |
|
hsM->fMap[0][1] = m[1][0]; |
|
hsM->fMap[0][2] = m[2][0]; |
|
hsM->fMap[0][3] = m[3][0]; |
|
|
|
hsM->fMap[1][0] = m[0][1]; |
|
hsM->fMap[1][1] = m[1][1]; |
|
hsM->fMap[1][2] = m[2][1]; |
|
hsM->fMap[1][3] = m[3][1]; |
|
|
|
hsM->fMap[2][0] = m[0][2]; |
|
hsM->fMap[2][1] = m[1][2]; |
|
hsM->fMap[2][2] = m[2][2]; |
|
hsM->fMap[2][3] = m[3][2]; |
|
|
|
hsM->NotIdentity(); |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
//// IGetEditableMeshKeyTimes //////////////////////////////////////////////// |
|
// Moved here after hsMeshConverter was obliterated. Only used in this class |
|
// anyway... |
|
|
|
bool hsControlConverter::IGetEditableMeshKeyTimes( plMaxNode *node, Tab<TimeValue> × ) |
|
{ |
|
hsGuardBegin( "hsControlConverter::GetEditableMeshKeyTimes" ); |
|
|
|
Animatable *anim; |
|
if( IGetSubAnimByName(node, TSTR("Object (Editable Mesh)"), anim) ) |
|
{ |
|
fErrorMsg->Set(!anim, node->GetName(), "First she says yes, then she says no.").Check(); |
|
|
|
int i; |
|
int nSub = anim->NumSubs(); |
|
for( i = 0; i < nSub; i++ ) |
|
{ |
|
if( anim->SubAnim(i) ) |
|
{ |
|
Control *ctl = GetControlInterface(anim->SubAnim(i)); |
|
hsControlConverter::Instance().CompositeKeyTimes(ctl, times); |
|
} |
|
} |
|
} |
|
return times.Count() > 0; |
|
hsGuardEnd; |
|
} |
|
|
|
//// IGetGeomKeyTimes //////////////////////////////////////////////////////// |
|
// Moved here after hsMeshConverter was obliterated. Only used in this class |
|
// anyway... |
|
|
|
bool hsControlConverter::IGetGeomKeyTimes( plMaxNode *node, Tab<TimeValue> × ) |
|
{ |
|
hsGuardBegin( "hsControlConverter::GetGeomKeyTimes" ); |
|
|
|
char *dgbNodeName = node->GetName(); |
|
Object *obj = node->GetObjectRef(); |
|
if( !obj ) |
|
return false; |
|
IDerivedObject *derObj = nil; |
|
if( obj->CanConvertToType(derivObjClassID) ) |
|
{ |
|
derObj = (IDerivedObject *)obj->ConvertToType(fConverterUtils.GetTime(fInterface), derivObjClassID); |
|
} |
|
else |
|
{ |
|
SClass_ID objID = obj->SuperClassID(); |
|
SClass_ID genID(GEN_DERIVOB_CLASS_ID); |
|
if( !(obj->SuperClassID() == SClass_ID(GEN_DERIVOB_CLASS_ID)) ) |
|
return false; |
|
if( objID != genID ) |
|
return false; |
|
derObj = (IDerivedObject *)obj; |
|
} |
|
|
|
int i; |
|
int nKeys = 0; |
|
for( i = 0; i < derObj->NumModifiers(); i++ ) |
|
{ |
|
Modifier *mod = derObj->GetModifier(i); |
|
char *dbgModName = mod->GetName(); |
|
if( mod ) |
|
{ |
|
ChannelMask mask = mod->ChannelsChanged(); |
|
if( mask & GEOM_CHANNEL ) |
|
{ |
|
IGetGeomKeyTimesRecur(mod, times); |
|
} |
|
} |
|
} |
|
|
|
return (times.Count() > 0); |
|
hsGuardEnd; |
|
} |
|
|
|
//// IGetGeomKeyTimesRecur /////////////////////////////////////////////////// |
|
// Moved here after hsMeshConverter was obliterated. Only used in this class |
|
// anyway... |
|
|
|
void hsControlConverter::IGetGeomKeyTimesRecur( Animatable *anim, Tab<TimeValue> × ) |
|
{ |
|
hsGuardBegin( "hsControlConverter::IGetGeomKeyTimesRecur" ); |
|
|
|
Control * ctl = GetControlInterface(anim); |
|
hsControlConverter::Instance().CompositeKeyTimes(ctl, times); |
|
|
|
int iSub; |
|
int nSub = anim->NumSubs(); |
|
for( iSub = 0; iSub < nSub; iSub++ ) |
|
{ |
|
if( anim->SubAnim(iSub) ) |
|
IGetGeomKeyTimesRecur(anim->SubAnim(iSub), times); |
|
} |
|
|
|
hsGuardEnd; |
|
} |
|
|
|
//// IGetGeomKeyTimesRecur /////////////////////////////////////////////////// |
|
// Moved here after hsMeshConverter was obliterated. Only used in this class |
|
// anyway... |
|
|
|
bool hsControlConverter::IGetSubAnimByName( Animatable *anim, TSTR &name, Animatable *&subAnim ) |
|
{ |
|
hsGuardBegin( "hsControlConverter::IGetSubAnimByName" ); |
|
|
|
if( anim ) |
|
{ |
|
int nSub = anim->NumSubs(); |
|
int i; |
|
for( i = 0; i < nSub; i++ ) |
|
{ |
|
if (anim->SubAnim(i)==nil) |
|
continue; |
|
TSTR subName = anim->SubAnimName(i); |
|
if( subName == name ) |
|
{ |
|
fErrorMsg->Set(!anim->SubAnim(i), name, "Found controller by name, but nobody home").Check(); |
|
subAnim = anim->SubAnim(i); |
|
return true; |
|
} |
|
else if( IGetSubAnimByName(anim->SubAnim(i), name, subAnim) ) |
|
{ |
|
return true; |
|
} |
|
} |
|
} |
|
subAnim = nil; |
|
return false; |
|
hsGuardEnd; |
|
} |
|
|
|
// bad craziness, isolated here. |
|
#include "plConvert.h" |
|
#include "plgDispatch.h" |
|
#include "MaxComponent/plAnimComponent.h" |
|
#include "MaxComponent/plCameraComponents.h" |
|
#include "pnMessage/plCameraMsg.h" |
|
#include "plMessage/plAnimCmdMsg.h" |
|
#include "pfCamera/plCameraModifier.h" |
|
#include "pnSceneObject/plSceneObject.h" |
|
|
|
void hsControlConverter::IExportAnimatedCameraFOV(plMaxNode* node, hsTArray <hsG3DSMaxKeyFrame>* kfArray) |
|
{ |
|
// grab the FOV settings at each keyframe here |
|
// create callback messages for the animation to send to the camera |
|
// to interpolate to the correct FOV at each keyframe |
|
|
|
plAnimComponentBase* pAnim = nil; |
|
int count = node->NumAttachedComponents(); |
|
int i; |
|
for (i = 0; i < count; i++) |
|
{ |
|
plComponentBase *comp = node->GetAttachedComponent(i); |
|
if (comp->ClassID() == ANIM_COMP_CID || comp->ClassID() == ANIM_GROUP_COMP_CID) |
|
{ |
|
pAnim = (plAnimComponentBase*)comp; |
|
break; |
|
} |
|
} |
|
|
|
plCamera1Component* pCamComp = nil; |
|
for (i = 0; i < count; i++) |
|
{ |
|
plComponentBase *comp = node->GetAttachedComponent(i); |
|
if (comp->ClassID() == FIXEDCAM_CID) |
|
{ |
|
pCamComp = (plCamera1Component*)comp; |
|
break; |
|
} |
|
} |
|
|
|
const plCameraModifier1* pCamMod = nil; |
|
count = node->GetSceneObject()->GetNumModifiers(); |
|
for (i=0; i < count; i++) |
|
{ |
|
pCamMod = plCameraModifier1::ConvertNoRef(node->GetSceneObject()->GetModifier(i)); |
|
if (pCamMod) |
|
break; |
|
|
|
} |
|
plCameraMsg* pCamMsg = new plCameraMsg; |
|
pCamMsg->SetCmd(plCameraMsg::kSetAnimated); |
|
pCamMsg->AddReceiver(pCamMod->GetKey()); |
|
plConvert::Instance().AddMessageToQueue(pCamMsg); |
|
Object* obj = node->EvalWorldState(hsConverterUtils::Instance().GetTime(node->GetInterface())).obj; |
|
GenCamera* theCam; |
|
hsTArray<float> fovW; |
|
hsTArray<float> fovH; |
|
for (i=0; i < kfArray->Count(); i++) |
|
{ |
|
TimeValue t = TimeValue(GetTicksPerFrame() * (kfArray[0][i].fFrame)); |
|
theCam = (GenCamera *) obj->ConvertToType(t, Class_ID(LOOKAT_CAM_CLASS_ID, 0)); |
|
float FOVvalue= theCam->GetFOV(t); // in radians |
|
FOVvalue *= (float)(180.f / M_PI); // to degrees |
|
int FOVType = theCam->GetFOVType(); |
|
float wDeg, hDeg; |
|
switch(FOVType) |
|
{ |
|
case 0: // FOV_W |
|
{ |
|
wDeg = FOVvalue; |
|
hDeg = (wDeg*3)/4; |
|
} |
|
break; |
|
case 1: // FOV_H |
|
{ |
|
hDeg = FOVvalue; |
|
wDeg = (hDeg*4)/3; |
|
} |
|
break; |
|
} |
|
fovW.Append(wDeg); |
|
fovH.Append(hDeg); |
|
|
|
} |
|
for (i=0; i < kfArray->Count(); i++) |
|
{ |
|
|
|
plCameraMsg* pFOVMsg = new plCameraMsg; |
|
plCameraConfig* pCfg = pFOVMsg->GetConfig(); |
|
|
|
if (i == kfArray->Count() - 1) |
|
{ |
|
pCfg->fFOVh = fovH[0]; |
|
pCfg->fFOVw = fovW[0]; |
|
pCfg->fAccel = kfArray[0][0].fFrame / MAX_FRAMES_PER_SEC; |
|
} |
|
else |
|
{ |
|
pCfg->fFOVh = fovH[i + 1]; |
|
pCfg->fFOVw = fovW[i + 1]; |
|
pCfg->fAccel = kfArray[0][i + 1].fFrame / MAX_FRAMES_PER_SEC; |
|
} |
|
|
|
|
|
pFOVMsg->SetCmd(plCameraMsg::kAddFOVKeyframe); |
|
pFOVMsg->AddReceiver(pCamMod->GetKey()); |
|
|
|
plEventCallbackMsg* pCall = new plEventCallbackMsg; |
|
pCall->fEvent = kTime; |
|
pCall->fEventTime = kfArray[0][i].fFrame / MAX_FRAMES_PER_SEC; |
|
pCall->fIndex = i; |
|
pCall->AddReceiver(pCamMod->GetKey()); |
|
plAnimCmdMsg* pMsg = new plAnimCmdMsg; |
|
pMsg->AddReceiver(pCamMod->GetKey()); |
|
pMsg->SetSender(pAnim->GetModKey(node)); |
|
pMsg->SetCmd(plAnimCmdMsg::kAddCallbacks); |
|
pMsg->SetAnimName(ENTIRE_ANIMATION_NAME); |
|
pMsg->fTime = kfArray[0][i].fFrame / MAX_FRAMES_PER_SEC; |
|
pMsg->AddCallback(pCall); |
|
hsRefCnt_SafeUnRef(pCall); |
|
plConvert::Instance().AddMessageToQueue(pFOVMsg); |
|
plConvert::Instance().AddMessageToQueue(pMsg); |
|
} |
|
}
|
|
|