

#include <assert.h>
#include "ScenegraphItemI.h"
#include "PajaTypes.h"
#include "MeshC.h"
#include <algorithm>
#include "ImportableImageI.h"

using namespace Composition;
using namespace PajaTypes;
using namespace FileIO;
using namespace Import;


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 );
}



TexCoordListC::TexCoordListC()
{
	// empty
}

TexCoordListC::~TexCoordListC()
{
	// empty
}

void
TexCoordListC::add_texcoord( const Vector2C& rTex )
{
	m_rTexCoords.push_back( rTex[0] );
	m_rTexCoords.push_back( rTex[1] );
}

uint32
TexCoordListC::get_texcoord_count() const
{
	return m_rTexCoords.size() / 2;
}

Vector2C
TexCoordListC::get_texcoord( uint32 ui32Index )
{
	ui32Index *= 2;
	assert( ui32Index < m_rTexCoords.size() );
	return Vector2C( m_rTexCoords[ui32Index], m_rTexCoords[ui32Index + 1] );
}

float32*
TexCoordListC::get_texcoord_pointer()
{
	return &(m_rTexCoords[0]);
}


enum TexCoordListChunksE {
	CHUNK_TEXCOORDLIST_GEOMETRY	= 0x1000,
};

const uint32	TEXCOORD_VERSION_1 = 1;
const uint32	TEXCOORD_VERSION_2 = 2;
const uint32	TEXCOORD_VERSION = 3;


// serialize
uint32
TexCoordListC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	uint32	ui32Temp;
	float32	f32Temp2[2];
	uint32	i;

	// Save geometry
	pSave->begin_chunk( CHUNK_TEXCOORDLIST_GEOMETRY, TEXCOORD_VERSION );
		// texcoords
		ui32Temp = m_rTexCoords.size() / 2;
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );

		for( i = 0; i < m_rTexCoords.size(); i += 2 ) {
			f32Temp2[0] = m_rTexCoords[i + 0];
			f32Temp2[1] = m_rTexCoords[i + 1];
			ui32Error = pSave->write( f32Temp2, sizeof( f32Temp2 ) );
		}
	pSave->end_chunk();

	return ui32Error;
}

