//
// DX9ImagePlugin.cpp
//
// DX9 Image Plugin
//
// Copyright (c) 2000-2004 memon/moppi productions
//

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <d3d9.h>
#include <d3dx9math.h>
//#include <dxerr9.h>

// Demopaja headers
#include "DemopajaVersion.h"
#include "PajaTypes.h"
#include "ClassDescC.h"
#include "ImportableI.h"
#include "DX9DeviceC.h"
#include "Dx9ViewportC.h"
#include "DX9Image.h"
#include "FileIO.h"
#include "AutoGizmoC.h"
#include "ImportableImageI.h"
#include "ImageResampleC.h"
#include "EffectMaskI.h"
#include "Vector2C.h"
#include "BBox2C.h"
#include "EffectMaskI.h"
#include "AudioSpectrumC.h"
#include "resource.h"

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

using namespace DX9ImagePlugin;


#define FVF_XYZCOLORUV_VERTEX			(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)
#define FVF_XYZCOLOR_VERTEX			(D3DFVF_XYZ | D3DFVF_DIFFUSE)

struct XYZCOLORUV_VERTEX
{
	float	x, y, z;
	DWORD	color;
	float	u, v;
};

struct XYZCOLOR_VERTEX
{
	float	x, y, z;
	DWORD	color;
};


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

//
// Helper functions
//

inline
uint32
lowest_bit_mask( uint32 v )
{
	return (v & -v);
}

static
uint32
ceil_power2( uint32 ui32Num )
{
	uint32	i = lowest_bit_mask( ui32Num );
	while( i < ui32Num )
		i <<= 1;
	return i;
}

static
int32
nearest_power2( int32 i32Num )
{
	int32	i = ceil_power2( i32Num );	// bigger
	int32	j = i / 2;					// smaller

	int32	i32DiffI = i - i32Num;
	int32	i32DiffJ = i32Num - j;

	// diff J has to be twice as little as diffI to be chosen.
	i32DiffI /= 2;

	if( i32DiffJ < i32DiffI )
		return j;

	return i;
}


//////////////////////////////////////////////////////////////////////////
//
//  DDS importer class descriptor.
//

DX9DDSImportDescC::DX9DDSImportDescC()
{
	// empty
}

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

void*
DX9DDSImportDescC::create()
{
	return DX9DDSImportC::create_new();
}

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

SuperClassIdC
DX9DDSImportDescC::get_super_class_id() const
{
	return SUPERCLASS_IMAGE;
}

ClassIdC
DX9DDSImportDescC::get_class_id() const
{
	return CLASS_DX9DDS_IMPORT;
}

const char*
DX9DDSImportDescC::get_name() const
{
	return "DDS Image";
}

const char*
DX9DDSImportDescC::get_desc() const
{
	return "Importer for DDS (.DDS) images";
}

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

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

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

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

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

const ClassIdC&
DX9DDSImportDescC::get_required_device_driver( uint32 ui32Idx )
{
	return PajaSystem::CLASS_DX9_DEVICEDRIVER;
}

uint32
DX9DDSImportDescC::get_ext_count() const
{
	return 9;
}

const char*
DX9DDSImportDescC::get_ext( uint32 ui32Index ) const
{
	if( ui32Index == 0 )
		return "dds";
	else if( ui32Index == 1 )
		return "bmp";
	else if( ui32Index == 2 )
		return "dib";
	else if( ui32Index == 3 )
		return "hdr";
	else if( ui32Index == 4 )
		return "jpg";
	else if( ui32Index == 5 )
		return "pfm";
	else if( ui32Index == 6 )
		return "png";
	else if( ui32Index == 7 )
		return "ppm";
	else if( ui32Index == 8 )
		return "tga";
	return 0;
}


//////////////////////////////////////////////////////////////////////////
//
//  Image effect class descriptor.
//

DX9ImageDescC::DX9ImageDescC()
{
	// empty
}

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

void*
DX9ImageDescC::create()
{
	return (void*)DX9ImageEffectC::create_new();
}

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

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

ClassIdC
DX9ImageDescC::get_class_id() const
{
	return CLASS_DX9IMAGE_EFFECT;
};

const char*
DX9ImageDescC::get_name() const
{
	return "Image (DX9)";
}

const char*
DX9ImageDescC::get_desc() const
{
	return "Image Effect (DX9)";
}

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

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

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

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

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

const ClassIdC&
DX9ImageDescC::get_required_device_driver( uint32 ui32Idx )
{
	return PajaSystem::CLASS_DX9_DEVICEDRIVER;
}


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

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


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

DX9ImageDescC			g_rDX9ImageDesc;
DX9DDSImportDescC	g_rDX9DDSImport;

#ifndef DEMOPAJAPLAYER

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

HINSTANCE	g_hInstance = 0;

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


