
// Insert your headers here
#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include "glext.h"
#include <stdio.h>
#include <string>
#include <stdarg.h>
#include "engine.h"

#include "DemopajaVersion.h"
#include "PajaTypes.h"
#include "ClassDescC.h"
#include "ParamI.h"
#include "EditableI.h"
#include "UndoC.h"
#include "ImportableImageI.h"
#include "BBox2C.h"
#include "Matrix2C.h"
#include "DeviceContextC.h"
#include "DeviceInterfaceI.h"
#include "OpenGLInterfaceC.h"
#include "ASELoaderC.h"
#include "CameraC.h"
#include "ScenegraphItemI.h"
#include "MeshRefC.h"

using namespace PajaTypes;
using namespace Composition;
using namespace Edit;
using namespace FileIO;
using namespace Import;
using namespace PluginClass;
using namespace PajaSystem;



PFNGLMULTITEXCOORD1FARBPROC		glMultiTexCoord1fARB		= NULL;
PFNGLMULTITEXCOORD2FARBPROC		glMultiTexCoord2fARB		= NULL;
PFNGLMULTITEXCOORD2FVARBPROC	glMultiTexCoord2fvARB		= NULL;
PFNGLMULTITEXCOORD3FARBPROC		glMultiTexCoord3fARB		= NULL;
PFNGLMULTITEXCOORD4FARBPROC		glMultiTexCoord4fARB		= NULL;
PFNGLACTIVETEXTUREARBPROC		glActiveTextureARB			= NULL;
PFNGLCLIENTACTIVETEXTUREARBPROC	glClientActiveTextureARB	= NULL;	

volatile bool					g_bMultiTexture = false;


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


// class descriptors
EngineDescC		g_rEngineDesc;
MASImportDescC	g_rMASImportDesc;


//
// The DLL
//

BOOL APIENTRY
DllMain( HANDLE hModule, DWORD ulReasonForCall, LPVOID lpReserved )
{
    switch( ulReasonForCall )
	{
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
    }
    return TRUE;
}



__declspec( dllexport )
int32
get_classdesc_count()
{
	return 2;
}

__declspec( dllexport )
ClassDescC*
get_classdesc( int32 i )
{
	if( i == 0 )
		return &g_rEngineDesc;
	else if( i == 1 )
		return &g_rMASImportDesc;
	return 0;
}

__declspec( dllexport )
int32
get_api_version()
{
	return DEMOPAJA_VERSION;
}

__declspec( dllexport )
char*
get_dll_name()
{
	return "3Dengine.dll - 3D Engine for Demopaja (c)2000 memon/moppi productions";
}


//
//
// OLD engine gizmo
//
//

EngineGizmoC::EngineGizmoC()
{
	init();
}

EngineGizmoC::EngineGizmoC( EffectI* pParent, uint32 ui32Id ) :
	GizmoI( pParent, ui32Id )
{
	init();
}

EngineGizmoC::EngineGizmoC( EditableI* pOriginal ) :
	GizmoI( pOriginal )
{
	//init();
}

void
EngineGizmoC::init()
{
	set_name( "Transform" );

	m_pParamPos = ParamVector2C::create_new( this, "Position", Vector2C(), 1, PARAM_STYLE_EDITBOX | PARAM_STYLE_ABS_POSITION, PARAM_ANIMATABLE );
	m_pParamPivot = ParamVector2C::create_new( this, "Pivot", Vector2C(), 2, PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_WORLD_SPACE	, PARAM_ANIMATABLE );
	m_pParamScale = ParamVector2C::create_new( this, "Scale", Vector2C( 1, 1 ), 3, PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 0.01f );
	m_pParamWidth = ParamIntC::create_new( this, "Width", 160, 4, PARAM_STYLE_EDITBOX, PARAM_NOT_ANIMATABLE, 1, 10000 );
	m_pParamHeight = ParamIntC::create_new( this, "Height", 120, 5, PARAM_STYLE_EDITBOX, PARAM_NOT_ANIMATABLE, 1, 10000 );
	m_pParamRenderMode = ParamIntC::create_new( this, "Render mode", 0, 6, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 2 );
	m_pParamRenderMode->add_label( 0, "Normal" );
	m_pParamRenderMode->add_label( 1, "Additive" );
	m_pParamRenderMode->add_label( 2, "Cartoon" );
	m_pParamFps = ParamIntC::create_new( this, "FPS", 30, 7, PARAM_STYLE_EDITBOX, PARAM_NOT_ANIMATABLE );
	m_pParamCamera = ParamIntC::create_new( this, "Camera", 0, 8, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 1 );
	m_pParamClearZ = ParamIntC::create_new( this, "Clear Z-Buffer", 1, 10, PARAM_STYLE_COMBOBOX, PARAM_NOT_ANIMATABLE, 0, 1 );
	m_pParamClearZ->add_label( 0, "Off" );
	m_pParamClearZ->add_label( 1, "On" );
	m_pParamFile = ParamFileC::create_new( this, "Scene", NULL_SUPERCLASS, CLASS_MAS_IMPORT, 9 );
}

EngineGizmoC::~EngineGizmoC()
{
	if( get_original() )
		return;

	m_pParamPos->release();
	m_pParamPivot->release();
	m_pParamScale->release();
	m_pParamWidth->release();
	m_pParamHeight->release();
	m_pParamFile->release();
	m_pParamRenderMode->release();
	m_pParamFps->release();
	m_pParamClearZ->release();
	m_pParamCamera->release();
}


EngineGizmoC*
EngineGizmoC::create_new( EffectI* pParent, uint32 ui32Id )
{
	return new EngineGizmoC( pParent, ui32Id );
}

DataBlockI*
EngineGizmoC::create()
{
	return new EngineGizmoC;
}

DataBlockI*
EngineGizmoC::create( EditableI* pOriginal )
{
	return new EngineGizmoC( pOriginal );
}

void
EngineGizmoC::copy( EditableI* pEditable )
{
	GizmoI::copy( pEditable );

	EngineGizmoC*	pGizmo = (EngineGizmoC*)pEditable;
	m_pParamPos->copy( pGizmo->m_pParamPos );
	m_pParamPivot->copy( pGizmo->m_pParamPivot );
	m_pParamScale->copy( pGizmo->m_pParamScale );
	m_pParamWidth->copy( pGizmo->m_pParamWidth );
	m_pParamHeight->copy( pGizmo->m_pParamHeight );
	m_pParamFile->copy( pGizmo->m_pParamFile );
	m_pParamRenderMode->copy( pGizmo->m_pParamRenderMode );
	m_pParamFps->copy( pGizmo->m_pParamFps );
	m_pParamClearZ->copy( pGizmo->m_pParamClearZ );
	m_pParamCamera->copy( pGizmo->m_pParamCamera );
}

void
EngineGizmoC::restore( EditableI* pEditable )
{
	GizmoI::restore( pEditable );

	EngineGizmoC*	pGizmo = (EngineGizmoC*)pEditable;
	m_pParamPos = pGizmo->m_pParamPos;
	m_pParamPivot = pGizmo->m_pParamPivot;
	m_pParamScale = pGizmo->m_pParamScale;
	m_pParamWidth = pGizmo->m_pParamWidth;
	m_pParamHeight = pGizmo->m_pParamHeight;
	m_pParamFile = pGizmo->m_pParamFile;
	m_pParamRenderMode = pGizmo->m_pParamRenderMode;
	m_pParamFps = pGizmo->m_pParamFps;
	m_pParamClearZ = pGizmo->m_pParamClearZ;
	m_pParamCamera = pGizmo->m_pParamCamera;
}


PajaTypes::int32
EngineGizmoC::get_parameter_count()
{
	return ENGINE_PARAM_COUNT;
}

ParamI*
EngineGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case ENGINE_PARAM_POS:
		return m_pParamPos; break;
	case ENGINE_PARAM_PIVOT:
		return m_pParamPivot; break;
	case ENGINE_PARAM_SCALE:
		return m_pParamScale; break;
	case ENGINE_PARAM_WIDTH:
		return m_pParamWidth; break;
	case ENGINE_PARAM_HEIGHT:
		return m_pParamHeight; break;
	case ENGINE_PARAM_RENDERMODE:
		return m_pParamRenderMode; break;
	case ENGINE_PARAM_CAMERA:
		return m_pParamCamera; break;
	case ENGINE_PARAM_FILE:
		return m_pParamFile; break;
	case ENGINE_PARAM_FPS:
		return m_pParamFps; break;
	case ENGINE_PARAM_CLEARZ:
		return m_pParamClearZ; break;
	}
	return 0;
}


void
EngineGizmoC::update_notify( uint32 ui32Id, int32 i32Time )
{
	if( ui32Id == 9 ) {
		// the file has changed
		// update labels
		m_pParamCamera->clear_labels();

		FileHandleC*	pHandle = 0;
		MASImportC*		pImp = 0;

		pHandle = m_pParamFile->get_file();
		if( pHandle )
			pImp = (MASImportC*)pHandle->get_importable();

		if( pImp ) {
			// get cameras to show in camera param
			if( pImp->get_camera_count() ) {
				for( uint32 i = 0; i < pImp->get_camera_count(); i++ ) {
					CameraC*	pCam = pImp->get_camera( i );
					if( !pCam )
						continue;
					m_pParamCamera->add_label( i, pCam->get_name() );
				}
				m_pParamCamera->set_min_max( 0, pImp->get_camera_count() );
			}
			else {
				m_pParamCamera->add_label( 0, "<none>" );
				m_pParamCamera->set_min_max( 0, 1 );
			}

			// get time
			int32	i32FPS, i32TicksPerFrame, i32FirstFrame, i32LastFrame;
			pImp->get_time_parameters( i32FPS, i32TicksPerFrame, i32FirstFrame, i32LastFrame );

			m_pParamFps->set_val( 0, i32FPS );

			m_pParamFile->set_time_offset( 0 );
			m_pParamFile->set_time_scale( 1 );
		}
	}
	else if( ui32Id == 7 ) {
		// FPS changed
		FileHandleC*	pHandle = 0;
		MASImportC*		pImp = 0;

		pHandle = m_pParamFile->get_file();
		if( pHandle )
			pImp = (MASImportC*)pHandle->get_importable();

		if( pImp ) {
			// get time
			int32	i32FPS, i32TicksPerFrame, i32FirstFrame, i32LastFrame;
			pImp->get_time_parameters( i32FPS, i32TicksPerFrame, i32FirstFrame, i32LastFrame );

			int32	i32AnimFPS = get_fps( i32Time );

			if( i32AnimFPS < 1 )
				m_pParamFile->set_time_scale( 1 );
			else
				m_pParamFile->set_time_scale( (float32)i32FPS / (float32)i32AnimFPS );
		}
	}

	GizmoI::update_notify( ui32Id, i32Time );

//	OutputDebugString( "update_notify\n" );
}

MASImportC*
EngineGizmoC::get_file()
{
	FileHandleC*	pHandle = 0;
	pHandle = m_pParamFile->get_file();
	if( pHandle )
		return (MASImportC*)pHandle->get_importable();
	return 0;
}

Vector2C
EngineGizmoC::get_pos( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamPos->get_val( i32Time, rVal );
	return rVal;
}

Vector2C
EngineGizmoC::get_pivot( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamPivot->get_val( i32Time, rVal );
	return rVal;
}

Vector2C
EngineGizmoC::get_scale( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamScale->get_val( i32Time, rVal );
	return rVal;
}

int32
EngineGizmoC::get_render_mode( PajaTypes::int32 i32Time )
{
	int32	i32Val;
	m_pParamRenderMode->get_val( i32Time, i32Val );
	return i32Val;
}

int32
EngineGizmoC::get_width( PajaTypes::int32 i32Time )
{
	int32	i32Val;
	m_pParamWidth->get_val( i32Time, i32Val );
	return i32Val;
}

int32
EngineGizmoC::get_height( PajaTypes::int32 i32Time )
{
	int32	i32Val;
	m_pParamHeight->get_val( i32Time, i32Val );
	return i32Val;
}


CameraC*
EngineGizmoC::get_camera( int32 i32Time )
{
	FileHandleC*	pHandle = 0;
	MASImportC*		pImp = 0;

	pHandle = m_pParamFile->get_file();

	if( pHandle )
		pImp = (MASImportC*)pHandle->get_importable();

	if( pImp ) {
		int32	i32Val;
		m_pParamCamera->get_val( i32Time, i32Val );
		return pImp->get_camera( i32Val );
	}
	
	return 0;
}

int32
EngineGizmoC::get_fps( int32 i32Time )
{
	int32	i32Val;
	m_pParamFps->get_val( i32Time, i32Val );
	return i32Val;
}

int32
EngineGizmoC::get_clear_z( int32 i32Time )
{
	int32	i32Val;
	m_pParamClearZ->get_val( i32Time, i32Val );
	return i32Val;
}


enum EngineGizmoChunksE {
	CHUNK_ENGINEGIZMO_BASE				= 0x1000,
	CHUNK_ENGINEGIZMO_PARAM_POS			= 0x2000,
	CHUNK_ENGINEGIZMO_PARAM_PIVOT		= 0x3000,
	CHUNK_ENGINEGIZMO_PARAM_SCALE		= 0x4000,
	CHUNK_ENGINEGIZMO_PARAM_WIDTH		= 0x5000,
	CHUNK_ENGINEGIZMO_PARAM_HEIGHT		= 0x6000,
	CHUNK_ENGINEGIZMO_PARAM_RENDERMODE	= 0x7000,
	CHUNK_ENGINEGIZMO_PARAM_FPS			= 0x8000,
	CHUNK_ENGINEGIZMO_PARAM_CLEARZ		= 0x9000,
	CHUNK_ENGINEGIZMO_PARAM_FILE		= 0xa000,
	CHUNK_ENGINEGIZMO_PARAM_CAMERA		= 0xb000,
};

