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



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

FlareDescC::FlareDescC()
{
	// empty
}

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

void*
FlareDescC::create()
{
	return (void*)FlareEffectC::create_new();
}

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

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

ClassIdC
FlareDescC::get_class_id() const
{
	return CLASS_FLARE_EFFECT;
};

const char*
FlareDescC::get_name() const
{
	return "Flare Effect";
}

const char*
FlareDescC::get_desc() const
{
	return "Flare Effect";
}

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

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

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

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

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

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

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

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


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

FlareDescC		g_rFlareDesc;


//////////////////////////////////////////////////////////////////////////
//
// The flare effect
//

FlareEffectC::FlareEffectC() :
	m_pCam( 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 Flare gizmo.
	//
	m_pFlareGizmo = AutoGizmoC::create_new( this, "Flare", ID_GIZMO_FLARE );

	m_pFlareGizmo->add_parameter( ParamColorC::create_new( m_pFlareGizmo, "Color", ColorC( 1, 1, 1, 1 ), ID_FLARE_COLOR,
		PARAM_STYLE_COLORPICKER_RGBA, PARAM_ANIMATABLE ) );

	ParamIntC*	pParamLighting = ParamIntC::create_new( m_pFlareGizmo, "Use Lighting", 1, ID_FLARE_USE_LIGHTING,
		PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 1 );
	pParamLighting->add_label( 0, "Off" );
	pParamLighting->add_label( 1, "On" );
	m_pFlareGizmo->add_parameter( pParamLighting );

	ParamIntC*	pRenderMode = ParamIntC::create_new( m_pFlareGizmo, "Render Mode", 1, ID_FLARE_RENDER_MODE,
		PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 2 );
	pRenderMode->add_label( 0, "Normal" );
	pRenderMode->add_label( 1, "Add" );
	pRenderMode->add_label( 2, "Mult" );
	m_pFlareGizmo->add_parameter( pRenderMode );

	m_pFlareGizmo->add_parameter( ParamFloatC::create_new( m_pFlareGizmo, "Extrude", 0, ID_FLARE_EXTRUDE,
						PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0, 0, 0.1f ) );

	m_pFlareGizmo->add_parameter( ParamFloatC::create_new( m_pFlareGizmo, "Size", 4, ID_FLARE_SIZE,
						PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0, 0, 0.1f ) );

	m_pFlareGizmo->add_parameter( ParamFileC::create_new( m_pFlareGizmo, "Image", SUPERCLASS_IMAGE, NULL_CLASSID, ID_FLARE_IMAGE, PARAM_STYLE_FILE, PARAM_ANIMATABLE ) );

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

}

FlareEffectC::FlareEffectC( EditableI* pOriginal ) :
	EffectI( pOriginal ),
	m_pTraGizmo( 0 ),
	m_pAttGizmo( 0 ),
	m_pFlareGizmo( 0 ),
	m_pTimeGizmo( 0 ),
	m_pCam( 0 )
{
	// clone constructor. initialize only.
}

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

	// Release gizmos.
	m_pTraGizmo->release();
	m_pAttGizmo->release();
	m_pFlareGizmo->release();
	m_pTimeGizmo->release();
}

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

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

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

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

	FlareEffectC*	pEffect = (FlareEffectC*)pEditable;
	m_pTraGizmo->copy( pEffect->m_pTraGizmo );
	m_pAttGizmo->copy( pEffect->m_pAttGizmo );
	m_pFlareGizmo->copy( pEffect->m_pFlareGizmo );
	m_pTimeGizmo->copy( pEffect->m_pTimeGizmo );
}

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

	FlareEffectC*	pEffect = (FlareEffectC*)pEditable;
	m_pTraGizmo = pEffect->m_pTraGizmo;
	m_pAttGizmo = pEffect->m_pAttGizmo;
	m_pFlareGizmo = pEffect->m_pFlareGizmo;
	m_pTimeGizmo = pEffect->m_pTimeGizmo;
}

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

GizmoI*
FlareEffectC::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_FLARE:
		return m_pFlareGizmo;
	case ID_GIZMO_TIME:
		return m_pTimeGizmo;
	}

	return 0;
}

ClassIdC
FlareEffectC::get_class_id()
{
	return CLASS_FLARE_EFFECT;
}

