//
// TransformFieldEffect.cpp
//
// Transform Field Effect
//
// Copyright (c) 2003 memon/moppi productions
//

#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <stdio.h>

// Demopaja headers
#include "DemopajaVersion.h"
#include "PajaTypes.h"
#include "ClassDescC.h"
#include "ImportableI.h"
#include "OpenGLDeviceC.h"
#include "OpenGLViewportC.h"
#include "ValueFieldPlugin.h"
#include "FileIO.h"
#include "AutoGizmoC.h"
#include "ImportableImageI.h"
#include "glext.h"
#include "Composition.h"

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

using namespace TransformFieldPlugin;


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


float32
ease( float32 f32U, float32 f32A, float32 f32B )
{
	float32	f32K;
	float32	f32S = f32A + f32B;
	
	if( f32U == 0.0f || f32U == 1.0f ) return f32U;
	if( f32S == 0.0 ) return f32U;
	if( f32S > 1.0f ) {
		f32A = f32A / f32S;
		f32B = f32B / f32S;
	}
	f32K = 1.0f / (2.0f - f32A - f32B);
	if( f32U < f32A )
		return ((f32K / f32A) * f32U * f32U);
	else if( f32U < 1.0f - f32B )
		return (f32K * (2.0f * f32U - f32A));
	else {
		f32U = 1.0f - f32U;
		return (1.0f - (f32K / f32B) * f32U * f32U);
	}
}


//////////////////////////////////////////////////////////////////////////
//
//  Transform Field effect class descriptor.
//

TransformFieldDescC::TransformFieldDescC()
{
	// empty
}

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

void*
TransformFieldDescC::create()
{
	return (void*)TransformFieldEffectC::create_new();
}

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

SuperClassIdC
TransformFieldDescC::get_super_class_id() const
{
	return SUPERCLASS_EFFECT_VALUEFIELD;
}

ClassIdC
TransformFieldDescC::get_class_id() const
{
	return CLASS_TRANSFORMFIELD_EFFECT;
}

const char*
TransformFieldDescC::get_name() const
{
	return "Field Transform";
}

const char*
TransformFieldDescC::get_desc() const
{
	return "Value Field Transform Effect";
}

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

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

const char*
TransformFieldDescC::get_url() const
{
	return "http://www.demopaja.org";
}

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

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

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


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

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


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

TransformFieldDescC	g_rTransformFieldDesc;

//////////////////////////////////////////////////////////////////////////
//
// The Transform Field effect
//

TransformFieldEffectC::TransformFieldEffectC()
{
	//
	// 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, "Pivot", Vector2C(), ID_TRANSFORM_PIVOT,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_WORLD_SPACE, PARAM_ANIMATABLE ) );

	m_pTraGizmo->add_parameter(	ParamFloatC::create_new( m_pTraGizmo, "Rotation", 0, ID_TRANSFORM_ROT,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_ANGLE, PARAM_ANIMATABLE, 0, 0, 1.0f ) );

	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( 100.0f, 100.0f ), ID_ATTRIBUTE_SIZE,
						PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE ) );

	m_pAttGizmo->add_parameter(	ParamFloatC::create_new( m_pAttGizmo, "Treshold", 0.5f, ID_ATTRIBUTE_TRESHOLD,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, 0, 1.0f, 0.01f ) );

	m_pAttGizmo->add_parameter(	ParamFloatC::create_new( m_pAttGizmo, "Ease In", 0.0f, ID_ATTRIBUTE_EASE_IN,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, 0, 1.0f, 0.01f ) );

	m_pAttGizmo->add_parameter(	ParamFloatC::create_new( m_pAttGizmo, "Ease Out", 0.0f, ID_ATTRIBUTE_EASE_OUT,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, 0, 1.0f, 0.01f ) );

	//
	// Create Deform gizmo.
	//
	m_pDeformGizmo = AutoGizmoC::create_new( this, "Deform", ID_GIZMO_DEFORM );

	m_pDeformGizmo->add_parameter(	ParamVector2C::create_new( m_pDeformGizmo, "Offset", Vector2C(), ID_DEFORM_OFFSET,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_OBJECT_SPACE, PARAM_ANIMATABLE ) );

	m_pDeformGizmo->add_parameter(	ParamFloatC::create_new( m_pDeformGizmo, "Rotation", 0, ID_DEFORM_ROTATION,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_ANGLE, PARAM_ANIMATABLE, 0, 0, 1.0f ) );

	m_pDeformGizmo->add_parameter(	ParamVector2C::create_new( m_pDeformGizmo, "Scale", Vector2C( 1, 1 ), ID_DEFORM_SCALE,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 0.01f ) );


}

