deargui-vpl/ref/virtools/Samples/ExportPlugins/3dsMax Export/animout.cpp

1046 lines
32 KiB
C++

//**************************************************************************
//* Animout.cpp - Virtools File Exporter
//*
//* Romain Sididris - Copyright (c) Virtools 2000
//*
//* (Based on Ascii File Exporter source code
//* By Christer Janson
//* Kinetix Development
//* Copyright (c) Kinetix 1997, All Rights Reserved. )
//*
//* This module handles controller key output and controller sampling.
//*
//***************************************************************************
#include "Precomp.h"
#include "Max2Nmo.h"
#define ALMOST_ZERO 1.0e-3f
BOOL EqualPoint3(Point3 p1, Point3 p2);
BOOL EqualFloat(float v1, float v2);
//uncomment this define when developpement is done about that
//#define SUPPORT_NEW_CONTROLLERS
//--- An independent X, Y, Z position controller
///// See file: indepos.cpp
#define IPOS_CONTROL_CLASS_ID Class_ID(0x118f7e02,0xffee238a)
#define IPOINT3_CONTROL_CLASS_ID Class_ID(0x118f7e02,0xfeee238b)
#define IPOINT4_CONTROL_CLASS_ID Class_ID(0x118f7e02,0xfeee238c)
/****************************************************************************
Export animation keys for a node, if the node use known Max controllers
(Linear,Bezier,TCB) PRS scale controller, these controller are converted to Virtools
format, otherwise if the object is animated we sample the animation to create
key frame data.
****************************************************************************/
void Max2Nmo::ExportAnimKeys(INode* node,CK3dEntity *entity)
{
BOOL bDoKeys = FALSE;
BOOL bForceKey = FALSE;
Control* tmC = node->GetTMController();
Control* pC = tmC->GetPositionController();
Control* rC = tmC->GetRotationController();
Control* sC = tmC->GetScaleController();
// Force at least one key to be created on biped controlled objects... :(
Class_ID cid = tmC->ClassID();
// For a biped controlled object some objects
// may not be animed but they may need at least one key to have
// a correct position when warping between different animation
// If the controller has at least one key, we force the creation of the animation
if ((cid == BIPBODY_CONTROL_CLASS_ID) || (cid == BIPSLAVE_CONTROL_CLASS_ID) ||
(cid == FOOTPRINT_CLASS_ID)) {
bForceKey = TRUE;
}
if ((cid == Class_ID(PRS_CONTROL_CLASS_ID,0)) && IsKnownController(pC) && IsKnownController(rC) && IsKnownController(sC)) {
bDoKeys = TRUE;
}
CKObjectAnimation* anim = NULL;
if (bDoKeys) {
anim = VirtoolsExporter->AddObjectAnimation(entity,entity->GetName(),node);
#ifdef SUPPORT_NEW_CONTROLLERS
if (pC->ClassID()==IPOS_CONTROL_CLASS_ID)
DumpIndePos(node,pC,anim); //try to dump default controler
else
#endif
DumpPosKeys(pC,anim);
#ifdef SUPPORT_NEW_CONTROLLERS
if (rC->ClassID()==IPOINT4_CONTROL_CLASS_ID || rC->ClassID()==IPOINT3_CONTROL_CLASS_ID)
DumpIndeRot(node,rC,anim); //try to dump default controler
else
#endif
DumpRotKeys(rC,anim);
DumpScaleKeys(sC,anim);
} else {
anim = VirtoolsExporter->AddObjectAnimation(entity,entity->GetName(),node);
DumpSampledAnim(node,anim);
}
if (bExportBlendShapeAnims)
{
// Blend shape anim
Modifier* morphModifier = FindModifier(node, MR3_CLASS_ID);
if (morphModifier)
{
Report(REPORT_MLEVEL, "\r\nSearching blend shape animations for node %s", node->GetName());
for (int i = 0; i < 100; ++i)
{
IParamBlock* pb = (IParamBlock*) morphModifier->SubAnim(i + 1);
if (!pb) continue;
Control* ctrl = pb->GetController(0);
// max encodes weights in the [0-100] range, we want weights in the [0-1] range -> hence the 0.01f factor
if (ctrl)
{
Report(REPORT_MLEVEL, "\r\nExporting animation of blend shape #%u (%s) for mesh %s", i, morphModifier->SubAnimName(i + 1), node->GetName());
if (IsKnownController(ctrl))
{
DumpFloatKeys(ctrl, anim, 0.01f, morphModifier->SubAnimName(i + 1));
}
else
{
DumpSampledFloatAnim(ctrl, anim, 0.01f, morphModifier->SubAnimName(i + 1));
}
}
}
}
}
//--- Check validity of every animation controller and report
if (anim) {
anim->SetLength(FrameTime(m_EndFrame));
unsigned int KeyLimitCount = bForceKey ? 1 : 2;
//if (GetIgnore1KeyAnimation())
// KeyLimitCount = 0xFFFFFFFF;
// Check rotation controller
CKAnimController* Rctrl = anim->GetRotationController();
if (Rctrl) {
if (Rctrl->GetKeyCount()<KeyLimitCount && GetIgnore1KeyAnimation()) {
anim->DeleteController(CKANIMATION_CONTROLLER_ROT);
Rctrl = NULL;
Report(REPORT_LLEVEL,"Rotation Controller deleted for object %s\r\n",
entity->GetName());
}
if (Rctrl && Rctrl->GetKeyCount()>=KeyLimitCount && !anim->GetScaleController()) {
// We add a fake scale controller with only one key
// to preserve errors from coming from lose of precision
// when applying successive rotation without reseting the scale
VxVector Pos,Scale;
VxQuaternion Quat;
Vx3DDecomposeMatrix(entity->GetLocalMatrix(),Quat,Pos,Scale);
CKAnimController* Sctrl = anim->CreateController(CKANIMATION_LINSCL_CONTROL);
Sctrl->AddKey(&CKScaleKey(0,Scale));
}
}
// Check position controller
CKAnimController* Pctrl = anim->GetPositionController();
if (Pctrl && Pctrl->GetKeyCount()<KeyLimitCount && GetIgnore1KeyAnimation()) {
anim->DeleteController(CKANIMATION_CONTROLLER_POS);
Pctrl = NULL;
Report(REPORT_LLEVEL,"Position Controller deleted for object %s\r\n",
entity->GetName());
}
// Check scale controller
CKAnimController* Sctrl = anim->GetScaleController();
if (Sctrl && Sctrl->GetKeyCount()<KeyLimitCount && GetIgnore1KeyAnimation()) {
anim->DeleteController(CKANIMATION_CONTROLLER_SCL);
Sctrl = NULL;
Report(REPORT_LLEVEL,"Scale Controller deleted for object %s\r\n",
entity->GetName());
}
// Check Off-Axis scale controller
CKAnimController* SActrl= anim->GetScaleAxisController();
if (SActrl && SActrl->GetKeyCount()<KeyLimitCount && GetIgnore1KeyAnimation()) {
anim->DeleteController(CKANIMATION_CONTROLLER_SCLAXIS);
SActrl = NULL;
Report(REPORT_LLEVEL,"Scale Axis Controller deleted for object %s\r\n",
entity->GetName());
}
// Report
if (Pctrl || Rctrl || Sctrl || SActrl) {
if (bDoKeys)
Report(REPORT_MLEVEL,"\r\nSaving standard animation...\r\n");
else
Report(REPORT_MLEVEL,"\r\nConverting unknown animation (Sampling)...\r\n");
Report(REPORT_LLEVEL,"Position: %d keys, Rotation: %d keys, Scale: %d keys\r\n",
Pctrl ? Pctrl->GetKeyCount() : 0,
Rctrl ? Rctrl->GetKeyCount() : 0,
Sctrl ? Sctrl->GetKeyCount() : 0);
if (SActrl && SActrl->GetKeyCount())
Report(REPORT_LLEVEL,"Warning : OffAxis Scale : %d keys\r\n",SActrl->GetKeyCount());
} else {
// every controller was deleted : remove the animation
// will be done by exporter...
}
}
}
/**************************************************************************************
To really see if a node is animated we can step through the animation range
and decompose the TM matrix for every frame and examine the components.
This way we can identify position, rotation and scale animation separately.
RS : Added support for off axis scale...
****************************************************************************************/
BOOL Max2Nmo::CheckForAnimation(INode* node, BOOL& bPos, BOOL& bRot, BOOL& bScale, BOOL& bScaleAxis)
{
TimeValue start = ip->GetAnimRange().Start();
TimeValue end = ip->GetAnimRange().End();
TimeValue t;
int delta = GetTicksPerFrame();
Matrix3 tm;
AffineParts ap;
Point3 firstPos;
float rotAngle, firstRotAngle;
Point3 rotAxis, firstRotAxis;
float scaleRotAngle, firstScaleRotAngle;
Point3 scaleRotAxis, firstScaleRotAxis;
Point3 firstScaleFactor;
bPos = bRot = bScale = bScaleAxis = FALSE;
for (t=start; t<=end; t+=delta) {
tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t));
decomp_affine(tm, &ap);
AngAxisFromQ(ap.q, &rotAngle, rotAxis); // Rotation
AngAxisFromQ(ap.u, &scaleRotAngle,scaleRotAxis); // Off Axis rotation
if (!ap.u.IsIdentity()) bScaleAxis = TRUE;
if (t != start) {
// Position changes
if (!bPos) {
if (!EqualPoint3(ap.t, firstPos)) {
bPos = TRUE;
}
}
// Rotation changes
if (!bRot) {
if (fabs(rotAngle - firstRotAngle) > ALMOST_ZERO) {
bRot = TRUE;
} else if (!EqualPoint3(rotAxis, firstRotAxis)) {
bRot = TRUE;
}
}
// Scale changes
if (!bScale) {
if (!EqualPoint3(ap.k, firstScaleFactor)) {
bScale = TRUE;
}
}
// Off-Axis Scale changes
if (!bScaleAxis) {
if (fabs(scaleRotAngle - firstScaleRotAngle) > ALMOST_ZERO) {
bScaleAxis = TRUE;
} else if (!EqualPoint3(scaleRotAxis, firstScaleRotAxis)) {
bScaleAxis = TRUE;
}
}
} else {
firstPos = ap.t;
firstRotAngle = rotAngle;
firstRotAxis = rotAxis;
firstScaleRotAngle = scaleRotAngle;
firstScaleRotAxis = scaleRotAxis;
firstScaleFactor = ap.k;
}
// No need to continue looping if all components are animated
if (bPos && bRot && bScale && bScaleAxis) break;
}
if (!(bPos || bRot || bScale) && bScaleAxis) bScaleAxis = FALSE;
return bPos || bRot || bScale || bScaleAxis;
}
/********************************************************************************
Samples the animation range of a Node and create keys every N frame (given by GetKeyframeStep in
the dialog box ). This is done by decomposing the TM matrix.
If two successive keys are equal, no new key is created.
********************************************************************************/
void Max2Nmo::DumpSampledAnim(INode* node,CKObjectAnimation* anim)
{
Control* cont = node->GetTMController();
Class_ID cont_cid = cont->ClassID();
TimeValue start = ip->GetAnimRange().Start();
TimeValue end = ip->GetAnimRange().End();
//--- There's still some "unkown" controller for which we can still decide
///// not to create sampled keys as we know they should have keys to produce anim
// 05/12/05 - Reverted this change (Ericg)
// In some cases, the position/rotation controller may return 0 keys, while there
// actually are on the sub-controllers for each axis.
//Reported from source safe by NicolasA because of Laf changes
// if( (cont_cid == Class_ID(PRS_CONTROL_CLASS_ID, 0)) ){
// int keyCount = 0;
// Control* cpos = node->GetTMController()->GetPositionController();
// Class_ID cpos_id = cpos->ClassID();
// if( cpos_id == IPOS_CONTROL_CLASS_ID ){
// Tab<TimeValue> times;
// Interval range( start, end );
// cpos->GetKeyTimes( times, range, KEYAT_POSITION );
// keyCount += times.Count();
// }
// Control* crot = node->GetTMController()->GetRotationController();
// Class_ID crot_id = cpos->ClassID();
// if( crot_id == IPOS_CONTROL_CLASS_ID ){
// Tab<TimeValue> times;
// Interval range( start, end );
// cpos->GetKeyTimes( times, range, KEYAT_ROTATION );
// keyCount += times.Count();
// }
// Control* cscl = node->GetTMController()->GetScaleController();
// Class_ID cscl_id = cpos->ClassID();
// if( cscl_id == IPOS_CONTROL_CLASS_ID ){
// Tab<TimeValue> times;
// Interval range( start, end );
// cpos->GetKeyTimes( times, range, KEYAT_SCALE );
// keyCount += times.Count();
// }
// // there's no key control interface so we can't use the beneath lines (too bad)
// /*
// IKeyControl *ikc = GetKeyControlInterface(cont);
// if (!ikc) return ;
// int numKeys = ikc->GetNumKeys();
// */
// //--- If there's no "detectable" keys don't continue
// if( keyCount == 0 ){
// return;
// }
// }
TimeValue t;
int delta = GetTicksPerFrame() * GetKeyFrameStep();
Matrix3 tm;
AffineParts ap;
//-- Light and Camera animation need an additional rotation around X axis to be valid
//-- in Virtools ( Z axis is supposed to be the direction of the lights and cameras )
Quat RotateX;
RotateX.Identity();
CK3dEntity* entity = anim->Get3dEntity();
if (CKIsChildClassOf(entity,CKCID_CAMERA) || CKIsChildClassOf(entity,CKCID_LIGHT)) {
RotateX=QFromAngAxis(HALFPI,Point3(1.0f,0.0f,0.0f));
}
Point3 prevPos;
Quat prevQ;
Quat prevU;
Point3 prevScl;
prevQ.Identity();
prevU.Identity();
CKAnimController* Pctrl = anim->CreateController(CKANIMATION_LINPOS_CONTROL);
CKAnimController* Rctrl = anim->CreateController(CKANIMATION_LINROT_CONTROL);
CKAnimController* Sctrl = anim->CreateController(CKANIMATION_LINSCL_CONTROL);
CKAnimController* SActrl = anim->CreateController(CKANIMATION_LINSCLAXIS_CONTROL);
for (t=start; t<=end; t+=delta) {
tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t));
decomp_affine(tm, &ap);
if (ReduceRedondantKeys()) {
// Position key
if (Pctrl) {
Point3 pos = ap.t;
if ((t==start) || (!EqualPoint3(pos, prevPos))) {
Pctrl->AddKey(&CKPositionKey(FrameTime(t),
VxVector(pos.x,pos.z,pos.y)));
}
prevPos = pos;
}
// Rotation Key
if (Rctrl) {
Quat q = ap.q / prevQ;
prevQ = ap.q;
if ((t==start) || !q.IsIdentity()) {
Quat Val = RotateX*ap.q;
Rctrl->AddKey(&CKRotationKey(FrameTime(t),
VxQuaternion(-Val.x,-Val.z,-Val.y,Val.w)));
}
}
// Scale Key
if (Sctrl) {
Point3 scl = ap.k;
if ((t==start) || !EqualPoint3(scl, prevScl)) {
Sctrl->AddKey(&CKScaleKey(FrameTime(t),
VxVector(scl.x,scl.z,scl.y)));
}
prevScl = scl;
}
// Off-Axis Scale Key
if (SActrl) {
Quat u = ap.u / prevU;
prevU = ap.u;
if (t== start || !u.IsIdentity()) {
Quat Val = RotateX*ap.u;
SActrl->AddKey(&CKScaleAxisKey(FrameTime(t),
VxQuaternion(-Val.x,-Val.z,-Val.y,Val.w)));
}
}
} else {
// Position key
if (Pctrl) {
Point3 pos = ap.t;
Pctrl->AddKey(&CKPositionKey(FrameTime(t),
VxVector(pos.x,pos.z,pos.y)));
prevPos = pos;
}
// Rotation Key
if (Rctrl) {
Quat q = ap.q / prevQ;
prevQ = ap.q;
Quat Val = RotateX*ap.q;
Rctrl->AddKey(&CKRotationKey(FrameTime(t),
VxQuaternion(-Val.x,-Val.z,-Val.y,Val.w)));
}
// Scale Key
if (Sctrl) {
Point3 scl = ap.k;
Sctrl->AddKey(&CKScaleKey(FrameTime(t),
VxVector(scl.x,scl.z,scl.y)));
prevScl = scl;
}
// Off-Axis Scale Key
if (SActrl) {
Quat u = ap.u / prevU;
prevU = ap.u;
Quat Val = RotateX*ap.u;
SActrl->AddKey(&CKScaleAxisKey(FrameTime(t),
VxQuaternion(-Val.x,-Val.z,-Val.y,Val.w)));
}
}
// Ensure to have the last animation key
if ((t<end) && (t + delta > end)) {
t = end-delta;
}
}
}
/********************************************************************************
Samples the animation range of a Node blend shape float track and create keys every N frame (given by GetKeyframeStep in
the dialog box ). If two successive keys are equal, no new key is created.
********************************************************************************/
void Max2Nmo::DumpSampledFloatAnim(Control* cont, CKObjectAnimation* anim, float scale, const char* targetBlendShapeName)
{
if (!cont) return;
TimeValue start = ip->GetAnimRange().Start();
TimeValue end = ip->GetAnimRange().End();
CKAnimController* BSctrl = anim->CreateController(CKANIMATION_LINFLOAT_CONTROL, targetBlendShapeName);
TimeValue t;
int delta = GetTicksPerFrame() * GetBlendShapesStep();
float value, prevValue;
for (t=start; t<=end; t+=delta)
{
cont->GetValue(t, &value, FOREVER);
value *= scale;
if (!ReduceRedondantKeys() || (t==start) || (!EqualFloat(value, prevValue)))
{
BSctrl->AddKey(&CKFloatKey(FrameTime(t), value));
prevValue = value;
}
}
}
/*****************************************************************
Convert Bezier keys Tangent flags from Max to Virtools
*****************************************************************/
CKBezierKeyFlags MaxBezFlags2Virtools(DWORD f)
{
CKBezierKeyFlags Flags;
DWORD InType = GetInTanType(f);
DWORD OutType = GetOutTanType(f);
switch (InType) {
case BEZKEY_SMOOTH: Flags.SetInTangentMode(BEZIER_KEY_AUTOSMOOTH); break;
case BEZKEY_LINEAR: Flags.SetInTangentMode(BEZIER_KEY_LINEAR); break;
case BEZKEY_STEP: Flags.SetInTangentMode(BEZIER_KEY_STEP); break;
case BEZKEY_FAST: Flags.SetInTangentMode(BEZIER_KEY_FAST); break;
case BEZKEY_SLOW: Flags.SetInTangentMode(BEZIER_KEY_SLOW); break;
case BEZKEY_USER: Flags.SetInTangentMode(BEZIER_KEY_TANGENTS); break;
}
switch (OutType) {
case BEZKEY_SMOOTH: Flags.SetOutTangentMode(BEZIER_KEY_AUTOSMOOTH); break;
case BEZKEY_LINEAR: Flags.SetOutTangentMode(BEZIER_KEY_LINEAR); break;
case BEZKEY_STEP: Flags.SetOutTangentMode(BEZIER_KEY_STEP); break;
case BEZKEY_FAST: Flags.SetOutTangentMode(BEZIER_KEY_FAST); break;
case BEZKEY_SLOW: Flags.SetOutTangentMode(BEZIER_KEY_SLOW); break;
case BEZKEY_USER: Flags.SetOutTangentMode(BEZIER_KEY_TANGENTS); break;
}
return Flags;
}
/********************************************************************************
Creates a Position Controller on a CKObjectAnimation using
Max Controller key frame data. the 3 standard type of controller
are supported : Linear,Bezier and TCB
*********************************************************************************/
void Max2Nmo::DumpPosKeys(Control* cont,CKObjectAnimation* anim)
{
if (!cont) return;
IKeyControl *ikc = GetKeyControlInterface(cont);
if (!ikc) return ;
int i,numKeys = ikc->GetNumKeys();
if (!numKeys) return ;
Class_ID cont_cid = cont->ClassID();
// If this is not a standard controller we return
if (cont_cid.PartB()) return;
//--- Create the appropriate Virtools position controller
CKANIMATION_CONTROLLER VirtoolsCtrlType;
switch (cont_cid.PartA()) {
case TCBINTERP_POSITION_CLASS_ID : VirtoolsCtrlType = CKANIMATION_TCBPOS_CONTROL; break;
case HYBRIDINTERP_POSITION_CLASS_ID : VirtoolsCtrlType = CKANIMATION_BEZIERPOS_CONTROL; break;
case LININTERP_POSITION_CLASS_ID : VirtoolsCtrlType = CKANIMATION_LINPOS_CONTROL; break;
default :
return;
}
CKAnimController* ctrl = anim->CreateController(VirtoolsCtrlType);
//--- And convert max keys
switch (cont_cid.PartA()) {
case TCBINTERP_POSITION_CLASS_ID :
for (i=0; i<numKeys; i++) {
ITCBPoint3Key key;
ikc->GetKey(i, &key); // Get Max Key
// Create Virtools Key
CKTCBPositionKey VirtoolsKey(FrameTime(key.time),
VxVector(key.val.x,key.val.z,key.val.y),
key.tens,key.cont,key.bias,
key.easeIn,key.easeOut);
ctrl->AddKey(&VirtoolsKey);
}
break;
case HYBRIDINTERP_POSITION_CLASS_ID :
for (i=0; i<numKeys; i++) {
IBezPoint3Key key;
ikc->GetKey(i, &key); // Get Max Key
// Convert Max Bezier tangent to Virtools format
CKBezierKeyFlags Flags=MaxBezFlags2Virtools(key.flags);
float ScaleFactor = GetFrameRate()*16.0f/3.0f;
VxVector InTang(key.intan.x,key.intan.z,key.intan.y),
OutTang(key.outtan.x,key.outtan.z,key.outtan.y);
InTang*=ScaleFactor;
OutTang*=ScaleFactor;
// Create Virtools Bezier Key
CKBezierPositionKey VirtoolsKey(FrameTime(key.time),
VxVector(key.val.x,key.val.z,key.val.y),
Flags,
InTang,OutTang);
ctrl->AddKey(&VirtoolsKey);
}
break;
case LININTERP_POSITION_CLASS_ID :
for (i=0; i<numKeys; i++) {
ILinPoint3Key key;
ikc->GetKey(i, &key); // Get Max Key
// Create Virtools linear Position Key
ctrl->AddKey(&CKPositionKey(FrameTime(key.time),
VxVector(key.val.x,key.val.z,key.val.y)));
}
break;
}
}
/********************************************************************************
Creates a float (blend shape) Controller on a CKObjectAnimation using
Max Controller key frame data. the 3 standard type of controller
are supported : Linear,Bezier and TCB
*********************************************************************************/
void Max2Nmo::DumpFloatKeys(Control* cont, CKObjectAnimation* anim, float scale, const char* targetBlendShapeName)
{
if (!cont) return;
IKeyControl *ikc = GetKeyControlInterface(cont);
if (!ikc) return ;
int i,numKeys = ikc->GetNumKeys();
if (!numKeys) return ;
Class_ID cont_cid = cont->ClassID();
// If this is not a standard controller we return
if (cont_cid.PartB()) return;
//--- Create the appropriate Virtools position controller
CKANIMATION_CONTROLLER VirtoolsCtrlType;
switch (cont_cid.PartA()) {
case TCBINTERP_FLOAT_CLASS_ID : VirtoolsCtrlType = CKANIMATION_TCBFLOAT_CONTROL; break;
case HYBRIDINTERP_FLOAT_CLASS_ID : VirtoolsCtrlType = CKANIMATION_BEZIERFLOAT_CONTROL; break;
case LININTERP_FLOAT_CLASS_ID : VirtoolsCtrlType = CKANIMATION_LINFLOAT_CONTROL; break;
default :
return;
}
CKAnimController* ctrl = anim->CreateController(VirtoolsCtrlType, targetBlendShapeName);
//--- And convert max keys
switch (cont_cid.PartA()) {
case TCBINTERP_FLOAT_CLASS_ID :
for (i=0; i<numKeys; i++) {
ITCBFloatKey key;
ikc->GetKey(i, &key); // Get Max Key
// Create Virtools Key
CKTCBFloatKey VirtoolsKey(FrameTime(key.time),
scale * key.val,
key.tens,key.cont,key.bias,
scale * key.easeIn, scale * key.easeOut);
ctrl->AddKey(&VirtoolsKey);
}
break;
case HYBRIDINTERP_FLOAT_CLASS_ID :
for (i=0; i<numKeys; i++) {
IBezFloatKey key;
ikc->GetKey(i, &key); // Get Max Key
// Convert Max Bezier tangent to Virtools format
CKBezierKeyFlags Flags=MaxBezFlags2Virtools(key.flags);
float ScaleFactor = GetFrameRate()*16.0f/3.0f;
// Create Virtools Bezier Key
CKBezierFloatKey VirtoolsKey(FrameTime(key.time),
scale * key.val,
Flags,
scale * key.intan * ScaleFactor, scale * key.outtan * ScaleFactor);
ctrl->AddKey(&VirtoolsKey);
}
break;
case LININTERP_FLOAT_CLASS_ID :
for (i=0; i<numKeys; i++) {
ILinFloatKey key;
ikc->GetKey(i, &key); // Get Max Key
// Create Virtools linear Position Key
ctrl->AddKey(&CKFloatKey(FrameTime(key.time), scale * key.val));
}
break;
}
}
/********************************************************************************
Creates a Rotation Controller on a CKObjectAnimation using
Max Controller key frame data. the 3 standard type of controller
are supported : Linear,TCB (Bezier will be converted to linear)
********************************************************************************/
void Max2Nmo::DumpRotKeys(Control* cont,CKObjectAnimation* anim)
{
if (!cont) return;
IKeyControl *ikc = GetKeyControlInterface(cont);
if (!ikc) return ;
int numKeys = ikc->GetNumKeys();
if (!numKeys) return ;
CK3dEntity* ent = anim->Get3dEntity();
//-- Light and Camera animation need an additional rotation around X axis to be valid
//-- in Virtools ( Z axis is supposed to be the direction of the lights and cameras )
Quat RotateX;
RotateX.Identity();
if (CKIsChildClassOf(ent,CKCID_CAMERA) || CKIsChildClassOf(ent,CKCID_LIGHT)) {
RotateX=QFromAngAxis(HALFPI,Point3(1.0f,0.0f,0.0f));
}
Class_ID cont_cid = cont->ClassID();
// If this is not a standard controller we return
if (cont_cid.PartB()) return;
//--- Create the appropriate Virtools position controller
CKANIMATION_CONTROLLER VirtoolsCtrlType;
switch (cont_cid.PartA()) {
case TCBINTERP_ROTATION_CLASS_ID : VirtoolsCtrlType = CKANIMATION_TCBROT_CONTROL; break;
case HYBRIDINTERP_ROTATION_CLASS_ID :
case LININTERP_ROTATION_CLASS_ID : VirtoolsCtrlType = CKANIMATION_LINROT_CONTROL; break;
default : return;
}
CKAnimController* ctrl = anim->CreateController(VirtoolsCtrlType);
//--- And convert key frame data
switch (cont_cid.PartA()) {
case TCBINTERP_ROTATION_CLASS_ID :
{
// TCB Rotation anim use relative keys in Max => Need to convert to absolute
// rotation for Virtools
Quat absoluteRotQuat;
Quat quatFromAngAxis;
absoluteRotQuat.Identity();
for (int i=0; i<numKeys; i++) {
// Get Max key
ITCBRotKey key;
ikc->GetKey(i, &key);
// Convert 2 Virtools format
if (key.val.angle<0) {
key.val.angle = -key.val.angle;
key.val.axis = -key.val.axis;
}
key.val.axis = Normalize(key.val.axis);
quatFromAngAxis=QFromAngAxis(key.val.angle,key.val.axis);
absoluteRotQuat*=(const Quat&)quatFromAngAxis;
Quat Val = RotateX*absoluteRotQuat;
// And create Virtools Key
CKTCBRotationKey VirtoolsKey(FrameTime(key.time),
VxQuaternion(-Val.x,-Val.z,-Val.y,Val.w),
key.tens,key.cont,key.bias,
key.easeIn,key.easeOut);
ctrl->AddKey(&VirtoolsKey);
}
} break;
case HYBRIDINTERP_ROTATION_CLASS_ID :
case LININTERP_ROTATION_CLASS_ID :
{
for (int i=0; i<numKeys; i++) {
ILinRotKey key;
ikc->GetKey(i, &key);
Quat Val = RotateX*key.val;
ctrl->AddKey(&CKRotationKey(FrameTime(key.time),
VxQuaternion(-Val.x,-Val.z,-Val.y,Val.w)));
}
} break;
}
}
/**********************************************************************************
Creates a Scale Controller on a CKObjectAnimation using
Max Controller key frame data
**********************************************************************************/
void Max2Nmo::DumpScaleKeys(Control* cont,CKObjectAnimation* anim)
{
if (!cont) return;
IKeyControl *ikc = GetKeyControlInterface(cont);
if (!ikc) return ;
int numKeys = ikc->GetNumKeys();
if (!numKeys) return ;
//----------- TCB position
if (cont->ClassID() == Class_ID(TCBINTERP_SCALE_CLASS_ID, 0)) {
CKAnimController* ctrl = anim->CreateController(CKANIMATION_TCBSCL_CONTROL);
for (int i=0; i<numKeys; i++) {
ITCBScaleKey key;
ikc->GetKey(i, &key);
CKTCBScaleKey VirtoolsKey(FrameTime(key.time),
VxVector(key.val.s.x,key.val.s.z,key.val.s.y),
key.tens,key.cont,key.bias,
key.easeIn,key.easeOut);
ctrl->AddKey(&VirtoolsKey);
}
}
//----------- Bezier position
else if (cont->ClassID() == Class_ID(HYBRIDINTERP_SCALE_CLASS_ID, 0))
{
CKAnimController* ctrl = anim->CreateController(CKANIMATION_BEZIERSCL_CONTROL);
for (int i=0; i<numKeys; i++) {
IBezScaleKey key;
ikc->GetKey(i, &key);
CKBezierKeyFlags Flags=MaxBezFlags2Virtools(key.flags);
float ScaleFactor = GetFrameRate()*16.0f/3.0f;
VxVector InTang(key.intan.x,key.intan.z,key.intan.y),OutTang(key.outtan.x,key.outtan.z,key.outtan.y);
InTang*=ScaleFactor;
OutTang*=ScaleFactor;
CKBezierScaleKey VirtoolsKey(FrameTime(key.time),
VxVector(key.val.s.x,key.val.s.z,key.val.s.y),
Flags,
InTang,OutTang);
ctrl->AddKey(&VirtoolsKey);
}
}
//----------- Linear position
else if (cont->ClassID() == Class_ID(LININTERP_SCALE_CLASS_ID, 0)) {
CKAnimController* ctrl = anim->CreateController(CKANIMATION_LINSCL_CONTROL);
for (int i=0; i<numKeys; i++) {
ILinScaleKey key;
ikc->GetKey(i, &key);
ctrl->AddKey(&CKScaleKey(FrameTime(key.time),
VxVector(key.val.s.x,key.val.s.z,key.val.s.y)));
}
}
}
//-- Not truly the correct way to compare floats of arbitary magnitude...
BOOL EqualPoint3(Point3 p1, Point3 p2)
{
if (fabs(p1.x - p2.x) > ALMOST_ZERO) return FALSE;
if (fabs(p1.y - p2.y) > ALMOST_ZERO) return FALSE;
if (fabs(p1.z - p2.z) > ALMOST_ZERO) return FALSE;
return TRUE;
}
BOOL EqualFloat(float v1, float v2)
{
if (fabs(v1 - v2) > ALMOST_ZERO) return FALSE;
return TRUE;
}
//--- Determine if a TM controller is known by the system.
BOOL Max2Nmo::IsKnownController(Control* cont)
{
ulong partA, partB;
if (!cont) return TRUE;
Class_ID cid = cont->ClassID();
#ifdef SUPPORT_NEW_CONTROLLERS
if (cid==IPOS_CONTROL_CLASS_ID)
return TRUE; //indepos
if (cid==IPOINT3_CONTROL_CLASS_ID)
return TRUE; //inderot, euler ?
if (cid==IPOINT4_CONTROL_CLASS_ID)
return TRUE; //inderot, quaternion ?
#endif
partA = cid.PartA();
partB = cid.PartB();
if (partB != 0x00) return FALSE;
switch (partA) {
case TCBINTERP_POSITION_CLASS_ID:
case TCBINTERP_ROTATION_CLASS_ID:
case TCBINTERP_SCALE_CLASS_ID:
case TCBINTERP_FLOAT_CLASS_ID:
case HYBRIDINTERP_POSITION_CLASS_ID:
case HYBRIDINTERP_ROTATION_CLASS_ID:
case HYBRIDINTERP_SCALE_CLASS_ID:
case HYBRIDINTERP_FLOAT_CLASS_ID:
case LININTERP_POSITION_CLASS_ID:
case LININTERP_ROTATION_CLASS_ID:
case LININTERP_SCALE_CLASS_ID:
case LININTERP_FLOAT_CLASS_ID:
return TRUE;
}
return FALSE;
}
BOOL Max2Nmo::DumpIndePos(INode* node,Control* cont,CKObjectAnimation* anim)
{
Class_ID cid = cont->ClassID();
if (cid!=IPOS_CONTROL_CLASS_ID)
return FALSE;
Tab<TimeValue> keyTimes;
TimeValue start = ip->GetAnimRange().Start();
TimeValue end = ip->GetAnimRange().End();
Interval range(start,end);
cont->GetKeyTimes(keyTimes,range,KEYAT_POSITION);
int tcount = keyTimes.Count();
if (tcount==0)
return TRUE;
//check subcontrolers
Control* subX = cont->GetXController();
Control* subY = cont->GetYController();
Control* subZ = cont->GetZController();
if (!subX || !subY || !subZ)
return FALSE;
Class_ID subX_cid = subX->ClassID();
Class_ID subY_cid = subY->ClassID();
Class_ID subZ_cid = subZ->ClassID();
// If this is not a standard controller we return
if (subX_cid.PartB() || subY_cid.PartB() || subZ_cid.PartB())
return FALSE;
// if different controler per axis, not supported
if (subX_cid.PartA()!=subY_cid.PartA() || subX_cid.PartA()!=subZ_cid.PartA() )
return FALSE;
IKeyControl *ikcX = GetKeyControlInterface(subX);
IKeyControl *ikcY = GetKeyControlInterface(subY);
IKeyControl *ikcZ = GetKeyControlInterface(subZ);
int ikcXIndex=0,ikcYIndex=0,ikcZIndex=0;
//--- Create the appropriate Virtools position controller
CKANIMATION_CONTROLLER VirtoolsCtrlType;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//POSITION KEYS
switch (subX_cid.PartA()) {
case TCBINTERP_FLOAT_CLASS_ID:
VirtoolsCtrlType = CKANIMATION_TCBPOS_CONTROL; break;
case HYBRIDINTERP_FLOAT_CLASS_ID:
VirtoolsCtrlType = CKANIMATION_BEZIERPOS_CONTROL; break;
case LININTERP_FLOAT_CLASS_ID:
VirtoolsCtrlType = CKANIMATION_LINPOS_CONTROL; break;
default : return FALSE;
}
//now we sample, coz we cannot use independant bezier / tcb
TimeValue t;
int delta = GetTicksPerFrame();// * GetKeyFrameStep();
CKAnimController* ctrl = anim->CreateController(VirtoolsCtrlType);
//browse times
for (int i=0;i<tcount;++i)
{
t = keyTimes[i];
Matrix3 tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t));
AffineParts ap;
decomp_affine(tm, &ap);
Point3 pos = ap.t;
switch(VirtoolsCtrlType)
{
case CKANIMATION_BEZIERPOS_CONTROL:
{
//watch previous & next frame
tm = node->GetNodeTM(t-delta) * Inverse(node->GetParentTM(t-delta));
decomp_affine(tm, &ap);
Point3 tan1 = pos-ap.t;
tm = node->GetNodeTM(t+delta) * Inverse(node->GetParentTM(t+delta));
decomp_affine(tm, &ap);
Point3 tan2 = ap.t-pos;
//float intan, outtan;
//must read key for bezier flags
IBezFloatKey keyX,keyY,keyZ;
DWORD flags=0;
ikcX->GetKey(ikcXIndex,&keyX);
ikcY->GetKey(ikcYIndex,&keyY);
ikcZ->GetKey(ikcZIndex,&keyZ);
if (keyX.time==t)
{
ikcXIndex++;
flags = keyX.flags;
tan1.x = keyX.intan;
tan2.x = keyX.outtan;
}
if (keyY.time==t)
{
ikcYIndex++;
flags = keyY.flags;
tan1.y = keyY.intan;
tan2.y = keyY.outtan;
}
if (keyZ.time==t)
{
ikcZIndex++;
flags = keyZ.flags;
tan1.z = keyZ.intan;
tan2.z = keyZ.outtan;
}//take only one flags, hope this is the good one then
// Convert Max Bezier tangent to Virtools format
CKBezierKeyFlags Flags=MaxBezFlags2Virtools(flags);
float ScaleFactor = GetFrameRate()*16.0f/3.0f;
VxVector InTang(tan1.x,tan1.z,tan1.y),OutTang(tan2.x,tan2.z,tan2.y);
InTang*=ScaleFactor;
OutTang*=ScaleFactor;
// Create Virtools Bezier Key
CKBezierPositionKey VirtoolsKey(FrameTime(t),
VxVector(pos.x,pos.z,pos.y),
Flags,
InTang,OutTang);
ctrl->AddKey(&VirtoolsKey);
}break;
case CKANIMATION_LINPOS_CONTROL:
{
ILinPoint3Key key;
ctrl->AddKey(&CKPositionKey(FrameTime(t),
VxVector(pos.x,pos.z,pos.y)));
}break;
case CKANIMATION_TCBPOS_CONTROL:
{
}break;
}
}
/* other way
IKeyControl *ikcX = GetKeyControlInterface(subX);
IKeyControl *ikcY = GetKeyControlInterface(subY);
IKeyControl *ikcZ = GetKeyControlInterface(subZ);
int xnum = subX->NumKeys();
int ynum = subY->NumKeys();
int znum = subZ->NumKeys();
*/
return TRUE;
}
BOOL Max2Nmo::DumpIndeRot(INode* node,Control* cont,CKObjectAnimation* anim)
{
/*
Class cid = cont->ClassID();
if (cid==IPOINT3_CONTROL_CLASS_ID)
{
}
else if (cid==IPOINT4_CONTROL_CLASS_ID)
{
}
*/
/*
HYBRIDINTERP_POINT4_CLASS_ID
TCBINTERP_POINT4_CLASS_ID
*/
return FALSE;
}