//
// Returns number of classes inside this plugin DLL.
//

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


//
// Returns class descriptors of the plugin classes.
//

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


//
// Returns the API version this DLL was made with.
//

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

//
// Returns the DLL name.
//

__declspec( dllexport )
char*
get_dll_name()
{
	return "DX9ImagePlugin.dll - Image Plugin (c)2000-2004 memon/moppi productions";
}

#endif



//////////////////////////////////////////////////////////////////////////
//
// The effect
//

DX9ImageEffectC::DX9ImageEffectC() :
	m_pEffect( 0 ),
	m_pVertDecl( 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, "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 );

	// Image
	m_pAttGizmo->add_parameter(	ParamFileC::create_new( m_pAttGizmo, "Image", SUPERCLASS_IMAGE, NULL_CLASSID, ID_ATTRIBUTE_IMAGE, PARAM_STYLE_FILE, PARAM_ANIMATABLE ) );

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

	// Offset
	m_pAttGizmo->add_parameter(	ParamVector2C::create_new( m_pAttGizmo, "Offset", Vector2C(), ID_ATTRIBUTE_OFFSET,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_OBJECT_SPACE, PARAM_ANIMATABLE ) );

	// Wrap
	ParamIntC*	pWrapMode = ParamIntC::create_new( m_pAttGizmo, "Wrap", 0, ID_ATTRIBUTE_WRAP, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 1 );
	pWrapMode->add_label( 0, "Clamp" );
	pWrapMode->add_label( 1, "Repeat" );
	m_pAttGizmo->add_parameter( pWrapMode );

	// Resample
	ParamIntC*	pResampleMode = ParamIntC::create_new( m_pAttGizmo, "Resample", 0, ID_ATTRIBUTE_RESAMPLE, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 1 );
	pResampleMode->add_label( 0, "Bilinear" );
	pResampleMode->add_label( 1, "Nearest" );
	m_pAttGizmo->add_parameter( pResampleMode );

	// Color
	m_pAttGizmo->add_parameter(	ParamColorC::create_new( m_pAttGizmo, "Color", ColorC( 1, 1, 1, 1 ), ID_ATTRIBUTE_COLOR,
							PARAM_STYLE_COLORPICKER_RGB, PARAM_ANIMATABLE ) );

	// Opacity
	m_pAttGizmo->add_parameter(	ParamFloatC::create_new( m_pAttGizmo, "Opacity", 1.0f, ID_ATTRIBUTE_OPACITY,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, 0, 1.0f, 0.01f ) );

	// Blend
	ParamIntC*	pBlendMode = ParamIntC::create_new( m_pAttGizmo, "Blend", 0, ID_ATTRIBUTE_BLEND, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 6 );
	pBlendMode->add_label( 0, "Normal" );
	pBlendMode->add_label( 1, "Replace" );
	pBlendMode->add_label( 2, "Add" );
	pBlendMode->add_label( 3, "Mult" );
	pBlendMode->add_label( 4, "Sub" );
	pBlendMode->add_label( 5, "Lighten" );
	pBlendMode->add_label( 6, "Darken" );
	m_pAttGizmo->add_parameter( pBlendMode );

	// Band
	ParamIntC*	pBand = ParamIntC::create_new( m_pAttGizmo, "Band", 0, ID_ATTRIBUTE_BAND, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 7 );
	pBand->add_label( 0, "Band 0" );
	pBand->add_label( 1, "Band 1" );
	pBand->add_label( 2, "Band 2" );
	pBand->add_label( 3, "Band 3" );
	pBand->add_label( 4, "Band 4" );
	pBand->add_label( 5, "Band 5" );
	pBand->add_label( 6, "Band 6" );
	pBand->add_label( 7, "Band 7" );
	m_pAttGizmo->add_parameter( pBand );

	// Opacity change
	m_pAttGizmo->add_parameter(	ParamFloatC::create_new( m_pAttGizmo, "Band Amount", 0.0f, ID_ATTRIBUTE_BAND_AMOUNT,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, 0, 1.0f, 0.01f ) );
}

DX9ImageEffectC::DX9ImageEffectC( EditableI* pOriginal ) :
	EffectI( pOriginal ),
	m_pTraGizmo( 0 ),
	m_pAttGizmo( 0 ),
	m_pEffect( 0 ),
	m_pVertDecl( 0 )
{
	// Empty. The parameters are not created in the clone constructor.
}

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

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

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

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

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

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

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

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

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

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

GizmoI*
DX9ImageEffectC::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;
	}

	return 0;
}

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

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

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

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

	// 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*
DX9ImageEffectC::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;
}

