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

#include "DemopajaVersion.h"
#include "PajaTypes.h"
#include "ClassDescC.h"
#include "ParamI.h"
#include "EditableI.h"
#include "UndoC.h"
#include "ImportableImageI.h"
#include "BBox2C.h"
#include "Matrix2C.h"
#include "DeviceContextC.h"
#include "DeviceInterfaceI.h"
#include "DemoInterfaceC.h"
#include "OpenGLViewportC.h"
#include "OpenGLDeviceC.h"
#include "TimeContextC.h"

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


// class descriptors
PlasmaDescC		g_rPlasmaDesc;


#ifndef PAJAPLAYER

//
// The DLL
//

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



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

__declspec( dllexport )
ClassDescC*
get_classdesc( int32 i )
{
	if( i == 0 )
		return &g_rPlasmaDesc;
	return 0;
}

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

__declspec( dllexport )
char*
get_dll_name()
{
	return "plasma.dll - Plasma Effect Plugin (c)2000 memon/moppi productions";
}

#endif

//
// main gizmo
//

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

PlasmaGizmoC::PlasmaGizmoC( EditableI* pOriginal ) :
	GizmoI( pOriginal )
{
	init();
}

void
PlasmaGizmoC::init()
{
	set_name( "Transform" );
	m_pParamPos = ParamVector2C::create_new( this, "Position", Vector2C(), 1, PARAM_STYLE_EDITBOX | PARAM_STYLE_ABS_POSITION, PARAM_ANIMATABLE );
	m_pParamPivot = ParamVector2C::create_new( this, "Pivot", Vector2C(), 2, PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_WORLD_SPACE	, PARAM_ANIMATABLE );
	m_pParamRot = ParamFloatC::create_new( this, "Rotation", 0, 3, PARAM_STYLE_EDITBOX | PARAM_STYLE_ANGLE, PARAM_ANIMATABLE, 0, 0, 1.0f );
	m_pParamScale = ParamVector2C::create_new( this, "Scale", Vector2C( 1, 1 ), 4, PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 0.01f );
}

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

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


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

DataBlockI*
PlasmaGizmoC::create()
{
	return new PlasmaGizmoC( 0, 0 );
}

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

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

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

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

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


PajaTypes::int32
PlasmaGizmoC::get_parameter_count()
{
	return PLASMA_PARAM_COUNT;
}

ParamI*
PlasmaGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case PLASMA_PARAM_POS:
		return m_pParamPos; break;
	case PLASMA_PARAM_PIVOT:
		return m_pParamPivot; break;
	case PLASMA_PARAM_ROT:
		return m_pParamRot; break;
	case PLASMA_PARAM_SCALE:
		return m_pParamScale; break;
	}
	return 0;
}


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

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

float32
PlasmaGizmoC::get_rot( int32 i32Time )
{
	float32	f32Val;
	m_pParamRot->get_val( i32Time, f32Val );
	return f32Val;
}

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



enum PlasmaGizmoChunksE {
	CHUNK_PLASMAGIZMO_BASE				= 0x1000,
	CHUNK_PLASMAGIZMO_PARAM_POS			= 0x2000,
	CHUNK_PLASMAGIZMO_PARAM_PIVOT		= 0x3000,
	CHUNK_PLASMAGIZMO_PARAM_ROT			= 0x4000,
	CHUNK_PLASMAGIZMO_PARAM_SCALE		= 0x5000,
};

const uint32	PLASMAGIZMO_VERSION = 1;



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

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

	// position
	pSave->begin_chunk( CHUNK_PLASMAGIZMO_PARAM_POS, PLASMAGIZMO_VERSION );
		ui32Error = m_pParamPos->save( pSave );
	pSave->end_chunk();

	// position
	pSave->begin_chunk( CHUNK_PLASMAGIZMO_PARAM_PIVOT, PLASMAGIZMO_VERSION );
		ui32Error = m_pParamPivot->save( pSave );
	pSave->end_chunk();

	// rotation
	pSave->begin_chunk( CHUNK_PLASMAGIZMO_PARAM_ROT, PLASMAGIZMO_VERSION );
		ui32Error = m_pParamRot->save( pSave );
	pSave->end_chunk();

	// scale
	pSave->begin_chunk( CHUNK_PLASMAGIZMO_PARAM_SCALE, PLASMAGIZMO_VERSION );
		ui32Error = m_pParamScale->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

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

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

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

		case CHUNK_PLASMAGIZMO_PARAM_ROT:
			{
				if( pLoad->get_chunk_version() == PLASMAGIZMO_VERSION )
					ui32Error = m_pParamRot->load( pLoad );
			}
			break;

		case CHUNK_PLASMAGIZMO_PARAM_SCALE:
			{
				if( pLoad->get_chunk_version() == PLASMAGIZMO_VERSION )
					ui32Error = m_pParamScale->load( pLoad );
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

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

	return IO_OK;
}


