//
// EnginePlugin
//
// This example plugin demostrates:
// - loading proprietary file format,
// - how to use the file duration methods
// - how to set the value of a parameter when another parameter changes
// - how to save and load file handles
// - how to use TimeContextC
//
// See MeshC.cpp for how to save a file handle.
// See ASELoaderC.cpp for how to load images using the request_import() method of ImportInterfaceC.
// 

//#define DEMOPAJA_PUBLIC

#define WIN32_LEAN_AND_MEAN     // Exclude rarely-used stuff from

// Windows headers
#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <algorithm>
#include "glext.h"

#include "extgl.h"

// Demopaja headers
#include "DemopajaVersion.h"
#include "PajaTypes.h"
#include "ClassDescC.h"
#include "ImportableImageI.h"
#include "OpenGLViewportC.h"
#include "MASPlugin.h"
#include "FileIO.h"
#include "AutoGizmoC.h"
#include "MASLoaderC.h"
#include "noise.h"
#include "glmatrix.h"

#include "PBufferC.h"

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

using namespace MASPlugin;


#define SHADOWBUFFER_SIZE	512


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


//////////////////////////////////////////////////////////////////////////
//
//  MAS importer class descriptor.
//

MASImportDescC::MASImportDescC()
{
	// empty
}

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

void*
MASImportDescC::create()
{
	return MASImportC::create_new();
}

int32
MASImportDescC::get_classtype() const
{
	return CLASS_TYPE_FILEIMPORT;
}

SuperClassIdC
MASImportDescC::get_super_class_id() const
{
	return SUPERCLASS_IMPORT;
}

ClassIdC
MASImportDescC::get_class_id() const
{
	return CLASS_ASE_IMPORT;
}

const char*
MASImportDescC::get_name() const
{
	return "MAS 3D Mesh";
}

const char*
MASImportDescC::get_desc() const
{
	return "Importer for MAS 3D meshes";
}

const char*
MASImportDescC::get_author_name() const
{
	return "Mikko \"memon\" Mononen";
}

const char*
MASImportDescC::get_copyright_message() const
{
	return "Copyright (c) 2000 Moppi Productions";
}

const char*
MASImportDescC::get_url() const
{
	return "http://moppi.inside.org/demopaja/";
}

const char*
MASImportDescC::get_help_filename() const
{
	return "res://MAShelp.html";
}

uint32
MASImportDescC::get_required_device_driver_count() const
{
	return 1;
}

const ClassIdC&
MASImportDescC::get_required_device_driver( uint32 ui32Idx )
{
	return PajaSystem::CLASS_OPENGL_DEVICEDRIVER;
}

uint32
MASImportDescC::get_ext_count() const
{
	return 1;
}

const char*
MASImportDescC::get_ext( uint32 ui32Index ) const
{
	if( ui32Index == 0 )
		return "MAS";
	return 0;
}


//////////////////////////////////////////////////////////////////////////
//
//  ASE player effect class descriptor.
//

EngineDescC::EngineDescC()
{
	// empty
}

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

void*
EngineDescC::create()
{
	return (void*)EngineEffectC::create_new();
}

int32
EngineDescC::get_classtype() const
{
	return CLASS_TYPE_EFFECT;
}

SuperClassIdC
EngineDescC::get_super_class_id() const
{
	return SUPERCLASS_EFFECT;
}

ClassIdC
EngineDescC::get_class_id() const
{
	return CLASS_ASEPLAYER_EFFECT;
};

const char*
EngineDescC::get_name() const
{
	return "MAS Player";
}

const char*
EngineDescC::get_desc() const
{
	return "MAS Player";
}

const char*
EngineDescC::get_author_name() const
{
	return "Mikko \"memon\" Mononen";
}

const char*
EngineDescC::get_copyright_message() const
{
	return "Copyright (c) 2000 Moppi Productions";
}

const char*
EngineDescC::get_url() const
{
	return "http://moppi.inside.org/demopaja/";
}

const char*
EngineDescC::get_help_filename() const
{
	return "res://enginehelp.html";
}

uint32
EngineDescC::get_required_device_driver_count() const
{
	return 1;
}

const ClassIdC&
EngineDescC::get_required_device_driver( uint32 ui32Idx )
{
	return PajaSystem::CLASS_OPENGL_DEVICEDRIVER;
}

uint32
EngineDescC::get_ext_count() const
{
	return 0;
}

const char*
EngineDescC::get_ext( uint32 ui32Index ) const
{
	return 0;
}


//////////////////////////////////////////////////////////////////////////
//
// Global descriptors, which will be returned to the Demopaja.
//

EngineDescC		g_rEngineDesc;
MASImportDescC	g_rMASImportDesc;


#ifndef PAJAPLAYER

//////////////////////////////////////////////////////////////////////////
//
// The DLL
//

BOOL APIENTRY
DllMain( HANDLE hModule, DWORD ulReasonForCall, LPVOID lpReserved )
{
	return TRUE;
}

// Returns number of classes inside this plugin DLL.
__declspec( dllexport )
int32
get_classdesc_count()
{
#ifdef DEMOPAJA_PUBLIC
	return 2;
#else
	return 3;
#endif
}

// Returns class descriptors of the plugin classes.
__declspec( dllexport )
ClassDescC*
get_classdesc( int32 i )
{
#ifdef DEMOPAJA_PUBLIC
	if( i == 0 )
		return &g_rEngineDesc;
	else if( i == 1 )
		return &g_rMASImportDesc;
#else
	if( i == 0 )
		return &g_rEngineDesc;
	else if( i == 1 )
		return &g_rMASImportDesc;
	else if( i == 2 )
		return &g_rFlareDesc;
#endif
	return 0;
}

// Returns the API version this DLL was made with.
__declspec( dllexport )
int32
get_api_version()
{
	return DEMOPAJA_VERSION;
}

// Returns the DLL name.
__declspec( dllexport )
char*
get_dll_name()
{
	return "masplugin.dll - 3D Animation player plugin (c) 2000 memon/moppi productions";
}


#endif	// PAJAPLAYER


//////////////////////////////////////////////////////////////////////////
//
// ASE Importer class implementation.
//

MASImportC::MASImportC() :
	m_bFileRefsValid( false ),
	m_i32CurrentFrame( 0 )
{
	// init perlin noise
	noise1( 0 );
}

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

MASImportC::~MASImportC()
{
	// Return if this is a clone.
	if( get_original() )
		return;

	uint32	i;
	for( i = 0; i < m_rScenegraph.size(); i++ )
		delete m_rScenegraph[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 )
{
	// Importables which loads the data from a file does
	// not have to implement this method, since they will
	// not be duplicated.
}

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

	m_sFileName = pFile->m_sFileName;
	m_bFileRefsValid = pFile->m_bFileRefsValid;
	m_rFileRefs = pFile->m_rFileRefs;

	m_rScenegraph = pFile->m_rScenegraph;
	m_rCameras = pFile->m_rCameras;
	m_rLights = pFile->m_rLights;
	m_i32FPS = pFile->m_i32FPS;
	m_i32TicksPerFrame = pFile->m_i32TicksPerFrame;
	m_i32FirstFrame = pFile->m_i32FirstFrame;
	m_i32LastFrame = pFile->m_i32LastFrame;

}

const char*
MASImportC::get_filename()
{
	return m_sFileName.c_str();
}

void
MASImportC::set_filename( const char* szName )
{
	m_sFileName = szName;
}


bool
MASImportC::load_file( const char* szName, DemoInterfaceC* pInterface )
{
	// Sotre interface pointer
	m_pDemoInterface = pInterface;

	MASLoaderC	rLdr;

	if( !rLdr.load( pInterface->get_absolute_path( szName ), pInterface ) )
		return false;

	uint32	i, j;
	for( i = 0; i < m_rScenegraph.size(); i++ )
		delete m_rScenegraph[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.get_cameras( m_rCameras );
	rLdr.get_lights( m_rLights );
	rLdr.get_scenegraph( m_rScenegraph );
	rLdr.get_time_parameters( m_i32FPS, m_i32TicksPerFrame, m_i32FirstFrame, m_i32LastFrame );

	m_sFileName = szName;

	m_bFileRefsValid = false;


	for( i = 0; i < get_scenegraphitem_count(); i++ ) {
		ScenegraphItemI*	pItem = get_scenegraphitem( i );
		if( pItem ) {
			if( pItem->get_type() == CGITEM_MESH ) {
				MeshC*	pMesh = (MeshC*)pItem;
				
				TRACE( "* Mesh: %s\n", pMesh->get_name() );
				TRACE( "  - vert count: %d\n", pMesh->get_vert_count() );
				int32	i32FaceCount = 0;
				for( j = 0; j < pMesh->get_index_list_count(); j++ )
				{
					IndexListC*	pList = pMesh->get_index_list( j );
					i32FaceCount += pList->get_index_count();
				}
				TRACE( "  - face count: %d (%d)\n", i32FaceCount / 3, i32FaceCount );

				for( j = 0; j < pMesh->get_texcoord_list_count(); j++ )
				{
					TexCoordListC*	pList = pMesh->get_texcoord_list( j );
					TRACE( "  - texcoord list: %d (%d)\n", pList->get_texcoord_count(), pMesh->get_vert_count() );
				}

			}
		}
	}



	return true;
}

void
MASImportC::initialize( uint32 ui32Reason, PajaSystem::DemoInterfaceC* pInterface )
{
	ImportableI::initialize( ui32Reason, pInterface );
}

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

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

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

const char*
MASImportC::get_info()
{
	static char	szInfo[256];
	_snprintf( szInfo, 255, "%d meshes, %d cameras, %d lights", m_rScenegraph.size(), m_rCameras.size(), m_rLights.size() );
	return szInfo;
}

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

void
MASImportC::eval_state( int32 i32Time )
{
	if( !m_pDemoInterface )
		return;
	TimeContextC* pTimeContext = m_pDemoInterface->get_time_context();

	int32	i32Length = m_i32LastFrame - m_i32FirstFrame;

	if( i32Length > 0 ) {
		i32Length *= m_i32TicksPerFrame;
		m_i32CurrentFrame = (int32)(pTimeContext->convert_time_to_fps( i32Time, m_i32FPS ) * (float64)m_i32TicksPerFrame);
		m_i32CurrentFrame %= i32Length;
		m_i32CurrentFrame += m_i32FirstFrame * m_i32TicksPerFrame;

		while( m_i32CurrentFrame < (m_i32FirstFrame * m_i32TicksPerFrame) )
			m_i32CurrentFrame += i32Length;
		while( m_i32CurrentFrame >= (m_i32LastFrame * m_i32TicksPerFrame) )
			m_i32CurrentFrame -= i32Length;
	}
	else
		m_i32CurrentFrame = 0;
}

int32
MASImportC::get_current_frame() const
{
	return m_i32CurrentFrame;
}

//
// Returns the duration of the file. The original timecode is in frames per second (FPS).
// TimeContextC has a method to convert FPS timecode to Demopaja timecode.
// FirstFrame and LastFrame are in frames.
//
int32
MASImportC::get_duration()
{
	if( !m_pDemoInterface )
		return -1;
	TimeContextC* pTimeContext = m_pDemoInterface->get_time_context();

	int32	i32Length = m_i32LastFrame - m_i32FirstFrame;
	return (int32)pTimeContext->convert_fps_to_time( i32Length, m_i32FPS );
}


//
// The two methods defines the range which is displayed in the GUI.
// Demopaja interpoaltes values between the range.
//
float32
MASImportC::get_start_label()
{
	return (float32)m_i32FirstFrame;
}


float32
MASImportC::get_end_label()
{
	return (float32)m_i32LastFrame;
}

void
MASImportC::count_file_references()
{
	if( !m_bFileRefsValid ) {
		m_rFileRefs.clear();
		for( uint32 i = 0; i < m_rScenegraph.size(); i++ ) {
			switch( m_rScenegraph[i]->get_type() ) {
			case CGITEM_MESH:
				{
					MeshC*	pMesh = (MeshC*)m_rScenegraph[i];
					for( uint32 j = 0; j < pMesh->get_index_list_count(); j++ ) {
						IndexListC*	pList = pMesh->get_index_list( j );
						if( pList->get_diffuse_texture() )
							m_rFileRefs.push_back( pList->get_diffuse_texture() );
						if( pList->get_reflection_texture() )
							m_rFileRefs.push_back( pList->get_reflection_texture() );
					}
				}
				break;

			case CGITEM_PARTICLESYSTEM:
				{
					ParticleSystemC*	pPSys = (ParticleSystemC*)m_rScenegraph[i];

					if( pPSys->get_texture() )
						m_rFileRefs.push_back( pPSys->get_texture() );
				}
				break;

			}
		}
		m_bFileRefsValid = true;
	}
}

//
// If you loaded files using the ImportInterfaceC you must implement
// these two methods. The first returns number of files used and the second
// returns the used handles.
//
uint32
MASImportC::get_reference_file_count()
{
	count_file_references();
	return m_rFileRefs.size();
}

FileHandleC*
MASImportC::get_reference_file( PajaTypes::uint32 ui32Index )
{
	count_file_references();

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

	return 0;
}


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

ScenegraphItemI*
MASImportC::get_scenegraphitem( uint32 ui32Index )
{
	if( ui32Index >= 0 && ui32Index < m_rScenegraph.size() )
		return m_rScenegraph[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;
}

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


enum MASImportChunksE {
	CHUNK_MASIMPORT_BASE		= 0x1000,
	CHUNK_MASIMPORT_MESH		= 0x2000,
	CHUNK_MASIMPORT_BONE		= 0x2100,
	CHUNK_MASIMPORT_SHAPE		= 0x2200,
	CHUNK_MASIMPORT_PARTICLE	= 0x2300,
	CHUNK_MASIMPORT_WSMOBJECT	= 0x2400,
	CHUNK_MASIMPORT_CAMERA		= 0x3000,
	CHUNK_MASIMPORT_LIGHT		= 0x4000,
};

const uint32	MASIMPORT_VERSION = 1;


uint32
MASImportC::save( FileIO::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;

	// 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_rScenegraph.size(); i++ ) {
		ScenegraphItemI*	pItem = m_rScenegraph[i];

		if( pItem->get_type() == CGITEM_MESH ) {
			pSave->begin_chunk( CHUNK_MASIMPORT_MESH, MASIMPORT_VERSION );
				ui32Error = pItem->save( pSave );
			pSave->end_chunk();
		}
		else if( pItem->get_type() == CGITEM_BONE ) {
			pSave->begin_chunk( CHUNK_MASIMPORT_BONE, MASIMPORT_VERSION );
				ui32Error = pItem->save( pSave );
			pSave->end_chunk();
		}
		else if( pItem->get_type() == CGITEM_SHAPE ) {
			pSave->begin_chunk( CHUNK_MASIMPORT_SHAPE, MASIMPORT_VERSION );
				ui32Error = pItem->save( pSave );
			pSave->end_chunk();
		}
		else if( pItem->get_type() == CGITEM_PARTICLESYSTEM ) {
			pSave->begin_chunk( CHUNK_MASIMPORT_PARTICLE, MASIMPORT_VERSION );
				ui32Error = pItem->save( pSave );
			pSave->end_chunk();
		}
		else if( pItem->get_type() == CGITEM_WSMOBJECT ) {
			pSave->begin_chunk( CHUNK_MASIMPORT_WSMOBJECT, MASIMPORT_VERSION );
				ui32Error = pItem->save( pSave );
			pSave->end_chunk();
		}
	}

	return ui32Error;
}


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

	return 0;
}

BoneC*
MASImportC::find_bone( uint32 ui32Id )
{
	// get bone by id
	for( uint32 i = 0; i < m_rScenegraph.size(); i++ ) {
		if( m_rScenegraph[i]->get_type() == CGITEM_BONE ) {
			BoneC*	pBone = (BoneC*)m_rScenegraph[i];
			if( pBone->get_bone_id() == ui32Id )
				return pBone;
		}
	}

	return 0;
}
*/

uint32
MASImportC::load( FileIO::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_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_MESH:
			if( pLoad->get_chunk_version() == MASIMPORT_VERSION ) {
				MeshC*	pMesh = new MeshC;
				// load data
				ui32Error = pMesh->load( pLoad );
				m_rScenegraph.push_back( pMesh );
			}
			break;

		case CHUNK_MASIMPORT_BONE:
			if( pLoad->get_chunk_version() == MASIMPORT_VERSION ) {
				BoneC*	pBone = new BoneC;
				// load data
				ui32Error = pBone->load( pLoad );
				m_rScenegraph.push_back( pBone );
			}
			break;

		case CHUNK_MASIMPORT_SHAPE:
			if( pLoad->get_chunk_version() == MASIMPORT_VERSION ) {
				ShapeC*	pShape = new ShapeC;
				// load data
				ui32Error = pShape->load( pLoad );
				m_rScenegraph.push_back( pShape );
			}
			break;

		case CHUNK_MASIMPORT_PARTICLE:
			if( pLoad->get_chunk_version() == MASIMPORT_VERSION ) {
				ParticleSystemC*	pPSys = new ParticleSystemC;
				// load data
				ui32Error = pPSys->load( pLoad );
				m_rScenegraph.push_back( pPSys );
			}
			break;

		case CHUNK_MASIMPORT_WSMOBJECT:
			if( pLoad->get_chunk_version() == MASIMPORT_VERSION ) {
				WSMObjectC*	pObj = new WSMObjectC;
				// load data
				ui32Error = pObj->load( pLoad );
				m_rScenegraph.push_back( pObj );
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

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

	return ui32Error;
}



//////////////////////////////////////////////////////////////////////////
//
// The engine effect
//


uint32	EngineEffectC::m_ui32RampTexId = 0;
PBufferC*	EngineEffectC::m_pPBuffer = 0;
int32		EngineEffectC::m_i32PBufferRefCount = 0;


void
EngineEffectC::init_pbuffer()
{
	m_bPBufferRef = true;

	if( m_i32PBufferRefCount == 0 )
	{
		if( !m_pPBuffer )
			m_pPBuffer = new PBufferC;

		if( m_pPBuffer && !m_pPBuffer->is_initialised() )
			m_pPBuffer->init( SHADOWBUFFER_SIZE, SHADOWBUFFER_SIZE, PBUFFER_TEXTURE_RGBA | PBUFFER_TEXTURE_DEPTH );

		// Create alpha ramp texture
		uint8	ui8RampTextureData[256 * 4 * 4];

		for( uint32 i = 0; i < 256; i++ )
		{
			for( uint32 j = 0; j < 4; j++ )
			{
				ui8RampTextureData[(i + (256 * j)) * 4 + 0] = i;
				ui8RampTextureData[(i + (256 * j)) * 4 + 1] = 0;
				ui8RampTextureData[(i + (256 * j)) * 4 + 2] = 128;
				ui8RampTextureData[(i + (256 * j)) * 4 + 3] = 255 - i;
			}
		}

		glGenTextures( 1, &m_ui32RampTexId );
		glBindTexture( GL_TEXTURE_2D, m_ui32RampTexId );
		glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );

//		glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA8, 256, 4, 0, GL_ALPHA, GL_UNSIGNED_BYTE, ui8RampTextureData );
		glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, 256, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, ui8RampTextureData );

	}
	m_i32PBufferRefCount++;
}

void
EngineEffectC::release_pbuffer()
{
	m_i32PBufferRefCount--;
	if( m_i32PBufferRefCount == 0 ) {
		TRACE( "delete pb\n" );
		delete m_pPBuffer;
		m_pPBuffer = 0;

		if( m_ui32RampTexId )
		{
			glDeleteTextures( 1, &m_ui32RampTexId );
			m_ui32RampTexId = 0;
		}
	}
}