const char*
FlareEffectC::get_class_name()
{
	return "Flare Effect";
}

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

	// Get the file parameter.
	ParamFileC*	pParam = (ParamFileC*)m_pAttGizmo->get_parameter( 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*
FlareEffectC::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
FlareEffectC::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( ID_ATTRIBUTE_FILE );
		ParamIntC*		pCamera = (ParamIntC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_CAMERA );
		ParamFloatC*	pFrame = (ParamFloatC*)m_pTimeGizmo->get_parameter_by_id( ID_TIME_FRAME );

		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() );
			pCamera->end_editing( pOldUndo );

		}
	}

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

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

	if( ui32Reason == INIT_INITIAL_UPDATE ) {
		init_gl_extensions();
	}
}

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

	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( ID_ATTRIBUTE_SIZE ))->get_val( i32Time, rSize );
	rSize *= 0.5f;

	m_rSize = rSize;

	Vector2C	rMin, rMax;

	rMin = -rSize;
	rMax = rSize;

	rMin *= m_rTM;
	rMax *= m_rTM;

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


	m_pCam = 0;

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

	((ParamFileC*)m_pAttGizmo->get_parameter( 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( ID_ATTRIBUTE_CAMERA ))->get_val( i32Time, i32Val );
		m_pCam = pImp->get_camera( i32Val );
	}

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

	((ParamColorC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_CLEARCOLOR ))->get_val( i32Time, m_rClearColor );
	((ParamIntC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_CLEAR ))->get_val( i32Time, m_i32ClearFlags );

	((ParamIntC*)m_pFlareGizmo->get_parameter( ID_FLARE_USE_LIGHTING ))->get_val( i32Time, m_i32FlareUseLighting );
	((ParamIntC*)m_pFlareGizmo->get_parameter( ID_FLARE_RENDER_MODE ))->get_val( i32Time, m_i32FlareRenderMode );
	((ParamFloatC*)m_pFlareGizmo->get_parameter( ID_FLARE_EXTRUDE ))->get_val( i32Time, m_f32FlareExtrude );
	((ParamFloatC*)m_pFlareGizmo->get_parameter( ID_FLARE_SIZE ))->get_val( i32Time, m_f32FlareSize );
	((ParamColorC*)m_pFlareGizmo->get_parameter( ID_FLARE_COLOR ))->get_val( i32Time, m_rFlareColor );


	//
	// 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( !m_pCam )
		return;


	int32	i32Frame = pImp->get_current_frame();

	m_pCam->eval_state( i32Frame );

	Vector3C	rCamPos, rCamTgt;
	float		f32FOV;

	rCamPos = m_pCam->get_position();
	rCamTgt = m_pCam->get_target_position();
	f32FOV = m_pCam->get_fov();

	pViewport->set_perspective( m_rBBox, f32FOV / (float32)M_PI * 180.0f, m_f32Aspect, m_pCam->get_near_plane() + 1.0f, m_pCam->get_far_plane() );

	if( m_i32ClearFlags == 1 ) {
		glClear( GL_DEPTH_BUFFER_BIT );
	}
	else if( m_i32ClearFlags == 2 ) {
		glClearColor( m_rClearColor[0], m_rClearColor[1], m_rClearColor[2], m_rClearColor[3] );
		glClear( GL_COLOR_BUFFER_BIT );
	}
	else if( m_i32ClearFlags == 3 ) {
		glClearColor( m_rClearColor[0], m_rClearColor[1], m_rClearColor[2], m_rClearColor[3] );
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	}


	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	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];

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

	glShadeModel( GL_SMOOTH );

	glEnable( GL_NORMALIZE  );
	glEnable( GL_DEPTH_TEST );
	glEnable( GL_BLEND );
	glDepthFunc( GL_LEQUAL );
	glEnable( GL_DEPTH_TEST );
	glDisable( GL_ALPHA_TEST );
	glCullFace( GL_BACK );
	glEnable( GL_CULL_FACE );
	glLightModeli( GL_LIGHT_MODEL_TWO_SIDE , 0 );
	
	glDepthMask( GL_FALSE );

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

			pLight->eval_state( i32Frame );

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

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

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

			glEnable( GL_LIGHT0 + i );

			glLightf( GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION, 1 );
			glLightf( GL_LIGHT0 + i, GL_LINEAR_ATTENUATION , 0 );
			glLightf( GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION , 0 );

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

		glEnable( GL_LIGHTING );
	}
	else {
		glDisable( GL_LIGHTING );
	}


	if( g_bSeparateSpec )
		glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR );

	// 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 );
		if( pItem->get_type() == CGITEM_MESH ) {
			MeshC*	pMesh = (MeshC*)pItem;
			pMesh->eval_geom( i32Frame );
		}
	}


	//
	// sort objects & find maximum number of vertices
	//

	std::vector<CGItemSortS>	rSortList;
	int32						i32MaxVerts = 0;

	for( i = 0; i < pImp->get_scenegraphitem_count(); i++ ) {
		ScenegraphItemI*	pItem = pImp->get_scenegraphitem( i );
		if( pItem ) {
			if( pItem->get_type() == CGITEM_MESH ) {
				MeshC*		pMesh = (MeshC*)pItem;
				CGItemSortS	rItem;
				rItem.m_pItem = pItem;
				rItem.m_rPos = pMesh->get_position() * rCamTM;
				rSortList.push_back( rItem );

				i32MaxVerts = __max( i32MaxVerts, (int32)pMesh->get_vert_count() );
			}
			else if( pItem->get_type() == CGITEM_SHAPE ) {
				ShapeC*	pShape = (ShapeC*)pItem;
				CGItemSortS	rItem;
				rItem.m_pItem = pItem;
				rItem.m_rPos = pShape->get_position() * rCamTM;
				rSortList.push_back( rItem );

				uint32	ui32VertCount = 0;
				for( uint32 j = 0; j < pShape->get_path_count(); j++ )
					ui32VertCount = pShape->get_path( j )->get_knot_count();

				i32MaxVerts = __max( i32MaxVerts, (int32)ui32VertCount );
			}
		}
	}

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

	// make sure there's enough psace for every vertex
	if( m_rSortList.size() < (uint32)i32MaxVerts )
		m_rSortList.resize( i32MaxVerts );

	if( m_rVertices.size() < (uint32)(i32MaxVerts * 3 * 4) )
		m_rVertices.resize( (i32MaxVerts * 3 * 4) );

	if( m_rNormals.size() < (uint32)(i32MaxVerts * 3 * 4) )
		m_rNormals.resize( (i32MaxVerts * 3 * 4) );

	if( m_rTexCoords.size() < (uint32)(i32MaxVerts * 2 * 4) ) {
		m_rTexCoords.resize( (i32MaxVerts * 2 * 4) );

		float32*	pCoords = &(m_rTexCoords[0]);
		for( uint32 i = 0; i < i32MaxVerts; i++ ) {
			pCoords[0] = 0;
			pCoords[1] = 1;

			pCoords[2] = 1;
			pCoords[3] = 1;

			pCoords[4] = 1;
			pCoords[5] = 0;

			pCoords[6] = 0;
			pCoords[7] = 0;

			pCoords += (2 * 4);
		}
	}


	//
	// build clipping planes
	//
	float32	f32XMin, f32XMax, f32YMin, f32YMax;
	float32	f32Aspect = 1;
	
	if( m_rSize[1] != 0 )
		f32Aspect = m_rSize[1] / m_rSize[0];

	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];
	Vector3C	rPlanes[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
	rPlanes[0] = rFrustumVerts[3].cross( rFrustumVerts[0] ).normalize();

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

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

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

	//
	// Setup texture
	//

	ImportableImageI*	pImpImage = 0;
	FileHandleC*		pImageHandle;
	int32				i32ImageFileTime;
	((ParamFileC*)m_pFlareGizmo->get_parameter( ID_FLARE_IMAGE ))->get_file( i32Time, pImageHandle, i32ImageFileTime );
	if( pImageHandle )
		pImpImage = (ImportableImageI*)pImageHandle->get_importable();
	if( pImpImage ) {
		pImpImage->eval_state( i32ImageFileTime );
		pImpImage->bind_texture( pDevice, 0, IMAGE_CLAMP | IMAGE_LINEAR );
		glEnable( GL_TEXTURE_2D );
	}
	else
		glDisable( GL_TEXTURE_2D );


	if( m_i32FlareUseLighting ) {
		float32	f32Dark[4] = { 0.25f, 0.25f, 0.25f, 1 };
		float32	f32White[4] = { 1, 1, 1, 1 };
		float32	f32Black[4] = { 0, 0, 0, 1 };

		glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, f32Dark );

		if( m_i32FlareRenderMode == RENDERMODE_MULT ) {
			float32	f32Flare[4];
			f32Flare[0] = m_rFlareColor[0] * m_rFlareColor[3];
			f32Flare[1] = m_rFlareColor[1] * m_rFlareColor[3];
			f32Flare[2] = m_rFlareColor[2] * m_rFlareColor[3];
			f32Flare[3] = m_rFlareColor[3];
			glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, f32Flare );
		}
		else
			glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, m_rFlareColor );

		glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, f32White );
		glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, f32Black );
		glMateriali( GL_FRONT_AND_BACK, GL_SHININESS, 25 );
	}
	else {
		if( m_i32FlareRenderMode == RENDERMODE_MULT )
			glColor4f( m_rFlareColor[0] * m_rFlareColor[3], m_rFlareColor[1] * m_rFlareColor[3], m_rFlareColor[2] * m_rFlareColor[3], m_rFlareColor[3] );
		else
			glColor4fv( m_rFlareColor );
	}

	//
	// draw objects
	//

	if( m_i32FlareRenderMode == RENDERMODE_NORMAL ) {

		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

		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() )
					render_mesh_sorted( &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() )
					render_shape_sorted( &rRenderContext, pShape, rCamTM, rPlanes );
			}
		}
	}
	else {

		if( m_i32FlareRenderMode == RENDERMODE_ADD )
			glBlendFunc( GL_SRC_ALPHA, GL_ONE );
		else
			glBlendFunc( GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA );


		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() )
					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() )
					render_shape( &rRenderContext, pShape, rCamTM, rPlanes );
			}
		}
	}

	if( m_i32FlareUseLighting ) {
		for( i = 0; i < pImp->get_light_count(); i++ )
			glDisable( GL_LIGHT0 + i );
	}

	glDepthMask( GL_TRUE );

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

}