const uint32	ENGINEGIZMO_VERSION = 1;



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

	// GizmoI stuff
	pSave->begin_chunk( CHUNK_ENGINEGIZMO_BASE, ENGINEGIZMO_VERSION );
		ui32Error = GizmoI::save( pSave );
	pSave->end_chunk();

	// position
	pSave->begin_chunk( CHUNK_ENGINEGIZMO_PARAM_POS, ENGINEGIZMO_VERSION );
		ui32Error = m_pParamPos->save( pSave );
	pSave->end_chunk();

	// position
	pSave->begin_chunk( CHUNK_ENGINEGIZMO_PARAM_PIVOT, ENGINEGIZMO_VERSION );
		ui32Error = m_pParamPivot->save( pSave );
	pSave->end_chunk();

	// scale
	pSave->begin_chunk( CHUNK_ENGINEGIZMO_PARAM_SCALE, ENGINEGIZMO_VERSION );
		ui32Error = m_pParamScale->save( pSave );
	pSave->end_chunk();

	// width
	pSave->begin_chunk( CHUNK_ENGINEGIZMO_PARAM_WIDTH, ENGINEGIZMO_VERSION );
		ui32Error = m_pParamWidth->save( pSave );
	pSave->end_chunk();

	// height
	pSave->begin_chunk( CHUNK_ENGINEGIZMO_PARAM_HEIGHT, ENGINEGIZMO_VERSION );
		ui32Error = m_pParamHeight->save( pSave );
	pSave->end_chunk();

	// render
	pSave->begin_chunk( CHUNK_ENGINEGIZMO_PARAM_RENDERMODE, ENGINEGIZMO_VERSION );
		ui32Error = m_pParamRenderMode->save( pSave );
	pSave->end_chunk();

	// FPS
	pSave->begin_chunk( CHUNK_ENGINEGIZMO_PARAM_FPS, ENGINEGIZMO_VERSION );
		ui32Error = m_pParamFps->save( pSave );
	pSave->end_chunk();

	// clearz
	pSave->begin_chunk( CHUNK_ENGINEGIZMO_PARAM_CLEARZ, ENGINEGIZMO_VERSION );
		ui32Error = m_pParamClearZ->save( pSave );
	pSave->end_chunk();

	// file
	pSave->begin_chunk( CHUNK_ENGINEGIZMO_PARAM_FILE, ENGINEGIZMO_VERSION );
		ui32Error = m_pParamFile->save( pSave );
	pSave->end_chunk();

	// camera
	pSave->begin_chunk( CHUNK_ENGINEGIZMO_PARAM_CAMERA, ENGINEGIZMO_VERSION );
		ui32Error = m_pParamCamera->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_ENGINEGIZMO_BASE:
			{
//				OutputDebugString( "        g_base\n" );
				if( pLoad->get_chunk_version() == ENGINEGIZMO_VERSION )
					ui32Error = GizmoI::load( pLoad );
			}
			break;

		case CHUNK_ENGINEGIZMO_PARAM_POS:
			{
//				OutputDebugString( "        g_pos\n" );
				if( pLoad->get_chunk_version() == ENGINEGIZMO_VERSION )
					ui32Error = m_pParamPos->load( pLoad );
			}
			break;

		case CHUNK_ENGINEGIZMO_PARAM_PIVOT:
			{
//				OutputDebugString( "        g_pivot\n" );
				if( pLoad->get_chunk_version() == ENGINEGIZMO_VERSION )
					ui32Error = m_pParamPivot->load( pLoad );
			}
			break;

		case CHUNK_ENGINEGIZMO_PARAM_SCALE:
			{
//				OutputDebugString( "        g_scale\n" );
				if( pLoad->get_chunk_version() == ENGINEGIZMO_VERSION )
					ui32Error = m_pParamScale->load( pLoad );
			}
			break;

		case CHUNK_ENGINEGIZMO_PARAM_WIDTH:
			{
//				OutputDebugString( "        g_width\n" );
				if( pLoad->get_chunk_version() == ENGINEGIZMO_VERSION )
					ui32Error = m_pParamWidth->load( pLoad );
			}
			break;

		case CHUNK_ENGINEGIZMO_PARAM_HEIGHT:
			{
//				OutputDebugString( "        g_scale\n" );
				if( pLoad->get_chunk_version() == ENGINEGIZMO_VERSION )
					ui32Error = m_pParamHeight->load( pLoad );
			}
			break;

		case CHUNK_ENGINEGIZMO_PARAM_RENDERMODE:
			{
//				OutputDebugString( "        g_render\n" );
				if( pLoad->get_chunk_version() == ENGINEGIZMO_VERSION )
					ui32Error = m_pParamRenderMode->load( pLoad );
			}
			break;

		case CHUNK_ENGINEGIZMO_PARAM_FPS:
			{
//				OutputDebugString( "        g_fps\n" );
				if( pLoad->get_chunk_version() == ENGINEGIZMO_VERSION )
					ui32Error = m_pParamFps->load( pLoad );
			}
			break;

		case CHUNK_ENGINEGIZMO_PARAM_CLEARZ:
			{
//				OutputDebugString( "        g_fps\n" );
				if( pLoad->get_chunk_version() == ENGINEGIZMO_VERSION )
					ui32Error = m_pParamClearZ->load( pLoad );
			}
			break;

		case CHUNK_ENGINEGIZMO_PARAM_FILE:
			{
//				OutputDebugString( "        g_file\n" );
				if( pLoad->get_chunk_version() == ENGINEGIZMO_VERSION )
					ui32Error = m_pParamFile->load( pLoad );
			}
			break;

		case CHUNK_ENGINEGIZMO_PARAM_CAMERA:
			{
//				OutputDebugString( "        g_file\n" );
				if( pLoad->get_chunk_version() == ENGINEGIZMO_VERSION )
					ui32Error = m_pParamCamera->load( pLoad );
			}
			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;
}


//
// transform gizmo
//

TransformGizmoC::TransformGizmoC()
{
	init();
}

TransformGizmoC::TransformGizmoC( EffectI* pParent, uint32 ui32Id ) :
	GizmoI( pParent, ui32Id )
{
	init();
}

TransformGizmoC::TransformGizmoC( EditableI* pOriginal ) :
	GizmoI( pOriginal )
{
	//init();
}

void
TransformGizmoC::init()
{
	set_name( "Transform" );

	m_pParamPos = ParamVector2C::create_new( this, "Position", Vector2C(), 1, PARAM_STYLE_EDITBOX | PARAM_STYLE_ABS_POSITION, PARAM_ANIMATABLE );
	m_pParamPivot = ParamVector2C::create_new( this, "Pivot", Vector2C(), 2, PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_WORLD_SPACE	, PARAM_ANIMATABLE );
	m_pParamScale = ParamVector2C::create_new( this, "Scale", Vector2C( 1, 1 ), 3, PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 0.01f );
}

TransformGizmoC::~TransformGizmoC()
{
	if( get_original() )
		return;

	m_pParamPos->release();
	m_pParamPivot->release();
	m_pParamScale->release();
}


TransformGizmoC*
TransformGizmoC::create_new( EffectI* pParent, uint32 ui32Id )
{
	return new TransformGizmoC( pParent, ui32Id );
}

DataBlockI*
TransformGizmoC::create()
{
	return new TransformGizmoC;
}

DataBlockI*
TransformGizmoC::create( EditableI* pOriginal )
{
	return new TransformGizmoC( pOriginal );
}

void
TransformGizmoC::copy( EditableI* pEditable )
{
	GizmoI::copy( pEditable );

	TransformGizmoC*	pGizmo = (TransformGizmoC*)pEditable;
	m_pParamPos->copy( pGizmo->m_pParamPos );
	m_pParamPivot->copy( pGizmo->m_pParamPivot );
	m_pParamScale->copy( pGizmo->m_pParamScale );
}

void
TransformGizmoC::restore( EditableI* pEditable )
{
	GizmoI::restore( pEditable );

	TransformGizmoC*	pGizmo = (TransformGizmoC*)pEditable;
	m_pParamPos = pGizmo->m_pParamPos;
	m_pParamPivot = pGizmo->m_pParamPivot;
	m_pParamScale = pGizmo->m_pParamScale;
}


PajaTypes::int32
TransformGizmoC::get_parameter_count()
{
	return TRANSFORM_PARAM_COUNT;
}

ParamI*
TransformGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case TRANSFORM_PARAM_POS:
		return m_pParamPos; break;
	case TRANSFORM_PARAM_PIVOT:
		return m_pParamPivot; break;
	case TRANSFORM_PARAM_SCALE:
		return m_pParamScale; break;
	}
	return 0;
}



Vector2C
TransformGizmoC::get_pos( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamPos->get_val( i32Time, rVal );
	return rVal;
}

Vector2C
TransformGizmoC::get_pivot( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamPivot->get_val( i32Time, rVal );
	return rVal;
}

Vector2C
TransformGizmoC::get_scale( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamScale->get_val( i32Time, rVal );
	return rVal;
}


enum TransformGizmoChunksE {
	CHUNK_TRANSFORMGIZMO_BASE				= 0x1000,
	CHUNK_TRANSFORMGIZMO_PARAM_POS			= 0x2000,
	CHUNK_TRANSFORMGIZMO_PARAM_PIVOT		= 0x3000,
	CHUNK_TRANSFORMGIZMO_PARAM_SCALE		= 0x4000,
};

const uint32	TRANSFORMGIZMO_VERSION = 1;



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

	// GizmoI stuff
	pSave->begin_chunk( CHUNK_TRANSFORMGIZMO_BASE, TRANSFORMGIZMO_VERSION );
		ui32Error = GizmoI::save( pSave );
	pSave->end_chunk();

	// position
	pSave->begin_chunk( CHUNK_TRANSFORMGIZMO_PARAM_POS, TRANSFORMGIZMO_VERSION );
		ui32Error = m_pParamPos->save( pSave );
	pSave->end_chunk();

	// position
	pSave->begin_chunk( CHUNK_TRANSFORMGIZMO_PARAM_PIVOT, TRANSFORMGIZMO_VERSION );
		ui32Error = m_pParamPivot->save( pSave );
	pSave->end_chunk();

	// scale
	pSave->begin_chunk( CHUNK_TRANSFORMGIZMO_PARAM_SCALE, TRANSFORMGIZMO_VERSION );
		ui32Error = m_pParamScale->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_TRANSFORMGIZMO_BASE:
			{
				if( pLoad->get_chunk_version() == TRANSFORMGIZMO_VERSION )
					ui32Error = GizmoI::load( pLoad );
			}
			break;

		case CHUNK_TRANSFORMGIZMO_PARAM_POS:
			{
				if( pLoad->get_chunk_version() == TRANSFORMGIZMO_VERSION )
					ui32Error = m_pParamPos->load( pLoad );
			}
			break;

		case CHUNK_TRANSFORMGIZMO_PARAM_PIVOT:
			{
				if( pLoad->get_chunk_version() == TRANSFORMGIZMO_VERSION )
					ui32Error = m_pParamPivot->load( pLoad );
			}
			break;

		case CHUNK_TRANSFORMGIZMO_PARAM_SCALE:
			{
				if( pLoad->get_chunk_version() == TRANSFORMGIZMO_VERSION )
					ui32Error = m_pParamScale->load( pLoad );
			}
			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;
}



//
// attribute gizmo
//

AttributeGizmoC::AttributeGizmoC()
{
	init();
}

AttributeGizmoC::AttributeGizmoC( EffectI* pParent, uint32 ui32Id ) :
	GizmoI( pParent, ui32Id )
{
	init();
}

AttributeGizmoC::AttributeGizmoC( EditableI* pOriginal ) :
	GizmoI( pOriginal )
{
	//init();
}

void
AttributeGizmoC::init()
{
	set_name( "Attributes" );

	m_pParamWidth = ParamIntC::create_new( this, "Width", 160, ATTRIBUTE_PARAM_WIDTH, PARAM_STYLE_EDITBOX, PARAM_NOT_ANIMATABLE, 1, 10000 );
	m_pParamHeight = ParamIntC::create_new( this, "Height", 120, ATTRIBUTE_PARAM_HEIGHT, PARAM_STYLE_EDITBOX, PARAM_NOT_ANIMATABLE, 1, 10000 );
	m_pParamRenderMode = ParamIntC::create_new( this, "Render mode", 0, ATTRIBUTE_PARAM_RENDERMODE, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 2 );
	m_pParamRenderMode->add_label( 0, "Normal" );
	m_pParamRenderMode->add_label( 1, "Additive" );
	m_pParamRenderMode->add_label( 2, "Cartoon" );
	m_pParamCamera = ParamIntC::create_new( this, "Camera", 0, ATTRIBUTE_PARAM_CAMERA, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 1 );
	m_pParamFps = ParamIntC::create_new( this, "FPS", 30, ATTRIBUTE_PARAM_FPS, PARAM_STYLE_EDITBOX, PARAM_NOT_ANIMATABLE );
	m_pParamClearZ = ParamIntC::create_new( this, "Clear Z-Buffer", 1, ATTRIBUTE_PARAM_CLEARZ, PARAM_STYLE_COMBOBOX, PARAM_NOT_ANIMATABLE, 0, 1 );
	m_pParamClearZ->add_label( 0, "Off" );
	m_pParamClearZ->add_label( 1, "On" );
	m_pParamFile = ParamFileC::create_new( this, "Scene", NULL_SUPERCLASS, CLASS_MAS_IMPORT, ATTRIBUTE_PARAM_FILE );
}

AttributeGizmoC::~AttributeGizmoC()
{
	if( get_original() )
		return;

	m_pParamWidth->release();
	m_pParamHeight->release();
	m_pParamFile->release();
	m_pParamRenderMode->release();
	m_pParamFps->release();
	m_pParamClearZ->release();
	m_pParamCamera->release();
}


AttributeGizmoC*
AttributeGizmoC::create_new( EffectI* pParent, uint32 ui32Id )
{
	return new AttributeGizmoC( pParent, ui32Id );
}

DataBlockI*
AttributeGizmoC::create()
{
	return new AttributeGizmoC;
}

DataBlockI*
AttributeGizmoC::create( EditableI* pOriginal )
{
	return new AttributeGizmoC( pOriginal );
}

void
AttributeGizmoC::copy( EditableI* pEditable )
{
	GizmoI::copy( pEditable );

	AttributeGizmoC*	pGizmo = (AttributeGizmoC*)pEditable;
	m_pParamWidth->copy( pGizmo->m_pParamWidth );
	m_pParamHeight->copy( pGizmo->m_pParamHeight );
	m_pParamFile->copy( pGizmo->m_pParamFile );
	m_pParamRenderMode->copy( pGizmo->m_pParamRenderMode );
	m_pParamFps->copy( pGizmo->m_pParamFps );
	m_pParamClearZ->copy( pGizmo->m_pParamClearZ );
	m_pParamCamera->copy( pGizmo->m_pParamCamera );
}

void
AttributeGizmoC::restore( EditableI* pEditable )
{
	GizmoI::restore( pEditable );

	AttributeGizmoC*	pGizmo = (AttributeGizmoC*)pEditable;
	m_pParamWidth = pGizmo->m_pParamWidth;
	m_pParamHeight = pGizmo->m_pParamHeight;
	m_pParamFile = pGizmo->m_pParamFile;
	m_pParamRenderMode = pGizmo->m_pParamRenderMode;
	m_pParamFps = pGizmo->m_pParamFps;
	m_pParamClearZ = pGizmo->m_pParamClearZ;
	m_pParamCamera = pGizmo->m_pParamCamera;
}


PajaTypes::int32
AttributeGizmoC::get_parameter_count()
{
	return ATTRIBUTE_PARAM_COUNT;
}

