//
// 3D Studio .ASE loader
//
// The loader is not complete. The person who designed the ASE file was either drunk,
// or dead tired, because the node TM and vertex transform stuff is plain stupid!
//

#pragma warning( disable : 4786 )		// long names by STL

#include "MASLoaderC.h"
#include <stdio.h>
#include <stdlib.h>

#include "ScenegraphItemI.h"
#include "MeshC.h"
#include "CameraC.h"
#include "LightC.h"
#include "ShapeC.h"
#include "ParticleSystemC.h"
#include "WSMObjectC.h"
#include "ContVector3C.h"
#include "ContQuatC.h"
#include "ContFloatC.h"
#include "ContBoolC.h"
#include "DecomposeAffineC.h"
#include "MaterialC.h"

using namespace PajaTypes;
using namespace std;
using namespace Composition;
using namespace Import;
using namespace PluginClass;
using namespace FileIO;



static
void
TRACE( const char* szFormat, ...  )
{
	char	szMsg[256];
	va_list	rList;
	va_start( rList, szFormat );
	_vsnprintf( szMsg, 255, szFormat, rList );
	va_end( rList );
	OutputDebugString( szMsg );
}


MASLoaderC::MASLoaderC()
{
	// empty
}


MASLoaderC::~MASLoaderC()
{
	uint32	i;

	for( i = 0; i < m_rMaterials.size(); i++ ) {
		delete m_rMaterials[i];
	}
	m_rMaterials.clear();
}


void
MASLoaderC::remove_nl( char* buf )
{
	uint32	len = strlen( buf );
	if( buf[len - 1] == '\n' )
		buf[len - 1] = '\0';
}

char*
MASLoaderC::extract_string( char* szBuf )
{
	if( !szBuf || !*szBuf )
		return 0;

	static char		szWord[512];

	char*	szSrc = szBuf;
	char*	szDst = szWord;

	// find first delimit
	while( *szSrc && *szSrc != '\"' )
		szSrc++;

	// step over the first delimiter
	szSrc++;

	// find second delimit and copy the between to szWord
	while( *szSrc && *szSrc != '\"' )
		*szDst++ = *szSrc++;

	// terminate the string
	*szDst = '\0';

	return szWord;
}


static
int
stream_get_row( char* pRow, int n, FILE* pStream )
{
	int		c;
	int		i = 0;
	
	do {
		c = fgetc( pStream );
		pRow[i] = c;
		i++;
		if( c == 0xd )
			break;
		if( i >= n )
			break;
	} while( !feof( pStream ) );
	pRow[i] = '\0';	// null terminate string
	
	return i;
}


void
MASLoaderC::read_row()
{
	if( !m_pStream )
		return;
	stream_get_row( m_szRow, 1000, m_pStream );
	remove_nl( m_szRow );
}

bool
MASLoaderC::eof()
{
	return feof( m_pStream ) != 0;
}

bool
MASLoaderC::is_token( const char* szToken )
{
	sscanf( m_szRow, "%s", m_szWord );
	return (strcmp( m_szWord, szToken ) == 0 );
}

char*
MASLoaderC::get_row()
{
	return m_szRow;
}

bool
MASLoaderC::is_block()
{
	for( uint32 i = 0; i < strlen( m_szRow ); i++ )
		if( m_szRow[i] == '{' )
			return true;
	return false;
}


uint32
MASLoaderC::parse_dummy()
{
	do {
		read_row();
		if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );
  
	return 0;
}


int32
MASLoaderC::get_ort( const char* szORT )
{
	int32	i32ORT = CONT_ORT_CONSTANT;

	if( strcmp( "ORT_CYCLE", szORT ) == 0 )
		i32ORT = CONT_ORT_REPEAT;
	else if( strcmp( "ORT_LOOP", szORT ) == 0 )
		i32ORT = CONT_ORT_LOOP;

	return i32ORT;
}


//
// Position tracks
//