void
FlareEffectC::render_mesh( 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];

		// transparent thingies has cam transform in it
//		if( !pMesh->is_transformed() )
		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();

//	if( !pMesh->is_transformed() ) {

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

	float32*	pVerts = pMesh->get_vert_pointer();
	float32*	pNorms = pMesh->get_norm_pointer();

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

	rAxisX *= m_f32FlareSize;
	rAxisY *= m_f32FlareSize;

	float32		f32Vert[4][3];
	Vector3C	rOffset[4];

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

	float32*	pOutVerts = &(m_rVertices[0]);
	float32*	pOutNorms = &(m_rNormals[0]);

	if( m_f32FlareExtrude > 0 ) {

//		glBegin( GL_QUADS );

		float32	f32V[3];
		for( i = 0; i < pMesh->get_vert_count(); i++ ) {

			f32V[0] = pVerts[0] + pNorms[0] * m_f32FlareExtrude;
			f32V[1] = pVerts[1] + pNorms[1] * m_f32FlareExtrude;
			f32V[2] = pVerts[2] + pNorms[2] * m_f32FlareExtrude;

			f32Vert[0][0] = f32V[0] + rOffset[0][0];
			f32Vert[0][1] = f32V[1] + rOffset[0][1];
			f32Vert[0][2] = f32V[2] + rOffset[0][2];

			f32Vert[1][0] = f32V[0] + rOffset[1][0];
			f32Vert[1][1] = f32V[1] + rOffset[1][1];
			f32Vert[1][2] = f32V[2] + rOffset[1][2];

			f32Vert[2][0] = f32V[0] + rOffset[2][0];
			f32Vert[2][1] = f32V[1] + rOffset[2][1];
			f32Vert[2][2] = f32V[2] + rOffset[2][2];

			f32Vert[3][0] = f32V[0] + rOffset[3][0];
			f32Vert[3][1] = f32V[1] + rOffset[3][1];
			f32Vert[3][2] = f32V[2] + rOffset[3][2];

/*			glNormal3fv( pNorms );

			glTexCoord2f( 0, 1 );
			glVertex3fv( f32Vert[0] );

			glTexCoord2f( 1, 1 );
			glVertex3fv( f32Vert[1] );

			glTexCoord2f( 1, 0 );
			glVertex3fv( f32Vert[2] );

			glTexCoord2f( 0, 0 );
			glVertex3fv( f32Vert[3] );
*/

			pOutVerts[0] = f32V[0] + rOffset[0][0];
			pOutVerts[1] = f32V[1] + rOffset[0][1];
			pOutVerts[2] = f32V[2] + rOffset[0][2];

			pOutVerts[3] = f32V[0] + rOffset[1][0];
			pOutVerts[4] = f32V[1] + rOffset[1][1];
			pOutVerts[5] = f32V[2] + rOffset[1][2];

			pOutVerts[6] = f32V[0] + rOffset[2][0];
			pOutVerts[7] = f32V[1] + rOffset[2][1];
			pOutVerts[8] = f32V[2] + rOffset[2][2];

			pOutVerts[9] = f32V[0] + rOffset[3][0];
			pOutVerts[10] = f32V[1] + rOffset[3][1];
			pOutVerts[11] = f32V[2] + rOffset[3][2];
			pOutVerts += 12;

			pOutNorms[0] = pNorms[0];
			pOutNorms[1] = pNorms[1];
			pOutNorms[2] = pNorms[2];

			pOutNorms[3] = pNorms[0];
			pOutNorms[4] = pNorms[1];
			pOutNorms[5] = pNorms[2];

			pOutNorms[6] = pNorms[0];
			pOutNorms[7] = pNorms[1];
			pOutNorms[8] = pNorms[2];

			pOutNorms[9] = pNorms[0];
			pOutNorms[10] = pNorms[1];
			pOutNorms[11] = pNorms[2];
			pOutNorms += 12;

			pVerts += 3;
			pNorms += 3;
		}

//		glEnd();

		glEnableClientState( GL_VERTEX_ARRAY );
		glEnableClientState( GL_NORMAL_ARRAY );
		glEnableClientState( GL_TEXTURE_COORD_ARRAY );
		glDisableClientState( GL_INDEX_ARRAY );

		glVertexPointer( 3, GL_FLOAT, 3 * sizeof( float32 ), &(m_rVertices[0]) );
		glNormalPointer( GL_FLOAT, 3 * sizeof( float32 ), &(m_rNormals[0]) );
		glTexCoordPointer( 2, GL_FLOAT, 2 * sizeof( float32 ), &(m_rTexCoords[0]) );

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

		glDrawArrays( GL_QUADS, 0, pMesh->get_vert_count() * 4 );

		if( g_bCompiledVertexArray )
			glUnlockArraysEXT();

		glDisableClientState( GL_VERTEX_ARRAY );
		glDisableClientState( GL_NORMAL_ARRAY );
		glDisableClientState( GL_TEXTURE_COORD_ARRAY );
	}
	else {

//		glBegin( GL_QUADS );

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

			f32Vert[0][0] = pVerts[0] + rOffset[0][0];
			f32Vert[0][1] = pVerts[1] + rOffset[0][1];
			f32Vert[0][2] = pVerts[2] + rOffset[0][2];

			f32Vert[1][0] = pVerts[0] + rOffset[1][0];
			f32Vert[1][1] = pVerts[1] + rOffset[1][1];
			f32Vert[1][2] = pVerts[2] + rOffset[1][2];

			f32Vert[2][0] = pVerts[0] + rOffset[2][0];
			f32Vert[2][1] = pVerts[1] + rOffset[2][1];
			f32Vert[2][2] = pVerts[2] + rOffset[2][2];

			f32Vert[3][0] = pVerts[0] + rOffset[3][0];
			f32Vert[3][1] = pVerts[1] + rOffset[3][1];
			f32Vert[3][2] = pVerts[2] + rOffset[3][2];

/*			glNormal3fv( pNorms );

			glTexCoord2f( 0, 1 );
			glVertex3fv( f32Vert[0] );

			glTexCoord2f( 1, 1 );
			glVertex3fv( f32Vert[1] );

			glTexCoord2f( 1, 0 );
			glVertex3fv( f32Vert[2] );

			glTexCoord2f( 0, 0 );
			glVertex3fv( f32Vert[3] );*/

			pOutVerts[0] = pVerts[0] + rOffset[0][0];
			pOutVerts[1] = pVerts[1] + rOffset[0][1];
			pOutVerts[2] = pVerts[2] + rOffset[0][2];

			pOutVerts[3] = pVerts[0] + rOffset[1][0];
			pOutVerts[4] = pVerts[1] + rOffset[1][1];
			pOutVerts[5] = pVerts[2] + rOffset[1][2];

			pOutVerts[6] = pVerts[0] + rOffset[2][0];
			pOutVerts[7] = pVerts[1] + rOffset[2][1];
			pOutVerts[8] = pVerts[2] + rOffset[2][2];

			pOutVerts[9] = pVerts[0] + rOffset[3][0];
			pOutVerts[10] = pVerts[1] + rOffset[3][1];
			pOutVerts[11] = pVerts[2] + rOffset[3][2];
			pOutVerts += 12;

			pOutNorms[0] = pNorms[0];
			pOutNorms[1] = pNorms[1];
			pOutNorms[2] = pNorms[2];

			pOutNorms[3] = pNorms[0];
			pOutNorms[4] = pNorms[1];
			pOutNorms[5] = pNorms[2];

			pOutNorms[6] = pNorms[0];
			pOutNorms[7] = pNorms[1];
			pOutNorms[8] = pNorms[2];

			pOutNorms[9] = pNorms[0];
			pOutNorms[10] = pNorms[1];
			pOutNorms[11] = pNorms[2];
			pOutNorms += 12;

			pVerts += 3;
			pNorms += 3;
		}