uint32
DX9ImageEffectC::update_notify( EditableI* pCaller )
{
	return PARAM_NOTIFY_NONE;
}

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

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

	DX9DeviceC*	pDevice = (DX9DeviceC*)pContext->query_interface( CLASS_DX9_DEVICEDRIVER );
	if( !pDevice )
		return;

	LPDIRECT3DDEVICE9	pD3DDevice = pDevice->get_d3ddevice();

	if( ui32Reason == INIT_DEVICE_CHANGED )
	{

		if( pDevice->get_state() == DEVICE_STATE_SHUTTINGDOWN )
		{
			if( m_pEffect )
			{
				m_pEffect->Release();
				m_pEffect = 0;
			}
		}

	}
	else if( ui32Reason == INIT_DEVICE_INVALIDATE )
	{
		// Invalidate device resources.
		if( m_pEffect )
			m_pEffect->OnLostDevice();
	}
	else if( ui32Reason == INIT_DEVICE_VALIDATE )
	{
		// Restore device resources.
		if( m_pEffect )
			m_pEffect->OnResetDevice();
	}
	else if( ui32Reason == INIT_INITIAL_UPDATE )
	{
		init_effect();
	}
}

void
DX9ImageEffectC::init_effect()
{
	DeviceContextC* pContext = m_pDemoInterface->get_device_context();
	// Get the DX9 device.
	DX9DeviceC*	pDevice = (DX9DeviceC*)pContext->query_interface( CLASS_DX9_DEVICEDRIVER );
	if( !pDevice )
		return;
	LPDIRECT3DDEVICE9	pD3DDevice = pDevice->get_d3ddevice();

	HRESULT	hr;

	LPD3DXBUFFER pError = NULL;

	if( FAILED( hr = D3DXCreateEffectFromResource( pD3DDevice, g_hInstance, MAKEINTRESOURCE( IDR_IMAGE_FX ), NULL, NULL, 0, NULL, &m_pEffect, &pError ) ) )
	{
//		TRACE( "create effect failed: %s\n", DXGetErrorString9( hr ) );
		if( pError )
			TRACE( "err: %s\n", pError->GetBufferPointer() );
	}

	if( pError )
		pError->Release();

	D3DVERTEXELEMENT9 vert2DDecl[] =
	{
		{0,  0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
		{0,  12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
		{0, 16, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
		D3DDECL_END()
	};

	// Create vertex declaration
	if( FAILED( pD3DDevice->CreateVertexDeclaration( vert2DDecl, &m_pVertDecl ) ) )
		TRACE( "vert decl failed\n" );
}

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

	// Requires multitexturing and bledn equation.
	DeviceContextC* pContext = m_pDemoInterface->get_device_context();
	TimeContextC* pTimeContext = m_pDemoInterface->get_time_context();


	Matrix2C	rPosMat, rRotMat, rScaleMat, rPivotMat;

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

	float32		f32Width = 25;
	float32		f32Height = 25;
	Vector2C	rMin, rMax;
	Vector2C	rVec;


	// Get the size from the file or use the defaults if no file.
//	DX9DDSImportC*	pImp = 0;
	ImportableImageI*	pImp = 0;
	FileHandleC*		pHandle;
	int32				i32FileTime;
	((ParamFileC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_IMAGE ))->get_file( i32Time, pHandle, i32FileTime );

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

	if( pImp )
	{
		pImp->eval_state( i32FileTime );
		f32Width = (float32)pImp->get_width() * 0.5f;
		f32Height = (float32)pImp->get_height() * 0.5f;
	}


	// Calcualte vertices of the rectangle.
	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;

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

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

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

	Vector2C	rImageScale( 1, 1 );
	Vector2C	rImageOffset( 0, 0 );
	int32			i32Wrap = WRAPMODE_CLAMP;
	int32			i32Resample = RESAMPLEMODE_BILINEAR;
	int32			i32Blend = BLENDMODE_NORMAL;
	int32			i32Band = 0;
	ColorC		rColor( 1, 1, 1 );
	float32		f32Opacity = 1.0f;
	float32		f32OpacityChange = 1.0f;


	// Get image scale.
	((ParamVector2C*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_SCALE ))->get_val( i32Time, rImageScale );
	// Get image offset.
	((ParamVector2C*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_OFFSET ))->get_val( i32Time, rImageOffset );
	// Get Wrap
	((ParamIntC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_WRAP ))->get_val( i32Time, i32Wrap );
	// Get Resample
	((ParamIntC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_RESAMPLE ))->get_val( i32Time, i32Resample );
	// Get Color.
	((ParamColorC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_COLOR ))->get_val( i32Time, rColor );
	// Get Opacity.
	((ParamFloatC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_OPACITY ))->get_val( i32Time, f32Opacity );
	// Get Blend mode.
	((ParamIntC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_BLEND ))->get_val( i32Time, i32Blend );
	// Get Band.
	((ParamIntC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_BAND ))->get_val( i32Time, i32Band );
	// Get Opacity Change.
	((ParamFloatC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_BAND_AMOUNT ))->get_val( i32Time, f32OpacityChange );


	// Get the DX9 device.
	DX9DeviceC*	pDevice = (DX9DeviceC*)pContext->query_interface( CLASS_DX9_DEVICEDRIVER );
	if( !pDevice )
		return;

	// Get audio spectrum.
	AudioSpectrumC*	pSpec = (AudioSpectrumC*)pContext->query_interface( CLASS_AUDIO_SPECTRUM );

	LPDIRECT3DDEVICE9	pD3DDevice = pDevice->get_d3ddevice();

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

	// Set orthographic projection.
	pViewport->set_ortho( m_rBBox, m_rBBox[0][0], m_rBBox[1][0], m_rBBox[1][1], m_rBBox[0][1] );


	int	i32TextureUnit = 0;

	// If there is image set it as current texture.
	int32	i32TextureFlags = 0;

	if( i32Wrap == WRAPMODE_CLAMP )
		i32TextureFlags |= IMAGE_CLAMP;
	else
		i32TextureFlags |= IMAGE_WRAP;

	if( i32Resample == RESAMPLEMODE_BILINEAR )
		i32TextureFlags |= IMAGE_LINEAR;
	else
		i32TextureFlags |= IMAGE_NEAREST;

	pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
	pD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
  pD3DDevice->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );
  pD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
  pD3DDevice->SetRenderState( D3DRS_STENCILENABLE, FALSE );
  pD3DDevice->SetRenderState( D3DRS_CLIPPING, TRUE );
  pD3DDevice->SetRenderState( D3DRS_CLIPPLANEENABLE, FALSE );
  pD3DDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_DISABLE );
  pD3DDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE );
  pD3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE );
  pD3DDevice->SetRenderState( D3DRS_COLORWRITEENABLE,
      D3DCOLORWRITEENABLE_RED  | D3DCOLORWRITEENABLE_GREEN |
      D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA );

	pD3DDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
	pD3DDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
	pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
	
	BBox2C	TexBounds;

	if( pImp )
	{
		TexBounds = pImp->get_tex_coord_bounds();
		pImp->bind_texture( pDevice, 0, i32TextureFlags );

    pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
    pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
    pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
    pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
    pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
    pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
    pD3DDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 );
    pD3DDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
	}
	else
	{
		pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_SELECTARG2 );
    pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
    pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG2 );
    pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
	}

	Vector2C	rTexCoords[4];

	rTexCoords[0][0] = TexBounds[0][0];
	rTexCoords[0][1] = TexBounds[0][1];

	rTexCoords[1][0] = TexBounds[1][0];
	rTexCoords[1][1] = TexBounds[0][1];

	rTexCoords[2][0] = TexBounds[1][0];
	rTexCoords[2][1] = TexBounds[1][1];

	rTexCoords[3][0] = TexBounds[0][0];
	rTexCoords[3][1] = TexBounds[1][1];

	rImageOffset[0] *= (1.0f / (f32Width * 2.0f)) * TexBounds.width();
	rImageOffset[1] *= (1.0f / (f32Height * 2.0f)) * TexBounds.height();

	Vector2C	rCenter( TexBounds.width() * 0.5f, TexBounds.height() * 0.5f );

	// Transform texture coords
	for( i = 0; i < 4; i++ )
		rTexCoords[i] = (rTexCoords[i] - rCenter - rImageOffset) * rImageScale + rCenter;

	if( pSpec && pSpec->get_band_count() )
	{
		int32					i32GlobalTime = i32Time;
		LayerC*				pParent = get_parent();
		TimeSegmentC*	pEffectSeg = get_timesegment();
		TimeSegmentC*	pLayerSeg = 0;

		if( pParent )
			pLayerSeg = pParent->get_timesegment();

		if( pLayerSeg )
			i32GlobalTime += pLayerSeg->get_segment_start();
		if( pEffectSeg )
			i32GlobalTime += pEffectSeg->get_segment_start();

		pSpec->eval_state( i32GlobalTime, pTimeContext );

		float32	f32Amount = (1.0f - f32OpacityChange) + pSpec->get_band_value( i32Band ) * f32OpacityChange;
		f32Opacity *= f32Amount;
	}


	if( !m_pEffect )
	{
		TRACE( "no effect\n" );
		return;
	}

	if( !m_pVertDecl )
	{
		TRACE( "no vert declaration\n" );
		return;
	}

	IDirect3DBaseTexture9*	pD3DTex = 0;

	if( pImp )
	{
		// Read back the D3D texture interface.
		pD3DDevice->GetTexture( 0, &pD3DTex );
		if( !pD3DTex )
		{
			TRACE( "could not get texture\n" );
			return;
		}
	}

	float32	f32FadeBlack = 0;
	float32	f32FadeWhite = 0;
	float32	f32Op = 1;

	if( FAILED( pD3DDevice->SetVertexDeclaration( m_pVertDecl ) ) )
		TRACE( "set vertex decl failed\n" );

	// Set the transform matrices
	D3DXMATRIX matVP, matView, matProj;

	pD3DDevice->GetTransform( D3DTS_VIEW, &matView );
	pD3DDevice->GetTransform( D3DTS_PROJECTION, &matProj );

	D3DXMatrixMultiply( &matVP, &matView, &matProj );

	m_pEffect->SetMatrix( "matWorldViewProj", &matVP );