EngineEffectC::EngineEffectC() :
	m_bPBufferRef( false )
{


	// init perlin noise
	noise1( 0 );

	//
	// Create Transform gizmo.
	//
	m_pTraGizmo = AutoGizmoC::create_new( this, "Transform", ID_GIZMO_TRANS );

	m_pTraGizmo->add_parameter(	ParamVector2C::create_new( m_pTraGizmo, "Position", Vector2C(), ID_TRANSFORM_POS,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_ABS_POSITION, PARAM_ANIMATABLE ) );
	
	m_pTraGizmo->add_parameter(	ParamVector2C::create_new( m_pTraGizmo, "Scale", Vector2C( 1, 1 ), ID_TRANSFORM_SCALE,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 0.01f ) );


	//
	// Create Attributes gizmo.
	//
	m_pAttGizmo = AutoGizmoC::create_new( this, "Attributes", ID_GIZMO_ATTRIB );


	m_pAttGizmo->add_parameter( ParamVector2C::create_new( m_pAttGizmo, "Size", Vector2C( 320, 200 ), ID_ATTRIBUTE_SIZE,
		PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 1 ) );

	m_pAttGizmo->add_parameter( ParamIntC::create_new( m_pAttGizmo, "Camera", 0, ID_ATTRIBUTE_CAMERA,
		PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 1 ) );

	ParamIntC*	pParamClear = ParamIntC::create_new( m_pAttGizmo, "Clear", 1, ID_ATTRIBUTE_CLEAR,
		PARAM_STYLE_COMBOBOX, PARAM_NOT_ANIMATABLE, 0, 3 );
	pParamClear->add_label( 0, "Off" );
	pParamClear->add_label( 1, "Z-Buffer" );
	pParamClear->add_label( 2, "Color Buffer" );
	pParamClear->add_label( 3, "Both" );
	m_pAttGizmo->add_parameter( pParamClear );

	m_pAttGizmo->add_parameter( ParamColorC::create_new( m_pAttGizmo, "Clear Color", ColorC( 0, 0, 0, 1 ), ID_ATTRIBUTE_CLEARCOLOR,
		PARAM_STYLE_COLORPICKER_RGBA, PARAM_ANIMATABLE ) );

	m_pAttGizmo->add_parameter( ParamFileC::create_new( m_pAttGizmo, "Scene", NULL_SUPERCLASS, CLASS_ASE_IMPORT, ID_ATTRIBUTE_FILE ) );

	//
	// Create Time gizmo.
	//
	m_pTimeGizmo = AutoGizmoC::create_new( this, "Timing", ID_GIZMO_TIME );
	
//	m_pTimeGizmo->add_parameter( ParamFloatC::create_new( m_pTimeGizmo, "FPS", 0, ID_TIME_FPS,
//						PARAM_STYLE_EDITBOX, PARAM_NOT_ANIMATABLE, 0, 1000.0f, 1.0f ) );

	m_pTimeGizmo->add_parameter( ParamFloatC::create_new( m_pTimeGizmo, "Frame", 0, ID_TIME_FRAME,
						PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0, 0, 1.0f ) );

	//
	// Create Toon gizmo.
	//
	m_pToonGizmo = AutoGizmoC::create_new( this, "Toon Renderer", ID_GIZMO_TOON );


	m_pToonGizmo->add_parameter( ParamFloatC::create_new( m_pToonGizmo, "Outer Wire Size", 4.0f, ID_TOON_WIRE_OUTERSIZE,
						PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0.5f, 15.0f, 0.1f ) );

	m_pToonGizmo->add_parameter( ParamFloatC::create_new( m_pToonGizmo, "Inner Wire Size", 1.5f, ID_TOON_WIRE_INNERSIZE,
						PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0.5f, 15.0f, 0.1f ) );

	m_pToonGizmo->add_parameter( ParamColorC::create_new( m_pToonGizmo, "Wire Color", ColorC( 0, 0, 0, 1 ), ID_TOON_WIRE_COLOR,
		PARAM_STYLE_COLORPICKER_RGBA, PARAM_ANIMATABLE ) );
		
	//
	// Create Fog gizmo.
	//
	m_pFogGizmo = AutoGizmoC::create_new( this, "Fog", ID_GIZMO_FOG );

	ParamIntC*	pParamMode = ParamIntC::create_new( m_pFogGizmo, "Mode", 0, ID_FOG_MODE,
		PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 3 );
	pParamMode->add_label( 0, "Off" );
	pParamMode->add_label( 1, "Linear" );
	pParamMode->add_label( 2, "Exp" );
	pParamMode->add_label( 3, "Exp2" );
	m_pFogGizmo->add_parameter( pParamMode );

	m_pFogGizmo->add_parameter( ParamFloatC::create_new( m_pFogGizmo, "Start Dist", 50.0f, ID_FOG_START,
						PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0, 0, 1 ) );

	m_pFogGizmo->add_parameter( ParamFloatC::create_new( m_pFogGizmo, "End Dist", 100.0f, ID_FOG_END,
						PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0, 0, 1 ) );

	m_pFogGizmo->add_parameter( ParamFloatC::create_new( m_pFogGizmo, "Density", 1.0f, ID_FOG_DENSITY,
						PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0, 0, 0.001f ) );

	//
	// Create line gizmo
	//

	m_pLineGizmo = AutoGizmoC::create_new( this, "Line", ID_GIZMO_LINE );


	m_pLineGizmo->add_parameter( ParamFloatC::create_new( m_pLineGizmo, "Width", 1.0f, ID_LINE_WIDTH,
						PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0.5f, 15.0f, 0.1f ) );

	ParamIntC*	pParamSmooth = ParamIntC::create_new( m_pLineGizmo, "Smoothing", 0, ID_LINE_SMOOTH,
		PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 1 );
	pParamSmooth->add_label( 0, "Off" );
	pParamSmooth->add_label( 1, "On" );
	m_pLineGizmo->add_parameter( pParamSmooth );


	//
	// Create DOF gizmo.
	//
	m_pDOFGizmo = AutoGizmoC::create_new( this, "DOF", ID_GIZMO_DOF );

	ParamIntC*	pParamOnOff = ParamIntC::create_new( m_pDOFGizmo, "Mode", 0, ID_DOF_MODE,
		PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 1 );
	pParamOnOff->add_label( 0, "Off" );
	pParamOnOff->add_label( 1, "On" );
	m_pDOFGizmo->add_parameter( pParamOnOff );

	m_pDOFGizmo->add_parameter( ParamFloatC::create_new( m_pDOFGizmo, "Start Dist", 50.0f, ID_DOF_START,
						PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0, 0, 1 ) );

	m_pDOFGizmo->add_parameter( ParamFloatC::create_new( m_pDOFGizmo, "End Dist", 100.0f, ID_DOF_END,
						PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0, 0, 1 ) );

}

EngineEffectC::EngineEffectC( EditableI* pOriginal ) :
	EffectI( pOriginal ),
	m_pTraGizmo( 0 ),
	m_pAttGizmo( 0 ),
	m_pTimeGizmo( 0 ),
	m_pToonGizmo( 0 ),
	m_pFogGizmo( 0 ),
	m_pLineGizmo( 0 ),
	m_pDOFGizmo( 0 ),
	m_bPBufferRef( false )
{
}

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

	if( m_bPBufferRef )
		release_pbuffer();

	// Release gizmos.
	m_pTraGizmo->release();
	m_pAttGizmo->release();
	m_pTimeGizmo->release();
	m_pToonGizmo->release();
	m_pFogGizmo->release();
	m_pLineGizmo->release();
	m_pDOFGizmo->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_pTraGizmo->copy( pEffect->m_pTraGizmo );
	m_pAttGizmo->copy( pEffect->m_pAttGizmo );
	m_pTimeGizmo->copy( pEffect->m_pTimeGizmo );
	m_pToonGizmo->copy( pEffect->m_pToonGizmo );
	m_pFogGizmo->copy( pEffect->m_pFogGizmo );
	m_pLineGizmo->copy( pEffect->m_pLineGizmo );
	m_pDOFGizmo->copy( pEffect->m_pDOFGizmo );
}

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

	EngineEffectC*	pEffect = (EngineEffectC*)pEditable;
	m_pTraGizmo = pEffect->m_pTraGizmo;
	m_pAttGizmo = pEffect->m_pAttGizmo;
	m_pTimeGizmo = pEffect->m_pTimeGizmo;
	m_pToonGizmo = pEffect->m_pToonGizmo;
	m_pFogGizmo = pEffect->m_pFogGizmo;
	m_pLineGizmo = pEffect->m_pLineGizmo;
	m_pDOFGizmo = pEffect->m_pDOFGizmo;
}

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

GizmoI*
EngineEffectC::get_gizmo( int32 i32Index )
{
	// Returns specified gizmo.
	// Since the ID's are zero based, we can use them as indices.
	switch( i32Index ) {
	case ID_GIZMO_TRANS:
		return m_pTraGizmo;
	case ID_GIZMO_ATTRIB:
		return m_pAttGizmo;
	case ID_GIZMO_TIME:
		return m_pTimeGizmo;
	case ID_GIZMO_TOON:
		return m_pToonGizmo;
	case ID_GIZMO_FOG:
		return m_pFogGizmo;
	case ID_GIZMO_LINE:
		return m_pLineGizmo;
	case ID_GIZMO_DOF:
		return m_pDOFGizmo;
	}

	return 0;
}

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

const char*
EngineEffectC::get_class_name()
{
	return "MAS Player";
}

void
EngineEffectC::set_default_file( int32 i32Time, FileHandleC* pHandle )
{
	// Sets the default file.

	// Get the file parameter.
	ParamFileC*	pParam = (ParamFileC*)m_pAttGizmo->get_parameter_by_id( ID_ATTRIBUTE_FILE );

	// Begin Undo block.
	UndoC*	pOldUndo = pParam->begin_editing( get_undo() );
		// Set the file.
		pParam->set_file( i32Time, pHandle );
	// End undo block.
	pParam->end_editing( pOldUndo );

}

ParamI*
EngineEffectC::get_default_param( int32 i32Param )
{

	if( i32Param == DEFAULT_PARAM_POSITION )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_POS );
	else if( i32Param == DEFAULT_PARAM_SCALE )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_SCALE );

	return 0;
}

uint32
EngineEffectC::update_notify( EditableI* pCaller )
{
	ParamI*	pParam = 0;
	GizmoI*	pGizmo = 0;
	if( pCaller->get_base_class_id() == BASECLASS_PARAMETER )
		pParam = (ParamI*)pCaller;
	if( pParam )
		pGizmo = pParam->get_parent();

	if( pParam && pGizmo && pGizmo->get_id() == ID_GIZMO_ATTRIB && pParam->get_id() == ID_ATTRIBUTE_FILE )
	{
		// The file has changed. Change Time.
		ParamFileC*		pFile = (ParamFileC*)m_pAttGizmo->get_parameter_by_id( ID_ATTRIBUTE_FILE );
		ParamFloatC*	pFrame = (ParamFloatC*)m_pTimeGizmo->get_parameter_by_id( ID_TIME_FRAME );
		ParamIntC*		pCamera = (ParamIntC*)m_pAttGizmo->get_parameter_by_id( ID_ATTRIBUTE_CAMERA );

		UndoC*		pOldUndo;
		UndoC*		pUndo = pFile->get_undo();

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

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

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

			// Update camera labels.
			pOldUndo = pCamera->begin_editing( pUndo );
			pCamera->clear_labels();
			for( uint32 i = 0; i < pImp->get_camera_count(); i++ ) {
				CameraC*	pCam = pImp->get_camera( i );
				if( !pCam )
					continue;
				pCamera->add_label( i, pCam->get_name() );
			}
			pCamera->set_min_max( 0, pImp->get_camera_count() - 1 );
			pCamera->end_editing( pOldUndo );
		}
	}

	// Relay update
	return EffectI::update_notify( pCaller );
}

void
EngineEffectC::initialize( uint32 ui32Reason, DemoInterfaceC* pInterface )
{
	EffectI::initialize( ui32Reason, pInterface );

	if( ui32Reason == INIT_INITIAL_UPDATE ) {
		init_gl_extensions();
		// clone constructor. initialize only.
		init_pbuffer();
	}
}


void
EngineEffectC::calc_frustum_planes( float32 f32FOV, float32 f32Aspect, Vector3C* pPlanes )
{
	//
	// build clipping planes
	//
	float32	f32XMin, f32XMax, f32YMin, f32YMax;
	
	f32XMax = 1 * (float32)tan( (f32FOV ) / 2.0 );
	f32XMin = -f32XMax;

	f32YMin = f32XMin * f32Aspect;
	f32YMax = f32XMax * f32Aspect;


	//
	// construct planes
	//
	//           1
	//          o
	//        / :\
	//      /   : \
	//    /     :  \
	// 0 o---___:   \
	//   |      ---__\
	//   |    2 o... _x  <---camera pos (0, 0, 0)
	//   |     .  _--
	//   |   . _--
	//   | ._--
	// 3 o-
	//


	Vector3C	rFrustumVerts[4];

	rFrustumVerts[0] = Vector3C( f32XMin, f32YMax, -1 );
	rFrustumVerts[1] = Vector3C( f32XMax, f32YMax, -1 );
	rFrustumVerts[2] = Vector3C( f32XMax, f32YMin, -1 );
	rFrustumVerts[3] = Vector3C( f32XMin, f32YMin, -1 );

	// left
	pPlanes[0] = rFrustumVerts[3].cross( rFrustumVerts[0] ).normalize();

	// right
	pPlanes[1] = rFrustumVerts[0].cross( rFrustumVerts[1] ).normalize();

	// top
	pPlanes[2] = rFrustumVerts[1].cross( rFrustumVerts[2] ).normalize();

	// bottom
	pPlanes[3] = rFrustumVerts[2].cross( rFrustumVerts[3] ).normalize();
}