uint32
TexCoordListC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	float32	f32Temp2[2];
	uint32	i;

	while( (ui32Error = pLoad->open_chunk()) == IO_OK ) {

		switch( pLoad->get_chunk_id() ) {

		case CHUNK_TEXCOORDLIST_GEOMETRY:
			if( pLoad->get_chunk_version() == TEXCOORD_VERSION_1 ) {
				uint32	ui32Count;
				// texcoords
				ui32Error = pLoad->read( &ui32Count, sizeof( ui32Count ) );
				for( i = 0; i < ui32Count; i++ ) {
					ui32Error = pLoad->read( f32Temp2, sizeof( f32Temp2 ) );
					add_texcoord( Vector2C( f32Temp2[0], 1.0f - f32Temp2[1] ) );
				}
			}
			else if( pLoad->get_chunk_version() == TEXCOORD_VERSION ) {
				uint32	ui32Count;
				// texcoords
				ui32Error = pLoad->read( &ui32Count, sizeof( ui32Count ) );
				for( i = 0; i < ui32Count; i++ ) {
					ui32Error = pLoad->read( f32Temp2, sizeof( f32Temp2 ) );
					add_texcoord( Vector2C( f32Temp2 ) );
				}
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

		if( ui32Error != IO_OK && ui32Error != IO_END ) {
			return ui32Error;
		}
	}

	if( ui32Error != IO_OK && ui32Error != IO_END ) {
		return ui32Error;
	}

	return IO_OK;
}




IndexListC::IndexListC() :
	m_pDiffuseTextureHandle( 0 ),
	m_ui32DiffuseMappingType( MAPPING_NONE ),
	m_ui32DiffuseTexChannel( 0 ),
	m_pReflectionTextureHandle( 0 ),
	m_ui32ReflectionMappingType( MAPPING_NONE ),
	m_ui32ReflectionTexChannel( 0 ),
	m_pLightmapTextureHandle( 0 ),
	m_ui32LightmapMappingType( MAPPING_NONE ),
	m_ui32LightmapTexChannel( 0 ),
	m_bTransparent( false ),
	m_f32Shininess( 0 ),
	m_f32Transparency( 0 ),
	m_bTwoSided( false ),
	m_f32SelfIllum( 0 ),
	m_pTransparencyCont( 0 ),
	m_pSelfIllumCont( 0 ),
	m_pDiffuseCont( 0 ),
	m_pSpecularCont( 0 )
{
	// empty
}

IndexListC::~IndexListC()
{
	delete m_pTransparencyCont;
	delete m_pSelfIllumCont;
	delete m_pDiffuseCont;
	delete m_pSpecularCont;
}

void
IndexListC::eval_state( int32 i32Time )
{
	if( m_pDiffuseCont ) {
		Vector3C	rColor = m_pDiffuseCont->get_value( i32Time );
		m_rDiffuse[0] = rColor[0];
		m_rDiffuse[1] = rColor[1];
		m_rDiffuse[2] = rColor[2];
	}
	if( m_pSpecularCont ) {
		Vector3C	rColor = m_pSpecularCont->get_value( i32Time );
		m_rSpecular[0] = rColor[0];
		m_rSpecular[1] = rColor[1];
		m_rSpecular[2] = rColor[2];
	}
	if( m_pTransparencyCont ) {
		m_f32Transparency = m_pTransparencyCont->get_value( i32Time );
		if( m_f32Transparency < 0 )
			m_f32Transparency = 0;
		if( m_f32Transparency > 1 )
			m_f32Transparency = 1;
	}
	if( m_pSelfIllumCont )
		m_f32SelfIllum = m_pSelfIllumCont->get_value( i32Time );

	if( m_f32Transparency > 0.001f )
		m_bTransparent = true;
	else
		m_bTransparent = false;
}

void
IndexListC::add_index( uint32 ui32Index )
{
	m_rIndices.push_back( ui32Index );
}

uint32
IndexListC::get_index_count() const
{
	if( m_rIndexCache.size() )
		return m_rIndexCache.size();
	return m_rIndices.size();
}

uint32
IndexListC::get_index( uint32 ui32Index )
{
	assert( ui32Index < m_rIndices.size() );
	if( m_rIndexCache.size() )
		return m_rIndexCache[ui32Index];
	return m_rIndices[ui32Index];
}

uint32*
IndexListC::get_index_pointer()
{
	if( m_rIndexCache.size() )
		return &(m_rIndexCache[0]);
	return &(m_rIndices[0]);
}

// material
void
IndexListC::set_diffuse_texture( FileHandleC* pHandle )
{
	m_pDiffuseTextureHandle = pHandle;
}

FileHandleC*
IndexListC::get_diffuse_texture() const
{
	return m_pDiffuseTextureHandle;
}

void
IndexListC::set_diffuse_mapping_type( uint32 ui32Type )
{
	m_ui32DiffuseMappingType = ui32Type;
}

uint32
IndexListC::get_diffuse_mapping_type() const
{
	return m_ui32DiffuseMappingType;
}

void
IndexListC::set_diffuse_tex_channel( uint32 ui32Ch )
{
	m_ui32DiffuseTexChannel = ui32Ch;
}

uint32
IndexListC::get_diffuse_tex_channel() const
{
	return m_ui32DiffuseTexChannel;
}


void
IndexListC::set_reflection_texture( FileHandleC* pHandle )
{
	m_pReflectionTextureHandle = pHandle;
}

FileHandleC*
IndexListC::get_reflection_texture() const
{
	return m_pReflectionTextureHandle;
}

void
IndexListC::set_reflection_mapping_type( uint32 ui32Type )
{
	m_ui32ReflectionMappingType = ui32Type;
}

uint32
IndexListC::get_reflection_mapping_type() const
{
	return m_ui32ReflectionMappingType;
}

void
IndexListC::set_reflection_tex_channel( uint32 ui32Ch )
{
	m_ui32ReflectionTexChannel = ui32Ch;
}

uint32
IndexListC::get_reflection_tex_channel() const
{
	return m_ui32ReflectionTexChannel;
}


void
IndexListC::set_lightmap_texture( FileHandleC* pHandle )
{
	m_pLightmapTextureHandle = pHandle;
}

FileHandleC*
IndexListC::get_lightmap_texture() const
{
	return m_pLightmapTextureHandle;
}

void
IndexListC::set_lightmap_mapping_type( uint32 ui32Type )
{
	m_ui32LightmapMappingType = ui32Type;
}

uint32
IndexListC::get_lightmap_mapping_type() const
{
	return m_ui32LightmapMappingType;
}

void
IndexListC::set_lightmap_tex_channel( uint32 ui32Ch )
{
	m_ui32LightmapTexChannel = ui32Ch;
}

uint32
IndexListC::get_lightmap_tex_channel() const
{
	return m_ui32LightmapTexChannel;
}


void
IndexListC::set_ambient( const ColorC &rAmbient )
{
	m_rAmbient = rAmbient;
}

void
IndexListC::set_diffuse( const ColorC &rDiffuse )
{
	m_rDiffuse = rDiffuse;
}

void
IndexListC::set_specular( const ColorC &rSpecular )
{
	m_rSpecular = rSpecular;
}

void
IndexListC::set_shininess( const float32 f32Shininess )
{
	m_f32Shininess = f32Shininess;
}

void
IndexListC::set_transparency( const float32 f32Trans )
{
	m_f32Transparency = f32Trans;

	if( m_f32Transparency > 0.001f )
		m_bTransparent = true;
}

void
IndexListC::set_two_sided( bool bState )
{
	m_bTwoSided = bState;
}

void
IndexListC::set_selfillum( const float32 f32SelfIllum )
{
	m_f32SelfIllum = f32SelfIllum;
}

const ColorC&
IndexListC::get_ambient() const
{
	return m_rAmbient;
}

const ColorC&
IndexListC::get_diffuse() const
{
	return m_rDiffuse;
}

const ColorC&
IndexListC::get_specular() const
{
	return m_rSpecular;
}

float32
IndexListC::get_shininess() const
{
	return m_f32Shininess;
}

float32
IndexListC::get_transparency() const
{
	return m_f32Transparency;
}

bool
IndexListC::get_two_sided() const
{
	return m_bTwoSided;
}

float32
IndexListC::get_selfillum() const
{
	return m_f32SelfIllum;
}


void
IndexListC::set_diffuse_controller( ContVector3C* pCont )
{
	m_pDiffuseCont = pCont;
}

void
IndexListC::set_specular_controller( ContVector3C* pCont )
{
	m_pSpecularCont = pCont;
}

void
IndexListC::set_transparency_controller( ContFloatC* pCont )
{
	m_pTransparencyCont = pCont;
}

void
IndexListC::set_selfillum_controller( ContFloatC* pCont )
{
	m_pSelfIllumCont = pCont;
}

bool
IndexListC::is_transparent() const
{

//	return false;

	if( m_bTransparent )
		return true;

	if( m_pDiffuseTextureHandle ) {
		ImportableImageI*	pTex = 0;
		if( m_pDiffuseTextureHandle )
			pTex = (ImportableImageI*)m_pDiffuseTextureHandle->get_importable();

		if( pTex && pTex->get_data_bpp() == 8 || pTex->get_data_bpp() == 32 )
			return true;
	}

	return false;
}


void
IndexListC::add_cusp_edge( uint8 ui8CuspEdge )
{
	m_rCuspEdges.push_back( ui8CuspEdge );
}

uint32
IndexListC::get_cusp_edge_count() const
{
	return m_rCuspEdges.size();
}

uint8
IndexListC::get_cusp_edge( uint32 ui32Index )
{
	assert( ui32Index < m_rCuspEdges.size() );
	return m_rCuspEdges[ui32Index];
}

uint8*
IndexListC::get_cusp_edge_pointer()
{
	return &(m_rCuspEdges[0]);
}



void
IndexListC::sort_faces( float32* pVertices )
{
	int32	i;

	if( m_rIndexCache.size() != m_rIndices.size() )
		m_rIndexCache.resize( m_rIndices.size() );

	if( m_rSortList.size() != m_rIndices.size() / 3 )
		m_rSortList.resize( m_rIndices.size() / 3 );

	// sort indices
	float32	f32Depth;
	uint32	ui32Idx, ui32BaseIdx;
	uint32	ui32A, ui32B, ui32C;
	for( i = 0; i < m_rIndices.size() / 3; i++ ) {

		ui32Idx = i * 3;

		ui32A = m_rIndices[ui32Idx + 0];
		ui32B = m_rIndices[ui32Idx + 1];
		ui32C = m_rIndices[ui32Idx + 2];

		f32Depth = pVertices[ui32A];

		if( pVertices[ui32B] < f32Depth )
			f32Depth = pVertices[ui32B];

		if( pVertices[ui32C] < f32Depth )
			f32Depth = pVertices[ui32C];

		m_rSortList[i].m_i32Depth = *(int32*)&f32Depth;
		m_rSortList[i].m_ui32BaseIdx = ui32Idx;
	}

	std::sort( m_rSortList.begin(), m_rSortList.end() );

	for( i = 0; i < m_rSortList.size(); i++ ) {
		ui32BaseIdx = m_rSortList[i].m_ui32BaseIdx;
		m_rIndexCache[i * 3 + 0] = m_rIndices[ui32BaseIdx + 0];
		m_rIndexCache[i * 3 + 1] = m_rIndices[ui32BaseIdx + 1];
		m_rIndexCache[i * 3 + 2] = m_rIndices[ui32BaseIdx + 2];
	}
}


enum IndexListChunksE {
	CHUNK_INDEXLIST_GEOMETRY			= 0x1000,
	CHUNK_INDEXLIST_MATERIAL			= 0x1001,
	CHUNK_INDEXLIST_DIFFUSE_CONT		= 0x2000,
	CHUNK_INDEXLIST_SPECULAR_CONT		= 0x2001,
	CHUNK_INDEXLIST_TRANSPARENCY_CONT	= 0x2002,
	CHUNK_INDEXLIST_SELFILLUM_CONT		= 0x2003,
};

const uint32	INDEXLIST_VERSION_1 = 1;
const uint32	INDEXLIST_VERSION_2 = 2;
const uint32	INDEXLIST_VERSION = 3;

// serialize
uint32
IndexListC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	uint32	ui32Temp;
	uint16	ui16Temp;
	uint8	ui8Temp;
	uint8	ui8Color[4];
	uint32	i;

	// Save geometry
	pSave->begin_chunk( CHUNK_INDEXLIST_GEOMETRY, INDEXLIST_VERSION );
		// indices
		ui32Temp = m_rIndices.size();
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );

		if( m_rIndices.size() < 0xffff ) {
			// use WORD indices for low poly meshes
			for( i = 0; i < m_rIndices.size(); i++ ) {
				ui16Temp = (uint16)m_rIndices[i];
				ui32Error = pSave->write( &ui16Temp, sizeof( ui16Temp ) );
			}
		}
		else {
			// use DWORD indices for high poly meshes
			for( i = 0; i < m_rIndices.size(); i++ ) {
				ui32Temp = m_rIndices[i];
				ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
			}
		}

		// cusp edges
		ui32Temp = m_rCuspEdges.size();
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
		for( i = 0; i < m_rCuspEdges.size(); i++ ) {
			ui8Temp = m_rCuspEdges[i];
			ui32Error = pSave->write( &ui8Temp, sizeof( ui8Temp ) );
		}
	pSave->end_chunk();

	pSave->begin_chunk( CHUNK_INDEXLIST_MATERIAL, INDEXLIST_VERSION );
		//ambient
		ui8Color[0] = (uint8)(m_rAmbient[0] * 255.0f);
		ui8Color[1] = (uint8)(m_rAmbient[1] * 255.0f);
		ui8Color[2] = (uint8)(m_rAmbient[2] * 255.0f);
		ui8Color[3] = (uint8)(m_rAmbient[3] * 255.0f);
		ui32Error = pSave->write( ui8Color, 4 );

		//diffuse
		ui8Color[0] = (uint8)(m_rDiffuse[0] * 255.0f);
		ui8Color[1] = (uint8)(m_rDiffuse[1] * 255.0f);
		ui8Color[2] = (uint8)(m_rDiffuse[2] * 255.0f);
		ui8Color[3] = (uint8)(m_rDiffuse[3] * 255.0f);
		ui32Error = pSave->write( ui8Color, 4 );

		//specular
		ui8Color[0] = (uint8)(m_rSpecular[0] * 255.0f);
		ui8Color[1] = (uint8)(m_rSpecular[1] * 255.0f);
		ui8Color[2] = (uint8)(m_rSpecular[2] * 255.0f);
		ui8Color[3] = (uint8)(m_rSpecular[3] * 255.0f);
		ui32Error = pSave->write( ui8Color, 4 );

		// shininess
		ui32Error = pSave->write( &m_f32Shininess, sizeof( m_f32Shininess ) );

		// transparency
		ui32Error = pSave->write( &m_f32Transparency, sizeof( m_f32Transparency ) );

		if( m_f32Transparency > 0.001f )
			m_bTransparent = true;
		else
			m_bTransparent = false;

		// self illumination
		ui32Error = pSave->write( &m_f32SelfIllum, sizeof( m_f32SelfIllum ) );

		// Two sided
		ui32Temp = m_bTwoSided ? 1 : 0;
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );

		//
		// Diffuse texture
		//

		// mapping type
		ui32Error = pSave->write( &m_ui32DiffuseMappingType, sizeof( m_ui32DiffuseMappingType ) );

		// tex channel
		ui32Error = pSave->write( &m_ui32DiffuseTexChannel, sizeof( m_ui32DiffuseTexChannel ) );

		// Tex handle
		if( m_pDiffuseTextureHandle )
			ui32Temp = m_pDiffuseTextureHandle->get_id();
		else
			ui32Temp = 0xffffffff;
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );

		//
		// Reflection texture
		//

		// mapping type
		ui32Error = pSave->write( &m_ui32ReflectionMappingType, sizeof( m_ui32ReflectionMappingType ) );

		// tex channel
		ui32Error = pSave->write( &m_ui32ReflectionTexChannel, sizeof( m_ui32ReflectionTexChannel ) );

		// Tex handle
		if( m_pReflectionTextureHandle )
			ui32Temp = m_pReflectionTextureHandle->get_id();
		else
			ui32Temp = 0xffffffff;
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );

		//
		// Lightmap texture
		//

		// mapping type
		ui32Error = pSave->write( &m_ui32LightmapMappingType, sizeof( m_ui32LightmapMappingType ) );

		// tex channel
		ui32Error = pSave->write( &m_ui32LightmapTexChannel, sizeof( m_ui32LightmapTexChannel ) );

		// Tex handle
		if( m_pLightmapTextureHandle )
			ui32Temp = m_pLightmapTextureHandle->get_id();
		else
			ui32Temp = 0xffffffff;
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );

	pSave->end_chunk();

	// diffuse
	if( m_pDiffuseCont ) {
		pSave->begin_chunk( CHUNK_INDEXLIST_DIFFUSE_CONT, INDEXLIST_VERSION );
			ui32Error = m_pDiffuseCont->save( pSave );
		pSave->end_chunk();
	}

	// specular
	if( m_pSpecularCont ) {
		pSave->begin_chunk( CHUNK_INDEXLIST_SPECULAR_CONT, INDEXLIST_VERSION );
			ui32Error = m_pSpecularCont->save( pSave );
		pSave->end_chunk();
	}

	// transparency
	if( m_pTransparencyCont ) {
		pSave->begin_chunk( CHUNK_INDEXLIST_TRANSPARENCY_CONT, INDEXLIST_VERSION );
			ui32Error = m_pTransparencyCont->save( pSave );
		pSave->end_chunk();
	}

	// selfillum
	if( m_pSelfIllumCont ) {
		pSave->begin_chunk( CHUNK_INDEXLIST_SELFILLUM_CONT, INDEXLIST_VERSION );
			ui32Error = m_pSelfIllumCont->save( pSave );
		pSave->end_chunk();
	}

	return ui32Error;
}

