//
// DX9TreePlugin.cpp
//
// DX9Tree Plugin
//
// Copyright (c) 2000-2004 memon/moppi productions
//

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

// Demopaja headers
#include "DemopajaVersion.h"
#include "PajaTypes.h"
#include "ClassDescC.h"
#include "ImportableI.h"
#include "DX9DeviceC.h"
#include "Dx9ViewportC.h"
#include "FileIO.h"
#include "AutoGizmoC.h"
#include "Vector2C.h"
#include "BBox2C.h"
#include "resource.h"

#include "DX9TreePlugin.h"
#include "DX9CameraPlugin.h"
#include "DX9DaylightPlugin.h"
#include "AudioSpectrumC.h"

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

using namespace DX9TreePlugin;

using namespace DX9MeshPlugin;
using namespace DX9CameraPlugin;
using namespace DX9DayLightPlugin;

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

//////////////////////////////////////////////////////////////////////////
//
//  DX9Tree effect class descriptor.
//

DX9TreeDescC::DX9TreeDescC()
{
	// empty
}

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

void*
DX9TreeDescC::create()
{
	return (void*)DX9TreeEffectC::create_new();
}

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

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

ClassIdC
DX9TreeDescC::get_class_id() const
{
	return CLASS_DX9TREE_EFFECT;
};

const char*
DX9TreeDescC::get_name() const
{
	return CLASS_DX9TREE_EFFECT_NAME;
}

const char*
DX9TreeDescC::get_desc() const
{
	return CLASS_DX9TREE_EFFECT_DESC;
}

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

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

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

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

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

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


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

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


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

DX9TreeDescC			g_rDX9TreeDesc;

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


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

__declspec( dllexport )
ClassDescC*
get_classdesc( int32 i )
{
	if( i == 0 )
		return &g_rDX9TreeDesc;
	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 "DX9TreePlugin.dll - Image Plugin (c)2000-2004 memon/moppi productions";
}

#endif



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

DX9TreeEffectC::DX9TreeEffectC() :
	m_pEffect( 0 ),
	m_pInstance( 0 )
{
	//
	// Create Attributes gizmo.
	//

	m_pAttGizmo = AutoGizmoC::create_new( this, "Attributes", ID_GIZMO_ATTRIB );

	// Tree mesh
	m_pAttGizmo->add_parameter(	ParamFileC::create_new( m_pAttGizmo, "Hero Mesh", NULL_SUPERCLASS, CLASS_DX9X_IMPORT, ID_ATTRIBUTE_FILE_MESH, PARAM_STYLE_FILE, PARAM_NOT_ANIMATABLE ) );

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

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

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

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

	// Band
	ParamIntC*	pBand = ParamIntC::create_new( m_pAttGizmo, "Band", 0, ID_ATTRIBUTE_VINE_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_VINE_AMOUNT,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, 0, 1.0f, 0.01f ) );
}

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

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

	if( m_pInstance )
		m_pInstance->Release();
	m_pInstance = 0;

	if( m_pEffect )
		m_pEffect->Release();

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

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

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

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

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

	// Make deep copy.
	DX9TreeEffectC*	pEffect = (DX9TreeEffectC*)pEditable;
	m_pAttGizmo->copy( pEffect->m_pAttGizmo );

	// Create new instance.
	ParamFileC*		pFile = (ParamFileC*)m_pAttGizmo->get_parameter_by_id( ID_ATTRIBUTE_FILE_MESH );

	FileHandleC*	pHandle = 0;
	DX9XImportC*	pImp = 0;

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

	if( pImp )
	{
		// Delete old instance.
		if( m_pInstance )
		{
			m_pInstance->Release();
			m_pInstance = 0;
		}
		CD3DModel*	pModel = pImp->get_model();
		if( pModel )
		{
			HRESULT	hr;
			if( FAILED( hr = pModel->CreateInstance( &m_pInstance ) ) )
			{
//				TRACE( "create instance: %s\n", DXGetErrorString9( hr ) );
				m_pInstance = 0;
			}
		}
		else
			m_pInstance = 0;
	}
}

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

	// Make shallow copy.
	DX9TreeEffectC*	pEffect = (DX9TreeEffectC*)pEditable;
	m_pAttGizmo = pEffect->m_pAttGizmo;
	m_pInstance = pEffect->m_pInstance;
	m_pEffect = pEffect->m_pEffect;
}

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