//
// attribute gizmo
//

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

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

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

	m_pParamColor = ParamColorC::create_new( this, "Color", ColorC( 1, 1, 1, 1 ), 5, PARAM_STYLE_COLORPICKER_RGBA, PARAM_ANIMATABLE );

	m_pParamTile = ParamVector2C::create_new( this, "Tile", Vector2C( 1, 1 ), 6, PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 0.1f );
	
	m_pParamSize = ParamVector2C::create_new( this, "Size", Vector2C( 320, 240 ), 6, PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 1 );
	
	m_pParamRenderMode = ParamIntC::create_new( this, "Render mode", 0, 7, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 2 );
	m_pParamRenderMode->add_label( 0, "Normal" );
	m_pParamRenderMode->add_label( 1, "Add" );
	m_pParamRenderMode->add_label( 2, "Mult" );

	m_pParamFile = ParamFileC::create_new( this, "Image", SUPERCLASS_IMAGE, NULL_CLASSID, 9, PARAM_STYLE_FILE, PARAM_ANIMATABLE );
}

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

	m_pParamColor->release();
	m_pParamFile->release();
	m_pParamRenderMode->release();
	m_pParamTile->release();
	m_pParamSize->release();
}


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

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

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

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

	AttributeGizmoC*	pGizmo = (AttributeGizmoC*)pEditable;
	m_pParamColor->copy( pGizmo->m_pParamColor );
	m_pParamFile->copy( pGizmo->m_pParamFile );
	m_pParamRenderMode->copy( pGizmo->m_pParamRenderMode );
	m_pParamTile->copy( pGizmo->m_pParamTile );
	m_pParamSize->copy( pGizmo->m_pParamSize );
}

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

	AttributeGizmoC*	pGizmo = (AttributeGizmoC*)pEditable;
	m_pParamColor = pGizmo->m_pParamColor;
	m_pParamFile = pGizmo->m_pParamFile;
	m_pParamRenderMode = pGizmo->m_pParamRenderMode;
	m_pParamTile = pGizmo->m_pParamTile;
	m_pParamSize = pGizmo->m_pParamSize;
}


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

ParamI*
AttributeGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case ATTRIBUTE_PARAM_COLOR:
		return m_pParamColor; break;
	case ATTRIBUTE_PARAM_FILE:
		return m_pParamFile; break;
	case ATTRIBUTE_PARAM_RENDERMODE:
		return m_pParamRenderMode; break;
	case ATTRIBUTE_PARAM_TILE:
		return m_pParamTile; break;
	case ATTRIBUTE_PARAM_SIZE:
		return m_pParamSize; break;
	}
	return 0;
}

ImportableImageI*
AttributeGizmoC::get_file( int32 i32Time, DeviceContextC* pContext, TimeContextC* pTimeContext )
{
	FileHandleC*	pHandle = 0;
	int32			i32FileTime = 0;
	m_pParamFile->get_file( i32Time, pHandle, i32FileTime );
	if( pHandle ) {
		ImportableImageI*	pImp = (ImportableImageI*)pHandle->get_importable();
		pImp->eval_state( i32FileTime );
		return pImp;
	}
	return 0;
}


Vector2C
AttributeGizmoC::get_size( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamSize->get_val( i32Time, rVal );
	return rVal;
}

ColorC
AttributeGizmoC::get_color( int32 i32Time )
{
	ColorC	rVal;
	m_pParamColor->get_val( i32Time, rVal );
	return rVal;
}

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

Vector2C
AttributeGizmoC::get_tile( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamTile->get_val( i32Time, rVal );
	return rVal;
}


enum AttributeGizmoChunksE {
	CHUNK_ATTRIBUTEGIZMO_BASE				= 0x1000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_COLOR		= 0x2000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_RENDERMODE	= 0x3000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_FILE			= 0x4000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_TILE			= 0x5000,
	CHUNK_ATTRIBUTEGIZMO_PARAM_SIZE			= 0x6000,
};

const uint32	ATTRIBUTEGIZMO_VERSION = 1;



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

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

	// color
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_PARAM_COLOR, ATTRIBUTEGIZMO_VERSION );
		ui32Error = m_pParamColor->save( pSave );
	pSave->end_chunk();

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

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

	// tile
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_PARAM_TILE, ATTRIBUTEGIZMO_VERSION );
		ui32Error = m_pParamTile->save( pSave );
	pSave->end_chunk();

	// size
	pSave->begin_chunk( CHUNK_ATTRIBUTEGIZMO_PARAM_SIZE, ATTRIBUTEGIZMO_VERSION );
		ui32Error = m_pParamSize->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

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

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

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

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

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

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

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

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

	return IO_OK;
}


//
// ripple gizmo
//

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

RippleGizmoC::RippleGizmoC( EditableI* pOriginal ) :
	GizmoI( pOriginal )
{
	init();
}

void
RippleGizmoC::init()
{
	set_name( "Ripple" );

	m_pParamAmount = ParamFloatC::create_new( this, "Amount", 30, 1, PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0, 100, 1 );
	m_pParamCenter = ParamVector2C::create_new( this, "Center", Vector2C(), 2, PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_OBJECT_SPACE, PARAM_ANIMATABLE );
	m_pParamFreq = ParamFloatC::create_new( this, "Frequence", 30, 3, PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0, 100, 1 );
	m_pParamSpeed = ParamFloatC::create_new( this, "Speed", 30, 4, PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, -100, 100, 1 );
}

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

	m_pParamAmount->release();
	m_pParamCenter->release();
	m_pParamFreq->release();
	m_pParamSpeed->release();
}


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

DataBlockI*
RippleGizmoC::create()
{
	return new RippleGizmoC( 0, 0 );
}

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

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

	RippleGizmoC*	pGizmo = (RippleGizmoC*)pEditable;
	m_pParamAmount->copy( pGizmo->m_pParamAmount );
	m_pParamCenter->copy( pGizmo->m_pParamCenter );
	m_pParamFreq->copy( pGizmo->m_pParamFreq );
	m_pParamSpeed->copy( pGizmo->m_pParamSpeed );
}

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

	RippleGizmoC*	pGizmo = (RippleGizmoC*)pEditable;
	m_pParamAmount = pGizmo->m_pParamAmount;
	m_pParamCenter = pGizmo->m_pParamCenter;
	m_pParamFreq = pGizmo->m_pParamFreq;
	m_pParamSpeed = pGizmo->m_pParamSpeed;
}


PajaTypes::int32
RippleGizmoC::get_parameter_count()
{
	return RIPPLE_PARAM_COUNT;
}