//	DWORD	dwCol = D3DCOLOR_ARGB( (int32)(f32Opacity * 255), (int32)(rColor[0] * 255), (int32)(rColor[1] * 255), (int32)(rColor[2] * 255) );
	DWORD	dwCol = D3DCOLOR_ARGB( 255, (int32)(rColor[0] * 255), (int32)(rColor[1] * 255), (int32)(rColor[2] * 255) );

	if( i32Blend == BLENDMODE_NORMAL )
	{
		f32FadeBlack = 0;
		f32FadeWhite = 0;
		f32Op = f32Opacity;
		pD3DDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD );
		pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
		pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
		dwCol = D3DCOLOR_ARGB( (int32)(f32Opacity * 255.0f), (int32)(rColor[0] * 255), (int32)(rColor[1] * 255), (int32)(rColor[2] * 255) );
	}
	else if( i32Blend == BLENDMODE_REPLACE )
	{
		f32FadeBlack = 0;
		f32FadeWhite = 0;
		pD3DDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD );
		pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
		pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ZERO );
		dwCol = D3DCOLOR_ARGB( (int32)(f32Opacity * 255.0f), (int32)(rColor[0] * 255), (int32)(rColor[1] * 255), (int32)(rColor[2] * 255) );
	}
	else if( i32Blend == BLENDMODE_ADD )
	{
		f32FadeBlack = 1.0f - f32Opacity;
		f32FadeWhite = 0;
		pD3DDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD );
		pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
		pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
		dwCol = D3DCOLOR_ARGB( 255, (int32)(rColor[0] * 255), (int32)(rColor[1] * 255), (int32)(rColor[2] * 255) );
	}
	else if( i32Blend == BLENDMODE_MULT )
	{
		f32FadeBlack = 0;
		f32FadeWhite = 1.0f - f32Opacity;
		pD3DDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD );
		pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR );
		pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ZERO );
		dwCol = D3DCOLOR_ARGB( 255, (int32)(rColor[0] * 255), (int32)(rColor[1] * 255), (int32)(rColor[2] * 255) );
	}
	else if( i32Blend == BLENDMODE_SUB )
	{
		f32FadeBlack = 1.0f - f32Opacity;
		f32FadeWhite = 0;
		pD3DDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_REVSUBTRACT );
		pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
		pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
		dwCol = D3DCOLOR_ARGB( 255, (int32)(rColor[0] * 255), (int32)(rColor[1] * 255), (int32)(rColor[2] * 255) );
	}
	else if( i32Blend == BLENDMODE_LIGHTEN )
	{
		f32FadeBlack = 1.0f - f32Opacity;
		f32FadeWhite = 0;
		pD3DDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_MAX );
		pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
		pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
		dwCol = D3DCOLOR_ARGB( 255, (int32)(rColor[0] * 255), (int32)(rColor[1] * 255), (int32)(rColor[2] * 255) );
	}
	else if( i32Blend == BLENDMODE_DARKEN )
	{
		f32FadeBlack = 0;
		f32FadeWhite = 1.0f - f32Opacity;
		pD3DDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_MIN );
		pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
		pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
		dwCol = D3DCOLOR_ARGB( 255, (int32)(rColor[0] * 255), (int32)(rColor[1] * 255), (int32)(rColor[2] * 255) );
	}


	if( FAILED( m_pEffect->SetFloat( "fadeBlack", f32FadeBlack ) ) )
		TRACE( "set float (fadeblack) failed\n" );
	if( FAILED( m_pEffect->SetFloat( "fadeWhite", f32FadeWhite ) ) )
		TRACE( "set float (fadewhite) failed\n" );

	XYZCOLORUV_VERTEX	rQuad[4];
	rQuad[0].x = m_rVertices[0][0];
	rQuad[0].y = m_rVertices[0][1];
	rQuad[0].z = 0;
	rQuad[0].color = dwCol;
	rQuad[0].u = rTexCoords[0][0];
	rQuad[0].v = rTexCoords[0][1];

	rQuad[1].x = m_rVertices[1][0];
	rQuad[1].y = m_rVertices[1][1];
	rQuad[1].z = 0;
	rQuad[1].color = dwCol;
	rQuad[1].u = rTexCoords[1][0];
	rQuad[1].v = rTexCoords[1][1];

	rQuad[2].x = m_rVertices[2][0];
	rQuad[2].y = m_rVertices[2][1];
	rQuad[2].z = 0;
	rQuad[2].color = dwCol;
	rQuad[2].u = rTexCoords[2][0];
	rQuad[2].v = rTexCoords[2][1];

	rQuad[3].x = m_rVertices[3][0];
	rQuad[3].y = m_rVertices[3][1];
	rQuad[3].z = 0;
	rQuad[3].color = dwCol;
	rQuad[3].u = rTexCoords[3][0];
	rQuad[3].v = rTexCoords[3][1];

	if( pImp )
	{
		if( FAILED( m_pEffect->SetTechnique( "ColorTextureF" ) ) )
			TRACE( "set technique failed\n" );

		pD3DDevice->SetFVF( FVF_XYZCOLORUV_VERTEX );
		pD3DDevice->SetPixelShader( NULL );

		if( FAILED( m_pEffect->SetTexture( "tTexture", pD3DTex ) ) )
			TRACE( "set texture failed\n" );

		UINT iPass, cPasses;
		if( FAILED( m_pEffect->Begin( &cPasses, 0 ) ) )
			TRACE( "begin effect failed\n" );

		for( iPass = 0; iPass < cPasses; iPass++ )
		{
			if( FAILED( m_pEffect->Pass( iPass ) ) )
				TRACE( "set pass %d failed\n", iPass );
			pD3DDevice->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, 2, rQuad, sizeof( XYZCOLORUV_VERTEX ) );
		}
		m_pEffect->End();
	}
	else
	{
		if( FAILED( m_pEffect->SetTechnique( "ColorOnlyF" ) ) )
			TRACE( "set technique failed\n" );

		UINT iPass, cPasses;
		if( FAILED( m_pEffect->Begin( &cPasses, 0 ) ) )
			TRACE( "begin effect failed\n" );

		for( iPass = 0; iPass < cPasses; iPass++ )
		{
			if( FAILED( m_pEffect->Pass( iPass ) ) )
				TRACE( "set pass %d failed\n", iPass );
			pD3DDevice->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, 2, rQuad, sizeof( XYZCOLORUV_VERTEX ) );
		}
		m_pEffect->End();
	}

	if( pD3DTex )
		pD3DTex->Release();

	pD3DDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD );
	pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

	pD3DDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
	pD3DDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
}

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

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