GizmoI*
DX9TreeEffectC::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_ATTRIB:
		return m_pAttGizmo;
	}

	return 0;
}

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

const char*
DX9TreeEffectC::get_class_name()
{
	// Return the class name. Should be same as in the class descriptor.
	return CLASS_DX9TREE_EFFECT_NAME;
}

void
DX9TreeEffectC::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*
DX9TreeEffectC::get_default_param( int32 i32Param )
{
	return 0;
}

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

	if( pParam && pGizmo && pGizmo->get_id() == ID_GIZMO_ATTRIB && pParam->get_id() == ID_ATTRIBUTE_FILE_MESH )
	{
	
		// The file has changed. Change Time.
		ParamFileC*		pFile = (ParamFileC*)m_pAttGizmo->get_parameter_by_id( ID_ATTRIBUTE_FILE_MESH );

		FileHandleC*	pHandle = 0;
		DX9XImportC*	pImp = 0;

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

		if( pImp )
		{
			// Delete old instance.
			if( m_pInstance )
			{
				m_pInstance->Release();
				m_pInstance = 0;
			}

			CD3DModel*	pModel = pImp->get_model();

			if( pModel )
			{
				HRESULT	hr;
				if( FAILED( hr = pModel->CreateInstance( &m_pInstance ) ) )
				{
//					TRACE( "create instance: %s\n", DXGetErrorString9( hr ) );
					TRACE( "create instance.\n" );
					m_pInstance = 0;
				}
			}
			else
				m_pInstance = 0;
		}
		else
			TRACE( "no imp\n" );
	}

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

void
DX9TreeEffectC::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();

		TRACE( "creating instance\n" );
		if( !m_pInstance )
		{
			// Create new instance.
			ParamFileC*		pFile = (ParamFileC*)m_pAttGizmo->get_parameter_by_id( ID_ATTRIBUTE_FILE_MESH );

			FileHandleC*	pHandle = 0;
			DX9XImportC*	pImp = 0;

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

			if( pImp )
			{
				// Delete old instance.
				if( m_pInstance )
				{
					m_pInstance->Release();
					m_pInstance = 0;
				}
				CD3DModel*	pModel = pImp->get_model();
				if( pModel )
				{
					HRESULT	hr;
					if( FAILED( hr = pModel->CreateInstance( &m_pInstance ) ) )
					{
//						TRACE( "create instance: %s\n", DXGetErrorString9( hr ) );
						m_pInstance = 0;
					}
				}
				else
				{
					TRACE( " - no model\n" );
					m_pInstance = 0;
				}
			}
			else
				TRACE( " - no imp\n" );
		}
		else
			TRACE( "nah!\n" );
	}
}


void
DX9TreeEffectC::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_MESH_FX ), NULL, NULL, 0, NULL, &m_pEffect, &pError ) ) )
	{
		TRACE( "create effect failed\n" );
	}
	if( pError )
		TRACE( "err: %s\n", pError->GetBufferPointer() );

	if( pError )
		pError->Release();
}