TransformFieldEffectC::TransformFieldEffectC( EditableI* pOriginal ) :
	EffectValueFieldI( pOriginal )
{
	// empty
}

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

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

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

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

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

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

	// Make deep copy.
	TransformFieldEffectC*	pEffect = (TransformFieldEffectC*)pEditable;
	m_pTraGizmo->copy( pEffect->m_pTraGizmo );
	m_pAttGizmo->copy( pEffect->m_pAttGizmo );
}

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

	// Make shallow copy.
	TransformFieldEffectC*	pEffect = (TransformFieldEffectC*)pEditable;
	m_pTraGizmo = pEffect->m_pTraGizmo;
	m_pAttGizmo = pEffect->m_pAttGizmo;
}

int32
TransformFieldEffectC::get_gizmo_count()
{
	// Return number of gizmos inside this effect.
	return GIZMO_COUNT;
}

GizmoI*
TransformFieldEffectC::get_gizmo( PajaTypes::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_DEFORM:
		return m_pDeformGizmo;
	}

	return 0;
}

ClassIdC
TransformFieldEffectC::get_class_id()
{
	// Return the class ID. Should be same as in the class descriptor.
	return CLASS_TRANSFORMFIELD_EFFECT;
}

const char*
TransformFieldEffectC::get_class_name()
{
	// Return the class name. Should be same as in the class descriptor.
	return "Field Transform";
}

void
TransformFieldEffectC::set_default_file( int32 i32Time, FileHandleC* pHandle )
{
	// empty
}