void
EngineEffectC::eval_state( int32 i32Time )
{
	if( !m_pDemoInterface )
		return;

	if( !g_bMultiTexture )
		return;

//	TRACE( "Engine {\n" );


	DeviceContextC* pContext = m_pDemoInterface->get_device_context();
	TimeContextC* pTimeContext = m_pDemoInterface->get_time_context();

	Matrix2C	rPosMat, rScaleMat, rPivotMat;
	Vector2C	rScale;
	Vector2C	rPos;

	// Get parameters which affect the transformation.
	((ParamVector2C*)m_pTraGizmo->get_parameter( ID_TRANSFORM_POS ))->get_val( i32Time, rPos );
	((ParamVector2C*)m_pTraGizmo->get_parameter( ID_TRANSFORM_SCALE ))->get_val( i32Time, rScale );

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

	m_rTM = rScaleMat * rPosMat;

	//
	// calc bounding box
	//
	Vector2C	rSize;
	((ParamVector2C*)m_pAttGizmo->get_parameter_by_id( ID_ATTRIBUTE_SIZE ))->get_val( i32Time, rSize );
	rSize *= 0.5f;

	Vector2C	rMin, rMax;

	rMin = -rSize;
	rMax = rSize;

	rMin *= m_rTM;
	rMax *= m_rTM;

	m_rBBox[0] = rMin;
	m_rBBox[1] = rMax;
	m_rBBox.normalize();


	ColorC		rClearColor( 0, 0, 0, 1 );
	int32			i32ClearFlags = 0;
	CameraC*	pCam = 0;

//	float32		m_f32OuterWireSize = 1.0f;
//	float32		m_f32InnerWireSize = 1.0f;
//	ColorC		m_rWireColor( 0, 0, 0, 1 );

	int32			i32FogMode = 0;
	float32		f32FogStart = 0;
	float32		f32FogEnd = 100.0f;
	float32		f32FogDensity = 1.0f;

	int32			i32DOFAlpha = 0;
	float32		f32DOFStart = 0.0f;
	float32		f32DOFEnd = 0.0f;

//	int32			m_i32LineSmooth = 0;
//	float32		m_f32LineWidth = 1;

	pCam = 0;

	//
	// Calculate current frame.
	//
	MASImportC*		pImp = 0;
	FileHandleC*	pHandle;
	int32			i32FileTime;

	((ParamFileC*)m_pAttGizmo->get_parameter_by_id( ID_ATTRIBUTE_FILE ))->get_file( i32Time, pHandle, i32FileTime );

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

		// If "Frame" parameter is animated, use it instead of FPS setting.
		ParamFloatC*	pParamFrame = (ParamFloatC*)m_pTimeGizmo->get_parameter_by_id( ID_TIME_FRAME );
		ControllerC*	pCont = pParamFrame->get_controller();
		if( pCont && pCont->get_key_count() ) {
			float32	f32Frame;
			pParamFrame->get_val( i32Time, f32Frame );

			float32	f32StartFrame = pImp->get_start_label();
			float32	f32EndFrame = pImp->get_end_label();

			if( (f32EndFrame - f32StartFrame) >= 0 ) {
				i32FileTime = (int32)(((f32Frame - f32StartFrame) / (f32EndFrame - f32StartFrame)) * pImp->get_duration());
			}
		}

		pImp->eval_state( i32FileTime );
	
		// get camera
		int32	i32Val;
		((ParamIntC*)m_pAttGizmo->get_parameter_by_id( ID_ATTRIBUTE_CAMERA ))->get_val( i32Time, i32Val );
		pCam = pImp->get_camera( i32Val );
	}

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

	((ParamFloatC*)m_pToonGizmo->get_parameter( ID_TOON_WIRE_OUTERSIZE ))->get_val( i32Time, m_f32OuterWireSize );
	((ParamFloatC*)m_pToonGizmo->get_parameter( ID_TOON_WIRE_INNERSIZE ))->get_val( i32Time, m_f32InnerWireSize );
	((ParamColorC*)m_pToonGizmo->get_parameter( ID_TOON_WIRE_COLOR ))->get_val( i32Time, m_rWireColor );

	((ParamColorC*)m_pAttGizmo->get_parameter_by_id( ID_ATTRIBUTE_CLEARCOLOR ))->get_val( i32Time, rClearColor );
	((ParamIntC*)m_pAttGizmo->get_parameter_by_id( ID_ATTRIBUTE_CLEAR ))->get_val( i32Time, i32ClearFlags );

	((ParamIntC*)m_pFogGizmo->get_parameter( ID_FOG_MODE ))->get_val( i32Time, i32FogMode );
	((ParamFloatC*)m_pFogGizmo->get_parameter( ID_FOG_START ))->get_val( i32Time, f32FogStart );
	((ParamFloatC*)m_pFogGizmo->get_parameter( ID_FOG_END ))->get_val( i32Time, f32FogEnd );
	((ParamFloatC*)m_pFogGizmo->get_parameter( ID_FOG_DENSITY ))->get_val( i32Time, f32FogDensity );

	((ParamIntC*)m_pLineGizmo->get_parameter( ID_LINE_SMOOTH ))->get_val( i32Time, m_i32LineSmooth );
	((ParamFloatC*)m_pLineGizmo->get_parameter( ID_LINE_WIDTH ))->get_val( i32Time, m_f32LineWidth );

	((ParamIntC*)m_pDOFGizmo->get_parameter_by_id( ID_DOF_MODE ))->get_val( i32Time, i32DOFAlpha );
	((ParamFloatC*)m_pDOFGizmo->get_parameter( ID_DOF_START ))->get_val( i32Time, f32DOFStart );
	((ParamFloatC*)m_pDOFGizmo->get_parameter( ID_DOF_END ))->get_val( i32Time, f32DOFEnd );

	//---------------------------------------------------------------------------------------------------
	//
	// Draw effect
	//

	uint32	i, j;
	// Get the OpenGL device.
	OpenGLDeviceC*	pDevice = (OpenGLDeviceC*)pContext->query_interface( CLASS_OPENGL_DEVICEDRIVER );
	if( !pDevice )
		return;

	// Get the OpenGL viewport.
	OpenGLViewportC*	pViewport = (OpenGLViewportC*)pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
	if( !pViewport )
		return;

	RenderContextS	rRenderContext;
	rRenderContext.m_i32Time = i32Time;
	rRenderContext.m_pContext = pContext;
	rRenderContext.m_pTimeContext = pTimeContext;
	rRenderContext.m_pDevice = pDevice;
	rRenderContext.m_pInterface = pViewport;

	//
	// setup camera
	//

	if( !pImp )
		return;

	if( !pCam )
		return;


	int32	i32Frame = pImp->get_current_frame();

	pCam->eval_state( i32Frame );

	Vector3C	rCamPos, rCamTgt;
	float		f32FOV;
	float32		f32Roll;

	rCamPos = pCam->get_position();
	rCamTgt = pCam->get_target_position();
	f32FOV = pCam->get_fov();
	f32Roll = pCam->get_roll();

	float32	f32CamNear = pCam->get_near_plane();
	if( f32CamNear < 1.0f )
		f32CamNear = 1.0f;
	float32	f32CamFar = pCam->get_far_plane();

	pViewport->set_perspective( m_rBBox, f32FOV / (float32)M_PI * (float32)180.0f, f32Aspect, f32CamNear, f32CamFar );

	glEnable( GL_DEPTH_TEST );
	glDepthMask( GL_TRUE );
	glDepthFunc( GL_LEQUAL );


	if( i32ClearFlags == 1 )
	{
		// Bug... cannot clear z-buffer on pbuffers when scissors are enabled.
		glDisable( GL_SCISSOR_TEST );
		glClearDepth( 1.0f );
		glClear( GL_DEPTH_BUFFER_BIT );
		glEnable( GL_SCISSOR_TEST );
	}
	else if( i32ClearFlags == 2 ) {
		glClearColor( rClearColor[0], rClearColor[1], rClearColor[2], rClearColor[3] );
		glClear( GL_COLOR_BUFFER_BIT );
	}
	else if( i32ClearFlags == 3 ) {
		glDisable( GL_SCISSOR_TEST );
		glClearDepth( 1.0f );
		glClear( GL_DEPTH_BUFFER_BIT );
		glEnable( GL_SCISSOR_TEST );

		glClearColor( rClearColor[0], rClearColor[1], rClearColor[2], rClearColor[3] );
		glClear( GL_COLOR_BUFFER_BIT );
	}


	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	glRotatef( -f32Roll / (float32)M_PI * 180.0f, 0, 0, 1 );
	gluLookAt( rCamPos[0], rCamPos[1], rCamPos[2], rCamTgt[0], rCamTgt[1], rCamTgt[2], 0, 1, 0 );

	GLfloat		f32CamTM[16];
	Matrix3C	rCamTM;

	glGetFloatv( GL_MODELVIEW_MATRIX , f32CamTM );

	for( i = 0; i < 4; i++ )
		for( j = 0; j < 3; j++ )
			rCamTM[i][j] = f32CamTM[i * 4 + j];

	//
	// Setup lights
	//

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

	glShadeModel( GL_SMOOTH );

	glEnable( GL_NORMALIZE  );
	glEnable( GL_BLEND );
	glEnable( GL_LIGHTING );
	glDisable( GL_ALPHA_TEST );
	glCullFace( GL_BACK );
	glEnable( GL_CULL_FACE );
	glLightModeli( GL_LIGHT_MODEL_TWO_SIDE , 0 );
	glBlendFunc( GL_ONE, GL_ZERO );


	int32	i32BasicLightCount = 0;
	int32	i32ComplexLightCount = 0;

	int32	i32ActiveLight = 0;

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

		pLight->eval_state( i32Frame );

		// skip projected texture stuff
		if( pLight->get_projection_texture() || pLight->get_cast_shadows() ) {
			i32ComplexLightCount++;
			continue;
		}
		else {
			i32BasicLightCount++;
		}

		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];

		glEnable( GL_LIGHT0 + i32ActiveLight );

		if( pLight->get_decay_type() == DECAY_NONE ) {
			glLightf( GL_LIGHT0 + i32ActiveLight, GL_CONSTANT_ATTENUATION, 1 );
			glLightf( GL_LIGHT0 + i32ActiveLight, GL_LINEAR_ATTENUATION, 0 );
			glLightf( GL_LIGHT0 + i32ActiveLight, GL_QUADRATIC_ATTENUATION, 0 );
		}
		else if( pLight->get_decay_type() == DECAY_INVERSE ) {
			glLightf( GL_LIGHT0 + i32ActiveLight, GL_CONSTANT_ATTENUATION, 0 );
			glLightf( GL_LIGHT0 + i32ActiveLight, GL_LINEAR_ATTENUATION, 1.0f / pLight->get_decay() );
			glLightf( GL_LIGHT0 + i32ActiveLight, GL_QUADRATIC_ATTENUATION, 0 );
		}
		else if( pLight->get_decay_type() == DECAY_INVERSE_SQR ) {
			glLightf( GL_LIGHT0 + i32ActiveLight, GL_CONSTANT_ATTENUATION, 0 );
			glLightf( GL_LIGHT0 + i32ActiveLight, GL_LINEAR_ATTENUATION, 0 );
			glLightf( GL_LIGHT0 + i32ActiveLight, GL_QUADRATIC_ATTENUATION, 1.0f / pLight->get_decay() );
		}

		if( pLight->get_light_type() == LIGHT_OMNI ) {
			// omni
			glLightf( GL_LIGHT0 + i32ActiveLight, GL_SPOT_EXPONENT, 0 );
			glLightf( GL_LIGHT0 + i32ActiveLight, GL_SPOT_CUTOFF, 180 );
		}
		else {
			// target
			float32	f32HotSpot = pLight->get_hotspot() / 2.0f;
			float32	f32FallOff = pLight->get_falloff() / 2.0f;

			if( f32HotSpot < 0 )
				f32HotSpot = 0;
			if( f32HotSpot > 90 )
				f32HotSpot = 90;

			if( f32FallOff < 0 )
				f32FallOff = 0;
			if( f32FallOff > 90 )
				f32FallOff = 90;

			glLightf( GL_LIGHT0 + i32ActiveLight, GL_SPOT_EXPONENT, (1.0f - f32HotSpot / f32FallOff) * 25.0f );
			glLightf( GL_LIGHT0 + i32ActiveLight, GL_SPOT_CUTOFF, f32FallOff );

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

			Vector3C	rDir = pLight->get_target() - pLight->get_position();
			rDir = rDir.normalize();

			f32Tgt[0] = rDir[0];
			f32Tgt[1] = rDir[1];
			f32Tgt[2] = rDir[2];

			glLightfv( GL_LIGHT0 + i32ActiveLight, GL_SPOT_DIRECTION, f32Tgt );
		}

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

		i32ActiveLight++;
	}

	glEnable( GL_LIGHTING );


	int32	i32ParticlePasses = i32ComplexLightCount + 1;

	if( g_bSeparateSpec )
		glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR );

	// setup fog
	if( i32FogMode != FOG_MODE_OFF ) {
		glEnable( GL_FOG );

		switch( i32FogMode ) {
		case FOG_MODE_LINEAR:
			glFogi( GL_FOG_MODE, GL_LINEAR );
			break;
		case FOG_MODE_EXP:
			glFogi( GL_FOG_MODE, GL_EXP );
			break;
		case FOG_MODE_EXP2:
			glFogi( GL_FOG_MODE, GL_EXP2 );
			break;
		}

		glFogf( GL_FOG_START, f32FogStart );
		glFogf( GL_FOG_END, f32FogEnd );
		glFogf( GL_FOG_DENSITY, f32FogDensity );

		glFogfv( GL_FOG_COLOR, rClearColor );
	}
	else {
		glDisable( GL_FOG );
	}

	// eval transformation
	for( i = 0; i < pImp->get_scenegraphitem_count(); i++ ) {
		ScenegraphItemI*	pItem = pImp->get_scenegraphitem( i );
		pItem->eval_state( i32Frame );
	}

	// eval geom
	for( i = 0; i < pImp->get_scenegraphitem_count(); i++ ) {
		ScenegraphItemI*	pItem = pImp->get_scenegraphitem( i );
		pItem->eval_geom( i32Frame );
	}


	//
	// sort objects
	//

	std::vector<CGItemSortS>	rSortList;

	for( i = 0; i < pImp->get_scenegraphitem_count(); i++ ) {
		ScenegraphItemI*	pItem = pImp->get_scenegraphitem( i );
		if( pItem && !pItem->get_hidden() ) {
			if( pItem->get_type() == CGITEM_MESH ) {
				MeshC*	pMesh = (MeshC*)pItem;
				CGItemSortS	rItem;
				rItem.m_pItem = pItem;
				rItem.m_rPos = pMesh->get_tm()[3] * rCamTM;
				rSortList.push_back( rItem );
			}
			else if( pItem->get_type() == CGITEM_PARTICLESYSTEM ) {
				ParticleSystemC*	pPSys = (ParticleSystemC*)pItem;
				CGItemSortS	rItem;
				rItem.m_pItem = pItem;
				rItem.m_rPos = pPSys->get_tm()[3] * rCamTM;
				rSortList.push_back( rItem );
			}
			else if( pItem->get_type() == CGITEM_WSMOBJECT ) {
				WSMObjectC*	pObj = (WSMObjectC*)pItem;
				CGItemSortS	rItem;
				rItem.m_pItem = pItem;
				rItem.m_rPos = pObj->get_tm()[3] * rCamTM;
				rSortList.push_back( rItem );
			}
			else if( pItem->get_type() == CGITEM_SHAPE ) {
				ShapeC*	pShape = (ShapeC*)pItem;
				CGItemSortS	rItem;
				rItem.m_pItem = pItem;
				rItem.m_rPos = pShape->get_tm()[3] * rCamTM;
				rSortList.push_back( rItem );
			}
		}
	}

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


	//
	// build clipping planes
	//
	Vector3C	rPlanes[4];
	calc_frustum_planes( f32FOV, f32Aspect, rPlanes );

	// draw bones
/*	for( i = 0; i < pImp->get_scenegraphitem_count(); i++ ) {
		ScenegraphItemI*	pItem = pImp->get_scenegraphitem( i );
		if( pItem->get_type() == CGITEM_BONE ) {
			render_bone( &rRenderContext, (BoneC*)pItem );
		}
	}*/


	//
	// draw solid objects first
	//

	for( i = 0; i < rSortList.size(); i++ ) {
		if( rSortList[i].m_pItem->get_type() == CGITEM_MESH ) {
			MeshC*	pMesh = (MeshC*)rSortList[i].m_pItem;
			if( pMesh->get_visible() && !pMesh->is_transparent() && !pMesh->get_additive() )
				rSortList[i].m_bCamVisible = render_mesh( &rRenderContext, pMesh, rCamTM, rPlanes );
		}
		else if( rSortList[i].m_pItem->get_type() == CGITEM_SHAPE  ) {
			ShapeC*	pShape = (ShapeC*)rSortList[i].m_pItem;
			if( pShape->get_visible() && !pShape->is_transparent() && !pShape->get_additive()  )
				rSortList[i].m_bCamVisible = render_shape( &rRenderContext, pShape, rCamTM, rPlanes );
		}
	}

	//
	// then transparent
	//

	for( i = 0; i < rSortList.size(); i++ ) {
		if( rSortList[i].m_pItem->get_type() == CGITEM_MESH ) {
			MeshC*	pMesh = (MeshC*)rSortList[i].m_pItem;
			if( pMesh->get_visible() && (pMesh->is_transparent() || pMesh->get_additive()) )
				rSortList[i].m_bCamVisible = render_mesh( &rRenderContext, pMesh, rCamTM, rPlanes );
		}
		else if( rSortList[i].m_pItem->get_type() == CGITEM_SHAPE  ) {
			ShapeC*	pShape = (ShapeC*)rSortList[i].m_pItem;
			if( pShape->get_visible() && (pShape->is_transparent() || pShape->get_additive()) )
				rSortList[i].m_bCamVisible = render_shape( &rRenderContext, pShape, rCamTM, rPlanes );
		}
	}

	i32ActiveLight = 0;
	for( i = 0; i < pImp->get_light_count(); i++ )
	{
		LightC*	pLight = pImp->get_light( i );
		// skip projected texture stuff
		if( pLight->get_projection_texture() || pLight->get_cast_shadows() )
			continue;

		glDisable( GL_LIGHT0 + i32ActiveLight );

		i32ActiveLight++;
	}


	glDisable( GL_LIGHTING );

	// setup fog to fade to black
	if( i32FogMode != FOG_MODE_OFF ) {
		float32	f32Black[4] = { 0, 0, 0, 1 };
		glFogfv( GL_FOG_COLOR, f32Black );
	}


	//
	// draw projected textures
	//


	glActiveTextureARB( GL_TEXTURE0_ARB );
	glClientActiveTextureARB( GL_TEXTURE0_ARB );
	glBindTexture( GL_TEXTURE_2D, 0 );
	glDisable( GL_TEXTURE_2D );
	glDisable( GL_TEXTURE_GEN_S );
	glDisable( GL_TEXTURE_GEN_T );
	glDisable( GL_TEXTURE_GEN_R );
	glDisable( GL_TEXTURE_GEN_Q );
	glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

	glActiveTextureARB( GL_TEXTURE1_ARB );
	glClientActiveTextureARB( GL_TEXTURE1_ARB );
	glBindTexture( GL_TEXTURE_2D, 0 );
	glDisable( GL_TEXTURE_2D );
	glDisable( GL_TEXTURE_GEN_S );
	glDisable( GL_TEXTURE_GEN_T );
	glDisable( GL_TEXTURE_GEN_R );
	glDisable( GL_TEXTURE_GEN_Q );
	glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

	
	uint32	ui32SpotTexture = GL_TEXTURE2_ARB;
	uint32	ui32ShadowTexture = GL_TEXTURE3_ARB;


//	TRACE( " - base done\n" );


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

		// skip _non-projected texture stuff_
		if( !(pLight->get_projection_texture() || pLight->get_cast_shadows()) )
			continue;

//		TRACE( " - SPOT '%s' {\n", pLight->get_name() );


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

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

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

		glEnable( GL_LIGHTING );

		glEnable( GL_LIGHT0 );

		if( pLight->get_decay_type() == DECAY_INVERSE ) {
			glLightf( GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0 );
			glLightf( GL_LIGHT0, GL_LINEAR_ATTENUATION, 1.0f / pLight->get_decay() );
			glLightf( GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0 );
		}
		else if( pLight->get_decay_type() == DECAY_INVERSE_SQR ) {
			glLightf( GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0 );
			glLightf( GL_LIGHT0, GL_LINEAR_ATTENUATION, 0 );
			glLightf( GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 1.0f / pLight->get_decay() );
		}
		else {
			// no decay
			glLightf( GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1 );
			glLightf( GL_LIGHT0, GL_LINEAR_ATTENUATION, 0 );
			glLightf( GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0 );
		}

		// full angle, full light
		glLighti( GL_LIGHT0, GL_SPOT_EXPONENT, 0 );
		glLighti( GL_LIGHT0, GL_SPOT_CUTOFF, 90 );

		GLfloat		f32Tgt[4] = { 1, 1, 1, 1 };

		Vector3C	rDir = rLtTgt - rLtPos;
		rDir = rDir.normalize();

		f32Tgt[0] = rDir[0];
		f32Tgt[1] = rDir[1];
		f32Tgt[2] = rDir[2];

		glLightfv( GL_LIGHT0, GL_SPOT_DIRECTION, f32Tgt );

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

		float32	f32LightFOV = pLight->get_falloff();


//		TRACE( "   - light set\n" );

		//
		// set texture projection
		//

		// setup texture

		ImportableImageI*	pTex = 0;
		FileHandleC*		pHandle = 0;
		pHandle = pLight->get_projection_texture();
		if( pHandle ) {
			pTex = (ImportableImageI*)pHandle->get_importable();
		}

		if( pTex )
			pTex->eval_state( rRenderContext.m_i32Time );

		bool	bSetupShadow = false;
		bool	bSetupProjector = false;

		// draw shadow index map
		if( pLight->get_cast_shadows() && m_pPBuffer && m_pPBuffer->is_initialised() ) {

//			TRACE( "   - render dept {\n" );

			if( !m_pPBuffer->begin_draw() ) {
//				TRACE( "begin_draw faield\n" );
				break;
			}


			glDisable( GL_LIGHTING );
			glDisable( GL_TEXTURE_2D );

			glClearColor( 0, 0, 0, 1 );
			glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );


			glMatrixMode( GL_PROJECTION );
			glPushMatrix();

			glMatrixMode( GL_MODELVIEW );
			glPushMatrix();

			// render projection texture color

			glMatrixMode( GL_PROJECTION );
			glLoadIdentity();
			glOrtho( 0, 1, 0, 1, -1, 1 );

			glMatrixMode( GL_MODELVIEW );
			glLoadIdentity();

			glDisable( GL_DEPTH_TEST );
			glDepthMask( GL_FALSE );

			// spot figure
			if( !pTex )
			{
				float32	f32OneX = 1.0f / (float32)m_pPBuffer->get_width();
				float32	f32OneY = 1.0f / (float32)m_pPBuffer->get_height();
				float32	f32FallOff = pLight->get_hotspot() / pLight->get_falloff();

				glBegin( GL_TRIANGLE_FAN );

				glColor3ub( 255, 255, 255 );
				for( j = 0; j < 32; j++ ) {
					float32	f32Angle = (float32)j / 32.0f * (float32)M_PI * 2.0f;
					glVertex2f( 0.5f + (0.5f * f32FallOff - 2.0f * f32OneX ) * (float32)cos( f32Angle ),
								0.5f + (0.5f * f32FallOff - 2.0f * f32OneY ) * (float32)sin( f32Angle ) );
				}

				glEnd();

				glBegin( GL_TRIANGLE_STRIP );

				for( j = 0; j <= 32; j++ ) {
					float32	f32Angle = (float32)j / 32.0f * (float32)M_PI * 2.0f;

					glColor3ub( 255, 255, 255 );
					glVertex2f( 0.5f + (0.5f * f32FallOff - 2.0f * f32OneX ) * (float32)cos( f32Angle ),
								0.5f + (0.5f * f32FallOff - 2.0f * f32OneY ) * (float32)sin( f32Angle ) );

					glColor3ub( 0, 0, 0 );
					glVertex2f( 0.5f + (0.5f - 2.0f * f32OneX ) * (float32)cos( f32Angle ),
								0.5f + (0.5f - 2.0f * f32OneY ) * (float32)sin( f32Angle ) );
				}

				glEnd();
			}

			glDepthMask( GL_TRUE );

			// render mesh

			glMatrixMode( GL_PROJECTION );
			glLoadIdentity();
		
			gluPerspective( f32LightFOV, 1.0f, f32CamNear, pCam->get_far_plane() );

			glMatrixMode( GL_MODELVIEW );
			glLoadIdentity();
			gluLookAt( rLtPos[0], rLtPos[1], rLtPos[2], rLtTgt[0], rLtTgt[1], rLtTgt[2], 0, 1, 0 );

			// get light TM
			GLfloat		f32LightTM[16];
			Matrix3C	rLightTM;
			glGetFloatv( GL_MODELVIEW_MATRIX , f32LightTM );
			for( i = 0; i < 4; i++ ) {
				for( j = 0; j < 3; j++ ) {
					rLightTM[i][j] = f32LightTM[i * 4 + j];
				}
			}

			Vector3C	rLightPlanes[4];
			calc_frustum_planes( f32LightFOV / 180.0f * (float32)M_PI, 1, rLightPlanes );

			glBlendFunc( GL_ONE, GL_ZERO );
			glDisable( GL_TEXTURE_2D );
			glEnable( GL_DEPTH_TEST );
			glColorMask( FALSE, FALSE, FALSE, FALSE );

			glPolygonOffset( 8.0f, 1.1f );
			glEnable( GL_POLYGON_OFFSET_FILL );

			glEnable( GL_ALPHA_TEST );
			glAlphaFunc( GL_GEQUAL, 0.5f );

