SBSPSS/Utils/GinExp/ExpAnimKeys.cpp
2001-01-12 22:40:39 +00:00

754 lines
23 KiB
C++

//**************************************************************************
//* Animout.cpp - Ascii File Exporter
//*
//* By Christer Janson
//* Kinetix Development
//*
//* January 20, 1997 CCJ Initial coding
//*
//* This module handles controller key output and controller sampling.
//*
//* Copyright (c) 1997, All Rights Reserved.
//***************************************************************************
#include "asciiexp.h"
#define ALMOST_ZERO 1.0e-3f
BOOL EqualPoint3(Point3 p1, Point3 p2);
/****************************************************************************
TM Animation output
****************************************************************************/
// Get hold of the transform controllers for the node...
void AsciiExp::ExportAnimKeys( INode* node )
{
BOOL bPosAnim;
BOOL bRotAnim;
BOOL bScaleAnim;
BOOL bDoKeys = FALSE;
// We can only export keys if all TM controllers are "known" to us.
// The reason for that is that some controllers control more than what
// they should. Consider a path position controller, if you turn on
// follow and banking, this position controller will also control
// rotation. If a node that had a path position controller also had a
// TCB rotation controller, the TCB keys would not describe the whole
// rotation of the node.
// For that reason we will only export keys if all controllers
// position, rotation and scale are linear, hybrid (bezier) or tcb.
/* if (!GetAlwaysSample())
{
Control* pC = node->GetTMController()->GetPositionController();
Control* rC = node->GetTMController()->GetRotationController();
Control* sC = node->GetTMController()->GetScaleController();
if (IsKnownController(pC) && IsKnownController(rC) && IsKnownController(sC))
{
bDoKeys = TRUE;
}
}
*/
if (bDoKeys)
{
// Only dump the track header if any of the controllers have keys
if (node->GetTMController()->GetPositionController()->NumKeys() ||
node->GetTMController()->GetRotationController()->NumKeys() ||
node->GetTMController()->GetScaleController()->NumKeys())
{
// fprintf(pStream,"%s\t%s {\n", indent.data(), ID_TM_ANIMATION);
// fprintf(pStream,"%s\t\t%s \"%s\"\n", indent.data(), ID_NODE_NAME, FixupName(node->GetName()));
DumpPosKeys(node->GetTMController()->GetPositionController());
DumpRotKeys(node->GetTMController()->GetRotationController());
DumpScaleKeys(node->GetTMController()->GetScaleController());
// fprintf(pStream,"%s\t}\n", indent.data());
}
}
else if (CheckForAnimation(node, bPosAnim, bRotAnim, bScaleAnim))
{
// fprintf(pStream,"%s\t%s {\n", indent.data(), ID_TM_ANIMATION);
// fprintf(pStream,"%s\t\t%s \"%s\"\n", indent.data(), ID_NODE_NAME, FixupName(node->GetName()));
if (bPosAnim) DumpPosSample(node);
if (bRotAnim) DumpRotSample(node);
if (bScaleAnim) DumpScaleSample(node);
// fprintf(pStream,"%s\t}\n", indent.data());
}
}
// 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.
//
// Some controllers makes it problematic to examine the TMContollers instead of
// the actual TMMatrix. For example, a path controller is a position controller,
// but if you turn on follow and banking, it will also affect the rotation component.
// If we want to, we can examine the position, rotation and scale controllers and
// if they all are Linear, Hybrid (bezier) or TCB, then we could export the actual keys.
// This is not at all difficult, but the importer has to know the exact interpolation
// algorithm in order to use it. The source code to the interpolation routines are available
// to ADN members.
//
// For an example of how to export actual keys, look at DumpPoint3Keys() below.
// This method will check the actual controller to determine if the controller is known.
// If we know how to work this controller, its actual keys will be exported,
// otherwise the controller will be sampled using the user specified sampling frequency.
BOOL AsciiExp::CheckForAnimation(INode* node, BOOL& bPos, BOOL& bRot, BOOL& bScale)
{
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;
Point3 firstScaleFactor;
bPos = bRot = bScale = 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);
if (t != start)
{
// We examine the rotation angle to see if the rotation component
// has changed.
// Although not entierly true, it should work.
// It is rare that the rotation axis is animated without
// the rotation angle being somewhat affected.
bPos = TRUE;
bRot = TRUE;
bScale = TRUE;
// if (!EqualPoint3(ap.t, firstPos)) bPos = TRUE;
// if (fabs(rotAngle - firstRotAngle) > ALMOST_ZERO) bRot = TRUE;
// if (!EqualPoint3(ap.k, firstScaleFactor)) bScale = TRUE;
}
else
{
firstPos = ap.t;
firstRotAngle = rotAngle;
firstScaleFactor = ap.k;
}
// No need to continue looping if all components are animated
if (bPos && bRot && bScale) break;
}
return bPos || bRot || bScale;
}
void AsciiExp::DumpPosQuatSample(INode* node)
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_POS_TRACK);
TimeValue start = ip->GetAnimRange().Start();
TimeValue end = ip->GetAnimRange().End();
TimeValue t;
int delta = GetTicksPerFrame();
Matrix3 tm;
AffineParts ap;
Point3 prevPos;
Quat prevQ;
Quat q;
char name[256];
sprintf( name, "%s", node->GetName() );
fprintf( tempStream, "OUTPUTTING - %s\n", name ); // DEBUG FILE
// fwrite( &name, sizeof(char), NAME_LENGTH, boneStream ); // WRITE BONE NAME
prevQ.Identity();
for (t=start; t<=end; t+=delta)
{
// TRANSLATION
tm = node->GetNodeTM(t);// * Inverse(node->GetParentTM(t));
decomp_affine(tm, &ap);
fwrite( &ap.t.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS
fwrite( &ap.t.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS
fwrite( &ap.t.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS
fwrite( &ap.q.x, sizeof(float), 1, expStream ); // WRITE BONE ROT X-AXIS
fwrite( &ap.q.z, sizeof(float), 1, expStream ); // WRITE BONE ROT Y-AXIS
fwrite( &ap.q.y, sizeof(float), 1, expStream ); // WRITE BONE ROT Z-AXIS
fwrite( &ap.q.w, sizeof(float), 1, expStream ); // WRITE BONE ROT W ANGLE
}
}
void AsciiExp::DumpPosSample(INode* node)
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_POS_TRACK);
TimeValue start = ip->GetAnimRange().Start();
TimeValue end = ip->GetAnimRange().End();
TimeValue t;
int delta = GetTicksPerFrame();
Matrix3 tm;
AffineParts ap;
Point3 prevPos;
Quat prevQ;
Quat q;
char name[256];
sprintf( name, "%s", node->GetName() );
fprintf( tempStream, "OUTPUTTING - %s\n", name ); // DEBUG FILE
// fwrite( &name, sizeof(char), NAME_LENGTH, boneStream ); // WRITE BONE NAME
prevQ.Identity();
for (t=start; t<=end; t+=delta)
{
// TRANSLATION
tm = node->GetNodeTM(t);// * Inverse(node->GetParentTM(t));
decomp_affine(tm, &ap);
Point3 fpos;
Point3 pos = ap.t;
prevPos = pos;
fpos.x = pos.x;
fpos.y = pos.z;
fpos.z = -pos.y;
fprintf( tempStream, " POS = %f %f %f\n", pos.x, pos.z, -pos.y );
// fwrite( &fpos.x, sizeof(float), 1, boneStream ); // WRITE BONE X-POS
// fwrite( &fpos.y, sizeof(float), 1, boneStream ); // WRITE BONE Y-POS
// fwrite( &fpos.z, sizeof(float), 1, boneStream ); // WRITE BONE Z-POS
// ROTATION
tm = node->GetNodeTM(t);// * Inverse(node->GetParentTM(t));
decomp_affine(tm, &ap);
if (t == start)
{
q = ap.q;
prevQ = ap.q; // SAVE BASE ROTATION, THEN USE THIS AS ROTATION ORIGIN
}
else
{
q = ap.q / prevQ;
}
fprintf( tempStream, " QUAT = %f %f %f %f\n", q.x, q.y, q.z, q.w );
// fwrite( &q.x, sizeof(float), 1, boneStream ); // WRITE BONE ROT X-AXIS
// fwrite( &q.y, sizeof(float), 1, boneStream ); // WRITE BONE ROT Y-AXIS
// fwrite( &q.z, sizeof(float), 1, boneStream ); // WRITE BONE ROT Z-AXIS
// fwrite( &q.w, sizeof(float), 1, boneStream ); // WRITE BONE ROT W ANGLE
}
}
void AsciiExp::DumpRotSample(INode* node)
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_ROT_TRACK);
TimeValue start = ip->GetAnimRange().Start();
TimeValue end = ip->GetAnimRange().End();
TimeValue t;
int delta = GetTicksPerFrame();
Matrix3 tm;
AffineParts ap;
Quat prevQ;
prevQ.Identity();
for (t=start; t<=end; t+=delta)
{
tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t));
decomp_affine(tm, &ap);
// Rotation keys should be relative, so we need to convert these
// absolute samples to relative values.
Quat q = ap.q / prevQ;
prevQ = ap.q;
// No point in exporting null keys...
// if (q.IsIdentity()) continue;
// Output the sample
fprintf( tempStream, " QUAT = %f %f %f %f\n", q.x, q.y, q.z, q.w );
// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_ROT_SAMPLE, t, Format(q));
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
void AsciiExp::DumpScaleSample(INode* node)
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_SCALE_TRACK);
TimeValue start = ip->GetAnimRange().Start();
TimeValue end = ip->GetAnimRange().End();
TimeValue t;
int delta = GetTicksPerFrame();
Matrix3 tm;
AffineParts ap;
Point3 prevFac;
for (t=start; t<=end; t+=delta)
{
tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t));
decomp_affine(tm, &ap);
// Skip identical keys
if (t!= start && EqualPoint3(ap.k, prevFac)) continue;
prevFac = ap.k;
// Output the sample
// fprintf(pStream, "%s\t\t\t%s %d\t%s %s\n", indent.data(), ID_SCALE_SAMPLE, t, Format(ap.k), Format(ap.u));
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
// Output point3 keys if this is a known point3 controller that
// supports key operations. Otherwise we will sample the controller
// once for each frame to get the value.
// Point3 controllers can control, for example, color.
void AsciiExp::DumpPoint3Keys(Control* cont)
{
// Bug out if no controller.
if (!cont) return;
int i;
IKeyControl *ikc = NULL;
// If the user wants us to always sample, we will ignore the KeyControlInterface
// if (!GetAlwaysSample()) ikc = GetKeyControlInterface(cont);
// TCB point3
if (ikc && cont->ClassID() == Class_ID(TCBINTERP_POINT3_CLASS_ID, 0))
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_POINT3_TCB);
for (i=0; i<ikc->GetNumKeys(); i++)
{
ITCBPoint3Key key;
ikc->GetKey(i, &key);
fprintf( tempStream, " KEYPOS = %f %f %f\n", key.val.x, key.val.y, key.val.z );
// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_TCB_POINT3_KEY, key.time, Format(key.val));
// Add TCB specific data
// fprintf(pStream, "\t%s\t%s\t%s\t%s\t%s\n", Format(key.tens), Format(key.cont), Format(key.bias), Format(key.easeIn), Format(key.easeOut));
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
// Bezier point3
else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_POINT3_CLASS_ID, 0))
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_POINT3_BEZIER);
for (i=0; i<ikc->GetNumKeys(); i++)
{
IBezPoint3Key key;
ikc->GetKey(i, &key);
fprintf( tempStream, " KEYPOS = %f %f %f\n", key.val.x, key.val.y, key.val.z );
// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_BEZIER_POINT3_KEY, key.time, Format(key.val));
// fprintf(pStream, "\t%s\t%s\t%d\n", Format(key.intan), Format(key.outtan), key.flags);
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
// Bezier color
else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_COLOR_CLASS_ID, 0))
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_COLOR_BEZIER);
for (i=0; i<ikc->GetNumKeys(); i++)
{
IBezPoint3Key key;
ikc->GetKey(i, &key);
fprintf( tempStream, " KEYPOS = %f %f %f\n", key.val.x, key.val.y, key.val.z );
// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_BEZIER_POINT3_KEY, key.time, Format(key.val));
// fprintf(pStream, "\t%s\t%s\t%d\n", Format(key.intan), Format(key.outtan), key.flags);
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
else
{
// Unknown controller, no key interface or sample on demand -
// This might be a procedural controller or something else we
// don't know about. The last resort is to get the value from the
// controller at every n frames.
TSTR name;
cont->GetClassName(name);
// fprintf(pStream,"%s\t\t%s \"%s\" {\n", indent.data(), ID_CONTROL_POINT3_SAMPLE, FixupName(name));
// If it is animated at all...
if (cont->IsAnimated())
{
// Get the range of the controller animation
Interval range;
// Get range of full animation
Interval animRange = ip->GetAnimRange();
TimeValue t = cont->GetTimeRange(TIMERANGE_ALL).Start();
Point3 value;
// While we are inside the animation...
while (animRange.InInterval(t))
{
// Sample the controller
range = FOREVER;
cont->GetValue(t, &value, range);
// Set time to start of controller validity interval
t = range.Start();
// Output the sample
fprintf( tempStream, " KEYPOS = %f %f %f\n", value.x, value.y, value.z );
// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_POINT3_KEY, t, Format(value));
// If the end of the controller validity is beyond the
// range of the animation
if (range.End() > cont->GetTimeRange(TIMERANGE_ALL).End())
{
break;
}
else
{
t = (range.End()/GetTicksPerFrame()+1) * GetTicksPerFrame();
}
}
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
}
// Output float keys if this is a known float controller that
// supports key operations. Otherwise we will sample the controller
// once for each frame to get the value.
void AsciiExp::DumpFloatKeys(Control* cont)
{
if (!cont) return;
int i;
IKeyControl *ikc = NULL;
// If the user wants us to always sample, we will ignore the KeyControlInterface
// if (!GetAlwaysSample()) ikc = GetKeyControlInterface(cont);
// TCB float
if (ikc && cont->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID, 0))
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_FLOAT_TCB);
for (i=0; i<ikc->GetNumKeys(); i++)
{
ITCBFloatKey key;
ikc->GetKey(i, &key);
// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_TCB_FLOAT_KEY, key.time, Format(key.val));
// fprintf(pStream, "\t%s\t%s\t%s\t%s\t%s\n", Format(key.tens), Format(key.cont), Format(key.bias), Format(key.easeIn), Format(key.easeOut));
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
// Bezier float
else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID, 0))
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_FLOAT_BEZIER);
for (i=0; i<ikc->GetNumKeys(); i++)
{
IBezFloatKey key;
ikc->GetKey(i, &key);
// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_BEZIER_FLOAT_KEY, key.time, Format(key.val));
// fprintf(pStream, "\t%s\t%s\t%d\n", Format(key.intan), Format(key.outtan), key.flags);
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
else if (ikc && cont->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID, 0))
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_FLOAT_LINEAR);
for (i=0; i<ikc->GetNumKeys(); i++)
{
ILinFloatKey key;
ikc->GetKey(i, &key);
// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_FLOAT_KEY, key.time, Format(key.val));
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
else
{
// Unknown controller, no key interface or sample on demand -
// This might be a procedural controller or something else we
// don't know about. The last resort is to get the value from the
// controller at every n frames.
TSTR name;
cont->GetClassName(name);
// fprintf(pStream,"%s\t\t%s \"%s\" {\n", indent.data(), ID_CONTROL_FLOAT_SAMPLE, FixupName(name));
// If it is animated at all...
if (cont->IsAnimated())
{
// Get the range of the controller animation
Interval range;
// Get range of full animation
Interval animRange = ip->GetAnimRange();
TimeValue t = cont->GetTimeRange(TIMERANGE_ALL).Start();
float value;
// While we are inside the animation...
while (animRange.InInterval(t))
{
// Sample the controller
range = FOREVER;
cont->GetValue(t, &value, range);
// Set time to start of controller validity interval
t = range.Start();
// Output the sample
// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_FLOAT_KEY, t, Format(value));
// If the end of the controller validity is beyond the
// range of the animation
if (range.End() > cont->GetTimeRange(TIMERANGE_ALL).End())
{
break;
}
else
{
t = (range.End()/GetTicksPerFrame()+1) * GetTicksPerFrame();
}
}
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
}
void AsciiExp::DumpPosKeys(Control* cont)
{
if (!cont) return;
int i;
IKeyControl *ikc = GetKeyControlInterface(cont);
// TCB position
if (ikc && cont->ClassID() == Class_ID(TCBINTERP_POSITION_CLASS_ID, 0))
{
int numKeys;
if (numKeys = ikc->GetNumKeys())
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_POS_TCB);
for (i=0; i<numKeys; i++)
{
ITCBPoint3Key key;
ikc->GetKey(i, &key);
// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_TCB_POS_KEY, key.time, Format(key.val));
// fprintf(pStream, "\t%s\t%s\t%s\t%s\t%s\n", Format(key.tens), Format(key.cont), Format(key.bias), Format(key.easeIn), Format(key.easeOut));
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
}
// Bezier position
else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_POSITION_CLASS_ID, 0))
{
int numKeys;
if(numKeys = ikc->GetNumKeys())
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_POS_BEZIER);
for (i=0; i<numKeys; i++)
{
IBezPoint3Key key;
ikc->GetKey(i, &key);
// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_BEZIER_POS_KEY, key.time, Format(key.val));
// fprintf(pStream, "\t%s\t%s\t%d\n", Format(key.intan), Format(key.outtan), key.flags);
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
}
// Linear position
else if (ikc && cont->ClassID() == Class_ID(LININTERP_POSITION_CLASS_ID, 0))
{
int numKeys;
if(numKeys = ikc->GetNumKeys())
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_POS_LINEAR);
for (i=0; i<numKeys; i++)
{
ILinPoint3Key key;
ikc->GetKey(i, &key);
// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_POS_KEY, key.time, Format(key.val));
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
}
}
void AsciiExp::DumpRotKeys(Control* cont)
{
if (!cont) return;
int i;
IKeyControl *ikc = GetKeyControlInterface(cont);
if (ikc && cont->ClassID() == Class_ID(TCBINTERP_ROTATION_CLASS_ID, 0))
{
int numKeys;
if (numKeys = ikc->GetNumKeys())
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_ROT_TCB);
for (i=0; i<numKeys; i++)
{
ITCBRotKey key;
ikc->GetKey(i, &key);
// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_TCB_ROT_KEY, key.time, Format(key.val));
// fprintf(pStream, "\t%s\t%s\t%s\t%s\t%s\n", Format(key.tens), Format(key.cont), Format(key.bias), Format(key.easeIn), Format(key.easeOut));
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
}
else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0))
{
int numKeys;
if (numKeys = ikc->GetNumKeys())
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_ROT_BEZIER);
for (i=0; i<numKeys; i++)
{
IBezQuatKey key;
ikc->GetKey(i, &key);
// Quaternions are converted to AngAxis when written to file
// There is no intan/outtan for Quat Rotations
// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_ROT_KEY, key.time, Format(key.val));
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
}
else if (ikc && cont->ClassID() == Class_ID(LININTERP_ROTATION_CLASS_ID, 0))
{
int numKeys;
if (numKeys = ikc->GetNumKeys())
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_ROT_LINEAR);
for (i=0; i<numKeys; i++)
{
ILinRotKey key;
ikc->GetKey(i, &key);
// Quaternions are converted to AngAxis when written to file
// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_ROT_KEY, key.time, Format(key.val));
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
}
}
void AsciiExp::DumpScaleKeys(Control* cont)
{
if (!cont) return;
int i;
IKeyControl *ikc = GetKeyControlInterface(cont);
if (ikc && cont->ClassID() == Class_ID(TCBINTERP_SCALE_CLASS_ID, 0))
{
int numKeys;
if (numKeys = ikc->GetNumKeys())
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_SCALE_TCB);
for (i=0; i<numKeys; i++)
{
ITCBScaleKey key;
ikc->GetKey(i, &key);
// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_TCB_SCALE_KEY, key.time, Format(key.val));
// fprintf(pStream, "\t%s\t%s\t%s\t%s\t%s\n", Format(key.tens), Format(key.cont), Format(key.bias), Format(key.easeIn), Format(key.easeOut));
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
}
else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_SCALE_CLASS_ID, 0))
{
int numKeys;
if (numKeys = ikc->GetNumKeys())
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_SCALE_BEZIER);
for (i=0; i<numKeys; i++)
{
IBezScaleKey key;
ikc->GetKey(i, &key);
// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_BEZIER_SCALE_KEY, key.time, Format(key.val));
// fprintf(pStream, "\t%s\t%s\t%d\n", Format(key.intan), Format(key.outtan), key.flags);
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
}
else if (ikc && cont->ClassID() == Class_ID(LININTERP_SCALE_CLASS_ID, 0))
{
int numKeys;
if (numKeys = ikc->GetNumKeys())
{
// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_SCALE_LINEAR);
for (i=0; i<numKeys; i++)
{
ILinScaleKey key;
ikc->GetKey(i, &key);
// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_SCALE_KEY, key.time, Format(key.val));
}
// fprintf(pStream,"%s\t\t}\n", indent.data());
}
}
}
// 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;
}
// Determine if a TM controller is known by the system.
AsciiExp::IsKnownController(Control* cont)
{
ulong partA, partB;
if (!cont) return FALSE;
partA = cont->ClassID().PartA();
partB = cont->ClassID().PartB();
if (partB != 0x00) return FALSE;
switch (partA)
{
case TCBINTERP_POSITION_CLASS_ID:
case TCBINTERP_ROTATION_CLASS_ID:
case TCBINTERP_SCALE_CLASS_ID:
case HYBRIDINTERP_POSITION_CLASS_ID:
case HYBRIDINTERP_ROTATION_CLASS_ID:
case HYBRIDINTERP_SCALE_CLASS_ID:
case LININTERP_POSITION_CLASS_ID:
case LININTERP_ROTATION_CLASS_ID:
case LININTERP_SCALE_CLASS_ID:
return TRUE;
}
return FALSE;
}