ParamI*
TransformFieldEffectC::get_default_param( int32 i32Param )
{
	// Return specified default parameter.
	if( i32Param == DEFAULT_PARAM_POSITION )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_POS );
	else if( i32Param == DEFAULT_PARAM_ROTATION )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_ROT );
	else if( i32Param == DEFAULT_PARAM_SCALE )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_SCALE );
	else if( i32Param == DEFAULT_PARAM_PIVOT )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_PIVOT );
	return 0;
}

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


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

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



	Vector2C	rScale;
	Vector2C	rPos;
	Vector2C	rPivot;
	float32		f32Rot;

	// 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_PIVOT ))->get_val( i32Time, rPivot );
	((ParamFloatC*)m_pTraGizmo->get_parameter( ID_TRANSFORM_ROT ))->get_val( i32Time, f32Rot );
	((ParamVector2C*)m_pTraGizmo->get_parameter( ID_TRANSFORM_SCALE ))->get_val( i32Time, rScale );

	// Calculate transformation matrix.
	Matrix2C	rPosMat, rPivotMat, rRotMat, rScaleMat;
	rPivotMat.set_trans( rPivot );
	rPosMat.set_trans( rPos );
	rRotMat.set_rot( f32Rot / 180.0f * (float32)M_PI );
	rScaleMat.set_scale( rScale ) ;
	m_rTM = rPivotMat * rRotMat * rScaleMat * rPosMat;

	Vector2C	rMin, rMax;
	Vector2C	rVec;


	// Get size
	((ParamVector2C*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_SIZE ))->get_val( i32Time, m_rSize );

	float32	f32Width = m_rSize[0] * 0.5f;
	float32	f32Height = m_rSize[1] * 0.5f;

	Vector2C	rBase = m_rTM[2];
	Vector2C	rAxisX = m_rTM[0] * f32Width;
	Vector2C	rAxisY = m_rTM[1] * f32Height;

	// Calculate bounding box
	for( uint32 i = 0; i < 36; i++ ) {
		float32	f32Angle = (float)i / 36.0f * (2.0f * M_PI);

		Vector2C	rVec = rBase + (float)sin( f32Angle ) * rAxisX + (float)cos( f32Angle ) * rAxisY;

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

	// Store bounding box.
	m_rBBox[0] = rMin;
	m_rBBox[1] = rMax;

	Vector2C	rImageScale( 1, 1 );
	Vector2C	rImageOffset( 0, 0 );


	// Get Treshold
	((ParamFloatC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_TRESHOLD ))->get_val( i32Time, m_f32Treshold );
	// Get Ease In.
	((ParamFloatC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_EASE_IN ))->get_val( i32Time, m_f32EaseIn );
	// Get Ease Out
	((ParamFloatC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_EASE_OUT ))->get_val( i32Time, m_f32EaseOut );

	// Get Deform Offset
	((ParamVector2C*)m_pDeformGizmo->get_parameter( ID_DEFORM_OFFSET ))->get_val( i32Time, m_rDeformOffset );
	// Get Deform Offset
	((ParamVector2C*)m_pDeformGizmo->get_parameter( ID_DEFORM_SCALE ))->get_val( i32Time, m_rDeformScale );
	// Get Deform Offset
	((ParamFloatC*)m_pDeformGizmo->get_parameter( ID_DEFORM_ROTATION ))->get_val( i32Time, m_f32DeformRotate );

	m_rDeformOffset[0] /= f32Width;
	m_rDeformOffset[1] /= f32Height;

	// Calculate deformation matrix.
	Matrix2C	rDeformPosMat, rDeformRotMat, rDeformScaleMat;
	rDeformPosMat.set_trans( m_rDeformOffset );
	rDeformRotMat.set_rot( m_f32DeformRotate / 180.0f * (float32)M_PI );
	rDeformScaleMat.set_scale( m_rDeformScale ) ;
	m_rDeformTM = rDeformRotMat * rDeformScaleMat * rDeformPosMat;

	// Calc base matrix
	m_rConvTM[0] = rAxisX;
	m_rConvTM[1] = rAxisY;
	m_rConvTM[2] = rBase;
	m_rInvConvTM = m_rConvTM.invert();
}