ParamI*
AttributeGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case ATTRIBUTE_PARAM_WIDTH:
		return m_pParamWidth; break;
	case ATTRIBUTE_PARAM_HEIGHT:
		return m_pParamHeight; break;
	case ATTRIBUTE_PARAM_RENDERMODE:
		return m_pParamRenderMode; break;
	case ATTRIBUTE_PARAM_CAMERA:
		return m_pParamCamera; break;
	case ATTRIBUTE_PARAM_FILE:
		return m_pParamFile; break;
	case ATTRIBUTE_PARAM_FPS:
		return m_pParamFps; break;
	case ATTRIBUTE_PARAM_CLEARZ:
		return m_pParamClearZ; break;
	}
	return 0;
}


void
AttributeGizmoC::update_notify( uint32 ui32Id, int32 i32Time )
{
	if( ui32Id == ATTRIBUTE_PARAM_FILE ) {
		// the file has changed
		// update labels

		// get undo from changed parameter
		UndoC*	pUndo = m_pParamFile->get_undo();
		UndoC*	pOldUndo;

		FileHandleC*	pHandle = 0;
		MASImportC*		pImp = 0;

		pHandle = m_pParamFile->get_file();
		if( pHandle )
			pImp = (MASImportC*)pHandle->get_importable();

		if( pImp ) {

			// get cameras to show in camera param
			// apply undo to parameter we change
			pOldUndo = m_pParamCamera->begin_editing( pUndo );
			m_pParamCamera->clear_labels();

			if( pImp->get_camera_count() ) {
				for( uint32 i = 0; i < pImp->get_camera_count(); i++ ) {
					CameraC*	pCam = pImp->get_camera( i );
					if( !pCam )
						continue;
					m_pParamCamera->add_label( i, pCam->get_name() );
				}
				m_pParamCamera->set_min_max( 0, pImp->get_camera_count() );
			}
			else {
				m_pParamCamera->add_label( 0, "<none>" );
				m_pParamCamera->set_min_max( 0, 1 );
			}

/*			for( uint32 i = 0; i < pImp->get_camera_count(); i++ ) {
				CameraC*	pCam = pImp->get_camera( i );
				if( !pCam )
					continue;
				m_pParamCamera->add_label( i, pCam->get_name() );
			}
			m_pParamCamera->set_min_max( 0, pImp->get_camera_count() );*/

			m_pParamCamera->end_editing( pOldUndo );

			// get time
			int32	i32FPS, i32TicksPerFrame, i32FirstFrame, i32LastFrame;
			pImp->get_time_parameters( i32FPS, i32TicksPerFrame, i32FirstFrame, i32LastFrame );

			pOldUndo = m_pParamFps->begin_editing( pUndo );
			m_pParamFps->set_val( 0, i32FPS );
			m_pParamFps->end_editing( pOldUndo );

			m_pParamFile->set_time_offset( 0 );
			m_pParamFile->set_time_scale( 1 );
		}
	}
	else if( ui32Id == ATTRIBUTE_PARAM_FPS ) {
		// FPS changed
		FileHandleC*	pHandle = 0;
		MASImportC*		pImp = 0;

		// get undo from changed parameter
		UndoC*	pUndo = m_pParamFps->get_undo();
		UndoC*	pOldUndo;

		pHandle = m_pParamFile->get_file();
		if( pHandle )
			pImp = (MASImportC*)pHandle->get_importable();

		if( pImp ) {

			// apply undo to parameter we change
			pOldUndo = m_pParamFile->begin_editing( pUndo );

			// get time
			int32	i32FPS, i32TicksPerFrame, i32FirstFrame, i32LastFrame;
			pImp->get_time_parameters( i32FPS, i32TicksPerFrame, i32FirstFrame, i32LastFrame );

			int32	i32AnimFPS = get_fps( i32Time );

			if( i32AnimFPS < 1 )
				m_pParamFile->set_time_scale( 1 );
			else
				m_pParamFile->set_time_scale( (float32)i32FPS / (float32)i32AnimFPS );

			m_pParamFile->end_editing( pOldUndo );
		}
	}

	GizmoI::update_notify( ui32Id, i32Time );

}

MASImportC*
AttributeGizmoC::get_file()
{
	FileHandleC*	pHandle = 0;
	pHandle = m_pParamFile->get_file();
	if( pHandle )
		return (MASImportC*)pHandle->get_importable();
	return 0;
}

int32
AttributeGizmoC::get_render_mode( PajaTypes::int32 i32Time )
{
	int32	i32Val;
	m_pParamRenderMode->get_val( i32Time, i32Val );
	return i32Val;
}

int32
AttributeGizmoC::get_width( PajaTypes::int32 i32Time )
{
	int32	i32Val;
	m_pParamWidth->get_val( i32Time, i32Val );
	return i32Val;
}

int32
AttributeGizmoC::get_height( PajaTypes::int32 i32Time )
{
	int32	i32Val;
	m_pParamHeight->get_val( i32Time, i32Val );
	return i32Val;
}


CameraC*
AttributeGizmoC::get_camera( int32 i32Time )
{
	FileHandleC*	pHandle = 0;
	MASImportC*		pImp = 0;

	pHandle = m_pParamFile->get_file();

	if( pHandle )
		pImp = (MASImportC*)pHandle->get_importable();

	if( pImp ) {
		int32	i32Val;
		m_pParamCamera->get_val( i32Time, i32Val );
		return pImp->get_camera( i32Val );
	}
	
	return 0;
}

int32
AttributeGizmoC::get_fps( int32 i32Time )
{
	int32	i32Val;
	m_pParamFps->get_val( i32Time, i32Val );
	return i32Val;
}

int32
AttributeGizmoC::get_clear_z( int32 i32Time )
{
	int32	i32Val;
	m_pParamClearZ->get_val( i32Time, i32Val );
	return i32Val;
}


enum AttributeGizmoChunksE {
	CHUNK_ATTRIBUTEGIZMO_BASE				= 0x1000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_WIDTH		= 0x2000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_HEIGHT		= 0x3000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_RENDERMODE	= 0x4000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_FPS			= 0x5000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_CLEARZ		= 0x6000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_FILE			= 0x7000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_CAMERA		= 0x8000,
};