//			TRACE( "     meshes {\n" );

			// solid
			for( j = 0; j < rSortList.size(); j++ ) {
				if( rSortList[j].m_pItem->get_type() == CGITEM_MESH ) {
					MeshC*	pMesh = (MeshC*)rSortList[j].m_pItem;
					if( pMesh->get_visible() && pMesh->get_cast_shadows() ) {
						rSortList[j].m_bLightVisible = render_mesh_index( &rRenderContext, pMesh, rLightTM, rLightPlanes );
					}
				}
				else if( rSortList[j].m_pItem->get_type() == CGITEM_SHAPE  ) {
					ShapeC*	pShape = (ShapeC*)rSortList[j].m_pItem;
					if( pShape->get_visible() )
						rSortList[j].m_bLightVisible = render_shape_index( &rRenderContext, pShape, rLightTM, rLightPlanes );
				}
				else if( rSortList[j].m_pItem->get_type() == CGITEM_PARTICLESYSTEM ) {
					ParticleSystemC*	pPSys = (ParticleSystemC*)rSortList[j].m_pItem;
					if( pPSys->get_visible() )
						render_particlesystem_index( &rRenderContext, pPSys, rLightTM, rLightPlanes );
				}
			}

//			TRACE( "     }\n" );

			glDisable( GL_ALPHA_TEST );

			glColorMask( TRUE, TRUE, TRUE, TRUE );

			glDisable( GL_POLYGON_OFFSET_FILL );

			m_pPBuffer->copy_texture();


			glMatrixMode( GL_PROJECTION );
			glPopMatrix();
			glMatrixMode( GL_MODELVIEW );
			glPopMatrix();

			m_pPBuffer->end_draw();

			glEnable( GL_LIGHTING );

			// Projected image
			if( pTex )
			{
				glActiveTextureARB( ui32SpotTexture );
				glClientActiveTextureARB( ui32SpotTexture );
				glEnable( GL_TEXTURE_2D );

				if( pTex ) {
					pTex->bind_texture( rRenderContext.m_pDevice, 0, IMAGE_CLAMP | IMAGE_LINEAR );
					glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
				}
				glEnable( GL_TEXTURE_2D );
			}
			else
			{
				// Shadow
				glActiveTextureARB( ui32SpotTexture );
				glClientActiveTextureARB( ui32SpotTexture );
				glEnable( GL_TEXTURE_2D );

				m_pPBuffer->bind( PBUFFER_TEXTURE_RGBA );
				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
			}

			// Shadow
			glActiveTextureARB( ui32ShadowTexture );
			glClientActiveTextureARB( ui32ShadowTexture );
			glEnable( GL_TEXTURE_2D );

			m_pPBuffer->bind( PBUFFER_TEXTURE_DEPTH );
			glTexParameteri( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL );
			glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );


			bSetupShadow = true;
			bSetupProjector = true;

//			TRACE( "     }\n" );

		}
		else {

			glActiveTextureARB( ui32SpotTexture );
			glClientActiveTextureARB( ui32SpotTexture );
			glEnable( GL_TEXTURE_2D );

			if( pTex ) {
				pTex->bind_texture( rRenderContext.m_pDevice, 0, IMAGE_CLAMP | IMAGE_LINEAR );
				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
			}
			glEnable( GL_TEXTURE_2D );

			bSetupShadow = false;
			bSetupProjector = true;
		}

//		TRACE( "     - texgen {\n" );

		GLdouble	lightViewMatrix[16];
		GLdouble	lightFrustumMatrix[16];
		GLdouble	m1[16], m2[16];
		GLdouble	p[4];

		buildLookAtMatrix( lightViewMatrix, rLtPos[0], rLtPos[1], rLtPos[2], rLtTgt[0], rLtTgt[1], rLtTgt[2], 0, 1, 0 );

		//gluPerspective( f32LightFOV, 1.0f, f32CamNear, pCam->get_far_plane() );

		buildPerspectiveMatrix( lightFrustumMatrix, f32LightFOV, 1.0f, f32CamNear, pCam->get_far_plane() );

		GLdouble Smatrix[16] = {
			0.5, 0,   0,   0,
			0,   0.5, 0,   0,
			0,   0,   0.5, 0,
			0.5, 0.5, 0.5, 1.0
		};

		copyMatrix( m1, Smatrix );
		multMatrices( m2, m1, lightFrustumMatrix );
		multMatrices( m1, m2, lightViewMatrix );


		if( bSetupProjector )
		{
			glActiveTextureARB( ui32SpotTexture );
			glClientActiveTextureARB( ui32SpotTexture );

			p[0] = m1[0];
			p[1] = m1[4];
			p[2] = m1[8];
			p[3] = m1[12];
			glTexGendv( GL_S, GL_EYE_PLANE, p );

			p[0] = m1[1];
			p[1] = m1[5];
			p[2] = m1[9];
			p[3] = m1[13];
			glTexGendv( GL_T, GL_EYE_PLANE, p );

			p[0] = m1[2];
			p[1] = m1[6];
			p[2] = m1[10];
			p[3] = m1[14];
			glTexGendv( GL_R, GL_EYE_PLANE, p );

			p[0] = m1[3];
			p[1] = m1[7];
			p[2] = m1[11];
			p[3] = m1[15];
			glTexGendv( GL_Q, GL_EYE_PLANE, p );

			glEnable(GL_TEXTURE_GEN_S);
			glEnable(GL_TEXTURE_GEN_T);
			glEnable(GL_TEXTURE_GEN_Q);
			glEnable(GL_TEXTURE_GEN_R);

			glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
			glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
			glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
			glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
		}

		if( bSetupShadow )
		{
			glActiveTextureARB( ui32ShadowTexture );
			glClientActiveTextureARB( ui32ShadowTexture );

			p[0] = m1[0];
			p[1] = m1[4];
			p[2] = m1[8];
			p[3] = m1[12];
			glTexGendv( GL_S, GL_EYE_PLANE, p );

			p[0] = m1[1];
			p[1] = m1[5];
			p[2] = m1[9];
			p[3] = m1[13];
			glTexGendv( GL_T, GL_EYE_PLANE, p );

			p[0] = m1[2];
			p[1] = m1[6];
			p[2] = m1[10];
			p[3] = m1[14];
			glTexGendv( GL_R, GL_EYE_PLANE, p );

			p[0] = m1[3];
			p[1] = m1[7];
			p[2] = m1[11];
			p[3] = m1[15];
			glTexGendv( GL_Q, GL_EYE_PLANE, p );

			glEnable(GL_TEXTURE_GEN_S);
			glEnable(GL_TEXTURE_GEN_T);
			glEnable(GL_TEXTURE_GEN_Q);
			glEnable(GL_TEXTURE_GEN_R);

			glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
			glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
			glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
			glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
		}

//		TRACE( "       }\n" );

//		TRACE( "     - draw spot {\n" );

		// draw solid objects first
		for( j = 0; j < rSortList.size(); j++ ) {

			if( rSortList[j].m_pItem->get_type() == CGITEM_MESH ) {
				MeshC*	pMesh = (MeshC*)rSortList[j].m_pItem;
				if( pMesh->get_visible() && !pMesh->is_transparent() && !pMesh->get_additive() &&
					rSortList[j].m_bCamVisible ) //&& rSortList[j].m_bLightVisible )
				{
					render_mesh_spot( &rRenderContext, pMesh, rCamTM, rPlanes );
				}
			}
			else if( rSortList[j].m_pItem->get_type() == CGITEM_SHAPE  ) {
				ShapeC*	pShape = (ShapeC*)rSortList[j].m_pItem;
				if( pShape->get_visible() && !pShape->is_transparent() && !pShape->get_additive() &&
					rSortList[j].m_bCamVisible ) //&& rSortList[j].m_bLightVisible )
				{
					render_shape_spot( &rRenderContext, pShape, rCamTM, rPlanes );
				}
			}
		}

//		TRACE( "        - trans\n" );

		// then transparent
		for( j = 0; j < rSortList.size(); j++ ) {

			if( rSortList[j].m_pItem->get_type() == CGITEM_MESH ) {
				MeshC*	pMesh = (MeshC*)rSortList[j].m_pItem;
				if( pMesh->get_visible() && (pMesh->is_transparent() || pMesh->get_additive()) &&
					rSortList[j].m_bCamVisible ) // && rSortList[j].m_bLightVisible )
				{
					render_mesh_spot( &rRenderContext, pMesh, rCamTM, rPlanes );
				}
			}
			else if( rSortList[j].m_pItem->get_type() == CGITEM_SHAPE  ) {
				ShapeC*	pShape = (ShapeC*)rSortList[j].m_pItem;
				if( pShape->get_visible() && (pShape->is_transparent() || pShape->get_additive()) &&
					rSortList[j].m_bCamVisible ) //&& rSortList[j].m_bLightVisible )
				{
					render_shape_spot( &rRenderContext, pShape, rCamTM, rPlanes );
				}
			}
		}

//		TRACE( "       }\n" );

		glMatrixMode( GL_MODELVIEW );

		glActiveTextureARB( ui32SpotTexture );
		glClientActiveTextureARB( ui32SpotTexture );
		glDisable( GL_TEXTURE_2D );
		glDisable( GL_TEXTURE_GEN_S );
		glDisable( GL_TEXTURE_GEN_T );
		glDisable( GL_TEXTURE_GEN_R );
		glDisable( GL_TEXTURE_GEN_Q );
		glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

		glActiveTextureARB( ui32ShadowTexture );
		glClientActiveTextureARB( ui32ShadowTexture );
		glDisable( GL_TEXTURE_2D );
		glDisable( GL_TEXTURE_GEN_S );
		glDisable( GL_TEXTURE_GEN_T );
		glDisable( GL_TEXTURE_GEN_R );
		glDisable( GL_TEXTURE_GEN_Q );
		glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );


		if( pLight->get_cast_shadows() && m_pPBuffer ) {

			m_pPBuffer->bind( PBUFFER_TEXTURE_DEPTH );
			// tex env
			glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

			glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE );
			glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT );
			glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR );
			glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE );
			glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR );

			glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_MODULATE );
			glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_PREVIOUS_EXT );	// GL_PREVIOUS
			glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_EXT, GL_SRC_ALPHA );
			glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_TEXTURE );
			glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_EXT, GL_SRC_ALPHA );
		}


//		TRACE( "   } (spot)\n" );
	}

	glActiveTextureARB( ui32SpotTexture );
	glClientActiveTextureARB( ui32SpotTexture );
	glBindTexture( GL_TEXTURE_2D, 0 );
	glDisable( GL_TEXTURE_2D );
	glDisable( GL_TEXTURE_GEN_S );
	glDisable( GL_TEXTURE_GEN_T );
	glDisable( GL_TEXTURE_GEN_R );
	glDisable( GL_TEXTURE_GEN_Q );
	glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

	glActiveTextureARB( ui32ShadowTexture );
	glClientActiveTextureARB( ui32ShadowTexture );
	glDisable( GL_TEXTURE_2D );
	glDisable( GL_TEXTURE_GEN_S );
	glDisable( GL_TEXTURE_GEN_T );
	glDisable( GL_TEXTURE_GEN_R );
	glDisable( GL_TEXTURE_GEN_Q );
	glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

	// Disable texture unit
	glActiveTextureARB( GL_TEXTURE0_ARB );
	glClientActiveTextureARB( GL_TEXTURE0_ARB );


	glDisable( GL_LIGHT0 );
	glDisable( GL_LIGHTING );


	//
	// particle systemst
	//

	if( i32FogMode != FOG_MODE_OFF )
	{
		glEnable( GL_FOG );
		// Reset fog color
		glFogfv( GL_FOG_COLOR, rClearColor );
	}


	for( i = 0; i < rSortList.size(); i++ ) {
		if( rSortList[i].m_pItem->get_type() == CGITEM_PARTICLESYSTEM ) {
			ParticleSystemC*	pPSys = (ParticleSystemC*)rSortList[i].m_pItem;
			if( pPSys->get_visible() )
				render_particlesystem( &rRenderContext, pPSys, rCamTM, rPlanes );
		}
/*		else if( rSortList[i].m_pItem->get_type() == CGITEM_WSMOBJECT ) {
			WSMObjectC*	pObj = (WSMObjectC*)rSortList[i].m_pItem;
//			if( pObj->get_visible() )
				render_wsm_object( &rRenderContext, pObj, rCamTM, rPlanes );
		}*/
	}

	glDisable( GL_FOG );

	if( g_bSeparateSpec )
		glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR );



	//
	// @depth gradient
	//

	glDisable( GL_ALPHA_TEST );
	glDisable( GL_LIGHTING );

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

	if( i32DOFAlpha == 1 )
	{

		glColor4f( 1, 1, 1, 1 );

		glBlendFunc( GL_ONE, GL_ZERO );

		glActiveTextureARB( GL_TEXTURE0_ARB );
		glClientActiveTextureARB( GL_TEXTURE0_ARB );

		glDisableClientState( GL_TEXTURE_COORD_ARRAY );


		glBindTexture( GL_TEXTURE_2D, m_ui32RampTexId );
		glEnable( GL_TEXTURE_2D );

//		glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

		glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT );

		glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE );
		glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE );
		glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR );

		glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE );
		glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE );
		glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_EXT, GL_SRC_ALPHA );

		glDisable(GL_TEXTURE_GEN_T);
		glDisable(GL_TEXTURE_GEN_R);

/*		float32	f32EnvColor[4] = { 1, 0, 0, 1 };

		glTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, f32EnvColor );

		glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_INTERPOLATE_EXT );
		glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT );
		glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR );
		glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_CONSTANT_COLOR_EXT );
		glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR );
		glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB_EXT, GL_TEXTURE );
		glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB_EXT, GL_SRC_ALPHA );

		glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE );
		glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE );
		glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_EXT, GL_SRC_ALPHA );*/


		GLdouble	camViewMatrix[16];
		GLdouble	camFrustumMatrix[16];
		GLdouble	m1[16], m2[16];
		GLdouble	p[4];

		for( i = 0; i < 16; i++ )
			camViewMatrix[i] = f32CamTM[i];

		buildPerspectiveMatrix( camFrustumMatrix, f32FOV / (float32)M_PI * (float32)180.0f, f32Aspect, f32DOFStart, f32DOFEnd );

		GLdouble Smatrix[16] = {
			0.5, 0,   0,   0,
			0,   0.5, 0,   0,
			0,   0,   0.5, 0,
			0.5, 0.5, 0.5, 1.0
		};

		copyMatrix( m1, Smatrix );
		multMatrices( m2, m1, camFrustumMatrix );
		multMatrices( m1, m2, camViewMatrix );


		p[0] = m1[2];
		p[1] = m1[6];
		p[2] = m1[10];
		p[3] = m1[14];

/*		p[0] = m1[0];
		p[1] = m1[4];
		p[2] = m1[8];
		p[3] = m1[12];*/
		glTexGendv( GL_S, GL_EYE_PLANE, p );

/*		p[0] = m1[1];
		p[1] = m1[5];
		p[2] = m1[9];
		p[3] = m1[13];
		glTexGendv( GL_T, GL_EYE_PLANE, p );*/

/*		p[0] = m1[2];
		p[1] = m1[6];
		p[2] = m1[10];
		p[3] = m1[14];
		glTexGendv( GL_R, GL_EYE_PLANE, p );*/

		p[0] = m1[3];
		p[1] = m1[7];
		p[2] = m1[11];
		p[3] = m1[15];
		glTexGendv( GL_Q, GL_EYE_PLANE, p );

		glEnable(GL_TEXTURE_GEN_S);
//		glEnable(GL_TEXTURE_GEN_T);
//		glEnable(GL_TEXTURE_GEN_R);
		glEnable(GL_TEXTURE_GEN_Q);

		glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
//		glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
//		glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );
		glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR );

		glColorMask( FALSE, FALSE, FALSE, TRUE );

		for( i = 0; i < rSortList.size(); i++ )
		{
			if( rSortList[i].m_pItem->get_type() == CGITEM_MESH )
			{
				MeshC*	pMesh = (MeshC*)rSortList[i].m_pItem;
				if( pMesh->get_visible() && !pMesh->is_transparent() && !pMesh->get_additive() )
					render_mesh_dof( &rRenderContext, pMesh, rCamTM, rPlanes );
			}
		}

		glColorMask( TRUE, TRUE, TRUE, TRUE );

		glDisable( GL_TEXTURE_2D );
		glDisable( GL_TEXTURE_GEN_S );
//		glDisable( GL_TEXTURE_GEN_T );
//		glDisable( GL_TEXTURE_GEN_R );
		glDisable( GL_TEXTURE_GEN_Q );
	}


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

//	TRACE( "}\n" );

}


void
EngineEffectC::render_bone( RenderContextS* pContext, BoneC* pBone )
{

	const Matrix3C&	rTM = pBone->get_tm();

	glPointSize( 7.0f );

	glDisable( GL_LIGHTING );

	glColor3ub( 255, 255, 255 );
	glBegin( GL_POINTS );
	glVertex3f( rTM[3][0], rTM[3][1], rTM[3][2] );
	glEnd();

	if( pBone->get_parent() ) {

		const Matrix3C&	rParTM = pBone->get_parent()->get_tm();

		glBegin( GL_LINES );
		glVertex3f( rTM[3][0], rTM[3][1], rTM[3][2] );
		glVertex3f( rParTM[3][0], rParTM[3][1], rParTM[3][2] );
		glEnd();
	}

	glPushMatrix();

	GLfloat	mat[16];

	mat[0]  = rTM[0][0];
	mat[1]  = rTM[0][1];
	mat[2]  = rTM[0][2];
	mat[3]  = 0;
	mat[4]  = rTM[1][0];
	mat[5]  = rTM[1][1];
	mat[6]  = rTM[1][2];
	mat[7]  = 0;
	mat[8]  = rTM[2][0];
	mat[9]  = rTM[2][1];
	mat[10] = rTM[2][2];
	mat[11] = 0;
	mat[12] = rTM[3][0];
	mat[13] = rTM[3][1];
	mat[14] = rTM[3][2];
	mat[15] = 1;

	glMultMatrixf( mat );

	glBegin( GL_LINES );
	
	glColor3ub( 255, 0, 0 );
	glVertex3f( 0, 0, 0 );
	glVertex3f( 5, 0, 0 );

	glColor3ub( 0, 255, 0 );
	glVertex3f( 0, 0, 0 );
	glVertex3f( 0, 5, 0 );

	glColor3ub( 0, 0, 255 );
	glVertex3f( 0, 0, 0 );
	glVertex3f( 0, 0, 5 );

	glEnd();

	glEnable( GL_LIGHTING );

	glPopMatrix();
}


