855 lines
19 KiB
C++
855 lines
19 KiB
C++
/*
|
|
** Command & Conquer Renegade(tm)
|
|
** Copyright 2025 Electronic Arts 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/>.
|
|
*/
|
|
|
|
//----------------------------------------------------------------------------
|
|
// amc_import.cpp
|
|
//
|
|
// Acclaim Motion Capture import module
|
|
//
|
|
// James McNeill
|
|
//
|
|
// Created October 1996
|
|
//
|
|
// Copyright (c) 1996 Westwood Studios
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <Max.h>
|
|
#include <istdplug.h>
|
|
#include <fstream.h>
|
|
|
|
#include "resource.h"
|
|
#include "exception.h"
|
|
#include "asf_data.h"
|
|
|
|
const float ANGLE_MULTIPLIER = 1.7453293e-2f; // degrees to radians
|
|
|
|
const first_frame_time = 320;
|
|
const amc_ticks_per_frame = 80;
|
|
static int max_ticks_per_frame = 160;
|
|
|
|
static HINSTANCE hInstance;
|
|
|
|
static TCHAR * GetString ( int id )
|
|
{
|
|
static TCHAR buf[256];
|
|
if (hInstance)
|
|
return LoadString(hInstance, id, buf, sizeof(buf)) ? buf : NULL;
|
|
return NULL;
|
|
}
|
|
|
|
static int MessageBox ( int s1, int s2, int option = MB_OK )
|
|
{
|
|
TSTR str1(GetString(s1));
|
|
TSTR str2(GetString(s2));
|
|
return MessageBox(GetActiveWindow(), str1, str2, option);
|
|
}
|
|
|
|
static int Alert ( int s1, int s2 = IDS_LIB_SHORT_DESC, int option = MB_OK )
|
|
{
|
|
return MessageBox(s1, s2, option);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// AMC_Import
|
|
//----------------------------------------------------------------------------
|
|
|
|
class AMC_Import : public SceneImport
|
|
{
|
|
public:
|
|
|
|
AMC_Import();
|
|
~AMC_Import();
|
|
|
|
int ExtCount(); // Number of extensions supported
|
|
const TCHAR * Ext(int n); // Extension #n
|
|
const TCHAR * LongDesc(); // Long ASCII description
|
|
const TCHAR * ShortDesc(); // Short ASCII description
|
|
const TCHAR * AuthorName(); // ASCII Author name
|
|
const TCHAR * CopyrightMessage(); // ASCII Copyright message
|
|
const TCHAR * OtherMessage1(); // Other message #1
|
|
const TCHAR * OtherMessage2(); // Other message #2
|
|
unsigned int Version(); // Version number * 100
|
|
void ShowAbout(HWND); // Show DLL's "About..." box
|
|
|
|
int DoImport
|
|
(
|
|
const TCHAR * name,
|
|
ImpInterface * i,
|
|
Interface * gi,
|
|
BOOL suppressPrompts
|
|
);
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
// DllMain
|
|
//----------------------------------------------------------------------------
|
|
|
|
static int controlsInit = FALSE;
|
|
|
|
BOOL WINAPI DllMain
|
|
(
|
|
HINSTANCE hinstDLL,
|
|
ULONG fdwReason,
|
|
LPVOID lpvReserved
|
|
)
|
|
{
|
|
hInstance = hinstDLL;
|
|
|
|
if ( !controlsInit )
|
|
{
|
|
controlsInit = TRUE;
|
|
InitCustomControls(hInstance);
|
|
InitCommonControls();
|
|
}
|
|
switch(fdwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH: break;
|
|
case DLL_THREAD_ATTACH: break;
|
|
case DLL_THREAD_DETACH: break;
|
|
case DLL_PROCESS_DETACH: break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// AMC_ClassDesc
|
|
//----------------------------------------------------------------------------
|
|
|
|
class AMC_ClassDesc : public ClassDesc
|
|
{
|
|
public:
|
|
int IsPublic() { return 1; }
|
|
void * Create(BOOL loading = FALSE) { return new AMC_Import; }
|
|
const TCHAR * ClassName() { return GetString(IDS_SHORT_DESC); }
|
|
SClass_ID SuperClassID() { return SCENE_IMPORT_CLASS_ID; }
|
|
Class_ID ClassID() { return Class_ID(0x5be11422, 0x6e0177f0); }
|
|
const TCHAR* Category() { return GetString(IDS_CATEGORY); }
|
|
};
|
|
|
|
static AMC_ClassDesc AMC_desc;
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This is the interface to Jaguar:
|
|
//----------------------------------------------------------------------------
|
|
|
|
__declspec( dllexport ) const TCHAR * LibDescription()
|
|
{
|
|
return GetString(IDS_LIB_LONG_DESC);
|
|
}
|
|
|
|
__declspec( dllexport ) int LibNumberClasses()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
__declspec( dllexport ) ClassDesc * LibClassDesc(int i)
|
|
{
|
|
switch(i)
|
|
{
|
|
case 0: return & AMC_desc; break;
|
|
default: return 0; break;
|
|
}
|
|
}
|
|
|
|
// Return version so can detect obsolete DLLs
|
|
__declspec( dllexport ) ULONG LibVersion()
|
|
{
|
|
return VERSION_3DSMAX;
|
|
}
|
|
|
|
//
|
|
// ASF import module functions follow:
|
|
//
|
|
|
|
AMC_Import::AMC_Import() {}
|
|
AMC_Import::~AMC_Import() {}
|
|
|
|
int AMC_Import::ExtCount()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
// Extensions supported for import/export modules
|
|
|
|
const TCHAR * AMC_Import::Ext(int n)
|
|
{
|
|
return _T("AMC");
|
|
}
|
|
|
|
const TCHAR * AMC_Import::LongDesc()
|
|
{
|
|
return GetString(IDS_LONG_DESC);
|
|
}
|
|
|
|
const TCHAR * AMC_Import::ShortDesc()
|
|
{
|
|
return GetString(IDS_SHORT_DESC);
|
|
}
|
|
|
|
const TCHAR * AMC_Import::AuthorName()
|
|
{
|
|
return GetString(IDS_AUTHOR_NAME);
|
|
}
|
|
|
|
const TCHAR * AMC_Import::CopyrightMessage()
|
|
{
|
|
return GetString(IDS_COPYRIGHT);
|
|
}
|
|
|
|
const TCHAR * AMC_Import::OtherMessage1()
|
|
{
|
|
return _T("");
|
|
}
|
|
|
|
const TCHAR * AMC_Import::OtherMessage2()
|
|
{
|
|
return _T("");
|
|
}
|
|
|
|
unsigned int AMC_Import::Version()
|
|
{
|
|
return 100;
|
|
}
|
|
|
|
void AMC_Import::ShowAbout(HWND hWnd)
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Key_Class
|
|
//----------------------------------------------------------------------------
|
|
|
|
struct Key_Class
|
|
{
|
|
TimeValue Time;
|
|
Quat Orientation;
|
|
Point3 Position;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Bone
|
|
//----------------------------------------------------------------------------
|
|
|
|
class Bone
|
|
{
|
|
public:
|
|
|
|
Bone ( const char * name, Bone * next )
|
|
{
|
|
Name = new char [ strlen (name) + 1 ];
|
|
strcpy ( Name, name );
|
|
Next = next;
|
|
Max_keys = 1;
|
|
Number_of_keys = 0;
|
|
Key = NULL;
|
|
}
|
|
|
|
~Bone ()
|
|
{
|
|
delete [] Name;
|
|
if ( Key != NULL )
|
|
delete [] Key;
|
|
}
|
|
|
|
void alloc_key_space ( Interface * gi )
|
|
{
|
|
if ( Key == NULL && Max_keys > 0 )
|
|
Key = new Key_Class [ Max_keys ];
|
|
|
|
// Find the bone's I-node.
|
|
// TODO: check to ensure we got the inode!
|
|
INode * inode = gi->GetINodeByName ( Name );
|
|
|
|
if (inode) {
|
|
// Add a key at frame zero to hold the original position.
|
|
|
|
Quat rotation ( 0.0f, 0.0f, 0.0f, 1.0f );
|
|
|
|
Matrix3 localTM = inode->GetNodeTM (0) *
|
|
Inverse (inode->GetParentTM (0));
|
|
rotation *= localTM;
|
|
|
|
add_key ( 0, rotation, Point3 (0,0,0) );
|
|
}
|
|
}
|
|
|
|
void inc_max_keys () { ++ Max_keys; }
|
|
|
|
void add_key ( TimeValue time, const Quat & rot, const Point3 & pos )
|
|
{
|
|
if ( Key != NULL && Number_of_keys < Max_keys )
|
|
{
|
|
Key [Number_of_keys].Time = time;
|
|
Key [Number_of_keys].Orientation = rot;
|
|
Key [Number_of_keys].Position = pos;
|
|
|
|
++ Number_of_keys;
|
|
}
|
|
}
|
|
|
|
void put_keys_into_max ( ImpInterface * imp_i, Interface * i );
|
|
|
|
Bone * next () const { return Next; }
|
|
char * name () const { return Name; }
|
|
|
|
protected:
|
|
|
|
char * Name;
|
|
Bone * Next;
|
|
unsigned Max_keys;
|
|
unsigned Number_of_keys;
|
|
Key_Class * Key;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Bone::put_keys_into_max
|
|
//----------------------------------------------------------------------------
|
|
|
|
void Bone::put_keys_into_max ( ImpInterface * imp_i, Interface * i )
|
|
{
|
|
unsigned k;
|
|
|
|
// Find the bone's I-node.
|
|
|
|
INode * inode = i->GetINodeByName ( Name );
|
|
if ( inode == NULL )
|
|
return;
|
|
|
|
// Make each quaternion be on the same side of the hypersphere as its
|
|
// predecessor.
|
|
|
|
for ( k = 1; k < Number_of_keys; ++ k )
|
|
{
|
|
Key [k].Orientation.MakeClosest ( Key [k-1].Orientation );
|
|
}
|
|
|
|
// Make each rotation relative to the previous one.
|
|
|
|
if ( Number_of_keys > 1 )
|
|
{
|
|
for ( unsigned k = Number_of_keys - 1; k > 0; -- k )
|
|
{
|
|
Key [k].Orientation = Key [k].Orientation / Key [k-1].Orientation;
|
|
}
|
|
}
|
|
|
|
// Add the keys to the bone's rotation controller.
|
|
|
|
// Replace the rotation controller if it is not the right type.
|
|
|
|
Control * c = inode->GetTMController ()->GetRotationController ();
|
|
|
|
if ( c && c->ClassID () != Class_ID (TCBINTERP_ROTATION_CLASS_ID, 0) )
|
|
{
|
|
Control *tcb = (Control*) i->CreateInstance ( CTRL_ROTATION_CLASS_ID,
|
|
Class_ID (TCBINTERP_ROTATION_CLASS_ID,0) );
|
|
if ( ! inode->GetTMController ()->SetRotationController(tcb) )
|
|
{
|
|
tcb->DeleteThis();
|
|
}
|
|
}
|
|
|
|
c = inode->GetTMController ()->GetRotationController ();
|
|
// c->SetORT ( ORT_LOOP, ORT_BEFORE );
|
|
// c->SetORT ( ORT_LOOP, ORT_AFTER );
|
|
|
|
IKeyControl * keys = GetKeyControlInterface ( c );
|
|
|
|
keys->SetNumKeys ( Number_of_keys );
|
|
|
|
// Create keys.
|
|
|
|
for ( k = 0; k < Number_of_keys; ++ k )
|
|
{
|
|
ITCBRotKey key;
|
|
key.tens = 0.0f;
|
|
key.cont = 0.0f;
|
|
key.bias = 0.0f;
|
|
key.easeIn = 0.0f;
|
|
key.easeOut = 0.0f;
|
|
|
|
key.time = Key [k].Time;
|
|
key.val = (AngAxis) Key [k].Orientation;
|
|
|
|
keys->SetKey ( k, & key );
|
|
}
|
|
|
|
keys->SortKeys ();
|
|
|
|
// &&& This is a hack; add position keys for the root node.
|
|
// Another hack: The positions are multiplied by a scale factor which
|
|
// should be derived from the ASF file.
|
|
float length_multiplier;
|
|
|
|
if ( strcmp ( Name, "root" ) == 0 )
|
|
{
|
|
// Since this is the root node, check for an AppData chunk defining
|
|
// the length multiplier.
|
|
// This "length multiplier" will convert whatever numbers are in the file
|
|
// into *inches*. We later convert this to whatever units Max is using...
|
|
AppDataChunk * chunk = inode->GetAppDataChunk
|
|
(
|
|
Class_ID(0x74975aa6, 0x1810323f),
|
|
SCENE_IMPORT_CLASS_ID,
|
|
2
|
|
);
|
|
|
|
if ( chunk == NULL )
|
|
{
|
|
// (gth) HACK HACK HACK! For some time, I had removed this 'length_multiplier'
|
|
// Many commando animations were created without the multiplier which was 1.0 / 0.0254
|
|
// so, shove that multiplier in if one wasn't found... This only comes into play
|
|
// when loading a motion into a max file that use the old importer to create the
|
|
// base pose.
|
|
length_multiplier = 1.0f / 0.0254f;
|
|
}
|
|
else
|
|
{
|
|
Position_Key_Scale_Chunk * data_p = (Position_Key_Scale_Chunk *) chunk->data;
|
|
length_multiplier = data_p->Position_Key_Scale;
|
|
}
|
|
|
|
// Account for the current units setting of Max (e.g. 1 unit = 1 Meter)
|
|
length_multiplier /= (float)GetMasterScale(UNITS_INCHES);
|
|
|
|
// Make sure we have the right kind of controller.
|
|
|
|
c = inode->GetTMController ()->GetPositionController ();
|
|
|
|
if ( c && c->ClassID () != Class_ID (TCBINTERP_POSITION_CLASS_ID, 0) )
|
|
{
|
|
Control *tcb = (Control*) i->CreateInstance ( CTRL_POSITION_CLASS_ID,
|
|
Class_ID (TCBINTERP_POSITION_CLASS_ID,0) );
|
|
if ( ! inode->GetTMController ()->SetPositionController(tcb) )
|
|
{
|
|
tcb->DeleteThis();
|
|
}
|
|
}
|
|
|
|
c = inode->GetTMController ()->GetPositionController ();
|
|
// c->SetORT ( ORT_LOOP, ORT_BEFORE );
|
|
// c->SetORT ( ORT_LOOP, ORT_AFTER );
|
|
|
|
keys = GetKeyControlInterface ( c );
|
|
|
|
keys->SetNumKeys ( Number_of_keys );
|
|
|
|
// Create keys.
|
|
|
|
for ( k = 0; k < Number_of_keys; ++ k )
|
|
{
|
|
ITCBPoint3Key key;
|
|
key.tens = 0.0f;
|
|
key.cont = 0.0f;
|
|
key.bias = 0.0f;
|
|
key.easeIn = 0.0f;
|
|
key.easeOut = 0.0f;
|
|
|
|
key.time = Key [k].Time;
|
|
key.val = Key [k].Position * length_multiplier;
|
|
|
|
keys->SetKey ( k, & key );
|
|
}
|
|
|
|
keys->SortKeys ();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Key_Manager
|
|
//----------------------------------------------------------------------------
|
|
|
|
class Key_Manager
|
|
{
|
|
public:
|
|
|
|
Key_Manager () { Bones = NULL; }
|
|
~Key_Manager ();
|
|
|
|
// Call this first to increment the maximum number of keys for a given
|
|
// bone.
|
|
|
|
void inc_max_keys ( const char * bone_name );
|
|
|
|
// After all the maximums are set properly, call this to allocate space
|
|
// for holding the keys.
|
|
|
|
void alloc_key_space ( Interface * gi );
|
|
|
|
// After allocating space, add keys by calling this.
|
|
|
|
void add_key
|
|
(
|
|
const char * bone_name,
|
|
TimeValue time,
|
|
const Quat & rot,
|
|
const Point3 & pos
|
|
);
|
|
|
|
// Once all the keys have been added, call this to massage them and
|
|
// put them into 3D Studio.
|
|
|
|
void put_keys_into_max ( ImpInterface *, Interface * );
|
|
|
|
protected:
|
|
|
|
Bone * Bones;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Key_Manager::~Key_Manager
|
|
//----------------------------------------------------------------------------
|
|
|
|
Key_Manager::~Key_Manager ()
|
|
{
|
|
Bone * p = Bones;
|
|
|
|
while ( p != NULL )
|
|
{
|
|
Bone * delete_p = p;
|
|
p = p->next ();
|
|
|
|
delete delete_p;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Key_Manager::inc_max_keys
|
|
//----------------------------------------------------------------------------
|
|
|
|
void Key_Manager::inc_max_keys ( const char * name )
|
|
{
|
|
for ( Bone * p = Bones; p != NULL; p = p->next () )
|
|
{
|
|
if ( strcmp ( p->name (), name ) == 0 )
|
|
break;
|
|
}
|
|
|
|
if ( p == NULL )
|
|
{
|
|
// This is a new bone; add it to the list.
|
|
|
|
p = new Bone ( name, Bones );
|
|
|
|
Bones = p;
|
|
}
|
|
|
|
p->inc_max_keys ();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Key_Manager::alloc_key_space
|
|
//----------------------------------------------------------------------------
|
|
|
|
void Key_Manager::alloc_key_space ( Interface * gi )
|
|
{
|
|
for ( Bone * p = Bones; p != NULL; p = p->next () )
|
|
{
|
|
p->alloc_key_space ( gi );
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Key_Manager::add_key
|
|
//----------------------------------------------------------------------------
|
|
|
|
void Key_Manager::add_key
|
|
(
|
|
const char * name,
|
|
TimeValue time,
|
|
const Quat & rot,
|
|
const Point3 & pos
|
|
)
|
|
{
|
|
for ( Bone * p = Bones; p != NULL; p = p->next () )
|
|
{
|
|
if ( strcmp ( p->name (), name ) == 0 )
|
|
break;
|
|
}
|
|
|
|
if ( p != NULL )
|
|
{
|
|
p->add_key ( time, rot, pos );
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Key_Manager::put_keys_into_max
|
|
//----------------------------------------------------------------------------
|
|
|
|
void Key_Manager::put_keys_into_max ( ImpInterface * imp_i, Interface * i )
|
|
{
|
|
for ( Bone * p = Bones; p != NULL; p = p->next () )
|
|
{
|
|
p->put_keys_into_max ( imp_i, i );
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// get_name
|
|
//----------------------------------------------------------------------------
|
|
|
|
void get_name
|
|
(
|
|
char * & line_p,
|
|
char * & name_p
|
|
)
|
|
{
|
|
name_p = line_p;
|
|
|
|
while ( ! isspace (*line_p) )
|
|
++ line_p;
|
|
|
|
*line_p = '\0';
|
|
++ line_p;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// read_frames
|
|
//----------------------------------------------------------------------------
|
|
|
|
static void read_frames
|
|
(
|
|
FILE * file,
|
|
ImpInterface * iface,
|
|
Interface * gi
|
|
)
|
|
{
|
|
char line [ 512 ];
|
|
Key_Manager key_manager;
|
|
|
|
// Start by identifying the bones in the system and the number of keys
|
|
// for each.
|
|
|
|
long start_pos = ftell ( file );
|
|
|
|
while (1)
|
|
{
|
|
char * rv = fgets ( line, sizeof line, file );
|
|
if ( rv == NULL )
|
|
break;
|
|
|
|
if ( ! isdigit (line [0]) )
|
|
{
|
|
char * line_p = line;
|
|
char * name_p;
|
|
|
|
get_name ( line_p, name_p );
|
|
|
|
key_manager.inc_max_keys ( name_p );
|
|
}
|
|
}
|
|
|
|
// Prepare to scan the file again. Allocate space to hold the keys.
|
|
|
|
fseek ( file, start_pos, SEEK_SET );
|
|
key_manager.alloc_key_space ( gi );
|
|
|
|
int frame_time = 0;
|
|
|
|
while (1)
|
|
{
|
|
char * rv = fgets ( line, sizeof line, file );
|
|
if ( rv == NULL )
|
|
break;
|
|
|
|
if ( isdigit (line [0]) )
|
|
{
|
|
// This line marks the start of a new frame.
|
|
|
|
int frame_number = strtol ( line, NULL, 10 );
|
|
frame_time = (frame_number - 1) * amc_ticks_per_frame +
|
|
first_frame_time;
|
|
}
|
|
else
|
|
{
|
|
// This line contains a bone's data for the current frame.
|
|
// Get the name of the bone whose key is being defined.
|
|
|
|
char * line_p = line;
|
|
char * name_p;
|
|
get_name ( line_p, name_p );
|
|
|
|
// Find the I-node with the name given in the input file.
|
|
|
|
INode * inode = gi->GetINodeByName ( name_p );
|
|
#if 0
|
|
if ( inode == NULL )
|
|
{
|
|
char message [ 256 ];
|
|
sprintf ( message, "Can't find node named \"%s\".",
|
|
name_p );
|
|
MessageBox ( GetActiveWindow (), message, "Parse error",
|
|
MB_OK );
|
|
return;
|
|
}
|
|
#else
|
|
if ( inode == NULL) {
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
// From the I-node, get the appdata chunk that defines the order
|
|
// in which rotations should be applied.
|
|
|
|
AppDataChunk * chunk = inode->GetAppDataChunk
|
|
(
|
|
Class_ID(0x74975aa6, 0x1810323f),
|
|
SCENE_IMPORT_CLASS_ID,
|
|
1
|
|
);
|
|
|
|
if ( chunk == NULL )
|
|
{
|
|
char message [ 256 ];
|
|
sprintf ( message, "\"%s\" has no app data chunk.",
|
|
name_p );
|
|
MessageBox ( GetActiveWindow (), message, "Parse error",
|
|
MB_OK );
|
|
return;
|
|
}
|
|
|
|
ASF_Data_Chunk * data_p = (ASF_Data_Chunk *) chunk->data;
|
|
|
|
// Read in angle settings and build a transform matrix from them.
|
|
|
|
Quat rotation (0.0f,0.0f,0.0f,1.0f);
|
|
Point3 translation ( 0, 0, 0 );
|
|
|
|
for ( unsigned i = 0; i < data_p->Number_of_axes; ++ i )
|
|
{
|
|
float value;
|
|
|
|
value = (float) strtod ( line_p, & line_p );
|
|
|
|
switch ( data_p->Axis [i] )
|
|
{
|
|
case ROTATE_X:
|
|
rotation *= (Quat) AngAxis ( Point3 (1, 0, 0),
|
|
-value * ANGLE_MULTIPLIER );
|
|
break;
|
|
case ROTATE_Y:
|
|
rotation *= (Quat) AngAxis ( Point3 (0, 1, 0),
|
|
-value * ANGLE_MULTIPLIER );
|
|
break;
|
|
case ROTATE_Z:
|
|
rotation *= (Quat) AngAxis ( Point3 (0, 0, 1),
|
|
-value * ANGLE_MULTIPLIER );
|
|
break;
|
|
case TRANSLATE_X:
|
|
translation.x = value;
|
|
break;
|
|
case TRANSLATE_Y:
|
|
translation.y = value;
|
|
break;
|
|
case TRANSLATE_Z:
|
|
translation.z = value;
|
|
break;
|
|
case TRANSLATE_LENGTH:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
Matrix3 localTM = inode->GetNodeTM (0) *
|
|
Inverse (inode->GetParentTM (0));
|
|
rotation *= localTM;
|
|
|
|
if ( frame_time % max_ticks_per_frame == 0 )
|
|
key_manager.add_key ( name_p, frame_time, rotation, translation );
|
|
|
|
}
|
|
}
|
|
|
|
key_manager.put_keys_into_max ( iface, gi );
|
|
|
|
gi->SetAnimRange ( Interval (0, frame_time) );
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// amc_load
|
|
//----------------------------------------------------------------------------
|
|
|
|
static int amc_load
|
|
(
|
|
const TCHAR * filename,
|
|
ImpInterface * iface,
|
|
Interface * gi
|
|
)
|
|
{
|
|
char line [ 512 ];
|
|
|
|
// Load the motion-capture data file.
|
|
|
|
FILE * file = fopen ( filename, "r" );
|
|
|
|
// Ignore the first line.
|
|
|
|
fgets ( line, sizeof line, file );
|
|
|
|
// Match the second line to ":FULLY-SPECIFIED".
|
|
|
|
fgets ( line, sizeof line, file );
|
|
if ( strcmp ( line, ":FULLY-SPECIFIED\n" ) != 0 )
|
|
{
|
|
MessageBox ( GetActiveWindow (), "First line.", "Parse error",
|
|
MB_OK );
|
|
return -1;
|
|
}
|
|
|
|
// Match the third line to ":DEGREES".
|
|
|
|
fgets ( line, sizeof line, file );
|
|
if ( strcmp ( line, ":DEGREES\n" ) != 0 )
|
|
{
|
|
MessageBox ( GetActiveWindow (), "Second line.", "Parse error",
|
|
MB_OK );
|
|
return -1;
|
|
}
|
|
|
|
// Build the data structures to hold the rotation keys.
|
|
|
|
read_frames ( file, iface, gi );
|
|
|
|
fclose ( file );
|
|
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// AMC_Import::DoImport
|
|
//----------------------------------------------------------------------------
|
|
|
|
int AMC_Import::DoImport
|
|
(
|
|
const TCHAR * filename,
|
|
ImpInterface * iface,
|
|
Interface * gi,
|
|
BOOL
|
|
)
|
|
{
|
|
max_ticks_per_frame = GetTicksPerFrame ();
|
|
|
|
int status = amc_load ( filename, iface, gi );
|
|
if ( status == 0 )
|
|
status = IMPEXP_CANCEL;
|
|
|
|
return (status <= 0) ? IMPEXP_FAIL : status;
|
|
}
|