| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818 |
- /*
- ** 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/>.
- */
- #include <Max.h>
- #include <istdplug.h>
- #include "asf_data.h"
- #include "read_asf.h"
- #include "exception.h"
- //----------------------------------------------------------------------------
- // ASF_Lexer::ASF_Lexer
- //----------------------------------------------------------------------------
- ASF_Lexer::ASF_Lexer
- (
- const char * file_name
- ):
- Input_file ( file_name, "r" )
- {
- Current_char = tolower ( fgetc ( Input_file.fp ) );
- advance ();
- }
- //----------------------------------------------------------------------------
- // ASF_Lexer::skip_whitespace
- //----------------------------------------------------------------------------
- // Skip whitespace and comments, leaving the Current_char pointing to the
- // first non-whitespace character in the input stream.
- //----------------------------------------------------------------------------
- void ASF_Lexer::skip_whitespace ()
- {
- while (1)
- {
- if ( Current_char == '#' ) // Strip out comments
- {
- while ( Current_char != '\n' && Current_char != EOF )
- Current_char = tolower ( fgetc ( Input_file.fp ) );
- }
- else if ( (isspace ( Current_char ) && Current_char != '\n') ||
- Current_char == ',' ||
- Current_char == '(' ||
- Current_char == ')' )
- {
- Current_char = tolower ( fgetc ( Input_file.fp ) );
- }
- else
- {
- break;
- }
- }
- }
- //----------------------------------------------------------------------------
- // ASF_Lexer::advance
- //----------------------------------------------------------------------------
- // Advance to the next token in the input stream.
- //----------------------------------------------------------------------------
- void ASF_Lexer::advance ()
- {
- skip_whitespace ();
- if ( Current_char == EOF )
- {
- Current_type = EOF_MARKER;
- strcpy ( Current_text, "<end of file>" );
- return;
- }
- if ( Current_char == '\n' )
- {
- Current_type = NEWLINE;
- strcpy ( Current_text, "<newline>" );
- Current_char = tolower ( fgetc ( Input_file.fp ) );
- return;
- }
- Current_type = TOKEN;
- // Read characters into the token text buffer.
- int i = 0;
- while ( ! isspace (Current_char) &&
- Current_char != '#' &&
- Current_char != ',' &&
- Current_char != '(' &&
- Current_char != ')' &&
- Current_char != EOF )
- {
- if ( i >= (MAX_TOKEN_LENGTH - 1) )
- {
- // Abort -- maximum token length exceeded.
- throw Parse_Error ( "Maximum token length exceeded." );
- }
- Current_text [i] = Current_char;
- ++ i;
- Current_char = tolower ( fgetc ( Input_file.fp ) );
- }
- Current_text [i] = '\0';
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::Skeleton_Class
- //----------------------------------------------------------------------------
- Skeleton_Class::Skeleton_Class
- (
- const char * file_name,
- ImpInterface * iface,
- Interface * gi
- ) :
- Angle_multiplier (DEGREES_TO_RADIANS),
- Length_multiplier (1.0f),
- First_bone (NULL),
- Import_interface (iface),
- Max_interface (gi)
- {
- ASF_Lexer lexer ( file_name );
- parse_units_block ( lexer );
- parse_root_block ( lexer );
- parse_bonedata_block ( lexer );
- parse_hierarchy_block ( lexer );
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::~Skeleton_Class
- //----------------------------------------------------------------------------
- Skeleton_Class::~Skeleton_Class ()
- {
- Bone_Class * p = First_bone;
- while ( p != NULL )
- {
- Bone_Class * delete_p = p;
- p = p->next_bone ();
- delete delete_p;
- }
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::parse_units_block
- //----------------------------------------------------------------------------
- void Skeleton_Class::parse_units_block ( ASF_Lexer & lexer )
- {
- skip_unrecognized_blocks ( lexer );
- match_token ( lexer, ":units" );
- match_newline ( lexer );
- BOOL mass_defined = FALSE;
- BOOL length_defined = FALSE;
- BOOL angle_defined = FALSE;
- while (1)
- {
- verify_token ( lexer );
- if ( strcmp ( lexer.text (), "mass" ) == 0 )
- {
- if ( mass_defined )
- throw Parse_Error ( "Multiple mass definitions." );
- lexer.advance ();
- skip_token ( lexer ); // Ignore the mass definition.
- match_newline ( lexer );
- mass_defined = TRUE;
- }
- else if ( strcmp ( lexer.text (), "length" ) == 0 )
- {
- if ( length_defined )
- throw Parse_Error ( "Multiple length definitions." );
- lexer.advance ();
- verify_token ( lexer );
- Length_multiplier = 1.0f / float_token ( lexer );
- match_newline ( lexer );
- length_defined = TRUE;
- }
- else if ( strcmp ( lexer.text (), "angle" ) == 0 )
- {
- if ( angle_defined )
- throw Parse_Error ( "Multiple angle definitions." );
- lexer.advance ();
- verify_token ( lexer );
- if ( strcmp ( lexer.text (), "deg" ) == 0 )
- Angle_multiplier = DEGREES_TO_RADIANS;
- else if ( strcmp ( lexer.text (), "rad" ) == 0 )
- Angle_multiplier = 1.0f;
- else
- throw Parse_Error
- ( "\"deg\" or \"rad\" expected after angle keyword." );
- lexer.advance ();
- match_newline ( lexer );
- angle_defined = TRUE;
- }
- else
- {
- break;
- }
- }
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::parse_root_block
- //----------------------------------------------------------------------------
- void Skeleton_Class::parse_root_block ( ASF_Lexer & lexer )
- {
- skip_unrecognized_blocks ( lexer );
- match_token ( lexer, ":root" );
- match_newline ( lexer );
- // Create a root bone.
- Bone_Class * new_bone = new Bone_Class;
- new_bone->set_name ( "root" );
- // The rotation order for orientation offset.
- match_token ( lexer, "axis" );
- skip_token ( lexer ); // &&& get rotation order for orientation offset.
- match_newline ( lexer );
- // The order of transformations for root.
- match_token ( lexer, "order" );
- for ( int i = 0; i < 6; ++ i )
- {
- verify_token ( lexer );
- if ( strcmp ( lexer.text (), "rx" ) == 0 )
- new_bone->add_axis ( ROTATE_X );
- else if ( strcmp ( lexer.text (), "ry" ) == 0 )
- new_bone->add_axis ( ROTATE_Y );
- else if ( strcmp ( lexer.text (), "rz" ) == 0 )
- new_bone->add_axis ( ROTATE_Z );
- else if ( strcmp ( lexer.text (), "tx" ) == 0 )
- new_bone->add_axis ( TRANSLATE_X );
- else if ( strcmp ( lexer.text (), "ty" ) == 0 )
- new_bone->add_axis ( TRANSLATE_Y );
- else if ( strcmp ( lexer.text (), "tz" ) == 0 )
- new_bone->add_axis ( TRANSLATE_Z );
- else
- throw Parse_Error ( "Unrecognized order token in :root." );
- lexer.advance ();
- }
- match_newline ( lexer );
- // Translation data for root node.
- match_token ( lexer, "position" );
- skip_token ( lexer ); // &&&
- skip_token ( lexer ); // &&&
- skip_token ( lexer ); // &&&
- match_newline ( lexer );
- // Rotation data to orient the skeleton.
- match_token ( lexer, "orientation" );
- float rot0 = float_token ( lexer ) * Angle_multiplier;
- float rot1 = float_token ( lexer ) * Angle_multiplier;
- float rot2 = float_token ( lexer ) * Angle_multiplier;
- match_newline ( lexer );
- Matrix3 axis_tm;
- axis_tm.IdentityMatrix ();
- axis_tm.RotateX ( rot0 );
- axis_tm.RotateY ( rot1 );
- axis_tm.RotateZ ( rot2 );
- new_bone->set_axis_tm ( axis_tm );
- new_bone->set_direction ( Point3 (0,0,0) );
- new_bone->set_length ( 0.0f );
- add_bone_to_list ( new_bone );
- new_bone->create_node ( Import_interface, Max_interface );
- // Add an AppData chunk to the root node to indicate how much to scale
- // its position keys.
- Position_Key_Scale_Chunk * data_p = (Position_Key_Scale_Chunk *)
- malloc ( sizeof (Position_Key_Scale_Chunk) );
- data_p->Position_Key_Scale = Length_multiplier;
- new_bone->add_app_data ( 2, data_p, sizeof (Position_Key_Scale_Chunk) );
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::parse_bonedata_block
- //----------------------------------------------------------------------------
- void Skeleton_Class::parse_bonedata_block ( ASF_Lexer & lexer )
- {
- skip_unrecognized_blocks ( lexer );
- match_token ( lexer, ":bonedata" );
- match_newline ( lexer );
- // Parse bone definition blocks.
- while (1)
- {
- verify_token ( lexer );
- if ( strcmp ( lexer.text (), "begin" ) == 0 )
- {
- parse_bone ( lexer );
- }
- else
- {
- break;
- }
- }
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::parse_hierarchy_block
- //----------------------------------------------------------------------------
- void Skeleton_Class::parse_hierarchy_block ( ASF_Lexer & lexer )
- {
- skip_unrecognized_blocks ( lexer );
- match_token ( lexer, ":hierarchy" );
- match_newline ( lexer );
- match_token ( lexer, "begin" );
- match_newline ( lexer );
- while (1)
- {
- verify_token ( lexer );
- if ( strcmp ( lexer.text (), "end" ) == 0 )
- {
- break;
- }
- else
- {
- parse_hierarchy_line ( lexer );
- }
- }
- lexer.advance ();
- match_newline ( lexer );
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::parse_hierarchy_line
- //----------------------------------------------------------------------------
- void Skeleton_Class::parse_hierarchy_line ( ASF_Lexer & lexer )
- {
- verify_token ( lexer );
- Bone_Class * parent_bone = find_bone ( lexer.text () );
- if ( parent_bone == NULL )
- throw Parse_Error ( "Undefined parent bone." );
- lexer.advance ();
- while ( lexer.type () == TOKEN )
- {
- Bone_Class * child_bone = find_bone ( lexer.text () );
- if ( child_bone == NULL )
- throw Parse_Error ( "Undefined child bone." );
- child_bone->set_parent ( parent_bone );
- lexer.advance ();
- }
- match_newline ( lexer );
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::find_bone
- //----------------------------------------------------------------------------
- Bone_Class * Skeleton_Class::find_bone ( const char * name )
- {
- Bone_Class * p = First_bone;
- while ( p != NULL )
- {
- if ( strcmp ( name, p->name () ) == 0 )
- break;
- p = p->next_bone ();
- }
- return p;
- }
- //----------------------------------------------------------------------------
- // lh_to_rh
- //----------------------------------------------------------------------------
- static Point3 lh_to_rh ( Point3 point )
- {
- Point3 new_point;
- new_point.x = point.x;
- new_point.y = -point.z;
- new_point.z = point.y;
- return new_point;
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::parse_bone
- //----------------------------------------------------------------------------
- void Skeleton_Class::parse_bone ( ASF_Lexer & lexer )
- {
- match_token ( lexer, "begin" );
- match_newline ( lexer );
- // Create a new bone object.
- Bone_Class * new_bone = new Bone_Class;
- // Optional ID number.
- verify_token ( lexer );
- if ( strcmp ( lexer.text (), "id" ) == 0 )
- {
- lexer.advance ();
- skip_token ( lexer ); // Ignore the bone ID number for now.
- match_newline ( lexer );
- }
- // Name.
- match_token ( lexer, "name" );
- verify_token ( lexer );
- new_bone->set_name ( lexer.text () );
- lexer.advance ();
- match_newline ( lexer );
- // Direction vector.
- match_token ( lexer, "direction" );
- Point3 direction;
- direction.x = float_token ( lexer );
- direction.y = float_token ( lexer );
- direction.z = float_token ( lexer );
- new_bone->set_direction ( direction );
- match_newline ( lexer );
- // Length.
- match_token ( lexer, "length" );
- new_bone->set_length ( float_token ( lexer ) * Length_multiplier / (float)GetMasterScale(UNITS_INCHES));
- match_newline ( lexer );
- // Rotation axis in world coordinates, with order of rotations.
- match_token ( lexer, "axis" );
- float rot0 = float_token ( lexer ) * Angle_multiplier;
- float rot1 = float_token ( lexer ) * Angle_multiplier;
- float rot2 = float_token ( lexer ) * Angle_multiplier;
- // &&& Ultimately this should handle any order of rotations.
- match_token ( lexer, "xyz" );
- match_newline ( lexer );
- Matrix3 axis_tm;
- axis_tm.IdentityMatrix ();
- axis_tm.RotateX ( rot0 );
- axis_tm.RotateY ( rot1 );
- axis_tm.RotateZ ( rot2 );
- new_bone->set_axis_tm ( axis_tm );
- // Optional mass of skinbody associated with this bone.
- verify_token ( lexer );
- if ( strcmp ( lexer.text (), "bodymass" ) == 0 )
- {
- lexer.advance ();
- skip_token ( lexer ); // Ignore the bodymass.
- match_newline ( lexer );
- }
- // Optional position of center of mass along the bone.
- verify_token ( lexer );
- if ( strcmp ( lexer.text (), "cofmass" ) == 0 )
- {
- lexer.advance ();
- skip_token ( lexer ); // Ignore the center of mass position.
- match_newline ( lexer );
- }
- // Optional degrees of freedom.
- verify_token ( lexer );
- if ( strcmp ( lexer.text (), "dof" ) == 0 )
- {
- lexer.advance ();
- if ( lexer.type () == TOKEN && strcmp ( lexer.text (), "rx" ) == 0 )
- {
- new_bone->add_axis ( ROTATE_X );
- lexer.advance ();
- }
- if ( lexer.type () == TOKEN && strcmp ( lexer.text (), "ry" ) == 0 )
- {
- new_bone->add_axis ( ROTATE_Y );
- lexer.advance ();
- }
- if ( lexer.type () == TOKEN && strcmp ( lexer.text (), "rz" ) == 0 )
- {
- new_bone->add_axis ( ROTATE_Z );
- lexer.advance ();
- }
- if ( lexer.type () == TOKEN && strcmp ( lexer.text (), "l" ) == 0 )
- {
- new_bone->add_axis ( TRANSLATE_LENGTH );
- lexer.advance ();
- }
- match_newline ( lexer );
- // Limits for the given degrees of freedom.
- match_token ( lexer, "limits" );
- // &&&
- while ( strcmp ( lexer.text (), "end" ) != 0 )
- lexer.advance ();
- }
- match_token ( lexer, "end" );
- match_newline ( lexer );
- // Add the bone to the list of bones.
- add_bone_to_list ( new_bone );
- new_bone->create_node ( Import_interface, Max_interface );
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::skip_unrecognized_blocks
- //----------------------------------------------------------------------------
- // It is assumed that the current lexeme is the first on a line.
- //----------------------------------------------------------------------------
- void Skeleton_Class::skip_unrecognized_blocks
- (
- ASF_Lexer & lexer
- )
- {
- while (1)
- {
- if ( lexer.type () == EOF_MARKER )
- break;
- if ( strcmp ( lexer.text (), ":units" ) == 0 )
- break;
- if ( strcmp ( lexer.text (), ":root" ) == 0 )
- break;
- if ( strcmp ( lexer.text (), ":bonedata" ) == 0 )
- break;
- if ( strcmp ( lexer.text (), ":hierarchy" ) == 0 )
- break;
- skip_to_next_line ( lexer );
- }
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::skip_to_next_line
- //----------------------------------------------------------------------------
- void Skeleton_Class::skip_to_next_line
- (
- ASF_Lexer & lexer
- )
- {
- while ( lexer.type () != NEWLINE &&
- lexer.type () != EOF_MARKER )
- lexer.advance ();
- if ( lexer.type () == NEWLINE )
- lexer.advance ();
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::match_newline
- //----------------------------------------------------------------------------
- void Skeleton_Class::match_newline
- (
- ASF_Lexer & lexer
- )
- {
- char message_buffer [ 512 ];
- if ( lexer.type () != NEWLINE )
- {
- sprintf ( message_buffer, "Expected newline; found \"%s\" instead.",
- lexer.text () );
- throw Parse_Error ( message_buffer );
- }
- lexer.advance ();
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::verify_token
- //----------------------------------------------------------------------------
- void Skeleton_Class::verify_token
- (
- ASF_Lexer & lexer
- )
- {
- char message_buffer [ 512 ];
- if ( lexer.type () != TOKEN )
- {
- sprintf ( message_buffer, "Expected token; found \"%s\" instead.",
- lexer.text () );
- throw Parse_Error ( message_buffer );
- }
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::skip_token
- //----------------------------------------------------------------------------
- void Skeleton_Class::skip_token
- (
- ASF_Lexer & lexer
- )
- {
- verify_token ( lexer );
- lexer.advance ();
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::float_token
- //----------------------------------------------------------------------------
- float Skeleton_Class::float_token
- (
- ASF_Lexer & lexer
- )
- {
- verify_token ( lexer );
- float value = (float) strtod ( lexer.text (), NULL );
- lexer.advance ();
- return value;
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::match_token
- //----------------------------------------------------------------------------
- void Skeleton_Class::match_token
- (
- ASF_Lexer & lexer,
- const char * token_text
- )
- {
- char message_buffer [ 512 ];
- verify_token ( lexer );
- if ( strcmp ( lexer.text (), token_text ) != 0 )
- {
- sprintf ( message_buffer, "Expected \"%s\"; found \"%s\" instead.",
- token_text, lexer.text () );
- throw Parse_Error ( message_buffer );
- }
- lexer.advance ();
- }
- //----------------------------------------------------------------------------
- // Skeleton_Class::add_bone_to_list
- //----------------------------------------------------------------------------
- void Skeleton_Class::add_bone_to_list
- (
- Bone_Class * new_bone
- )
- {
- new_bone->set_next_bone ( First_bone );
- First_bone = new_bone;
- }
- //----------------------------------------------------------------------------
- // Bone_Class::create_node
- //----------------------------------------------------------------------------
- void Bone_Class::create_node
- (
- ImpInterface * import_interface,
- Interface * max_interface
- )
- {
- Node = import_interface->CreateNode ();
- Node->SetName ( Name );
- // Create a box object.
- GeomObject * obj = (GeomObject *) max_interface->CreateInstance
- ( GEOMOBJECT_CLASS_ID, Class_ID (BOXOBJ_CLASS_ID, 0) );
- IParamArray *iBoxParams = obj->GetParamBlock();
- assert(iBoxParams);
- // Set the value of width, height and length.
- int width_index = obj->GetParamBlockIndex(BOXOBJ_WIDTH);
- assert(width_index >= 0);
- iBoxParams->SetValue(width_index,TimeValue(0),Length / 4);
- int height_index = obj->GetParamBlockIndex(BOXOBJ_HEIGHT);
- assert(height_index >= 0);
- iBoxParams->SetValue(height_index,TimeValue(0),Length);
- int length_index = obj->GetParamBlockIndex(BOXOBJ_LENGTH);
- assert(length_index >= 0);
- iBoxParams->SetValue(length_index,TimeValue(0),Length / 4);
- Node->Reference ( (Object *) obj );
- Node->GetINode ()->SetNodeTM ( 0, Axis_tm );
- // Add the node to the scene.
- import_interface->AddNodeToScene ( Node );
- // Add an AppData chunk to the inode to indicate which transformation
- // axes are active.
- ASF_Data_Chunk * data_p = (ASF_Data_Chunk *)
- malloc ( sizeof (ASF_Data_Chunk) );
- *data_p = Active_axes;
- add_app_data ( 1, data_p, sizeof (ASF_Data_Chunk) );
- }
- //----------------------------------------------------------------------------
- // Bone_Class::set_parent
- //----------------------------------------------------------------------------
- void Bone_Class::set_parent ( Bone_Class * parent_bone )
- {
- Parent_bone = parent_bone;
- parent_bone->Node->GetINode ()->AttachChild ( Node->GetINode () );
- // Build the child node's transform matrix.
- Matrix3 parent_tm = Node->GetINode ()->GetParentTM ( 0 );
- Matrix3 child_tm = Axis_tm;
- child_tm.Translate ( Normalize (parent_bone->Direction) *
- parent_bone->Length );
- child_tm.Translate ( parent_tm.GetTrans () );
- Node->GetINode ()->SetNodeTM ( 0, child_tm );
- }
- //----------------------------------------------------------------------------
- // Bone_Class::add_app_data
- //----------------------------------------------------------------------------
- void Bone_Class::add_app_data ( int chunk_id, void * data, int data_size )
- {
- Node->GetINode ()->AddAppDataChunk
- (
- Class_ID(0x74975aa6, 0x1810323f),
- SCENE_IMPORT_CLASS_ID,
- chunk_id,
- data_size,
- data
- );
- }
|