void
EngineEffectC::render_wsm_object( RenderContextS* pContext, WSMObjectC* pObj, const PajaTypes::Matrix3C& rCamTM, PajaTypes::Vector3C* pPlanes )
{
	uint32	i;

	Matrix3C	rTM = pObj->get_tm();


	glPushMatrix();


	GLfloat	mat[16];

	mat[0]  = rTM[0][0];
	mat[1]  = rTM[0][1];
	mat[2]  = rTM[0][2];
	mat[3]  = 0;
	mat[4]  = rTM[1][0];
	mat[5]  = rTM[1][1];
	mat[6]  = rTM[1][2];
	mat[7]  = 0;
	mat[8]  = rTM[2][0];
	mat[9]  = rTM[2][1];
	mat[10] = rTM[2][2];
	mat[11] = 0;
	mat[12] = rTM[3][0];
	mat[13] = rTM[3][1];
	mat[14] = rTM[3][2];
	mat[15] = 1;

	glMultMatrixf( mat );


	glDisable( GL_LIGHTING );

	float32	f32Width = 2.0f; //pObj->get_width() / 2.0f;
	float32	f32Height = 2.0f; //pPSys->get_height() / 2.0f;

	if( pObj->get_collision_object() ) {
		CollisionObjectI*	pColObj = pObj->get_collision_object();
		if( pColObj->get_type() == WSM_DEFLECTOR ) {
			DeflectorC*	pDef = (DeflectorC*)pColObj;
			f32Width = pDef->get_width() * 0.5f;
			f32Height = pDef->get_height() * 0.5f;
		}
	}

	glColor3ub( 255, 255, 255 );

	// top
	glBegin( GL_LINE_LOOP );
	glVertex3f( -f32Width, 0, -f32Height );
	glVertex3f(  f32Width, 0, -f32Height );
	glVertex3f(  f32Width, 0,  f32Height );
	glVertex3f( -f32Width, 0,  f32Height );
	glEnd();

	glBegin( GL_LINES );
	glVertex3f( 0, 0, 0 );
	glVertex3f( 0, __max( f32Width, f32Height ), 0 );
	glEnd();

	glEnd();

	glPopMatrix();

	glEnable( GL_LIGHTING );
}



void
EngineEffectC::render_particlesystem( RenderContextS* pContext, ParticleSystemC* pPSys,
						    const Matrix3C& rCamTM, Vector3C* pPlanes )
{

	uint32	i;

	Matrix3C	rTM = pPSys->get_tm();


	float32	f32Mat[16];
	float32	f32MatTrans[16];
	glGetFloatv( GL_MODELVIEW_MATRIX, f32Mat );

	// transpose matrix (rotation only)
	for( uint32 ui32Row = 0; ui32Row < 3; ui32Row++ )
		for( uint32 ui32Col = 0; ui32Col < 3; ui32Col++ )
			f32MatTrans[ui32Row * 4 + ui32Col] = f32Mat[ui32Col * 4 + ui32Row];

	// use transposed axii
	Vector3C	rAxisX( f32MatTrans[0], f32MatTrans[1], f32MatTrans[2] );
	Vector3C	rAxisY( f32MatTrans[4], f32MatTrans[5], f32MatTrans[6] );
	Vector3C	rAxisZ( f32MatTrans[8], f32MatTrans[9], f32MatTrans[10] );


	uint32	ui32TexUnit = 0;

	glActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );
	glClientActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );

//	glMatrixMode( GL_TEXTURE );
//	glLoadIdentity();
//	glMatrixMode( GL_MODELVIEW );


	// draw it!
	if( pPSys->count_live() ) {

		bool bDisableDepthWrite = false;

		ColorC	rDiffuse = pPSys->get_color();
		rDiffuse[3] = 1.0f - pPSys->get_transparency();

		// Texture stuff
		ImportableImageI*	pTex = 0;
		FileHandleC*		pHandle = 0;

		//
		// Diffuse texture
		//
		pTex = 0;
		pHandle = pPSys->get_texture();

		if( pHandle ) {
			pTex = (ImportableImageI*)pHandle->get_importable();
//			if( !pTex )
//				TRACE( "no texture found!\n" );
		}

		if( pTex ) {

			glEnable( GL_TEXTURE_2D );

			pTex->eval_state( pContext->m_i32Time );
			pTex->bind_texture( pContext->m_pDevice, 0, IMAGE_WRAP | IMAGE_LINEAR );

			glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

			// Set diffuse material to white
			if( pTex->get_data_bpp() >  8 ) {
				rDiffuse[0] = rDiffuse[1] = rDiffuse[2] = 1.0f;
			}
		}

		// the mult tells how much each pass should render.
		// passCount = 1 + numver-of-spotlights
		glColor4f( rDiffuse[0], rDiffuse[1], rDiffuse[2], rDiffuse[3] );


		if( pPSys->get_transparency_type() == TRANSPARENCY_ADD ) {
			glBlendFunc( GL_SRC_ALPHA, GL_ONE );
		}
		else {
			if( pPSys->get_transparency() < 1.0f ) {
				glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
			}
			else
				glBlendFunc( GL_SRC_ALPHA, GL_ZERO );
		}

		glDepthMask( GL_FALSE );

		if( pPSys->get_particle_type() == PARTICLE_SNOW ) {
			float32*	pPos = pPSys->get_particle_position_ptr();
			int32*		pAge = pPSys->get_particle_age_ptr();
			float32		f32Size = pPSys->get_drop_size();


			Vector3C	rOffset[4];

			rOffset[0] = -rAxisX - rAxisY;
			rOffset[1] = rAxisX - rAxisY;
			rOffset[2] = rAxisX + rAxisY;
			rOffset[3] = -rAxisX + rAxisY;

			float32	f32Pos[3];

			glBegin( GL_QUADS );

			for( uint32 i = 0; i < pPSys->get_particle_count(); i++ ) {
				if( *pAge >= 0 ) {

					float32	f32S = f32Size;
					float32	f32U = (float32)*pAge / (float32)pPSys->get_life_time();
					if( f32U > 0.75f ) {
						f32S *= 1.0f - ((f32U - 0.75f) / (0.25f));
					}

					f32Pos[0] = pPos[0] - rOffset[0][0] * f32S;
					f32Pos[1] = pPos[1] - rOffset[0][1] * f32S;
					f32Pos[2] = pPos[2] - rOffset[0][2] * f32S;
					glTexCoord2f( 0, 0 );
					glVertex3fv( f32Pos );

					f32Pos[0] = pPos[0] - rOffset[1][0] * f32S;
					f32Pos[1] = pPos[1] - rOffset[1][1] * f32S;
					f32Pos[2] = pPos[2] - rOffset[1][2] * f32S;
					glTexCoord2f( 1, 0 );
					glVertex3fv( f32Pos );

					f32Pos[0] = pPos[0] - rOffset[2][0] * f32S;
					f32Pos[1] = pPos[1] - rOffset[2][1] * f32S;
					f32Pos[2] = pPos[2] - rOffset[2][2] * f32S;
					glTexCoord2f( 1, 1 );
					glVertex3fv( f32Pos );

					f32Pos[0] = pPos[0] - rOffset[3][0] * f32S;
					f32Pos[1] = pPos[1] - rOffset[3][1] * f32S;
					f32Pos[2] = pPos[2] - rOffset[3][2] * f32S;
					glTexCoord2f( 0, 1 );
					glVertex3fv( f32Pos );
				}
				pAge++;
				pPos += 3;
			}

			glEnd();
		}
		else {
			float32*	pPos = pPSys->get_particle_position_ptr();
			float32*	pVel = pPSys->get_particle_velocity_ptr();
			int32*		pAge = pPSys->get_particle_age_ptr();

			#define TIME_TICKSPERSEC	4800
			#define RAINSIZEFACTOR (float(TIME_TICKSPERSEC)/120.0f)

			float32		f32Size = pPSys->get_drop_size();

			Vector3C	rOffset[4];
			float32	f32Pos[3];
			glBegin( GL_QUADS );

			Vector3C	rV, rV0, rV1;

			for( uint32 i = 0; i < pPSys->get_particle_count(); i++ ) {
				if( *pAge >= 0 ) {

					float32	f32S = f32Size;
					float32	f32U = (float32)*pAge / (float32)pPSys->get_life_time();
					if( f32U > 0.75f ) {
						f32S *= 1.0f - ((f32U - 0.75f) / (0.25f));
					}


					Vector3C	rAxisXB, rAxisYB;

					rAxisXB = Vector3C( pVel ) * f32S * RAINSIZEFACTOR;
					rAxisYB = (rAxisZ.cross( rAxisXB )).normalize();
					rAxisYB *= f32S * 0.2f;

					rOffset[0] = -rAxisXB - rAxisYB;
					rOffset[1] = rAxisXB - rAxisYB;
					rOffset[2] = rAxisXB + rAxisYB;
					rOffset[3] = -rAxisXB + rAxisYB;


					f32Pos[0] = pPos[0] - rOffset[0][0];
					f32Pos[1] = pPos[1] - rOffset[0][1];
					f32Pos[2] = pPos[2] - rOffset[0][2];
					glTexCoord2f( 0, 0 );
					glVertex3fv( f32Pos );

					f32Pos[0] = pPos[0] - rOffset[1][0];
					f32Pos[1] = pPos[1] - rOffset[1][1];
					f32Pos[2] = pPos[2] - rOffset[1][2];
					glTexCoord2f( 1, 0 );
					glVertex3fv( f32Pos );

					f32Pos[0] = pPos[0] - rOffset[2][0];
					f32Pos[1] = pPos[1] - rOffset[2][1];
					f32Pos[2] = pPos[2] - rOffset[2][2];
					glTexCoord2f( 1, 1 );
					glVertex3fv( f32Pos );

					f32Pos[0] = pPos[0] - rOffset[3][0];
					f32Pos[1] = pPos[1] - rOffset[3][1];
					f32Pos[2] = pPos[2] - rOffset[3][2];
					glTexCoord2f( 0, 1 );
					glVertex3fv( f32Pos );
				}
				pAge++;
				pPos += 3;
				pVel += 3;
			}

			glEnd();

		}

		glDepthMask( GL_TRUE );

		glDisable( GL_TEXTURE_2D );
	}
}

void
EngineEffectC::render_particlesystem_index( RenderContextS* pContext, ParticleSystemC* pPSys,
						    const Matrix3C& rCamTM, Vector3C* pPlanes )
{

	uint32	i;

	Matrix3C	rTM = pPSys->get_tm();


	float32	f32Mat[16];
	float32	f32MatTrans[16];
	glGetFloatv( GL_MODELVIEW_MATRIX, f32Mat );

	// transpose matrix (rotation only)
	for( uint32 ui32Row = 0; ui32Row < 3; ui32Row++ )
		for( uint32 ui32Col = 0; ui32Col < 3; ui32Col++ )
			f32MatTrans[ui32Row * 4 + ui32Col] = f32Mat[ui32Col * 4 + ui32Row];

	// use transposed axii
	Vector3C	rAxisX( f32MatTrans[0], f32MatTrans[1], f32MatTrans[2] );
	Vector3C	rAxisY( f32MatTrans[4], f32MatTrans[5], f32MatTrans[6] );
	Vector3C	rAxisZ( f32MatTrans[8], f32MatTrans[9], f32MatTrans[10] );


	uint32	ui32TexUnit = 0;

	glActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );
	glClientActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );

//	glMatrixMode( GL_TEXTURE );
//	glLoadIdentity();
//	glMatrixMode( GL_MODELVIEW );


	// draw it!
	if( pPSys->count_live() ) {

		bool bDisableDepthWrite = false;

		ColorC	rDiffuse = pPSys->get_color();
		rDiffuse[3] = 1.0f - pPSys->get_transparency();

		// Texture stuff
		ImportableImageI*	pTex = 0;
		FileHandleC*		pHandle = 0;

		//
		// Diffuse texture
		//
		pTex = 0;
		pHandle = pPSys->get_texture();

		if( pHandle ) {
			pTex = (ImportableImageI*)pHandle->get_importable();
//			if( !pTex )
//				TRACE( "no texture found!\n" );
		}

		if( pTex ) {

			glEnable( GL_TEXTURE_2D );

			pTex->eval_state( pContext->m_i32Time );
			pTex->bind_texture( pContext->m_pDevice, 0, IMAGE_WRAP | IMAGE_LINEAR );

			glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

			// Set diffuse material to white
			if( pTex->get_data_bpp() >  8 ) {
				rDiffuse[0] = rDiffuse[1] = rDiffuse[2] = 1.0f;
			}
		}

		// the mult tells how much each pass should render.
		// passCount = 1 + numver-of-spotlights
		glColor4f( rDiffuse[0], rDiffuse[1], rDiffuse[2], rDiffuse[3] );


		glBlendFunc( GL_ONE, GL_ZERO );

		if( pPSys->get_particle_type() == PARTICLE_SNOW ) {
			float32*	pPos = pPSys->get_particle_position_ptr();
			int32*		pAge = pPSys->get_particle_age_ptr();
			float32		f32Size = pPSys->get_drop_size();


			Vector3C	rOffset[4];

			rOffset[0] = -rAxisX - rAxisY;
			rOffset[1] = rAxisX - rAxisY;
			rOffset[2] = rAxisX + rAxisY;
			rOffset[3] = -rAxisX + rAxisY;

			float32	f32Pos[3];

			glBegin( GL_QUADS );

			for( uint32 i = 0; i < pPSys->get_particle_count(); i++ ) {
				if( *pAge >= 0 ) {

					float32	f32S = f32Size;
					float32	f32U = (float32)*pAge / (float32)pPSys->get_life_time();
					if( f32U > 0.75f ) {
						f32S *= 1.0f - ((f32U - 0.75f) / (0.25f));
					}

					f32Pos[0] = pPos[0] - rOffset[0][0] * f32S;
					f32Pos[1] = pPos[1] - rOffset[0][1] * f32S;
					f32Pos[2] = pPos[2] - rOffset[0][2] * f32S;
					glTexCoord2f( 0, 0 );
					glVertex3fv( f32Pos );

					f32Pos[0] = pPos[0] - rOffset[1][0] * f32S;
					f32Pos[1] = pPos[1] - rOffset[1][1] * f32S;
					f32Pos[2] = pPos[2] - rOffset[1][2] * f32S;
					glTexCoord2f( 1, 0 );
					glVertex3fv( f32Pos );

					f32Pos[0] = pPos[0] - rOffset[2][0] * f32S;
					f32Pos[1] = pPos[1] - rOffset[2][1] * f32S;
					f32Pos[2] = pPos[2] - rOffset[2][2] * f32S;
					glTexCoord2f( 1, 1 );
					glVertex3fv( f32Pos );

					f32Pos[0] = pPos[0] - rOffset[3][0] * f32S;
					f32Pos[1] = pPos[1] - rOffset[3][1] * f32S;
					f32Pos[2] = pPos[2] - rOffset[3][2] * f32S;
					glTexCoord2f( 0, 1 );
					glVertex3fv( f32Pos );
				}
				pAge++;
				pPos += 3;
			}

			glEnd();
		}
		else {
			float32*	pPos = pPSys->get_particle_position_ptr();
			float32*	pVel = pPSys->get_particle_velocity_ptr();
			int32*		pAge = pPSys->get_particle_age_ptr();

			#define TIME_TICKSPERSEC	4800
			#define RAINSIZEFACTOR (float(TIME_TICKSPERSEC)/120.0f)

			float32		f32Size = pPSys->get_drop_size();

			Vector3C	rOffset[4];
			float32	f32Pos[3];
			glBegin( GL_QUADS );

			Vector3C	rV, rV0, rV1;

			for( uint32 i = 0; i < pPSys->get_particle_count(); i++ ) {
				if( *pAge >= 0 ) {

					float32	f32S = f32Size;
					float32	f32U = (float32)*pAge / (float32)pPSys->get_life_time();
					if( f32U > 0.75f ) {
						f32S *= 1.0f - ((f32U - 0.75f) / (0.25f));
					}


					Vector3C	rAxisXB, rAxisYB;

					rAxisXB = Vector3C( pVel ) * f32S * RAINSIZEFACTOR;
					rAxisYB = (rAxisZ.cross( rAxisXB )).normalize();
					rAxisYB *= f32S * 0.2f;

					rOffset[0] = -rAxisXB - rAxisYB;
					rOffset[1] = rAxisXB - rAxisYB;
					rOffset[2] = rAxisXB + rAxisYB;
					rOffset[3] = -rAxisXB + rAxisYB;


					f32Pos[0] = pPos[0] - rOffset[0][0];
					f32Pos[1] = pPos[1] - rOffset[0][1];
					f32Pos[2] = pPos[2] - rOffset[0][2];
					glTexCoord2f( 0, 0 );
					glVertex3fv( f32Pos );

					f32Pos[0] = pPos[0] - rOffset[1][0];
					f32Pos[1] = pPos[1] - rOffset[1][1];
					f32Pos[2] = pPos[2] - rOffset[1][2];
					glTexCoord2f( 1, 0 );
					glVertex3fv( f32Pos );

					f32Pos[0] = pPos[0] - rOffset[2][0];
					f32Pos[1] = pPos[1] - rOffset[2][1];
					f32Pos[2] = pPos[2] - rOffset[2][2];
					glTexCoord2f( 1, 1 );
					glVertex3fv( f32Pos );

					f32Pos[0] = pPos[0] - rOffset[3][0];
					f32Pos[1] = pPos[1] - rOffset[3][1];
					f32Pos[2] = pPos[2] - rOffset[3][2];
					glTexCoord2f( 0, 1 );
					glVertex3fv( f32Pos );
				}
				pAge++;
				pPos += 3;
				pVel += 3;
			}

			glEnd();

		}

		glDisable( GL_TEXTURE_2D );
	}
}


