//
// BlurPlugin.cpp
//
// Blur Plugin
//
// Copyright (c) 2000 memon/moppi productions
//

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.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 "DX9BlurPlugin.h"
#include "FileIO.h"
#include "AutoGizmoC.h"
#include "ImportableImageI.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 DX9BlurPlugin;


#define FVF_XYZCOLORUV_VERTEX			(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)
struct XYZCOLORUV_VERTEX
{
	float	x, y, z;
	DWORD	color;
	float	u, v;
};

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



//////////////////////////////////////////////////////////////////////////
//
//  Blur effect class descriptor.
//

DX9BlurDescC::DX9BlurDescC()
{
	// empty
}

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

void*
DX9BlurDescC::create()
{
	return (void*)DX9BlurEffectC::create_new();
}

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

SuperClassIdC
DX9BlurDescC::get_super_class_id() const
{
	return SUPERCLASS_EFFECT_POST_PROCESS;
}

ClassIdC
DX9BlurDescC::get_class_id() const
{
	return CLASS_DX9BLUR_EFFECT;
};

const char*
DX9BlurDescC::get_name() const
{
	return "Blur (DX9)";
}

const char*
DX9BlurDescC::get_desc() const
{
	return "Blur Effect (DX9)";
}

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

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

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

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

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

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


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

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


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

DX9BlurDescC	g_rBlurDesc;

#ifndef PAJAPLAYER

//////////////////////////////////////////////////////////////////////////
//
// 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_rBlurDesc;
	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 "DX9BlurPlugin.dll - Blur Effect plugin (c)2004 memon/moppi productions";
}

#endif



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

DX9BlurEffectC::DX9BlurEffectC() :
	m_pGBuffer( 0 ),
	m_pGBufferHalf( 0 ),
	m_pEffect( 0 ),
	m_pVertDecl( 0 )
{
	//
	// Create Attributes gizmo.
	//
	m_pAttGizmo = AutoGizmoC::create_new( this, "Attributes", ID_GIZMO_ATTRIB );

	// Blur Amount
	m_pAttGizmo->add_parameter(	ParamFloatC::create_new( m_pAttGizmo, "Size", 1, ID_ATTRIBUTE_SIZE,
						PARAM_STYLE_EDITBOX, PARAM_ANIMATABLE, 0, 32, 0.1f ) );

	// Blend type
	ParamIntC*	pBlendMode = ParamIntC::create_new( m_pAttGizmo, "Blend Mode", 0, ID_ATTRIBUTE_BLEND_MODE, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 2 );
	pBlendMode->add_label( 0, "Normal" );
	pBlendMode->add_label( 1, "Add" );
	pBlendMode->add_label( 2, "Mult" );
	m_pAttGizmo->add_parameter( pBlendMode );

	// Blur blend amount
	m_pAttGizmo->add_parameter(	ParamFloatC::create_new( m_pAttGizmo, "Blend Amount", 0.0f, ID_ATTRIBUTE_BLEND_AMOUNT,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, 0.0f, 1.0f, 0.01f ) );
}

DX9BlurEffectC::DX9BlurEffectC( EditableI* pOriginal ) :
	EffectPostProcessI( pOriginal ),
	m_pAttGizmo( 0 ),
	m_pGBuffer( 0 ),
	m_pGBufferHalf( 0 ),
	m_pEffect( 0 ),
	m_pVertDecl( 0 )
{
	// Empty. The parameters are not created in the clone constructor.
}

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

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

	if( m_pEffect )
		m_pEffect->Release();
	if( m_pVertDecl )
		m_pVertDecl->Release();
}

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

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

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

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

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

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

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

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

GizmoI*
DX9BlurEffectC::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
DX9BlurEffectC::get_class_id()
{
	// Return the class ID. Should be same as in the class descriptor.
	return CLASS_DX9BLUR_EFFECT;
}

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

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

ParamI*
DX9BlurEffectC::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_SCALE )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_SCALE );
	else if( i32Param == DEFAULT_PARAM_PIVOT )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_PIVOT );*/
	return 0;
}

void
DX9BlurEffectC::initialize( uint32 ui32Reason, 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
DX9BlurEffectC::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_BLUR_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" );

	// Warm up the temp buffer stack.
	m_pGBuffer = (DX9BufferC*)pDevice->get_temp_graphicsbuffer( GRAPHICSDEVICE_TEMPBUF_COLOR | GRAPHICSDEVICE_TEMPBUF_DEPTH );
	m_pGBufferHalf = (DX9BufferC*)pDevice->get_temp_graphicsbuffer( GRAPHICSDEVICE_TEMPBUF_COLOR | GRAPHICSDEVICE_TEMPBUF_HALF_SIZE );
	pDevice->free_temp_graphicsbuffer( m_pGBuffer );
	pDevice->free_temp_graphicsbuffer( m_pGBufferHalf );
	m_pGBuffer = 0;
	m_pGBufferHalf = 0;
}

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