uint32
IndexListC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	uint32	ui32Temp;
	uint16	ui16Temp;
	uint8	ui8Temp;
	uint8	ui8Color[4];
	uint32	i;

	while( (ui32Error = pLoad->open_chunk()) == IO_OK ) {

		switch( pLoad->get_chunk_id() ) {

		case CHUNK_INDEXLIST_MATERIAL:
			if( pLoad->get_chunk_version() <= INDEXLIST_VERSION ) {
				//ambient
				ui32Error = pLoad->read( ui8Color, 4 );
				m_rAmbient.convert_from_uint8( ui8Color[0], ui8Color[1], ui8Color[2], ui8Color[3] );

				//diffuse
				ui32Error = pLoad->read( ui8Color, 4 );
				m_rDiffuse.convert_from_uint8( ui8Color[0], ui8Color[1], ui8Color[2], ui8Color[3] );

				//specular
				ui32Error = pLoad->read( ui8Color, 4 );
				m_rSpecular.convert_from_uint8( ui8Color[0], ui8Color[1], ui8Color[2], ui8Color[3] );

				// shininess
				ui32Error = pLoad->read( &m_f32Shininess, sizeof( m_f32Shininess ) );

				// transparency
				ui32Error = pLoad->read( &m_f32Transparency, sizeof( m_f32Transparency ) );
				if( m_f32Transparency > 0.001f )
					m_bTransparent = true;
				else
					m_bTransparent = false;

				// self illumination
				ui32Error = pLoad->read( &m_f32SelfIllum, sizeof( m_f32SelfIllum ) );

				// two sided
				ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
				if( ui32Temp )
					m_bTwoSided = true;

				// Diffuse map

				// mapping type
				ui32Error = pLoad->read( &m_ui32DiffuseMappingType, sizeof( m_ui32DiffuseMappingType ) );

				// tex channel
				ui32Error = pLoad->read( &m_ui32DiffuseTexChannel, sizeof( m_ui32DiffuseTexChannel ) );

				// texture handle
				ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
				if( ui32Temp != 0xffffffff )
					pLoad->add_file_handle_patch( (void**)&m_pDiffuseTextureHandle, ui32Temp );

				// Reflection map

				// mapping type
				ui32Error = pLoad->read( &m_ui32ReflectionMappingType, sizeof( m_ui32ReflectionMappingType ) );

				// tex channel
				ui32Error = pLoad->read( &m_ui32ReflectionTexChannel, sizeof( m_ui32ReflectionTexChannel ) );

				// texture handle
				ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
				if( ui32Temp != 0xffffffff )
					pLoad->add_file_handle_patch( (void**)&m_pReflectionTextureHandle, ui32Temp );

				uint32	ui32Version = pLoad->get_chunk_version();

				// Reflection map
				if( pLoad->get_chunk_version() >= INDEXLIST_VERSION ) {

					// mapping type
					ui32Error = pLoad->read( &m_ui32LightmapMappingType, sizeof( m_ui32LightmapMappingType ) );

					// tex channel
					ui32Error = pLoad->read( &m_ui32LightmapTexChannel, sizeof( m_ui32LightmapTexChannel ) );

					// texture handle
					ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
					if( ui32Temp != 0xffffffff )
						pLoad->add_file_handle_patch( (void**)&m_pLightmapTextureHandle, ui32Temp );
				}

			}
			break;

		case CHUNK_INDEXLIST_GEOMETRY:
			if( pLoad->get_chunk_version() <= INDEXLIST_VERSION ) {
				uint32	ui32Count;
				// indices
				ui32Error = pLoad->read( &ui32Count, sizeof( ui32Count ) );

				if( pLoad->get_chunk_version() == INDEXLIST_VERSION_1 ) {
					// version 1 used DWORDS for indices
					for( i = 0; i < ui32Count; i++ ) {
						ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
						add_index( ui32Temp );
					}
				}
				else {
					if( ui32Count < 0xffff ) {
						// use WORD
						for( i = 0; i < ui32Count; i++ ) {
							ui32Error = pLoad->read( &ui16Temp, sizeof( ui16Temp ) );
							add_index( (uint32)ui16Temp );
						}
					}
					else {
						// use DWORD
						for( i = 0; i < ui32Count; i++ ) {
							ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
							add_index( ui32Temp );
						}
					}
				}

				// cusp edges
				ui32Error = pLoad->read( &ui32Count, sizeof( ui32Count ) );
				for( i = 0; i < ui32Count; i++ ) {
					ui32Error = pLoad->read( &ui8Temp, sizeof( ui8Temp ) );
					add_cusp_edge( ui8Temp );
				}
			}
			break;

		case CHUNK_INDEXLIST_DIFFUSE_CONT:
			if( pLoad->get_chunk_version() >= INDEXLIST_VERSION_2 ) {
				ContVector3C*	pCont = new ContVector3C;
				ui32Error = pCont->load( pLoad );
				m_pDiffuseCont = pCont;
			}
			break;


		case CHUNK_INDEXLIST_SPECULAR_CONT:
			if( pLoad->get_chunk_version() >= INDEXLIST_VERSION_2 ) {
				ContVector3C*	pCont = new ContVector3C;
				ui32Error = pCont->load( pLoad );
				m_pSpecularCont = pCont;
			}
			break;

		case CHUNK_INDEXLIST_TRANSPARENCY_CONT:
			if( pLoad->get_chunk_version() >= INDEXLIST_VERSION_2 ) {
				ContFloatC*	pCont = new ContFloatC;
				ui32Error = pCont->load( pLoad );
				m_pTransparencyCont = pCont;
			}
			break;

		case CHUNK_INDEXLIST_SELFILLUM_CONT:
			if( pLoad->get_chunk_version() >= INDEXLIST_VERSION_2 ) {
				ContFloatC*	pCont = new ContFloatC;
				ui32Error = pCont->load( pLoad );
				m_pSelfIllumCont = pCont;
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

		if( ui32Error != IO_OK && ui32Error != IO_END ) {
			return ui32Error;
		}
	}

	if( ui32Error != IO_OK && ui32Error != IO_END ) {
		return ui32Error;
	}

	return IO_OK;
}


//
// Old bone modifier
//

OldSkinModifierC::OldSkinModifierC()
{
}

OldSkinModifierC::~OldSkinModifierC()
{
}

uint32
OldSkinModifierC::get_type() const
{
	return MODIFIER_OLD_SKIN;
}


BoneC*
OldSkinModifierC::find_bone( uint32 ui32Id, std::vector<ScenegraphItemI*>& rScenegraph )
{
	// get bone by id
	for( uint32 i = 0; i < rScenegraph.size(); i++ ) {
		if( rScenegraph[i]->get_type() == CGITEM_BONE ) {
			BoneC*	pBone = (BoneC*)rScenegraph[i];
			if( pBone->get_bone_id() == ui32Id )
				return pBone;
		}
	}

	return 0;
}

void
OldSkinModifierC::update_dependencies( MeshC* pMesh, std::vector<ScenegraphItemI*> rScenegraph )
{
	for( uint32 j = 0; j < get_bonelist_count(); j++ ) {
		BoneC*	pBone = find_bone( get_bonelist_boneid( j ), rScenegraph );
		if( pBone )
			set_bonelist_bone( j, pBone );
	}
}


void
OldSkinModifierC::process( MeshC* pMesh, int32 i32Time, uint32 ui32VertCount, float32* pInVertices, float32* pInNormals,
					 float32* pOutVertices, float32* pOutNormals )
{
	//
	// bone modifier
	//

	uint32	i, j;

	for( i = 0; i < ui32VertCount * 3; i++ ) {
		pOutVertices[i] = 0;
		pOutNormals[i] = 0;
	}

	Matrix3C	rTM, rBaseTM, rNormTM;
	Vector3C	rVert;

	Matrix3C	rInvMeshTM = pMesh->get_base_tm().inverse();

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

		if( !m_rBoneVertLists[i].m_pBone )
			continue;

		rBaseTM = m_rBoneVertLists[i].m_pBone->get_base_tm() * (pMesh->get_base_tm().inverse());
		rTM = rBaseTM.inverse() * m_rBoneVertLists[i].m_pBone->get_tm() * rInvMeshTM;

		rNormTM = rTM;
		rNormTM[3] = Vector3C( 0, 0, 0 );

		for( j = 0; j < m_rBoneVertLists[i].m_rIndices.size(); j++ ) {
			uint32	ui32Idx = m_rBoneVertLists[i].m_rIndices[j];
			float32	f32Weight = m_rBoneVertLists[i].m_rWeights[j];

			rVert = Vector3C( pInVertices[ui32Idx * 3 + 0], pInVertices[ui32Idx * 3 + 1], pInVertices[ui32Idx * 3 + 2] );
			rVert *= rTM;
			rVert *= f32Weight;

			//m_rVertexCache[ui32Idx] = m_rVertexCache[ui32Idx] + rVert;
			pOutVertices[ui32Idx * 3 + 0] += rVert[0];
			pOutVertices[ui32Idx * 3 + 1] += rVert[1];
			pOutVertices[ui32Idx * 3 + 2] += rVert[2];

			rVert = Vector3C( pInNormals[ui32Idx * 3 + 0], pInNormals[ui32Idx * 3 + 1], pInNormals[ui32Idx * 3 + 2] );
			rVert *= rNormTM;
			rVert *= f32Weight;

			//m_rNormalCache[ui32Idx] = m_rNormalCache[ui32Idx] + rVert;
			pOutNormals[ui32Idx * 3 + 0] += rVert[0];
			pOutNormals[ui32Idx * 3 + 1] += rVert[1];
			pOutNormals[ui32Idx * 3 + 2] += rVert[2];
		}
	}

}



void
OldSkinModifierC::add_bone_vertex( uint32 ui32VertIdx, float32 f32Weight, uint32 ui32BoneId )
{
	uint32	i;
	uint32	ui32ListIdx = 0xffffffff;

	for( i = 0; i < m_rBoneVertLists.size(); i++ ) {
		if( m_rBoneVertLists[i].m_ui32BoneId == ui32BoneId ) {
			ui32ListIdx = i;
			break;
		}
	}

	if( ui32ListIdx == 0xffffffff ) {
		BoneVertListS	rList;
		rList.m_pBone = 0;
		rList.m_ui32BoneId = ui32BoneId;
		m_rBoneVertLists.push_back( rList );
		ui32ListIdx = m_rBoneVertLists.size() - 1;
	}

	m_rBoneVertLists[ui32ListIdx].m_rIndices.push_back( ui32VertIdx );
	m_rBoneVertLists[ui32ListIdx].m_rWeights.push_back( f32Weight );
}

uint32
OldSkinModifierC::get_bonelist_count() const
{
	return m_rBoneVertLists.size();
}

BoneC*
OldSkinModifierC::get_bonelist_bone( uint32 ui32Idx ) const
{
	if( ui32Idx < m_rBoneVertLists.size() )
		return m_rBoneVertLists[ui32Idx].m_pBone;
	return 0;
}

uint32
OldSkinModifierC::get_bonelist_boneid( uint32 ui32Idx ) const
{
	if( ui32Idx < m_rBoneVertLists.size() )
		return m_rBoneVertLists[ui32Idx].m_ui32BoneId;
	return 0xffffffff;
}

void
OldSkinModifierC::set_bonelist_bone( uint32 ui32Idx, BoneC* pBone )
{
	if( ui32Idx < m_rBoneVertLists.size() )
		m_rBoneVertLists[ui32Idx].m_pBone = pBone;
}

void
OldSkinModifierC::add_bonevertlist( BoneVertListS& rList )
{
	m_rBoneVertLists.push_back( rList );
}


enum OldSkinChunksE {
	CHUNK_OLDSKIN_BONEWEIGHTLIST	= 0x1000,
};

const uint32	OLDSKIN_VERSION = 1;

uint32
OldSkinModifierC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	uint32	ui32Temp;
	float32	f32Temp;
	uint32	i, j;

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

		pSave->begin_chunk( CHUNK_OLDSKIN_BONEWEIGHTLIST, OLDSKIN_VERSION );

			// bone id
			ui32Temp = m_rBoneVertLists[i].m_ui32BoneId;
			ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );

			// indices
			ui32Temp = m_rBoneVertLists[i].m_rIndices.size();
			ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
			for( j = 0; j < m_rBoneVertLists[i].m_rIndices.size(); j++ ) {
				ui32Temp = m_rBoneVertLists[i].m_rIndices[j];
				ui32Error = pSave->write( &ui32Temp, sizeof( &ui32Temp ) );
			}

			// weights
			ui32Temp = m_rBoneVertLists[i].m_rWeights.size();
			ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
			for( j = 0; j < m_rBoneVertLists[i].m_rWeights.size(); j++ ) {
				f32Temp = m_rBoneVertLists[i].m_rWeights[j];
				ui32Error = pSave->write( &f32Temp, sizeof( &f32Temp ) );
			}

		pSave->end_chunk();
	}

	return ui32Error;
}