//		glEnd();

		glEnableClientState( GL_VERTEX_ARRAY );
		glEnableClientState( GL_NORMAL_ARRAY );
		glEnableClientState( GL_TEXTURE_COORD_ARRAY );
		glDisableClientState( GL_INDEX_ARRAY );

		glVertexPointer( 3, GL_FLOAT, 3 * sizeof( float32 ), &(m_rVertices[0]) );
		glNormalPointer( GL_FLOAT, 3 * sizeof( float32 ), &(m_rNormals[0]) );
		glTexCoordPointer( 2, GL_FLOAT, 2 * sizeof( float32 ), &(m_rTexCoords[0]) );

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

		glDrawArrays( GL_QUADS, 0, pMesh->get_vert_count() * 4 );

		if( g_bCompiledVertexArray )
			glUnlockArraysEXT();

		glDisableClientState( GL_VERTEX_ARRAY );
		glDisableClientState( GL_NORMAL_ARRAY );
		glDisableClientState( GL_TEXTURE_COORD_ARRAY );
	}

	glPopMatrix();
}

void
FlareEffectC::render_mesh_sorted( RenderContextS* pContext, MeshC* pMesh,
						    const Matrix3C& rCamTM, Vector3C* pPlanes )
{
	if( !pMesh->get_vert_count() )
		return;

	int32	i;

	Matrix3C	rTM = pMesh->get_tm();
	Matrix3C	rMultTM;


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

		// transparent thingies has cam transform in it
//		if( !pMesh->is_transformed() )
		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();

//	if( !pMesh->is_transformed() ) {

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

		rMultTM = rTM * rCamTM;
//	}
//	else {
//		rMultTM = rCamTM;
//	}

	float32*	pVerts = pMesh->get_vert_pointer();
	float32*	pNorms = pMesh->get_norm_pointer();


	// Sort vertices
	int32		i32VertCount = pMesh->get_vert_count();
	float32		f32Depth;
	float32*	pSortVerts = pVerts;
	uint32		ui32Idx = 0;

	for( i = 0; i < i32VertCount; i++ ) {
		f32Depth = pSortVerts[0] * rMultTM[0][2] + pSortVerts[1] * rMultTM[1][2] + pSortVerts[2] * rMultTM[2][2] + rMultTM[3][2];
		m_rSortList[i].m_i32Depth = *(int32*)&f32Depth;
		m_rSortList[i].m_ui32Idx = ui32Idx;
		pSortVerts += 3;
		ui32Idx += 3;
	}

	std::sort( m_rSortList.begin(), m_rSortList.begin() + i32VertCount );


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

	rAxisX *= m_f32FlareSize;
	rAxisY *= m_f32FlareSize;

	float32		f32Vert[4][3];
	Vector3C	rOffset[4];

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

	float32*	pNormal;
	float32*	pVertex;

	float32*	pOutVerts = &(m_rVertices[0]);
	float32*	pOutNorms = &(m_rNormals[0]);

	if( m_f32FlareExtrude > 0 ) {

//		glBegin( GL_QUADS );

		float32	f32V[3];
		for( i = 0; i < i32VertCount; i++ ) {

			pNormal = &pNorms[m_rSortList[i].m_ui32Idx];
			pVertex = &pVerts[m_rSortList[i].m_ui32Idx];

			f32V[0] = pVertex[0] + pNormal[0] * m_f32FlareExtrude;
			f32V[1] = pVertex[1] + pNormal[1] * m_f32FlareExtrude;
			f32V[2] = pVertex[2] + pNormal[2] * m_f32FlareExtrude;

			f32Vert[0][0] = f32V[0] + rOffset[0][0];
			f32Vert[0][1] = f32V[1] + rOffset[0][1];
			f32Vert[0][2] = f32V[2] + rOffset[0][2];

			f32Vert[1][0] = f32V[0] + rOffset[1][0];
			f32Vert[1][1] = f32V[1] + rOffset[1][1];
			f32Vert[1][2] = f32V[2] + rOffset[1][2];

			f32Vert[2][0] = f32V[0] + rOffset[2][0];
			f32Vert[2][1] = f32V[1] + rOffset[2][1];
			f32Vert[2][2] = f32V[2] + rOffset[2][2];

			f32Vert[3][0] = f32V[0] + rOffset[3][0];
			f32Vert[3][1] = f32V[1] + rOffset[3][1];
			f32Vert[3][2] = f32V[2] + rOffset[3][2];

/*			glNormal3fv( pNormal );

			glTexCoord2f( 0, 1 );
			glVertex3fv( f32Vert[0] );

			glTexCoord2f( 1, 1 );
			glVertex3fv( f32Vert[1] );

			glTexCoord2f( 1, 0 );
			glVertex3fv( f32Vert[2] );

			glTexCoord2f( 0, 0 );
			glVertex3fv( f32Vert[3] );*/

			pOutVerts[0] = f32V[0] + rOffset[0][0];
			pOutVerts[1] = f32V[1] + rOffset[0][1];
			pOutVerts[2] = f32V[2] + rOffset[0][2];

			pOutVerts[3] = f32V[0] + rOffset[1][0];
			pOutVerts[4] = f32V[1] + rOffset[1][1];
			pOutVerts[5] = f32V[2] + rOffset[1][2];

			pOutVerts[6] = f32V[0] + rOffset[2][0];
			pOutVerts[7] = f32V[1] + rOffset[2][1];
			pOutVerts[8] = f32V[2] + rOffset[2][2];

			pOutVerts[9] = f32V[0] + rOffset[3][0];
			pOutVerts[10] = f32V[1] + rOffset[3][1];
			pOutVerts[11] = f32V[2] + rOffset[3][2];
			pOutVerts += 12;

			pOutNorms[0] = pNormal[0];
			pOutNorms[1] = pNormal[1];
			pOutNorms[2] = pNormal[2];

			pOutNorms[3] = pNormal[0];
			pOutNorms[4] = pNormal[1];
			pOutNorms[5] = pNormal[2];

			pOutNorms[6] = pNormal[0];
			pOutNorms[7] = pNormal[1];
			pOutNorms[8] = pNormal[2];

			pOutNorms[9] = pNormal[0];
			pOutNorms[10] = pNormal[1];
			pOutNorms[11] = pNormal[2];
			pOutNorms += 12;
		}

//		glEnd();

		glEnableClientState( GL_VERTEX_ARRAY );
		glEnableClientState( GL_NORMAL_ARRAY );
		glEnableClientState( GL_TEXTURE_COORD_ARRAY );
		glDisableClientState( GL_INDEX_ARRAY );

		glVertexPointer( 3, GL_FLOAT, 3 * sizeof( float32 ), &(m_rVertices[0]) );
		glNormalPointer( GL_FLOAT, 3 * sizeof( float32 ), &(m_rNormals[0]) );
		glTexCoordPointer( 2, GL_FLOAT, 2 * sizeof( float32 ), &(m_rTexCoords[0]) );

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

		glDrawArrays( GL_QUADS, 0, pMesh->get_vert_count() * 4 );

		if( g_bCompiledVertexArray )
			glUnlockArraysEXT();

		glDisableClientState( GL_VERTEX_ARRAY );
		glDisableClientState( GL_NORMAL_ARRAY );
		glDisableClientState( GL_TEXTURE_COORD_ARRAY );
	}
	else {

//		glBegin( GL_QUADS );

		for( i = 0; i < i32VertCount; i++ ) {

			pNormal = &pNorms[m_rSortList[i].m_ui32Idx];
			pVertex = &pVerts[m_rSortList[i].m_ui32Idx];

			f32Vert[0][0] = pVertex[0] + rOffset[0][0];
			f32Vert[0][1] = pVertex[1] + rOffset[0][1];
			f32Vert[0][2] = pVertex[2] + rOffset[0][2];

			f32Vert[1][0] = pVertex[0] + rOffset[1][0];
			f32Vert[1][1] = pVertex[1] + rOffset[1][1];
			f32Vert[1][2] = pVertex[2] + rOffset[1][2];

			f32Vert[2][0] = pVertex[0] + rOffset[2][0];
			f32Vert[2][1] = pVertex[1] + rOffset[2][1];
			f32Vert[2][2] = pVertex[2] + rOffset[2][2];

			f32Vert[3][0] = pVertex[0] + rOffset[3][0];
			f32Vert[3][1] = pVertex[1] + rOffset[3][1];
			f32Vert[3][2] = pVertex[2] + rOffset[3][2];


/*			glNormal3fv( pNormal );

			glTexCoord2f( 0, 1 );
			glVertex3fv( f32Vert[0] );

			glTexCoord2f( 1, 1 );
			glVertex3fv( f32Vert[1] );

			glTexCoord2f( 1, 0 );
			glVertex3fv( f32Vert[2] );

			glTexCoord2f( 0, 0 );
			glVertex3fv( f32Vert[3] );*/

			pOutVerts[0] = pVertex[0] + rOffset[0][0];
			pOutVerts[1] = pVertex[1] + rOffset[0][1];
			pOutVerts[2] = pVertex[2] + rOffset[0][2];

			pOutVerts[3] = pVertex[0] + rOffset[1][0];
			pOutVerts[4] = pVertex[1] + rOffset[1][1];
			pOutVerts[5] = pVertex[2] + rOffset[1][2];

			pOutVerts[6] = pVertex[0] + rOffset[2][0];
			pOutVerts[7] = pVertex[1] + rOffset[2][1];
			pOutVerts[8] = pVertex[2] + rOffset[2][2];

			pOutVerts[9] = pVertex[0] + rOffset[3][0];
			pOutVerts[10] = pVertex[1] + rOffset[3][1];
			pOutVerts[11] = pVertex[2] + rOffset[3][2];
			pOutVerts += 12;

			pOutNorms[0] = pNormal[0];
			pOutNorms[1] = pNormal[1];
			pOutNorms[2] = pNormal[2];

			pOutNorms[3] = pNormal[0];
			pOutNorms[4] = pNormal[1];
			pOutNorms[5] = pNormal[2];

			pOutNorms[6] = pNormal[0];
			pOutNorms[7] = pNormal[1];
			pOutNorms[8] = pNormal[2];

			pOutNorms[9] = pNormal[0];
			pOutNorms[10] = pNormal[1];
			pOutNorms[11] = pNormal[2];
			pOutNorms += 12;

		}

//		glEnd();

		glEnableClientState( GL_VERTEX_ARRAY );
		glEnableClientState( GL_NORMAL_ARRAY );
		glEnableClientState( GL_TEXTURE_COORD_ARRAY );
		glDisableClientState( GL_INDEX_ARRAY );

		glVertexPointer( 3, GL_FLOAT, 3 * sizeof( float32 ), &(m_rVertices[0]) );
		glNormalPointer( GL_FLOAT, 3 * sizeof( float32 ), &(m_rNormals[0]) );
		glTexCoordPointer( 2, GL_FLOAT, 2 * sizeof( float32 ), &(m_rTexCoords[0]) );

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

		glDrawArrays( GL_QUADS, 0, pMesh->get_vert_count() * 4 );

		if( g_bCompiledVertexArray )
			glUnlockArraysEXT();

		glDisableClientState( GL_VERTEX_ARRAY );
		glDisableClientState( GL_NORMAL_ARRAY );
		glDisableClientState( GL_TEXTURE_COORD_ARRAY );
	}

	glEnd();

	glPopMatrix();
}