ParamI*
RippleGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case RIPPLE_PARAM_AMOUNT:
		return m_pParamAmount; break;
	case RIPPLE_PARAM_CENTER:
		return m_pParamCenter; break;
	case RIPPLE_PARAM_FREQ:
		return m_pParamFreq; break;
	case RIPPLE_PARAM_SPEED:
		return m_pParamSpeed; break;
	}
	return 0;
}

float32
RippleGizmoC::get_amount( int32 i32Time )
{
	float32	f32Val;
	m_pParamAmount->get_val( i32Time, f32Val );
	return f32Val / 100.0f;
}
Vector2C
RippleGizmoC::get_center( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamCenter->get_val( i32Time, rVal );
	return rVal;
}

float32
RippleGizmoC::get_freq( int32 i32Time )
{
	float32	f32Val;
	m_pParamFreq->get_val( i32Time, f32Val );
	return f32Val;	// no division needed
}

float32
RippleGizmoC::get_speed( int32 i32Time )
{
	float32	f32Val;
	m_pParamSpeed->get_val( i32Time, f32Val );
	return f32Val / 100.0f;
}

enum RippleGizmoChunksE {
	CHUNK_RIPPLEGIZMO_BASE				= 0x1000,
	CHUNK_RIPPLEGIZMO_PARAM_AMOUNT		= 0x2000,
	CHUNK_RIPPLEGIZMO_PARAM_CENTER		= 0x3000,
	CHUNK_RIPPLEGIZMO_PARAM_FREQ		= 0x4000,
	CHUNK_RIPPLEGIZMO_PARAM_SPEED		= 0x5000,
};

const uint32	RIPPLEGIZMO_VERSION = 1;



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

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

	// amount
	pSave->begin_chunk( CHUNK_RIPPLEGIZMO_PARAM_AMOUNT, RIPPLEGIZMO_VERSION );
		ui32Error = m_pParamAmount->save( pSave );
	pSave->end_chunk();

	// center
	pSave->begin_chunk( CHUNK_RIPPLEGIZMO_PARAM_CENTER, RIPPLEGIZMO_VERSION );
		ui32Error = m_pParamCenter->save( pSave );
	pSave->end_chunk();

	// freq
	pSave->begin_chunk( CHUNK_RIPPLEGIZMO_PARAM_FREQ, RIPPLEGIZMO_VERSION );
		ui32Error = m_pParamFreq->save( pSave );
	pSave->end_chunk();

	// speed
	pSave->begin_chunk( CHUNK_RIPPLEGIZMO_PARAM_SPEED, RIPPLEGIZMO_VERSION );
		ui32Error = m_pParamSpeed->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

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

		case CHUNK_RIPPLEGIZMO_PARAM_AMOUNT:
			{
				if( pLoad->get_chunk_version() == RIPPLEGIZMO_VERSION )
					ui32Error = m_pParamAmount->load( pLoad );
			}
			break;

		case CHUNK_RIPPLEGIZMO_PARAM_CENTER:
			{
				if( pLoad->get_chunk_version() == RIPPLEGIZMO_VERSION )
					ui32Error = m_pParamCenter->load( pLoad );
			}
			break;

		case CHUNK_RIPPLEGIZMO_PARAM_FREQ:
			{
				if( pLoad->get_chunk_version() == RIPPLEGIZMO_VERSION )
					ui32Error = m_pParamFreq->load( pLoad );
			}
			break;

		case CHUNK_RIPPLEGIZMO_PARAM_SPEED:
			{
				if( pLoad->get_chunk_version() == RIPPLEGIZMO_VERSION )
					ui32Error = m_pParamSpeed->load( pLoad );
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

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

	return IO_OK;
}


//
// warp gizmo
//

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

WarpGizmoC::WarpGizmoC( EditableI* pOriginal ) :
	GizmoI( pOriginal )
{
	init();
}

void
WarpGizmoC::init()
{
	set_name( "Warp" );

	m_pParamAmount = ParamVector2C::create_new( this, "Amount", Vector2C( 30, 30 ), 1, PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, Vector2C( 0, 0 ), Vector2C( 100, 100 ), 1 );
	m_pParamCenter = ParamVector2C::create_new( this, "Center", Vector2C(), 2, PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_OBJECT_SPACE, PARAM_ANIMATABLE );
	m_pParamFreq = ParamVector2C::create_new( this, "Frequence", Vector2C( 30, 30 ), 3, PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, Vector2C( 0, 0 ), Vector2C( 100, 100 ), 1 );
	m_pParamSpeed = ParamVector2C::create_new( this, "Speed", Vector2C( 30, 30 ), 4, PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, Vector2C( -100, -100 ), Vector2C( 100, 100 ), 1 );
}

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

	m_pParamAmount->release();
	m_pParamCenter->release();
	m_pParamFreq->release();
	m_pParamSpeed->release();
}


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

DataBlockI*
WarpGizmoC::create()
{
	return new WarpGizmoC( 0, 0 );
}

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

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

	WarpGizmoC*	pGizmo = (WarpGizmoC*)pEditable;
	m_pParamAmount->copy( pGizmo->m_pParamAmount );
	m_pParamCenter->copy( pGizmo->m_pParamCenter );
	m_pParamFreq->copy( pGizmo->m_pParamFreq );
	m_pParamSpeed->copy( pGizmo->m_pParamSpeed );
}

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

	WarpGizmoC*	pGizmo = (WarpGizmoC*)pEditable;
	m_pParamAmount = pGizmo->m_pParamAmount;
	m_pParamCenter = pGizmo->m_pParamCenter;
	m_pParamFreq = pGizmo->m_pParamFreq;
	m_pParamSpeed = pGizmo->m_pParamSpeed;
}


PajaTypes::int32
WarpGizmoC::get_parameter_count()
{
	return WARP_PARAM_COUNT;
}