uint32
OldSkinModifierC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	uint32	ui32Temp;
	float32	f32Temp;
	uint32	i;

	while( (ui32Error = pLoad->open_chunk()) == IO_OK ) {

		switch( pLoad->get_chunk_id() ) {

		case CHUNK_OLDSKIN_BONEWEIGHTLIST:

			if( pLoad->get_chunk_version() == OLDSKIN_VERSION ) {

				BoneVertListS	rList;

				rList.m_pBone = 0;

				// bone id
				ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
				rList.m_ui32BoneId = ui32Temp;

				// indices
				uint32	ui32IndexCount;
				ui32Error = pLoad->read( &ui32IndexCount, sizeof( ui32IndexCount ) );

				for( i = 0; i < ui32IndexCount; i++ ) {
					ui32Error = pLoad->read( &ui32Temp, sizeof( &ui32Temp ) );
					rList.m_rIndices.push_back( ui32Temp );
				}

				// weights
				uint32	ui32WeightCount;
				ui32Error = pLoad->read( &ui32WeightCount, sizeof( ui32WeightCount ) );
				for( i = 0; i < ui32WeightCount; i++ ) {
					ui32Error = pLoad->read( &f32Temp, sizeof( &f32Temp ) );
					rList.m_rWeights.push_back( f32Temp );
				}

				m_rBoneVertLists.push_back( rList );
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

		if( ui32Error != IO_OK && ui32Error != IO_END ) {
			return ui32Error;
		}
	}

	if( ui32Error != IO_OK && ui32Error != IO_END ) {
		return ui32Error;
	}

	return IO_OK;
}



BoneWeightListC::BoneWeightListC() :
	m_pBone( 0 )
{
	// empty
}

BoneWeightListC::~BoneWeightListC()
{
	// empty
}

void
BoneWeightListC::add_weight( uint32 ui32Index, float32 f32Weight )
{
	m_rIndices.push_back( ui32Index );
	m_rWeights.push_back( f32Weight );
}

void
BoneWeightListC::get_weight( uint32 ui32Idx, uint32& ui32Index, float32& f32Weight )
{
	assert( ui32Idx < m_rIndices.size() );
	ui32Index = m_rIndices[ui32Idx];
	f32Weight = m_rWeights[ui32Idx];
}

uint32*
BoneWeightListC::get_weight_index_ptr()
{
	return &(m_rIndices[0]);
}

float32*
BoneWeightListC::get_weight_value_ptr()
{
	return &(m_rWeights[0]);
}

uint32
BoneWeightListC::get_weight_count() const
{
	return m_rIndices.size();
}

void
BoneWeightListC::set_base_tm( const Matrix3C& rTm )
{
	m_rBaseTM = rTm;
}

const Matrix3C&
BoneWeightListC::get_base_tm() const
{
	return m_rBaseTM;
}

void
BoneWeightListC::set_bone_name( const char* szName )
{
	m_sBoneName = szName;
}

const char*
BoneWeightListC::get_bone_name() const
{
	return m_sBoneName.c_str();
}

void
BoneWeightListC::set_bone( ScenegraphItemI* pBone )
{
	m_pBone = pBone;
}

ScenegraphItemI*
BoneWeightListC::get_bone()
{
	return m_pBone;
}

enum BoneWListChunksE {
	CHUNK_BONEWLIST_BASE		= 0x1000,
};

const uint32	BONEWLIST_VERSION = 1;

uint32
BoneWeightListC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	float32	f32Matrix[12];
	uint32	i, j;

	pSave->begin_chunk( CHUNK_BONEWLIST_BASE, BONEWLIST_VERSION );

		std::string	sStr;
		
		// write bone name
		sStr =  m_sBoneName;
		if( sStr.size() > 255 )
			sStr.resize( 255 );
		ui32Error = pSave->write_str( sStr.c_str() );

		// save base TM
		for( i = 0; i < 4; i++ )
			for( j = 0; j < 3; j++ )
				f32Matrix[(i * 3) + j] = m_rBaseTM[i][j];
		ui32Error = pSave->write( f32Matrix, sizeof( f32Matrix ) );

		// save indices
		uint32	ui32Tmp;
		uint16	ui16Tmp;
		ui32Tmp = m_rIndices.size();
		ui32Error = pSave->write( &ui32Tmp, sizeof( ui32Tmp ) );
		if( m_rIndices.size() < 0xffff ) {
			// use WORD indices
			for( i = 0; i < m_rIndices.size(); i++ ) {
				ui16Tmp = m_rIndices[i];
				ui32Error = pSave->write( &ui16Tmp, sizeof( ui16Tmp ) );
			}
		}
		else {
			// use DWORD indices
			for( i = 0; i < m_rIndices.size(); i++ ) {
				ui32Tmp = m_rIndices[i];
				ui32Error = pSave->write( &ui32Tmp, sizeof( ui32Tmp ) );
			}
		}

		// save weights
		float32	f32Tmp;
		ui32Tmp = m_rWeights.size();
		ui32Error = pSave->write( &ui32Tmp, sizeof( ui32Tmp ) );
		for( i = 0; i < m_rIndices.size(); i++ ) {
			f32Tmp = m_rWeights[i];
			ui32Error = pSave->write( &f32Tmp, sizeof( f32Tmp ) );
		}

	pSave->end_chunk();

	return ui32Error;
}

uint32
BoneWeightListC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	float32	f32Matrix[12];
	uint32	i, j;
	char	szStr[256];

	while( (ui32Error = pLoad->open_chunk()) == IO_OK ) {

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_BONEWLIST_BASE:
			{
				if( pLoad->get_chunk_version() == BONEWLIST_VERSION ) {

					// read parent name
					ui32Error = pLoad->read_str( szStr );
					m_sBoneName = szStr;

					// read base TM
					ui32Error = pLoad->read( f32Matrix, sizeof( f32Matrix ) );
					for( i = 0; i < 4; i++ )
						for( j = 0; j < 3; j++ )
							m_rBaseTM[i][j] = f32Matrix[(i * 3) + j];

					// indices
					uint32	ui32IndexCount;
					uint32	ui32Idx;
					uint16	ui16Idx;
					ui32Error = pLoad->read( &ui32IndexCount, sizeof( ui32IndexCount ) );
					if( ui32IndexCount < 0xffff ) {
						// use WORD indices
						for( i = 0; i < ui32IndexCount; i++ ) {
							ui32Error = pLoad->read( &ui16Idx, sizeof( ui16Idx ) );
							m_rIndices.push_back( (uint32)ui16Idx );
						}
					}
					else {
						// use DWORD indices
						for( i = 0; i < ui32IndexCount; i++ ) {
							ui32Error = pLoad->read( &ui32Idx, sizeof( ui32Idx ) );
							m_rIndices.push_back( ui32Idx );
						}
					}

					// weights
					uint32	ui32WeightCount;
					float32	f32Weight;
					ui32Error = pLoad->read( &ui32WeightCount, sizeof( ui32WeightCount ) );
					for( i = 0; i < ui32WeightCount; i++ ) {
						ui32Error = pLoad->read( &f32Weight, sizeof( f32Weight ) );
						m_rWeights.push_back( f32Weight );
					}
				}
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

		if( ui32Error != IO_OK && ui32Error != IO_END ) {
			return ui32Error;
		}
	}

	return ui32Error;
}



SkinModifierC::SkinModifierC()
{
}

SkinModifierC::~SkinModifierC()
{
}

void
SkinModifierC::process( MeshC* pMesh, int32 i32Time, uint32 ui32VertCount, float32* pInVertices, float32* pInNormals,
					 float32* pOutVertices, float32* pOutNormals)
{
	//
	// bone modifier
	//

	uint32	i, j;

	for( i = 0; i < ui32VertCount * 3; i++ ) {
		pOutVertices[i] = 0;
		pOutNormals[i] = 0;
	}

	Matrix3C	rMeshRefMatrix = m_rInvSkinBaseTM;

	Matrix3C	rTM, rNormTM;
	Vector3C	rVert;

	for( i = 0; i < m_rBoneWeightLists.size(); i++ ) {
		BoneWeightListC*	pList = m_rBoneWeightLists[i];
		if( !pList->get_bone() )
			continue;

		ScenegraphItemI*	pBone = pList->get_bone();

		Matrix3C	rBoneBaseMatrix = pList->get_base_tm();
		Matrix3C	rBoneRefMatrix = m_rSkinBaseTM * rBoneBaseMatrix.inverse();
		rTM = rBoneRefMatrix * pBone->get_tm() * rMeshRefMatrix;

		rNormTM = rTM;
		rNormTM[3] = Vector3C( 0, 0, 0 );

		uint32*		pIndices = pList->get_weight_index_ptr();
		float32*	pWeights = pList->get_weight_value_ptr();

		for( j = 0; j < pList->get_weight_count(); j++ ) {

			uint32	ui32Idx = (*pIndices) * 3;
			float32	f32Weight = *pWeights;

			rVert = Vector3C( pInVertices[ui32Idx + 0], pInVertices[ui32Idx + 1], pInVertices[ui32Idx + 2] );
			rVert *= rTM;
			rVert *= f32Weight;

			pOutVertices[ui32Idx + 0] += rVert[0];
			pOutVertices[ui32Idx + 1] += rVert[1];
			pOutVertices[ui32Idx + 2] += rVert[2];

			rVert = Vector3C( pInNormals[ui32Idx + 0], pInNormals[ui32Idx + 1], pInNormals[ui32Idx + 2] );
			rVert *= rNormTM;
			rVert *= f32Weight;

			pOutNormals[ui32Idx + 0] += rVert[0];
			pOutNormals[ui32Idx + 1] += rVert[1];
			pOutNormals[ui32Idx + 2] += rVert[2];

			pIndices++;
			pWeights++;
		}
	}
}

uint32
SkinModifierC::get_type() const
{
	return MODIFIER_SKIN;
}


ScenegraphItemI*
SkinModifierC::find_bone( const char* szName, std::vector<ScenegraphItemI*>& rScenegraph )
{
	// get bone by name
	for( uint32 i = 0; i < rScenegraph.size(); i++ ) {
		if( strcmp( szName, rScenegraph[i]->get_name() ) == 0 ) {
			return rScenegraph[i];
		}
	}
	return 0;
}

void
SkinModifierC::update_dependencies( MeshC* pMesh, std::vector<ScenegraphItemI*> rScenegraph )
{
	for( uint32 j = 0; j < m_rBoneWeightLists.size(); j++ ) {
		BoneWeightListC*	pList = m_rBoneWeightLists[j];
		pList->set_bone( find_bone( pList->get_bone_name(), rScenegraph ) );
	}

	if( pMesh->get_parent() ) {
		m_rSkinBaseTM *= pMesh->get_parent()->get_base_tm();
	}
	m_rInvSkinBaseTM = m_rSkinBaseTM.inverse();
}