void
DX9BlurEffectC::eval_state( int32 i32Time )
{
	if( !m_pDemoInterface )
	{
		TRACE( "no demoiface\n" );
		return;
	}

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

	float32		f32Blur = 1.0f;
	// Get blur.
	((ParamFloatC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_BLEND_AMOUNT ))->get_val( i32Time, f32Blur );


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

	// Get half buffer for downsampling.
	m_pGBufferHalf = (DX9BufferC*)pDevice->get_temp_graphicsbuffer( GRAPHICSDEVICE_TEMPBUF_COLOR | GRAPHICSDEVICE_TEMPBUF_HALF_SIZE );

	LPDIRECT3DDEVICE9	pD3DDevice = pDevice->get_d3ddevice();

	// Get the DX9 viewport.
	DX9ViewportC*	pViewport = (DX9ViewportC*)pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
	if( !pViewport )
	{
		TRACE( "blur: no viewport\n" );
		pDevice->free_temp_graphicsbuffer( m_pGBuffer );
		pDevice->free_temp_graphicsbuffer( m_pGBufferHalf );
		return;
	}

	if( !m_pGBuffer )
	{
		TRACE( "blur: no temp buffer\n" );
		pDevice->free_temp_graphicsbuffer( m_pGBuffer );
		pDevice->free_temp_graphicsbuffer( m_pGBufferHalf );
		return;
	}

	pD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

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


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

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


	LPDIRECT3DTEXTURE9	pTex = m_pGBuffer->get_texture();
	LPDIRECT3DTEXTURE9	pTexExtra = m_pGBuffer->get_extra_texture();

	LPDIRECT3DTEXTURE9	pTexHalf = m_pGBufferHalf->get_texture();

	if( !pTex || !pTexExtra || !pTexHalf )
	{
		TRACE( "no tex\n" );
		pDevice->free_temp_graphicsbuffer( m_pGBuffer );
		pDevice->free_temp_graphicsbuffer( m_pGBufferHalf );
		return;
	}

	if( !m_pEffect )
	{
		TRACE( "no effect\n" );
		pDevice->free_temp_graphicsbuffer( m_pGBuffer );
		pDevice->free_temp_graphicsbuffer( m_pGBufferHalf );
		return;
	}

	if( !m_pVertDecl )
	{
		TRACE( "no vert declaration\n" );
		pDevice->free_temp_graphicsbuffer( m_pGBuffer );
		pDevice->free_temp_graphicsbuffer( m_pGBufferHalf );
		return;
	}

	BBox2C	rLayout = pViewport->get_layout();
	BBox2C	rTexBounds = m_pGBuffer->get_tex_coord_bounds();

	DWORD	dwCol = D3DCOLOR_ARGB( 255, 255, 255, 255 );

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

	rQuad[1].x = rLayout[1][0];
	rQuad[1].y = rLayout[1][1];
	rQuad[1].z = 0;
	rQuad[1].color = dwCol;
	rQuad[1].u = rTexBounds[1][0];
	rQuad[1].v = rTexBounds[0][1];

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

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

	FLOAT dx = 1.0f / (FLOAT)640;
	FLOAT dy = 1.0f / (FLOAT)480;

	D3DXVECTOR4 v[12];

	v[0]  = D3DXVECTOR4(-0.326212f * dx, -0.405805f * dy, 0.0f, 0.0f);
	v[1]  = D3DXVECTOR4(-0.840144f * dx, -0.07358f * dy, 0.0f, 0.0f);
	v[2]  = D3DXVECTOR4(-0.695914f * dx, 0.457137f * dy, 0.0f, 0.0f);
	v[3]  = D3DXVECTOR4(-0.203345f * dx, 0.620716f * dy, 0.0f, 0.0f);
	v[4]  = D3DXVECTOR4(0.96234f * dx, -0.194983f * dy, 0.0f, 0.0f);
	v[5]  = D3DXVECTOR4(0.473434f * dx, -0.480026f * dy, 0.0f, 0.0f);
	v[6]  = D3DXVECTOR4(0.519456f * dx, 0.767022f * dy, 0.0f, 0.0f);
	v[7]  = D3DXVECTOR4(0.185461f * dx, -0.893124f * dy, 0.0f, 0.0f);
	v[8]  = D3DXVECTOR4(0.507431f * dx, 0.064425f * dy, 0.0f, 0.0f);
	v[9]  = D3DXVECTOR4(0.89642f * dx, 0.412458f * dy, 0.0f, 0.0f);
	v[10] = D3DXVECTOR4(-0.32194f * dx, -0.932615f * dy, 0.0f, 0.0f);
	v[11] = D3DXVECTOR4(-0.791559f * dx, -0.597705f * dy, 0.0f, 0.0f);

	m_pEffect->SetVectorArray("filterTaps", v, 12);

	if( FAILED( m_pEffect->SetFloat( "radius", f32Blur * 16.0f ) ) )
		TRACE( "set float (blur) failed\n" );

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


	//
	// Render first to the small texture.
	//
/*
	GraphicsBufferI*	pOldGBuffer = pDevice->set_render_target( m_pGBufferHalf );

	GraphicsViewportI*	pBufViewport = (GraphicsViewportI*)pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
	if( !pBufViewport )
	{
		pDevice->set_render_target( pOldGBuffer );
		pDevice->free_temp_graphicsbuffer( m_pGBuffer );
		pDevice->free_temp_graphicsbuffer( m_pGBufferHalf );
		TRACE( "no viewport\n" );
		return;
	}

	// Save current layout and viewport
	BBox2C	rOldLayout = pBufViewport->get_layout();
	BBox2C	rOldViewport = pBufViewport->get_viewport();
	float32	f32OldAspect = pBufViewport->get_pixel_aspect_ratio();

	// set newly calculated layout viewport
	// the viewport is set after begin_draw() because begin draw saves the viewport and projection matrices.
	pBufViewport->set_layout( rLayout );
	pBufViewport->set_viewport( rLayout );
	pBufViewport->set_pixel_aspect_ratio( 1.0f  );

	pDevice->clear_device( GRAPHICSDEVICE_ALLBUFFERS, ColorC( 0, 0, 0, 0 ) );
	pDevice->begin_draw();
	pDevice->begin_effects();

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

	{
		if( FAILED( m_pEffect->SetTechnique( "FirstPassF" ) ) )
			TRACE( "set technique 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 );

		if( FAILED( m_pEffect->SetTexture( "tSceneColor", pTex ) ) )
			TRACE( "set texture failed\n" );
		if( FAILED( m_pEffect->SetTexture( "tBlurDepth", pTexExtra ) ) )
			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();
	}

	// restore old layout and viewport
	pBufViewport->set_layout( rOldLayout );
	pBufViewport->set_viewport( rOldViewport );
	pBufViewport->set_pixel_aspect_ratio( f32OldAspect );

	pDevice->end_effects();
	pDevice->end_draw();

	// flush device (swap buffers)
	pDevice->flush();

	pDevice->set_render_target( pOldGBuffer );
*/

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

	pD3DDevice->SetTexture( 0, NULL );
	pD3DDevice->SetTexture( 1, NULL );

	//
	// Render first to the small texture.
	//

	{
//		if( FAILED( m_pEffect->SetTechnique( "SecondPassF" ) ) )
		HRESULT	hr;
		if( FAILED( hr = m_pEffect->SetTechnique( "FirstPassF" ) ) )
			TRACE( "BB set technique failed %s\n", DXGetErrorDescription9( hr ) );
//			TRACE( "BB set technique 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 );

		if( FAILED( m_pEffect->SetTexture( "tSceneColor", pTex ) ) )
			TRACE( "set texture failed\n" );
//		if( FAILED( m_pEffect->SetTexture( "tSceneColorHalf", pTexHalf ) ) )
//			TRACE( "set texture failed\n" );
		if( FAILED( m_pEffect->SetTexture( "tBlurDepth", pTexExtra ) ) )
			TRACE( "set texture failed blur depth\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();
	}

	pD3DDevice->SetTexture( 0, NULL );
	pD3DDevice->SetTexture( 1, NULL );

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

	// Free the temp buffers for other usage.
	pDevice->free_temp_graphicsbuffer( m_pGBuffer );
	pDevice->free_temp_graphicsbuffer( m_pGBufferHalf );
}

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

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

bool
DX9BlurEffectC::hit_test( const Vector2C& rPoint )
{
	return false;
/*
	// 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;*/
}

GraphicsBufferI*
DX9BlurEffectC::get_render_target()
{
	DeviceContextC* pContext = m_pDemoInterface->get_device_context();
	if( !pContext )
		return 0;

	DX9DeviceC*	pDevice = (DX9DeviceC*)pContext->query_interface( CLASS_DX9_DEVICEDRIVER );
	if( !pDevice )
	{
		TRACE( "blur->getrt: no device\n" );
		return 0;
	}

	m_pGBuffer = (DX9BufferC*)pDevice->get_temp_graphicsbuffer( GRAPHICSDEVICE_TEMPBUF_COLOR | GRAPHICSDEVICE_TEMPBUF_DEPTH );

	return m_pGBuffer;
}


enum DX9BlurEffectChunksE {
	CHUNK_BLUR_BASE =			0x1000,
	CHUNK_BLUR_TRANSGIZMO =		0x2000,
	CHUNK_BLUR_ATTRIBGIZMO =	0x3000,
};

const uint32	BLUR_VERSION = 1;

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

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

	// Transform
/*	pSave->begin_chunk( CHUNK_BLUR_TRANSGIZMO, BLUR_VERSION );
		ui32Error = m_pTraGizmo->save( pSave );
	pSave->end_chunk();*/

	// Attribute
	pSave->begin_chunk( CHUNK_BLUR_ATTRIBGIZMO, BLUR_VERSION );
		ui32Error = m_pAttGizmo->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

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

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

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

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

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

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

	return ui32Error;
}