ParamI*
WarpGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case WARP_PARAM_AMOUNT:
		return m_pParamAmount; break;
	case WARP_PARAM_CENTER:
		return m_pParamCenter; break;
	case WARP_PARAM_FREQ:
		return m_pParamFreq; break;
	case WARP_PARAM_SPEED:
		return m_pParamSpeed; break;
	}
	return 0;
}

Vector2C
WarpGizmoC::get_amount( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamAmount->get_val( i32Time, rVal );
	rVal[0] /= 100.0f;
	rVal[1] /= 100.0f;
	return rVal;
}

Vector2C
WarpGizmoC::get_center( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamCenter->get_val( i32Time, rVal );
	return rVal;
}

Vector2C
WarpGizmoC::get_freq( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamFreq->get_val( i32Time, rVal );
	rVal[0] /= 4.0f;
	rVal[1] /= 4.0f;
	return rVal;
}

Vector2C
WarpGizmoC::get_speed( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamSpeed->get_val( i32Time, rVal );
	rVal[0] /= 100.0f;
	rVal[1] /= 100.0f;
	return rVal;
}

enum WarpGizmoChunksE {
	CHUNK_WARPGIZMO_BASE			= 0x1000,
	CHUNK_WARPGIZMO_PARAM_AMOUNT	= 0x2000,
	CHUNK_WARPGIZMO_PARAM_CENTER	= 0x3000,
	CHUNK_WARPGIZMO_PARAM_FREQ		= 0x4000,
	CHUNK_WARPGIZMO_PARAM_SPEED		= 0x5000,
};

const uint32	WARPGIZMO_VERSION = 1;



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

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

	// amount
	pSave->begin_chunk( CHUNK_WARPGIZMO_PARAM_AMOUNT, WARPGIZMO_VERSION );
		ui32Error = m_pParamAmount->save( pSave );
	pSave->end_chunk();

	// center
	pSave->begin_chunk( CHUNK_WARPGIZMO_PARAM_CENTER, WARPGIZMO_VERSION );
		ui32Error = m_pParamCenter->save( pSave );
	pSave->end_chunk();

	// freq
	pSave->begin_chunk( CHUNK_WARPGIZMO_PARAM_FREQ, WARPGIZMO_VERSION );
		ui32Error = m_pParamFreq->save( pSave );
	pSave->end_chunk();

	// speed
	pSave->begin_chunk( CHUNK_WARPGIZMO_PARAM_SPEED, WARPGIZMO_VERSION );
		ui32Error = m_pParamSpeed->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

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

		case CHUNK_WARPGIZMO_PARAM_AMOUNT:
			{
				if( pLoad->get_chunk_version() == WARPGIZMO_VERSION )
					ui32Error = m_pParamAmount->load( pLoad );
			}
			break;

		case CHUNK_WARPGIZMO_PARAM_CENTER:
			{
				if( pLoad->get_chunk_version() == WARPGIZMO_VERSION )
					ui32Error = m_pParamCenter->load( pLoad );
			}
			break;

		case CHUNK_WARPGIZMO_PARAM_FREQ:
			{
				if( pLoad->get_chunk_version() == WARPGIZMO_VERSION )
					ui32Error = m_pParamFreq->load( pLoad );
			}
			break;

		case CHUNK_WARPGIZMO_PARAM_SPEED:
			{
				if( pLoad->get_chunk_version() == WARPGIZMO_VERSION )
					ui32Error = m_pParamSpeed->load( pLoad );
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

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

	return IO_OK;
}


//
// ripple gizmo
//

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

RotGizmoC::RotGizmoC( EditableI* pOriginal ) :
	GizmoI( pOriginal )
{
	init();
}

void
RotGizmoC::init()
{
	set_name( "Rotation" );

	m_pParamAmount = ParamFloatC::create_new( this, "Amount", 30, 1, PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0, 100, 1 );
	m_pParamCenter = ParamVector2C::create_new( this, "Center", Vector2C(), 2, PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_OBJECT_SPACE, PARAM_ANIMATABLE );
	m_pParamSpeed = ParamVector2C::create_new( this, "Speed", Vector2C( 30, 30 ), 4, PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, Vector2C( -100, -100 ), Vector2C( 100, 100 ), 1 );
}

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

	m_pParamAmount->release();
	m_pParamCenter->release();
	m_pParamSpeed->release();
}


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

DataBlockI*
RotGizmoC::create()
{
	return new RotGizmoC( 0, 0 );
}

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

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

	RotGizmoC*	pGizmo = (RotGizmoC*)pEditable;
	m_pParamAmount->copy( pGizmo->m_pParamAmount );
	m_pParamCenter->copy( pGizmo->m_pParamCenter );
	m_pParamSpeed->copy( pGizmo->m_pParamSpeed );
}

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

	RotGizmoC*	pGizmo = (RotGizmoC*)pEditable;
	m_pParamAmount = pGizmo->m_pParamAmount;
	m_pParamCenter = pGizmo->m_pParamCenter;
	m_pParamSpeed = pGizmo->m_pParamSpeed;
}


PajaTypes::int32
RotGizmoC::get_parameter_count()
{
	return ROT_PARAM_COUNT;
}

ParamI*
RotGizmoC::get_parameter( PajaTypes::int32 i32Index )
{
	switch( i32Index ) {
	case ROT_PARAM_AMOUNT:
		return m_pParamAmount; break;
	case ROT_PARAM_CENTER:
		return m_pParamCenter; break;
	case ROT_PARAM_SPEED:
		return m_pParamSpeed; break;
	}
	return 0;
}

float32
RotGizmoC::get_amount( int32 i32Time )
{
	float32	f32Val;
	m_pParamAmount->get_val( i32Time, f32Val );
	return f32Val / 10.0f;
}

Vector2C
RotGizmoC::get_center( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamCenter->get_val( i32Time, rVal );
	return rVal;
}