const uint32	ATTRIBUTEGIZMO_VERSION = 1;



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

	// GizmoI stuff
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_BASE, ATTRIBUTEGIZMO_VERSION );
		ui32Error = GizmoI::save( pSave );
	pSave->end_chunk();

	// width
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_PARAM_WIDTH, ATTRIBUTEGIZMO_VERSION );
		ui32Error = m_pParamWidth->save( pSave );
	pSave->end_chunk();

	// height
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_PARAM_HEIGHT, ATTRIBUTEGIZMO_VERSION );
		ui32Error = m_pParamHeight->save( pSave );
	pSave->end_chunk();

	// render
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_PARAM_RENDERMODE, ATTRIBUTEGIZMO_VERSION );
		ui32Error = m_pParamRenderMode->save( pSave );
	pSave->end_chunk();

	// FPS
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_PARAM_FPS, ATTRIBUTEGIZMO_VERSION );
		ui32Error = m_pParamFps->save( pSave );
	pSave->end_chunk();

	// clearz
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_PARAM_CLEARZ, ATTRIBUTEGIZMO_VERSION );
		ui32Error = m_pParamClearZ->save( pSave );
	pSave->end_chunk();

	// file
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_PARAM_FILE, ATTRIBUTEGIZMO_VERSION );
		ui32Error = m_pParamFile->save( pSave );
	pSave->end_chunk();

	// camera
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_PARAM_CAMERA, ATTRIBUTEGIZMO_VERSION );
		ui32Error = m_pParamCamera->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_ATTRIBUTEGIZMO_BASE:
			{
				if( pLoad->get_chunk_version() == ATTRIBUTEGIZMO_VERSION )
					ui32Error = GizmoI::load( pLoad );
			}
			break;

		case CHUNK_ATTRIBUTEGIZMO_PARAM_WIDTH:
			{
				if( pLoad->get_chunk_version() == ATTRIBUTEGIZMO_VERSION )
					ui32Error = m_pParamWidth->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBUTEGIZMO_PARAM_HEIGHT:
			{
				if( pLoad->get_chunk_version() == ATTRIBUTEGIZMO_VERSION )
					ui32Error = m_pParamHeight->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBUTEGIZMO_PARAM_RENDERMODE:
			{
				if( pLoad->get_chunk_version() == ATTRIBUTEGIZMO_VERSION )
					ui32Error = m_pParamRenderMode->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBUTEGIZMO_PARAM_FPS:
			{
				if( pLoad->get_chunk_version() == ATTRIBUTEGIZMO_VERSION )
					ui32Error = m_pParamFps->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBUTEGIZMO_PARAM_CLEARZ:
			{
				if( pLoad->get_chunk_version() == ATTRIBUTEGIZMO_VERSION )
					ui32Error = m_pParamClearZ->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBUTEGIZMO_PARAM_FILE:
			{
				if( pLoad->get_chunk_version() == ATTRIBUTEGIZMO_VERSION )
					ui32Error = m_pParamFile->load( pLoad );
			}
			break;

		case CHUNK_ATTRIBUTEGIZMO_PARAM_CAMERA:
			{
				if( pLoad->get_chunk_version() == ATTRIBUTEGIZMO_VERSION )
					ui32Error = m_pParamCamera->load( pLoad );
			}
			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;
}


//
// The effect
//


EngineEffectC::EngineEffectC() :
	m_i32Frame( 0 ),
	m_i32Time( 0 ),
	m_pEngineGizmo( 0 )
{
	m_pTransGizmo = TransformGizmoC::create_new( this, 0 );
	m_pAttribGizmo = AttributeGizmoC::create_new( this, 0 );
}

EngineEffectC::EngineEffectC( EditableI* pOriginal ) :
	EffectI( pOriginal ),
	m_i32Frame( 0 ),
	m_i32Time( 0 ),
	m_pTransGizmo( 0 ),
	m_pAttribGizmo( 0 ),
	m_pEngineGizmo( 0 )
{
	// empty
}

EngineEffectC::~EngineEffectC()
{
	if( get_original() )
		return;

	m_pTransGizmo->release();
	m_pAttribGizmo->release();
}

EngineEffectC*
EngineEffectC::create_new()
{
	return new EngineEffectC;
}

DataBlockI*
EngineEffectC::create()
{
	return new EngineEffectC;
}

DataBlockI*
EngineEffectC::create( EditableI* pOriginal )
{
	return new EngineEffectC( pOriginal );
}

void
EngineEffectC::copy( EditableI* pEditable )
{
	EffectI::copy( pEditable );

	EngineEffectC*	pEffect = (EngineEffectC*)pEditable;
	m_pTransGizmo->copy( pEffect->m_pTransGizmo );
	m_pAttribGizmo->copy( pEffect->m_pAttribGizmo );
}

void
EngineEffectC::restore( EditableI* pEditable )
{
	EffectI::restore( pEditable );

	EngineEffectC*	pEffect = (EngineEffectC*)pEditable;
	m_pTransGizmo = pEffect->m_pTransGizmo;
	m_pAttribGizmo = pEffect->m_pAttribGizmo;
}

const char*
EngineEffectC::get_class_name()
{
	return "3D Engine";
}

int32
EngineEffectC::get_gizmo_count()
{
	return 2;
}

GizmoI*
EngineEffectC::get_gizmo( PajaTypes::int32 i32Index )
{
	if( i32Index == 0 )
		return m_pTransGizmo;
	else if( i32Index == 1 )
		return m_pAttribGizmo;
	return 0;
}

ClassIdC
EngineEffectC::get_class_id()
{
	return CLASS_3DENGINE_EFFECT;
}

void
EngineEffectC::set_default_file( FileHandleC* pHandle )
{
	ParamFileC*	pParam = (ParamFileC*)m_pAttribGizmo->get_parameter( ATTRIBUTE_PARAM_FILE );

	UndoC*	pOldUndo;
	pOldUndo = pParam->begin_editing( get_undo() );
	pParam->set_file( pHandle );
	pParam->end_editing( pOldUndo );
}

ParamI*
EngineEffectC::get_default_param( PajaTypes::int32 i32Param )
{
	if( i32Param == DEFAULT_PARAM_POSITION )
		return m_pTransGizmo->get_parameter( TRANSFORM_PARAM_POS );
	else if( i32Param == DEFAULT_PARAM_SCALE )
		return m_pTransGizmo->get_parameter( TRANSFORM_PARAM_SCALE );
	else if( i32Param == DEFAULT_PARAM_PIVOT )
		return m_pTransGizmo->get_parameter( TRANSFORM_PARAM_PIVOT );
	return 0;
}

BBox2C
EngineEffectC::get_bbox()
{
	return m_rBBox;
}

void
EngineEffectC::initialize( DeviceContextC* pContext, TimeContextC* pTimeContext )
{
	// init opengl
	if( !g_bMultiTexture ) {

		char*	szExtensions;	
		szExtensions = (char*)glGetString( GL_EXTENSIONS );

		if( szExtensions && strstr( szExtensions, "GL_ARB_multitexture" ) != 0 &&
			strstr( szExtensions, "GL_EXT_texture_env_combine" ) != 0 ) {	

			glMultiTexCoord1fARB	 = (PFNGLMULTITEXCOORD1FARBPROC)wglGetProcAddress( "glMultiTexCoord1fARB" );
			glMultiTexCoord2fARB	 = (PFNGLMULTITEXCOORD2FARBPROC)wglGetProcAddress( "glMultiTexCoord2fARB" );
			glMultiTexCoord2fvARB	 = (PFNGLMULTITEXCOORD2FVARBPROC)wglGetProcAddress( "glMultiTexCoord2fvARB" );
			glMultiTexCoord3fARB	 = (PFNGLMULTITEXCOORD3FARBPROC)wglGetProcAddress( "glMultiTexCoord3fARB" );
			glMultiTexCoord4fARB	 = (PFNGLMULTITEXCOORD4FARBPROC)wglGetProcAddress( "glMultiTexCoord4fARB" );
			glActiveTextureARB		 = (PFNGLACTIVETEXTUREARBPROC)wglGetProcAddress( "glActiveTextureARB" );
			glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC)wglGetProcAddress( "glClientActiveTextureARB" );
			g_bMultiTexture = true;
		}
	}


	if( m_pEngineGizmo ) {

		//
		// we have the old gizmo loaded.
		// convert it to the new structure and delete it
		// the parameters belongs to the testgizmo so we must change the parents too!

		ParamI*	pParam;

		// copy transform
		pParam = m_pTransGizmo->get_parameter( TRANSFORM_PARAM_POS );
		pParam->copy( m_pEngineGizmo->get_parameter( ENGINE_PARAM_POS ) );
		pParam->set_parent( m_pTransGizmo );

		pParam = m_pTransGizmo->get_parameter( TRANSFORM_PARAM_PIVOT );
		pParam->copy( m_pEngineGizmo->get_parameter( ENGINE_PARAM_PIVOT ) );
		pParam->set_parent( m_pTransGizmo );

		pParam = m_pTransGizmo->get_parameter( TRANSFORM_PARAM_SCALE );
		pParam->copy( m_pEngineGizmo->get_parameter( ENGINE_PARAM_SCALE ) );
		pParam->set_parent( m_pTransGizmo );

		// copy attributes
		pParam = m_pAttribGizmo->get_parameter( ATTRIBUTE_PARAM_WIDTH );
		pParam->copy( m_pEngineGizmo->get_parameter( ENGINE_PARAM_WIDTH ) );
		pParam->set_parent( m_pAttribGizmo );

		pParam = m_pAttribGizmo->get_parameter( ATTRIBUTE_PARAM_HEIGHT );
		pParam->copy( m_pEngineGizmo->get_parameter( ENGINE_PARAM_HEIGHT ) );
		pParam->set_parent( m_pAttribGizmo );

		pParam = m_pAttribGizmo->get_parameter( ATTRIBUTE_PARAM_RENDERMODE );
		pParam->copy( m_pEngineGizmo->get_parameter( ENGINE_PARAM_RENDERMODE ) );
		pParam->set_parent( m_pAttribGizmo );
		
		pParam = m_pAttribGizmo->get_parameter( ATTRIBUTE_PARAM_CAMERA );
		pParam->copy( m_pEngineGizmo->get_parameter( ENGINE_PARAM_CAMERA ) );
		pParam->set_parent( m_pAttribGizmo );
		
		pParam = m_pAttribGizmo->get_parameter( ATTRIBUTE_PARAM_FPS );
		pParam->copy( m_pEngineGizmo->get_parameter( ENGINE_PARAM_FPS ) );
		pParam->set_parent( m_pAttribGizmo );

		pParam = m_pAttribGizmo->get_parameter( ATTRIBUTE_PARAM_CLEARZ );
		pParam->copy( m_pEngineGizmo->get_parameter( ENGINE_PARAM_CLEARZ ) );
		pParam->set_parent( m_pAttribGizmo );

		pParam = m_pAttribGizmo->get_parameter( ATTRIBUTE_PARAM_FILE );
		pParam->copy( m_pEngineGizmo->get_parameter( ENGINE_PARAM_FILE ) );
		pParam->set_parent( m_pAttribGizmo );

		// kill the old gizmo
		m_pEngineGizmo->release();
		m_pEngineGizmo = 0;
	}
}

void
EngineEffectC::eval_state( int32 i32Time, TimeContextC* pTimeContext )
{

	Matrix2C	rPosMat, rScaleMat, rPivotMat;

	Vector2C	rScale = m_pTransGizmo->get_scale( i32Time );
	Vector2C	rPos = m_pTransGizmo->get_pos( i32Time );
	Vector2C	rPivot = m_pTransGizmo->get_pivot( i32Time );

	rPivotMat.set_trans( rPivot );
	rPosMat.set_trans( rPos );
	rScaleMat.set_scale( rScale ) ;

	m_rTM = rPivotMat * rScaleMat * rPosMat;

	//
	// calc bounding box
	//
	float32		f32Width = m_pAttribGizmo->get_width( i32Time ) / 2;
	float32		f32Height = m_pAttribGizmo->get_height( i32Time ) / 2;
	Vector2C	rMin, rMax;
	Vector2C	rVec;

	m_rVertices[0][0] = -f32Width;		// top-left
	m_rVertices[0][1] = -f32Height;

	m_rVertices[1][0] =  f32Width;		// top-right
	m_rVertices[1][1] = -f32Height;

	m_rVertices[2][0] =  f32Width;		// bottom-right
	m_rVertices[2][1] =  f32Height;

	m_rVertices[3][0] = -f32Width;		// bottom-left
	m_rVertices[3][1] =  f32Height;

	for( uint32 i = 0; i < 4; i++ ) {
		rVec = m_rTM * m_rVertices[i];
		m_rVertices[i] = rVec;

		if( !i ) {
			rMin = rMax = rVec;
		}
		else {
			if( rVec[0] < rMin[0] ) rMin[0] = rVec[0];
			if( rVec[1] < rMin[1] ) rMin[1] = rVec[1];
			if( rVec[0] > rMax[0] ) rMax[0] = rVec[0];
			if( rVec[1] > rMax[1] ) rMax[1] = rVec[1];
		}
	}

	// set
	m_rBBox[0] = rMin;
	m_rBBox[1] = rMax;

	// get rendermode
	m_i32RenderMode = m_pAttribGizmo->get_render_mode( i32Time );

	m_i32Time = i32Time;


	MASImportC*	pImp = (MASImportC*)m_pAttribGizmo->get_file();
	if( pImp ) {

		int32	i32FPS;
		int32	i32TicksPerFrame;
		int32	i32FirstFrame;
		int32	i32LastFrame;

		pImp->get_time_parameters( i32FPS, i32TicksPerFrame, i32FirstFrame, i32LastFrame );

		m_i32Frame = (int32)(pTimeContext->convert_time_to_fps( i32Time, m_pAttribGizmo->get_fps( i32Time ) ) * (float32)i32TicksPerFrame);
		m_i32Frame += i32FirstFrame * i32TicksPerFrame;
	}
	else
		m_i32Frame = 0;

	// set aspectratio
	if( rScale[0] != 0 && rScale[1] != 0 ) {
		m_f32Aspect = fabs( rScale[0] / rScale[1] );
	}
	else {
		m_f32Aspect = 1.0f;
	}
}


bool
EngineEffectC::hit_test( const PajaTypes::Vector2C& rPoint )
{
	if( rPoint[0] >= m_rBBox[0][0] && rPoint[0] <= m_rBBox[1][0] &&
		rPoint[1] >= m_rBBox[0][1] && rPoint[1] <= m_rBBox[1][1] )
		return true;
	return false;
}



uint32
EngineEffectC::render_normal_single_pass( OpenGLInterfaceC*	pInterface, GeomListC* pGList, bool bDrawAdditives )
{
	if( !g_bMultiTexture ) {
		assert( 0 );
		return 0;
	}

	if( pGList->get_layer_count() < 1 )
		return 0;

	if( pGList->get_layer_count() == 1 ) {

		//
		// one layer
		//

		glActiveTextureARB( GL_TEXTURE0_ARB );			


		MtlLayerC*	pLayer = pGList->get_layer( 0 );

		uint16	ui16Flags = pLayer->get_flags();
		bool	bDoLight = false;



		// material settings
		if( ui16Flags & MtlLayerC::LAYER_LIGHTING_AMBIENT ) {
			float32	f32Amb[4];
			ColorC	rAmbient = pLayer->get_ambient();
			f32Amb[0] = 0; //rAmbient[0];
			f32Amb[1] = 0; //rAmbient[1];
			f32Amb[2] = 0.1f; //rAmbient[2];
			f32Amb[3] = 1;
			glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, f32Amb );
			bDoLight = true;
		}
		if( ui16Flags & MtlLayerC::LAYER_LIGHTING_DIFFUSE ) {
			float32	f32Diff[4] = { 1, 1, 1, 1 };
			ColorC	rDiffuse = pLayer->get_diffuse();
			if( !(ui16Flags & MtlLayerC::LAYER_TEXTURE) ) {
				// If this layer has texture use texture color as diffuse
				// else use diffuse color from layer
				f32Diff[0] = rDiffuse[0];
				f32Diff[1] = rDiffuse[1];
				f32Diff[2] = rDiffuse[2];
				f32Diff[3] = 1;
			}

			glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, f32Diff );

			f32Diff[0] = pLayer->get_self_illumination();
			f32Diff[1] = pLayer->get_self_illumination();
			f32Diff[2] = pLayer->get_self_illumination();

			glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, f32Diff );

			bDoLight = true;
		}
		if( ui16Flags & MtlLayerC::LAYER_LIGHTING_SPECULAR ) {
			float32	f32Spec[4];
			ColorC	rSpecular = pLayer->get_ambient();
			f32Spec[0] = 1; //rSpecular[0];
			f32Spec[1] = 1; //rSpecular[1];
			f32Spec[2] = 1; //rSpecular[2];
			f32Spec[3] = 1;
			glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, f32Spec );
			glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, 10 );
			bDoLight = true;
		}

		if( bDoLight )
			glEnable( GL_LIGHTING );
		else
			glDisable( GL_LIGHTING );

		if( ui16Flags & MtlLayerC::LAYER_TWO_SIDED ) {
			glDisable( GL_CULL_FACE );
			glLightModeli( GL_LIGHT_MODEL_TWO_SIDE , 1 );
		}
		else {
			glCullFace( GL_BACK );
			glEnable( GL_CULL_FACE );
			glLightModeli( GL_LIGHT_MODEL_TWO_SIDE , 0 );
		}

		// vertex color
		uint8*	pColors = 0;
		uint16*	pColorIndices = 0;

		if( ui16Flags & MtlLayerC::LAYER_VERTEX_COLOR ) {
			glEnable( GL_COLOR_MATERIAL );
			glColorMaterial( GL_FRONT_AND_BACK, GL_DIFFUSE );
			pColors = pGList->get_color_pointer();
			pColorIndices = pGList->get_color_index_pointer();
		}


		// Blending
		glBlendFunc( GL_ONE, GL_ZERO );
		if( ui16Flags & MtlLayerC::LAYER_BLEND_MULTIPLY ) {
			if( bDrawAdditives )
				return 0;
			glBlendFunc( GL_DST_COLOR, GL_ZERO );
			glDepthMask( GL_FALSE );
		}

		if( ui16Flags & MtlLayerC::LAYER_BLEND_ADD ) {
			if( !bDrawAdditives )
				return 1;
			glBlendFunc( GL_ONE, GL_ONE );
			glDepthMask( GL_FALSE );
		}

		if( ui16Flags & MtlLayerC::LAYER_BLEND_REPLACE ) {
			if( bDrawAdditives )
				return 0;
			glBlendFunc( GL_ONE, GL_ZERO );
		}


		float32*	pTexCoords = 0;
		uint16*		pTexIndices = 0;
		
		// Texture stuff


		ImportableImageI*	pTex = 0;

		if( ui16Flags & MtlLayerC::LAYER_TEXTURE ) {
			FileHandleC*	pHandle = pLayer->get_texture();
			if( pHandle )
				pTex = (ImportableImageI*)pHandle->get_importable(); //pDoc->GetTexture( pLayer->get_texture_id() );
		}

		if( pTex ) {

//				OutputDebugString( "texture ok?\n" );

			glEnable( GL_TEXTURE_2D );

			pTex->bind_texture( pInterface, IMAGE_WRAP | IMAGE_LINEAR );

			// automatic tex-coord generation
			if( pLayer->get_mapping_type() == MtlLayerC::MAPPING_SPHERICAL_ENV ||
				pLayer->get_mapping_type() == MtlLayerC::MAPPING_CYLINDRICAL_ENV ||
				pLayer->get_mapping_type() == MtlLayerC::MAPPING_SHRINKWRAP_ENV ) {
				glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
				glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
				glEnable( GL_TEXTURE_GEN_S );
				glEnable( GL_TEXTURE_GEN_T );
			}
			else if( pLayer->get_mapping_type() == MtlLayerC::MAPPING_COORDS ) {
				uint32	ui32Channel = pLayer->get_mapping_channel();
				TCSetC*	pSet = pGList->get_texture_channel( ui32Channel );
				pTexCoords =  pSet->get_coord_pointer();
				pTexIndices = pSet->get_index_pointer();
//					OutputDebugString( "tex coord\n" );
			}
		}
		else {
			glDisable( GL_TEXTURE_2D );
		}


		glColor3ub( 255, 255, 255 );


		float32*	pVertices = pGList->get_vertex_pointer();
		float32*	pNormals = pGList->get_normal_pointer();

		uint16*		pIndices = pGList->get_index_pointer();
		uint16*		pNormalIndices = pGList->get_normal_index_pointer();


		glBegin( GL_TRIANGLES );

		for( uint32 f = 0; f < pGList->get_index_count(); f++ ) {

			glNormal3fv( &pNormals[(*pNormalIndices) * 3] );
			pNormalIndices++;

			if( pColors ) {
				glColor4ubv( &pColors[(*pColorIndices) * 4] );
				pColorIndices++;
			}

			if( pTexCoords ) {
				glTexCoord2fv( &pTexCoords[(*pTexIndices) * 2] );
				pTexIndices++;
			}

			glVertex3fv( &pVertices[(*pIndices) * 3] );
			pIndices++;
		}

		glEnd();

		glActiveTextureARB( GL_TEXTURE0_ARB );
		glDisable( GL_TEXTURE_2D );
		glDisable( GL_COLOR_MATERIAL );
		glDisable( GL_TEXTURE_GEN_S );
		glDisable( GL_TEXTURE_GEN_T );

		glDepthMask( GL_TRUE );

	}
	else {

		//
		// two layers
		//

		MtlLayerC*	pLayers[2];
		pLayers[0] = pGList->get_layer( 0 );
		pLayers[1] = pGList->get_layer( 1 );

		uint16	ui16Flags[2];
		ui16Flags[0] = pLayers[0]->get_flags();
		ui16Flags[1] = pLayers[1]->get_flags();

		int32	i32TextureLayers = 0;

		bool	bDoLight = false;


		const uint32	ui32TexDiffLayer =		MtlLayerC::LAYER_LIGHTING_AMBIENT | MtlLayerC::LAYER_LIGHTING_DIFFUSE |
												MtlLayerC::LAYER_LIGHTING_SPECULAR | MtlLayerC::LAYER_TEXTURE |
												MtlLayerC::LAYER_BLEND_REPLACE;

		const uint32	ui32DiffLayer =			MtlLayerC::LAYER_LIGHTING_AMBIENT | MtlLayerC::LAYER_LIGHTING_DIFFUSE |
												MtlLayerC::LAYER_LIGHTING_SPECULAR | MtlLayerC::LAYER_BLEND_REPLACE;

		const uint32	ui32VertColDiffLayer =	MtlLayerC::LAYER_LIGHTING_AMBIENT | MtlLayerC::LAYER_LIGHTING_DIFFUSE |
												MtlLayerC::LAYER_LIGHTING_SPECULAR | MtlLayerC::LAYER_VERTEX_COLOR |
												MtlLayerC::LAYER_BLEND_REPLACE;

		const uint32	ui32MultBaseTexLayer =	MtlLayerC::LAYER_TEXTURE | MtlLayerC::LAYER_BLEND_REPLACE;

		const uint32	ui32MultTexLayer =		MtlLayerC::LAYER_LIGHTING_AMBIENT | MtlLayerC::LAYER_LIGHTING_DIFFUSE |
												MtlLayerC::LAYER_LIGHTING_SPECULAR | MtlLayerC::LAYER_TEXTURE |
												MtlLayerC::LAYER_BLEND_MULTIPLY;

		const uint32	ui32EnvLayer =			MtlLayerC::LAYER_TEXTURE | MtlLayerC::LAYER_BLEND_ADD;


		float32*	pTexCoords[2] = { 0, 0 };
		uint16*		pTexIndices[2] = { 0, 0 };

		// vertex color
		uint8*	pColors = 0;
		uint16*	pColorIndices = 0;

		
		glDepthMask( GL_TRUE );

		// Blending
		glBlendFunc( GL_ONE, GL_ZERO );
		if( ui16Flags[0] & MtlLayerC::LAYER_BLEND_MULTIPLY ) {
			if( bDrawAdditives )
				return 0;
			glBlendFunc( GL_DST_COLOR, GL_ZERO );
			glDepthMask( GL_FALSE );
		}

		if( ui16Flags[0] & MtlLayerC::LAYER_BLEND_ADD ) {
			if( !bDrawAdditives )
				return 1;
			glBlendFunc( GL_ONE, GL_ONE );
		}

		if( ui16Flags[0] & MtlLayerC::LAYER_BLEND_REPLACE ) {
			if( bDrawAdditives )
				return 0;
			glBlendFunc( GL_ONE, GL_ZERO );
		}


		glBlendFunc( GL_ONE, GL_ZERO );

		if( ui16Flags[0] == ui32TexDiffLayer && ui16Flags[1] == ui32EnvLayer ) {
			// diffuse tex + env tex

//			TRACE( "diff tex + env tex\n" );

			// Texture stuff

			// diffuse

			glActiveTextureARB( GL_TEXTURE0_ARB + i32TextureLayers );

			ImportableImageI*	pTex = 0;

			if( ui16Flags[0] & MtlLayerC::LAYER_TEXTURE ) {
				FileHandleC*	pHandle = pLayers[0]->get_texture();
				if( pHandle )
					pTex = (ImportableImageI*)pHandle->get_importable(); //pDoc->GetTexture( pLayer->get_texture_id() );
			}

			if( pTex ) {

				glEnable( GL_TEXTURE_2D );
				pTex->bind_texture( pInterface, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

				// automatic tex-coord generation
				if( pLayers[0]->get_mapping_type() == MtlLayerC::MAPPING_SPHERICAL_ENV ||
					pLayers[0]->get_mapping_type() == MtlLayerC::MAPPING_CYLINDRICAL_ENV ||
					pLayers[0]->get_mapping_type() == MtlLayerC::MAPPING_SHRINKWRAP_ENV ) {
					glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
					glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
					glEnable( GL_TEXTURE_GEN_S );
					glEnable( GL_TEXTURE_GEN_T );
				}
				else if( pLayers[0]->get_mapping_type() == MtlLayerC::MAPPING_COORDS ) {
					uint32	ui32Channel = pLayers[0]->get_mapping_channel();
					TCSetC*	pSet = pGList->get_texture_channel( ui32Channel );
					pTexCoords[i32TextureLayers] =  pSet->get_coord_pointer();
					pTexIndices[i32TextureLayers] = pSet->get_index_pointer();
				}
				i32TextureLayers++;
			}
			else {
				glDisable( GL_TEXTURE_2D );
			}


			// reflection

			glActiveTextureARB( GL_TEXTURE0_ARB + i32TextureLayers );

			if( ui16Flags[1] & MtlLayerC::LAYER_TEXTURE ) {
				FileHandleC*	pHandle = pLayers[1]->get_texture();
				if( pHandle )
					pTex = (ImportableImageI*)pHandle->get_importable(); //pDoc->GetTexture( pLayer->get_texture_id() );
			}

			if( pTex ) {

				glEnable( GL_TEXTURE_2D );
				pTex->bind_texture( pInterface, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );

				// automatic tex-coord generation
				if( pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_SPHERICAL_ENV ||
					pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_CYLINDRICAL_ENV ||
					pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_SHRINKWRAP_ENV ) {
					glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
					glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
					glEnable( GL_TEXTURE_GEN_S );
					glEnable( GL_TEXTURE_GEN_T );
				}
				else if( pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_COORDS ) {
					uint32	ui32Channel = pLayers[1]->get_mapping_channel();
					TCSetC*	pSet = pGList->get_texture_channel( ui32Channel );
					pTexCoords[i32TextureLayers] =  pSet->get_coord_pointer();
					pTexIndices[i32TextureLayers] = pSet->get_index_pointer();
				}
				i32TextureLayers++;
			}
			else {
				glDisable( GL_TEXTURE_2D );
			}


		}
		else if( ui16Flags[0] == ui32DiffLayer && ui16Flags[1] == ui32EnvLayer ) {
			// diffuse + env tex

			//
			//
			// kludge!!! why doesnt below code work!!!
			//
			//

			return render_normal( pInterface, pGList, bDrawAdditives );

/*


//			TRACE( "diff + env tex\n" );

			// Texture stuff

			glActiveTextureARB( GL_TEXTURE0_ARB );
			glDisable( GL_TEXTURE_2D );
			glActiveTextureARB( GL_TEXTURE1_ARB );
			glDisable( GL_TEXTURE_2D );

			glActiveTextureARB( GL_TEXTURE0_ARB + i32TextureLayers );

			ImportableImageI*	pTex = 0;

			if( ui16Flags[1] & MtlLayerC::LAYER_TEXTURE ) {
				FileHandleC*	pHandle = pLayers[1]->get_texture();
				if( pHandle )
					pTex = (ImportableImageI*)pHandle->get_importable(); //pDoc->GetTexture( pLayer->get_texture_id() );
			}

			if( pTex ) {

				glEnable( GL_TEXTURE_2D );
				pTex->bind_texture( IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );

				// automatic tex-coord generation
				if( pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_SPHERICAL_ENV ||
					pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_CYLINDRICAL_ENV ||
					pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_SHRINKWRAP_ENV ) {
					glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
					glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
					glEnable( GL_TEXTURE_GEN_S );
					glEnable( GL_TEXTURE_GEN_T );
				}
				else if( pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_COORDS ) {
					uint32	ui32Channel = pLayers[1]->get_mapping_channel();
					TCSetC*	pSet = pGList->get_texture_channel( ui32Channel );
					pTexCoords[i32TextureLayers] =  pSet->get_coord_pointer();
					pTexIndices[i32TextureLayers] = pSet->get_index_pointer();
				}
			}
			else {
				glDisable( GL_TEXTURE_2D );
			}

			i32TextureLayers++;
*/
		}
		else if( ui16Flags[0] == ui32VertColDiffLayer && ui16Flags[1] == ui32EnvLayer ) {
			// vert color diffuse + env tex

//			TRACE( "vert col diff + env tex\n" );

			glEnable( GL_COLOR_MATERIAL );
			glColorMaterial( GL_FRONT_AND_BACK, GL_DIFFUSE );

			pColors = pGList->get_color_pointer();
			pColorIndices = pGList->get_color_index_pointer();

//			TRACE( "color pointers: %p, %p\n", pColors, pColorIndices );

			// Texture stuff

			glActiveTextureARB( GL_TEXTURE0_ARB + i32TextureLayers );

			ImportableImageI*	pTex = 0;

			if( ui16Flags[1] & MtlLayerC::LAYER_TEXTURE ) {
				FileHandleC*	pHandle = pLayers[1]->get_texture();
				if( pHandle )
					pTex = (ImportableImageI*)pHandle->get_importable(); //pDoc->GetTexture( pLayer->get_texture_id() );
			}

			if( pTex ) {

				glEnable( GL_TEXTURE_2D );
				pTex->bind_texture( pInterface, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );

				// automatic tex-coord generation
				if( pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_SPHERICAL_ENV ||
					pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_CYLINDRICAL_ENV ||
					pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_SHRINKWRAP_ENV ) {
					glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
					glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
					glEnable( GL_TEXTURE_GEN_S );
					glEnable( GL_TEXTURE_GEN_T );
				}
				else if( pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_COORDS ) {
					uint32	ui32Channel = pLayers[1]->get_mapping_channel();
					TCSetC*	pSet = pGList->get_texture_channel( ui32Channel );
					pTexCoords[i32TextureLayers] =  pSet->get_coord_pointer();
					pTexIndices[i32TextureLayers] = pSet->get_index_pointer();
				}
			}
			else {
				glDisable( GL_TEXTURE_2D );
			}

			i32TextureLayers++;


		}
		else if( ui16Flags[0] == ui32MultBaseTexLayer && ui16Flags[1] == ui32MultTexLayer ) {
			// diffuse tex + mult tex

//			TRACE( "diff tex + mult tex\n" );

			// diffuse

			glActiveTextureARB( GL_TEXTURE0_ARB + i32TextureLayers );

			ImportableImageI*	pTex = 0;

			if( ui16Flags[0] & MtlLayerC::LAYER_TEXTURE ) {
				FileHandleC*	pHandle = pLayers[0]->get_texture();
				if( pHandle )
					pTex = (ImportableImageI*)pHandle->get_importable(); //pDoc->GetTexture( pLayer->get_texture_id() );
			}

			if( pTex ) {

				glEnable( GL_TEXTURE_2D );
				pTex->bind_texture( pInterface, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );

				// automatic tex-coord generation
				if( pLayers[0]->get_mapping_type() == MtlLayerC::MAPPING_SPHERICAL_ENV ||
					pLayers[0]->get_mapping_type() == MtlLayerC::MAPPING_CYLINDRICAL_ENV ||
					pLayers[0]->get_mapping_type() == MtlLayerC::MAPPING_SHRINKWRAP_ENV ) {
					glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
					glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
					glEnable( GL_TEXTURE_GEN_S );
					glEnable( GL_TEXTURE_GEN_T );
				}
				else if( pLayers[0]->get_mapping_type() == MtlLayerC::MAPPING_COORDS ) {
					uint32	ui32Channel = pLayers[0]->get_mapping_channel();
					TCSetC*	pSet = pGList->get_texture_channel( ui32Channel );
					pTexCoords[i32TextureLayers] =  pSet->get_coord_pointer();
					pTexIndices[i32TextureLayers] = pSet->get_index_pointer();
				}
				i32TextureLayers++;
			}
			else {
				glDisable( GL_TEXTURE_2D );
			}


			// mult

			glActiveTextureARB( GL_TEXTURE0_ARB + i32TextureLayers );

			if( ui16Flags[1] & MtlLayerC::LAYER_TEXTURE ) {
				FileHandleC*	pHandle = pLayers[1]->get_texture();
				if( pHandle )
					pTex = (ImportableImageI*)pHandle->get_importable(); //pDoc->GetTexture( pLayer->get_texture_id() );
			}

			if( pTex ) {

				glEnable( GL_TEXTURE_2D );
				pTex->bind_texture( pInterface, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

				// automatic tex-coord generation
				if( pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_SPHERICAL_ENV ||
					pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_CYLINDRICAL_ENV ||
					pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_SHRINKWRAP_ENV ) {
					glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
					glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
					glEnable( GL_TEXTURE_GEN_S );
					glEnable( GL_TEXTURE_GEN_T );
				}
				else if( pLayers[1]->get_mapping_type() == MtlLayerC::MAPPING_COORDS ) {
					uint32	ui32Channel = pLayers[1]->get_mapping_channel();
					TCSetC*	pSet = pGList->get_texture_channel( ui32Channel );
					pTexCoords[i32TextureLayers] =  pSet->get_coord_pointer();
					pTexIndices[i32TextureLayers] = pSet->get_index_pointer();
				}
				i32TextureLayers++;
			}
			else {
				glDisable( GL_TEXTURE_2D );
			}


		}
		else
			return render_normal( pInterface, pGList, bDrawAdditives );


		//
		// accept lighting settings only from layer 0
		//

		uint32	ui32LightLayer = 0;
		if( ui16Flags[1] & MtlLayerC::LAYER_LIGHTING_DIFFUSE )
			ui32LightLayer = 1;

		// material settings
		if( ui16Flags[ui32LightLayer] & MtlLayerC::LAYER_LIGHTING_AMBIENT ) {
			float32	f32Amb[4];
			ColorC	rAmbient = pLayers[ui32LightLayer]->get_ambient();
			f32Amb[0] = 0; //rAmbient[0];
			f32Amb[1] = 0; //rAmbient[1];
			f32Amb[2] = 0.1f; //rAmbient[2];
			f32Amb[3] = 1;
			glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, f32Amb );
			bDoLight = true;
		}
		if( ui16Flags[ui32LightLayer] & MtlLayerC::LAYER_LIGHTING_DIFFUSE ) {
			float32	f32Diff[4] = { 1, 1, 1, 1 };
			ColorC	rDiffuse = pLayers[ui32LightLayer]->get_diffuse();
			if( !(ui16Flags[ui32LightLayer] & MtlLayerC::LAYER_TEXTURE) ) {
				// If this layer has texture use texture color as diffuse
				// else use diffuse color from layer
				f32Diff[0] = rDiffuse[0];
				f32Diff[1] = rDiffuse[1];
				f32Diff[2] = rDiffuse[2];
				f32Diff[3] = 1;
			}

			glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, f32Diff );

			f32Diff[0] = pLayers[ui32LightLayer]->get_self_illumination();
			f32Diff[1] = pLayers[ui32LightLayer]->get_self_illumination();
			f32Diff[2] = pLayers[ui32LightLayer]->get_self_illumination();

			glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, f32Diff );

			bDoLight = true;
		}
		if( ui16Flags[ui32LightLayer] & MtlLayerC::LAYER_LIGHTING_SPECULAR ) {
			float32	f32Spec[4];
			ColorC	rSpecular = pLayers[ui32LightLayer]->get_ambient();
			f32Spec[0] = 1; //rSpecular[0];
			f32Spec[1] = 1; //rSpecular[1];
			f32Spec[2] = 1; //rSpecular[2];
			f32Spec[3] = 1;
			glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, f32Spec );
			glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, 10 );
			bDoLight = true;
		}


		if( bDoLight )
			glEnable( GL_LIGHTING );
		else
			glDisable( GL_LIGHTING );


		glBlendFunc( GL_ONE, GL_ZERO );


		glColor3ub( 128, 128, 128 );


