gremlin/libs/trimeshloader/src/tl3ds.c

910 lines
19 KiB
C
Raw Normal View History

2011-01-19 22:20:08 +00:00
/*
* Copyright (c) 2007 Gero Mueller <gero.mueller@cloo.de>
*
* 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 <string.h>
#include <stdlib.h>
/*----------------------------------------------------------------------------*/
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;
}