void
TransformFieldEffectC::draw_ui( uint32 ui32Flags )
{
	// Drawing colors
	ColorC	rColFocus;
	ColorC	rColHi;
	ColorC	rColGrid;
	rColFocus.convert_from_uint8( 160, 160, 255 );
	rColHi.convert_from_uint8( 60, 60, 128 );
	rColGrid.convert_from_uint8( 160, 160, 255, 128 );


	ColorC	rDrawColor = rColHi;

	if( get_flags() & ITEM_SELECTED )
		rDrawColor = rColFocus;


	int	i;

	if( !m_pDemoInterface )
		return;

	DeviceContextC* pContext = m_pDemoInterface->get_device_context();
	TimeContextC* pTimeContext = m_pDemoInterface->get_time_context();
	OpenGLDeviceC*	pDevice = (OpenGLDeviceC*)pContext->query_interface( CLASS_OPENGL_DEVICEDRIVER );
	if( !pDevice )
		return;

	float32	f32Width = m_rSize[0] * 0.5f;
	float32	f32Height = m_rSize[1] * 0.5f;

	Vector2C	rBase = m_rTM[2];
	Vector2C	rAxisX = m_rTM[0] * f32Width;
	Vector2C	rAxisY = m_rTM[1] * f32Height;

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

	// Outer
	glColor4f( rDrawColor[0], rDrawColor[1], rDrawColor[2], 0.5f );

	glBegin( GL_LINE_LOOP );

	for( i = 0; i < 36; i++ )
	{
		float32	f32Angle = ((float)i / 36.0f) * 2.0f * M_PI;
		Vector2C	rPos = rBase + (float)sin( f32Angle ) * rAxisX + (float)cos( f32Angle ) * rAxisY;
		glVertex2f( rPos[0], rPos[1] );
	}

	glEnd();


	glColor4f( rDrawColor[0], rDrawColor[1], rDrawColor[2], 1.0f );

	// Treshold
	glBegin( GL_LINE_LOOP );

	for( i = 0; i < 36; i++ )
	{
		float32	f32Angle = ((float)i / 36.0f) * 2.0f * M_PI;
		Vector2C	rPos = rBase + (float)sin( f32Angle ) * rAxisX * m_f32Treshold + (float)cos( f32Angle ) * rAxisY * m_f32Treshold;
		glVertex2f( rPos[0], rPos[1] );
	}

	glEnd();


	// Treshold
	glBegin( GL_LINES );

	for( i = 0; i < 36; i++ )
	{
		float32	f32Angle = ((float)i / 36.0f) * 2.0f * M_PI;
		Vector2C	rPos = rBase + (float)sin( f32Angle ) * rAxisX * m_f32Treshold + (float)cos( f32Angle ) * rAxisY * m_f32Treshold;

		rPos = rPos * m_rInvConvTM;
		rPos = rPos * m_rDeformTM;
		rPos *= m_rConvTM;

		glVertex2f( rPos[0], rPos[1] );
	}

	glEnd();


	Matrix2C	rIdentity;
	rIdentity = rIdentity.set_identity();

	// Draw lines
	for( i = 0; i < 4; i++ )
	{
		float32	f32Angle = (float)i / 4.0f * 2.0f * M_PI;

		glBegin( GL_LINE_STRIP );

		for( int32 j = 0; j < 20; j++ )
		{
			float	fAlpha = (float)j / 19.0f;
			float	fVal = m_f32Treshold + (1.0f - m_f32Treshold) * fAlpha;
			Vector2C	rPos = rBase + (float)sin( f32Angle ) * rAxisX * fVal + (float)cos( f32Angle ) * rAxisY * fVal;
			Vector2C	rOffset = sample_value_vector2( rPos, rIdentity );
			rPos += rOffset;

			glColor4f( rDrawColor[0], rDrawColor[1], rDrawColor[2], 0.5f + (1.0f - ease( fAlpha, m_f32EaseIn, m_f32EaseOut )) * 0.5f );

			glVertex2f( rPos[0], rPos[1] );
		}

		glEnd();
	}

	glDisable( GL_BLEND );
}

BBox2C
TransformFieldEffectC::get_bbox()
{
	// Return the bounding box.
	return m_rBBox;
}

const Matrix2C&
TransformFieldEffectC::get_transform_matrix() const
{
	// Return the trnasformation matrix.
	return m_rTM;
}

bool
TransformFieldEffectC::hit_test( const Vector2C& rPoint )
{
	Vector2C	rConvPoint = rPoint * m_rInvConvTM;
	if( rConvPoint.length() <= 1.0f )
		return true;
	return false;
}

float
TransformFieldEffectC::sample_value_float( const PajaTypes::Vector2C& rSamplePos, const PajaTypes::Matrix2C& rTM )
{
	return 0;
}

