/* * Copyright (c) 2007 Gero Mueller * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. */ #include "tl3ds.h" #include #include /*----------------------------------------------------------------------------*/ typedef enum tl3dsParsingState { TDS_STATE_READ_CHUNK_ID, TDS_STATE_READ_CHUNK_LENGTH, TDS_STATE_READ_OBJECT_NAME, TDS_STATE_SKIP_CHUNK, TDS_STATE_READ_POINT_COUNT, TDS_STATE_READ_POINTS, TDS_STATE_READ_TEXCOORD_COUNT, TDS_STATE_READ_TEXCOORDS, TDS_STATE_READ_FACE_COUNT, TDS_STATE_READ_FACES, TDS_STATE_DONE } tl3dsParsingState; /*----------------------------------------------------------------------------*/ typedef struct tl3dsObject { char *name; unsigned int index, count; } tl3dsObject; /*----------------------------------------------------------------------------*/ struct tl3dsState { unsigned short chunk_id; unsigned int chunk_length; char *buffer; unsigned int buffer_size; unsigned int buffer_length; unsigned int counter; unsigned int item_count; tl3dsParsingState parsing_state; float *point_buffer; unsigned int point_buffer_size; unsigned int point_count; float *texcoord_buffer; unsigned int texcoord_buffer_size; unsigned int texcoord_count; unsigned short *face_buffer; unsigned int face_buffer_size; unsigned int face_count; tl3dsObject **object_buffer; unsigned int object_count; }; /*----------------------------------------------------------------------------*/ static unsigned int tds_le() { const char endian[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; unsigned int i = *((unsigned int *)endian); /* LE uint64: i = 1 */ /* LE uint32: i = 1 */ /* LE uint16: i = 1 */ /* BE uint32: i > 1 */ /* BE uint32: i > 1 */ /* BE uint16: i > 1 */ if( i == 1 ) return 1; else return 0; } /*----------------------------------------------------------------------------*/ static float tds_read_le_float( const char *ptr ) { float f = 0; char *fptr = (char *)&f; if( tds_le() ) { fptr[0] = ptr[0]; fptr[1] = ptr[1]; fptr[2] = ptr[2]; fptr[3] = ptr[3]; } else { fptr[0] = ptr[3]; fptr[1] = ptr[2]; fptr[2] = ptr[1]; fptr[3] = ptr[0]; } return f; } /*----------------------------------------------------------------------------*/ static unsigned short tds_read_le_ushort( const char *ptr ) { unsigned short s = 0; char *sptr = (char *)&s; if( tds_le() ) { sptr[0] = ptr[0]; sptr[1] = ptr[1]; } else { sptr[0] = ptr[1]; sptr[1] = ptr[0]; } return s; } /*----------------------------------------------------------------------------*/ static unsigned int tds_read_le_uint( const char *ptr ) { unsigned int i = 0; char *iptr = (char *)&i; if( tds_le() ) { iptr[0] = ptr[0]; iptr[1] = ptr[1]; iptr[2] = ptr[2]; iptr[3] = ptr[3]; } else { iptr[0] = ptr[3]; iptr[1] = ptr[2]; iptr[2] = ptr[1]; iptr[3] = ptr[0]; } return i; } /*----------------------------------------------------------------------------*/ static int tds_buffer_reserve( tl3dsState *state, unsigned int size ) { unsigned int new_size = 1; char *new_buffer = 0; if( state == NULL ) return 1; if( state->buffer_size >= size ) return 0; while( new_size < size ) new_size = new_size * 2; new_buffer = (char *)realloc( state->buffer, new_size ); if( new_buffer ) { state->buffer = new_buffer; state->buffer_size = new_size; } return 0; } /*----------------------------------------------------------------------------*/ static void tds_buffer_add( tl3dsState *state, char c ) { if( tds_buffer_reserve( state, state->buffer_length + 1 ) != 0 ) return; state->buffer[ state->buffer_length ] = c; state->buffer_length++; } /*----------------------------------------------------------------------------*/ static int tds_object_buffer_add( tl3dsState *state, const char *name, unsigned int name_length ) { tl3dsObject **new_object_buffer = 0; unsigned int new_object_count = state->object_count + 1; new_object_buffer = (tl3dsObject **)realloc( state->object_buffer, sizeof(tl3dsObject *) * new_object_count ); if( new_object_buffer ) { /* create the new object */ tl3dsObject *new_object = (tl3dsObject *)malloc( sizeof(tl3dsObject) ); memset( new_object, 0, sizeof(tl3dsObject) ); /* copy the name */ new_object->name = (char *)malloc( name_length ); memcpy( new_object->name, name, name_length ); /* add the new object */ new_object_buffer[ new_object_count - 1 ] = new_object; /* update state */ state->object_buffer = new_object_buffer; state->object_count = new_object_count; return 0; } return 1; } /*----------------------------------------------------------------------------*/ static void tds_point_buffer_grow( tl3dsState *state, unsigned int count ) { unsigned int new_size = (state->point_count + count ) * 3 * sizeof(float); float *new_buffer = realloc( state->point_buffer, new_size ); if( new_buffer ) { state->point_buffer = new_buffer; state->point_buffer_size = new_size; } } /*----------------------------------------------------------------------------*/ static void tds_point_buffer_add( tl3dsState *state, float x, float y, float z ) { unsigned int new_size = (state->point_count + 1 ) * 3 * sizeof(float); if( state->point_buffer_size < new_size ) return; state->point_buffer[state->point_count * 3] = x; state->point_buffer[state->point_count * 3 + 1] = y; state->point_buffer[state->point_count * 3 + 2] = z; state->point_count++; } /*----------------------------------------------------------------------------*/ static void tds_texcoord_buffer_grow( tl3dsState *state, unsigned int count ) { unsigned int new_size = (state->texcoord_count + count ) * 2 * sizeof(float); float *new_buffer = realloc( state->texcoord_buffer, new_size ); if( new_buffer ) { state->texcoord_buffer = new_buffer; state->texcoord_buffer_size = new_size; } } /*----------------------------------------------------------------------------*/ static void tds_texcoord_buffer_add( tl3dsState *state, float u, float v ) { unsigned int new_size = (state->texcoord_count + 1 ) * 2 * sizeof(float); if( state->texcoord_buffer_size < new_size ) return; state->texcoord_buffer[state->texcoord_count * 2] = u; state->texcoord_buffer[state->texcoord_count * 2 + 1] = v; state->texcoord_count++; } /*----------------------------------------------------------------------------*/ static void tds_face_buffer_grow( tl3dsState *state, unsigned int count ) { unsigned int new_size = (state->face_count + count ) * 3 * sizeof(unsigned short); unsigned short *new_buffer = realloc( state->face_buffer, new_size ); if( new_buffer ) { state->face_buffer = new_buffer; state->face_buffer_size = new_size; } } /*----------------------------------------------------------------------------*/ static void tds_face_buffer_add( tl3dsState *state, unsigned short a, unsigned short b, unsigned short c ) { unsigned int new_size = (state->face_count + 1 ) * 3 * sizeof(unsigned short); if( state->face_buffer_size < new_size ) return; state->face_buffer[state->face_count * 3] = a; state->face_buffer[state->face_count * 3 + 1] = b; state->face_buffer[state->face_count * 3 + 2] = c; state->face_count++; } /*----------------------------------------------------------------------------*/ tl3dsState *tl3dsCreateState() { tl3dsState *state = malloc( sizeof(tl3dsState) ); if( state ) { memset( state, 0, sizeof(tl3dsState) ); state->parsing_state = TDS_STATE_READ_CHUNK_ID; } return state; } /*----------------------------------------------------------------------------*/ int tl3dsResetState( tl3dsState *state ) { unsigned int i; if( state->buffer ) free( state->buffer ); if( state->object_buffer ) { for( i = 0; i < state->object_count; i++ ) { tl3dsObject *obj = state->object_buffer[i]; if( obj == 0 ) continue; if( obj->name ) free( obj->name ); free( obj ); } free( state->object_buffer ); } if( state->point_buffer ) free( state->point_buffer ); if( state->texcoord_buffer ) free( state->texcoord_buffer ); if( state->face_buffer ) free( state->face_buffer ); memset( state, 0, sizeof(tl3dsState) ); state->parsing_state = TDS_STATE_READ_CHUNK_ID; return 0; } /*----------------------------------------------------------------------------*/ void tl3dsDestroyState( tl3dsState *state ) { if( state ) { tl3dsResetState( state ); free( state ); } } /*----------------------------------------------------------------------------*/ int tl3dsParse( tl3dsState *state, const char *buffer, unsigned int length, int last ) { unsigned int i = 0; if( state == NULL ) return 1; while( i < length ) { char c = buffer[i]; switch( state->parsing_state ) { case TDS_STATE_READ_CHUNK_ID: tds_buffer_add( state, c ); if( state->buffer_length == 2 ) { state->chunk_id = tds_read_le_ushort( state->buffer ); state->buffer_length = 0; state->parsing_state = TDS_STATE_READ_CHUNK_LENGTH; } ++i; break; case TDS_STATE_READ_CHUNK_LENGTH: tds_buffer_add( state, c ); if( state->buffer_length == 4 ) { state->chunk_length = tds_read_le_uint( state->buffer ); state->buffer_length = 0; switch( state->chunk_id ) { case 0x4d4d: /* MAIN CHUNK */ case 0x4100: /* TRI_OBJECT */ case 0x3d3d: /* 3D EDITOR CHUNK */ state->parsing_state = TDS_STATE_READ_CHUNK_ID; break; case 0x4000: /* OBJECT */ state->parsing_state = TDS_STATE_READ_OBJECT_NAME; break; case 0x4110: /* POINT_ARRAY */ state->parsing_state = TDS_STATE_READ_POINT_COUNT; break; case 0x4120: /* FACE_ARRAY */ state->parsing_state = TDS_STATE_READ_FACE_COUNT; break; case 0x4140: /* TEX_ARRAY */ state->parsing_state = TDS_STATE_READ_TEXCOORD_COUNT; break; default: state->parsing_state = TDS_STATE_SKIP_CHUNK; state->counter = 6; break; } } ++i; break; case TDS_STATE_READ_OBJECT_NAME: tds_buffer_add( state, c ); if( c == 0 ) { tds_object_buffer_add( state, state->buffer, state->buffer_length ); /* continue with chunks */ state->parsing_state = TDS_STATE_READ_CHUNK_ID; state->buffer_length = 0; } ++i; break; case TDS_STATE_READ_POINT_COUNT: tds_buffer_add( state, c ); if( state->buffer_length == 2 ) { state->item_count = tds_read_le_ushort( state->buffer ); tds_point_buffer_grow( state, state->item_count ); state->parsing_state = TDS_STATE_READ_POINTS; state->buffer_length = 0; state->counter = 0; } ++i; break; case TDS_STATE_READ_POINTS: tds_buffer_add( state, c ); if( state->buffer_length == 12 ) { tds_point_buffer_add( state, tds_read_le_float( state->buffer ), tds_read_le_float( state->buffer + 4 ), tds_read_le_float( state->buffer + 8 ) ); state->counter++; state->buffer_length = 0; if( state->counter >= state->item_count ) { state->parsing_state = TDS_STATE_READ_CHUNK_ID; state->buffer_length = 0; } } ++i; break; case TDS_STATE_READ_TEXCOORD_COUNT: tds_buffer_add( state, c ); if( state->buffer_length == 2 ) { state->item_count = tds_read_le_ushort( state->buffer ); tds_texcoord_buffer_grow( state, state->item_count ); state->parsing_state = TDS_STATE_READ_TEXCOORDS; state->buffer_length = 0; state->counter = 0; } ++i; break; case TDS_STATE_READ_TEXCOORDS: tds_buffer_add( state, c ); if( state->buffer_length == 8 ) { tds_texcoord_buffer_add( state, tds_read_le_float( state->buffer ), tds_read_le_float( state->buffer + 4 ) ); state->counter++; state->buffer_length = 0; if( state->counter >= state->item_count ) state->parsing_state = TDS_STATE_READ_CHUNK_ID; } ++i; break; case TDS_STATE_READ_FACE_COUNT: tds_buffer_add( state, c ); if( state->buffer_length == 2 ) { state->item_count = tds_read_le_ushort( state->buffer ); tds_face_buffer_grow( state, state->item_count ); state->object_buffer[state->object_count-1]->count = state->item_count; if( state->object_count > 1 ) state->object_buffer[state->object_count - 1]->index = state->object_buffer[state->object_count-2]->index + state->object_buffer[state->object_count-2]->count; state->parsing_state = TDS_STATE_READ_FACES; state->buffer_length = 0; state->counter = 0; } ++i; break; case TDS_STATE_READ_FACES: tds_buffer_add( state, c ); if( state->buffer_length == 8 ) { tds_face_buffer_add( state, tds_read_le_ushort( state->buffer ), tds_read_le_ushort( state->buffer + 2), tds_read_le_ushort( state->buffer + 4 ) ); state->counter++; state->buffer_length = 0; if( state->counter >= state->item_count ) state->parsing_state = TDS_STATE_READ_CHUNK_ID; } ++i; break; case TDS_STATE_SKIP_CHUNK: ++i; ++state->counter; if( state->counter >= state->chunk_length ) state->parsing_state = TDS_STATE_READ_CHUNK_ID; break; default: ++i; break; } } if( last ) state->parsing_state = TDS_STATE_DONE; return 0; } /*----------------------------------------------------------------------------*/ unsigned int tl3dsObjectCount( tl3dsState *state ) { if( state == NULL ) return 0; if( state->parsing_state != TDS_STATE_DONE ) return 0; return state->object_count; } /*----------------------------------------------------------------------------*/ const char *tl3dsObjectName( tl3dsState *state, unsigned int object ) { if( state == NULL ) return NULL; if( state->parsing_state != TDS_STATE_DONE ) return NULL; if( object >= state->object_count ) return NULL; return state->object_buffer[object]->name; } /*----------------------------------------------------------------------------*/ unsigned int tl3dsObjectFaceCount( tl3dsState *state, unsigned int object ) { if( state == NULL ) return 0; if( state->parsing_state != TDS_STATE_DONE ) return 0; if( object >= state->object_count ) return 0; return state->object_buffer[object]->count; } /*----------------------------------------------------------------------------*/ unsigned int tl3dsObjectFaceIndex( tl3dsState *state, unsigned int object ) { if( state == NULL ) return 0; if( state->parsing_state != TDS_STATE_DONE ) return 0; if( object >= state->object_count ) return 0; return state->object_buffer[object]->index; } /*----------------------------------------------------------------------------*/ unsigned int tl3dsVertexCount( tl3dsState *state ) { if( state == NULL ) return 0; if( state->parsing_state != TDS_STATE_DONE ) return 0; return state->point_count; } /*----------------------------------------------------------------------------*/ int tl3dsGetVertexDouble( tl3dsState *state, unsigned int index, double *x, double *y, double *z, double *tu, double *tv, double *nx, double *ny, double *nz ) { if( state == NULL ) return 1; if( index >= state->point_count ) return 1; if( state->point_buffer && index < state->point_count ) { if( x ) *x = (float)state->point_buffer[ index * 3 ]; if( y ) *y = (float)state->point_buffer[ index * 3 + 1]; if( z ) *z = (float)state->point_buffer[ index * 3 + 2]; } if( state->texcoord_buffer && index < state->texcoord_count ) { if( tu ) *tu = (float)state->texcoord_buffer[ index * 2 ]; if( tv ) *tv = (float)state->texcoord_buffer[ index * 2 + 1]; } if( nx ) *nx = 0; if( ny ) *ny = 0; if( nz ) *nz = 0; return 0; } /*----------------------------------------------------------------------------*/ int tl3dsGetVertex( tl3dsState *state, unsigned int index, float *x, float *y, float *z, float *tu, float *tv, float *nx, float *ny, float *nz ) { if( state == NULL ) return 1; if( index >= state->point_count ) return 1; if( state->point_buffer && index < state->point_count ) { if( x ) *x = (float)state->point_buffer[ index * 3 ]; if( y ) *y = (float)state->point_buffer[ index * 3 + 1]; if( z ) *z = (float)state->point_buffer[ index * 3 + 2]; } if( state->texcoord_buffer && index < state->texcoord_count ) { if( tu ) *tu = (float)state->texcoord_buffer[ index * 2 ]; if( tv ) *tv = (float)state->texcoord_buffer[ index * 2 + 1]; } if( nx ) *nx = 0; if( ny ) *ny = 0; if( nz ) *nz = 0; return 0; } /*----------------------------------------------------------------------------*/ unsigned int tl3dsFaceCount( tl3dsState *state ) { if( state == NULL ) return 0; return state->face_count; } /*----------------------------------------------------------------------------*/ int tl3dsGetFaceInt( tl3dsState *state, unsigned int index, unsigned int *a, unsigned int *b, unsigned int *c ) { unsigned int face = 0; if( state == NULL ) return 1; if( state->face_buffer && face <= state->face_count ) { if( a ) *a = state->face_buffer[ index * 3 ]; if( b ) *b = state->face_buffer[ index * 3 + 1 ]; if( c ) *c = state->face_buffer[ index * 3 + 2 ]; } return 0; } /*----------------------------------------------------------------------------*/ int tl3dsGetFace( tl3dsState *state, unsigned int index, unsigned short *a, unsigned short *b, unsigned short *c ) { unsigned int face = 0; if( state == NULL ) return 1; if( state->face_buffer && face <= state->face_count ) { if( a ) *a = state->face_buffer[ index * 3 ]; if( b ) *b = state->face_buffer[ index * 3 + 1 ]; if( c ) *c = state->face_buffer[ index * 3 + 2 ]; } return 0; } /*----------------------------------------------------------------------------*/ int tl3dsCheckFileExtension( const char *filename ) { const char *ext = 0, *tmp = filename; if( filename == NULL ) return 1; while( *tmp != 0 ) { if( *tmp == '.' ) ext = tmp + 1; tmp++; } /* no extension found */ if( ext == 0 ) return 1; if( (ext[0] == '3') && (ext[1] == 'd' || ext[1] == 'D') && (ext[2] == 's' || ext[2] == 'S') && (ext[3] == 0) ) return 0; return 1; }