void
SkinModifierC::set_skin_base_tm( const Matrix3C& rTM )
{
	m_rSkinBaseTM = rTM;
}

void
SkinModifierC::add_boneweightlist( BoneWeightListC* pList )
{
	m_rBoneWeightLists.push_back( pList );
}

uint32
SkinModifierC::get_boneweightlist_count() const
{
	return m_rBoneWeightLists.size();
}

BoneWeightListC*
SkinModifierC::get_boneweightlist( uint32 ui32Idx ) const
{
	assert( ui32Idx < m_rBoneWeightLists.size() );
	return m_rBoneWeightLists[ui32Idx];
}


enum SkinModifierChunksE {
	CHUNK_SKINMOD_BONEWEIGHLIST	= 0x1000,
	CHUNK_SKINMOD_BASETM		= 0x1001,
};

const uint32	SKINMOD_VERSION = 1;

uint32
SkinModifierC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	float32	f32Matrix[12];
	uint32	i, j;

	// save bone weights
	for( i = 0; i < m_rBoneWeightLists.size(); i++ ) {
		pSave->begin_chunk( CHUNK_SKINMOD_BONEWEIGHLIST, SKINMOD_VERSION );
			m_rBoneWeightLists[i]->save( pSave );
		pSave->end_chunk();
	}

	// save skin base TM
	pSave->begin_chunk( CHUNK_SKINMOD_BASETM, SKINMOD_VERSION );
		for( i = 0; i < 4; i++ )
			for( j = 0; j < 3; j++ )
				f32Matrix[(i * 3) + j] = m_rSkinBaseTM[i][j];
		ui32Error = pSave->write( f32Matrix, sizeof( f32Matrix ) );
	pSave->end_chunk();

	return ui32Error;
}

uint32
SkinModifierC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	float32	f32Matrix[12];
	uint32	i, j;

	while( (ui32Error = pLoad->open_chunk()) == IO_OK ) {

		switch( pLoad->get_chunk_id() ) {

		case CHUNK_SKINMOD_BONEWEIGHLIST:
			if( pLoad->get_chunk_version() == SKINMOD_VERSION ) {
				BoneWeightListC*	pList = new BoneWeightListC;
				ui32Error = pList->load( pLoad );
				add_boneweightlist( pList );
			}
			break;

		case CHUNK_SKINMOD_BASETM:
			if( pLoad->get_chunk_version() == SKINMOD_VERSION ) {
				// read skin base TM
				ui32Error = pLoad->read( f32Matrix, sizeof( f32Matrix ) );
				for( i = 0; i < 4; i++ )
					for( j = 0; j < 3; j++ )
						m_rSkinBaseTM[i][j] = f32Matrix[(i * 3) + j];
			}
			break;


		default:
			assert( 0 );
		}

		pLoad->close_chunk();

		if( ui32Error != IO_OK && ui32Error != IO_END ) {
			return ui32Error;
		}
	}

	if( ui32Error != IO_OK && ui32Error != IO_END ) {
		return ui32Error;
	}

	return IO_OK;
}




//
// Morph Modifier
//

MorphChannelC::MorphChannelC() :
	m_pPercentageCont( 0 ),
	m_f32Percentage( 0 ),
	m_bUseLimits( false ),
	m_f32Min( 0 ),
	m_f32Max( 100 )
{
	// empty
}

MorphChannelC::~MorphChannelC()
{
	delete m_pPercentageCont;
}

void
MorphChannelC::add_delta( uint32 ui32Idx, const Vector3C& rPoint, const Vector3C& rNormal )
{
	m_rIndices.push_back( ui32Idx * 3 );

	m_rVertexDeltas.push_back( rPoint[0] );
	m_rVertexDeltas.push_back( rPoint[1] );
	m_rVertexDeltas.push_back( rPoint[2] );

	m_rNormalDeltas.push_back( rNormal[0] );
	m_rNormalDeltas.push_back( rNormal[1] );
	m_rNormalDeltas.push_back( rNormal[2] );
}

void
MorphChannelC::set_percentage_controller( ContFloatC* pCont )
{
	m_pPercentageCont = pCont;
}

void
MorphChannelC::set_percentage( float32 f32Value )
{
	m_f32Percentage = f32Value;
}

float32
MorphChannelC::get_percentage( int32 i32Time )
{
	float32	f32Percent = m_f32Percentage;
	if( m_pPercentageCont )
		f32Percent =  m_pPercentageCont->get_value( i32Time );

	if( f32Percent < 0 )
		f32Percent = 0;
	if( f32Percent > 100 )
		f32Percent = 100;

	if( m_bUseLimits ) {
		if( f32Percent < m_f32Min )
			f32Percent = m_f32Min;
		if( f32Percent > m_f32Max )
			f32Percent = m_f32Max;
	}

	return f32Percent;
}

void
MorphChannelC::set_use_limits( bool bState )
{
	m_bUseLimits = bState;
}

bool
MorphChannelC::get_use_limits() const
{
	return m_bUseLimits;
}

void
MorphChannelC::set_limits( float32 f32Min, float32 f32Max )
{
	m_f32Min = f32Min;
	m_f32Max = f32Max;
}

float32
MorphChannelC::get_min_limit() const
{
	return m_f32Min;
}

float32
MorphChannelC::get_max_limit() const
{
	return m_f32Max;
}

void
MorphChannelC::process( MeshC* pMesh, int32 i32Time, uint32 ui32VertCount, float32* pOutVertices, float32* pOutNormals )
{
	float32	f32Percent = get_percentage( i32Time ) / 100.0f;

	float32*	pVertDelta = &(m_rVertexDeltas[0]);
	float32*	pNormDelta = &(m_rNormalDeltas[0]);
	uint32*		pIndices = &(m_rIndices[0]);

	for( uint32 i = 0; i < m_rIndices.size(); i++ ) {
		float32*	pOutVert = &pOutVertices[*pIndices];
		float32*	pOutNorm = &pOutNormals[*pIndices];

		// vert
		*pOutVert++ += (*pVertDelta++) * f32Percent;
		*pOutVert++ += (*pVertDelta++) * f32Percent;
		*pOutVert++ += (*pVertDelta++) * f32Percent;

		// normal
		*pOutNorm++ += (*pNormDelta++) * f32Percent;
		*pOutNorm++ += (*pNormDelta++) * f32Percent;
		*pOutNorm++ += (*pNormDelta++) * f32Percent;

		pIndices++;
	}
}



/*
	ContFloatC*			m_pPercentageCont;
	PajaTypes::float32	m_f32Percentage;
	PajaTypes::float32	m_f32Min, m_f32Max;
	bool				m_bUseLimits;

	std::vector<PajaTypes::uint32>	m_rIndices;
	std::vector<PajaTypes::float32>	m_rVertexDeltas;
	std::vector<PajaTypes::float32>	m_rNormalDeltas;
*/

enum MorphModChannelModifierChunksE {
	CHUNK_MORPHMODCHANNEL_BASE			= 0x1000,
	CHUNK_MORPHMODCHANNEL_USELIMITS		= 0x1001,
	CHUNK_MORPHMODCHANNEL_DELTAS		= 0x1002,
	CHUNK_MORPHMODCHANNEL_CONTROLLER	= 0x1003,
};

const uint32	MORPHMODCHANNEL_VERSION = 1;


uint32
MorphChannelC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	uint32	i;
	uint16	ui16Temp;
	uint32	ui32Temp;
	float32	f32Temp3[3];

	// save base info
	pSave->begin_chunk( CHUNK_MORPHMODCHANNEL_BASE, MORPHMODCHANNEL_VERSION );
		// percentage
		ui32Error = pSave->write( &m_f32Percentage, sizeof( m_f32Percentage ) );
		// limits min
		ui32Error = pSave->write( &m_f32Min, sizeof( m_f32Min ) );
		// limits max
		ui32Error = pSave->write( &m_f32Max, sizeof( m_f32Max ) );
	pSave->end_chunk();

	if( m_bUseLimits ) {
		pSave->begin_chunk( CHUNK_MORPHMODCHANNEL_USELIMITS, MORPHMODCHANNEL_VERSION );
		pSave->end_chunk();
	}

	// save morph deltas
	pSave->begin_chunk( CHUNK_MORPHMODCHANNEL_DELTAS, MORPHMODCHANNEL_VERSION );

		// delta count
		uint32	ui32DeltaCount = m_rIndices.size();
		ui32Error = pSave->write( &ui32DeltaCount, sizeof( ui32DeltaCount ) );

		// indices
		if( ui32DeltaCount < 0xffff ) {
			// save WORD indices
			for( i = 0; i < ui32DeltaCount; i++ ) {
				ui16Temp = (uint16)m_rIndices[i];
				ui32Error = pSave->write( &ui16Temp, sizeof( ui16Temp ) );
			}
		}
		else {
			// save DWORD indices
			for( i = 0; i < ui32DeltaCount; i++ ) {
				ui32Temp = m_rIndices[i];
				ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
			}
		}

		// vertex deltas
		for( i = 0; i < ui32DeltaCount; i++ ) {
			f32Temp3[0] = m_rVertexDeltas[i * 3 + 0];
			f32Temp3[1] = m_rVertexDeltas[i * 3 + 1];
			f32Temp3[2] = m_rVertexDeltas[i * 3 + 2];
			ui32Error = pSave->write( f32Temp3, sizeof( f32Temp3 ) );
		}

		// normal deltas
		for( i = 0; i < ui32DeltaCount; i++ ) {
			f32Temp3[0] = m_rNormalDeltas[i * 3 + 0];
			f32Temp3[1] = m_rNormalDeltas[i * 3 + 1];
			f32Temp3[2] = m_rNormalDeltas[i * 3 + 2];
			ui32Error = pSave->write( f32Temp3, sizeof( f32Temp3 ) );
		}
	
	pSave->end_chunk();


	// visibility controller
	if( m_pPercentageCont ) {
		pSave->begin_chunk( CHUNK_MORPHMODCHANNEL_CONTROLLER, MORPHMODCHANNEL_VERSION );
			ui32Error = m_pPercentageCont->save( pSave );
		pSave->end_chunk();
	}


	return ui32Error;
}