/*		for( i = 0; i < 2; i++ ) {
			// vertex color
			uint8*	pColors = 0;
			uint16*	pColorIndices = 0;

			if( ui16Flags & MtlLayerC::LAYER_VERTEX_COLOR ) {
				glEnable( GL_COLOR_MATERIAL );
				glColorMaterial( GL_FRONT_AND_BACK, GL_DIFFUSE );
				pColors = pGList->get_color_pointer();
				pColorIndices = pGList->get_color_index_pointer();
			}
*/

			// Blending
/*			glBlendFunc( GL_ONE, GL_ZERO );
			if( ui16Flags & MtlLayerC::LAYER_BLEND_MULTIPLY ) {
				glBlendFunc( GL_DST_COLOR, GL_ZERO );
			}
			else if( ui16Flags & MtlLayerC::LAYER_BLEND_ADD ) {
				glBlendFunc( GL_ONE, GL_ONE );
			}
			else if( ui16Flags & MtlLayerC::LAYER_BLEND_REPLACE ) {
				glBlendFunc( GL_ONE, GL_ZERO );
			}
*/

		float32*	pVertices = pGList->get_vertex_pointer();
		float32*	pNormals = pGList->get_normal_pointer();

		uint16*		pIndices = pGList->get_index_pointer();
		uint16*		pNormalIndices = pGList->get_normal_index_pointer();


		glBegin( GL_TRIANGLES );

		for( uint32 f = 0; f < pGList->get_index_count(); f++ ) {

			glNormal3fv( &pNormals[(*pNormalIndices) * 3] );
			pNormalIndices++;

			if( pColors ) {
				glColor4ubv( &pColors[(*pColorIndices) * 4] );
				pColorIndices++;
			}

			if( pTexCoords[0] ) {
				glMultiTexCoord2fvARB( GL_TEXTURE0_ARB, &pTexCoords[0][(*pTexIndices[0]) * 2] );
				pTexIndices[0]++;
			}

			if( pTexCoords[1] ) {
				glMultiTexCoord2fvARB( GL_TEXTURE1_ARB, &pTexCoords[1][(*pTexIndices[1]) * 2] );
				pTexIndices[1]++;
			}

			glVertex3fv( &pVertices[(*pIndices) * 3] );
			pIndices++;
		}

		glEnd();

		glDisable( GL_COLOR_MATERIAL );


		glActiveTextureARB(GL_TEXTURE1_ARB);		
		glDisable( GL_TEXTURE_GEN_S );
		glDisable( GL_TEXTURE_GEN_T );
		glDisable( GL_TEXTURE_2D );

		glActiveTextureARB(GL_TEXTURE0_ARB);			
		glDisable( GL_TEXTURE_GEN_S );
		glDisable( GL_TEXTURE_GEN_T );
		glDisable( GL_TEXTURE_2D );

	}

	return 0;
}