bool
EngineEffectC::render_mesh( RenderContextS* pContext, MeshC* pMesh,
						    const Matrix3C& rCamTM, Vector3C* pPlanes )
{

	uint32	i;

	Matrix3C	rTM = pMesh->get_tm();

	if( pMesh->is_transparent() )
		pMesh->sort_faces( rCamTM );

	//
	// check visibility
	//

	Vector3C	rMin, rMax;
	rMin = pMesh->get_bbox_min();
	rMax = pMesh->get_bbox_max();

	Vector3C	rBBox[8];
	rBBox[0] = Vector3C( rMin[0], rMin[1], rMin[2] );
	rBBox[1] = Vector3C( rMin[0], rMin[1], rMax[2] );
	rBBox[2] = Vector3C( rMax[0], rMin[1], rMax[2] );
	rBBox[3] = Vector3C( rMax[0], rMin[1], rMin[2] );
	rBBox[4] = Vector3C( rMin[0], rMax[1], rMin[2] );
	rBBox[5] = Vector3C( rMin[0], rMax[1], rMax[2] );
	rBBox[6] = Vector3C( rMax[0], rMax[1], rMax[2] );
	rBBox[7] = Vector3C( rMax[0], rMax[1], rMin[2] );

	uint32	ui32D[8];

	for( i = 0; i < 8; i++ ) {
		Vector3C	rVec = rBBox[i];
		rVec *= rTM;
		rVec *= rCamTM;

		ui32D[i] = 0;

		if( rVec.dot( pPlanes[0] ) < 0 ) ui32D[i] |= 1 << 0;
		if( rVec.dot( pPlanes[1] ) < 0 ) ui32D[i] |= 1 << 1;
		if( rVec.dot( pPlanes[2] ) < 0 ) ui32D[i] |= 1 << 2;
		if( rVec.dot( pPlanes[3] ) < 0 ) ui32D[i] |= 1 << 3;

	}

	uint32	ui32AndBits = ui32D[0] & ui32D[1] & ui32D[2] & ui32D[3] & ui32D[4] & ui32D[5] & ui32D[6] & ui32D[7];
	uint32	ui32OrBits = ui32D[0] | ui32D[1] | ui32D[2] | ui32D[3] | ui32D[4] | ui32D[5] | ui32D[6] | ui32D[7];


	if( ui32AndBits )					// All out
		return false;


	glPushMatrix();

	GLfloat	mat[16];

	mat[0]  = rTM[0][0];
	mat[1]  = rTM[0][1];
	mat[2]  = rTM[0][2];
	mat[3]  = 0;
	mat[4]  = rTM[1][0];
	mat[5]  = rTM[1][1];
	mat[6]  = rTM[1][2];
	mat[7]  = 0;
	mat[8]  = rTM[2][0];
	mat[9]  = rTM[2][1];
	mat[10] = rTM[2][2];
	mat[11] = 0;
	mat[12] = rTM[3][0];
	mat[13] = rTM[3][1];
	mat[14] = rTM[3][2];
	mat[15] = 1;

	glMultMatrixf( mat );


	glDisableClientState( GL_VERTEX_ARRAY );
	glDisableClientState( GL_NORMAL_ARRAY );
	glDisableClientState( GL_COLOR_ARRAY );
	glDisableClientState( GL_EDGE_FLAG_ARRAY );
	glDisableClientState( GL_INDEX_ARRAY );


	glEnableClientState( GL_VERTEX_ARRAY );
	glEnableClientState( GL_NORMAL_ARRAY );

	glVertexPointer( 3, GL_FLOAT, 3 * sizeof( float32 ), pMesh->get_vert_pointer() );
	glNormalPointer( GL_FLOAT, 3 * sizeof( float32 ), pMesh->get_norm_pointer() );


	if( pMesh->get_toon() ) {

		glDisable( GL_LIGHTING );
		glDisable( GL_TEXTURE_2D );
		glEnable( GL_POLYGON_OFFSET_LINE );

		//
		// render "toon" outline
		//
		glPolygonOffset( 2, 1.0 );
		glEnable( GL_BLEND );
		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
		glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
		glCullFace( GL_FRONT );
		glEnable( GL_CULL_FACE );

		glColor3fv( m_rWireColor );

		// outline

		glLineWidth( m_f32OuterWireSize );

		for( i = 0; i < pMesh->get_index_list_count(); i++ ) {
			IndexListC*	pList = pMesh->get_index_list( i );

			if( g_bCompiledVertexArray )
				glLockArraysEXT( 0, pMesh->get_vert_count() );

			glDrawElements( GL_TRIANGLES, pList->get_index_count(), GL_UNSIGNED_INT, pList->get_index_pointer() );

			if( g_bCompiledVertexArray )
				glUnlockArraysEXT();
		}

		// cusp edges

		glLineWidth( m_f32InnerWireSize );

		for( i = 0; i < pMesh->get_index_list_count(); i++ ) {

			IndexListC*	pList = pMesh->get_index_list( i );

			uint32*		pIndices = pList->get_index_pointer();
			uint8*		pEdges = pList->get_cusp_edge_pointer();
			float32*	pVerts = pMesh->get_vert_pointer();

			glBegin( GL_LINES );
			for( uint32 j = 0; j < pList->get_index_count() / 3; j++ ) {

				if( pEdges[0] ) {
					glVertex3fv( &pVerts[pIndices[0] * 3] );
					glVertex3fv( &pVerts[pIndices[1] * 3] );
				}

				if( pEdges[1] ) {
					glVertex3fv( &pVerts[pIndices[1] * 3] );
					glVertex3fv( &pVerts[pIndices[2] * 3] );
				}

				if( pEdges[2] ) {
					glVertex3fv( &pVerts[pIndices[2] * 3] );
					glVertex3fv( &pVerts[pIndices[0] * 3] );
				}

				pEdges += 3;
				pIndices += 3;
			}
			glEnd();
		}

		glDisableClientState( GL_EDGE_FLAG_ARRAY );

		glEnable( GL_LIGHTING );
		glDisable( GL_POLYGON_OFFSET_LINE );
		glPolygonOffset( 0, 0 );
		glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
		glCullFace( GL_BACK );
	}

	//
	// draw index lists
	//

	for( i = 0; i < pMesh->get_index_list_count(); i++ ) {

		IndexListC*	pList = pMesh->get_index_list( i );

		float32	f32Amb[4];
		float32	f32Diff[4];
		float32	f32Spec[4];
		float32	f32Emission[4];

		ColorC	rAmbient = pList->get_ambient();
		ColorC	rDiffuse = pList->get_diffuse();
		ColorC	rSpecular = pList->get_specular();

		f32Amb[0] = rAmbient[0] * 0.25f;
		f32Amb[1] = rAmbient[1] * 0.25f;
		f32Amb[2] = rAmbient[2] * 0.25f;
		f32Amb[3] = 1;
		glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, f32Amb );

		f32Diff[0] = rDiffuse[0];
		f32Diff[1] = rDiffuse[1];
		f32Diff[2] = rDiffuse[2];

//		if( pList->is_transparent() )
			f32Diff[3] = 1.0f - pList->get_transparency();
//		else
//			f32Diff[3] = 1.0f;
		
		glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, f32Diff );

		f32Spec[0] = rSpecular[0];
		f32Spec[1] = rSpecular[1];
		f32Spec[2] = rSpecular[2];
		f32Spec[3] = 1;
		glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, f32Spec );

		f32Emission[0] = rDiffuse[0] * pList->get_selfillum();
		f32Emission[1] = rDiffuse[1] * pList->get_selfillum();
		f32Emission[2] = rDiffuse[2] * pList->get_selfillum();
		f32Emission[3] = 1;
		glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, f32Emission );

		glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, 2 + (int32)(pList->get_shininess() * 126.0f) );

		
		// Texture stuff


		uint32	ui32TexUnit = 0;

		TexCoordListC*		pTexCoordList = 0;
		ImportableImageI*	pTex = 0;
		FileHandleC*		pHandle = 0;


		//
		// Diffuse texture
		//

		if( pList->get_diffuse_mapping_type() == MAPPING_EXPLICIT ) {
			pTexCoordList = pMesh->get_texcoord_list( pList->get_diffuse_tex_channel() );
		}

		pTex = 0;
		pHandle = pList->get_diffuse_texture();

		if( pHandle ) {
			pTex = (ImportableImageI*)pHandle->get_importable();
//			if( !pTex )
//				TRACE( "no texture found!\n" );
		}

		if( pTex ) {

			if( pList->get_diffuse_mapping_type() == MAPPING_EXPLICIT && pTexCoordList && pTexCoordList->get_texcoord_count() ) {

				glActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );
				glClientActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );

				glEnable( GL_TEXTURE_2D );
				pTex->eval_state( pContext->m_i32Time );
				pTex->bind_texture( pContext->m_pDevice, ui32TexUnit, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

				glEnableClientState( GL_TEXTURE_COORD_ARRAY );
				glTexCoordPointer( 2, GL_FLOAT, 2 * sizeof( float32 ), pTexCoordList->get_texcoord_pointer() );

				// Set diffuse material to white
				if( pTex->get_data_bpp() >  8 ) {
					f32Diff[0] = f32Diff[1] = f32Diff[2] = 1.0f;
					glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, f32Diff );
				}

				ui32TexUnit++;
			}
			else if( pList->get_diffuse_mapping_type() == MAPPING_SPHERICAL ) {

				glActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );
				glClientActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );

				glEnable( GL_TEXTURE_2D );
				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 );
				pTex->eval_state( pContext->m_i32Time );
				pTex->bind_texture( pContext->m_pDevice, ui32TexUnit, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

				glDisableClientState( GL_TEXTURE_COORD_ARRAY );

				// Set diffuse material to white
				f32Diff[0] = f32Diff[1] = f32Diff[2] = 1.0f;
				glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, f32Diff );

				ui32TexUnit++;
			}
		}


		//
		// Lightmap
		//


		if( pList->get_lightmap_mapping_type() == MAPPING_EXPLICIT )
			pTexCoordList = pMesh->get_texcoord_list( pList->get_lightmap_tex_channel() );

		pTex = 0;
		pHandle = pList->get_lightmap_texture();

		if( pHandle )
			pTex = (ImportableImageI*)pHandle->get_importable();

		if( pTex ) {

			if( pList->get_lightmap_mapping_type() == MAPPING_EXPLICIT && pTexCoordList && pTexCoordList->get_texcoord_count() ) {

				glActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );
				glClientActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );

				glEnable( GL_TEXTURE_2D );
				pTex->eval_state( pContext->m_i32Time );
				pTex->bind_texture( pContext->m_pDevice, ui32TexUnit, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

				glEnableClientState( GL_TEXTURE_COORD_ARRAY );
				glTexCoordPointer( 2, GL_FLOAT, 2 * sizeof( float32 ), pTexCoordList->get_texcoord_pointer() );

				ui32TexUnit++;
			}
			else if( pList->get_lightmap_mapping_type() == MAPPING_SPHERICAL ) {

				glActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );
				glClientActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );

				glEnable( GL_TEXTURE_2D );
				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 );
				pTex->eval_state( pContext->m_i32Time );
				pTex->bind_texture( pContext->m_pDevice, ui32TexUnit, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

				glDisableClientState( GL_TEXTURE_COORD_ARRAY );

				ui32TexUnit++;
			}
		}


		//
		// Reflection
		//

		if( pList->get_reflection_mapping_type() == MAPPING_EXPLICIT )
			pTexCoordList = pMesh->get_texcoord_list( pList->get_reflection_tex_channel() );

		pTex = 0;
		pHandle = pList->get_reflection_texture();

		if( pHandle )
			pTex = (ImportableImageI*)pHandle->get_importable();

		if( pTex ) {

			if( pList->get_reflection_mapping_type() == MAPPING_EXPLICIT && pTexCoordList && pTexCoordList->get_texcoord_count() ) {

				glActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );
				glClientActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );

				glEnable( GL_TEXTURE_2D );
				pTex->eval_state( pContext->m_i32Time );
				pTex->bind_texture( pContext->m_pDevice, ui32TexUnit, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );

				glEnableClientState( GL_TEXTURE_COORD_ARRAY );
				glTexCoordPointer( 2, GL_FLOAT, 2 * sizeof( float32 ), pTexCoordList->get_texcoord_pointer() );

				ui32TexUnit++;
			}
			else if( pList->get_reflection_mapping_type() == MAPPING_SPHERICAL ) {

				glActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );
				glClientActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );

				glEnable( GL_TEXTURE_2D );
				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 );
				pTex->eval_state( pContext->m_i32Time );
				pTex->bind_texture( pContext->m_pDevice, ui32TexUnit, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );

				glDisableClientState( GL_TEXTURE_COORD_ARRAY );

				ui32TexUnit++;
			}
		}


		bool	bDisableDepthWrite = false;

		if( pList->get_two_sided() )
			glDisable( GL_CULL_FACE );
		else
			glEnable( GL_CULL_FACE );

		if( pMesh->get_additive() ) {
			glBlendFunc( GL_SRC_ALPHA, GL_ONE );
			bDisableDepthWrite = true;
		}
		else {
			if( pList->is_transparent() && !pMesh->get_alphatest() ) {
				glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
				bDisableDepthWrite = true;
			}
			else
				glBlendFunc( GL_ONE, GL_ZERO );
		}

		if( pMesh->get_alphatest() ) {
			glEnable( GL_ALPHA_TEST );
			glAlphaFunc( GL_GEQUAL, 0.5f );
		}

		if( bDisableDepthWrite )
			glDepthMask( GL_FALSE );

		if( g_bCompiledVertexArray )
			glLockArraysEXT( 0, pMesh->get_vert_count() );

		glDrawElements( GL_TRIANGLES, pList->get_index_count(), GL_UNSIGNED_INT, pList->get_index_pointer() );

		if( g_bCompiledVertexArray )
			glUnlockArraysEXT();

		if( bDisableDepthWrite )
			glDepthMask( GL_TRUE );

		if( pMesh->get_alphatest() ) {
			glDisable( GL_ALPHA_TEST );
			glAlphaFunc( GL_ALWAYS, 0 );	// reset to default
		}

		glEnable( GL_CULL_FACE );

		// Disable used units
		for( int32 j = 0; j < ui32TexUnit; j++ )
		{
			glActiveTextureARB( GL_TEXTURE0_ARB + j );
			glClientActiveTextureARB( GL_TEXTURE0_ARB + j );
			glDisable( GL_TEXTURE_2D );
			glDisable( GL_TEXTURE_GEN_S );
			glDisable( GL_TEXTURE_GEN_T );
			glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
			glDisableClientState( GL_TEXTURE_COORD_ARRAY );
		}
	}

	glDisableClientState( GL_VERTEX_ARRAY );
	glDisableClientState( GL_NORMAL_ARRAY );

	glPopMatrix();

	return true;
}



void
EngineEffectC::render_mesh_spot( RenderContextS* pContext, MeshC* pMesh, const Matrix3C& rCamTM, Vector3C* pPlanes )
{

//	TRACE( "[%s]", pMesh->get_name() );

	uint32	i;

	Matrix3C	rTM = pMesh->get_tm();

	glPushMatrix();

	GLfloat	mat[16];

	mat[0]  = rTM[0][0];
	mat[1]  = rTM[0][1];
	mat[2]  = rTM[0][2];
	mat[3]  = 0;
	mat[4]  = rTM[1][0];
	mat[5]  = rTM[1][1];
	mat[6]  = rTM[1][2];
	mat[7]  = 0;
	mat[8]  = rTM[2][0];
	mat[9]  = rTM[2][1];
	mat[10] = rTM[2][2];
	mat[11] = 0;
	mat[12] = rTM[3][0];
	mat[13] = rTM[3][1];
	mat[14] = rTM[3][2];
	mat[15] = 1;

	glMultMatrixf( mat );


	glDisableClientState( GL_VERTEX_ARRAY );
	glDisableClientState( GL_NORMAL_ARRAY );
	glDisableClientState( GL_COLOR_ARRAY );
	glDisableClientState( GL_EDGE_FLAG_ARRAY );
	glDisableClientState( GL_INDEX_ARRAY );


	glEnableClientState( GL_VERTEX_ARRAY );
	glEnableClientState( GL_NORMAL_ARRAY );

	glVertexPointer( 3, GL_FLOAT, 3 * sizeof( float32 ), pMesh->get_vert_pointer() );
	glNormalPointer( GL_FLOAT, 3 * sizeof( float32 ), pMesh->get_norm_pointer() );


	//
	// draw index lists
	//

	for( i = 0; i < pMesh->get_index_list_count(); i++ ) {

//		TRACE( " - list %d\n", i );

		IndexListC*	pList = pMesh->get_index_list( i );

		float32	f32Amb[4] = { 0, 0, 0, 1 };
		float32	f32Diff[4];
		float32	f32Spec[4];
		float32	f32Emission[4] = { 0, 0, 0, 1 };

		ColorC	rDiffuse = pList->get_diffuse();
		ColorC	rSpecular = pList->get_specular();

		glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, f32Amb );

		float32	f32Alpha = 1.0f - pList->get_transparency();

		f32Diff[0] = rDiffuse[0] * f32Alpha;
		f32Diff[1] = rDiffuse[1] * f32Alpha;
		f32Diff[2] = rDiffuse[2] * f32Alpha;

		// alpha index
		f32Diff[3] = f32Alpha;
		glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, f32Diff );

		f32Spec[0] = rSpecular[0] * f32Alpha;
		f32Spec[1] = rSpecular[1] * f32Alpha;
		f32Spec[2] = rSpecular[2] * f32Alpha;
		f32Spec[3] = 1;
		glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, f32Spec );

		glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, f32Emission );

		glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, 2 + (int32)(pList->get_shininess() * 126.0f) );

//		TRACE( "   - material\n" );
		
		// Texture stuff
		TexCoordListC*		pTexCoordList = 0;
		ImportableImageI*	pTex = 0;
		FileHandleC*		pHandle = 0;
		uint32	ui32TexUnit = 0;


		//
		// Diffuse texture
		//

		if( pList->get_diffuse_mapping_type() == MAPPING_EXPLICIT ) {
			pTexCoordList = pMesh->get_texcoord_list( pList->get_diffuse_tex_channel() );
		}

		pTex = 0;
		pHandle = pList->get_diffuse_texture();

		if( pHandle ) {
			pTex = (ImportableImageI*)pHandle->get_importable();
//			if( !pTex )
//				TRACE( "no texture found!\n" );
		}

		if( pTex ) {

			if( pList->get_diffuse_mapping_type() == MAPPING_EXPLICIT && pTexCoordList && pTexCoordList->get_texcoord_count() )
			{
				glActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );
				glClientActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );

				glEnable( GL_TEXTURE_2D );
				pTex->eval_state( pContext->m_i32Time );
				pTex->bind_texture( pContext->m_pDevice, ui32TexUnit, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

				glEnableClientState( GL_TEXTURE_COORD_ARRAY );
				glTexCoordPointer( 2, GL_FLOAT, 2 * sizeof( float32 ), pTexCoordList->get_texcoord_pointer() );

				// Set diffuse material to white
				if( pTex->get_data_bpp() >  8 ) {
					f32Diff[0] = f32Diff[1] = f32Diff[2] = 1.0f;
					glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, f32Diff );
				}

				ui32TexUnit++;
			}
			else if( pList->get_diffuse_mapping_type() == MAPPING_SPHERICAL )
			{

				glActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );
				glClientActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );

				glEnable( GL_TEXTURE_2D );
				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 );
				pTex->eval_state( pContext->m_i32Time );
				pTex->bind_texture( pContext->m_pDevice, ui32TexUnit, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

				glDisableClientState( GL_TEXTURE_COORD_ARRAY );

				// Set diffuse material to white
				f32Diff[0] = f32Diff[1] = f32Diff[2] = 1.0f;
				glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, f32Diff );

				ui32TexUnit++;
			}
		}

		//
		// Lightmap
		//

		if( pList->get_lightmap_mapping_type() == MAPPING_EXPLICIT )
			pTexCoordList = pMesh->get_texcoord_list( pList->get_lightmap_tex_channel() );

		pTex = 0;
		pHandle = pList->get_lightmap_texture();

		if( pHandle )
			pTex = (ImportableImageI*)pHandle->get_importable();

		if( pTex ) {

			if( pList->get_lightmap_mapping_type() == MAPPING_EXPLICIT && pTexCoordList && pTexCoordList->get_texcoord_count() )
			{
				glActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );
				glClientActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );

				glEnable( GL_TEXTURE_2D );
				pTex->eval_state( pContext->m_i32Time );
				pTex->bind_texture( pContext->m_pDevice, ui32TexUnit, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

				glEnableClientState( GL_TEXTURE_COORD_ARRAY );
				glTexCoordPointer( 2, GL_FLOAT, 2 * sizeof( float32 ), pTexCoordList->get_texcoord_pointer() );

				ui32TexUnit++;
			}
			else if( pList->get_lightmap_mapping_type() == MAPPING_SPHERICAL )
			{
				glActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );
				glClientActiveTextureARB( GL_TEXTURE0_ARB + ui32TexUnit );

				glEnable( GL_TEXTURE_2D );
				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 );
				pTex->eval_state( pContext->m_i32Time );
				pTex->bind_texture( pContext->m_pDevice, ui32TexUnit, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

				glDisableClientState( GL_TEXTURE_COORD_ARRAY );

				ui32TexUnit++;
			}
		}