uint32
MorphChannelC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	uint16	ui16Temp;
	uint32	ui32Temp;
	float32	f32Temp3[3];
	uint32	i;

	while( (ui32Error = pLoad->open_chunk()) == IO_OK ) {

		switch( pLoad->get_chunk_id() ) {

		case CHUNK_MORPHMODCHANNEL_BASE:
			if( pLoad->get_chunk_version() == MORPHMODCHANNEL_VERSION ) {
				// percentage
				ui32Error = pLoad->read( &m_f32Percentage, sizeof( m_f32Percentage ) );
				// limits min
				ui32Error = pLoad->read( &m_f32Min, sizeof( m_f32Min ) );
				// limits max
				ui32Error = pLoad->read( &m_f32Max, sizeof( m_f32Max ) );
			}
			break;

		case CHUNK_MORPHMODCHANNEL_USELIMITS:
			if( pLoad->get_chunk_version() == MORPHMODCHANNEL_VERSION ) {
				m_bUseLimits = true;
			}
			break;

		case CHUNK_MORPHMODCHANNEL_DELTAS:
			if( pLoad->get_chunk_version() == MORPHMODCHANNEL_VERSION ) {
				// delta count
				uint32	ui32DeltaCount = m_rIndices.size();
				ui32Error = pLoad->read( &ui32DeltaCount, sizeof( ui32DeltaCount ) );

				// indices
				if( ui32DeltaCount < 0xffff ) {
					// load WORD indices
					for( i = 0; i < ui32DeltaCount; i++ ) {
						ui32Error = pLoad->read( &ui16Temp, sizeof( ui16Temp ) );
						m_rIndices.push_back( (uint32)ui16Temp );
					}
				}
				else {
					// save DWORD indices
					for( i = 0; i < ui32DeltaCount; i++ ) {
						ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
						m_rIndices.push_back( ui32Temp );
					}
				}

				// vertex deltas
				for( i = 0; i < ui32DeltaCount; i++ ) {
					ui32Error = pLoad->read( f32Temp3, sizeof( f32Temp3 ) );
					m_rVertexDeltas.push_back( f32Temp3[0] );
					m_rVertexDeltas.push_back( f32Temp3[1] );
					m_rVertexDeltas.push_back( f32Temp3[2] );
				}

				// normal deltas
				for( i = 0; i < ui32DeltaCount; i++ ) {
					ui32Error = pLoad->read( f32Temp3, sizeof( f32Temp3 ) );
					m_rNormalDeltas.push_back( f32Temp3[0] );
					m_rNormalDeltas.push_back( f32Temp3[1] );
					m_rNormalDeltas.push_back( f32Temp3[2] );
				}
			}
			break;

		case CHUNK_MORPHMODCHANNEL_CONTROLLER:
			if( pLoad->get_chunk_version() == MORPHMODCHANNEL_VERSION ) {
				ContFloatC*	pCont = new ContFloatC;
				ui32Error = pCont->load( pLoad );
				m_pPercentageCont = pCont;
			}
			break;


		default:
			assert( 0 );
		}

		pLoad->close_chunk();

		if( ui32Error != IO_OK && ui32Error != IO_END ) {
			return ui32Error;
		}
	}

	if( ui32Error != IO_OK && ui32Error != IO_END ) {
		return ui32Error;
	}

	return IO_OK;
}


// modifier

MorphModifierC::MorphModifierC()
{
	// empty
}

MorphModifierC::~MorphModifierC()
{
	for( uint32 i = 0; i< m_rChannels.size(); i++ )
		delete m_rChannels[i];
	m_rChannels.clear();
}

void
MorphModifierC::process( MeshC* pMesh, int32 i32Time, uint32 ui32VertCount, float32* pInVertices, float32* pInNormals,
					 float32* pOutVertices, float32* pOutNormals )
{
	uint32	i;

	float32* pOutV = pOutVertices;
	float32* pOutN = pOutNormals;

	memcpy( pOutVertices, pInVertices, ui32VertCount * 3 * sizeof( float32 ) );
	memcpy( pOutNormals, pInNormals, ui32VertCount * 3 * sizeof( float32 ) );

	for( i = 0; i < m_rChannels.size(); i++ ) {
		m_rChannels[i]->process( pMesh, i32Time, ui32VertCount, pOutVertices, pOutNormals );
	}
}

uint32
MorphModifierC::get_type() const
{
	return MODIFIER_MORPH;
}

void
MorphModifierC::update_dependencies( MeshC* pMesh, std::vector<ScenegraphItemI*> rScenegraph )
{
	// empty
}

void
MorphModifierC::add_morph_channel( MorphChannelC* pChannel )
{
	if( pChannel )
		m_rChannels.push_back( pChannel );
}



enum MorphModModifierChunksE {
	CHUNK_MORPHMOD_CHANNEL			= 0x1000,
};

const uint32	MORPHMOD_VERSION = 1;


uint32
MorphModifierC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	uint32	i;


	for( i = 0; i < m_rChannels.size(); i++ ) {
		pSave->begin_chunk( CHUNK_MORPHMOD_CHANNEL, MORPHMOD_VERSION );
			ui32Error = m_rChannels[i]->save( pSave );
		pSave->end_chunk();
	}

	return ui32Error;
}

uint32
MorphModifierC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;

	while( (ui32Error = pLoad->open_chunk()) == IO_OK ) {

		switch( pLoad->get_chunk_id() ) {

		case CHUNK_MORPHMOD_CHANNEL:
			if( pLoad->get_chunk_version() == MORPHMOD_VERSION ) {
				MorphChannelC*	pChan = new MorphChannelC;
				ui32Error = pChan->load( pLoad );
				m_rChannels.push_back( pChan );
			}
			break;


		default:
			assert( 0 );
		}

		pLoad->close_chunk();

		if( ui32Error != IO_OK && ui32Error != IO_END ) {
			return ui32Error;
		}
	}

	if( ui32Error != IO_OK && ui32Error != IO_END ) {
		return ui32Error;
	}

	return IO_OK;
}




//
// Mesh
//


MeshC::MeshC() :
	m_pPosCont( 0 ),
	m_pRotCont( 0 ),
	m_pScaleCont( 0 ),
	m_pScaleRotCont( 0 ),
	m_pVisCont( 0 ),
	m_bAdditive( false ),
	m_bToon( false ),
	m_bVis( true ),
	m_bAlphaTest( false ),
	m_bCastShadows( true )
{
	// empty
}

MeshC::~MeshC()
{
	delete m_pPosCont;
	delete m_pRotCont;
	delete m_pScaleCont;
	delete m_pScaleRotCont;
	delete m_pVisCont;

	uint32	i;

	for( i = 0; i < m_rIndexLists.size(); i++ )
		delete m_rIndexLists[i];
	m_rIndexLists.clear();

	for( i = 0; i < m_rTexCoordLists.size(); i++ )
		delete m_rTexCoordLists[i];
	m_rTexCoordLists.clear();

	for( i = 0; i < m_rModifiers.size(); i++ )
		delete m_rModifiers[i];
	m_rModifiers.clear();

}

int32
MeshC::get_type()
{
	return CGITEM_MESH;
}

void
MeshC::set_position( const Vector3C& rPos )
{
	m_rPosition = rPos;
}


const
Vector3C&
MeshC::get_position()
{
	return m_rPosition;
}

void
MeshC::set_scale( const Vector3C& rScale )
{
	m_rScale = rScale;
}

const
Vector3C&
MeshC::get_scale()
{
	return m_rScale;
}

void
MeshC::set_rotation( const QuatC& rRot )
{
	m_rRotation = rRot;
}

const
QuatC&
MeshC::get_rotation()
{
	return m_rRotation;
}

void
MeshC::set_scale_rotation( const QuatC& rRot )
{
	m_rScaleRot = rRot;
}

const
QuatC&
MeshC::get_scale_rotation()
{
	return m_rScaleRot;
}

bool
MeshC::is_transparent() const
{
	if( m_bAlphaTest )
		return false;
	for( uint32 i = 0; i < m_rIndexLists.size(); i++ )
		if( m_rIndexLists[i]->is_transparent() )
			return true;
	return false;
}

void
MeshC::set_toon( bool bState )
{
	m_bToon = bState;
}

bool
MeshC::get_toon() const
{
	return m_bToon;
}

void
MeshC::set_alphatest( bool bState )
{
	m_bAlphaTest = bState;
}

bool
MeshC::get_alphatest() const
{
	return m_bAlphaTest;
}

void
MeshC::set_cast_shadows( bool bState )
{
	m_bCastShadows = bState;
}

bool
MeshC::get_cast_shadows() const
{
	return m_bCastShadows;
}

void
MeshC::set_additive( bool bState )
{
	m_bAdditive = bState;
}

bool
MeshC::get_additive() const
{
	return m_bAdditive;
}


void
MeshC::eval_state( int32 i32Time )
{
	m_bVis = true;
	if( m_pVisCont )
	{
		m_bVis = m_pVisCont->get_value( i32Time );
	}
	if( m_pParent && m_pParent->get_type() == CGITEM_MESH )
	{
		MeshC*	pParent = (MeshC*)m_pParent;
		if( !pParent->get_visible() )
			m_bVis = false;
	}

	if( m_pPosCont )
		m_rPosition = m_pPosCont->get_value( i32Time );
	if( m_pScaleCont )
		m_rScale = m_pScaleCont->get_value( i32Time );
	if( m_pScaleRotCont ) {
		m_rScaleRot = m_pScaleRotCont->get_value( i32Time );
	}
	if( m_pRotCont )
		m_rRotation = m_pRotCont->get_value( i32Time );


	Matrix3C	rScaleMat;
	Matrix3C	rScaleRotMat;
	Matrix3C	rRotMat;
	Matrix3C	rInvScaleRotMat;
	QuatC		rScaleRot( m_rScaleRot );
	QuatC		rRot( m_rRotation );
	QuatC		rInvScaleRot = rScaleRot.unit_inverse();

	rScaleMat.set_scale( m_rScale );
	rScaleRotMat.set_rot( rScaleRot );
	rRotMat.set_rot( rRot );
	rInvScaleRotMat.set_rot( rInvScaleRot );

	// Compose matrix
	m_rTM = rInvScaleRotMat * rScaleMat * rScaleRotMat * rRotMat;
	m_rTM[3] = m_rPosition;

	if( m_pParent ) {
//		TRACE( "has parent %s\n", m_pParent->get_name() );
		m_rTM *= m_pParent->get_tm();
	}

	for( uint32 i = 0; i < m_rIndexLists.size(); i++ )
		m_rIndexLists[i]->eval_state( i32Time );
}


void
MeshC::eval_geom( int32 i32Time )
{
	if( m_rModifiers.size() ) {

		// resize caches
		if( m_rVertexCache.size() != m_rVertices.size() )
			m_rVertexCache.resize( m_rVertices.size() );
		if( m_rNormalCache.size() != m_rNormals.size() )
			m_rNormalCache.resize( m_rNormals.size() );

		uint32	i;

		float32*	pInVertices = &(m_rVertices[0]);
		float32*	pInNormals = &(m_rNormals[0]);
		float32*	pOutVertices = &(m_rVertexCache[0]);
		float32*	pOutNormals = &(m_rNormalCache[0]);

		for( i = 0; i < m_rModifiers.size(); i++ ) {
			m_rModifiers[i]->process( this, i32Time, m_rVertices.size() / 3, pInVertices, pInNormals, pOutVertices, pOutNormals );
		}

		calc_bbox();
	}
}