ContVector3C*
MASLoaderC::parse_tcb_pos_track()
{

	ContVector3C*	pCont = new ContVector3C;

	int32		i32Time;
	float32		f32X, f32Y, f32Z;
	float32		f32Tens, f32Cont, f32Bias, f32EaseIn, f32EaseOut;
	char		szStartORT[64], szEndORT[64];

	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			pCont->set_start_ort( get_ort( szStartORT ) );
			pCont->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*KEYS" ) ) {
			read_row();
			while( !is_token( "}" ) ) {
				KeyVector3C*	pKey = pCont->add_key();

				sscanf( get_row(), "%d %f %f %f %f %f %f %f %f",
					&i32Time, &f32X, &f32Y, &f32Z,
					&f32Tens, &f32Cont, &f32Bias,
					&f32EaseIn, &f32EaseOut );

				pKey->set_time( i32Time );
				pKey->set_value( Vector3C( f32X, f32Y, f32Z ) );
				pKey->set_tens( f32Tens );
				pKey->set_cont( f32Cont );
				pKey->set_bias( f32Bias);
				pKey->set_ease_in( f32EaseIn );
				pKey->set_ease_out( f32EaseOut );

				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pCont->sort_keys();
	pCont->prepare();

	if( pCont->get_key_count() == 0 ) {
		delete pCont;
		pCont = 0;
	}

	return pCont;
}


ContVector3C*
MASLoaderC::parse_lin_pos_track()
{
	ContVector3C*	pCont = new ContVector3C( KEY_LINEAR );

	int32		i32Time;
	float32		f32X, f32Y, f32Z;
	char		szStartORT[64], szEndORT[64];

	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			pCont->set_start_ort( get_ort( szStartORT ) );
			pCont->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*KEYS" ) ) {
			read_row();
			while( !is_token( "}" ) ) {
				KeyVector3C*	pKey = pCont->add_key();

				sscanf( get_row(), "%d %f %f %f",
					&i32Time, &f32X, &f32Y, &f32Z );

				pKey->set_time( i32Time );
				pKey->set_value( Vector3C( f32X, f32Y, f32Z ) );
				pKey->set_tens( 0 );
				pKey->set_cont( 0 );
				pKey->set_bias( 0 );
				pKey->set_ease_in( 0 );
				pKey->set_ease_out( 0 );

				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pCont->sort_keys();
	pCont->prepare();

	if( pCont->get_key_count() == 0 ) {
		delete pCont;
		pCont = 0;
	}

	return pCont;
}

ContVector3C*
MASLoaderC::parse_sample_pos_track()
{
	ContVector3C*	pCont = new ContVector3C( KEY_LINEAR );

	int32		i32Time;
	float32		f32X, f32Y, f32Z;
	char		szStartORT[64], szEndORT[64];

	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			pCont->set_start_ort( get_ort( szStartORT ) );
			pCont->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*SAMPLES" ) ) {
			read_row();
			while( !is_token( "}" ) ) {
				KeyVector3C*	pKey = pCont->add_key();

				sscanf( get_row(), "%d %f %f %f",
					&i32Time, &f32X, &f32Y, &f32Z );

				pKey->set_time( i32Time );
				pKey->set_value( Vector3C( f32X, f32Y, f32Z ) );
				pKey->set_tens( 0 );
				pKey->set_cont( 0 );
				pKey->set_bias( 0 );
				pKey->set_ease_in( 0 );
				pKey->set_ease_out( 0 );

				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pCont->sort_keys();
	pCont->prepare();

	if( pCont->get_key_count() == 0 ) {
		delete pCont;
		pCont = 0;
	}

	return pCont;
}


//
// Vector3 Tracks
//

ContVector3C*
MASLoaderC::parse_tcb_vector3_track()
{
	ContVector3C*	pCont = new ContVector3C;

	int32		i32Time;
	float32		f32X, f32Y, f32Z;
	float32		f32Tens, f32Cont, f32Bias, f32EaseIn, f32EaseOut;
	char		szStartORT[64], szEndORT[64];
	
	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			pCont->set_start_ort( get_ort( szStartORT ) );
			pCont->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*KEYS" ) ) {
			read_row();
			while( !is_token( "}" ) ) {
				KeyVector3C*	pKey = pCont->add_key();

				sscanf( get_row(), "%d %f %f %f %f %f %f %f %f",
					&i32Time, &f32X, &f32Y, &f32Z,
					&f32Tens, &f32Cont, &f32Bias,
					&f32EaseIn, &f32EaseOut );

				pKey->set_time( i32Time );
				pKey->set_value( Vector3C( f32X, f32Y, f32Z ) );
				pKey->set_tens( f32Tens );
				pKey->set_cont( f32Cont );
				pKey->set_bias( f32Bias);
				pKey->set_ease_in( f32EaseIn );
				pKey->set_ease_out( f32EaseOut );
				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pCont->sort_keys();
	pCont->prepare();

	if( pCont->get_key_count() == 0 ) {
		delete pCont;
		pCont = 0;
	}

	return pCont;
}


ContVector3C*
MASLoaderC::parse_lin_vector3_track()
{
	ContVector3C*	pCont = new ContVector3C( KEY_LINEAR );

	int32		i32Time;
	float32		f32X, f32Y, f32Z;
	char		szStartORT[64], szEndORT[64];

	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			pCont->set_start_ort( get_ort( szStartORT ) );
			pCont->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*KEYS" ) ) {
			read_row();
			while( !is_token( "}" ) ) {
				KeyVector3C*	pKey = pCont->add_key();

				sscanf( get_row(), "%d %f %f %f",
					&i32Time, &f32X, &f32Y, &f32Z );

				pKey->set_time( i32Time );
				pKey->set_value( Vector3C( f32X, f32Y, f32Z ) );
				pKey->set_tens( 0 );
				pKey->set_cont( 0 );
				pKey->set_bias( 0 );
				pKey->set_ease_in( 0 );
				pKey->set_ease_out( 0 );

				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pCont->sort_keys();
	pCont->prepare();

	if( pCont->get_key_count() == 0 ) {
		delete pCont;
		pCont = 0;
	}

	return pCont;
}

ContVector3C*
MASLoaderC::parse_sample_vector3_track()
{
	ContVector3C*	pCont = new ContVector3C( KEY_LINEAR );

	int32		i32Time;
	float32		f32X, f32Y, f32Z;
	char		szStartORT[64], szEndORT[64];

	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			pCont->set_start_ort( get_ort( szStartORT ) );
			pCont->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*SAMPLES" ) ) {
			read_row();
			while( !is_token( "}" ) ) {
				KeyVector3C*	pKey = pCont->add_key();

				sscanf( get_row(), "%d %f %f %f",
					&i32Time, &f32X, &f32Y, &f32Z );

				pKey->set_time( i32Time );
				pKey->set_value( Vector3C( f32X, f32Y, f32Z ) );
				pKey->set_tens( 0 );
				pKey->set_cont( 0 );
				pKey->set_bias( 0 );
				pKey->set_ease_in( 0 );
				pKey->set_ease_out( 0 );

				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pCont->sort_keys();
	pCont->prepare();

	if( pCont->get_key_count() == 0 ) {
		delete pCont;
		pCont = 0;
	}

	return pCont;
}

//
// Float tracks
//

ContFloatC*
MASLoaderC::parse_tcb_float_track()
{
	ContFloatC*	pCont = new ContFloatC;

	int32		i32Time;
	float32		f32Val;
	float32		f32Tens, f32Cont, f32Bias, f32EaseIn, f32EaseOut;
	char		szStartORT[64], szEndORT[64];
	
	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			pCont->set_start_ort( get_ort( szStartORT ) );
			pCont->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*KEYS" ) ) {
			read_row();
			while( !is_token( "}" ) ) {
				KeyFloatC*	pKey = pCont->add_key();

				sscanf( get_row(), "%d %f %f %f %f %f %f",
					&i32Time, &f32Val,
					&f32Tens, &f32Cont, &f32Bias,
					&f32EaseIn, &f32EaseOut );

				pKey->set_time( i32Time );
				pKey->set_value( f32Val );
				pKey->set_tens( f32Tens );
				pKey->set_cont( f32Cont );
				pKey->set_bias( f32Bias);
				pKey->set_ease_in( f32EaseIn );
				pKey->set_ease_out( f32EaseOut );
				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pCont->sort_keys();
	pCont->prepare();

	if( pCont->get_key_count() == 0 ) {
		delete pCont;
		pCont = 0;
	}

	return pCont;
}

ContFloatC*
MASLoaderC::parse_lin_float_track()
{
	ContFloatC*	pCont = new ContFloatC( KEY_LINEAR );

	int32		i32Time;
	float32		f32Val;
	char		szStartORT[64], szEndORT[64];
	
	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			pCont->set_start_ort( get_ort( szStartORT ) );
			pCont->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*KEYS" ) ) {
			read_row();
			while( !is_token( "}" ) ) {
				KeyFloatC*	pKey = pCont->add_key();

				sscanf( get_row(), "%d %f",
					&i32Time, &f32Val );

				pKey->set_time( i32Time );
				pKey->set_value( f32Val );
				pKey->set_tens( 0 );
				pKey->set_cont( 0 );
				pKey->set_bias( 0);
				pKey->set_ease_in( 0 );
				pKey->set_ease_out( 0 );

				pCont->set_type( KEY_LINEAR );
				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pCont->sort_keys();
	pCont->prepare();

	if( pCont->get_key_count() == 0 ) {
		delete pCont;
		pCont = 0;
	}

	return pCont;
}

ContFloatC*
MASLoaderC::parse_sample_float_track()
{
	ContFloatC*	pCont = new ContFloatC( KEY_LINEAR );

	int32		i32Time;
	float32		f32Val;
	char		szStartORT[64], szEndORT[64];
	
	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			pCont->set_start_ort( get_ort( szStartORT ) );
			pCont->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*SAMPLES" ) ) {
			read_row();
			while( !is_token( "}" ) ) {
				KeyFloatC*	pKey = pCont->add_key();

				sscanf( get_row(), "%d %f",
					&i32Time, &f32Val );

				pKey->set_time( i32Time );
				pKey->set_value( f32Val );
				pKey->set_tens( 0 );
				pKey->set_cont( 0 );
				pKey->set_bias( 0);
				pKey->set_ease_in( 0 );
				pKey->set_ease_out( 0 );

				pCont->set_type( KEY_LINEAR );

				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pCont->sort_keys();
	pCont->prepare();

	if( pCont->get_key_count() == 0 ) {
		delete pCont;
		pCont = 0;
	}

	return pCont;
}


//
// Rotation tracks
//

ContQuatC*
MASLoaderC::parse_tcb_rot_track()
{
	ContQuatC*	pCont = new ContQuatC;

	int32		i32Time;
	float32		f32X, f32Y, f32Z, f32W;
	float32		f32Tens, f32Cont, f32Bias, f32EaseIn, f32EaseOut;
	char		szStartORT[64], szEndORT[64];
	
	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			pCont->set_start_ort( get_ort( szStartORT ) );
			pCont->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*KEYS" ) ) {
			read_row();
			while( !is_token( "}" ) ) {

				KeyQuatC*	pKey = pCont->add_key();

				sscanf( get_row(), "%d %f %f %f %f %f %f %f %f %f",
					&i32Time, &f32X, &f32Y, &f32Z, &f32W,
					&f32Tens, &f32Cont, &f32Bias,
					&f32EaseIn, &f32EaseOut );

				pKey->set_time( i32Time );
				pKey->set_axis( Vector3C( f32X, f32Y, f32Z ) );
				pKey->set_angle( f32W );
				pKey->set_tens( f32Tens );
				pKey->set_cont( f32Cont );
				pKey->set_bias( f32Bias);
				pKey->set_ease_in( f32EaseIn );
				pKey->set_ease_out( f32EaseOut );

				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pCont->sort_keys();
	pCont->prepare();

	if( pCont->get_key_count() == 0 ) {
		delete pCont;
		pCont = 0;
	}

	return pCont;
}

ContQuatC*
MASLoaderC::parse_lin_rot_track()
{
	ContQuatC*	pCont = new ContQuatC( KEY_LINEAR );

	int32		i32Time;
	float32		f32X, f32Y, f32Z, f32W;
	char		szStartORT[64], szEndORT[64];
	
	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			pCont->set_start_ort( get_ort( szStartORT ) );
			pCont->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*KEYS" ) ) {
			read_row();
			while( !is_token( "}" ) ) {

				KeyQuatC*	pKey = pCont->add_key();

				sscanf( get_row(), "%d %f %f %f %f",
					&i32Time, &f32X, &f32Y, &f32Z, &f32W );

				pKey->set_time( i32Time );
				pKey->set_axis( Vector3C( f32X, f32Y, f32Z ) );
				pKey->set_angle( f32W );
				pKey->set_tens( 0 );
				pKey->set_cont( 0 );
				pKey->set_bias( 0 );
				pKey->set_ease_in( 0 );
				pKey->set_ease_out( 0 );

				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pCont->sort_keys();
	pCont->prepare();

	if( pCont->get_key_count() == 0 ) {
		delete pCont;
		pCont = 0;
	}

	return pCont;
}

ContQuatC*
MASLoaderC::parse_sample_rot_track()
{
	ContQuatC*	pCont = new ContQuatC( KEY_LINEAR );

	int32		i32Time;
	float32		f32X, f32Y, f32Z, f32W;
	char		szStartORT[64], szEndORT[64];
	
	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			pCont->set_start_ort( get_ort( szStartORT ) );
			pCont->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*SAMPLES" ) ) {
			read_row();
			while( !is_token( "}" ) ) {

				KeyQuatC*	pKey = pCont->add_key();

				sscanf( get_row(), "%d %f %f %f %f",
					&i32Time, &f32X, &f32Y, &f32Z, &f32W );

				pKey->set_time( i32Time );
				pKey->set_axis( Vector3C( f32X, f32Y, f32Z ) );
				pKey->set_angle( f32W );
				pKey->set_tens( 0 );
				pKey->set_cont( 0 );
				pKey->set_bias( 0 );
				pKey->set_ease_in( 0 );
				pKey->set_ease_out( 0 );

				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pCont->sort_keys();
	pCont->prepare();

	if( pCont->get_key_count() == 0 ) {
		delete pCont;
		pCont = 0;
	}

	return pCont;
}


//
// Scale Tracks
//

void
MASLoaderC::parse_tcb_scale_track( ContVector3C** pOutContScale, ContQuatC** pOutContRot )
{
	ContVector3C*	pContScale = new ContVector3C;
	ContQuatC*		pContRot = new ContQuatC;

	int32		i32Time;
	float32		f32SX, f32SY, f32SZ, f32X, f32Y, f32Z, f32W;
	float32		f32Tens, f32Cont, f32Bias, f32EaseIn, f32EaseOut;
	char		szStartORT[64], szEndORT[64];
	
	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			
			pContRot->set_start_ort( get_ort( szStartORT ) );
			pContRot->set_end_ort( get_ort( szEndORT ) );

			pContScale->set_start_ort( get_ort( szStartORT ) );
			pContScale->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*KEYS" ) ) {
			read_row();
			while( !is_token( "}" ) ) {

				KeyQuatC*		pRotKey = pContRot->add_key();
				KeyVector3C*	pScaleKey = pContScale->add_key();

				sscanf( get_row(), "%d %f %f %f %f %f %f %f %f %f %f %f %f",
					&i32Time, &f32SX, &f32SY, &f32SZ,
					&f32X, &f32Y, &f32Z, &f32W,
					&f32Tens, &f32Cont, &f32Bias,
					&f32EaseIn, &f32EaseOut );

				pScaleKey->set_time( i32Time );
				pScaleKey->set_value( Vector3C( f32SX, f32SY, f32SZ ) );
				pScaleKey->set_tens( f32Tens );
				pScaleKey->set_cont( f32Cont );
				pScaleKey->set_bias( f32Bias);
				pScaleKey->set_ease_in( f32EaseIn );
				pScaleKey->set_ease_out( f32EaseOut );

				pRotKey->set_time( i32Time );
				pRotKey->set_axis( Vector3C( f32X, f32Y, f32Z ) );
				pRotKey->set_angle( f32W );
				pRotKey->set_tens( f32Tens );
				pRotKey->set_cont( f32Cont );
				pRotKey->set_bias( f32Bias);
				pRotKey->set_ease_in( f32EaseIn );
				pRotKey->set_ease_out( f32EaseOut );

				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pContScale->sort_keys();
	pContScale->prepare();

	pContRot->sort_keys();
	pContRot->prepare();

	if( pContScale->get_key_count() == 0 ) {
		delete pContScale;
		pContScale = 0;
	}
	if( pContRot->get_key_count() == 0 ) {
		delete pContRot;
		pContRot = 0;
	}

	*pOutContScale = pContScale;
	*pOutContRot = pContRot;
}

void
MASLoaderC::parse_lin_scale_track( ContVector3C** pOutContScale, ContQuatC** pOutContRot  )
{
	ContVector3C*	pContScale = new ContVector3C( KEY_LINEAR );
	ContQuatC*		pContRot = new ContQuatC( KEY_LINEAR );

	int32		i32Time;
	float32		f32SX, f32SY, f32SZ, f32X, f32Y, f32Z, f32W;
	char		szStartORT[64], szEndORT[64];
	
	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			
			pContRot->set_start_ort( get_ort( szStartORT ) );
			pContRot->set_end_ort( get_ort( szEndORT ) );

			pContScale->set_start_ort( get_ort( szStartORT ) );
			pContScale->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*KEYS" ) ) {
			read_row();
			while( !is_token( "}" ) ) {

				KeyQuatC*		pRotKey = pContRot->add_key();
				KeyVector3C*	pScaleKey = pContScale->add_key();

				sscanf( get_row(), "%d %f %f %f %f %f %f %f",
					&i32Time, &f32SX, &f32SY, &f32SZ,
					&f32X, &f32Y, &f32Z, &f32W );

				pScaleKey->set_time( i32Time );
				pScaleKey->set_value( Vector3C( f32SX, f32SY, f32SZ ) );
				pScaleKey->set_tens( 0 );
				pScaleKey->set_cont( 0 );
				pScaleKey->set_bias( 0 );
				pScaleKey->set_ease_in( 0 );
				pScaleKey->set_ease_out( 0 );

				pRotKey->set_time( i32Time );
				pRotKey->set_axis( Vector3C( f32X, f32Y, f32Z ) );
				pRotKey->set_angle( f32W );
				pRotKey->set_tens( 0 );
				pRotKey->set_cont( 0 );
				pRotKey->set_bias( 0 );
				pRotKey->set_ease_in( 0 );
				pRotKey->set_ease_out( 0 );

				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pContScale->sort_keys();
	pContScale->prepare();

	pContRot->sort_keys();
	pContRot->prepare();

	if( pContScale->get_key_count() == 0 ) {
		delete pContScale;
		pContScale = 0;
	}
	if( pContRot->get_key_count() == 0 ) {
		delete pContRot;
		pContRot = 0;
	}

	*pOutContScale = pContScale;
	*pOutContRot = pContRot;
}

void
MASLoaderC::parse_sample_scale_track( ContVector3C** pOutContScale, ContQuatC** pOutContRot  )
{
	ContVector3C*	pContScale = new ContVector3C( KEY_LINEAR );
	ContQuatC*		pContRot = new ContQuatC( KEY_LINEAR );

	int32		i32Time;
	float32		f32SX, f32SY, f32SZ, f32X, f32Y, f32Z, f32W;
	char		szStartORT[64], szEndORT[64];
	
	do {
		read_row();

		if( is_token( "*ORT" ) ) {
			sscanf( get_row(), "%*s %s %s", szStartORT, szEndORT );
			pContRot->set_start_ort( get_ort( szStartORT ) );
			pContRot->set_end_ort( get_ort( szEndORT ) );
			pContScale->set_start_ort( get_ort( szStartORT ) );
			pContScale->set_end_ort( get_ort( szEndORT ) );
		}
		else if( is_token( "*SAMPLES" ) ) {
			read_row();
			while( !is_token( "}" ) ) {

				KeyQuatC*		pRotKey = pContRot->add_key();
				KeyVector3C*	pScaleKey = pContScale->add_key();

				sscanf( get_row(), "%d %f %f %f %f %f %f %f",
					&i32Time, &f32SX, &f32SY, &f32SZ,
					&f32X, &f32Y, &f32Z, &f32W );

				pScaleKey->set_time( i32Time );
				pScaleKey->set_value( Vector3C( f32SX, f32SY, f32SZ ) );
				pScaleKey->set_tens( 0 );
				pScaleKey->set_cont( 0 );
				pScaleKey->set_bias( 0 );
				pScaleKey->set_ease_in( 0 );
				pScaleKey->set_ease_out( 0 );

				pRotKey->set_time( i32Time );
				pRotKey->set_axis( Vector3C( f32X, f32Y, f32Z ) );
				pRotKey->set_angle( f32W );
				pRotKey->set_tens( 0 );
				pRotKey->set_cont( 0 );
				pRotKey->set_bias( 0 );
				pRotKey->set_ease_in( 0 );
				pRotKey->set_ease_out( 0 );

				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pContScale->sort_keys();
	pContScale->prepare();
	pContRot->sort_keys();
	pContRot->prepare();

	if( pContScale->get_key_count() == 0 ) {
		delete pContScale;
		pContScale = 0;
	}
	if( pContRot->get_key_count() == 0 ) {
		delete pContRot;
		pContRot = 0;
	}

	*pOutContScale = pContScale;
	*pOutContRot = pContRot;
}


//
// Bool tracks
//

ContBoolC*
MASLoaderC::parse_bool_track(  )
{
	ContBoolC*	pCont = new ContBoolC;

	int32		i32Time;
	float32		f32StartVal;
	
	do {
		read_row();

		if( is_token( "*START_ON_OFF" ) ) {
			sscanf( get_row(), "%*s %f", &f32StartVal );
			pCont->set_start_val( f32StartVal > 0 ? true : false );
		}
		else if( is_token( "*KEYS" ) ) {
			read_row();
			while( !is_token( "}" ) ) {
				KeyBoolC*	pKey = pCont->add_key();

				sscanf( get_row(), "%d",
					&i32Time );

				pKey->set_time( i32Time );
				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pCont->sort_keys();

	return pCont;
}

bool
MASLoaderC::parse_look_at_node_tm( Vector3C& rPos, PajaTypes::float32& f32Roll )
{
	float32		f32X, f32Y, f32Z, f32Val;
	bool		bIsTarget = false;

	f32Roll = 0;

	do {
		read_row();

		if( is_token( "*TM_POS" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32X, &f32Y, &f32Z );
			rPos[0] = f32X;
			rPos[1] = f32Y;
			rPos[2] = f32Z;
		}
		else if( is_token( "*ROLL_ANGLE" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			f32Roll = f32Val;
		}
		else if( is_token( "*NODE_NAME" ) ) {
			if( strstr( get_row(), ".Target" ) )
				bIsTarget = true;
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );

	return bIsTarget;
}


//
// light
//


void
MASLoaderC::parse_light_param_anim( LightC* pLight )
{
	char	szParam[128] = "";

	ContFloatC*		pFloatCont = 0;
	ContVector3C*	pVecCont = 0;

	do {
		read_row();

		if( is_token( "*PARAMETER" ) ) {
			sscanf( get_row(), "%*s %s", szParam );
		}
		else if( is_token( "*CONTROL_FLOAT_TCB" ) ) {
			pFloatCont = parse_tcb_float_track();
		}
		else if( is_token( "*CONTROL_FLOAT_SAMPLE" ) ) {
			pFloatCont = parse_sample_float_track();
		}
		else if( is_token( "*CONTROL_FLOAT_LINEAR" ) ) {
			pFloatCont = parse_lin_float_track();
		}
		else if( is_token( "*CONTROL_POINT3_TCB" ) ) {
			pVecCont = parse_tcb_vector3_track();
		}
		else if( is_token( "*CONTROL_POINT3_SAMPLE" ) ) {
			pVecCont = parse_sample_vector3_track();
		}
		else if( is_token( "*CONTROL_POINT3_LINEAR" ) ) {
			pVecCont = parse_lin_vector3_track();
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	
	if( pFloatCont ) {
		if( strcmp( szParam, "LIGHT_INTENS" ) == 0 )
			pLight->set_multiplier_controller( pFloatCont );
		else if( strcmp( szParam, "LIGHT_FALLOFF" ) == 0 )
			pLight->set_falloff_controller( pFloatCont );
		else if( strcmp( szParam, "LIGHT_HOTSPOT" ) == 0 )
			pLight->set_hotspot_controller( pFloatCont );
		else if( strcmp( szParam, "LIGHT_DECAY" ) == 0 )
			pLight->set_decay_controller( pFloatCont );
		else
			delete pFloatCont;
	}
	if( pVecCont ) {
		if( strcmp( szParam, "LIGHT_COLOR" ) == 0 )
			pLight->set_color_controller( pVecCont );
		else
			delete pVecCont;
	}
}

void
MASLoaderC::parse_light_settings( LightC* pLight )
{
	float32	f32Val, f32R, f32G, f32B;

	do {
		read_row();
		if( is_token( "*LIGHT_COLOR" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32R, &f32G, &f32B );
			pLight->set_color( ColorC( f32R, f32G, f32B ) );
		}
		else if( is_token( "*LIGHT_INTENS" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pLight->set_multiplier( f32Val );
		}
		else if( is_token( "*LIGHT_HOTSPOT" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pLight->set_hotspot( f32Val );
		}
		else if( is_token( "*LIGHT_FALLOFF" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pLight->set_falloff( f32Val );
		}
		else if( is_token( "*LIGHT_DECAY" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pLight->set_decay( f32Val );
		}
		else if( is_token( "*PROJ_MAP" ) ) {
			std::string	sName = extract_string( get_row() );
			pLight->set_projection_texture( get_texture( sName ) );
		}
		else if( is_token( "*LIGHT_DECAY_TYPE" ) ) {
			char	szType[64];
			sscanf( get_row(), "%*s %s", szType );
			if( strcmp( szType, "NONE" ) == 0 )
				pLight->set_decay_type( DECAY_NONE );
			else if( strcmp( szType, "INVERSE" ) == 0 )
				pLight->set_decay_type( DECAY_INVERSE );
			else if( strcmp( szType, "INVERSE_SQR" ) == 0 )
				pLight->set_decay_type( DECAY_INVERSE_SQR );
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );
}

void
MASLoaderC::parse_light_tm_anim( LightC* pLight )
{
	bool		bIsTarget = false;

	do {
		read_row();

		if( is_token( "*NODE_NAME" ) ) {
			if( strstr( get_row(), ".Target" ) )
				bIsTarget = true;
		}
		else if( is_token( "*CONTROL_POS_TCB" ) ) {
			ContVector3C*	pCont = parse_tcb_pos_track();
			if( !bIsTarget ) {
				pLight->set_position_controller( pCont );
			}
			else {
				pLight->set_target_controller( pCont );
			}
		}
		else if( is_token( "*CONTROL_POS_TRACK" ) ) {
			ContVector3C*	pCont = parse_sample_pos_track();
			if( !bIsTarget ) {
				pLight->set_position_controller( pCont );
			}
			else {
				pLight->set_target_controller( pCont );
			}
		}
		else if( is_token( "*CONTROL_POS_LINEAR" ) ) {
			ContVector3C*	pCont = parse_lin_pos_track();
			if( !pCont->get_key_count() ) {
				delete pCont;
				continue;
			}
			if( !bIsTarget ) {
				pLight->set_position_controller( pCont );
			}
			else {
				pLight->set_target_controller( pCont );
			}
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );
}


LightC*
MASLoaderC::parse_light()
{
	LightC*	pLight = new LightC;
	if( !pLight )
		return 0;

	Vector3C	rPos;
	float32		f32Roll;

	do {
		read_row();

		if( is_token( "*NODE_NAME" ) ) {
			pLight->set_name( extract_string( get_row() ) );
		}
		else if( is_token( "*NODE_TM" ) ) {
			if( !parse_look_at_node_tm( rPos, f32Roll ) )
				pLight->set_position( rPos );
			else
				pLight->set_target( rPos );
		}
		else if( is_token( "*LIGHT_SETTINGS" ) ) {
			parse_light_settings( pLight );
		}
		else if( is_token( "*LIGHT_TYPE" ) ) {
			char	szType[64];
			sscanf( get_row(), "%*s %s", szType );
			if( strcmp( szType, "Target" ) == 0 )
				pLight->set_light_type( LIGHT_SPOT );
			else
				pLight->set_light_type( LIGHT_OMNI );
		}
		else if( is_token( "*LIGHT_SHADOWS" ) ) {
			char	szType[64];
			sscanf( get_row(), "%*s %s", szType );
			if( strcmp( szType, "Off" ) == 0 )
				pLight->set_cast_shadows( false );
			else
				pLight->set_cast_shadows( true );
		}
		else if( is_token( "*LIGHT_ANIMATION" ) ) {
			do {
				read_row();
				if( is_token( "*PARAMETER_ANIMATION" ) ) {
					parse_light_param_anim( pLight );
				}
				else if( is_block() )
					parse_dummy();
				else if( is_token( "}" ) )
					break;
			} while( !eof() );
		}
		else if( is_token( "*TM_ANIMATION" ) ) {
			parse_light_tm_anim( pLight );
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );

	return pLight;
}


//
// camera
//


void
MASLoaderC::parse_camera_param_anim( CameraC* pCam )
{
	char	szParam[128] = "";

	ContFloatC*		pFloatCont = 0;

	do {
		read_row();

		if( is_token( "*PARAMETER" ) ) {
			sscanf( get_row(), "%*s %s", szParam );
		}
		else if( is_token( "*CONTROL_FLOAT_TCB" ) ) {
			pFloatCont = parse_tcb_float_track();
		}
		else if( is_token( "*CONTROL_FLOAT_SAMPLE" ) ) {
			pFloatCont = parse_sample_float_track();
		}
		else if( is_token( "*CONTROL_FLOAT_LINEAR" ) ) {
			pFloatCont = parse_lin_float_track();
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	if( pFloatCont ) {
		if( strcmp( szParam, "CAMERA_FOV" ) == 0 ) {
			pCam->set_fov_controller( pFloatCont );
			TRACE( "fov anim\n" );
		}
		else
			delete pFloatCont;
	}
}


void
MASLoaderC::parse_camera_settings( CameraC* pCam )
{
	float32	f32Val;

	do {
		read_row();
		if( is_token( "*CAMERA_NEAR" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pCam->set_near_plane( f32Val );
		}
		else if( is_token( "*CAMERA_FAR" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pCam->set_far_plane( f32Val );
		}
		else if( is_token( "*CAMERA_FOV" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pCam->set_fov( f32Val );
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );
}

void
MASLoaderC::parse_camera_tm_anim( CameraC* pCam )
{
	bool		bIsTarget = false;

	do {
		read_row();

		if( is_token( "*NODE_NAME" ) ) {
			if( strstr( get_row(), ".Target" ) )
				bIsTarget = true;
		}
		else if( is_token( "*CONTROL_POS_TCB" ) ) {
			if( bIsTarget ) {
				ContVector3C*	pCont = parse_tcb_pos_track();
				pCam->set_target_position_controller( pCont );
			}
			else {
				ContVector3C*	pCont = parse_tcb_pos_track();
				pCam->set_position_controller( pCont );
			}
		}
		else if( is_token( "*CONTROL_POS_TRACK" ) ) {
			if( bIsTarget ) {
				ContVector3C*	pCont = parse_sample_pos_track();
				pCam->set_target_position_controller( pCont );
			}
			else {
				ContVector3C*	pCont = parse_sample_pos_track();
				pCam->set_position_controller( pCont );
			}
		}
		else if( is_token( "*CONTROL_POS_LINEAR" ) ) {
			if( bIsTarget ) {
				ContVector3C*	pCont = parse_lin_pos_track();
				pCam->set_target_position_controller( pCont );
			}
			else {
				ContVector3C*	pCont = parse_lin_pos_track();
				pCam->set_position_controller( pCont );
			}
		}
		else if( is_token( "*CONTROL_FLOAT_TCB" ) ) {
			ContFloatC*	pCont = parse_tcb_float_track();
			pCam->set_roll_controller( pCont );
		}
		else if( is_token( "*CONTROL_FLOAT_TRACK" ) ) {
			ContFloatC*	pCont = parse_sample_float_track();
			pCam->set_roll_controller( pCont );
		}
		else if( is_token( "*CONTROL_FLOAT_LINEAR" ) ) {
			ContFloatC*	pCont = parse_lin_float_track();
			pCam->set_roll_controller( pCont );
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );
}


CameraC*
MASLoaderC::parse_camera()
{
	CameraC*	pCam = new CameraC;
	if( !pCam )
		return 0;

	Vector3C	rPos;
	float32		f32Roll;

	do {
		read_row();

		if( is_token( "*NODE_NAME" ) ) {
			pCam->set_name( extract_string( get_row() ) );
		}
		else if( is_token( "*NODE_TM" ) ) {
			if( parse_look_at_node_tm( rPos, f32Roll ) ) {
				pCam->set_target_position( rPos );
			}
			else {
				pCam->set_position( rPos );
				pCam->set_roll( f32Roll );
			}
		}
		else if( is_token( "*CAMERA_SETTINGS" ) ) {
			parse_camera_settings( pCam );
		}
		else if( is_token( "*CAMERA_ANIMATION" ) ) {
			do {
				read_row();
				if( is_token( "*PARAMETER_ANIMATION" ) ) {
					parse_camera_param_anim( pCam );
				}
				else if( is_block() )
					parse_dummy();
				else if( is_token( "}" ) )
					break;
			} while( !eof() );
		}
		else if( is_token( "*TM_ANIMATION" ) ) {
			parse_camera_tm_anim( pCam );
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );

	return pCam;
}


void
MASLoaderC::parse_tcset( MASMeshS& rMesh, uint32 ui32Channel ) //MeshC* pMesh )
{
	uint32		ui32TexVertices = 0, ui32TexFaces = 0;
	float32		f32X, f32Y;
	uint32		ui32A, ui32B, ui32C;
	uint32		ui32VA, ui32VB, ui32VC;

	std::vector<Vector2C>	rCoords;

	do {
		read_row();

		if( is_token( "*MESH_NUMTVERTEX" ) ) {
			sscanf( get_row(), "%*s %d", &ui32TexVertices);
		}
		else if( is_token( "*MESH_TVERTLIST" ) ) {
			for( uint32 i = 0; i < ui32TexVertices; i++ ) {
				read_row();
				if( eof() )
					return;
				sscanf( get_row(), "%f %f", &f32X, &f32Y);
				rCoords.push_back( Vector2C( f32X, f32Y ) );
			}
			read_row();	// "}"  (end of block)
			if( eof() )
				return;
		}
		else if( is_token( "*MESH_NUMTVFACES" ) ) {
			sscanf( get_row(), "%*s %d", &ui32TexFaces );
		}
		else if( is_token( "*MESH_TFACELIST" ) ) {
			for( uint32 i = 0; i < ui32TexFaces; i++ ) {
				read_row();
				if( eof() )
					return;
				sscanf( get_row(), "%d %d %d", &ui32A, &ui32B, &ui32C );

//				pMesh->add_texcoord_index( ui32A );
//				pMesh->add_texcoord_index( ui32B );
//				pMesh->add_texcoord_index( ui32C );

				// add texture vertices to vertex buffers
				ui32VA = rMesh.m_rFaces[i].m_ui32VA;
				ui32VB = rMesh.m_rFaces[i].m_ui32VB;
				ui32VC = rMesh.m_rFaces[i].m_ui32VC;

				ui32A = rMesh.m_rVertices[ui32VA].add_texcoord( rCoords[ui32A], ui32Channel );
				ui32B = rMesh.m_rVertices[ui32VB].add_texcoord( rCoords[ui32B], ui32Channel );
 				ui32C = rMesh.m_rVertices[ui32VC].add_texcoord( rCoords[ui32C], ui32Channel );

				rMesh.m_rFaces[i].set_texindex( ui32A, ui32B, ui32C, ui32Channel );
			}
			read_row();	// "}"  (end of block)
			if( eof() )
				return;
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;

	} while( !eof() );

}

void
MASLoaderC::parse_mesh( MASMeshS& rMesh, uint32& ui32MaxTexCh )
{
	uint32		ui32Vertices = 0, ui32Faces = 0, ui32Channel;
	float32		f32X, f32Y, f32Z;
	uint32		ui32A, ui32B, ui32C, ui32Edge, ui32Smooth, ui32Mtl;


	do {
		read_row();

		if( is_token( "*MESH_NUMVERTEX" ) ) {
			sscanf( get_row(), "%*s %d", &ui32Vertices );
		}
		else if( is_token( "*MESH_NUMFACES" ) ) {
			sscanf( get_row(), "%*s %d", &ui32Faces );
		}
		else if( is_token( "*MESH_VERTEX_LIST" ) ) {
			for( uint32 i = 0; i < ui32Vertices; i++ ) {
				read_row();
				if( eof() )
					return;
				sscanf( get_row(), "%f %f %f", &f32X, &f32Y, &f32Z );

				MASVertexC	rVert;

				rVert.m_rVert[0] = f32X;
				rVert.m_rVert[1] = f32Y;
				rVert.m_rVert[2] = f32Z;

				rMesh.m_rVertices.push_back( rVert );
			}
			read_row();	// "}"  (end of block)
			if( eof() )
				return;
		}
		else if( is_token( "*MESH_FACE_LIST" ) ) {
			for( uint32 i = 0; i < ui32Faces; i++ ) {
				read_row();
				if( eof() )
					return;

				sscanf( get_row(), "%d %d %d %x %x %d", &ui32A, &ui32B, &ui32C, &ui32Edge, &ui32Smooth, &ui32Mtl );

				MASFaceC	rFace;

				rFace.m_ui32VA = ui32A;
				rFace.m_ui32VB = ui32B;
				rFace.m_ui32VC = ui32C;
				rFace.m_ui32Edge = ui32Edge;
				rFace.m_ui32Smooth = ui32Smooth;
				rFace.m_ui32Mtl = ui32Mtl;

				rMesh.m_rFaces.push_back( rFace );

			}
			read_row();	// "}"  (end of block)
			if( eof() )
				return;
		}
		else if( is_token( "*MESH_MAPPINGCHANNEL" ) ) {
			sscanf( get_row(), "%*s %d", &ui32Channel );

			if( ui32Channel > ui32MaxTexCh )
				ui32MaxTexCh = ui32Channel;

			parse_tcset( rMesh, ui32Channel - 1 );

//			if( ui32Channel == 1 )
//				parse_tcset( pMesh );
//			else
//				parse_dummy();
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );


}

void
MASLoaderC::parse_object_tm( ScenegraphItemI* pItem )
{
	float32		f32X, f32Y, f32Z, f32A;

	Vector3C	rRotAxis;
	float32		f32RotAngle;
	Vector3C	rPos;
	Vector3C	rScale;
	Vector3C	rScaleAxis;
	float32		f32ScaleAngle;
	Matrix3C	rTM;

	do {
		read_row();

		if( is_token( "*TM_POS" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32X, &f32Y, &f32Z );
			rPos[0] = f32X;
			rPos[1] = f32Y;
			rPos[2] = f32Z;
		}
		else if( is_token( "*TM_ROTAXIS" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32X, &f32Y, &f32Z );
			rRotAxis[0] = f32X;
			rRotAxis[1] = f32Y;
			rRotAxis[2] = f32Z;
		}
		else if( is_token( "*TM_ROTANGLE" ) ) {
			sscanf( get_row(), "%*s %f", &f32A );
			f32RotAngle = f32A;
		}
		else if( is_token( "*TM_SCALE" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32X, &f32Y, &f32Z );
			rScale[0] = f32X;
			rScale[1] = f32Y;
			rScale[2] = f32Z;
		}
		else if( is_token( "*TM_SCALEAXIS" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32X, &f32Y, &f32Z );
			rScaleAxis[0] = f32X;
			rScaleAxis[1] = f32Y;
			rScaleAxis[2] = f32Z;
		}
		else if( is_token( "*TM_SCALEAXISANG" ) ) {
			sscanf( get_row(), "%*s %f", &f32A );
			f32ScaleAngle = f32A;
		}
		else if( is_token( "*TM_ROW0" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32X, &f32Y, &f32Z );
			rTM[0][0] = f32X;
			rTM[0][1] = f32Y;
			rTM[0][2] = f32Z;
		}
		else if( is_token( "*TM_ROW1" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32X, &f32Y, &f32Z );
			rTM[1][0] = f32X;
			rTM[1][1] = f32Y;
			rTM[1][2] = f32Z;
		}
		else if( is_token( "*TM_ROW2" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32X, &f32Y, &f32Z );
			rTM[2][0] = f32X;
			rTM[2][1] = f32Y;
			rTM[2][2] = f32Z;
		}
		else if( is_token( "*TM_ROW3" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32X, &f32Y, &f32Z );
			rTM[3][0] = f32X;
			rTM[3][1] = f32Y;
			rTM[3][2] = f32Z;
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;

	} while( !eof() );


	QuatC		rScaleRot;
	QuatC		rRot;

	rRot.from_axis_angle( rRotAxis, f32RotAngle );
	rScaleRot.from_axis_angle( rScaleAxis, f32ScaleAngle );


	if( pItem->get_type() == CGITEM_BONE ) {
		BoneC*	pBone = (BoneC*)pItem;
		pBone->set_position( rPos );
		pBone->set_rotation( rRot );
		pBone->set_base_tm( rTM );
	}
	else if( pItem->get_type() == CGITEM_MESH ) {
		MeshC*	pMesh = (MeshC*)pItem;
		pMesh->set_position( rPos );
		pMesh->set_rotation( rRot );
		pMesh->set_scale( rScale );
		pMesh->set_scale_rotation( rScaleRot );
		pMesh->set_base_tm( rTM );
	}
	else if( pItem->get_type() == CGITEM_PARTICLESYSTEM ) {
		ParticleSystemC*	pPSys = (ParticleSystemC*)pItem;
		pPSys->set_position( rPos );
		pPSys->set_rotation( rRot );
		pPSys->set_base_tm( rTM );
	}
	else if( pItem->get_type() == CGITEM_WSMOBJECT ) {
		WSMObjectC*	pObj = (WSMObjectC*)pItem;
		pObj->set_position( rPos );
		pObj->set_rotation( rRot );
		pObj->set_base_tm( rTM );
	}
	else if( pItem->get_type() == CGITEM_SHAPE ) {
		ShapeC*	pShape = (ShapeC*)pItem;
		pShape->set_position( rPos );
		pShape->set_rotation( rRot );
		pShape->set_scale( rScale );
		pShape->set_scale_rotation( rScaleRot );
		pShape->set_base_tm( rTM );
	}
}


void
MASLoaderC::parse_object_vis_track( ScenegraphItemI* pItem )
{
	ContBoolC*	pVisCont = 0;

	do {
		read_row();

		if( is_token( "*CONTROL_FLOAT_BOOL" ) ) {
			pVisCont = parse_bool_track();
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );


	if( pItem->get_type() == CGITEM_MESH ) {
		MeshC*	pMesh = (MeshC*)pItem;
		pMesh->set_vis_controller( pVisCont );
	}
/*	else if( pItem->get_type() == CGITEM_WSMOBJECT ) {
		WSMObjectC*	pObj = (WSMObjectC*)pItem;
		pObj->set_vis_controller( pVisCont );
	}*/
	else if( pItem->get_type() == CGITEM_PARTICLESYSTEM ) {
		ParticleSystemC*	pPSys = (ParticleSystemC*)pItem;
		pPSys->set_vis_controller( pVisCont );
	}
	else {
		delete pVisCont;
	}
}

void
MASLoaderC::parse_object_tm_anim( ScenegraphItemI* pItem )
{
	ContVector3C*	pPosCont = 0;
	ContQuatC*		pRotCont = 0;
	ContVector3C*	pScaleCont = 0;
	ContQuatC*		pScaleRotCont = 0;

	do {
		read_row();

		if( is_token( "*CONTROL_POS_TCB" ) ) {
			pPosCont = parse_tcb_pos_track();
		}
		else if( is_token( "*CONTROL_ROT_TCB" ) ) {
			pRotCont = parse_tcb_rot_track();
		}
		else if( is_token( "*CONTROL_SCALE_TCB" ) ) {
			parse_tcb_scale_track( &pScaleCont, &pScaleRotCont );
		}
		else if( is_token( "*CONTROL_POS_TRACK" ) ) {
			pPosCont = parse_sample_pos_track();
		}
		else if( is_token( "*CONTROL_ROT_TRACK" ) ) {
			pRotCont = parse_sample_rot_track();
		}
		else if( is_token( "*CONTROL_SCALE_TRACK" ) ) {
			parse_sample_scale_track( &pScaleCont, &pScaleRotCont );
		}
		else if( is_token( "*CONTROL_POS_LINEAR" ) ) {
			pPosCont = parse_lin_pos_track();
		}
		else if( is_token( "*CONTROL_ROT_LINEAR" ) ) {
			pRotCont = parse_lin_rot_track();
		}
		else if( is_token( "*CONTROL_SCALE_LINEAR" ) ) {
			parse_lin_scale_track( &pScaleCont, &pScaleRotCont );
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );


	if( pItem->get_type() == CGITEM_MESH ) {
		MeshC*	pMesh = (MeshC*)pItem;
		pMesh->set_position_controller( pPosCont );
		pMesh->set_rotation_controller( pRotCont );
		pMesh->set_scale_controller( pScaleCont );
		pMesh->set_scale_rotation_controller( pScaleRotCont );
	}
	else if( pItem->get_type() == CGITEM_SHAPE ) {
		ShapeC*	pShape = (ShapeC*)pItem;
		pShape->set_position_controller( pPosCont );
		pShape->set_rotation_controller( pRotCont );
		pShape->set_scale_controller( pScaleCont );
		pShape->set_scale_rotation_controller( pScaleRotCont );
	}
	else if( pItem->get_type() == CGITEM_BONE ) {
		BoneC*	pBone = (BoneC*)pItem;
		pBone->set_position_controller( pPosCont );
		pBone->set_rotation_controller( pRotCont );

		delete pScaleCont;
		delete pScaleRotCont;
	}
	else if( pItem->get_type() == CGITEM_PARTICLESYSTEM ) {
		ParticleSystemC*	pPSys = (ParticleSystemC*)pItem;
		pPSys->set_position_controller( pPosCont );
		pPSys->set_rotation_controller( pRotCont );

		delete pScaleCont;
		delete pScaleRotCont;
	}
	else if( pItem->get_type() == CGITEM_WSMOBJECT ) {
		WSMObjectC*	pObj = (WSMObjectC*)pItem;
		pObj->set_position_controller( pPosCont );
		pObj->set_rotation_controller( pRotCont );

		delete pScaleCont;
		delete pScaleRotCont;
	}
	else {
		delete pPosCont;
		delete pRotCont;
		delete pScaleCont;
		delete pScaleRotCont;
	}

}


void
MASLoaderC::parse_skin_modifier_bone_tm( Matrix3C& rTM )
{
	float32		f32X, f32Y, f32Z;

	do {
		read_row();

		if( is_token( "*TM_ROW0" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32X, &f32Y, &f32Z );
			rTM[0][0] = f32X;
			rTM[0][1] = f32Y;
			rTM[0][2] = f32Z;
		}
		else if( is_token( "*TM_ROW1" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32X, &f32Y, &f32Z );
			rTM[1][0] = f32X;
			rTM[1][1] = f32Y;
			rTM[1][2] = f32Z;
		}
		else if( is_token( "*TM_ROW2" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32X, &f32Y, &f32Z );
			rTM[2][0] = f32X;
			rTM[2][1] = f32Y;
			rTM[2][2] = f32Z;
		}
		else if( is_token( "*TM_ROW3" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32X, &f32Y, &f32Z );
			rTM[3][0] = f32X;
			rTM[3][1] = f32Y;
			rTM[3][2] = f32Z;
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;

	} while( !eof() );
}

void
MASLoaderC::parse_skin_modifier_bone( MASMeshS& rMesh )
{
	Matrix3C			rInitTM;
	uint32				ui32VertexCount;
	MASBoneWeightListS	rList;

	do {
		read_row();

		if( is_token( "*BONE_NAME" ) ) {
			rList.m_sBoneName = extract_string( get_row() );
		}
		else if( is_token( "*BONE_INIT_TM" ) ) {
			parse_skin_modifier_bone_tm( rInitTM );
			rList.m_rBaseTM = rInitTM;
		}
		else if( is_token( "*BONE_NUMVERTEX" ) ) {
			sscanf( get_row(), "%*s %d", &ui32VertexCount );
		}
		else if( is_token( "*BONE_VERTICES" ) ) {

			uint32	ui32Idx;
			float32	f32Weight;

			for( uint32 i = 0; i < ui32VertexCount; i++ ) {

				read_row();
				if( eof() )
					break;

				sscanf( get_row(), "%d %f", &ui32Idx, &f32Weight );

				rList.m_rIndices.push_back( ui32Idx );
				rList.m_rWeights.push_back( f32Weight );
			}
			read_row();	// "}"  (end of block)
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );

	rMesh.m_rBoneWeighLists.push_back( rList );
}

void
MASLoaderC::parse_skin_modifier( MASMeshS& rMesh )
{
	Matrix3C	rInitTM;

	do {
		read_row();

		if( is_token( "*SKIN_INIT_TM" ) ) {
			parse_skin_modifier_bone_tm( rInitTM );
			rMesh.m_rSkinBaseTM = rInitTM;
		}
		else if( is_token( "*SKIN_BONE" ) ) {
			parse_skin_modifier_bone( rMesh );
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );

}


void
MASLoaderC::parse_morph_modifier_channel_anim( MASMorphChannelS* pChannel )
{
	bool		bIsTarget = false;

	do {
		read_row();

		if( is_token( "*NODE_NAME" ) ) {
			if( strstr( get_row(), ".Target" ) )
				bIsTarget = true;
		}
		else if( is_token( "*CONTROL_FLOAT_TCB" ) ) {
			ContFloatC*	pCont = parse_tcb_float_track();
			pChannel->m_pPercentageController = pCont;
		}
		else if( is_token( "*CONTROL_FLOAT_SAMPLE" ) ) {
			ContFloatC*	pCont = parse_sample_float_track();
			pChannel->m_pPercentageController = pCont;
		}
		else if( is_token( "*CONTROL_FLOAT_LINEAR" ) ) {
			ContFloatC*	pCont = parse_lin_float_track();
			pChannel->m_pPercentageController = pCont;
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );
}


void
MASLoaderC::parse_morph_modifier_channel( MASMeshS& rMesh )
{
	uint32	ui32Vertices = 0;
	float32	f32Min, f32Max;
	float32	f32X, f32Y, f32Z;
	float32	f32Percent;

	MASMorphChannelS*	pChannel = new MASMorphChannelS;

	do {
		read_row();

		if( is_token( "*CHANNEL_NAME" ) ) {
			// name
		}
		else if( is_token( "*CHANNEL_LIMITS" ) ) {
			sscanf( get_row(), "%*s %f %f", &f32Min, &f32Max );
			pChannel->m_f32Min = f32Min;
			pChannel->m_f32Max = f32Max;
		}
		else if( is_token( "*CHANNEL_USE_LIMITS" ) ) {
			pChannel->m_bUseLimits = true;
		}
		else if( is_token( "*CHANNEL_NUMVERTEX" ) ) {
			sscanf( get_row(), "%*s %d", &ui32Vertices );
		}
		else if( is_token( "*CHANNEL_VERTICES" ) ) {
			for( uint32 i = 0; i < ui32Vertices; i++ ) {
				read_row();
				if( eof() )
					return;
				sscanf( get_row(), "%f %f %f", &f32X, &f32Y, &f32Z );

				MASVertexC	rVert;

				rVert.m_rVert[0] = f32X;
				rVert.m_rVert[1] = f32Y;
				rVert.m_rVert[2] = f32Z;

				pChannel->m_rVertices.push_back( rVert );
			}
			read_row();	// "}"  (end of block)
			if( eof() )
				return;
		}
		else if( is_token( "*CHANNEL_PERCENT" ) ) {
			sscanf( get_row(), "%*s %f", &f32Percent );
			pChannel->m_f32Percentage = f32Percent;
		}
		else if( is_token( "*CHANNEL_ANIM" ) ) {
			parse_morph_modifier_channel_anim( pChannel );
		}

		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) )
			break;
	} while( !eof() );

	// add channel
	rMesh.m_rMorphChannels.push_back( pChannel );
}

void
MASLoaderC::parse_morph_modifier( MASMeshS& rMesh )
{
	do {
		read_row();

		if( is_token( "*MORPH_CHANNEL" ) ) {
			parse_morph_modifier_channel( rMesh );
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );
}


ScenegraphItemI*
MASLoaderC::parse_geomobject()
{

	float32		f32Weight;
	uint32		ui32Mtl, ui32Idx, ui32BoneId, ui32Val;
	ColorC		rDiffuse;

	MeshC*		pMesh = new MeshC;
	if( !pMesh )
		return 0;

	bool		bHasMtl = false;
	Matrix3C	rTM;

	MASMeshS	rMesh;
	uint32		ui32MaxTexCh = 0;

	do {
		read_row();

		if( is_token( "*NODE_NAME" ) ) {
			pMesh->set_name( extract_string( get_row() ) );
		}
		else if( is_token( "*NODE_PARENT" ) ) {
			pMesh->set_parent_name( extract_string( get_row() ) );
		}
		else if( is_token( "*NODE_TM" ) ) {
			parse_object_tm( pMesh );
		}
		else if( is_token( "*NODE_IS_HIDDEN" ) ) {
			pMesh->set_hidden( true );
		}
		else if( is_token( "*MESH" ) ) {
			parse_mesh( rMesh, ui32MaxTexCh );//pMesh );
		}
		else if( is_token( "*MATERIAL_REF" ) ) {
			sscanf( get_row(), "%*s %d", &ui32Mtl );
			if( ui32Mtl < m_rMaterials.size() ) {
				bHasMtl = true;
			}
		}
		else if( is_token( "*WIREFRAME_COLOR" ) ) {
			if( !bHasMtl ) {
				float32	f32R, f32G, f32B;
				sscanf( get_row(), "%*s %f %f %f", &f32R, &f32G, &f32B );
				rDiffuse[0] = f32R;
				rDiffuse[1] = f32G;
				rDiffuse[2] = f32B;
			}
      	}
		else if( is_token( "*TM_ANIMATION" ) ) {
			parse_object_tm_anim( pMesh );
		}
		else if( is_token( "*NODE_VISIBILITY_TRACK" ) ) {
			parse_object_vis_track( pMesh );
		}
		else if( is_token( "*BONE_WEIGHTS" ) ) {
			// Versio 3.10 -- 3dsmax R3
			read_row();
			while( !is_token( "}" ) ) {

				sscanf( get_row(), "%d %f %d",
					&ui32Idx, &f32Weight, &ui32BoneId );

				MASBoneWeightS	rWeight;
				rWeight.m_ui32Vert = ui32Idx;
				rWeight.m_f32Weight = f32Weight;
				rWeight.m_ui32Bone = ui32BoneId;

				rMesh.m_rBoneWeights.push_back( rWeight );

				read_row();
			}
		}
		else if( is_token( "*MESH_SKIN_MODIFIER" ) ) {
			// Versio 3.20>
			parse_skin_modifier( rMesh );
		}
		else if( is_token( "*MESH_MORPHER_MODIFIER" ) ) {
			// Versio 3.20>
			parse_morph_modifier( rMesh );
		}
		else if( is_token( "*USER_PROP" ) ) {
			char*	szProp = extract_string( get_row() );

			if( strstr( szProp, "ADDITIVE" ) != NULL )
				rMesh.m_bAdditive = true;
			if( strstr( szProp, "TOON" ) != NULL )
				rMesh.m_bToon = true;
			if( strstr( szProp, "ALPHATEST" ) != NULL )
				rMesh.m_bAlphaTest = true;
		}
		else if( is_token( "*PROP_CASTSHADOW" ) ) {
			sscanf( get_row(), "%*s %d", &ui32Val );
			if( ui32Val )
				rMesh.m_bCastShadows = true;
			else
				rMesh.m_bCastShadows = false;
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;

	} while( !eof() );


	uint32	i, j, k;


	//
	// calculate normals
	//

	for( i = 0; i < rMesh.m_rFaces.size(); i++ ) {
		uint32	ui32VA = rMesh.m_rFaces[i].m_ui32VA;
		uint32	ui32VB = rMesh.m_rFaces[i].m_ui32VB;
		uint32	ui32VC = rMesh.m_rFaces[i].m_ui32VC;

		Vector3C	rA, rB, rNorm;
		rA = rMesh.m_rVertices[ui32VB].m_rVert - rMesh.m_rVertices[ui32VA].m_rVert;
		rB = rMesh.m_rVertices[ui32VC].m_rVert - rMesh.m_rVertices[ui32VA].m_rVert;
		rNorm = rA.cross( rB );
		rNorm = rNorm.normalize();

		rMesh.m_rFaces[i].m_rNorm = rNorm;

		rMesh.m_rFaces[i].m_ui32NA = rMesh.m_rVertices[ui32VA].add_norm( rNorm, rMesh.m_rFaces[i].m_ui32Smooth );
		rMesh.m_rFaces[i].m_ui32NB = rMesh.m_rVertices[ui32VB].add_norm( rNorm, rMesh.m_rFaces[i].m_ui32Smooth );
		rMesh.m_rFaces[i].m_ui32NC = rMesh.m_rVertices[ui32VC].add_norm( rNorm, rMesh.m_rFaces[i].m_ui32Smooth );
	}

	// calc normals for morphs
	if( rMesh.m_rMorphChannels.size() ) {
		for( j = 0; j < rMesh.m_rMorphChannels.size(); j++ ) {
			for( i = 0; i < rMesh.m_rFaces.size(); i++ ) {
				uint32	ui32VA = rMesh.m_rFaces[i].m_ui32VA;
				uint32	ui32VB = rMesh.m_rFaces[i].m_ui32VB;
				uint32	ui32VC = rMesh.m_rFaces[i].m_ui32VC;

				Vector3C	rA, rB, rNorm;
				rA = rMesh.m_rMorphChannels[j]->m_rVertices[ui32VB].m_rVert - rMesh.m_rMorphChannels[j]->m_rVertices[ui32VA].m_rVert;
				rB = rMesh.m_rMorphChannels[j]->m_rVertices[ui32VC].m_rVert - rMesh.m_rMorphChannels[j]->m_rVertices[ui32VA].m_rVert;
				rNorm = rA.cross( rB );
				rNorm = rNorm.normalize();

				rMesh.m_rMorphChannels[j]->m_rVertices[ui32VA].add_norm( rNorm, rMesh.m_rFaces[i].m_ui32Smooth );
				rMesh.m_rMorphChannels[j]->m_rVertices[ui32VB].add_norm( rNorm, rMesh.m_rFaces[i].m_ui32Smooth );
				rMesh.m_rMorphChannels[j]->m_rVertices[ui32VC].add_norm( rNorm, rMesh.m_rFaces[i].m_ui32Smooth );
			}
		}
	}

	//
	// calculate edges
	//

	for( i = 0; i < rMesh.m_rFaces.size(); i++ ) {
		uint32	ui32VA = rMesh.m_rFaces[i].m_ui32VA;
		uint32	ui32VB = rMesh.m_rFaces[i].m_ui32VB;
		uint32	ui32VC = rMesh.m_rFaces[i].m_ui32VC;
		
		rMesh.m_rFaces[i].m_ui32EA = rMesh.add_edge( ui32VA, ui32VB, i );
		rMesh.m_rFaces[i].m_ui32EB = rMesh.add_edge( ui32VB, ui32VC, i );
		rMesh.m_rFaces[i].m_ui32EC = rMesh.add_edge( ui32VC, ui32VA, i );
	}

	// calc cusp edges
	for( i = 0; i < rMesh.m_rEdges.size(); i++ ) {
		uint32	ui32FA = rMesh.m_rEdges[i].m_ui32FA;
		uint32	ui32FB = rMesh.m_rEdges[i].m_ui32FB;

		// edges sharing only one face are cusp no matter what.
		if( ui32FA == 0xffffffff || ui32FB == 0xffffffff ) {
			rMesh.m_rEdges[i].m_bCuspEdge = true;
			continue;
		}

		// if the faces don't share same smoothing groups the edge is cusp
//		if( ((rMesh.m_rFaces[ui32FA].m_ui32Smooth) & (rMesh.m_rFaces[ui32FB].m_ui32Smooth)) == 0 )
		if( rMesh.m_rFaces[ui32FA].m_rNorm.dot( rMesh.m_rFaces[ui32FB].m_rNorm ) < cos( 45.0 / 180.0 * M_PI ) )
			rMesh.m_rEdges[i].m_bCuspEdge = true;
	}

	//
	// register vertex usage
	//

	for( i = 0; i < rMesh.m_rFaces.size(); i++ ) {
		uint32	ui32VA = rMesh.m_rFaces[i].m_ui32VA;
		uint32	ui32VB = rMesh.m_rFaces[i].m_ui32VB;
		uint32	ui32VC = rMesh.m_rFaces[i].m_ui32VC;

		uint32	ui32NA = rMesh.m_rFaces[i].m_ui32NA;
		uint32	ui32NB = rMesh.m_rFaces[i].m_ui32NB;
		uint32	ui32NC = rMesh.m_rFaces[i].m_ui32NC;

		std::vector<uint32>	rTexIdx;

		rTexIdx.clear();
		for( j = 0; j < rMesh.m_rVertices[ui32VA].m_rTexChannels.size(); j++ )
			rTexIdx.push_back( rMesh.m_rFaces[i].m_rTA[j] );
		// register usage for base mesh
		rMesh.m_rFaces[i].m_ui32UA = rMesh.m_rVertices[ui32VA].add_usage( ui32NA, rTexIdx );
		// register usage for morph channels
		for( j = 0; j < rMesh.m_rMorphChannels.size(); j++ )
			rMesh.m_rMorphChannels[j]->m_rVertices[ui32VA].add_usage( ui32NA, rTexIdx );


		rTexIdx.clear();
		for( j = 0; j < rMesh.m_rVertices[ui32VB].m_rTexChannels.size(); j++ )
			rTexIdx.push_back( rMesh.m_rFaces[i].m_rTB[j] );
		// register usage for base mesh
		rMesh.m_rFaces[i].m_ui32UB = rMesh.m_rVertices[ui32VB].add_usage( ui32NB, rTexIdx );
		// register usage for morph channels
		for( j = 0; j < rMesh.m_rMorphChannels.size(); j++ )
			rMesh.m_rMorphChannels[j]->m_rVertices[ui32VB].add_usage( ui32NB, rTexIdx );


		rTexIdx.clear();
		for( j = 0; j < rMesh.m_rVertices[ui32VC].m_rTexChannels.size(); j++ )
			rTexIdx.push_back( rMesh.m_rFaces[i].m_rTC[j] );
		// register usage for base mesh
		rMesh.m_rFaces[i].m_ui32UC = rMesh.m_rVertices[ui32VC].add_usage( ui32NC, rTexIdx );
		// register usage for morph channels
		for( j = 0; j < rMesh.m_rMorphChannels.size(); j++ )
			rMesh.m_rMorphChannels[j]->m_rVertices[ui32VC].add_usage( ui32NC, rTexIdx );

	}


	std::vector<MASTexChannelS>	rTexChannels;
	uint32						ui32BaseIdx = 0;

	rTexChannels.resize( ui32MaxTexCh );

	for( i = 0; i < rMesh.m_rVertices.size(); i++ ) {
		for( j = 0; j < rMesh.m_rVertices[i].m_rVertUsage.size(); j++ ) {
			uint32	ui32N = rMesh.m_rVertices[i].m_rVertUsage[j].m_ui32N;
			uint32	ui32T;

			pMesh->add_vert( rMesh.m_rVertices[i].m_rVert );
			pMesh->add_norm( rMesh.m_rVertices[i].m_rNorms[ui32N] );

			for( k = 0; k < ui32MaxTexCh; k++ ) {
				if( k < rMesh.m_rVertices[i].m_rTexChannels.size() && k < rMesh.m_rVertices[i].m_rVertUsage[j].m_rT.size() ) {
					ui32T = rMesh.m_rVertices[i].m_rVertUsage[j].m_rT[k];
					if( rMesh.m_rVertices[i].m_rTexChannels[k].m_rCoords.size() )
						rTexChannels[k].m_rCoords.push_back( rMesh.m_rVertices[i].m_rTexChannels[k].m_rCoords[ui32T] );
					else
						rTexChannels[k].m_rCoords.push_back( Vector2C( 0, 0 ) );
				}
				else
					rTexChannels[k].m_rCoords.push_back( Vector2C( 0, 0 ) );
			}
		}

		rMesh.m_rVertices[i].m_ui32BaseIdx = ui32BaseIdx;

		ui32BaseIdx += rMesh.m_rVertices[i].m_rVertUsage.size();
	}


	//
	// Morpher stuff
	//
	if( rMesh.m_rMorphChannels.size() ) {


		MorphModifierC*	pMod = new MorphModifierC;

		for( k = 0; k < rMesh.m_rMorphChannels.size(); k++ ) {

			if( rMesh.m_rMorphChannels[k]->m_rVertices.size() != rMesh.m_rVertices.size() ) {
				TRACE( "morph channel vertex count is different from the base mesh! (m: %d, b: %d)\n", rMesh.m_rMorphChannels[k]->m_rVertices.size(), rMesh.m_rVertices.size() );
				break;
			}

			MorphChannelC*	pChan = new MorphChannelC;

			pChan->set_limits( rMesh.m_rMorphChannels[k]->m_f32Min, rMesh.m_rMorphChannels[k]->m_f32Max );
			pChan->set_use_limits( rMesh.m_rMorphChannels[k]->m_bUseLimits );
			
			pChan->set_percentage( rMesh.m_rMorphChannels[k]->m_f32Percentage );

			pChan->set_percentage_controller( rMesh.m_rMorphChannels[k]->m_pPercentageController );
			rMesh.m_rMorphChannels[k]->m_pPercentageController = 0;

			int32	i32VertexIdx = 0;

			for( i = 0; i < rMesh.m_rVertices.size(); i++ ) {

				if( rMesh.m_rMorphChannels[k]->m_rVertices[i].m_rVertUsage.size() != rMesh.m_rVertices[i].m_rVertUsage.size() ) {
					TRACE( "morph channel vertex USAGE is different from the base mesh! (m: %d, b: %d)\n", rMesh.m_rMorphChannels[k]->m_rVertices[i].m_rVertUsage.size(), rMesh.m_rVertices[i].m_rVertUsage.size() );
					delete pChan;
					pChan = 0;
					break;
				}

				for( j = 0; j < rMesh.m_rVertices[i].m_rVertUsage.size(); j++ ) {

					uint32	ui32N = rMesh.m_rVertices[i].m_rVertUsage[j].m_ui32N;

					Vector3C	rBaseVertex = rMesh.m_rVertices[i].m_rVert;
					Vector3C	rBaseNormal = rMesh.m_rVertices[i].m_rNorms[ui32N];

					Vector3C	rMorphVertex = rMesh.m_rMorphChannels[k]->m_rVertices[i].m_rVert;
					Vector3C	rMorphNormal = rMesh.m_rMorphChannels[k]->m_rVertices[i].m_rNorms[ui32N];

//					if( rBaseVertex[0] != rMorphVertex[0] &&
//						rBaseVertex[1] != rMorphVertex[1] &&
//						rBaseVertex[2] != rMorphVertex[2] ) {

						Vector3C	rDeltaV = rMorphVertex - rBaseVertex;
						Vector3C	rDeltaN = rMorphNormal - rBaseNormal;

						pChan->add_delta( i32VertexIdx, rDeltaV, rDeltaN );
//					}

					i32VertexIdx++;
				}
			}

			if( pChan ) {
				pMod->add_morph_channel( pChan );
			}
		}

		pMesh->add_modifier( pMod );
	}


	//
	// Bone stuff
	//
	if( m_ui32Version <= 310 ) {

		if( rMesh.m_rBoneWeights.size() ) {

			OldSkinModifierC*	pMod = new OldSkinModifierC;

			// put bone weights
			for( i = 0; i < rMesh.m_rBoneWeights.size(); i++ ) {
				uint32	ui32V = rMesh.m_rBoneWeights[i].m_ui32Vert;
				for( j = 0; j < rMesh.m_rVertices[ui32V].m_rVertUsage.size(); j++ )
					pMod->add_bone_vertex( rMesh.m_rVertices[ui32V].m_ui32BaseIdx + j, rMesh.m_rBoneWeights[i].m_f32Weight, rMesh.m_rBoneWeights[i].m_ui32Bone );
			}

			pMesh->add_modifier( pMod );
		}
	}
	else {
		// version 3.20 >
		// put bone weights

		if( rMesh.m_rBoneWeighLists.size() ) {

			SkinModifierC*	pMod = new SkinModifierC;

			pMod->set_skin_base_tm( rMesh.m_rSkinBaseTM );

			for( i = 0; i < rMesh.m_rBoneWeighLists.size(); i++ ) {
				MASBoneWeightListS&	rList = rMesh.m_rBoneWeighLists[i];

				BoneWeightListC*	pWList = new BoneWeightListC;

				pWList->set_bone_name( rList.m_sBoneName.c_str() );
				pWList->set_base_tm( rList.m_rBaseTM );

				for( j = 0; j < rList.m_rIndices.size(); j++ ) {
					uint32	ui32V = rList.m_rIndices[j];
					if( ui32V < rMesh.m_rVertices.size() ) {
						for( k = 0; k < rMesh.m_rVertices[ui32V].m_rVertUsage.size(); k++ ) {
							int32	i32NewIdx = rMesh.m_rVertices[ui32V].m_ui32BaseIdx + k;
							float32	f32Weight = rList.m_rWeights[j];
							pWList->add_weight( i32NewIdx, f32Weight );
						}
					}
				}

				pMod->add_boneweightlist( pWList );

			}
			pMesh->add_modifier( pMod );
		}
	}


	// put texture channels
	for( j = 0; j < rTexChannels.size(); j++ ) {

		TexCoordListC*	pList = new TexCoordListC;

		for( i = 0; i < rTexChannels[j].m_rCoords.size(); i++ )
			pList->add_texcoord( rTexChannels[j].m_rCoords[i] );

		pMesh->add_texcoord_list( pList );
	}

	bool	bAdditive = false;

	// build indices
	if( bHasMtl && m_rMaterials[ui32Mtl]->m_rSubMaterials.size() ) {

		std::vector<MASIndexListS>	rIndexLists;

		for( i = 0; i < rMesh.m_rFaces.size(); i++ ) {

			MASMaterialS*	pMtl = m_rMaterials[ui32Mtl];

			// clamp material index
			rMesh.m_rFaces[i].m_ui32Mtl %= pMtl->m_rSubMaterials.size();

			int32	i32Index = -1;
			for( j = 0; j < rIndexLists.size(); j++ ) {
				if( rIndexLists[j].m_ui32MtlIndex == rMesh.m_rFaces[i].m_ui32Mtl )
					i32Index = j;
			}

			// add new list if it does not exist
			if( i32Index == -1 ) {
				MASIndexListS	rList;
				rList.m_pList = new IndexListC;

				uint32	ui32SubMtl = rMesh.m_rFaces[i].m_ui32Mtl;

				rList.m_pList->set_ambient( pMtl->m_rSubMaterials[ui32SubMtl]->m_rAmbient );
				rList.m_pList->set_diffuse( pMtl->m_rSubMaterials[ui32SubMtl]->m_rDiffuse );
				rList.m_pList->set_specular( pMtl->m_rSubMaterials[ui32SubMtl]->m_rSpecular );
				rList.m_pList->set_shininess( pMtl->m_rSubMaterials[ui32SubMtl]->m_f32Shininess );
				rList.m_pList->set_transparency( pMtl->m_rSubMaterials[ui32SubMtl]->m_f32Transparency);
				rList.m_pList->set_two_sided( pMtl->m_rSubMaterials[ui32SubMtl]->m_bTwoSided );
				rList.m_pList->set_selfillum( pMtl->m_rSubMaterials[ui32SubMtl]->m_f32SelfIllum );

				// controllers
				if( pMtl->m_rSubMaterials[ui32SubMtl]->m_pTransparencyCont )
					rList.m_pList->set_transparency_controller( pMtl->m_rSubMaterials[ui32SubMtl]->m_pTransparencyCont->duplicate() );
				if( pMtl->m_rSubMaterials[ui32SubMtl]->m_pSelfIllumCont )
					rList.m_pList->set_selfillum_controller( pMtl->m_rSubMaterials[ui32SubMtl]->m_pSelfIllumCont->duplicate() );
				if( pMtl->m_rSubMaterials[ui32SubMtl]->m_pDiffuseCont )
					rList.m_pList->set_diffuse_controller( pMtl->m_rSubMaterials[ui32SubMtl]->m_pDiffuseCont->duplicate() );
				if( pMtl->m_rSubMaterials[ui32SubMtl]->m_pSpecularCont )
					rList.m_pList->set_specular_controller( pMtl->m_rSubMaterials[ui32SubMtl]->m_pSpecularCont->duplicate() );

				rList.m_pList->set_diffuse_texture( get_texture( pMtl->m_rSubMaterials[ui32SubMtl]->m_sDiffuseTexName ) );
				rList.m_pList->set_diffuse_mapping_type( pMtl->m_rSubMaterials[ui32SubMtl]->m_ui32DiffuseMappingType );
				rList.m_pList->set_diffuse_tex_channel( pMtl->m_rSubMaterials[ui32SubMtl]->m_ui32DiffuseTexChannel );

				rList.m_pList->set_reflection_texture( get_texture( pMtl->m_rSubMaterials[ui32SubMtl]->m_sReflectionTexName ) );
				rList.m_pList->set_reflection_mapping_type( pMtl->m_rSubMaterials[ui32SubMtl]->m_ui32ReflectionMappingType );
				rList.m_pList->set_reflection_tex_channel( pMtl->m_rSubMaterials[ui32SubMtl]->m_ui32ReflectionTexChannel );

				rList.m_pList->set_lightmap_texture( get_texture( pMtl->m_rSubMaterials[ui32SubMtl]->m_sLightmapTexName ) );
				rList.m_pList->set_lightmap_mapping_type( pMtl->m_rSubMaterials[ui32SubMtl]->m_ui32LightmapMappingType );
				rList.m_pList->set_lightmap_tex_channel( pMtl->m_rSubMaterials[ui32SubMtl]->m_ui32LightmapTexChannel );


				rList.m_ui32MtlIndex = rMesh.m_rFaces[i].m_ui32Mtl;
				rIndexLists.push_back( rList );
				i32Index = rIndexLists.size() - 1;

				if( m_rMaterials[ui32Mtl]->m_rSubMaterials[ui32SubMtl]->m_bAdditive )
					bAdditive = true;
			}

			// add indices
			uint32	ui32VA = rMesh.m_rFaces[i].m_ui32VA;
			uint32	ui32VB = rMesh.m_rFaces[i].m_ui32VB;
			uint32	ui32VC = rMesh.m_rFaces[i].m_ui32VC;

			rIndexLists[i32Index].m_pList->add_index( rMesh.m_rVertices[ui32VA].m_ui32BaseIdx + rMesh.m_rFaces[i].m_ui32UA );
			rIndexLists[i32Index].m_pList->add_index( rMesh.m_rVertices[ui32VB].m_ui32BaseIdx + rMesh.m_rFaces[i].m_ui32UB );
			rIndexLists[i32Index].m_pList->add_index( rMesh.m_rVertices[ui32VC].m_ui32BaseIdx + rMesh.m_rFaces[i].m_ui32UC );

			// set cusp edges
			rIndexLists[i32Index].m_pList->add_cusp_edge( rMesh.m_rEdges[rMesh.m_rFaces[i].m_ui32EA].m_bCuspEdge ? 1 : 0 );
			rIndexLists[i32Index].m_pList->add_cusp_edge( rMesh.m_rEdges[rMesh.m_rFaces[i].m_ui32EB].m_bCuspEdge ? 1 : 0 );
			rIndexLists[i32Index].m_pList->add_cusp_edge( rMesh.m_rEdges[rMesh.m_rFaces[i].m_ui32EC].m_bCuspEdge ? 1 : 0 );
		}

		// add lists to mesh
		for( i = 0; i < rIndexLists.size(); i++ )
			pMesh->add_index_list( rIndexLists[i].m_pList );
	}
	else {
		// only one material

		IndexListC*	pList = new IndexListC;

		// add indices
		for( i = 0; i < rMesh.m_rFaces.size(); i++ ) {
			uint32	ui32VA = rMesh.m_rFaces[i].m_ui32VA;
			uint32	ui32VB = rMesh.m_rFaces[i].m_ui32VB;
			uint32	ui32VC = rMesh.m_rFaces[i].m_ui32VC;

			pList->add_index( rMesh.m_rVertices[ui32VA].m_ui32BaseIdx + rMesh.m_rFaces[i].m_ui32UA );
			pList->add_index( rMesh.m_rVertices[ui32VB].m_ui32BaseIdx + rMesh.m_rFaces[i].m_ui32UB );
			pList->add_index( rMesh.m_rVertices[ui32VC].m_ui32BaseIdx + rMesh.m_rFaces[i].m_ui32UC );

			// set cusp edges
			pList->add_cusp_edge( rMesh.m_rEdges[rMesh.m_rFaces[i].m_ui32EA].m_bCuspEdge ? 1 : 0 );
			pList->add_cusp_edge( rMesh.m_rEdges[rMesh.m_rFaces[i].m_ui32EB].m_bCuspEdge ? 1 : 0 );
			pList->add_cusp_edge( rMesh.m_rEdges[rMesh.m_rFaces[i].m_ui32EC].m_bCuspEdge ? 1 : 0 );
		}

		if( !bHasMtl ) {
			pList->set_diffuse( rDiffuse );
		}
		else {
			pList->set_ambient( m_rMaterials[ui32Mtl]->m_rAmbient );
			pList->set_diffuse( m_rMaterials[ui32Mtl]->m_rDiffuse );
			pList->set_specular( m_rMaterials[ui32Mtl]->m_rSpecular );
			pList->set_shininess( m_rMaterials[ui32Mtl]->m_f32Shininess );
			pList->set_transparency( m_rMaterials[ui32Mtl]->m_f32Transparency);
			pList->set_two_sided( m_rMaterials[ui32Mtl]->m_bTwoSided );
			pList->set_selfillum( m_rMaterials[ui32Mtl]->m_f32SelfIllum );

			// controllers
			if( m_rMaterials[ui32Mtl]->m_pTransparencyCont )
				pList->set_transparency_controller( m_rMaterials[ui32Mtl]->m_pTransparencyCont->duplicate() );
			if( m_rMaterials[ui32Mtl]->m_pSelfIllumCont )
				pList->set_selfillum_controller( m_rMaterials[ui32Mtl]->m_pSelfIllumCont->duplicate() );
			if( m_rMaterials[ui32Mtl]->m_pDiffuseCont )
				pList->set_diffuse_controller( m_rMaterials[ui32Mtl]->m_pDiffuseCont->duplicate() );
			if( m_rMaterials[ui32Mtl]->m_pSpecularCont )
				pList->set_specular_controller( m_rMaterials[ui32Mtl]->m_pSpecularCont->duplicate() );

			pList->set_diffuse_texture( get_texture( m_rMaterials[ui32Mtl]->m_sDiffuseTexName ) );
			pList->set_diffuse_mapping_type( m_rMaterials[ui32Mtl]->m_ui32DiffuseMappingType );
			pList->set_diffuse_tex_channel( m_rMaterials[ui32Mtl]->m_ui32DiffuseTexChannel );

			pList->set_reflection_texture( get_texture( m_rMaterials[ui32Mtl]->m_sReflectionTexName ) );
			pList->set_reflection_mapping_type( m_rMaterials[ui32Mtl]->m_ui32ReflectionMappingType );
			pList->set_reflection_tex_channel( m_rMaterials[ui32Mtl]->m_ui32ReflectionTexChannel );

			pList->set_lightmap_texture( get_texture( m_rMaterials[ui32Mtl]->m_sLightmapTexName ) );
			pList->set_lightmap_mapping_type( m_rMaterials[ui32Mtl]->m_ui32LightmapMappingType );
			pList->set_lightmap_tex_channel( m_rMaterials[ui32Mtl]->m_ui32LightmapTexChannel );

			if( m_rMaterials[ui32Mtl]->m_bAdditive )
				bAdditive = true;
		}

		pMesh->add_index_list( pList );
	}

	if( rMesh.m_bAdditive )
		bAdditive = true;

	pMesh->set_additive( bAdditive );
	pMesh->set_toon( rMesh.m_bToon );
	pMesh->set_alphatest( rMesh.m_bAlphaTest );
	pMesh->set_cast_shadows( rMesh.m_bCastShadows );

	pMesh->calc_bbox();

	return pMesh;
}


void
MASLoaderC::parse_particle_param_anim( ParticleSystemC* pPSys )
{
	char	szParam[128] = "";

	ContFloatC*	pCont = 0;

	do {
		read_row();

		if( is_token( "*PARAMETER" ) ) {
			sscanf( get_row(), "%*s %s", szParam );
		}
		else if( is_token( "*CONTROL_FLOAT_TCB" ) ) {
			pCont = parse_tcb_float_track();
		}
		else if( is_token( "*CONTROL_FLOAT_SAMPLE" ) ) {
			pCont = parse_sample_float_track();
		}
		else if( is_token( "*CONTROL_FLOAT_LINEAR" ) ) {
			pCont = parse_lin_float_track();
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	if( pCont ) {
		if( strcmp( szParam, "DROP_SIZE" ) == 0 )
			pPSys->set_drop_size_cont( pCont );
		else if( strcmp( szParam, "SPEED" ) == 0 )
			pPSys->set_speed_cont( pCont );
		else if( strcmp( szParam, "VARIATION" ) == 0 )
			pPSys->set_variation_cont( pCont );
		else if( strcmp( szParam, "START_TIME" ) == 0 )
			pPSys->set_start_time_cont( pCont );
		else if( strcmp( szParam, "LIFE_TIME" ) == 0 )
			pPSys->set_life_time_cont( pCont );
		else if( strcmp( szParam, "EMITTER_WIDTH" ) == 0 )
			pPSys->set_width_cont( pCont );
		else if( strcmp( szParam, "EMITTER_HEIGHT" ) == 0 )
			pPSys->set_height_cont( pCont );
		else if( strcmp( szParam, "BIRTH_RATE" ) == 0 )
			pPSys->set_birthrate_cont( pCont );
		else if( strcmp( szParam, "TUMBLE" ) == 0 )
			pPSys->set_tumble_cont( pCont );
		else if( strcmp( szParam, "TUMBLE_SCALE" ) == 0 )
			pPSys->set_tumble_scale_cont( pCont );
		else
			delete pCont;
	}
}

void
MASLoaderC::parse_particle( ParticleSystemC* pPSys )
{
	int32	i32Val;
	float32	f32Val;

	do {
		read_row();

		if( is_token( "*VIEWPORT_PARTICLES" ) ) {
			sscanf( get_row(), "%*s %d", &i32Val );
			if( i32Val > pPSys->get_max_particle_count() )
				pPSys->set_max_particle_count( i32Val );
		}
		else if( is_token( "*RENDER_PARTICLES" ) ) {
			sscanf( get_row(), "%*s %d", &i32Val );
			if( i32Val > pPSys->get_max_particle_count() )
				pPSys->set_max_particle_count( i32Val );
		}
		else if( is_token( "*DROP_SIZE" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pPSys->set_drop_size( f32Val );
		}
		else if( is_token( "*SPEED" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pPSys->set_speed( f32Val );
		}
		else if( is_token( "*VARIATION" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pPSys->set_variation( f32Val );
		}
		else if( is_token( "*DISPLAY_TYPE" ) ) {
			sscanf( get_row(), "%*s %d", &i32Val );
		}
		else if( is_token( "*START_TIME" ) ) {
			sscanf( get_row(), "%*s %d", &i32Val );
			pPSys->set_start_time( i32Val );
		}
		else if( is_token( "*LIFE_TIME" ) ) {
			sscanf( get_row(), "%*s %d", &i32Val );
			pPSys->set_life_time( i32Val );
		}
		else if( is_token( "*EMITTER_WIDTH" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pPSys->set_width( f32Val );
		}
		else if( is_token( "*EMITTER_HEIGHT" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pPSys->set_height( f32Val );
		}
		else if( is_token( "*BIRTH_RATE" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pPSys->set_birth_rate( f32Val );
		}
		else if( is_token( "*CONSTANT_BIRTH_RATE" ) ) {
			pPSys->set_constant_birth_rate( true );
		}
		else if( is_token( "*RENDER_TYPE" ) ) {
			sscanf( get_row(), "%*s %d", &i32Val );
		}
		else if( is_token( "*TUMBLE" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pPSys->set_tumble( f32Val );
		}
		else if( is_token( "*TUMBLE_SCALE" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pPSys->set_tumble_scale( f32Val );
		}
		else if( is_token( "*PARAMETER_ANIMATION" ) ) {
			parse_particle_param_anim( pPSys );
		}
		else if( is_token( "*MODIFIERS" ) ) {
			read_row();
			while( !is_token( "}" ) ) {
				pPSys->add_ws_modifier( extract_string( get_row() ) );
				read_row();
			}
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );
}

ScenegraphItemI*
MASLoaderC::parse_particleobject()
{
	uint32		ui32Mtl;
	ColorC		rDiffuse;

	ParticleSystemC*	pPSys = new ParticleSystemC;
	if( !pPSys )
		return 0;

	pPSys->set_step_size( m_i32TicksPerFrame );

	bool		bHasMtl = false;
	Matrix3C	rTM;

	do {
		read_row();

		if( is_token( "*NODE_NAME" ) ) {
			pPSys->set_name( extract_string( get_row() ) );
		}
		else if( is_token( "*NODE_PARENT" ) ) {
			pPSys->set_parent_name( extract_string( get_row() ) );
		}
		else if( is_token( "*NODE_TM" ) ) {
			parse_object_tm( pPSys );
		}
		else if( is_token( "*NODE_IS_HIDDEN" ) ) {
			pPSys->set_hidden( true );
		}
		else if( is_token( "*PARTICLE_SYSTEM_RAIN" ) ) {
			pPSys->set_particle_type( PARTICLE_RAIN );
			parse_particle( pPSys );
		}
		else if( is_token( "*PARTICLE_SYSTEM_SNOW" ) ) {
			pPSys->set_particle_type( PARTICLE_SNOW );
			parse_particle( pPSys );
		}
		else if( is_token( "*MATERIAL_REF" ) ) {
			sscanf( get_row(), "%*s %d", &ui32Mtl );
			if( ui32Mtl < m_rMaterials.size() ) {
				bHasMtl = true;
			}
		}
		else if( is_token( "*WIREFRAME_COLOR" ) ) {
			if( !bHasMtl ) {
				float32	f32R, f32G, f32B;
				sscanf( get_row(), "%*s %f %f %f", &f32R, &f32G, &f32B );
				rDiffuse[0] = f32R;
				rDiffuse[1] = f32G;
				rDiffuse[2] = f32B;
			}
      	}
		else if( is_token( "*TM_ANIMATION" ) ) {
			parse_object_tm_anim( pPSys );
		}
		else if( is_token( "*NODE_VISIBILITY_TRACK" ) ) {
			parse_object_vis_track( pPSys );
		}
		else if( is_token( "*USER_PROP" ) ) {
			char*	szProp = extract_string( get_row() );

/*			if( strstr( szProp, "ADDITIVE" ) != NULL )
				rMesh.m_bAdditive = true;
			if( strstr( szProp, "TOON" ) != NULL )
				rMesh.m_bToon = true;*/
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;

	} while( !eof() );


	if( !bHasMtl ) {
		pPSys->set_color( rDiffuse );
	}
	else {
		pPSys->set_color( m_rMaterials[ui32Mtl]->m_rDiffuse );
		pPSys->set_transparency( m_rMaterials[ui32Mtl]->m_f32Transparency);
		pPSys->set_selfillum( m_rMaterials[ui32Mtl]->m_f32SelfIllum );
		pPSys->set_texture( get_texture( m_rMaterials[ui32Mtl]->m_sDiffuseTexName ) );

		if( m_rMaterials[ui32Mtl]->m_bAdditive )
			pPSys->set_transparency_type( TRANSPARENCY_ADD );
	}

	return pPSys;
}



void
MASLoaderC::parse_wsm_object_wind_param_anim( WindC* pWind )
{
	char	szParam[128] = "";

	ContFloatC*	pCont = 0;

	do {
		read_row();

		if( is_token( "*PARAMETER" ) ) {
			sscanf( get_row(), "%*s %s", szParam );
		}
		else if( is_token( "*CONTROL_FLOAT_TCB" ) ) {
			pCont = parse_tcb_float_track();
		}
		else if( is_token( "*CONTROL_FLOAT_SAMPLE" ) ) {
			pCont = parse_sample_float_track();
		}
		else if( is_token( "*CONTROL_FLOAT_LINEAR" ) ) {
			pCont = parse_lin_float_track();
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	if( pCont ) {
		if( strcmp( szParam, "STRENGTH" ) == 0 )
			pWind->set_strength_cont( pCont );
		else if( strcmp( szParam, "DECAY" ) == 0 )
			pWind->set_decay_cont( pCont );
		else if( strcmp( szParam, "TURBULENCE" ) == 0 )
			pWind->set_turbulence_cont( pCont );
		else if( strcmp( szParam, "FREQUENCY" ) == 0 )
			pWind->set_frequency_cont( pCont );
		else if( strcmp( szParam, "SCALE" ) == 0 )
			pWind->set_scale_cont( pCont );
		else
			delete pCont;
	}
}

void
MASLoaderC::parse_wsm_object_gravity_param_anim( GravityC* pGrav )
{
	char	szParam[128] = "";

	ContFloatC*	pCont = 0;

	do {
		read_row();

		if( is_token( "*PARAMETER" ) ) {
			sscanf( get_row(), "%*s %s", szParam );
		}
		else if( is_token( "*CONTROL_FLOAT_TCB" ) ) {
			pCont = parse_tcb_float_track();
		}
		else if( is_token( "*CONTROL_FLOAT_SAMPLE" ) ) {
			pCont = parse_sample_float_track();
		}
		else if( is_token( "*CONTROL_FLOAT_LINEAR" ) ) {
			pCont = parse_lin_float_track();
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	if( pCont ) {
		if( strcmp( szParam, "STRENGTH" ) == 0 )
			pGrav->set_strength_cont( pCont );
		else if( strcmp( szParam, "DECAY" ) == 0 )
			pGrav->set_decay_cont( pCont );
		else
			delete pCont;
	}
}

void
MASLoaderC::parse_wsm_object_deflector_param_anim( DeflectorC* pDef )
{
	char	szParam[128] = "";

	ContFloatC*	pCont = 0;

	do {
		read_row();

		if( is_token( "*PARAMETER" ) ) {
			sscanf( get_row(), "%*s %s", szParam );
		}
		else if( is_token( "*CONTROL_FLOAT_TCB" ) ) {
			pCont = parse_tcb_float_track();
		}
		else if( is_token( "*CONTROL_FLOAT_SAMPLE" ) ) {
			pCont = parse_sample_float_track();
		}
		else if( is_token( "*CONTROL_FLOAT_LINEAR" ) ) {
			pCont = parse_lin_float_track();
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	if( pCont ) {
		if( strcmp( szParam, "BOUNCE" ) == 0 )
			pDef->set_bounce_cont( pCont );
		else if( strcmp( szParam, "WIDTH" ) == 0 )
			pDef->set_width_cont( pCont );
		else if( strcmp( szParam, "HEIGHT" ) == 0 )
			pDef->set_height_cont( pCont );
		else if( strcmp( szParam, "VARIATION" ) == 0 )
			pDef->set_variation_cont( pCont );
		else if( strcmp( szParam, "CHAOS" ) == 0 )
			pDef->set_chaos_cont( pCont );
		else if( strcmp( szParam, "FRICTION" ) == 0 )
			pDef->set_friction_cont( pCont );
		else if( strcmp( szParam, "INHERIT_VELOCITY" ) == 0 )
			pDef->set_inherit_vel_cont( pCont );
		else
			delete pCont;
	}
}


void
MASLoaderC::parse_wsm_object_wind( WSMObjectC* pObj )
{
	int32	i32Val;
	float32	f32Val;

	WindC*	pWind = new WindC( pObj );

	do {
		read_row();

		if( is_token( "*STRENGTH" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pWind->set_strength( f32Val );
		}
		else if( is_token( "*DECAY" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pWind->set_decay( f32Val );
		}
		else if( is_token( "*TYPE" ) ) {
			sscanf( get_row(), "%*s %d", &i32Val );
			pWind->set_wind_type( i32Val );
		}
		else if( is_token( "*ICON_SIZE" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pWind->set_size( f32Val );
		}
		else if( is_token( "*TURBULENCE" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pWind->set_turbulence( f32Val );
		}
		else if( is_token( "*FREQUENCY" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pWind->set_frequency( f32Val );
		}
		else if( is_token( "*SCALE" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pWind->set_scale( f32Val );
		}
		else if( is_token( "*PARAMETER_ANIMATION" ) ) {
			parse_wsm_object_wind_param_anim( pWind );
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pObj->set_force_field( pWind );
}

void
MASLoaderC::parse_wsm_object_gravity( WSMObjectC* pObj )
{
	int32	i32Val;
	float32	f32Val;

	GravityC*	pGrav = new GravityC( pObj );

	do {
		read_row();

		if( is_token( "*STRENGTH" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pGrav->set_strength( f32Val );
		}
		else if( is_token( "*DECAY" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pGrav->set_decay( f32Val );
		}
		else if( is_token( "*TYPE" ) ) {
			sscanf( get_row(), "%*s %d", &i32Val );
			pGrav->set_gravity_type( i32Val );
		}
		else if( is_token( "*ICON_SIZE" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pGrav->set_size( f32Val );
		}
		else if( is_token( "*PARAMETER_ANIMATION" ) ) {
			parse_wsm_object_gravity_param_anim( pGrav );
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pObj->set_force_field( pGrav );
}


void
MASLoaderC::parse_wsm_object_deflector( WSMObjectC* pObj )
{
	int32	i32Val;
	float32	f32Val;

	DeflectorC*	pDef = new DeflectorC( pObj );

	do {
		read_row();

		if( is_token( "*BOUNCE" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pDef->set_bounce( f32Val );
		}
		else if( is_token( "*WIDTH" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pDef->set_width( f32Val );
		}
		else if( is_token( "*HEIGHT" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pDef->set_height( f32Val );
		}
		else if( is_token( "*VARIATION" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pDef->set_variation( f32Val );
		}
		else if( is_token( "*CHAOS" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pDef->set_chaos( f32Val );
		}
		else if( is_token( "*FRICTION" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pDef->set_friction( f32Val );
		}
		else if( is_token( "*INHERIT_VELOCITY" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pDef->set_inherit_velocity( f32Val );
		}
		else if( is_token( "*PARAMETER_ANIMATION" ) ) {
			parse_wsm_object_deflector_param_anim( pDef );
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pObj->set_collision_object( pDef );
}

ScenegraphItemI*
MASLoaderC::parse_wsm_object()
{
	Matrix3C	rTM;
	WSMObjectC*	pObj = new WSMObjectC;

	if( !pObj )
		return 0;

	do {
		read_row();

		if( is_token( "*NODE_NAME" ) ) {
			pObj->set_name( extract_string( get_row() ) );
		}
		else if( is_token( "*NODE_PARENT" ) ) {
			pObj->set_parent_name( extract_string( get_row() ) );
		}
		else if( is_token( "*NODE_TM" ) ) {
			parse_object_tm( pObj );
		}
		else if( is_token( "*TM_ANIMATION" ) ) {
			parse_object_tm_anim( pObj );
		}
		else if( is_token( "*NODE_VISIBILITY_TRACK" ) ) {
			parse_object_vis_track( pObj );
		}
		else if( is_token( "*WIND" ) ) {
			parse_wsm_object_wind( pObj );
		}
		else if( is_token( "*GRAVITY" ) ) {
			parse_wsm_object_gravity( pObj );
		}
		else if( is_token( "*DEFLECTOR" ) ) {
			parse_wsm_object_deflector( pObj );
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	return pObj;
}


ScenegraphItemI*
MASLoaderC::parse_helperobject()
{
	Matrix3C	rTM;
	MeshC*		pMesh = new MeshC;

	if( !pMesh )
		return 0;

	do {
		read_row();

		if( is_token( "*NODE_NAME" ) ) {
			pMesh->set_name( extract_string( get_row() ) );
		}
		else if( is_token( "*NODE_PARENT" ) ) {
			pMesh->set_parent_name( extract_string( get_row() ) );
		}
		else if( is_token( "*NODE_TM" ) ) {
			parse_object_tm( pMesh );
		}
		else if( is_token( "*TM_ANIMATION" ) ) {
			parse_object_tm_anim( pMesh );
		}
		else if( is_token( "*NODE_VISIBILITY_TRACK" ) ) {
			parse_object_vis_track( pMesh );
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	return pMesh;
}


ScenegraphItemI*
MASLoaderC::parse_bone()
{
	BoneC*	pBone = new BoneC;

	uint32	ui32BoneId;

	if( !pBone )
		return 0;

	do {
		read_row();

		if( is_token( "*NODE_NAME" ) ) {
			pBone->set_name( extract_string( get_row() ) );
		}
		else if( is_token( "*NODE_PARENT" ) ) {
			pBone->set_parent_name( extract_string( get_row() ) );
		}
		else if( is_token( "*NODE_TM" ) ) {
			parse_object_tm( pBone );
		}
		else if( is_token( "*TM_ANIMATION" ) ) {
			parse_object_tm_anim( pBone );
		}
		else if( is_token( "*BONE_ID" ) ) {
			sscanf( get_row(), "%*s %d", &ui32BoneId );
			pBone->set_bone_id( ui32BoneId );
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	return pBone;
}



PathC*
MASLoaderC::parse_path()
{
	PathC*	pPath = new PathC;

	float32	f32KnotX = 0, f32KnotY = 0, f32KnotZ = 0;
	float32	f32InVecX = 0, f32InVecY = 0, f32InVecZ = 0;
	float32	f32OutVecX = 0, f32OutVecY = 0, f32OutVecZ = 0;

	if( !pPath )
		return 0;

	do {
		read_row();

		if( is_token( "*SPLINE3D_KNOT" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32KnotX, &f32KnotY, &f32KnotZ );
		}
		else if( is_token( "*SPLINE3D_OUTVECTOR" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32OutVecX, &f32OutVecY, &f32OutVecZ );
		}
		else if( is_token( "*SPLINE3D_INVECTOR" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32InVecX, &f32InVecY, &f32InVecZ );

			KnotC	rKnot;
			Vector3C	rKnotVec( f32KnotX, f32KnotY, f32KnotZ );
			Vector3C	rInVec( f32InVecX, f32InVecY, f32InVecZ );
			Vector3C	rOutVec( f32OutVecX, f32OutVecY, f32OutVecZ );

			rKnot.set_knot( rKnotVec );
			rKnot.set_out_vector( rOutVec );
			rKnot.set_in_vector( rInVec );

			pPath->add_knot( rKnot );
		}
		else if( is_token( "*SPLINE3D_CLOSED" ) ) {
			pPath->set_closed( true );
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pPath->calc_outline();

	return pPath;
}


ScenegraphItemI*
MASLoaderC::parse_shape()
{
	ShapeC*		pShape = new ShapeC;
	bool		bHasMtl = false;
	bool		bAdditive = false;
	uint32		ui32Mtl;
	ColorC		rDiffuse;

	uint32	ui32SplineCount = 0;

	if( !pShape )
		return 0;

	do {
		read_row();

		if( is_token( "*NODE_NAME" ) ) {
			pShape->set_name( extract_string( get_row() ) );
		}
		else if( is_token( "*NODE_PARENT" ) ) {
			pShape->set_parent_name( extract_string( get_row() ) );
		}
		else if( is_token( "*NODE_TM" ) ) {
			parse_object_tm( pShape );
		}
		else if( is_token( "*TM_ANIMATION" ) ) {
			parse_object_tm_anim( pShape );
		}
		else if( is_token( "*SPLINE3D_LINECOUNT" ) ) {
			sscanf( get_row(), "%*s %d", &ui32SplineCount );
		}
		else if( is_token( "*SPLINE3D_LINE" ) ) {
			PathC*	pPath = parse_path();
			pShape->add_path( pPath );
		}
		else if( is_token( "*MATERIAL_REF" ) ) {
			sscanf( get_row(), "%*s %d", &ui32Mtl );
			if( ui32Mtl < m_rMaterials.size() ) {
				bHasMtl = true;
			}
		}
		else if( is_token( "*WIREFRAME_COLOR" ) ) {
			if( !bHasMtl ) {
				float32	f32R, f32G, f32B;
				sscanf( get_row(), "%*s %f %f %f", &f32R, &f32G, &f32B );
				rDiffuse[0] = f32R;
				rDiffuse[1] = f32G;
				rDiffuse[2] = f32B;
			}
      	}
		else if( is_token( "*USER_PROP" ) ) {
			char*	szProp = extract_string( get_row() );

			if( strstr( szProp, "ADDITIVE" ) != NULL )
				bAdditive = true;
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	pShape->calc_bbox();


	if( !bHasMtl )
		pShape->set_diffuse( rDiffuse );
	else {
		pShape->set_ambient( m_rMaterials[ui32Mtl]->m_rAmbient );
		pShape->set_diffuse( m_rMaterials[ui32Mtl]->m_rDiffuse );
		pShape->set_specular( m_rMaterials[ui32Mtl]->m_rSpecular );
		pShape->set_shininess( m_rMaterials[ui32Mtl]->m_f32Shininess );
		pShape->set_transparency( m_rMaterials[ui32Mtl]->m_f32Transparency);
		pShape->set_selfillum( m_rMaterials[ui32Mtl]->m_f32SelfIllum );
	}

	pShape->set_additive( bAdditive );

	return pShape;
}


void
MASLoaderC::parse_map( string& sName, uint32& ui32Mapping, uint32& ui32Channel )
{
	char	szType[256];
	uint32	ui32Ch;

	ui32Mapping = MAPPING_NONE;

	do {
		read_row();
    
		if( is_token( "*BITMAP" ) ) {
			sName = extract_string( get_row() );
		}
		else if( is_token( "*MAP_TYPE" ) ) {

			//Explicit 1
			sscanf( get_row(), "%*s %s", szType );

			if( strcmp( szType, "Explicit" ) == 0 ) {
				ui32Mapping = MAPPING_EXPLICIT;
				sscanf( get_row(), "%*s %*s %d", &ui32Ch );
				ui32Channel = ui32Ch - 1;	// channel is one based
			}
			else if( strcmp( szType, "Spherical" ) == 0 ) {
				ui32Mapping = MAPPING_SPHERICAL;
			}
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );
}

void
MASLoaderC::parse_material_param_anim( MASMaterialS* pMtl )
{
	char	szParam[128] = "";

	ContFloatC*		pFloatCont = 0;
	ContVector3C*	pVecCont = 0;

	do {
		read_row();

		if( is_token( "*PARAMETER" ) ) {
			sscanf( get_row(), "%*s %s", szParam );
		}
		else if( is_token( "*CONTROL_FLOAT_TCB" ) ) {
			pFloatCont = parse_tcb_float_track();
		}
		else if( is_token( "*CONTROL_FLOAT_SAMPLE" ) ) {
			pFloatCont = parse_sample_float_track();
		}
		else if( is_token( "*CONTROL_FLOAT_LINEAR" ) ) {
			pFloatCont = parse_lin_float_track();
		}
		else if( is_token( "*CONTROL_POINT3_TCB" ) ) {
			pVecCont = parse_tcb_vector3_track();
		}
		else if( is_token( "*CONTROL_POINT3_SAMPLE" ) ) {
			pVecCont = parse_sample_vector3_track();
		}
		else if( is_token( "*CONTROL_POINT3_LINEAR" ) ) {
			pVecCont = parse_lin_vector3_track();
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) ) {
			break;
		}
	} while( !eof() );

	if( pFloatCont ) {
		if( strcmp( szParam, "OPACITY" ) == 0 ) {
			// invert opacity to transparency
			for( uint32 i = 0; i < pFloatCont->get_key_count(); i++ ) {
				KeyFloatC*	pKey = pFloatCont->get_key( i );
				pKey->set_value( 1.0f - pKey->get_value() );
			}
			pMtl->m_pTransparencyCont = pFloatCont;
		}
		else if( strcmp( szParam, "SELF_ILLUM" ) == 0 )
			pMtl->m_pSelfIllumCont = pFloatCont;
	}
	if( pVecCont ) {
		if( strcmp( szParam, "DIFFUSE" ) == 0 )
			pMtl->m_pDiffuseCont = pVecCont;
		else if( strcmp( szParam, "SPECULAR" ) == 0 )
			pMtl->m_pSpecularCont = pVecCont;
	}
}

void
MASLoaderC::parse_material( MASMaterialS* pMtl )
{
	float32	f32R, f32G, f32B, f32Val;
	uint32	ui32SubMtlCount = 0;

	read_row();
  
	do {
		read_row();

		if( is_token( "*MAP_DIFFUSE" ) ) {
			parse_map( pMtl->m_sDiffuseTexName, pMtl->m_ui32DiffuseMappingType, pMtl->m_ui32DiffuseTexChannel );
		}
		else if( is_token( "*MAP_REFLECT" ) ) {
			parse_map( pMtl->m_sReflectionTexName, pMtl->m_ui32ReflectionMappingType, pMtl->m_ui32ReflectionTexChannel );
		}
		else if( is_token( "*MAP_SELFILLUM" ) ) {
			parse_map( pMtl->m_sLightmapTexName, pMtl->m_ui32LightmapMappingType, pMtl->m_ui32LightmapTexChannel );
			TRACE( "has lightmap '%s' on channle %d\n", pMtl->m_sLightmapTexName.c_str(), pMtl->m_ui32LightmapTexChannel );
		}
		else if( is_token( "*MATERIAL_SHINE" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pMtl->m_f32Shininess = f32Val;
		}
		else if( is_token( "*MATERIAL_TRANSPARENCY" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pMtl->m_f32Transparency = f32Val;
		}
		else if( is_token( "*MATERIAL_SHINESTRENGTH" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			if( f32Val > 1.0 )
				f32Val = 1.0;
			if( f32Val < 0 )
				f32Val = 0;
			pMtl->m_rSpecular *= f32Val;
		}
		else if( is_token( "*MATERIAL_AMBIENT" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32R, &f32G, &f32B );
			pMtl->m_rAmbient = ColorC( f32R, f32G, f32B );
		}
		else if( is_token( "*MATERIAL_DIFFUSE" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32R, &f32G, &f32B );
			pMtl->m_rDiffuse = ColorC( f32R, f32G, f32B );
		}
		else if( is_token( "*MATERIAL_SPECULAR" ) ) {
			sscanf( get_row(), "%*s %f %f %f", &f32R, &f32G, &f32B );
			pMtl->m_rSpecular = ColorC( f32R, f32G, f32B );
		}
		else if( is_token( "*MATERIAL_SELFILLUM" ) ) {
			sscanf( get_row(), "%*s %f", &f32Val );
			pMtl->m_f32SelfIllum = f32Val;
		}
		else if( is_token( "*MATERIAL_TWOSIDED" ) ) {
			pMtl->m_bTwoSided = true;
		}
		else if( is_token( "*MATERIAL_XP_TYPE" ) ) {
			if( strstr( get_row(), "Additive" ) ) {
				pMtl->m_bAdditive = true;
			}
		}
		else if( is_token( "*PARAMETER_ANIMATION" ) ) {
			parse_material_param_anim( pMtl );
		}
		else if( is_token( "*NUMSUBMTLS" ) ) {
			sscanf( get_row(), "%*s %d", &ui32SubMtlCount );		
		}
		else if( is_token( "*SUBMATERIAL" ) ) {
			MASMaterialS* pNewMtl = new MASMaterialS;
			parse_material( pNewMtl );
			pMtl->m_rSubMaterials.push_back( pNewMtl );
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;

	} while( !eof() );

}

void
MASLoaderC::parse_materials()
{
	uint32	ui32NumMaterials;

	do {
		read_row();

		if( is_token( "*MATERIAL_COUNT" ) ) {
			sscanf( get_row(), "%*s %d", &ui32NumMaterials );

			for( uint32 i = 0; i < ui32NumMaterials; i++ ) {
				MASMaterialS* pMtl = new MASMaterialS;
				parse_material( pMtl );
				m_rMaterials.push_back( pMtl );
			}
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );

}



void
MASLoaderC::get_time_parameters( int32& i32FPS, int32& i32TicksPerFrame, int32& i32FirstFrame, int32& i32LastFrame )
{
	i32FPS = m_i32FPS;
	i32TicksPerFrame = m_i32TicksPerFrame;
	i32FirstFrame = m_i32FirstFrame;
	i32LastFrame = m_i32LastFrame;
}


void
MASLoaderC::parse_scene()
{
	do {
		read_row();
		if( is_token( "*SCENE_FIRSTFRAME" ) ) {
			sscanf( get_row(), "%*s %d", &m_i32FirstFrame );
		}
		else if( is_token( "*SCENE_LASTFRAME" ) ) {
			sscanf( get_row(), "%*s %d", &m_i32LastFrame );
		}
		else if( is_token( "*SCENE_FRAMESPEED" ) ) {
			sscanf( get_row(), "%*s %d", &m_i32FPS );
		}
		else if( is_token( "*SCENE_TICKSPERFRAME" ) ) {
			sscanf( get_row(), "%*s %d", &m_i32TicksPerFrame );
		}
		else if( is_block() )
			parse_dummy();
		else if( is_token( "}" ) )
			break;
	} while( !eof() );
}


ScenegraphItemI*
MASLoaderC::find_object( const string& sName )
{
	for( uint32 i = 0; i < m_rScenegraph.size(); i++ ) {
		if( sName.compare( m_rScenegraph[i]->get_name() ) == 0 )
			return m_rScenegraph[i];
	}

	return 0;
}

void
MASLoaderC::add_scenegraphitem( ScenegraphItemI* pItem )
{
	if( pItem ) {
		ScenegraphItemI*	pParent = 0;
		if( strlen( pItem->get_parent_name() ) ) {
			pParent = find_object( pItem->get_parent_name() );
		}
		pItem->set_parent( pParent );
		m_rScenegraph.push_back( pItem );
	}	
}

bool
MASLoaderC::load( const char* szName, PajaSystem::DemoInterfaceC* pInterface )
{
	m_pInterface = pInterface;

	if( (m_pStream = fopen( szName, "rb" )) == 0 )
		return false;

	// read the first row
	read_row();

	if( !strlen( get_row() ) || strstr( get_row(), "*3DSMAX_ASCIIEXPORT" ) == 0 ) {
		fclose( m_pStream );
		return false;
	}

	do {

		if( is_token( "*3DSMAX_ASCIIEXPORT" ) ) {
			sscanf( get_row(), "%*s %d", &m_ui32Version );
		}
		else if( is_token( "*SCENE" ) ) {
			parse_scene();
		}
		else if( is_token( "*MATERIAL_LIST" ) ) {
			parse_materials();
		}
		else if( is_token( "*CAMERAOBJECT" ) ) {
			// Don't store cameras to scenegraph.
			CameraC*	pCam = parse_camera();
			if( pCam )
				m_rCameras.push_back( pCam );
		}
		else if( is_token( "*LIGHTOBJECT" ) ) {
			// Don't store lights to scenegraph.
			LightC*		pLight = parse_light();
			if( pLight )
				m_rLights.push_back( pLight );
		}
		else if( is_token( "*GEOMOBJECT" ) ) {
			ScenegraphItemI*	pItem = parse_geomobject();
			add_scenegraphitem( pItem );
		}
		else if( is_token( "*PARTICLEOBJECT" ) ) {
			ScenegraphItemI*	pItem = parse_particleobject();
			add_scenegraphitem( pItem );
		}
		else if( is_token( "*WSMOBJECT" ) ) {
			ScenegraphItemI*	pItem = parse_wsm_object();
			add_scenegraphitem( pItem );
		}
		else if( is_token( "*GROUPHEAD" ) ) {
			ScenegraphItemI*	pItem = parse_helperobject();
			add_scenegraphitem( pItem );
		}
		else if( is_token( "*HELPEROBJECT" ) ) {
			ScenegraphItemI*	pItem = parse_helperobject();
			add_scenegraphitem( pItem );
		}
		else if( is_token( "*BONE" ) ) {
			ScenegraphItemI*	pItem = parse_bone();
			add_scenegraphitem( pItem );
		}
		else if( is_token( "*SHAPEOBJECT" ) ) {
			ScenegraphItemI*	pItem = parse_shape();
			add_scenegraphitem( pItem );
		}
		else if( is_block() ) {
			parse_dummy();
		}
		else if( is_token( "}" ) )
			break;

		read_row();

	} while( !eof() );


	uint32	i;

	//
	// Find correct bones, to store in the bone vertex lists on the meshes, based on the bone IDs.
	//
/*	for( i = 0; i < m_rScenegraph.size(); i++ ) {
		if( m_rScenegraph[i]->get_type() == CGITEM_MESH ) {
			MeshC*	pMesh = (MeshC*)m_rScenegraph[i];

			if( pMesh->get_bonelist_count() ) {
				for( uint32 j = 0; j < pMesh->get_bonelist_count(); j++ ) {

					BoneC*	pFoundBone = 0;

					// get bone by id
					for( uint32 k = 0; k < m_rScenegraph.size(); k++ ) {
						if( m_rScenegraph[k]->get_type() == CGITEM_BONE ) {
							BoneC*	pBone = (BoneC*)m_rScenegraph[k];
							if( pBone->get_bone_id() == pMesh->get_bonelist_boneid( j ) ) {
								pFoundBone = pBone;
								break;
							}
						}
					}

					if( pFoundBone )
						pMesh->set_bonelist_bone( j, pFoundBone );

				}
			}
		}
	}*/

	//
	// update dependancies
	//
	for( i = 0; i < m_rScenegraph.size(); i++ ) {
		m_rScenegraph[i]->update_dependencies( m_rScenegraph );
	}


	//
	// In the file base TMs does not have hierarchy applied on them.
	// Traverse the scenegraph and apply parent matrices to each base TM.
	//
	for( i = 0; i < m_rScenegraph.size(); i++ ) {
		ScenegraphItemI*	pItem = m_rScenegraph[i];
		if( m_rScenegraph[i]->get_type() == CGITEM_MESH || m_rScenegraph[i]->get_type() == CGITEM_SHAPE ) {
			if( pItem->get_parent() ) {
				Matrix3C	rBaseTM = pItem->get_base_tm() * pItem->get_parent()->get_base_tm();
				pItem->set_base_tm( rBaseTM );
			}
		}
	}



	fclose( m_pStream );

	return true;
}

FileHandleC*
MASLoaderC::get_texture( string& sOrigName )
{
	if( sOrigName.empty() ) {
//		TRACE( "get_texture: no name\n" );
		return 0;
	}

	TRACE( "requesting '%s'-", sOrigName.c_str() );

	FileHandleC*	pHandle = m_pInterface->request_import( sOrigName.c_str(), SUPERCLASS_IMAGE, NULL_CLASSID );
//	if( !pHandle )
//		OutputDebugString( "NO texture HANDLE!!!\n" );

	if( !pHandle ) {
		TRACE( "get_texture: '%s' NULL HANDLE!\n", sOrigName.c_str() );
	}
	else {
		TRACE( "get_texture: '%s' succeed\n", pHandle->get_importable()->get_filename() );
	}

	return pHandle;
}

void
MASLoaderC::get_scenegraph( std::vector<ScenegraphItemI*>& rScenegraph )
{
	rScenegraph = m_rScenegraph;
}

void
MASLoaderC::get_lights( std::vector<LightC*>& rLights )
{
	rLights = m_rLights;
}

void
MASLoaderC::get_cameras( std::vector<CameraC*>& rCameras )
{
	rCameras = m_rCameras;
}