Vector2C
TransformFieldEffectC::sample_value_vector2( const PajaTypes::Vector2C& rSamplePos, const PajaTypes::Matrix2C& rTM )
{
	Matrix2C	rMat = rTM * m_rInvConvTM;
	Matrix2C	rInvMat = rMat.invert();

	// Convert to object space
	Vector2C	rPos = rSamplePos * rMat;

	float32	f32Length = rPos.length();

	if( f32Length > 1.0f )
		return Vector2C( 0, 0 );

	float32	f32Alpha = 1.0f;
	if( f32Length > m_f32Treshold )
	{
		f32Alpha = 1.0f - (f32Length - m_f32Treshold) / (1.0f - m_f32Treshold);
		f32Alpha = ease( f32Alpha, m_f32EaseIn, m_f32EaseOut );
	}

//	Vector2C	rDeformedPos = rPos * m_rDeformTM;

	Vector2C	rDeformedPos;


	float32	f32Rot = (m_f32DeformRotate / 180.0f * M_PI) * f32Alpha;

	float32	f32SinA = (float32)sin( f32Rot );
	float32	f32CosA = (float32)cos( f32Rot );

	// Rotate
	rDeformedPos[0] = rPos[0] * f32CosA - rPos[1] * f32SinA;
	rDeformedPos[1] = rPos[1] * f32CosA + rPos[0] * f32SinA;

	// Scale
	rDeformedPos[0] *= f32Alpha * m_rDeformScale[0] + (1.0f - f32Alpha) * 1.0f;
	rDeformedPos[1] *= f32Alpha * m_rDeformScale[1] + (1.0f - f32Alpha) * 1.0f;

	// Translate
	rDeformedPos[0] += f32Alpha * m_rDeformOffset[0];
	rDeformedPos[1] += f32Alpha * m_rDeformOffset[1];

	rDeformedPos *= rInvMat;

	return rDeformedPos - rSamplePos;
}

void
TransformFieldEffectC::sample_value_array_float( const PajaTypes::Vector2C* pSamplePosArray, float* pOutArray, PajaTypes::uint32 ui32NumSamples, const PajaTypes::Matrix2C& rTM )
{
}

void
TransformFieldEffectC::sample_value_array_vector2( const PajaTypes::Vector2C* pSamplePosArray, PajaTypes::Vector2C* pOutArray, PajaTypes::uint32 ui32NumSamples, const PajaTypes::Matrix2C& rTM )
{
}

enum TransformFieldEffectChunksE {
	CHUNK_TRANSFORMFIELD_BASE =			0x1000,
	CHUNK_TRANSFORMFIELD_TRANSGIZMO =		0x2000,
	CHUNK_TRANSFORMFIELD_ATTRIBGIZMO =	0x3000,
};

const uint32	TRANSFORMFIELD_VERSION = 1;

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

	// EffectI base class
	pSave->begin_chunk( CHUNK_TRANSFORMFIELD_BASE, TRANSFORMFIELD_VERSION );
		ui32Error = EffectI::save( pSave );
	pSave->end_chunk();

	// Transform
	pSave->begin_chunk( CHUNK_TRANSFORMFIELD_TRANSGIZMO, TRANSFORMFIELD_VERSION );
		ui32Error = m_pTraGizmo->save( pSave );
	pSave->end_chunk();

	// Attribute
	pSave->begin_chunk( CHUNK_TRANSFORMFIELD_ATTRIBGIZMO, TRANSFORMFIELD_VERSION );
		ui32Error = m_pAttGizmo->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_TRANSFORMFIELD_BASE:
			// EffectI base class
			if( pLoad->get_chunk_version() == TRANSFORMFIELD_VERSION )
				ui32Error = EffectI::load( pLoad );
			break;

		case CHUNK_TRANSFORMFIELD_TRANSGIZMO:
			// Transform
			if( pLoad->get_chunk_version() == TRANSFORMFIELD_VERSION )
				ui32Error = m_pTraGizmo->load( pLoad );
			break;

		case CHUNK_TRANSFORMFIELD_ATTRIBGIZMO:
			// Attribute
			if( pLoad->get_chunk_version() == TRANSFORMFIELD_VERSION )
				ui32Error = m_pAttGizmo->load( pLoad );
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

	return ui32Error;
}