//		TRACE( "   - lightmap\n" );

		if( pList->get_two_sided() )
			glDisable( GL_CULL_FACE );
		else
			glEnable( GL_CULL_FACE );

		if( pMesh->get_alphatest() ) {
			glEnable( GL_ALPHA_TEST );
			glAlphaFunc( GL_GEQUAL, 0.5f );
		}

		// projective texture uses additive blending
		glBlendFunc( GL_SRC_ALPHA, GL_ONE );

		if( g_bCompiledVertexArray )
			glLockArraysEXT( 0, pMesh->get_vert_count() );

//		TRACE( "   - lock\n" );

		glDrawElements( GL_TRIANGLES, pList->get_index_count(), GL_UNSIGNED_INT, pList->get_index_pointer() );

		if( g_bCompiledVertexArray )
			glUnlockArraysEXT();

//		TRACE( "   - unlock\n" );

		glEnable( GL_CULL_FACE );

		if( pMesh->get_alphatest() ) {
			glDisable( GL_ALPHA_TEST );
			glAlphaFunc( GL_ALWAYS, 0 );	// reset to default
		}

		// Disable texture unit
		for( uint32 j = 0; j < ui32TexUnit; j++ )
		{
			glActiveTextureARB( GL_TEXTURE0_ARB + j );
			glClientActiveTextureARB( GL_TEXTURE0_ARB + j );
			glDisable( GL_TEXTURE_2D );
			glDisable( GL_TEXTURE_GEN_S );
			glDisable( GL_TEXTURE_GEN_T );
			glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
			glDisableClientState( GL_TEXTURE_COORD_ARRAY );
		}

	}

	glDisableClientState( GL_VERTEX_ARRAY );
	glDisableClientState( GL_NORMAL_ARRAY );

	glPopMatrix();

//	TRACE( "}\n" );
}



bool
EngineEffectC::render_mesh_index( RenderContextS* pContext, MeshC* pMesh,
						    const Matrix3C& rCamTM, Vector3C* pPlanes )
{
/*
	uint32		i;
	Matrix3C	rTM = pMesh->get_tm();

	//
	// check visibility
	//

	Vector3C	rMin, rMax;
	rMin = pMesh->get_bbox_min();
	rMax = pMesh->get_bbox_max();

	Vector3C	rBBox[8];
	rBBox[0] = Vector3C( rMin[0], rMin[1], rMin[2] );
	rBBox[1] = Vector3C( rMin[0], rMin[1], rMax[2] );
	rBBox[2] = Vector3C( rMax[0], rMin[1], rMax[2] );
	rBBox[3] = Vector3C( rMax[0], rMin[1], rMin[2] );
	rBBox[4] = Vector3C( rMin[0], rMax[1], rMin[2] );
	rBBox[5] = Vector3C( rMin[0], rMax[1], rMax[2] );
	rBBox[6] = Vector3C( rMax[0], rMax[1], rMax[2] );
	rBBox[7] = Vector3C( rMax[0], rMax[1], rMin[2] );

	uint32	ui32D[8];

	for( i = 0; i < 8; i++ ) {
		Vector3C	rVec = rBBox[i];

		rVec *= rTM;
		rVec *= rCamTM;

		ui32D[i] = 0;

		if( rVec.dot( pPlanes[0] ) < 0 ) ui32D[i] |= 1 << 0;
		if( rVec.dot( pPlanes[1] ) < 0 ) ui32D[i] |= 1 << 1;
		if( rVec.dot( pPlanes[2] ) < 0 ) ui32D[i] |= 1 << 2;
		if( rVec.dot( pPlanes[3] ) < 0 ) ui32D[i] |= 1 << 3;

	}

	uint32	ui32AndBits = ui32D[0] & ui32D[1] & ui32D[2] & ui32D[3] & ui32D[4] & ui32D[5] & ui32D[6] & ui32D[7];
	uint32	ui32OrBits = ui32D[0] | ui32D[1] | ui32D[2] | ui32D[3] | ui32D[4] | ui32D[5] | ui32D[6] | ui32D[7];

	if( ui32AndBits )					// All out
		return false;

	glPushMatrix();

	GLfloat	mat[16];

	mat[0]  = rTM[0][0];
	mat[1]  = rTM[0][1];
	mat[2]  = rTM[0][2];
	mat[3]  = 0;
	mat[4]  = rTM[1][0];
	mat[5]  = rTM[1][1];
	mat[6]  = rTM[1][2];
	mat[7]  = 0;
	mat[8]  = rTM[2][0];
	mat[9]  = rTM[2][1];
	mat[10] = rTM[2][2];
	mat[11] = 0;
	mat[12] = rTM[3][0];
	mat[13] = rTM[3][1];
	mat[14] = rTM[3][2];
	mat[15] = 1;

	glMultMatrixf( mat );


	glDisableClientState( GL_VERTEX_ARRAY );
	glDisableClientState( GL_NORMAL_ARRAY );
	glDisableClientState( GL_COLOR_ARRAY );
	glDisableClientState( GL_EDGE_FLAG_ARRAY );
	glDisableClientState( GL_INDEX_ARRAY );


	glEnableClientState( GL_VERTEX_ARRAY );

	glVertexPointer( 3, GL_FLOAT, 3 * sizeof( float32 ), pMesh->get_vert_pointer() );

	//
	// draw index lists
	//

	for( i = 0; i < pMesh->get_index_list_count(); i++ ) {

		IndexListC*	pList = pMesh->get_index_list( i );

		if( g_bCompiledVertexArray )
			glLockArraysEXT( 0, pMesh->get_vert_count() );

		glDrawElements( GL_TRIANGLES, pList->get_index_count(), GL_UNSIGNED_INT, pList->get_index_pointer() );

		if( g_bCompiledVertexArray )
			glUnlockArraysEXT();
	}

	glDisableClientState( GL_VERTEX_ARRAY );

	glPopMatrix();
*/


	uint32	i;

	Matrix3C	rTM = pMesh->get_tm();

	if( pMesh->is_transparent() )
		pMesh->sort_faces( rCamTM );

	//
	// check visibility
	//

	Vector3C	rMin, rMax;
	rMin = pMesh->get_bbox_min();
	rMax = pMesh->get_bbox_max();

	Vector3C	rBBox[8];
	rBBox[0] = Vector3C( rMin[0], rMin[1], rMin[2] );
	rBBox[1] = Vector3C( rMin[0], rMin[1], rMax[2] );
	rBBox[2] = Vector3C( rMax[0], rMin[1], rMax[2] );
	rBBox[3] = Vector3C( rMax[0], rMin[1], rMin[2] );
	rBBox[4] = Vector3C( rMin[0], rMax[1], rMin[2] );
	rBBox[5] = Vector3C( rMin[0], rMax[1], rMax[2] );
	rBBox[6] = Vector3C( rMax[0], rMax[1], rMax[2] );
	rBBox[7] = Vector3C( rMax[0], rMax[1], rMin[2] );

	uint32	ui32D[8];

	for( i = 0; i < 8; i++ ) {
		Vector3C	rVec = rBBox[i];
		rVec *= rTM;
		rVec *= rCamTM;

		ui32D[i] = 0;

		if( rVec.dot( pPlanes[0] ) < 0 ) ui32D[i] |= 1 << 0;
		if( rVec.dot( pPlanes[1] ) < 0 ) ui32D[i] |= 1 << 1;
		if( rVec.dot( pPlanes[2] ) < 0 ) ui32D[i] |= 1 << 2;
		if( rVec.dot( pPlanes[3] ) < 0 ) ui32D[i] |= 1 << 3;

	}

	uint32	ui32AndBits = ui32D[0] & ui32D[1] & ui32D[2] & ui32D[3] & ui32D[4] & ui32D[5] & ui32D[6] & ui32D[7];
	uint32	ui32OrBits = ui32D[0] | ui32D[1] | ui32D[2] | ui32D[3] | ui32D[4] | ui32D[5] | ui32D[6] | ui32D[7];


	if( ui32AndBits )					// All out
		return false;


	glPushMatrix();

	GLfloat	mat[16];

	mat[0]  = rTM[0][0];
	mat[1]  = rTM[0][1];
	mat[2]  = rTM[0][2];
	mat[3]  = 0;
	mat[4]  = rTM[1][0];
	mat[5]  = rTM[1][1];
	mat[6]  = rTM[1][2];
	mat[7]  = 0;
	mat[8]  = rTM[2][0];
	mat[9]  = rTM[2][1];
	mat[10] = rTM[2][2];
	mat[11] = 0;
	mat[12] = rTM[3][0];
	mat[13] = rTM[3][1];
	mat[14] = rTM[3][2];
	mat[15] = 1;

	glMultMatrixf( mat );


	glDisableClientState( GL_VERTEX_ARRAY );
	glDisableClientState( GL_NORMAL_ARRAY );
	glDisableClientState( GL_COLOR_ARRAY );
	glDisableClientState( GL_EDGE_FLAG_ARRAY );
	glDisableClientState( GL_INDEX_ARRAY );


	glEnableClientState( GL_VERTEX_ARRAY );
	glEnableClientState( GL_NORMAL_ARRAY );

	glVertexPointer( 3, GL_FLOAT, 3 * sizeof( float32 ), pMesh->get_vert_pointer() );
	glNormalPointer( GL_FLOAT, 3 * sizeof( float32 ), pMesh->get_norm_pointer() );


	//
	// draw index lists
	//

	for( i = 0; i < pMesh->get_index_list_count(); i++ ) {

		IndexListC*	pList = pMesh->get_index_list( i );

		ColorC	rDiffuse = pList->get_diffuse();
		float32	f32Alpha = 1.0f - pList->get_transparency();

		glColor4f( 1, 1, 1, f32Alpha );

		// Texture stuff
		TexCoordListC*		pTexCoordList = 0;
		ImportableImageI*	pTex = 0;
		FileHandleC*		pHandle = 0;

		//
		// Diffuse texture
		//

		if( pList->get_diffuse_mapping_type() == MAPPING_EXPLICIT ) {
			pTexCoordList = pMesh->get_texcoord_list( pList->get_diffuse_tex_channel() );
		}

		pTex = 0;
		pHandle = pList->get_diffuse_texture();

		if( pHandle ) {
			pTex = (ImportableImageI*)pHandle->get_importable();
			if( !pTex )
				TRACE( "no texture found!\n" );
		}

		bool	bHasTex = false;

		if( pTex ) {


			if( pList->get_diffuse_mapping_type() == MAPPING_EXPLICIT && pTexCoordList && pTexCoordList->get_texcoord_count() ) {

				glActiveTextureARB( GL_TEXTURE0_ARB );
				glClientActiveTextureARB( GL_TEXTURE0_ARB );

				glEnable( GL_TEXTURE_2D );
				pTex->eval_state( pContext->m_i32Time );
				pTex->bind_texture( pContext->m_pDevice, 0, IMAGE_WRAP | IMAGE_LINEAR );

				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

				glEnableClientState( GL_TEXTURE_COORD_ARRAY );
				glTexCoordPointer( 2, GL_FLOAT, 2 * sizeof( float32 ), pTexCoordList->get_texcoord_pointer() );

				// Set diffuse material to white

				bHasTex = true;
			}
			else if( pList->get_diffuse_mapping_type() == MAPPING_SPHERICAL ) {

				glActiveTextureARB( GL_TEXTURE0_ARB );
				glClientActiveTextureARB( GL_TEXTURE0_ARB );

				glEnable( GL_TEXTURE_2D );
				pTex->eval_state( pContext->m_i32Time );
				pTex->bind_texture( pContext->m_pDevice, 0, IMAGE_WRAP | IMAGE_LINEAR );
				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 );
				glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

				glDisableClientState( GL_TEXTURE_COORD_ARRAY );

				// Set diffuse material to white

				bHasTex = true;
			}

		}

		if( pList->get_two_sided() )
			glDisable( GL_CULL_FACE );
		else
			glEnable( GL_CULL_FACE );

		// projective texture uses additive blending
		glBlendFunc( GL_ONE, GL_ZERO );

		if( g_bCompiledVertexArray )
			glLockArraysEXT( 0, pMesh->get_vert_count() );

		glDrawElements( GL_TRIANGLES, pList->get_index_count(), GL_UNSIGNED_INT, pList->get_index_pointer() );

		if( g_bCompiledVertexArray )
			glUnlockArraysEXT();

		glEnable( GL_CULL_FACE );

		if( bHasTex )
		{
			// Disable texture unit
			glActiveTextureARB( GL_TEXTURE0_ARB );
			glClientActiveTextureARB( GL_TEXTURE0_ARB );
			glDisable( GL_TEXTURE_2D );
			glDisable( GL_TEXTURE_GEN_S );
			glDisable( GL_TEXTURE_GEN_T );
			glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
			glDisableClientState( GL_TEXTURE_COORD_ARRAY );
		}
	}

	glDisableClientState( GL_VERTEX_ARRAY );
	glDisableClientState( GL_NORMAL_ARRAY );

	glPopMatrix();


	return true;
}


void
EngineEffectC::render_mesh_dof( RenderContextS* pContext, MeshC* pMesh,
						    const Matrix3C& rCamTM, Vector3C* pPlanes )
{
	uint32		i;
	Matrix3C	rTM = pMesh->get_tm();

	//
	// check visibility
	//

	Vector3C	rMin, rMax;
	rMin = pMesh->get_bbox_min();
	rMax = pMesh->get_bbox_max();

	Vector3C	rBBox[8];
	rBBox[0] = Vector3C( rMin[0], rMin[1], rMin[2] );
	rBBox[1] = Vector3C( rMin[0], rMin[1], rMax[2] );
	rBBox[2] = Vector3C( rMax[0], rMin[1], rMax[2] );
	rBBox[3] = Vector3C( rMax[0], rMin[1], rMin[2] );
	rBBox[4] = Vector3C( rMin[0], rMax[1], rMin[2] );
	rBBox[5] = Vector3C( rMin[0], rMax[1], rMax[2] );
	rBBox[6] = Vector3C( rMax[0], rMax[1], rMax[2] );
	rBBox[7] = Vector3C( rMax[0], rMax[1], rMin[2] );

	uint32	ui32D[8];

	for( i = 0; i < 8; i++ ) {
		Vector3C	rVec = rBBox[i];

		rVec *= rTM;
		rVec *= rCamTM;

		ui32D[i] = 0;

		if( rVec.dot( pPlanes[0] ) < 0 ) ui32D[i] |= 1 << 0;
		if( rVec.dot( pPlanes[1] ) < 0 ) ui32D[i] |= 1 << 1;
		if( rVec.dot( pPlanes[2] ) < 0 ) ui32D[i] |= 1 << 2;
		if( rVec.dot( pPlanes[3] ) < 0 ) ui32D[i] |= 1 << 3;

	}

	uint32	ui32AndBits = ui32D[0] & ui32D[1] & ui32D[2] & ui32D[3] & ui32D[4] & ui32D[5] & ui32D[6] & ui32D[7];
	uint32	ui32OrBits = ui32D[0] | ui32D[1] | ui32D[2] | ui32D[3] | ui32D[4] | ui32D[5] | ui32D[6] | ui32D[7];

	if( ui32AndBits )					// All out
		return;

	glPushMatrix();

	GLfloat	mat[16];

	mat[0]  = rTM[0][0];
	mat[1]  = rTM[0][1];
	mat[2]  = rTM[0][2];
	mat[3]  = 0;
	mat[4]  = rTM[1][0];
	mat[5]  = rTM[1][1];
	mat[6]  = rTM[1][2];
	mat[7]  = 0;
	mat[8]  = rTM[2][0];
	mat[9]  = rTM[2][1];
	mat[10] = rTM[2][2];
	mat[11] = 0;
	mat[12] = rTM[3][0];
	mat[13] = rTM[3][1];
	mat[14] = rTM[3][2];
	mat[15] = 1;

	glMultMatrixf( mat );


	glDisableClientState( GL_VERTEX_ARRAY );
	glDisableClientState( GL_NORMAL_ARRAY );
	glDisableClientState( GL_COLOR_ARRAY );
	glDisableClientState( GL_EDGE_FLAG_ARRAY );
	glDisableClientState( GL_INDEX_ARRAY );


	glEnableClientState( GL_VERTEX_ARRAY );

	glVertexPointer( 3, GL_FLOAT, 3 * sizeof( float32 ), pMesh->get_vert_pointer() );

	//
	// draw index lists
	//

	for( i = 0; i < pMesh->get_index_list_count(); i++ ) {

		IndexListC*	pList = pMesh->get_index_list( i );

		if( g_bCompiledVertexArray )
			glLockArraysEXT( 0, pMesh->get_vert_count() );

		glDrawElements( GL_TRIANGLES, pList->get_index_count(), GL_UNSIGNED_INT, pList->get_index_pointer() );

		if( g_bCompiledVertexArray )
			glUnlockArraysEXT();
	}

	glDisableClientState( GL_VERTEX_ARRAY );

	glPopMatrix();

}