void
FlareEffectC::render_shape( RenderContextS* pContext, ShapeC* pShape, const Matrix3C& rCamTM, Vector3C* pPlanes )
{
	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];

		// transparent thingies has cam transform in it
		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 );

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

	rAxisX *= m_f32FlareSize;
	rAxisY *= m_f32FlareSize;

	float32		f32Vert[4][3];
	Vector3C	rOffset[4];

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

	glBegin( GL_QUADS );

	glNormal3f( rAxisZ[0], rAxisZ[1], rAxisZ[2] );

	for( i = 0; i < pShape->get_path_count(); i++ ) {
		PathC*	pPath = pShape->get_path( i );
		for( uint32 j = 0; j < pPath->get_knot_count(); j++ ) {

			Vector3C	rKnot = pPath->get_knot( j ).get_knot();

			f32Vert[0][0] = rKnot[0] + rOffset[0][0];
			f32Vert[0][1] = rKnot[1] + rOffset[0][1];
			f32Vert[0][2] = rKnot[2] + rOffset[0][2];

			f32Vert[1][0] = rKnot[0] + rOffset[1][0];
			f32Vert[1][1] = rKnot[1] + rOffset[1][1];
			f32Vert[1][2] = rKnot[2] + rOffset[1][2];

			f32Vert[2][0] = rKnot[0] + rOffset[2][0];
			f32Vert[2][1] = rKnot[1] + rOffset[2][1];
			f32Vert[2][2] = rKnot[2] + rOffset[2][2];

			f32Vert[3][0] = rKnot[0] + rOffset[3][0];
			f32Vert[3][1] = rKnot[1] + rOffset[3][1];
			f32Vert[3][2] = rKnot[2] + rOffset[3][2];

			glTexCoord2f( 0, 1 );
			glVertex3fv( f32Vert[0] );

			glTexCoord2f( 1, 1 );
			glVertex3fv( f32Vert[1] );

			glTexCoord2f( 1, 0 );
			glVertex3fv( f32Vert[2] );

			glTexCoord2f( 0, 0 );
			glVertex3fv( f32Vert[3] );
		}
	}

	glEnd();

	glPopMatrix();
}