bool
DX9ImageEffectC::hit_test( const 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;
}


enum DX9ImageEffectChunksE {
	CHUNK_IMAGE_BASE =				0x10,
	CHUNK_IMAGE_TRANSGIZMO =	0x20,
	CHUNK_IMAGE_ATTRIBGIZMO =	0x30,
};

const uint32	IMAGE_VERSION = 1;

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

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

	// Transform
	pSave->begin_chunk( CHUNK_IMAGE_TRANSGIZMO, IMAGE_VERSION );
		ui32Error = m_pTraGizmo->save( pSave );
	pSave->end_chunk();

	// Attribute
	pSave->begin_chunk( CHUNK_IMAGE_ATTRIBGIZMO, IMAGE_VERSION );
		ui32Error = m_pAttGizmo->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

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

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

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

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

	return ui32Error;
}



//////////////////////////////////////////////////////////////////////////
//
// DDS Importer class implementation.
//

DX9DDSImportC::DX9DDSImportC() :
	m_pD3DTexture( 0 ),
	m_pData( 0 ),
	m_ui32DataSize( 0 ),
	m_i32Width( 0 ),
	m_i32Height( 0 ),
	m_i32Bpp( 0 )
{
	m_rTexBounds[0] = Vector2C( 0, 0 );
	m_rTexBounds[1] = Vector2C( 1, 1 );
}