Vector2C
RotGizmoC::get_speed( int32 i32Time )
{
	Vector2C	rVal;
	m_pParamSpeed->get_val( i32Time, rVal );
	rVal[0] /= 100.0f;
	rVal[1] /= 100.0f;
	return rVal;
}

enum RotGizmoChunksE {
	CHUNK_ROTGIZMO_BASE				= 0x1000,
	CHUNK_ROTGIZMO_PARAM_AMOUNT		= 0x2000,
	CHUNK_ROTGIZMO_PARAM_CENTER		= 0x3000,
	CHUNK_ROTGIZMO_PARAM_SPEED		= 0x4000,
};

const uint32	ROTGIZMO_VERSION = 1;



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

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

	// amount
	pSave->begin_chunk( CHUNK_ROTGIZMO_PARAM_AMOUNT, ROTGIZMO_VERSION );
		ui32Error = m_pParamAmount->save( pSave );
	pSave->end_chunk();

	// center
	pSave->begin_chunk( CHUNK_ROTGIZMO_PARAM_CENTER, ROTGIZMO_VERSION );
		ui32Error = m_pParamCenter->save( pSave );
	pSave->end_chunk();

	// speed
	pSave->begin_chunk( CHUNK_ROTGIZMO_PARAM_SPEED, ROTGIZMO_VERSION );
		ui32Error = m_pParamSpeed->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

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

		case CHUNK_ROTGIZMO_PARAM_AMOUNT:
			{
				if( pLoad->get_chunk_version() == ROTGIZMO_VERSION )
					ui32Error = m_pParamAmount->load( pLoad );
			}
			break;

		case CHUNK_ROTGIZMO_PARAM_CENTER:
			{
				if( pLoad->get_chunk_version() == ROTGIZMO_VERSION )
					ui32Error = m_pParamCenter->load( pLoad );
			}
			break;

		case CHUNK_ROTGIZMO_PARAM_SPEED:
			{
				if( pLoad->get_chunk_version() == ROTGIZMO_VERSION )
					ui32Error = m_pParamSpeed->load( pLoad );
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

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

	return IO_OK;
}


//
// The effect
//


PlasmaEffectC::PlasmaEffectC()
{
	m_pGizmo = PlasmaGizmoC::create_new( this, 1 );
	m_pRippleGizmo = RippleGizmoC::create_new( this, 2 );
	m_pWarpGizmo = WarpGizmoC::create_new( this, 3 );
	m_pRotGizmo = RotGizmoC::create_new( this, 4 );
	m_pAttribGizmo = AttributeGizmoC::create_new( this, 5 );
}

PlasmaEffectC::PlasmaEffectC( EditableI* pOriginal ) :
	EffectI( pOriginal )
{
	m_pGizmo = PlasmaGizmoC::create_new( this, 1 );
	m_pRippleGizmo = RippleGizmoC::create_new( this, 2 );
	m_pWarpGizmo = WarpGizmoC::create_new( this, 3 );
	m_pRotGizmo = RotGizmoC::create_new( this, 4 );
	m_pAttribGizmo = AttributeGizmoC::create_new( this, 5 );
}

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

	m_pGizmo->release();
	m_pRippleGizmo->release();
	m_pWarpGizmo->release();
	m_pRotGizmo->release();
	m_pAttribGizmo->release();
}

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

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

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

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

	PlasmaEffectC*	pEffect = (PlasmaEffectC*)pEditable;
	m_pGizmo->copy( pEffect->m_pGizmo );
	m_pRippleGizmo->copy( pEffect->m_pRippleGizmo );
	m_pWarpGizmo->copy( pEffect->m_pWarpGizmo );
	m_pRotGizmo->copy( pEffect->m_pRotGizmo );
	m_pAttribGizmo->copy( pEffect->m_pAttribGizmo );
}

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

	PlasmaEffectC*	pEffect = (PlasmaEffectC*)pEditable;
	m_pGizmo = pEffect->m_pGizmo;
	m_pRippleGizmo = pEffect->m_pRippleGizmo;
	m_pWarpGizmo = pEffect->m_pWarpGizmo;
	m_pRotGizmo = pEffect->m_pRotGizmo;
	m_pAttribGizmo = pEffect->m_pAttribGizmo;
}

const char*
PlasmaEffectC::get_class_name()
{
	return "Plasma Effect";
}

int32
PlasmaEffectC::get_gizmo_count()
{
	return 5;
}

GizmoI*
PlasmaEffectC::get_gizmo( PajaTypes::int32 i32Index )
{
	if( i32Index == 0 )
		return m_pGizmo;
	else if( i32Index == 1 )
		return m_pAttribGizmo;
	else if( i32Index == 2 )
		return m_pRippleGizmo;
	else if( i32Index == 3 )
		return m_pWarpGizmo;
	else if( i32Index == 4 )
		return m_pRotGizmo;
	return 0;
}

ClassIdC
PlasmaEffectC::get_class_id()
{
	return CLASS_PLASMAEFFECT;
}

void
PlasmaEffectC::set_default_file( int32 i32Time, FileHandleC* pHandle )
{
	ParamFileC*	pParam = (ParamFileC*)m_pAttribGizmo->get_parameter( ATTRIBUTE_PARAM_FILE );

	UndoC*	pOldUndo;
	if( get_undo() ) {
		pOldUndo = pParam->begin_editing( get_undo() );
	}

	pParam->set_file( i32Time, pHandle );

	if( get_undo() ) {
		pParam->end_editing( pOldUndo );
	}
}