uint32
EngineEffectC::render_normal( OpenGLInterfaceC*	pInterface, GeomListC* pGList, bool bDrawAdditives )
{

	if( g_bMultiTexture )
		glActiveTextureARB(GL_TEXTURE0_ARB);		

	for( uint32 k = 0; k < pGList->get_layer_count(); k++ ) {
		MtlLayerC*	pLayer = pGList->get_layer( k );

		uint16	ui16Flags = pLayer->get_flags();
		bool	bDoLight = false;

		if( ui16Flags & MtlLayerC::LAYER_TWO_SIDED ) {
			glDisable( GL_CULL_FACE );
			glLightModeli( GL_LIGHT_MODEL_TWO_SIDE , 1 );
		}
		else {
			glCullFace( GL_BACK );
			glEnable( GL_CULL_FACE );
			glLightModeli( GL_LIGHT_MODEL_TWO_SIDE , 0 );
		}

		// material settings
		if( ui16Flags & MtlLayerC::LAYER_LIGHTING_AMBIENT ) {
			float32	f32Amb[4];
			ColorC	rAmbient = pLayer->get_ambient();
			f32Amb[0] = 0; //rAmbient[0];
			f32Amb[1] = 0; //rAmbient[1];
			f32Amb[2] = 0.1f; //rAmbient[2];
			f32Amb[3] = 1;
			glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, f32Amb );
			bDoLight = true;
		}
		if( ui16Flags & MtlLayerC::LAYER_LIGHTING_DIFFUSE ) {
			float32	f32Diff[4] = { 1, 1, 1, 1 };
			ColorC	rDiffuse = pLayer->get_diffuse();
			if( !(ui16Flags & MtlLayerC::LAYER_TEXTURE) ) {
				// If this layer has texture use texture color as diffuse
				// else use diffuse color from layer
				f32Diff[0] = rDiffuse[0];
				f32Diff[1] = rDiffuse[1];
				f32Diff[2] = rDiffuse[2];
				f32Diff[3] = 1;
			}

			glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, f32Diff );

			f32Diff[0] = pLayer->get_self_illumination();
			f32Diff[1] = pLayer->get_self_illumination();
			f32Diff[2] = pLayer->get_self_illumination();

			glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, f32Diff );

			bDoLight = true;
		}
		if( ui16Flags & MtlLayerC::LAYER_LIGHTING_SPECULAR ) {
			float32	f32Spec[4];
			ColorC	rSpecular = pLayer->get_ambient();
			f32Spec[0] = 1; //rSpecular[0];
			f32Spec[1] = 1; //rSpecular[1];
			f32Spec[2] = 1; //rSpecular[2];
			f32Spec[3] = 1;
			glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, f32Spec );
			glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, 10 );
			bDoLight = true;
		}


		if( bDoLight )
			glEnable( GL_LIGHTING );
		else
			glDisable( GL_LIGHTING );


		// vertex color
		uint8*	pColors = 0;
		uint16*	pColorIndices = 0;
/*
		if( ui16Flags & MtlLayerC::LAYER_VERTEX_COLOR ) {
			glEnable( GL_COLOR_MATERIAL );
			glColorMaterial( GL_FRONT_AND_BACK, GL_DIFFUSE );
			pColors = pGList->get_color_pointer();
			pColorIndices = pGList->get_color_index_pointer();
		}
*/

		// Blending
		glBlendFunc( GL_ONE, GL_ZERO );
		if( ui16Flags & MtlLayerC::LAYER_BLEND_MULTIPLY ) {
			if( k == 0 && bDrawAdditives )
				return 0;
			glBlendFunc( GL_DST_COLOR, GL_ZERO );
			glDepthMask( GL_FALSE );
		}

		if( ui16Flags & MtlLayerC::LAYER_BLEND_ADD ) {
			if( k == 0 && !bDrawAdditives )
				return 1;
			glBlendFunc( GL_ONE, GL_ONE );
			glDepthMask( GL_FALSE );
		}

		if( ui16Flags & MtlLayerC::LAYER_BLEND_REPLACE ) {
			if( k == 0 && bDrawAdditives )
				return 0;
			glBlendFunc( GL_ONE, GL_ZERO );
		}



		float32*	pTexCoords = 0;
		uint16*		pTexIndices = 0;
		
		// Texture stuff

		ImportableImageI*	pTex = 0;

		if( ui16Flags & MtlLayerC::LAYER_TEXTURE ) {
			FileHandleC*	pHandle = pLayer->get_texture();
			if( pHandle )
				pTex = (ImportableImageI*)pHandle->get_importable(); //pDoc->GetTexture( pLayer->get_texture_id() );
		}

		if( pTex ) {

//				OutputDebugString( "texture ok?\n" );

			glEnable( GL_TEXTURE_2D );

			pTex->bind_texture( pInterface, IMAGE_WRAP | IMAGE_LINEAR );

			// automatic tex-coord generation
			if( pLayer->get_mapping_type() == MtlLayerC::MAPPING_SPHERICAL_ENV ||
				pLayer->get_mapping_type() == MtlLayerC::MAPPING_CYLINDRICAL_ENV ||
				pLayer->get_mapping_type() == MtlLayerC::MAPPING_SHRINKWRAP_ENV ) {
				glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
				glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
				glEnable( GL_TEXTURE_GEN_S );
				glEnable( GL_TEXTURE_GEN_T );
			}
			else if( pLayer->get_mapping_type() == MtlLayerC::MAPPING_COORDS ) {
				uint32	ui32Channel = pLayer->get_mapping_channel();
				TCSetC*	pSet = pGList->get_texture_channel( ui32Channel );
				pTexCoords =  pSet->get_coord_pointer();
				pTexIndices = pSet->get_index_pointer();
//					OutputDebugString( "tex coord\n" );
			}
		}
		else {
			glDisable( GL_TEXTURE_2D );
		}


		glColor3ub( 255, 255, 255 );




		float32*	pVertices = pGList->get_vertex_pointer();
		float32*	pNormals = pGList->get_normal_pointer();

		uint16*		pIndices = pGList->get_index_pointer();
		uint16*		pNormalIndices = pGList->get_normal_index_pointer();

		assert( pVertices && pNormals && pIndices && pNormalIndices );

		glBegin( GL_TRIANGLES );

		for( uint32 f = 0; f < pGList->get_index_count(); f++ ) {

			glNormal3fv( &pNormals[(*pNormalIndices) * 3] );
			pNormalIndices++;

			if( pColors ) {
				glColor4ubv( &pColors[(*pColorIndices) * 4] );
				pColorIndices++;
			}

			if( pTexCoords ) {
				glTexCoord2fv( &pTexCoords[(*pTexIndices) * 2] );
				pTexIndices++;
			}

			glVertex3fv( &pVertices[(*pIndices) * 3] );
			pIndices++;
		}

		glEnd();


		glDisable( GL_COLOR_MATERIAL );
		glDisable( GL_TEXTURE_GEN_S );
		glDisable( GL_TEXTURE_GEN_T );
		glDisable( GL_TEXTURE_2D );
		glDepthMask( GL_TRUE );

/*
		glDisable( GL_LIGHTING );

		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
		glEnable( GL_BLEND );

		glBegin( GL_LINES );

		pIndices = pGList->get_index_pointer();
		pNormalIndices = pGList->get_normal_index_pointer();

		for( f = 0; f < pGList->get_index_count(); f++ ) {

			float32*	pNorm = &pNormals[(*pNormalIndices) * 3];
			float32*	pVert = &pVertices[(*pIndices) * 3];

			glColor4ub( 255, 225, 64, 255 );
			glVertex3f( pVert[0], pVert[1], pVert[2] );

			glColor4ub( 255, 225, 64, 0 );
			glVertex3f( pVert[0] + pNorm[0] * 5, pVert[1] + pNorm[1] * 5, pVert[2] + pNorm[2] * 5 );

			pNormalIndices++;
			pIndices++;
		}

		glEnd();
*/
	}	

	return 0;
}

void
EngineEffectC::render_additive( OpenGLInterfaceC* pInterface, GeomListC* pGList )
{
	if( g_bMultiTexture )
		glActiveTextureARB(GL_TEXTURE0_ARB);

	for( uint32 k = 0; k < pGList->get_layer_count(); k++ ) {
		MtlLayerC*	pLayer = pGList->get_layer( k );

		uint16	ui16Flags = pLayer->get_flags();
		bool	bDoLight = false;

		// material settings
		if( ui16Flags & MtlLayerC::LAYER_LIGHTING_AMBIENT ) {
			float32	f32Amb[4];
			ColorC	rAmbient = pLayer->get_ambient();
			f32Amb[0] = 0; //rAmbient[0];
			f32Amb[1] = 0; //rAmbient[1];
			f32Amb[2] = 0.1f; //rAmbient[2];
			f32Amb[3] = 1;
			glMaterialfv( GL_FRONT, GL_AMBIENT, f32Amb );
			bDoLight = true;
		}
		if( ui16Flags & MtlLayerC::LAYER_LIGHTING_DIFFUSE ) {
			float32	f32Diff[4] = { 1, 1, 1, 1 };
			ColorC	rDiffuse = pLayer->get_diffuse();
			if( !(ui16Flags & MtlLayerC::LAYER_TEXTURE) ) {
				// If this layer has texture use texture color as diffuse
				// else use diffuse color from layer
				f32Diff[0] = rDiffuse[0];
				f32Diff[1] = rDiffuse[1];
				f32Diff[2] = rDiffuse[2];
				f32Diff[3] = 1;
			}

			glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, f32Diff );

			f32Diff[0] = pLayer->get_self_illumination();
			f32Diff[1] = pLayer->get_self_illumination();
			f32Diff[2] = pLayer->get_self_illumination();

			glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, f32Diff );

			bDoLight = true;
		}
		if( ui16Flags & MtlLayerC::LAYER_LIGHTING_SPECULAR ) {
			float32	f32Spec[4];
			ColorC	rSpecular = pLayer->get_ambient();
			f32Spec[0] = 1;
			f32Spec[1] = 1;
			f32Spec[2] = 1;
			f32Spec[3] = 1;
			glMaterialfv( GL_FRONT, GL_SPECULAR, f32Spec );
			glMateriali( GL_FRONT, GL_SHININESS, 10 );
			bDoLight = true;
		}

		if( bDoLight )
			glEnable( GL_LIGHTING );
		else
			glDisable( GL_LIGHTING );


		// vertex color
		uint8*	pColors = 0;
		uint16*	pColorIndices = 0;
/*
		if( ui16Flags & MtlLayerC::LAYER_VERTEX_COLOR ) {
			glEnable( GL_COLOR_MATERIAL );
			glColorMaterial( GL_FRONT_AND_BACK, GL_DIFFUSE );
			pColors = pGList->get_color_pointer();
			pColorIndices = pGList->get_color_index_pointer();
		}
*/

		// Blending
/*		glBlendFunc( GL_ONE, GL_ZERO );
		if( ui16Flags & MtlLayerC::LAYER_BLEND_MULTIPLY ) {
			glBlendFunc( GL_DST_COLOR, GL_ZERO );
		}
		else if( ui16Flags & MtlLayerC::LAYER_BLEND_ADD ) {*/
			glBlendFunc( GL_ONE, GL_ONE );