DX9DDSImportC::DX9DDSImportC( EditableI* pOriginal ) :
	ImportableImageI( pOriginal ),
	m_pD3DTexture( 0 ),
	m_pData( 0 ),
	m_ui32DataSize( 0 ),
	m_i32Width( 0 ),
	m_i32Height( 0 ),
	m_i32Bpp( 0 )
{
	m_rTexBounds[0] = Vector2C( 0, 0 );
	m_rTexBounds[1] = Vector2C( 1, 1 );
}

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

	// Delete creted texture.
	if( m_pD3DTexture )
		m_pD3DTexture->Release();

	delete [] m_pData;
}

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

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

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

void
DX9DDSImportC::copy( EditableI* pEditable )
{
	// Importables which loads the data from a file does
	// not have to implement this method, since they will
	// not be duplicated.
}

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

	m_pD3DTexture = pFile->m_pD3DTexture;
	m_sFileName = pFile->m_sFileName;
	m_pData = pFile->m_pData;
	m_ui32DataSize = pFile->m_ui32DataSize;
	m_i32Width = pFile->m_i32Width;
	m_i32Height = pFile->m_i32Height;
	m_i32Bpp = pFile->m_i32Bpp;
}

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

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




// loads the file
bool
DX9DDSImportC::load_file( const char* szName, DemoInterfaceC* pInterface )
{
	m_pDemoInterface = pInterface;

	DeviceContextC* pContext = m_pDemoInterface->get_device_context();

	// Get the OpenGL device.
	DX9DeviceC*	pDevice = (DX9DeviceC*)pContext->query_interface( CLASS_DX9_DEVICEDRIVER );
	if( !pDevice )
		return false;
	LPDIRECT3DDEVICE9	pD3DDevice = pDevice->get_d3ddevice();

	// Delete existing data.
	delete [] m_pData;
	m_pData = 0;
	m_ui32DataSize = 0;
	if( m_pD3DTexture )
		m_pD3DTexture->Release();
	m_pD3DTexture = 0;


	FILE*		pStream;
	if( (pStream = fopen( pInterface->get_absolute_path( szName ), "rb" )) == 0 )
	{
		return false;
	}

	m_sFileName = szName;

	// Calc file size
	fseek( pStream, 0L, SEEK_END );
	m_ui32DataSize = ftell( pStream );
	fseek( pStream, 0L, SEEK_SET );

	if( !m_ui32DataSize )
	{
		fclose( pStream );
		return false;
	}

	m_pData = new uint8[m_ui32DataSize];

	if( fread( m_pData, m_ui32DataSize, 1, pStream ) != 1 )
	{
		fclose( pStream );
		return false;
	}

	fclose( pStream );

	return true;
}