ParamI*
PlasmaEffectC::get_default_param( PajaTypes::int32 i32Param )
{
	if( i32Param == DEFAULT_PARAM_POSITION )
		return m_pGizmo->get_parameter( PLASMA_PARAM_POS );
	else if( i32Param == DEFAULT_PARAM_ROTATION )
		return m_pGizmo->get_parameter( PLASMA_PARAM_ROT );
	else if( i32Param == DEFAULT_PARAM_SCALE )
		return m_pGizmo->get_parameter( PLASMA_PARAM_SCALE );
	else if( i32Param == DEFAULT_PARAM_PIVOT )
		return m_pGizmo->get_parameter( PLASMA_PARAM_PIVOT );
	return 0;
}

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

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

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

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

	int32		i, j;
	Matrix2C	rPosMat, rRotMat, rScaleMat, rPivotMat;

	Vector2C	rScale = m_pGizmo->get_scale( i32Time );
	Vector2C	rPos = m_pGizmo->get_pos( i32Time );
	Vector2C	rPivot = m_pGizmo->get_pivot( i32Time );
	float32		f32Rot = m_pGizmo->get_rot( i32Time );

	rPivotMat.set_trans( rPivot );
	rPosMat.set_trans( rPos );
	rRotMat.set_rot( f32Rot / 180.0f * M_PI );
	rScaleMat.set_scale( rScale ) ;

	m_rTM = rPivotMat * rRotMat * rScaleMat * rPosMat;

	//
	// calc bounding box
	//
	float32		f32Width = 25;
	float32		f32Height = 25;
	Vector2C	rMin, rMax;
	Vector2C	rVec;

	rVec = m_pAttribGizmo->get_size( i32Time );
	f32Width = rVec[0] * 0.5f;
	f32Height = rVec[1] * 0.5f;

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

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

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

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

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

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

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

	// get color
	m_rColor = m_pAttribGizmo->get_color( i32Time );
	// get rendermode
	m_i32RenderMode = m_pAttribGizmo->get_render_mode( i32Time );
	// get tile
	m_rTile = m_pAttribGizmo->get_tile( i32Time );


	//
	// ripple
	//
	m_f32RippleAmount = m_pRippleGizmo->get_amount( i32Time );
	m_rRippleCenter = m_pRippleGizmo->get_center( i32Time );
	m_f32RippleFreq = m_pRippleGizmo->get_freq( i32Time );
	m_f32RippleSpeed = m_pRippleGizmo->get_speed( i32Time );
	m_rRippleCenter[0] /= f32Width * 2.0f;
	m_rRippleCenter[1] /= f32Height * 2.0f;
	m_rRippleCenter += Vector2C( 0.5f, 0.5f );

	//
	//warp
	//
	m_rWarpAmount = m_pWarpGizmo->get_amount( i32Time );
	m_rWarpCenter = m_pWarpGizmo->get_center( i32Time );
	m_rWarpFreq = m_pWarpGizmo->get_freq( i32Time );
	m_rWarpSpeed = m_pWarpGizmo->get_speed( i32Time );
	m_rWarpCenter[0] /= f32Width * 2.0f;
	m_rWarpCenter[1] /= f32Height * 2.0f;
	m_rWarpCenter += Vector2C( 0.5f, 0.5f );

	//
	// rot
	//
	m_f32RotAmount = m_pRotGizmo->get_amount( i32Time );
	m_rRotCenter = m_pRotGizmo->get_center( i32Time );
	m_rRotSpeed = m_pRotGizmo->get_speed( i32Time );
	m_rRotCenter[0] /= f32Width * 2.0f;
	m_rRotCenter[1] /= f32Height * 2.0f;
	m_rRotCenter += Vector2C( 0.5f, 0.5f );



	OpenGLDeviceC*	pDevice = (OpenGLDeviceC*)pContext->query_interface( CLASS_OPENGL_DEVICEDRIVER );
	if( !pDevice )
		return;

	OpenGLViewportC*	pViewport = (OpenGLViewportC*)pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
	if( !pViewport )
		return;


	pViewport->set_ortho( m_rBBox, m_rBBox[0][0], m_rBBox[1][0], m_rBBox[1][1], m_rBBox[0][1] );


	ImportableImageI*	pImp = m_pAttribGizmo->get_file( i32Time, pContext, pTimeContext );

	if( pImp ) {
		pImp->bind_texture( pDevice, 0, IMAGE_WRAP | IMAGE_LINEAR );
		glEnable( GL_TEXTURE_2D );
	}
	else
		glDisable( GL_TEXTURE_2D );

	glDisable( GL_DEPTH_TEST );
	glEnable( GL_BLEND );

	glDepthMask( GL_FALSE );


	float32	f32Offset = i32Time / 128.0f;

	float32	uBuf[81 * 61];
	float32	vBuf[81 * 61];

	float32	f32X, f32Y;
	float32	f32GX, f32GY;
	float32 f32Sin, f32Cos;
	float32	f32DiffX, f32DiffY;
	float32 f32Rad;
	float32 f32SinRad;

	f32Cos = (float32)cos( sin( f32Offset * m_rRotSpeed[0] * 0.1f ) * m_f32RotAmount );
	f32Sin = (float32)sin( sin( f32Offset * m_rRotSpeed[1] * 0.1f ) * m_f32RotAmount );

	for( j = 0; j <= 30; j++ ) {
		for( i = 0; i <= 40 ; i++ ) {

			f32GX = (float32)i * (1.0f / 40.0f);
			f32GY = (float32)j * (1.0f / 30.0f);

			// rot
			f32X = (f32GX - m_rRotCenter[0]) * f32Cos - (f32GY - m_rRotCenter[1]) * f32Sin + m_rRotCenter[0];
			f32Y = (f32GY - m_rRotCenter[1]) * f32Cos + (f32GX - m_rRotCenter[0]) * f32Sin + m_rRotCenter[1];

			// warp
			f32X = (f32X - m_rWarpCenter[0]) * (1 + sin( f32Y * m_rWarpFreq[0] + f32Offset * m_rWarpSpeed[0]) * m_rWarpAmount[0]) + m_rWarpCenter[0];
			f32Y = (f32Y - m_rWarpCenter[1]) * (1 + sin( f32X * m_rWarpFreq[1] + f32Offset * m_rWarpSpeed[1]) * m_rWarpAmount[1]) + m_rWarpCenter[1];

			// ripple
			f32DiffX = f32GX - m_rRippleCenter[0];
			f32DiffY = f32GY - m_rRippleCenter[1];
			f32Rad = (float32)sqrt( f32DiffX * f32DiffX + f32DiffY * f32DiffY );

			f32SinRad = (float32)sin( f32Rad * m_f32RippleFreq + f32Offset * m_f32RippleSpeed ) * m_f32RippleAmount;
			f32X += f32X * f32SinRad;
			f32Y += f32Y * f32SinRad;

			// store & tile
			uBuf[j * 41 + i] = f32X * m_rTile[0];	// ugh... kludge!
			vBuf[j * 41 + i] = f32Y * m_rTile[1];	// reverse V
		}
	}


	if( m_i32RenderMode == 0 ) {
		// normal
		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	}
	else if( m_i32RenderMode == 1 ) {
		// add
		glBlendFunc( GL_SRC_ALPHA, GL_ONE );
	}
	else {
		// mult
		glBlendFunc( GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA );
	}

	if( m_i32RenderMode == 2 ) {
		// mult
		glColor4f( m_rColor[0] * m_rColor[3], m_rColor[1] * m_rColor[3], m_rColor[2] * m_rColor[3], m_rColor[3] );
	}
	else
		glColor4f( m_rColor[0], m_rColor[1], m_rColor[2], m_rColor[3] );



	Vector2C	rAxisX, rAxisY, rStartY, rX, rNextX;

	rAxisX = (m_rVertices[1] - m_rVertices[0]) / 40.0f;
	rAxisY = (m_rVertices[2] - m_rVertices[1]) / 30.0f;

	rStartY = m_rVertices[0];

	for( i = 0; i < 30 * 41; i += 41 ) {
		
		rX = rStartY;

		glBegin( GL_TRIANGLE_STRIP );
		for( j = 0; j < 41; j++ ) {

			glTexCoord2f( uBuf[i + j], vBuf[i + j] );
			glVertex2f( rX[0], rX[1] );

			rNextX = rX + rAxisY;

			glTexCoord2f( uBuf[i + j + 41], vBuf[i + j + 41] );
			glVertex2f( rNextX[0], rNextX[1] );

			rX += rAxisX;
		}
		glEnd();

		rStartY += rAxisY;
	}

	glDepthMask( GL_TRUE );
}