/*		}
		else if( ui16Flags & MtlLayerC::LAYER_BLEND_REPLACE ) {
			glBlendFunc( GL_ONE, GL_ZERO );
		}*/



		float32*	pTexCoords = 0;
		uint16*		pTexIndices = 0;
		
		// Texture stuff

		ImportableImageI*	pTex = 0;

		if( ui16Flags & MtlLayerC::LAYER_TEXTURE ) {
			FileHandleC*	pHandle = pLayer->get_texture();
			if( pHandle )
				pTex = (ImportableImageI*)pHandle->get_importable(); //pDoc->GetTexture( pLayer->get_texture_id() );
		}

		if( pTex ) {

//				OutputDebugString( "texture ok?\n" );

			glEnable( GL_TEXTURE_2D );

			pTex->bind_texture( pInterface, IMAGE_WRAP | IMAGE_LINEAR );

			// automatic tex-coord generation
			if( pLayer->get_mapping_type() == MtlLayerC::MAPPING_SPHERICAL_ENV ||
				pLayer->get_mapping_type() == MtlLayerC::MAPPING_CYLINDRICAL_ENV ||
				pLayer->get_mapping_type() == MtlLayerC::MAPPING_SHRINKWRAP_ENV ) {
				glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
				glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP );
				glEnable( GL_TEXTURE_GEN_S );
				glEnable( GL_TEXTURE_GEN_T );
			}
			else if( pLayer->get_mapping_type() == MtlLayerC::MAPPING_COORDS ) {
				uint32	ui32Channel = pLayer->get_mapping_channel();
				TCSetC*	pSet = pGList->get_texture_channel( ui32Channel );
				pTexCoords =  pSet->get_coord_pointer();
				pTexIndices = pSet->get_index_pointer();
//					OutputDebugString( "tex coord\n" );
			}
		}
		else {
			glDisable( GL_TEXTURE_2D );
		}

		glColor3ub( 255, 255, 255 );

		float32*	pVertices = pGList->get_vertex_pointer();
		float32*	pNormals = pGList->get_normal_pointer();

		uint16*		pIndices = pGList->get_index_pointer();
		uint16*		pNormalIndices = pGList->get_normal_index_pointer();

		assert( pVertices && pNormals && pIndices && pNormalIndices );

		glBegin( GL_TRIANGLES );

		for( uint32 f = 0; f < pGList->get_index_count(); f++ ) {

			glNormal3fv( &pNormals[(*pNormalIndices) * 3] );
			pNormalIndices++;

			if( pColors ) {
				glColor4ubv( &pColors[(*pColorIndices) * 4] );
				pColorIndices++;
			}

			if( pTexCoords ) {
				glTexCoord2fv( &pTexCoords[(*pTexIndices) * 2] );
				pTexIndices++;
			}

			glVertex3fv( &pVertices[(*pIndices) * 3] );
			pIndices++;
		}

		glEnd();

		glDisable( GL_COLOR_MATERIAL );
		glDisable( GL_TEXTURE_GEN_S );
		glDisable( GL_TEXTURE_GEN_T );
		glDisable( GL_TEXTURE_2D );
	}	
}


void
EngineEffectC::render_cartoon_wire( OpenGLInterfaceC* pInterface, GeomListC* pGList )
{

	bool	bTwoSided = false;

	for( uint32 k = 0; k < pGList->get_layer_count(); k++ ) {
		MtlLayerC*	pLayer = pGList->get_layer( k );

		uint16	ui16Flags = pLayer->get_flags();
		if( ui16Flags & MtlLayerC::LAYER_TWO_SIDED )
			bTwoSided = true;
	}

	if( bTwoSided ) {
		glDisable( GL_CULL_FACE );
	}
	else {
		glCullFace( GL_FRONT );
		glEnable( GL_CULL_FACE );
	}


	glDisable( GL_LIGHTING );

	// Blending
	glBlendFunc( GL_ONE, GL_ZERO );

	glDisable( GL_TEXTURE_2D );

	glDepthFunc( GL_LESS );

	glColor3ub( 0, 0, 0 );

	glEnable( GL_POLYGON_OFFSET_LINE );
	glPolygonOffset( 3, 1.0 );
//	glPolygonOffset( -3, -1.0 );

	glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
	glLineWidth( 3.5f );

	float32*	pVertices = pGList->get_vertex_pointer();
	uint16*		pIndices = pGList->get_index_pointer();

	assert( pVertices && pIndices );

	glBegin( GL_TRIANGLES );

	for( uint32 f = 0; f < pGList->get_index_count(); f++ ) {
		glVertex3fv( &pVertices[(*pIndices) * 3] );
		pIndices++;
	}

	glEnd();

	glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
	glDisable( GL_CULL_FACE );

	glDisable( GL_POLYGON_OFFSET_LINE );
	glPolygonOffset( 0, 0 );

}

uint32
EngineEffectC::render_item( OpenGLInterfaceC* pInterface, ScenegraphItemI* pItem, int32 i32Frame, bool bDrawAdditives )
{
	if( !pItem )
		return 0;

//	if( !pMesh )
//		return;

	pItem->eval_state( i32Frame );

	if( !pItem->get_visibility() )
		return 0;

	glPushMatrix();

	float32		f32X, f32Y, f32Z, f32A;
	Vector3C	rPos = pItem->get_position();
	QuatC		rRot = pItem->get_rotation();
	Vector3C	rScale = pItem->get_scale();
	QuatC		rScaleRot = pItem->get_scale_rotation();
	QuatC		rInvScaleRot = rScaleRot.unit_inverse();


//	char	szMsg[256];
//	_snprintf( szMsg, 255, "rot: %f %f %f %f\n", rRot[0], rRot[1], rRot[2], rRot[3] );
//	OutputDebugString( szMsg );

//	if( pItem->get_scale_controller() )
//		OutputDebugString( "scale anim\n" );

	glTranslatef( rPos[0], rPos[1], rPos[2] );

	rRot.to_axis_angle( f32X, f32Y, f32Z, f32A );
	glRotatef( -f32A / (float32)M_PI * 180.0f, f32X, f32Y, f32Z );

	rInvScaleRot.to_axis_angle( f32X, f32Y, f32Z, f32A );
	glRotatef( -f32A / (float32)M_PI * 180.0f, f32X, f32Y, f32Z );

	glScalef( rScale[0], rScale[1], rScale[2] );

	rScaleRot.to_axis_angle( f32X, f32Y, f32Z, f32A );
	glRotatef( -f32A / (float32)M_PI * 180.0f, f32X, f32Y, f32Z );

	uint32	ui32Additives = 0;

	Mesh3DC*	pMesh = pItem->get_mesh();
	if( pMesh ) {
		for( uint32 j = 0; j < pMesh->get_geomlist_count(); j++ ) {

			GeomListC*	pGList = pMesh->get_geomlist( j );

			if( !pGList )
				continue;

			if( m_i32RenderMode == RENDER_NORMAL ) {
				if( g_bMultiTexture && pGList->get_layer_count() < 3 )
					ui32Additives += render_normal_single_pass( pInterface, pGList, bDrawAdditives );
				else
					ui32Additives += render_normal( pInterface, pGList, bDrawAdditives );
			}
			else if( m_i32RenderMode == RENDER_ADDITIVE ) {
				render_additive( pInterface, pGList );
			}
			else if( m_i32RenderMode == RENDER_CARTOON ) {
				if( g_bMultiTexture && pGList->get_layer_count() < 3 )
					ui32Additives += render_normal_single_pass( pInterface, pGList, bDrawAdditives );
				else
					ui32Additives += render_normal( pInterface, pGList, bDrawAdditives );

				render_cartoon_wire( pInterface, pGList );
			}
		}
	}

	// draw childs
	for( uint32 i = 0; i < pItem->get_child_count(); i++ )
		ui32Additives += render_item( pInterface, pItem->get_child( i ), i32Frame, bDrawAdditives );

	glPopMatrix();

	return ui32Additives;
}


void
EngineEffectC::do_frame( DeviceContextC* pContext )
{
	uint32	i;
	OpenGLInterfaceC*	pInterface = (OpenGLInterfaceC*)pContext->query_interface( INTERFACE_OPENGL );

	if( !pInterface ) {
		OutputDebugString( "!!! no interface\n" );
		return;
	}


	MASImportC*	pImp = (MASImportC*)m_pAttribGizmo->get_file();

	if( !pImp )
		return;

	CameraC*	pCam = m_pAttribGizmo->get_camera( m_i32Time );

	if( !pCam )
		return;

	Vector3C	rPos, rTgt;
	float		f32Roll, f32FOV;

	pCam->eval_state( m_i32Frame );

	rPos = pCam->get_position();
	rTgt = pCam->get_target_position();
	f32Roll = pCam->get_roll();
	f32FOV = pCam->get_fov();



	pInterface->set_perspective( m_rBBox, f32FOV / M_PI * 180.0f, m_f32Aspect, pCam->get_near_plane() + 1.0f, pCam->get_far_plane() );

	// clear z-buffer
	if( m_pAttribGizmo->get_clear_z( m_i32Time ) ) {

		glClearDepth( 1.0f );
		glClear( GL_DEPTH_BUFFER_BIT );

/*		pInterface->set_ortho( m_rBBox, m_rBBox.width(), m_rBBox.height(), pCam->get_near_plane() + 1.0f, pCam->get_far_plane() );

//		glDisable( GL_BLEND );
//		glDisable( GL_TEXTURE_2D );
		glEnable( GL_DEPTH_TEST );
		glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
		glDepthFunc( GL_ALWAYS );
//		glBlendFunc( GL_ONE, GL_ZERO );

		float32	f32Far = -pCam->get_far_plane() * (1.0f - (1.0f / 65535.0f));

		glBegin( GL_QUADS );
		glVertex3f( m_rBBox[0][0], m_rBBox[0][1], f32Far );
		glVertex3f( m_rBBox[0][0], m_rBBox[1][1], f32Far );
		glVertex3f( m_rBBox[1][0], m_rBBox[1][1], f32Far );
		glVertex3f( m_rBBox[1][0], m_rBBox[0][1], f32Far );
		glEnd();

		glDepthFunc( GL_LEQUAL );

		glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );*/
	}



	glMatrixMode( GL_MODELVIEW );
	gluLookAt( rPos[0], rPos[1], rPos[2], rTgt[0], rTgt[1], rTgt[2], 0, 1, 0 );
//	glTranslatef( 0, -50, -200 );
	glRotatef( -f32Roll / M_PI * 180.0f, 0, 0, 1 );


//	glColor3ub( 255, 0, 255 );
//	glBegin( GL_LINES );

//	glVertex3f( rPos[0], rPos[1], rPos[2] );
//	glVertex3f( rTgt[0], rTgt[1], rTgt[2] );

//	gluLookAt( rPos[0], rPos[1], rPos[2], rTgt[0], rTgt[1], rTgt[2], 0, 1, 0 );

//	glEnd();

	GLfloat	f32White[] = { 1, 1, 1, 1 };

	glShadeModel( GL_SMOOTH );

	glEnable( GL_NORMALIZE  );
	glEnable( GL_DEPTH_TEST );
	glEnable( GL_BLEND );
	glEnable( GL_LIGHTING );

	for( i = 0; i < pImp->get_light_count(); i++ ) {
		LightC*	pLight = pImp->get_light( i );

		pLight->eval_state( m_i32Frame );

		Vector3C	rPos = pLight->get_position();
		ColorC		rColor = pLight->get_color();
		float32		f32Mult = pLight->get_multiplier();
		GLfloat		f32Diffuse[] = { 1, 1, 1, 1 };
		GLfloat		f32Pos[] = { 1, 1, 1, 1 };

		f32Diffuse[0] = rColor[0] * f32Mult;
		f32Diffuse[1] = rColor[1] * f32Mult;
		f32Diffuse[2] = rColor[2] * f32Mult;

		f32Pos[0] = rPos[0];
		f32Pos[1] = rPos[1];
		f32Pos[2] = rPos[2];

		float32	f32Decay = 1.0f / pLight->get_decay_start();
		float32	f32Zero = 0;
		float32	f32One = 1;

		if( pLight->get_do_decay() ) {
			glLightfv( GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION, &f32Zero );
			glLightfv( GL_LIGHT0 + i, GL_LINEAR_ATTENUATION , &f32Decay );
			glLightfv( GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION , &f32Zero );
		}
		else {
			glLightfv( GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION, &f32One );
			glLightfv( GL_LIGHT0 + i, GL_LINEAR_ATTENUATION , &f32Zero );
			glLightfv( GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION , &f32Zero );
		}

		glEnable( GL_LIGHT0 + i );
		glLightfv( GL_LIGHT0 + i, GL_DIFFUSE, f32Diffuse );
		glLightfv( GL_LIGHT0 + i, GL_SPECULAR, f32White );
		glLightfv( GL_LIGHT0 + i, GL_POSITION , f32Pos );

	}

	glDepthFunc( GL_LEQUAL );

//	glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );


	if( m_i32RenderMode == RENDER_NORMAL ) {
		glEnable( GL_DEPTH_TEST );
		glEnable( GL_ALPHA_TEST );
		glAlphaFunc( GL_GREATER, 0.1f );
	}
	else if( m_i32RenderMode == RENDER_ADDITIVE ) {
		glDisable( GL_DEPTH_TEST );
		glDisable( GL_CULL_FACE );
		glDisable( GL_ALPHA_TEST );
	}
	else if( m_i32RenderMode == RENDER_CARTOON ) {
		glEnable( GL_DEPTH_TEST );
		glEnable( GL_ALPHA_TEST );
		glAlphaFunc( GL_GREATER, 0.1f );
	}

	glMatrixMode( GL_MODELVIEW );

	uint32	ui32Additives = 0;

	for( i = 0; i < pImp->get_scenegraphitem_count(); i++ )
		ui32Additives += render_item( pInterface, pImp->get_scenegraphitem( i ), m_i32Frame, false );

	if( ui32Additives )
		for( i = 0; i < pImp->get_scenegraphitem_count(); i++ )
			render_item( pInterface, pImp->get_scenegraphitem( i ), m_i32Frame, true );

	for( i = 0; i < pImp->get_light_count(); i++ )
		glDisable( GL_LIGHT0 + i );

	glDisable( GL_CULL_FACE );
	glDisable( GL_LIGHTING );
	glDisable( GL_NORMALIZE  );
	glDisable( GL_ALPHA_TEST );

}

const Matrix2C&
EngineEffectC::get_transform_matrix() const
{
	return m_rTM;
}


enum EngineEffectChunksE {
	CHUNK_ENGINEEFFECT_BASE =			0x1000,
	CHUNK_ENGINEEFFECT_GIZMO =			0x2000,		// obsolete
	CHUNK_ENGINEEFFECT_TRANSGIZMO =		0x3000,
	CHUNK_ENGINEEFFECT_ATTRIBGIZMO =	0x4000,
};