void
FlareEffectC::render_shape_sorted( RenderContextS* pContext, ShapeC* pShape, const Matrix3C& rCamTM, Vector3C* pPlanes )
{
	if( !pShape->get_path_count() )
		return;

	uint32	i;

	Matrix3C	rTM = pShape->get_tm();
	Matrix3C	rMultTM;


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

		// transparent thingies has cam transform in it
		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 );

	rMultTM = rTM * rCamTM;


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

	rAxisX *= m_f32FlareSize;
	rAxisY *= m_f32FlareSize;

	float32		f32Vert[4][3];
	Vector3C	rOffset[4];

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

	glBegin( GL_QUADS );

	glNormal3f( rAxisZ[0], rAxisZ[1], rAxisZ[2] );

	for( i = 0; i < pShape->get_path_count(); i++ ) {
		PathC*	pPath = pShape->get_path( i );

		uint32	j;

		// Sort vertices
		int32		i32VertCount = pPath->get_knot_count();
		float32		f32Depth;

		for( j = 0; j < i32VertCount; j++ ) {
			Vector3C	rKnot = pPath->get_knot( j ).get_knot();
			f32Depth = rKnot[0] * rMultTM[0][2] + rKnot[1] * rMultTM[1][2] + rKnot[2] * rMultTM[2][2] + rMultTM[3][2];
			m_rSortList[j].m_i32Depth = *(int32*)&f32Depth;
			m_rSortList[j].m_ui32Idx = j;
		}

		std::sort( m_rSortList.begin(), m_rSortList.begin() + i32VertCount );

		for( j = 0; j < i32VertCount; j++ ) {

			Vector3C	rKnot = pPath->get_knot( m_rSortList[j].m_ui32Idx ).get_knot();

			f32Vert[0][0] = rKnot[0] + rOffset[0][0];
			f32Vert[0][1] = rKnot[1] + rOffset[0][1];
			f32Vert[0][2] = rKnot[2] + rOffset[0][2];

			f32Vert[1][0] = rKnot[0] + rOffset[1][0];
			f32Vert[1][1] = rKnot[1] + rOffset[1][1];
			f32Vert[1][2] = rKnot[2] + rOffset[1][2];

			f32Vert[2][0] = rKnot[0] + rOffset[2][0];
			f32Vert[2][1] = rKnot[1] + rOffset[2][1];
			f32Vert[2][2] = rKnot[2] + rOffset[2][2];

			f32Vert[3][0] = rKnot[0] + rOffset[3][0];
			f32Vert[3][1] = rKnot[1] + rOffset[3][1];
			f32Vert[3][2] = rKnot[2] + rOffset[3][2];

			glTexCoord2f( 0, 1 );
			glVertex3fv( f32Vert[0] );

			glTexCoord2f( 1, 1 );
			glVertex3fv( f32Vert[1] );

			glTexCoord2f( 1, 0 );
			glVertex3fv( f32Vert[2] );

			glTexCoord2f( 0, 0 );
			glVertex3fv( f32Vert[3] );
		}
	}

	glEnd();

	glPopMatrix();
}

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

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

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