void
MeshC::sort_faces( const Matrix3C& rCamTM )
{
	if( is_transparent() ) {

		uint32	i;

		// transform vertices
		Matrix3C	rTM;
		rTM = m_rTM * rCamTM;

		uint32	ui32VertexCount = get_vert_count();
		float32*	pVertices = get_vert_pointer();

		if( m_rSortCache.size() != ui32VertexCount )
			m_rSortCache.resize( ui32VertexCount );

		// transform only Z component
		pVertices;

		for( i = 0; i < ui32VertexCount; i++ ) {
			m_rSortCache[i] = pVertices[0] * rTM[0][2] + pVertices[1] * rTM[1][2] + pVertices[2] * rTM[2][2] + rTM[3][2];
			pVertices += 3;
		}

		for( i = 0; i < m_rIndexLists.size(); i++ )
			m_rIndexLists[i]->sort_faces( &(m_rSortCache[0]) );
	}
}


void
MeshC::set_position_controller( ContVector3C* pCont )
{
	m_pPosCont = pCont;
}

ContVector3C*
MeshC::get_position_controller()
{
	return m_pPosCont;
}

void
MeshC::set_rotation_controller( ContQuatC* pCont )
{
	m_pRotCont = pCont;
}

ContQuatC*
MeshC::get_rotation_controller()
{
	return m_pRotCont;
}

void
MeshC::set_scale_controller( ContVector3C* pCont )
{
	m_pScaleCont = pCont;
}

ContVector3C*
MeshC::get_scale_controller()
{
	return m_pScaleCont;
}

void
MeshC::set_scale_rotation_controller( ContQuatC* pCont )
{
	m_pScaleRotCont = pCont;
}

ContQuatC*
MeshC::get_scale_rotation_controller()
{
	return m_pScaleRotCont;
}

void
MeshC::set_vis_controller( ContBoolC* pCont )
{
	m_pVisCont = pCont;
}

ContBoolC*
MeshC::get_vis_controller()
{
	return m_pVisCont;
}

bool
MeshC::get_visible() const
{
	return m_bVis;
}


void
MeshC::add_index_list( IndexListC* pList )
{
	m_rIndexLists.push_back( pList );
}

uint32
MeshC::get_index_list_count() const
{
	return m_rIndexLists.size();
}

IndexListC*
MeshC::get_index_list( uint32 ui32Index )
{
	assert( ui32Index < m_rIndexLists.size() );
	return m_rIndexLists[ui32Index];
}


void
MeshC::add_texcoord_list( TexCoordListC* pList )
{
	m_rTexCoordLists.push_back( pList );
}

uint32
MeshC::get_texcoord_list_count() const
{
	return m_rTexCoordLists.size();
}

TexCoordListC*
MeshC::get_texcoord_list( uint32 ui32Index )
{
	if( ui32Index < m_rTexCoordLists.size() )
		return m_rTexCoordLists[ui32Index];
	return 0;
}


void
MeshC::add_vert( const Vector3C& rVec )
{
	m_rVertices.push_back( rVec[0] );
	m_rVertices.push_back( rVec[1] );
	m_rVertices.push_back( rVec[2] );
}

void
MeshC::add_norm( const Vector3C& rNorm )
{
	m_rNormals.push_back( rNorm[0] );
	m_rNormals.push_back( rNorm[1] );
	m_rNormals.push_back( rNorm[2] );
}

uint32
MeshC::get_vert_count() const
{
	if( m_rVertexCache.size() )
		return m_rVertexCache.size() / 3;
	return m_rVertices.size() / 3;
}

uint32
MeshC::get_norm_count() const
{
	if( m_rNormalCache.size() )
		return m_rNormalCache.size() / 3;
	return m_rNormals.size() / 3;
}

Vector3C
MeshC::get_vert( uint32 ui32Index )
{
	ui32Index *= 3;
	assert( ui32Index < m_rVertices.size() );
	if( m_rVertexCache.size() )
		return Vector3C( m_rVertexCache[ui32Index], m_rVertexCache[ui32Index + 1], m_rVertexCache[ui32Index + 2] );
	return Vector3C( m_rVertices[ui32Index], m_rVertices[ui32Index + 1], m_rVertices[ui32Index + 2] );
}

Vector3C
MeshC::get_norm( uint32 ui32Index )
{
	ui32Index *= 3;
	assert( ui32Index < m_rNormals.size() );
	if( m_rNormalCache.size() )
		return Vector3C( m_rNormalCache[ui32Index], m_rNormalCache[ui32Index + 1], m_rNormalCache[ui32Index + 2] );
	return Vector3C( m_rNormals[ui32Index], m_rNormals[ui32Index + 1], m_rNormals[ui32Index + 2] );
}

float32*
MeshC::get_vert_pointer()
{
	if( m_rVertexCache.size() )
		return &(m_rVertexCache[0]);
	return &(m_rVertices[0]);
}

float32*
MeshC::get_norm_pointer()
{
	if( m_rNormalCache.size() )
		return &(m_rNormalCache[0]);
	return &(m_rNormals[0]);
}



void
MeshC::add_modifier( ModifierI* pMod )
{
	m_rModifiers.push_back( pMod );
}

void
MeshC::update_dependencies( std::vector<ScenegraphItemI*> rScenegraph )
{
	uint32	i;
	// find parent
	m_pParent = 0;
	if( !m_sParentName.empty() ) {
		for( i = 0; i < rScenegraph.size(); i++ ) {
			if( m_sParentName.compare( rScenegraph[i]->get_name() ) == 0 ) {
				m_pParent = rScenegraph[i];
				break;
			}
		}
	}

	for( i = 0; i < m_rModifiers.size(); i++ )
		m_rModifiers[i]->update_dependencies( this, rScenegraph );
}


void
MeshC::calc_bbox()
{
	if( m_rVertexCache.size() ) {
		// calc bbox from cache
		m_rMin = m_rMax = Vector3C( m_rVertexCache[0], m_rVertexCache[1], m_rVertexCache[2] );
		for( uint32 i = 0; i < m_rVertexCache.size(); i += 3 ) {
			m_rMin[0] = __min( m_rVertexCache[i + 0], m_rMin[0] );
			m_rMin[1] = __min( m_rVertexCache[i + 1], m_rMin[1] );
			m_rMin[2] = __min( m_rVertexCache[i + 2], m_rMin[2] );
			m_rMax[0] = __max( m_rVertexCache[i + 0], m_rMax[0] );
			m_rMax[1] = __max( m_rVertexCache[i + 1], m_rMax[1] );
			m_rMax[2] = __max( m_rVertexCache[i + 2], m_rMax[2] );
		}
	}
	else if( m_rVertices.size() ) {
		// calc bbox from orig vertices
		m_rMin = m_rMax = Vector3C( m_rVertices[0], m_rVertices[1], m_rVertices[2] );
		for( uint32 i = 0; i < m_rVertices.size(); i += 3 ) {
			m_rMin[0] = __min( m_rVertices[i + 0], m_rMin[0] );
			m_rMin[1] = __min( m_rVertices[i + 1], m_rMin[1] );
			m_rMin[2] = __min( m_rVertices[i + 2], m_rMin[2] );
			m_rMax[0] = __max( m_rVertices[i + 0], m_rMax[0] );
			m_rMax[1] = __max( m_rVertices[i + 1], m_rMax[1] );
			m_rMax[2] = __max( m_rVertices[i + 2], m_rMax[2] );
		}
	}
}

Vector3C
MeshC::get_bbox_min() const
{
	return m_rMin;
}

Vector3C
MeshC::get_bbox_max() const
{
	return m_rMax;
}



enum MeshChunksE {
	CHUNK_MESH_BASE				= 0x1000,
	CHUNK_MESH_STATIC			= 0x1001,
	CHUNK_MESH_GEOMETRY			= 0x1002,
	CHUNK_MESH_MATERIAL			= 0x1003,
	CHUNK_MESH_BONEWEIGHTLIST	= 0x1004,		// deprecated!
	CHUNK_MESH_INDEXLIST		= 0x1005,
	CHUNK_MESH_TEXCOORDLIST		= 0x1006,
	CHUNK_MESH_ADDITIVE			= 0x1007,
	CHUNK_MESH_TOON				= 0x1008,
	CHUNK_MESH_ALPHATEST		= 0x1009,
	CHUNK_MESH_CASTSHADOWS		= 0x100a,
	CHUNK_MESH_CONT_POS			= 0x2000,
	CHUNK_MESH_CONT_ROT			= 0x2001,
	CHUNK_MESH_CONT_SCALE		= 0x2002,
	CHUNK_MESH_CONT_SCALEROT	= 0x2003,
	CHUNK_MESH_CONT_VIS			= 0x2004,
	CHUNK_MESH_MODIFIER			= 0x3000,
};

const uint32	MESH_VERSION = 1;