void
DX9TreeEffectC::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();

	// Reset bounding box and TM.
	m_rBBox[0] = Vector2C( 0, 0 );
	m_rBBox[1] = Vector2C( 0, 0 );
	m_rTM.set_identity();

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

	if( pHandle )
		pImp = (DX9XImportC*)pHandle->get_importable();
	else
		return;

	int32			i32Band = 0;
	float32		f32Opacity = 1.0f;
	float32		f32OpacityChange = 1.0f;
	Vector2C	VineOffset( 0, 0 );
	Vector2C	VineScale( 0, 0 );

	// Get Opacity.
	((ParamFloatC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_VINE_OPACITY ))->get_val( i32Time, f32Opacity );
	// Get Band.
	((ParamIntC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_VINE_BAND ))->get_val( i32Time, i32Band );
	// Get Opacity Change.
	((ParamFloatC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_VINE_AMOUNT ))->get_val( i32Time, f32OpacityChange );
	// Get image scale.
	((ParamVector2C*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_VINE_SCALE ))->get_val( i32Time, VineScale );
	// Get image offset.
	((ParamVector2C*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_VINE_OFFSET ))->get_val( i32Time, VineOffset );

	ImportableImageI*	pImpVine = 0;
	((ParamFileC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_VINE_IMAGE ))->get_file( i32Time, pHandle, i32FileTime );

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

	if( pImpVine )
	{
		pImp->eval_state( i32FileTime );
	}

	// Get the DX9 device.
	DX9DeviceC*	pDevice = (DX9DeviceC*)pContext->query_interface( CLASS_DX9_DEVICEDRIVER );
	if( !pDevice )
	{
		TRACE( "no device\n" );
		return;
	}

	LPDIRECT3DDEVICE9	pD3DDevice = pDevice->get_d3ddevice();

	// Get the OpenGL viewport.
	DX9ViewportC*	pViewport = (DX9ViewportC*)pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
	if( !pViewport )
	{
		TRACE( "no viewport\n" );
		return;
	}

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

	if( !m_pInstance )
		TRACE( "eval: no inst\n" );

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

	if( pImp && m_pInstance )
	{
		CD3DModel*	pModel = pImp->get_model();
		if( !pModel )
		{
			TRACE( "no model\n" );
			return;
		}

		// Find camera object.
		LayerC*	pParent = get_parent();
		if( !pParent )
			return;

		DX9CameraEffectC*	pCamEffect = 0;
		DX9DayLightEffectC*	pLightEffect = 0;

		// Find first visible camera.
		for( int32 i = 0; i < pParent->get_effect_count(); i++ )
		{
			EffectI*	pEffect = pParent->get_effect( i );
			if( pEffect->get_class_id() == CLASS_DX9CAMERA_EFFECT )
			{
				if( !pCamEffect && pEffect->get_timesegment()->is_visible( i32Time ) )
				{
					pCamEffect = (DX9CameraEffectC*)pEffect;
				}
			}
			else if( pEffect->get_class_id() == CLASS_DX9DAYLIGHT_EFFECT )
			{
				if( !pLightEffect && pEffect->get_timesegment()->is_visible( i32Time ) )
				{
					pLightEffect = (DX9DayLightEffectC*)pEffect;
				}
			}
		}

		if( !pCamEffect || !pLightEffect )
			return;

		D3DXMATRIX*	pMatView = pCamEffect->get_camera_matrix();
		D3DXMATRIX*	pMatProj = pCamEffect->get_proj_matrix();
		float32			f32FocalDistance = pCamEffect->get_focal_distance();
		float32			f32FocalRange = pCamEffect->get_focal_range();

		float	fObjectRadius = pModel->GetBoundingSphereRadius();

		pD3DDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
		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, TRUE );
		pD3DDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
		pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
		
		pD3DDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD );
		pD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
		pD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

		BBox2C	TexBounds;
		
		bool	bFog = false;

		if( pCamEffect->get_fog_type() != 0 )
		{
			float32	f32Start = pCamEffect->get_fog_start();
			float32	f32End = pCamEffect->get_fog_end();
			ColorC	Col = pCamEffect->get_fog_color();
			DWORD	dwCol = D3DCOLOR_ARGB( 255, (int32)(Col[0] * 255), (int32)(Col[1] * 255), (int32)(Col[2] * 255) );

			pD3DDevice->SetRenderState( D3DRS_FOGENABLE, TRUE );
			pD3DDevice->SetRenderState( D3DRS_FOGTABLEMODE, D3DFOG_LINEAR );
			pD3DDevice->SetRenderState( D3DRS_FOGSTART, *(DWORD *)(&f32Start) );
			pD3DDevice->SetRenderState( D3DRS_FOGEND,   *(DWORD *)(&f32End) );
			pD3DDevice->SetRenderState( D3DRS_FOGCOLOR, dwCol );

			bFog = true;
		}
		else
		{
			pD3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE );
		}

    // Setup the light

    // Set Light for vertex shader
    D3DXVECTOR4 vLightDir( 0.0f, 1.0f, -1.0f, 0.0f );
		D3DXVECTOR4 vLightColor( 1, 1, 1, 1 );

		// Primary
		LightC*	pPrimaryLight = pLightEffect->get_primary_light();
		if( pPrimaryLight )
		{
			Vector3C	Pos = pPrimaryLight->get_position();
			Vector3C	Target = pPrimaryLight->get_target();
			Vector3C	Dir = (Pos - Target).normalize();
			ColorC		Col = pPrimaryLight->get_color() * pPrimaryLight->get_multiplier();
			vLightDir.x = Dir[0];
			vLightDir.y = Dir[1];
			vLightDir.z = Dir[2];
			vLightColor.x = Col[0];
			vLightColor.y = Col[1];
			vLightColor.z = Col[2];
	    m_pEffect->SetVector( "light1Dir", &vLightDir );
	    m_pEffect->SetVector( "light1Diffuse", &vLightColor );
		}
		else
		{
			vLightColor.x = 0;
			vLightColor.y = 0;
			vLightColor.z = 0;
	    m_pEffect->SetVector( "light1Diffuse", &vLightColor );
		}

		// Fill 1
		LightC*	pFill1Light = pLightEffect->get_fill_light_1();
		if( pFill1Light )
		{
			Vector3C	Pos = pFill1Light->get_position();
			Vector3C	Target = pFill1Light->get_target();
			Vector3C	Dir = (Pos - Target).normalize();
			ColorC		Col = pFill1Light->get_color() * pFill1Light->get_multiplier();
			vLightDir.x = Dir[0];
			vLightDir.y = Dir[1];
			vLightDir.z = Dir[2];
			vLightColor.x = Col[0];
			vLightColor.y = Col[1];
			vLightColor.z = Col[2];
	    m_pEffect->SetVector( "light2Dir", &vLightDir );
	    m_pEffect->SetVector( "light2Diffuse", &vLightColor );
		}
		else
		{
			vLightColor.x = 0;
			vLightColor.y = 0;
			vLightColor.z = 0;
	    m_pEffect->SetVector( "light2Diffuse", &vLightColor );
		}

		// Fill 2
		LightC*	pFill2Light = pLightEffect->get_fill_light_2();
		if( pFill2Light )
		{
			Vector3C	Pos = pFill2Light->get_position();
			Vector3C	Target = pFill2Light->get_target();
			Vector3C	Dir = (Pos - Target).normalize();
			ColorC		Col = pFill2Light->get_color() * pFill2Light->get_multiplier();
			vLightDir.x = Dir[0];
			vLightDir.y = Dir[1];
			vLightDir.z = Dir[2];
			vLightColor.x = Col[0];
			vLightColor.y = Col[1];
			vLightColor.z = Col[2];
	    m_pEffect->SetVector( "light3Dir", &vLightDir );
	    m_pEffect->SetVector( "light3Diffuse", &vLightColor );
		}
		else
		{
			vLightColor.x = 0;
			vLightColor.y = 0;
			vLightColor.z = 0;
	    m_pEffect->SetVector( "light3Diffuse", &vLightColor );
		}

    // Setup the projection matrix
    D3DXMATRIXA16	matProjTrans;

		{
			HRESULT	hr;
			if( FAILED( hr = m_pEffect->SetTechnique( "tBasic" ) ) )
				TRACE( "!!!set tech 'tBasic' failed \n" );
		}

    m_pEffect->SetMatrix( "mViewProj", pMatProj );
    m_pEffect->SetVector( "lhtDir", &vLightDir );

		// Set the pixel shader output based on the extra buffer.
		uint32	ui32PSIndex = 0;
		if( pDevice->get_flags() & GRAPHICSBUFFER_INIT_EXTRA )
			ui32PSIndex = 1;

		m_pEffect->SetInt( "NumRenderTarget", ui32PSIndex );


		if( f32FocalRange < 0.0001f )
			f32FocalRange = 0.0001f;

		// Set DOF parameters.
		m_pEffect->SetFloat( "focalDist", f32FocalDistance );
		m_pEffect->SetFloat( "focalRange", 1.0f / f32FocalRange );

		D3DXVECTOR3	vObjectCenter = pModel->GetBoundingSphereCenter();

		D3DXMATRIXA16 matWorld;
		D3DXMatrixIdentity( &matWorld );

		float64	f64Time = pTimeContext->convert_time_to_fps( i32Time, 1.0 );

		RenderContextS	RC;
		RC.iTime = i32Time;
		RC.pDevice = pDevice;
		RC.pEffect = m_pEffect;
		RC.pMatView = pMatView;

		// Setup tex stage
    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 );

    pD3DDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_MODULATE );
    pD3DDevice->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
    pD3DDevice->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
    pD3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
    pD3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
    pD3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
    pD3DDevice->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 1 );
    pD3DDevice->SetTextureStageState( 1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );


		if( pImpVine )
		{
			TexBounds = pImpVine->get_tex_coord_bounds();
			pImpVine->bind_texture( pDevice, 2, IMAGE_LINEAR | IMAGE_WRAP );

			pD3DDevice->SetTextureStageState( 2, D3DTSS_COLOROP,   D3DTOP_ADD );
			pD3DDevice->SetTextureStageState( 2, D3DTSS_COLORARG1, D3DTA_TEXTURE );
			pD3DDevice->SetTextureStageState( 2, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
			pD3DDevice->SetTextureStageState( 2, D3DTSS_ALPHAOP,   D3DTOP_ADD );
			pD3DDevice->SetTextureStageState( 2, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
			pD3DDevice->SetTextureStageState( 2, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
			pD3DDevice->SetTextureStageState( 2, D3DTSS_TEXCOORDINDEX, 2 );
			pD3DDevice->SetTextureStageState( 2, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );

			// Read back the D3D texture interface.
			IDirect3DBaseTexture9*	pD3DTex = 0;
			pD3DDevice->GetTexture( 2, &pD3DTex );

			HRESULT	hr;
			if( FAILED( hr = m_pEffect->SetTexture( "tVineTex", pD3DTex ) ) )
				TRACE( "set base texture failed\n" );

			if( pD3DTex )
				pD3DTex->Release();
		}

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

		D3DXVECTOR4	Temp( 0, 0, 0, 0 );

		Temp.x = VineScale[0];
		Temp.y = VineScale[1];
		m_pEffect->SetVector( "vineScale", &Temp );

		Temp.x = VineOffset[0];
		Temp.y = VineOffset[1];
		m_pEffect->SetVector( "vineOffset", &Temp );

		m_pEffect->SetFloat( "vineOpacity", f32Opacity );

		m_pInstance->Update( f64Time, &matWorld );
		m_pInstance->Draw( &RC );

		pD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

		if( bFog )
			pD3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE );
	}
	else
	{
		TRACE( "no imp\n" );
	}
}

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

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

bool
DX9TreeEffectC::hit_test( const Vector2C& rPoint )
{
	return false;
}


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

const uint32	IMAGE_VERSION = 1;

uint32
DX9TreeEffectC::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();

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

	return ui32Error;
}

uint32
DX9TreeEffectC::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_ATTRIBGIZMO:
			// Transform
			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;
}