enum FlareEffectChunksE {
	CHUNK_FLAREEFFECT_BASE =			0x1000,
	CHUNK_FLAREEFFECT_TRANSGIZMO =		0x2000,
	CHUNK_FLAREEFFECT_ATTRIBGIZMO =		0x3000,
	CHUNK_FLAREEFFECT_TIMEGIZMO =		0x4000,
	CHUNK_FLAREEFFECT_FLAREGIZMO =		0x5000,
};

const uint32	FLAREEFFECT_VERSION = 1;

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

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

	// transform
	pSave->begin_chunk( CHUNK_FLAREEFFECT_TRANSGIZMO, FLAREEFFECT_VERSION );
		ui32Error = m_pTraGizmo->save( pSave );
	pSave->end_chunk();

	// attribute
	pSave->begin_chunk( CHUNK_FLAREEFFECT_ATTRIBGIZMO, FLAREEFFECT_VERSION );
		ui32Error = m_pAttGizmo->save( pSave );
	pSave->end_chunk();

	// time
	pSave->begin_chunk( CHUNK_FLAREEFFECT_TIMEGIZMO, FLAREEFFECT_VERSION );
		ui32Error = m_pTimeGizmo->save( pSave );
	pSave->end_chunk();

	// flare
	pSave->begin_chunk( CHUNK_FLAREEFFECT_FLAREGIZMO, FLAREEFFECT_VERSION );
		ui32Error = m_pFlareGizmo->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_FLAREEFFECT_BASE:
			if( pLoad->get_chunk_version() == FLAREEFFECT_VERSION )
				ui32Error = EffectI::load( pLoad );
			break;

		case CHUNK_FLAREEFFECT_TRANSGIZMO:
			if( pLoad->get_chunk_version() == FLAREEFFECT_VERSION )
				ui32Error = m_pTraGizmo->load( pLoad );
			break;

		case CHUNK_FLAREEFFECT_ATTRIBGIZMO:
			if( pLoad->get_chunk_version() == FLAREEFFECT_VERSION )
				ui32Error = m_pAttGizmo->load( pLoad );
			break;

		case CHUNK_FLAREEFFECT_TIMEGIZMO:
			if( pLoad->get_chunk_version() == FLAREEFFECT_VERSION )
				ui32Error = m_pTimeGizmo->load( pLoad );
			break;

		case CHUNK_FLAREEFFECT_FLAREGIZMO:
			if( pLoad->get_chunk_version() == FLAREEFFECT_VERSION )
				ui32Error = m_pFlareGizmo->load( pLoad );
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

	return ui32Error;
}