uint32
MeshC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	uint32	ui32Temp;
	float32	f32Temp3[3];
	float32	f32Temp4[4];
	uint32	i;

	eval_state( 0 );


	// Sace scenegraphitem base.
	pSave->begin_chunk( CHUNK_MESH_BASE, MESH_VERSION );
		ScenegraphItemI::save( pSave );
	pSave->end_chunk();

	// Save static transformation.
	pSave->begin_chunk( CHUNK_MESH_STATIC, MESH_VERSION );
		// position
		f32Temp3[0] = m_rPosition[0];
		f32Temp3[1] = m_rPosition[1];
		f32Temp3[2] = m_rPosition[2];
		ui32Error = pSave->write( f32Temp3, sizeof( f32Temp3 ) );
		// rotation
		f32Temp4[0] = m_rRotation[0];
		f32Temp4[1] = m_rRotation[1];
		f32Temp4[2] = m_rRotation[2];
		f32Temp4[3] = m_rRotation[3];
		ui32Error = pSave->write( f32Temp4, sizeof( f32Temp4 ) );
		// scale
		f32Temp3[0] = m_rScale[0];
		f32Temp3[1] = m_rScale[1];
		f32Temp3[2] = m_rScale[2];
		ui32Error = pSave->write( f32Temp3, sizeof( f32Temp3 ) );
		// scale rot
		f32Temp4[0] = m_rScaleRot[0];
		f32Temp4[1] = m_rScaleRot[1];
		f32Temp4[2] = m_rScaleRot[2];
		f32Temp4[3] = m_rScaleRot[3];
		ui32Error = pSave->write( f32Temp4, sizeof( f32Temp4 ) );
	pSave->end_chunk();

	// Save geometry
	pSave->begin_chunk( CHUNK_MESH_GEOMETRY, MESH_VERSION );
		// vertices
		ui32Temp = m_rVertices.size()  /3;
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
		for( i = 0; i < m_rVertices.size(); i += 3 ) {
			f32Temp3[0] = m_rVertices[i + 0];
			f32Temp3[1] = m_rVertices[i + 1];
			f32Temp3[2] = m_rVertices[i + 2];
			ui32Error = pSave->write( f32Temp3, sizeof( f32Temp3 ) );
		}

		// normals
		ui32Temp = m_rNormals.size() / 3;
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
		for( i = 0; i < m_rNormals.size(); i += 3 ) {
			f32Temp3[0] = m_rNormals[i + 0];
			f32Temp3[1] = m_rNormals[i + 1];
			f32Temp3[2] = m_rNormals[i + 2];
			ui32Error = pSave->write( f32Temp3, sizeof( f32Temp3 ) );
		}
	pSave->end_chunk();

	// index lists
	for( i = 0; i < m_rIndexLists.size(); i++ ) {
		pSave->begin_chunk( CHUNK_MESH_INDEXLIST, MESH_VERSION );
			ui32Error = m_rIndexLists[i]->save( pSave );
		pSave->end_chunk();
	}

	// tex coord lists
	for( i = 0; i < m_rTexCoordLists.size(); i++ ) {
		pSave->begin_chunk( CHUNK_MESH_TEXCOORDLIST, MESH_VERSION );
			ui32Error = m_rTexCoordLists[i]->save( pSave );
		pSave->end_chunk();
	}

	// pos controller
	if( m_pPosCont ) {
		pSave->begin_chunk( CHUNK_MESH_CONT_POS, MESH_VERSION );
			ui32Error = m_pPosCont->save( pSave );
		pSave->end_chunk();
	}

	// rot controller
	if( m_pRotCont ) {
		pSave->begin_chunk( CHUNK_MESH_CONT_ROT, MESH_VERSION );
			ui32Error = m_pRotCont->save( pSave );
		pSave->end_chunk();
	}

	// scale controller
	if( m_pScaleCont ) {
		pSave->begin_chunk( CHUNK_MESH_CONT_SCALE, MESH_VERSION );
			ui32Error = m_pScaleCont->save( pSave );
		pSave->end_chunk();
	}

	// scale rot controller
	if( m_pScaleRotCont ) {
		pSave->begin_chunk( CHUNK_MESH_CONT_SCALEROT, MESH_VERSION );
			ui32Error = m_pScaleRotCont->save( pSave );
		pSave->end_chunk();
	}

	// visibility controller
	if( m_pVisCont ) {
		pSave->begin_chunk( CHUNK_MESH_CONT_VIS, MESH_VERSION );
			ui32Error = m_pVisCont->save( pSave );
		pSave->end_chunk();
	}

	// additive flag
	if( m_bAdditive ) {
		pSave->begin_chunk( CHUNK_MESH_ADDITIVE, MESH_VERSION );
		pSave->end_chunk();
	}

	// toon flag
	if( m_bToon ) {
		pSave->begin_chunk( CHUNK_MESH_TOON, MESH_VERSION );
		pSave->end_chunk();
	}

	// alphatest flag
	if( m_bAlphaTest ) {
		pSave->begin_chunk( CHUNK_MESH_ALPHATEST, MESH_VERSION );
		pSave->end_chunk();
	}

	// cast shadows (NOTE! the flag is inverted!!)
	if( !m_bCastShadows ) {
		pSave->begin_chunk( CHUNK_MESH_CASTSHADOWS, MESH_VERSION );
		pSave->end_chunk();
	}

	// modifiers
	for( i = 0; i < m_rModifiers.size(); i++ ) {
		pSave->begin_chunk( CHUNK_MESH_MODIFIER, MESH_VERSION );
			// save type
			ui32Temp = m_rModifiers[i]->get_type();
			ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
			// save mod
			ui32Error = m_rModifiers[i]->save( pSave );
		pSave->end_chunk();
	}

	return ui32Error;
}

uint32
MeshC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	uint32	ui32Temp;
	float32	f32Temp;
	float32	f32Temp3[3];
	float32	f32Temp4[4];
	uint32	i;

	OldSkinModifierC*	pSkin = 0;

	while( (ui32Error = pLoad->open_chunk()) == IO_OK ) {

		switch( pLoad->get_chunk_id() ) {

		case CHUNK_MESH_BASE:
			if( pLoad->get_chunk_version() == MESH_VERSION )
				ScenegraphItemI::load( pLoad );

			break;

		case CHUNK_MESH_STATIC:
			if( pLoad->get_chunk_version() == MESH_VERSION ) {

				// position
				ui32Error = pLoad->read( f32Temp3, sizeof( f32Temp3 ) );
				m_rPosition[0] = f32Temp3[0];
				m_rPosition[1] = f32Temp3[1];
				m_rPosition[2] = f32Temp3[2];

				// rotation
				ui32Error = pLoad->read( f32Temp4, sizeof( f32Temp4 ) );
				m_rRotation[0] = f32Temp4[0];
				m_rRotation[1] = f32Temp4[1];
				m_rRotation[2] = f32Temp4[2];
				m_rRotation[3] = f32Temp4[3];

				// scale
				ui32Error = pLoad->read( f32Temp3, sizeof( f32Temp3 ) );
				m_rScale[0] = f32Temp3[0];
				m_rScale[1] = f32Temp3[1];
				m_rScale[2] = f32Temp3[2];

				// scale rot
				ui32Error = pLoad->read( f32Temp4, sizeof( f32Temp4 ) );
				m_rScaleRot[0] = f32Temp4[0];
				m_rScaleRot[1] = f32Temp4[1];
				m_rScaleRot[2] = f32Temp4[2];
				m_rScaleRot[3] = f32Temp4[3];
			}
			break;

		case CHUNK_MESH_GEOMETRY:
			if( pLoad->get_chunk_version() == MESH_VERSION ) {
				uint32	i, ui32Count;
				// vertices
				ui32Error = pLoad->read( &ui32Count, sizeof( ui32Count ) );
				for( i = 0; i < ui32Count; i++ ) {
					ui32Error = pLoad->read( f32Temp3, sizeof( f32Temp3 ) );
					add_vert( Vector3C( f32Temp3 ) );
				}

				// normals
				ui32Error = pLoad->read( &ui32Count, sizeof( ui32Count ) );
				for( i = 0; i < ui32Count; i++ ) {
					ui32Error = pLoad->read( f32Temp3, sizeof( f32Temp3 ) );
					add_norm( Vector3C( f32Temp3 ) );
				}
			}
			break;

		case CHUNK_MESH_BONEWEIGHTLIST:

			if( pLoad->get_chunk_version() == MESH_VERSION ) {

				// old stuff!!!

				if( !pSkin )
					pSkin = new OldSkinModifierC;

				OldSkinModifierC::BoneVertListS	rList;

				rList.m_pBone = 0;

				// bone id
				ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
				rList.m_ui32BoneId = ui32Temp;

				// indices
				uint32	ui32IndexCount;
				ui32Error = pLoad->read( &ui32IndexCount, sizeof( ui32IndexCount ) );

				for( i = 0; i < ui32IndexCount; i++ ) {
					ui32Error = pLoad->read( &ui32Temp, sizeof( &ui32Temp ) );
					rList.m_rIndices.push_back( ui32Temp );
				}

				// weights
				uint32	ui32WeightCount;
				ui32Error = pLoad->read( &ui32WeightCount, sizeof( ui32WeightCount ) );
				for( i = 0; i < ui32WeightCount; i++ ) {
					ui32Error = pLoad->read( &f32Temp, sizeof( &f32Temp ) );
					rList.m_rWeights.push_back( f32Temp );
				}

				pSkin->add_bonevertlist( rList );
			}

		case CHUNK_MESH_INDEXLIST:
			if( pLoad->get_chunk_version() == MESH_VERSION ) {
				IndexListC*	pList = new IndexListC;
				ui32Error = pList->load( pLoad );
				add_index_list( pList );
			}
			break;

		case CHUNK_MESH_TEXCOORDLIST:
			if( pLoad->get_chunk_version() == MESH_VERSION ) {
				TexCoordListC*	pList = new TexCoordListC;
				// error!
				ui32Error = pList->load( pLoad );
				add_texcoord_list( pList );
			}
			break;

		case CHUNK_MESH_CONT_POS:
			if( pLoad->get_chunk_version() == MESH_VERSION ) {
				ContVector3C*	pCont = new ContVector3C;
				ui32Error = pCont->load( pLoad );
				m_pPosCont = pCont;
			}
			break;

		case CHUNK_MESH_CONT_ROT:
			if( pLoad->get_chunk_version() == MESH_VERSION ) {
				ContQuatC*	pCont = new ContQuatC;
				ui32Error = pCont->load( pLoad );
				m_pRotCont = pCont;
			}
			break;

		case CHUNK_MESH_CONT_SCALE:
			if( pLoad->get_chunk_version() == MESH_VERSION ) {
				ContVector3C*	pCont = new ContVector3C;
				ui32Error = pCont->load( pLoad );
				m_pScaleCont = pCont;
			}
			break;

		case CHUNK_MESH_CONT_SCALEROT:
			if( pLoad->get_chunk_version() == MESH_VERSION ) {
				ContQuatC*	pCont = new ContQuatC;
				ui32Error = pCont->load( pLoad );
				m_pScaleRotCont = pCont;
			}
			break;

		case CHUNK_MESH_CONT_VIS:
			if( pLoad->get_chunk_version() == MESH_VERSION ) {
				ContBoolC*	pCont = new ContBoolC;
				ui32Error = pCont->load( pLoad );
				m_pVisCont = pCont;
			}
			break;

		case CHUNK_MESH_ADDITIVE:
			if( pLoad->get_chunk_version() == MESH_VERSION )
				m_bAdditive = true;
			break;

		case CHUNK_MESH_TOON:
			if( pLoad->get_chunk_version() == MESH_VERSION )
				m_bToon = true;
			break;

		case CHUNK_MESH_ALPHATEST:
			if( pLoad->get_chunk_version() == MESH_VERSION )
				m_bAlphaTest = true;
			break;

		case CHUNK_MESH_CASTSHADOWS:
			// NOTE! inverted flag!!
			if( pLoad->get_chunk_version() == MESH_VERSION )
				m_bCastShadows = false;
			break;

		case CHUNK_MESH_MODIFIER:
			if( pLoad->get_chunk_version() == MESH_VERSION ) {
				// type
				ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );

				switch( ui32Temp ) {

				case MODIFIER_OLD_SKIN:
					{
						OldSkinModifierC*	pMod = new OldSkinModifierC;
						ui32Error = pMod->load( pLoad );
						add_modifier( pMod );
					}
					break;

				case MODIFIER_SKIN:
					{
						SkinModifierC*	pMod = new SkinModifierC;
						ui32Error = pMod->load( pLoad );
						add_modifier( pMod );
					}
					break;

				case MODIFIER_MORPH:
					{
						MorphModifierC*	pMod = new MorphModifierC;
						ui32Error = pMod->load( pLoad );
						add_modifier( pMod );
					}
					break;

				}
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

		if( ui32Error != IO_OK && ui32Error != IO_END ) {
			return ui32Error;
		}
	}

	if( ui32Error != IO_OK && ui32Error != IO_END ) {
		return ui32Error;
	}

	calc_bbox();

	// add old skin
	if( pSkin )
		add_modifier( pSkin );

	return IO_OK;
}