void
DX9DDSImportC::initialize( uint32 ui32Reason, DemoInterfaceC* pInterface )
{
	ImportableI::initialize( ui32Reason, pInterface );

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

	DX9DeviceC*	pDevice = (DX9DeviceC*)pContext->query_interface( CLASS_DX9_DEVICEDRIVER );
	if( !pDevice )
		return;

	if( ui32Reason == INIT_DEVICE_CHANGED )
	{

		if( pDevice->get_state() == DEVICE_STATE_SHUTTINGDOWN )
		{
			// Delete textures
			if( m_pD3DTexture )
			{
				m_pD3DTexture->Release();
				m_pD3DTexture = 0;
			}
		}

	}
	else if( ui32Reason == INIT_DEVICE_INVALIDATE )
	{
		// Invalidate device resources.
/*		if( pDevice->get_state() == DEVICE_STATE_LOST )
		{
			// Delete textures
			if( m_pD3DTexture )
			{
				m_pD3DTexture->Release();
				m_pD3DTexture = 0;
			}
		}*/
	}
	else if( ui32Reason == INIT_DEVICE_VALIDATE )
	{
		// Restore device resources.
/*		if( pDevice->get_state() == DEVICE_STATE_LOST )
		{
			init_texture();
		}*/
	}
	else if( ui32Reason == INIT_INITIAL_UPDATE )
	{
		init_texture();
	}
}

bool
DX9DDSImportC::init_texture()
{
	DeviceContextC* pContext = m_pDemoInterface->get_device_context();
	DX9DeviceC*	pDevice = (DX9DeviceC*)pContext->query_interface( CLASS_DX9_DEVICEDRIVER );
	if( !pDevice )
		return false;
	LPDIRECT3DDEVICE9	pD3DDevice = pDevice->get_d3ddevice();

	HRESULT	hRes;
	hRes = D3DXCreateTextureFromFileInMemory( pD3DDevice, m_pData, m_ui32DataSize, &m_pD3DTexture );
	if( D3D_OK != hRes )
	{
		return false;
	}

	D3DSURFACE_DESC	Desc;
	hRes = m_pD3DTexture->GetLevelDesc( 0, &Desc );
	if( D3D_OK == hRes )
	{
		m_i32Width = Desc.Width;
		m_i32Height = Desc.Height;
	}

	m_rTexBounds[0][0] = 0;
	m_rTexBounds[0][1] = 0;
	m_rTexBounds[1][0] = 1;
	m_rTexBounds[1][1] = 1;

	return true;
}

ClassIdC
DX9DDSImportC::get_class_id()
{
	return CLASS_DX9DDS_IMPORT;
}

const char*
DX9DDSImportC::get_class_name()
{
	return "DDS Image (DX9)";
}

float32
DX9DDSImportC::get_width()
{
	return (float32)m_i32Width;
}

float32
DX9DDSImportC::get_height()
{
	return (float32)m_i32Height;
}

int32
DX9DDSImportC::get_data_width()
{
	return m_i32Width;
}

int32
DX9DDSImportC::get_data_height()
{
	return m_i32Height;
}