bool
EngineEffectC::render_shape( RenderContextS* pContext, ShapeC* pShape, const Matrix3C& rCamTM, Vector3C* pPlanes, bool bProjTex )
{
	uint32	i;

	Matrix3C	rTM = pShape->get_tm();

	//
	// check visibility
	//

	Vector3C	rMin, rMax;
	rMin = pShape->get_bbox_min();
	rMax = pShape->get_bbox_max();


	Vector3C	rBBox[8];
	rBBox[0] = Vector3C( rMin[0], rMin[1], rMin[2] );
	rBBox[1] = Vector3C( rMin[0], rMin[1], rMax[2] );
	rBBox[2] = Vector3C( rMax[0], rMin[1], rMax[2] );
	rBBox[3] = Vector3C( rMax[0], rMin[1], rMin[2] );
	rBBox[4] = Vector3C( rMin[0], rMax[1], rMin[2] );
	rBBox[5] = Vector3C( rMin[0], rMax[1], rMax[2] );
	rBBox[6] = Vector3C( rMax[0], rMax[1], rMax[2] );
	rBBox[7] = Vector3C( rMax[0], rMax[1], rMin[2] );

	uint32	ui32D[8];

	for( i = 0; i < 8; i++ ) {
		Vector3C	rVec = rBBox[i];

		rVec *= rTM;
		rVec *= rCamTM;

		ui32D[i] = 0;

		if( rVec.dot( pPlanes[0] ) < 0 ) ui32D[i] |= 1 << 0;
		if( rVec.dot( pPlanes[1] ) < 0 ) ui32D[i] |= 1 << 1;
		if( rVec.dot( pPlanes[2] ) < 0 ) ui32D[i] |= 1 << 2;
		if( rVec.dot( pPlanes[3] ) < 0 ) ui32D[i] |= 1 << 3;

	}

	uint32	ui32AndBits = ui32D[0] & ui32D[1] & ui32D[2] & ui32D[3] & ui32D[4] & ui32D[5] & ui32D[6] & ui32D[7];
	uint32	ui32OrBits = ui32D[0] | ui32D[1] | ui32D[2] | ui32D[3] | ui32D[4] | ui32D[5] | ui32D[6] | ui32D[7];


	if( ui32AndBits )					// All out
		return false;


	if( m_i32LineSmooth )
		glEnable( GL_LINE_SMOOTH );

	glLineWidth( m_f32LineWidth );

	glPushMatrix();

	GLfloat	mat[16];

	mat[0]  = rTM[0][0];
	mat[1]  = rTM[0][1];
	mat[2]  = rTM[0][2];
	mat[3]  = 0;
	mat[4]  = rTM[1][0];
	mat[5]  = rTM[1][1];
	mat[6]  = rTM[1][2];
	mat[7]  = 0;
	mat[8]  = rTM[2][0];
	mat[9]  = rTM[2][1];
	mat[10] = rTM[2][2];
	mat[11] = 0;
	mat[12] = rTM[3][0];
	mat[13] = rTM[3][1];
	mat[14] = rTM[3][2];
	mat[15] = 1;

	glMultMatrixf( mat );


	glEnableClientState( GL_VERTEX_ARRAY );

	glDisableClientState( GL_NORMAL_ARRAY );
	glDisableClientState( GL_COLOR_ARRAY );
	glDisableClientState( GL_EDGE_FLAG_ARRAY );
	glDisableClientState( GL_INDEX_ARRAY );


	glDisable( GL_TEXTURE_2D );


	float32	f32Mat[16];
	float32	f32MatTrans[16];
	glGetFloatv( GL_MODELVIEW_MATRIX, f32Mat );

	// transpose matrix (rotation only)
	for( uint32 ui32Row = 0; ui32Row < 3; ui32Row++ )
		for( uint32 ui32Col = 0; ui32Col < 3; ui32Col++ )
			f32MatTrans[ui32Row * 4 + ui32Col] = f32Mat[ui32Col * 4 + ui32Row];

	// use transposed axii

	float32	f32Amb[4];
	float32	f32Diff[4];
	float32	f32Spec[4];
	float32	f32Emission[4];

	ColorC	rAmbient = pShape->get_ambient();
	ColorC	rDiffuse = pShape->get_diffuse();
	ColorC	rSpecular = pShape->get_specular();

	f32Amb[0] = rAmbient[0] * 0.25f;
	f32Amb[1] = rAmbient[1] * 0.25f;
	f32Amb[2] = rAmbient[2] * 0.25f;
	f32Amb[3] = 1;
	glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, f32Amb );

	f32Diff[0] = rDiffuse[0];
	f32Diff[1] = rDiffuse[1];
	f32Diff[2] = rDiffuse[2];
	if( pShape->is_transparent() )
		f32Diff[3] = 1.0f - pShape->get_transparency();
	else
		f32Diff[3] = 1.0f;
		
	
	glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, f32Diff );

	f32Spec[0] = rSpecular[0];
	f32Spec[1] = rSpecular[1];
	f32Spec[2] = rSpecular[2];
	f32Spec[3] = 1;
	glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, f32Spec );

	f32Emission[0] = rDiffuse[0] * pShape->get_selfillum();
	f32Emission[1] = rDiffuse[1] * pShape->get_selfillum();
	f32Emission[2] = rDiffuse[2] * pShape->get_selfillum();
	f32Emission[3] = 1;
	glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, f32Emission );

	glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, 2 + (int32)(pShape->get_shininess() * 125.0f) );


	bool	bDisableDepthWrite = false;

	if( pShape->get_additive() ) {
		glBlendFunc( GL_SRC_ALPHA, GL_ONE );
		bDisableDepthWrite = true;
	}
	else {
		if( pShape->is_transparent() ) {
			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
			bDisableDepthWrite = true;
		}
		else
			glBlendFunc( GL_SRC_ALPHA, GL_ZERO );
	}

	if( bDisableDepthWrite )
		glDepthMask( GL_FALSE );

	glNormal3f( f32MatTrans[8], f32MatTrans[9], f32MatTrans[10] );


	for( i = 0; i < pShape->get_path_count(); i++ ) {
		PathC*	pPath = pShape->get_path( i );
		glVertexPointer( 3, GL_FLOAT, 3 * sizeof( float32 ), pPath->get_vertex_pointer() );

		if( g_bCompiledVertexArray )
			glLockArraysEXT( 0, pPath->get_vertex_count() );

		glDrawArrays( GL_LINE_STRIP, 0, pPath->get_vertex_count() );

		if( g_bCompiledVertexArray )
			glUnlockArraysEXT();
	}

	glDisableClientState( GL_VERTEX_ARRAY );

	if( bDisableDepthWrite )
		glDepthMask( GL_TRUE );

	if( m_i32LineSmooth )
		glDisable( GL_LINE_SMOOTH );

	glPopMatrix();

	return true;
}

void
EngineEffectC::render_shape_spot( RenderContextS* pContext, ShapeC* pShape, const Matrix3C& rCamTM, Vector3C* pPlanes, bool bProjTex )
{
	uint32	i;

	Matrix3C	rTM = pShape->get_tm();

	//
	// check visibility
	//

	Vector3C	rMin, rMax;
	rMin = pShape->get_bbox_min();
	rMax = pShape->get_bbox_max();


	Vector3C	rBBox[8];
	rBBox[0] = Vector3C( rMin[0], rMin[1], rMin[2] );
	rBBox[1] = Vector3C( rMin[0], rMin[1], rMax[2] );
	rBBox[2] = Vector3C( rMax[0], rMin[1], rMax[2] );
	rBBox[3] = Vector3C( rMax[0], rMin[1], rMin[2] );
	rBBox[4] = Vector3C( rMin[0], rMax[1], rMin[2] );
	rBBox[5] = Vector3C( rMin[0], rMax[1], rMax[2] );
	rBBox[6] = Vector3C( rMax[0], rMax[1], rMax[2] );
	rBBox[7] = Vector3C( rMax[0], rMax[1], rMin[2] );

	uint32	ui32D[8];

	for( i = 0; i < 8; i++ ) {
		Vector3C	rVec = rBBox[i];

		rVec *= rTM;
		rVec *= rCamTM;

		ui32D[i] = 0;

		if( rVec.dot( pPlanes[0] ) < 0 ) ui32D[i] |= 1 << 0;
		if( rVec.dot( pPlanes[1] ) < 0 ) ui32D[i] |= 1 << 1;
		if( rVec.dot( pPlanes[2] ) < 0 ) ui32D[i] |= 1 << 2;
		if( rVec.dot( pPlanes[3] ) < 0 ) ui32D[i] |= 1 << 3;

	}

	uint32	ui32AndBits = ui32D[0] & ui32D[1] & ui32D[2] & ui32D[3] & ui32D[4] & ui32D[5] & ui32D[6] & ui32D[7];
	uint32	ui32OrBits = ui32D[0] | ui32D[1] | ui32D[2] | ui32D[3] | ui32D[4] | ui32D[5] | ui32D[6] | ui32D[7];


	if( ui32AndBits )					// All out
		return;


	if( m_i32LineSmooth )
		glEnable( GL_LINE_SMOOTH );

	glLineWidth( m_f32LineWidth );

	glPushMatrix();

	GLfloat	mat[16];

	mat[0]  = rTM[0][0];
	mat[1]  = rTM[0][1];
	mat[2]  = rTM[0][2];
	mat[3]  = 0;
	mat[4]  = rTM[1][0];
	mat[5]  = rTM[1][1];
	mat[6]  = rTM[1][2];
	mat[7]  = 0;
	mat[8]  = rTM[2][0];
	mat[9]  = rTM[2][1];
	mat[10] = rTM[2][2];
	mat[11] = 0;
	mat[12] = rTM[3][0];
	mat[13] = rTM[3][1];
	mat[14] = rTM[3][2];
	mat[15] = 1;

	glMultMatrixf( mat );


	glEnableClientState( GL_VERTEX_ARRAY );

	glDisableClientState( GL_NORMAL_ARRAY );
	glDisableClientState( GL_COLOR_ARRAY );
	glDisableClientState( GL_EDGE_FLAG_ARRAY );
	glDisableClientState( GL_INDEX_ARRAY );


	glDisable( GL_TEXTURE_2D );


	float32	f32Mat[16];
	float32	f32MatTrans[16];
	glGetFloatv( GL_MODELVIEW_MATRIX, f32Mat );

	// transpose matrix (rotation only)
	for( uint32 ui32Row = 0; ui32Row < 3; ui32Row++ )
		for( uint32 ui32Col = 0; ui32Col < 3; ui32Col++ )
			f32MatTrans[ui32Row * 4 + ui32Col] = f32Mat[ui32Col * 4 + ui32Row];

	// use transposed axii

	float32	f32Amb[4];
	float32	f32Diff[4];
	float32	f32Spec[4];
	float32	f32Emission[4];

	ColorC	rAmbient = pShape->get_ambient();
	ColorC	rDiffuse = pShape->get_diffuse();
	ColorC	rSpecular = pShape->get_specular();

	f32Amb[0] = rAmbient[0] * 0.25f;
	f32Amb[1] = rAmbient[1] * 0.25f;
	f32Amb[2] = rAmbient[2] * 0.25f;
	f32Amb[3] = 1;
	glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, f32Amb );

	f32Diff[0] = rDiffuse[0];
	f32Diff[1] = rDiffuse[1];
	f32Diff[2] = rDiffuse[2];
	if( pShape->is_transparent() )
		f32Diff[3] = 1.0f - pShape->get_transparency();
	else
		f32Diff[3] = 1.0f;
		
	
	glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, f32Diff );

	f32Spec[0] = rSpecular[0];
	f32Spec[1] = rSpecular[1];
	f32Spec[2] = rSpecular[2];
	f32Spec[3] = 1;
	glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, f32Spec );

	f32Emission[0] = rDiffuse[0] * pShape->get_selfillum();
	f32Emission[1] = rDiffuse[1] * pShape->get_selfillum();
	f32Emission[2] = rDiffuse[2] * pShape->get_selfillum();
	f32Emission[3] = 1;
	glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, f32Emission );

	glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, 2 + (int32)(pShape->get_shininess() * 125.0f) );


	bool	bDisableDepthWrite = false;

	glBlendFunc( GL_SRC_ALPHA, GL_ONE );

	if( bDisableDepthWrite )
		glDepthMask( GL_FALSE );

	glNormal3f( f32MatTrans[8], f32MatTrans[9], f32MatTrans[10] );


	for( i = 0; i < pShape->get_path_count(); i++ ) {
		PathC*	pPath = pShape->get_path( i );
		glVertexPointer( 3, GL_FLOAT, 3 * sizeof( float32 ), pPath->get_vertex_pointer() );

		if( g_bCompiledVertexArray )
			glLockArraysEXT( 0, pPath->get_vertex_count() );

		glDrawArrays( GL_LINE_STRIP, 0, pPath->get_vertex_count() );

		if( g_bCompiledVertexArray )
			glUnlockArraysEXT();
	}

	glDisableClientState( GL_VERTEX_ARRAY );

	if( bDisableDepthWrite )
		glDepthMask( GL_TRUE );

	if( m_i32LineSmooth )
		glDisable( GL_LINE_SMOOTH );

	glPopMatrix();
}

bool
EngineEffectC::render_shape_index( RenderContextS* pContext, ShapeC* pShape, const Matrix3C& rCamTM, Vector3C* pPlanes, bool bProjTex )
{
	uint32	i;

	Matrix3C	rTM = pShape->get_tm();

	//
	// check visibility
	//

	Vector3C	rMin, rMax;
	rMin = pShape->get_bbox_min();
	rMax = pShape->get_bbox_max();


	Vector3C	rBBox[8];
	rBBox[0] = Vector3C( rMin[0], rMin[1], rMin[2] );
	rBBox[1] = Vector3C( rMin[0], rMin[1], rMax[2] );
	rBBox[2] = Vector3C( rMax[0], rMin[1], rMax[2] );
	rBBox[3] = Vector3C( rMax[0], rMin[1], rMin[2] );
	rBBox[4] = Vector3C( rMin[0], rMax[1], rMin[2] );
	rBBox[5] = Vector3C( rMin[0], rMax[1], rMax[2] );
	rBBox[6] = Vector3C( rMax[0], rMax[1], rMax[2] );
	rBBox[7] = Vector3C( rMax[0], rMax[1], rMin[2] );

	uint32	ui32D[8];

	for( i = 0; i < 8; i++ ) {
		Vector3C	rVec = rBBox[i];

		rVec *= rTM;
		rVec *= rCamTM;

		ui32D[i] = 0;

		if( rVec.dot( pPlanes[0] ) < 0 ) ui32D[i] |= 1 << 0;
		if( rVec.dot( pPlanes[1] ) < 0 ) ui32D[i] |= 1 << 1;
		if( rVec.dot( pPlanes[2] ) < 0 ) ui32D[i] |= 1 << 2;
		if( rVec.dot( pPlanes[3] ) < 0 ) ui32D[i] |= 1 << 3;

	}

	uint32	ui32AndBits = ui32D[0] & ui32D[1] & ui32D[2] & ui32D[3] & ui32D[4] & ui32D[5] & ui32D[6] & ui32D[7];
	uint32	ui32OrBits = ui32D[0] | ui32D[1] | ui32D[2] | ui32D[3] | ui32D[4] | ui32D[5] | ui32D[6] | ui32D[7];


	if( ui32AndBits )					// All out
		return false;


	glLineWidth( m_f32LineWidth );

	glPushMatrix();

	GLfloat	mat[16];

	mat[0]  = rTM[0][0];
	mat[1]  = rTM[0][1];
	mat[2]  = rTM[0][2];
	mat[3]  = 0;
	mat[4]  = rTM[1][0];
	mat[5]  = rTM[1][1];
	mat[6]  = rTM[1][2];
	mat[7]  = 0;
	mat[8]  = rTM[2][0];
	mat[9]  = rTM[2][1];
	mat[10] = rTM[2][2];
	mat[11] = 0;
	mat[12] = rTM[3][0];
	mat[13] = rTM[3][1];
	mat[14] = rTM[3][2];
	mat[15] = 1;

	glMultMatrixf( mat );


	glEnableClientState( GL_VERTEX_ARRAY );

	glDisableClientState( GL_NORMAL_ARRAY );
	glDisableClientState( GL_COLOR_ARRAY );
	glDisableClientState( GL_EDGE_FLAG_ARRAY );
	glDisableClientState( GL_INDEX_ARRAY );


	glDisable( GL_TEXTURE_2D );


	float32	f32Mat[16];
	float32	f32MatTrans[16];
	glGetFloatv( GL_MODELVIEW_MATRIX, f32Mat );

	// transpose matrix (rotation only)
	for( uint32 ui32Row = 0; ui32Row < 3; ui32Row++ )
		for( uint32 ui32Col = 0; ui32Col < 3; ui32Col++ )
			f32MatTrans[ui32Row * 4 + ui32Col] = f32Mat[ui32Col * 4 + ui32Row];

	// use transposed axii

	glBlendFunc( GL_ONE, GL_ZERO );

	glNormal3f( f32MatTrans[8], f32MatTrans[9], f32MatTrans[10] );

	for( i = 0; i < pShape->get_path_count(); i++ ) {
		PathC*	pPath = pShape->get_path( i );
		glVertexPointer( 3, GL_FLOAT, 3 * sizeof( float32 ), pPath->get_vertex_pointer() );

		if( g_bCompiledVertexArray )
			glLockArraysEXT( 0, pPath->get_vertex_count() );

		glDrawArrays( GL_LINE_STRIP, 0, pPath->get_vertex_count() );

		if( g_bCompiledVertexArray )
			glUnlockArraysEXT();
	}

	glDisableClientState( GL_VERTEX_ARRAY );

	glPopMatrix();

	return true;
}



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

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

bool
EngineEffectC::hit_test( const Vector2C& rPoint )
{
	return m_rBBox.contains( rPoint );
}

enum EngineEffectChunksE {
	CHUNK_ENGINEEFFECT_BASE =			0x1000,
	CHUNK_ENGINEEFFECT_TRANSGIZMO =		0x2000,
	CHUNK_ENGINEEFFECT_ATTRIBGIZMO =	0x3000,
	CHUNK_ENGINEEFFECT_TIMEGIZMO =		0x4000,
	CHUNK_ENGINEEFFECT_TOONGIZMO =		0x5000,
	CHUNK_ENGINEEFFECT_FOGGIZMO =		0x6000,
	CHUNK_ENGINEEFFECT_LINEGIZMO =		0x7000,
	CHUNK_ENGINEEFFECT_DOFGIZMO =		0x8000,
};

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_pTraGizmo->save( pSave );
	pSave->end_chunk();

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

	// time
	pSave->begin_chunk( CHUNK_ENGINEEFFECT_TIMEGIZMO, ENGINEEFFECT_VERSION );
		ui32Error = m_pTimeGizmo->save( pSave );
	pSave->end_chunk();

	// toon
	pSave->begin_chunk( CHUNK_ENGINEEFFECT_TOONGIZMO, ENGINEEFFECT_VERSION );
		ui32Error = m_pToonGizmo->save( pSave );
	pSave->end_chunk();

	// fog
	pSave->begin_chunk( CHUNK_ENGINEEFFECT_FOGGIZMO, ENGINEEFFECT_VERSION );
		ui32Error = m_pFogGizmo->save( pSave );
	pSave->end_chunk();

	// line
	pSave->begin_chunk( CHUNK_ENGINEEFFECT_LINEGIZMO, ENGINEEFFECT_VERSION );
		ui32Error = m_pLineGizmo->save( pSave );
	pSave->end_chunk();

	// line
	pSave->begin_chunk( CHUNK_ENGINEEFFECT_DOFGIZMO, ENGINEEFFECT_VERSION );
		ui32Error = m_pDOFGizmo->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_TRANSGIZMO:
			if( pLoad->get_chunk_version() == ENGINEEFFECT_VERSION )
				ui32Error = m_pTraGizmo->load( pLoad );
			break;

		case CHUNK_ENGINEEFFECT_ATTRIBGIZMO:
			if( pLoad->get_chunk_version() == ENGINEEFFECT_VERSION )
				ui32Error = m_pAttGizmo->load( pLoad );
			break;

		case CHUNK_ENGINEEFFECT_TIMEGIZMO:
			if( pLoad->get_chunk_version() == ENGINEEFFECT_VERSION )
				ui32Error = m_pTimeGizmo->load( pLoad );
			break;

		case CHUNK_ENGINEEFFECT_TOONGIZMO:
			if( pLoad->get_chunk_version() == ENGINEEFFECT_VERSION )
				ui32Error = m_pToonGizmo->load( pLoad );
			break;

		case CHUNK_ENGINEEFFECT_FOGGIZMO:
			if( pLoad->get_chunk_version() == ENGINEEFFECT_VERSION )
				ui32Error = m_pFogGizmo->load( pLoad );
			break;

		case CHUNK_ENGINEEFFECT_LINEGIZMO:
			if( pLoad->get_chunk_version() == ENGINEEFFECT_VERSION )
				ui32Error = m_pLineGizmo->load( pLoad );
			break;

		case CHUNK_ENGINEEFFECT_DOFGIZMO:
			if( pLoad->get_chunk_version() == ENGINEEFFECT_VERSION )
				ui32Error = m_pDOFGizmo->load( pLoad );
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

	return ui32Error;
}