const uint32	ENGINEEFFECT_VERSION = 1;

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

	// EffectI stuff
	pSave->begin_chunk( CHUNK_ENGINEEFFECT_BASE, ENGINEEFFECT_VERSION );
		ui32Error = EffectI::save( pSave );
	pSave->end_chunk();

	// transform
	pSave->begin_chunk( CHUNK_ENGINEEFFECT_TRANSGIZMO, ENGINEEFFECT_VERSION );
		ui32Error = m_pTransGizmo->save( pSave );
	pSave->end_chunk();

	// attribute
	pSave->begin_chunk( CHUNK_ENGINEEFFECT_ATTRIBGIZMO, ENGINEEFFECT_VERSION );
		ui32Error = m_pAttribGizmo->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_ENGINEEFFECT_BASE:
			{
				if( pLoad->get_chunk_version() == ENGINEEFFECT_VERSION )
					ui32Error = EffectI::load( pLoad );
			}
			break;

		case CHUNK_ENGINEEFFECT_GIZMO:
			{
				// old gizmo
				if( pLoad->get_chunk_version() == ENGINEEFFECT_VERSION ) {
					m_pEngineGizmo = EngineGizmoC::create_new( this, 0 );
					ui32Error = m_pEngineGizmo->load( pLoad );
				}
			}
			break;

		case CHUNK_ENGINEEFFECT_TRANSGIZMO:
			{
				if( pLoad->get_chunk_version() == ENGINEEFFECT_VERSION )
					ui32Error = m_pTransGizmo->load( pLoad );
			}
			break;

		case CHUNK_ENGINEEFFECT_ATTRIBGIZMO:
			{
				if( pLoad->get_chunk_version() == ENGINEEFFECT_VERSION )
					ui32Error = m_pAttribGizmo->load( pLoad );
			}
			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;
}


//
// Moppi ASCII import
//

MASImportC::MASImportC() :
	m_bFileRefsValid( false )
{
	// empty
}

MASImportC::MASImportC( EditableI* pOriginal ) :
	ImportableI( pOriginal ),
	m_bFileRefsValid( false )
{
	// empty
}

MASImportC::~MASImportC()
{
	if( get_original() )
		return;

	uint32	i;

	for( i = 0; i < m_rScenegraphItems.size(); i++ )
		delete m_rScenegraphItems[i];
	for( i = 0; i < m_rMeshes.size(); i++ )
		delete m_rMeshes[i];
	for( i = 0; i < m_rCameras.size(); i++ )
		delete m_rCameras[i];
	for( i = 0; i < m_rLights.size(); i++ )
		delete m_rLights[i];
}

MASImportC*
MASImportC::create_new()
{
	return new MASImportC;
}

DataBlockI*
MASImportC::create()
{
	return new MASImportC;
}

DataBlockI*
MASImportC::create( EditableI* pOriginal )
{
	return new MASImportC( pOriginal );
}

void
MASImportC::copy( EditableI* pEditable )
{
	MASImportC*	pFile = (MASImportC*)pEditable;

	m_sFileName = pFile->m_sFileName;

	// duplicate data

//	std::vector<Mesh3DC*>		m_rMeshes;
//	std::vector<ScenegraphItemI*>	m_rScenegraphItems;
//	std::vector<CameraC*>		m_rCameras;
//	std::vector<LightC*>		m_rLights;
}

void
MASImportC::restore( EditableI* pEditable )
{
	MASImportC*	pFile = (MASImportC*)pEditable;
	m_sFileName = pFile->m_sFileName;
}


// the interface

// returns the name of the file this importable refers to
const char*
MASImportC::get_filename()
{
	return m_sFileName.c_str();
}

// loads the file
bool
MASImportC::load_file( const char* szName, ImportInterfaceC* pInterface )
{
	ASELoaderC	rLdr;

	if( !rLdr.load( szName ) )
		return false;

	// delete old stuff
	uint32	i;
	for( i = 0; i < m_rScenegraphItems.size(); i++ )
		delete m_rScenegraphItems[i];
	for( i = 0; i < m_rMeshes.size(); i++ )
		delete m_rMeshes[i];
	for( i = 0; i < m_rCameras.size(); i++ )
		delete m_rCameras[i];
	for( i = 0; i < m_rLights.size(); i++ )
		delete m_rLights[i];

	rLdr.convert( m_rScenegraphItems, m_rMeshes, m_rCameras, m_rLights, pInterface );
	rLdr.get_time_parameters( m_i32FPS, m_i32TicksPerFrame, m_i32FirstFrame, m_i32LastFrame );

	m_sFileName = szName;

	m_bFileRefsValid = false;

	return true;
}

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


SuperClassIdC
MASImportC::get_super_class_id()
{
	return SUPERCLASS_IMPORT;
}

ClassIdC
MASImportC::get_class_id()
{
	return CLASS_MAS_IMPORT;
}

const char*
MASImportC::get_class_name()
{
	return "MAS 3D Scene";
}


void
MASImportC::count_file_references()
{
	m_rFileRefs.clear();

	for( uint32 i = 0; i < m_rMeshes.size(); i++ ) {
		Mesh3DC*	pMesh = m_rMeshes[i];

		for( uint32 j = 0; j < pMesh->get_geomlist_count(); j++ ) {
			GeomListC*	pList = pMesh->get_geomlist( j );
			for( uint32 k = 0; k < pList->get_layer_count(); k++ ) {
				MtlLayerC*	pLayer = pList->get_layer( k );
				if( pLayer->get_texture() )
					m_rFileRefs.push_back( pLayer->get_texture() );
			}
		}
	}

	m_bFileRefsValid = true;
}

uint32
MASImportC::get_reference_file_count()
{
	if( !m_bFileRefsValid )
		count_file_references();

	return m_rFileRefs.size();
}

FileHandleC*
MASImportC::get_reference_file( uint32 ui32Index )
{
	if( !m_bFileRefsValid )
		count_file_references();

	if( ui32Index >= 0 && ui32Index < m_rFileRefs.size() )
		return m_rFileRefs[ui32Index];

	return 0;
}


const char*
MASImportC::get_info()
{
	static char	szInfo[256];

	_snprintf( szInfo, 255, "%d meshes, %d cameras, %d lights", m_rMeshes.size(), m_rCameras.size(), m_rLights.size() );

//	_snprintf( szInfo, 255, "%d x %d x %dbit", m_i32Width, m_i32Height, m_i32Bpp );
	return szInfo;
}

ClassIdC
MASImportC::get_default_effect()
{
	return CLASS_3DENGINE_EFFECT;
}


int32
MASImportC::get_duration( TimeContextC* pTimeContext )
{
	int32	i32Length = m_i32LastFrame - m_i32FirstFrame;
	return pTimeContext->convert_fps_to_time( i32Length, m_i32FPS );
}

float32
MASImportC::get_start_label( TimeContextC* pTimeContext )
{
	return m_i32FirstFrame;
}

float32
MASImportC::get_end_label( TimeContextC* pTimeContext )
{
	return m_i32LastFrame;
}


uint32
MASImportC::get_mesh_count()
{
	return m_rMeshes.size();
}

Mesh3DC*
MASImportC::get_mesh( uint32 ui32Index )
{
	if( ui32Index >= 0 && ui32Index < m_rMeshes.size() )
		return m_rMeshes[ui32Index];
	return 0;
}

uint32
MASImportC::get_scenegraphitem_count()
{
	return m_rScenegraphItems.size();
}

ScenegraphItemI*
MASImportC::get_scenegraphitem( uint32 ui32Index )
{
	if( ui32Index >= 0 && ui32Index < m_rScenegraphItems.size() )
		return m_rScenegraphItems[ui32Index];
	return 0;
}

uint32
MASImportC::get_camera_count()
{
	return m_rCameras.size();
}

CameraC*
MASImportC::get_camera( uint32 ui32Index )
{
	if( ui32Index >= 0 && ui32Index < m_rCameras.size() )
		return m_rCameras[ui32Index];
	return 0;
}

uint32
MASImportC::get_light_count()
{
	return m_rLights.size();
}

LightC*
MASImportC::get_light( uint32 ui32Index )
{
	if( ui32Index >= 0 && ui32Index < m_rLights.size() )
		return m_rLights[ui32Index];
	return 0;
}



enum MASImportChunksE {
	CHUNK_MASIMPORT_BASE		= 0x1000,
	CHUNK_MASIMPORT_MESH3D		= 0x2000,
	CHUNK_MASIMPORT_MESHREF		= 0x3000,
	CHUNK_MASIMPORT_MESHMORPH	= 0x3001,
	CHUNK_MASIMPORT_CAMERA		= 0x4000,
	CHUNK_MASIMPORT_LIGHT		= 0x5000,
};

const uint32	MASIMPORT_VERSION = 1;


uint32
MASImportC::save( SaveC* pSave )
{
	uint32		ui32Error = IO_OK;
	std::string	sStr;

	// file base
	pSave->begin_chunk( CHUNK_MASIMPORT_BASE, MASIMPORT_VERSION );
		sStr = m_sFileName;
		if( sStr.size() > 255 )
			sStr.resize( 255 );
		ui32Error = pSave->write_str( sStr.c_str() );
		ui32Error = pSave->write( &m_i32FPS, sizeof( m_i32FPS ) );
		ui32Error = pSave->write( &m_i32TicksPerFrame, sizeof( m_i32TicksPerFrame ) );
		ui32Error = pSave->write( &m_i32FirstFrame, sizeof( m_i32FirstFrame ) );
		ui32Error = pSave->write( &m_i32LastFrame, sizeof( m_i32LastFrame ) );
	pSave->end_chunk();

	// file data
	uint32	i;

	// meshes
	for( i = 0; i < m_rMeshes.size(); i++ ) {
		pSave->begin_chunk( CHUNK_MASIMPORT_MESH3D, MASIMPORT_VERSION );
			ui32Error = pSave->write( &i, sizeof( i ) );
			m_rMeshes[i]->set_id( i );
			ui32Error = m_rMeshes[i]->save( pSave );
		pSave->end_chunk();
	}

	// cameras
	for( i = 0; i < m_rCameras.size(); i++ ) {
		pSave->begin_chunk( CHUNK_MASIMPORT_CAMERA, MASIMPORT_VERSION );
			ui32Error = m_rCameras[i]->save( pSave );
		pSave->end_chunk();
	}

	// lights
	for( i = 0; i < m_rLights.size(); i++ ) {
		pSave->begin_chunk( CHUNK_MASIMPORT_LIGHT, MASIMPORT_VERSION );
			ui32Error = m_rLights[i]->save( pSave );
		pSave->end_chunk();
	}

	uint32	ui32Id = 0;

	// scenegraph items
	for( i = 0; i < m_rScenegraphItems.size(); i++ ) {
		save_scenegraphitem( pSave, m_rScenegraphItems[i], 0xffffffff, ui32Id );
	}

	return ui32Error;
}

uint32
MASImportC::save_scenegraphitem( SaveC* pSave, ScenegraphItemI* pItem, uint32 ui32ParentId, uint32& ui32Id )
{
	uint32	ui32Error = IO_OK;
	uint32	ui32CurId = ui32Id;

	if( pItem->get_type() == CGITEM_MESHREF ) {
		pSave->begin_chunk( CHUNK_MASIMPORT_MESHREF, MASIMPORT_VERSION );
			ui32Error = pSave->write( &ui32ParentId, sizeof( ui32ParentId ) );
			ui32Error = pSave->write( &ui32Id, sizeof( ui32Id ) );
			ui32Error = pItem->save( pSave );
			ui32Id++;
		pSave->end_chunk();
	}

	for( uint32 i = 0; i < pItem->get_child_count(); i++ ) {
		ui32Error = save_scenegraphitem( pSave, pItem->get_child( i ), ui32CurId, ui32Id );
	}

	return ui32Error;
}

/*
uint32
MASImportC::load_scenegraphitem( LoadC* pLoad )
{
}
*/

uint32
MASImportC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	char	szStr[256];

	uint32	ui32Id = 0;
	uint32	ui32ParentId = 0;

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_MASIMPORT_BASE:
			{
				if( pLoad->get_chunk_version() == MASIMPORT_VERSION ) {
					ui32Error = pLoad->read_str( szStr );
					m_sFileName = szStr;
					ui32Error = pLoad->read( &m_i32FPS, sizeof( m_i32FPS ) );
					ui32Error = pLoad->read( &m_i32TicksPerFrame, sizeof( m_i32TicksPerFrame ) );
					ui32Error = pLoad->read( &m_i32FirstFrame, sizeof( m_i32FirstFrame ) );
					ui32Error = pLoad->read( &m_i32LastFrame, sizeof( m_i32LastFrame ) );
				}
			}
			break;

		case CHUNK_MASIMPORT_MESH3D:
			{
				if( pLoad->get_chunk_version() == MASIMPORT_VERSION ) {
					Mesh3DC*	pMesh = new Mesh3DC;
					ui32Error = pLoad->read( &ui32Id, sizeof( ui32Id ) );
					ui32Error = pMesh->load( pLoad );
					pMesh->set_id( ui32Id );
					m_rMeshes.push_back( pMesh );
				}
			}
			break;

		case CHUNK_MASIMPORT_CAMERA:
			{
				if( pLoad->get_chunk_version() == MASIMPORT_VERSION ) {
					CameraC*	pCam = new CameraC;
					ui32Error = pCam->load( pLoad );
					m_rCameras.push_back( pCam );
				}
			}
			break;

		case CHUNK_MASIMPORT_LIGHT:
			{
				if( pLoad->get_chunk_version() == MASIMPORT_VERSION ) {
					LightC*	pCam = new LightC;
					ui32Error = pCam->load( pLoad );
					m_rLights.push_back( pCam );
				}
			}
			break;

		case CHUNK_MASIMPORT_MESHREF:
			{
				if( pLoad->get_chunk_version() == MASIMPORT_VERSION ) {
					MeshRefC*	pRef = new MeshRefC;

					ui32Error = pLoad->read( &ui32ParentId, sizeof( ui32ParentId ) );
					ui32Error = pLoad->read( &ui32Id, sizeof( ui32Id ) );
					ui32Error = pRef->load( pLoad );
					pRef->set_id( ui32Id );

					DbgPrintf( "mesh id: %d  %d\n", 123, pRef->get_mesh_id() );

					pRef->set_mesh( find_mesh( pRef->get_mesh_id() ) );

					ScenegraphItemI*	pParent = 0;
					if( ui32ParentId != 0xffffffff ) {
						for( uint32 i = 0; i < m_rScenegraphItems.size(); i++ ) {
							pParent = find_parent( m_rScenegraphItems[i], ui32ParentId );
							if( pParent )
								break;
						}
					}

					if( pParent )
						pParent->add_child( pRef );
					else
						m_rScenegraphItems.push_back( pRef );
				}
			}
			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;
}

ScenegraphItemI*
MASImportC::find_parent( ScenegraphItemI* pItem, PajaTypes::uint32 ui32ParentId )
{
	ScenegraphItemI*	pParent = 0;

	if( pItem->get_id() == ui32ParentId )
		return pItem;

	for( uint32 i = 0; i < pItem->get_child_count(); i++ ) {
		pParent = find_parent( pItem->get_child( i ), ui32ParentId );
		if( pParent )
			return pParent;
	}

	return 0;
}

Mesh3DC*
MASImportC::find_mesh( PajaTypes::uint32 ui32MeshId )
{
	for( uint32 i = 0; i < m_rMeshes.size(); i++ ) {
		if( m_rMeshes[i]->get_id() == ui32MeshId )
			return m_rMeshes[i];
	}
	return 0;
}