bool
PlasmaEffectC::hit_test( const PajaTypes::Vector2C& rPoint )
{
	// point in polygon test
	// from c.g.a FAQ
	int		i, j;
	bool	bInside = false;

	for( i = 0, j = 4 - 1; i < 4; j = i++ ) {
		if( ( ((m_rVertices[i][1] <= rPoint[1]) && (rPoint[1] < m_rVertices[j][1])) ||
			((m_rVertices[j][1] <= rPoint[1]) && (rPoint[1] < m_rVertices[i][1])) ) &&
			(rPoint[0] < (m_rVertices[j][0] - m_rVertices[i][0]) * (rPoint[1] - m_rVertices[i][1]) / (m_rVertices[j][1] - m_rVertices[i][1]) + m_rVertices[i][0]) )

			bInside = !bInside;
	}

	return bInside;
}

/*
void
PlasmaEffectC::do_frame( DeviceContextC* pContext, TimeContextC* pTimeContext )
{

	OpenGLDeviceC*	pDevice = (OpenGLDeviceC*)pContext->query_interface( CLASS_OPENGL_DEVICEDRIVER );
	if( !pDevice )
		return;

	OpenGLViewportC*	pViewport = (OpenGLViewportC*)pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
	if( !pViewport )
		return;


	pViewport->set_ortho( m_rBBox, m_rBBox[0][0], m_rBBox[1][0], m_rBBox[1][1], m_rBBox[0][1] );


	ImportableImageI*	pImp = m_pAttribGizmo->get_file( m_i32Time, pContext, pTimeContext );

	if( pImp ) {
		pImp->bind_texture( pDevice, IMAGE_WRAP | IMAGE_LINEAR );
		glEnable( GL_TEXTURE_2D );
	}
	else
		glDisable( GL_TEXTURE_2D );

	glDisable( GL_DEPTH_TEST );
	glEnable( GL_BLEND );

	glDepthMask( GL_FALSE );


	float32	f32Offset = m_i32Time / 128.0f;

	float32	uBuf[81 * 61];
	float32	vBuf[81 * 61];

	int32	i, j;

	float32	f32X, f32Y;
	float32	f32GX, f32GY;
	float32 f32Sin, f32Cos;
	float32	f32DiffX, f32DiffY;
	float32 f32Rad;
	float32 f32SinRad;

	f32Cos = (float32)cos( sin( f32Offset * m_rRotSpeed[0] * 0.1f ) * m_f32RotAmount );
	f32Sin = (float32)sin( sin( f32Offset * m_rRotSpeed[1] * 0.1f ) * m_f32RotAmount );

	for( j = 0; j <= 30; j++ ) {
		for( i = 0; i <= 40 ; i++ ) {

			f32GX = (float32)i * (1.0f / 40.0f);
			f32GY = (float32)j * (1.0f / 30.0f);

			// rot
			f32X = (f32GX - m_rRotCenter[0]) * f32Cos - (f32GY - m_rRotCenter[1]) * f32Sin + m_rRotCenter[0];
			f32Y = (f32GY - m_rRotCenter[1]) * f32Cos + (f32GX - m_rRotCenter[0]) * f32Sin + m_rRotCenter[1];

			// warp
			f32X = (f32X - m_rWarpCenter[0]) * (1 + sin( f32Y * m_rWarpFreq[0] + f32Offset * m_rWarpSpeed[0]) * m_rWarpAmount[0]) + m_rWarpCenter[0];
			f32Y = (f32Y - m_rWarpCenter[1]) * (1 + sin( f32X * m_rWarpFreq[1] + f32Offset * m_rWarpSpeed[1]) * m_rWarpAmount[1]) + m_rWarpCenter[1];

			// ripple
			f32DiffX = f32GX - m_rRippleCenter[0];
			f32DiffY = f32GY - m_rRippleCenter[1];
			f32Rad = (float32)sqrt( f32DiffX * f32DiffX + f32DiffY * f32DiffY );

			f32SinRad = (float32)sin( f32Rad * m_f32RippleFreq + f32Offset * m_f32RippleSpeed ) * m_f32RippleAmount;
			f32X += f32X * f32SinRad;
			f32Y += f32Y * f32SinRad;

			// store & tile
			uBuf[j * 41 + i] = f32X * m_rTile[0];	// ugh... kludge!
			vBuf[j * 41 + i] = f32Y * m_rTile[1];	// reverse V
		}
	}


	if( m_i32RenderMode == 0 ) {
		// normal
		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	}
	else if( m_i32RenderMode == 1 ) {
		// add
		glBlendFunc( GL_SRC_ALPHA, GL_ONE );
	}
	else {
		// mult
		glBlendFunc( GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA );
	}

	if( m_i32RenderMode == 2 ) {
		// mult
		glColor4f( m_rColor[0] * m_rColor[3], m_rColor[1] * m_rColor[3], m_rColor[2] * m_rColor[3], m_rColor[3] );
	}
	else
		glColor4f( m_rColor[0], m_rColor[1], m_rColor[2], m_rColor[3] );



	Vector2C	rAxisX, rAxisY, rStartY, rX, rNextX;

	rAxisX = (m_rVertices[1] - m_rVertices[0]) / 40.0f;
	rAxisY = (m_rVertices[2] - m_rVertices[1]) / 30.0f;

	rStartY = m_rVertices[0];

	for( i = 0; i < 30 * 41; i += 41 ) {
		
		rX = rStartY;

		glBegin( GL_TRIANGLE_STRIP );
		for( j = 0; j < 41; j++ ) {

			glTexCoord2f( uBuf[i + j], vBuf[i + j] );
			glVertex2f( rX[0], rX[1] );

			rNextX = rX + rAxisY;

			glTexCoord2f( uBuf[i + j + 41], vBuf[i + j + 41] );
			glVertex2f( rNextX[0], rNextX[1] );

			rX += rAxisX;
		}
		glEnd();

		rStartY += rAxisY;
	}

	glDepthMask( GL_TRUE );
}
*/

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