int32
DX9DDSImportC::get_data_pitch()
{
	return m_i32Width;
}

BBox2C&
DX9DDSImportC::get_tex_coord_bounds()
{
	return m_rTexBounds;
}

int32
DX9DDSImportC::get_data_bpp()
{
	return m_i32Bpp;
}

uint8*
DX9DDSImportC::get_data()
{
	return m_pData;
}


void
DX9DDSImportC::bind_texture( DeviceInterfaceI* pInterface, uint32 ui32Stage, uint32 ui32Properties )
{
	if( pInterface->get_class_id() != CLASS_DX9_DEVICEDRIVER )
		return;

	DeviceContextC* pContext = m_pDemoInterface->get_device_context();
	DX9DeviceC*	pDevice = (DX9DeviceC*)pContext->query_interface( CLASS_DX9_DEVICEDRIVER );
	if( !pDevice )
		return;
	LPDIRECT3DDEVICE9	pD3DDevice = pDevice->get_d3ddevice();

	if( m_pD3DTexture )
	{
		pD3DDevice->SetTexture( ui32Stage, m_pD3DTexture );

		if( ui32Properties & IMAGE_LINEAR )
		{
			pD3DDevice->SetSamplerState( ui32Stage, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
			pD3DDevice->SetSamplerState( ui32Stage, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
			pD3DDevice->SetSamplerState( ui32Stage, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
		}
		else
		{
			pD3DDevice->SetSamplerState( ui32Stage, D3DSAMP_MINFILTER, D3DTEXF_POINT );
			pD3DDevice->SetSamplerState( ui32Stage, D3DSAMP_MAGFILTER, D3DTEXF_POINT );
			pD3DDevice->SetSamplerState( ui32Stage, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
		}

		if( ui32Properties & IMAGE_CLAMP )
		{
			pD3DDevice->SetSamplerState( ui32Stage, D3DSAMP_ADDRESSU,  D3DTADDRESS_CLAMP );
			pD3DDevice->SetSamplerState( ui32Stage, D3DSAMP_ADDRESSV,  D3DTADDRESS_CLAMP );
		}
		else
		{
			pD3DDevice->SetSamplerState( ui32Stage, D3DSAMP_ADDRESSU,  D3DTADDRESS_WRAP );
			pD3DDevice->SetSamplerState( ui32Stage, D3DSAMP_ADDRESSV,  D3DTADDRESS_WRAP );
		}
	}
}

const char*
DX9DDSImportC::get_info()
{
	static char	szInfo[256] = "";

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

ClassIdC
DX9DDSImportC::get_default_effect()
{
	return CLASS_DX9IMAGE_EFFECT;
}


int32
DX9DDSImportC::get_duration()
{
	return -1;
}

float32
DX9DDSImportC::get_start_label()
{
	return 0;
}

float32
DX9DDSImportC::get_end_label()
{
	return 0;
}

IDirect3DTexture9*
DX9DDSImportC::get_texture()
{
	return m_pD3DTexture;
}

void
DX9DDSImportC::eval_state( int32 i32Time )
{
	// empty
}


enum DX9DDSImportChunksE {
	CHUNK_DDSIMPORT_BASE =	0x1000,
	CHUNK_DDSIMPORT_DATA =	0x2000,
};

const uint32	DDSIMPORT_VERSION = 1;

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

	// file base
	pSave->begin_chunk( CHUNK_DDSIMPORT_BASE, DDSIMPORT_VERSION );
		sStr = m_sFileName;
		if( sStr.size() > 255 )
			sStr.resize( 255 );
		ui32Error = pSave->write_str( sStr.c_str() );
	pSave->end_chunk();

	// file data
	pSave->begin_chunk( CHUNK_DDSIMPORT_DATA, DDSIMPORT_VERSION );
		ui32Error = pSave->write( &m_ui32DataSize, sizeof( m_ui32DataSize ) );
		ui32Error = pSave->write( m_pData, m_ui32DataSize );
	pSave->end_chunk();

	return ui32Error;
}

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

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_DDSIMPORT_BASE:
			{
				if( pLoad->get_chunk_version() <= DDSIMPORT_VERSION ) {
					ui32Error = pLoad->read_str( szStr );
					m_sFileName = szStr;
				}
			}
			break;

		case CHUNK_DDSIMPORT_DATA:
			{
				if( pLoad->get_chunk_version() <= DDSIMPORT_VERSION )
				{
					// Delete existing data.
					delete [] m_pData;
					if( m_pD3DTexture )
						m_pD3DTexture->Release();
					m_pD3DTexture = 0;

					ui32Error = pLoad->read( &m_ui32DataSize, sizeof( m_ui32DataSize ) );

					m_pData = new uint8[m_ui32DataSize];

					ui32Error = pLoad->read( m_pData, m_ui32DataSize );
				}
			}
			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;
}