enum PlasmaEffectChunksE {
	CHUNK_PLASMAEFFECT_BASE =			0x1000,
	CHUNK_PLASMAEFFECT_GIZMO =			0x2000,
	CHUNK_PLASMAEFFECT_RIPPLEGIZMO =	0x3000,
	CHUNK_PLASMAEFFECT_WARPGIZMO =		0x4000,
	CHUNK_PLASMAEFFECT_ROTGIZMO =		0x5000,
	CHUNK_PLASMAEFFECT_ATTRIBGIZMO =	0x6000,
};

const uint32	PLASMAEFFECT_VERSION = 1;

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

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

	// base gizmo
	pSave->begin_chunk( CHUNK_PLASMAEFFECT_GIZMO, PLASMAEFFECT_VERSION );
		ui32Error = m_pGizmo->save( pSave );
	pSave->end_chunk();

	// attribute gizmo
	pSave->begin_chunk( CHUNK_PLASMAEFFECT_ATTRIBGIZMO, PLASMAEFFECT_VERSION );
		ui32Error = m_pAttribGizmo->save( pSave );
	pSave->end_chunk();

	// ripple gizmo
	pSave->begin_chunk( CHUNK_PLASMAEFFECT_RIPPLEGIZMO, PLASMAEFFECT_VERSION );
		ui32Error = m_pRippleGizmo->save( pSave );
	pSave->end_chunk();

	// warp gizmo
	pSave->begin_chunk( CHUNK_PLASMAEFFECT_WARPGIZMO, PLASMAEFFECT_VERSION );
		ui32Error = m_pWarpGizmo->save( pSave );
	pSave->end_chunk();

	// rot gizmo
	pSave->begin_chunk( CHUNK_PLASMAEFFECT_ROTGIZMO, PLASMAEFFECT_VERSION );
		ui32Error = m_pRotGizmo->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

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

		case CHUNK_PLASMAEFFECT_GIZMO:
			{
				if( pLoad->get_chunk_version() == PLASMAEFFECT_VERSION )
					ui32Error = m_pGizmo->load( pLoad );
			}
			break;

		case CHUNK_PLASMAEFFECT_ATTRIBGIZMO:
			{
				if( pLoad->get_chunk_version() == PLASMAEFFECT_VERSION )
					ui32Error = m_pAttribGizmo->load( pLoad );
			}
			break;

		case CHUNK_PLASMAEFFECT_RIPPLEGIZMO:
			{
				if( pLoad->get_chunk_version() == PLASMAEFFECT_VERSION )
					ui32Error = m_pRippleGizmo->load( pLoad );
			}
			break;

		case CHUNK_PLASMAEFFECT_WARPGIZMO:
			{
				if( pLoad->get_chunk_version() == PLASMAEFFECT_VERSION )
					ui32Error = m_pWarpGizmo->load( pLoad );
			}
			break;

		case CHUNK_PLASMAEFFECT_ROTGIZMO:
			{
				if( pLoad->get_chunk_version() == PLASMAEFFECT_VERSION )
					ui32Error = m_pRotGizmo->load( pLoad );
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

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

	return IO_OK;
}
