www.pudn.com > Samples-latest.zip > BasicSH.cpp


//-----------------------------------------------------------------------------
// File: BasicSH.cpp
//
// Desc: This sample shows the basic usage of the SH-Utils
//		 by using the ZFXMath::SphericalHarmonics::Device.
//       This includes the creation of a custom light function,
//		 its compression, rotation and visual reconstruction.
//-----------------------------------------------------------------------------
#include 
#include 
#include 
#include "resource.h"
#include 
#include 

#include 

#include "ReconstructSH.h"

using namespace ZFXMath;

typedef float																	SHPrecisionType;
typedef float																	SHFuncValueType;

// Our custom light source, which implements the spherical function interface.
// This is the way to compress an arbitrary lighting situation into SH-Coefficients.
struct TestLight : public TReconstructSH< SHPrecisionType, SHFuncValueType >
{
	SHFuncValueType EvalFunction(const SphericalHarmonics::TSample& s) const
	{
		return SHFuncValueType( ( Max(0, 5 * Cos(s.theta) - 4) + Max(0, -4 * Sin(s.theta - (SHPrecisionType)PI) * Cos(s.phi - 2.5f) - 3) ) * 1.3f );
	}
};

typedef TReconstructSH< SHPrecisionType, SHFuncValueType >						ReconstructSH;

typedef SphericalHarmonics::TDevice< SHPrecisionType, SHFuncValueType >			SHDevice;

typedef SphericalHarmonics::TCoeffs							Coeffs;
typedef SphericalHarmonics::TRotateMatrix< SHPrecisionType, SHFuncValueType >	SHRotateMatrix;



const int NUM_RECONSTRUCT_SAMPLES = 30;


D3DXVECTOR3 LightDir = (*D3DXVec3Normalize( &LightDir, &D3DXVECTOR3(1.0f, 1.0f, -1.0f) ));

//-----------------------------------------------------------------------------
// Name: class CMyD3DApplication
// Desc: Main class to run this application. Most functionality is inherited
//       from the CD3DApplication base class.
//-----------------------------------------------------------------------------
class CMyD3DApplication : public CD3DApplication
{
private:

    ID3DXFont*             		 m_pFont;              			// Font for drawing text
	bool                   		 m_bShowHelp;          			// If true, it renders the UI control text
	bool                   		 m_WireFrame;
    CModelViewerCamera     		 m_Camera;             			// A model viewing camera
    LPD3DXEFFECT           		 m_pEffect;            			// D3DX effect interface

    LPD3DXMESH             		 m_pMesh;              			// Mesh object
    LPDIRECT3DTEXTURE9     		 m_pMeshTexture;				// Mesh texture

    D3DXHANDLE             		 m_hTime;                        // Handle to var in the effect 
    D3DXHANDLE             		 m_hWorld;                       // Handle to var in the effect 
    D3DXHANDLE             		 m_hMeshTexture;                 // Handle to var in the effect 
    D3DXHANDLE             		 m_hWorldViewProjection;         // Handle to var in the effect 
    D3DXHANDLE             		 m_hTechniqueRenderScene;        // Handle to technique in the effect 

	float*						m_pVertexBufferStaticSH;
	float*						m_pVertexBufferDynamicSH;
	float*						m_pVBOriginalFunction;

	// SH STUFF //
	bool						m_ReconstructToSphere;

	int							m_NumBands;
	int							m_NumSamples;

	// SH-Tool
	SHDevice*					m_pSHDevice;

	// Unrotated SH coefficients
	Coeffs						m_InitialCoeffs;

	// Function containing coeff rotation destination
	ReconstructSH*				m_pFunction;

	TestLight*					m_pTestLight;

	SHRotateMatrix				m_SHRotateMatrix;


protected:
    HRESULT OneTimeSceneInit();
    HRESULT InitDeviceObjects();
    HRESULT RestoreDeviceObjects();
    HRESULT InvalidateDeviceObjects();
    HRESULT DeleteDeviceObjects();
    HRESULT FinalCleanup();
    HRESULT Render();
    HRESULT FrameMove();
    HRESULT ConfirmDevice( D3DCAPS9* pCaps, DWORD dwBehavior, 
                           D3DFORMAT adapterFormat, D3DFORMAT backBufferFormat );

	void	CreateSHStuff();

    HRESULT LoadMesh( TCHAR* strFileName, LPD3DXMESH* ppMesh );
    HRESULT RenderText();

public:
    CMyD3DApplication();
    LRESULT MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
};



//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: Entry point to the program. Initializes everything, and goes into a
//       message-processing loop. Idle time is used to render the scene.
//-----------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
    CMyD3DApplication d3dApp;

    InitCommonControls();

    if( FAILED( d3dApp.Create( hInst ) ) )
        return 0;

    return d3dApp.Run();
}



//-----------------------------------------------------------------------------
// Name: CMyD3DApplication()
// Desc: Application constructor.   Paired with ~CMyD3DApplication()
//       Member variables should be initialized to a known state here.  
//       The application window has not yet been created and no Direct3D device 
//       has been created, so any initialization that depends on a window or 
//       Direct3D should be deferred to a later stage. 
//-----------------------------------------------------------------------------
CMyD3DApplication::CMyD3DApplication() : 
	m_pSHDevice(NULL),
	m_NumBands(4),
	m_NumSamples(100),
	m_InitialCoeffs(m_NumBands),
	m_pFunction(NULL),
	m_pTestLight(NULL),
	m_SHRotateMatrix(m_NumBands),
	m_pVertexBufferStaticSH(NULL),
	m_pVertexBufferDynamicSH(NULL),
	m_pVBOriginalFunction(NULL)
{
    m_strWindowTitle = _T("BasicSH");
    m_d3dEnumeration.AppUsesDepthBuffer = TRUE;
    m_dwCreationWidth  = 800;
    m_dwCreationHeight = 600;
    m_pFont = NULL;
	m_WireFrame = false;
	m_ReconstructToSphere = false;
    m_pEffect = NULL;

    m_hWorld = NULL;
    m_hMeshTexture = NULL;
    m_hWorldViewProjection = NULL;
    m_hTechniqueRenderScene = NULL;

    m_pMesh = NULL;
    m_pMeshTexture = NULL;
}




//-----------------------------------------------------------------------------
// Name: OneTimeSceneInit()
// Desc: Paired with FinalCleanup().
//       The window has been created and the IDirect3D9 interface has been
//       created, but the device has not been created yet.  Here you can
//       perform application-related initialization and cleanup that does
//       not depend on a device.
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::OneTimeSceneInit()
{
    return S_OK;
}



//-----------------------------------------------------------------------------
// Name: InitDeviceObjects()
// Desc: Paired with DeleteDeviceObjects()
//       The device has been created.  Resources that are not lost on
//       Reset() can be created here -- resources in D3DPOOL_MANAGED,
//       D3DPOOL_SCRATCH, or D3DPOOL_SYSTEMMEM.  Image surfaces created via
//       CreateImageSurface are never lost and can be created here.  Vertex
//       shaders and pixel shaders can also be created here as they are not
//       lost on Reset().
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::InitDeviceObjects()
{
    HRESULT hr;

    // Initialize the font
    HDC hDC = GetDC( NULL );
    int nHeight = -MulDiv( 9, GetDeviceCaps(hDC, LOGPIXELSY), 72 );
    ReleaseDC( NULL, hDC );
    if( FAILED( hr = D3DXCreateFont( m_pd3dDevice, nHeight, 0, FW_BOLD, 0, FALSE, DEFAULT_CHARSET, 
                         OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, 
                         TEXT("Arial"), &m_pFont ) ) )
        return DXTRACE_ERR( TEXT("D3DXCreateFont"), hr );

    // Load the mesh
    LoadMesh( "teapot.x", &m_pMesh );

    // Read the D3DX effect file
    hr = D3DXCreateEffectFromFile( m_pd3dDevice, "BasicSH.fx", NULL, NULL, 0, NULL, &m_pEffect, NULL );

    // If this fails, there should be debug output as to 
    // they the .fx file failed to compile
    if( FAILED( hr ) )
        return DXTRACE_ERR( TEXT("D3DXCreateEffectFromFile"), hr );

    // Setup the camera's projection parameters
    float fAspectRatio = m_d3dsdBackBuffer.Width / (FLOAT)m_d3dsdBackBuffer.Height;
    m_Camera.SetProjParams( D3DX_PI/4, fAspectRatio, 0.1f, 1000.0f );
    m_Camera.SetWindow( m_d3dsdBackBuffer.Width, m_d3dsdBackBuffer.Height );

    // Setup the camera's view parameters
    D3DXVECTOR3 vecEye(0.0f, 0.0f, 0.0f);
    D3DXVECTOR3 vecAt (0.0f, 0.0f, 0.0f);
    m_Camera.SetViewParams( &vecEye, &vecAt );
	m_Camera.SetRadius( 6.0f );

	m_Camera.SetButtonMasks( MOUSE_LEFT_BUTTON, MOUSE_WHEEL, MOUSE_MIDDLE_BUTTON );

	CreateSHStuff();

    return S_OK;
}

void CMyD3DApplication::CreateSHStuff()
{

	// init the SHDevice
	m_pSHDevice = new SHDevice( m_NumBands, m_NumSamples );

	m_pTestLight = new TestLight;

	// Project the test light onto SH coefficients
	m_pSHDevice->Project( *m_pTestLight, m_InitialCoeffs );

	// SHDevice is no longer required
	delete m_pSHDevice;

	// Create the 3D representation of the reconstructed test light function
	m_pFunction = new ReconstructSH(m_InitialCoeffs);

	m_pVertexBufferStaticSH  = new float[ NUM_RECONSTRUCT_SAMPLES * NUM_RECONSTRUCT_SAMPLES * 6 * 7 ];
																				  // 6 Vertices * Stride
	m_pVertexBufferDynamicSH = new float[ NUM_RECONSTRUCT_SAMPLES * NUM_RECONSTRUCT_SAMPLES * 6 * 7 ];

	m_pVBOriginalFunction    = new float[ NUM_RECONSTRUCT_SAMPLES * NUM_RECONSTRUCT_SAMPLES * 6 * 7 ];

	m_pFunction->Reconstruct( m_pVertexBufferStaticSH, NUM_RECONSTRUCT_SAMPLES, m_ReconstructToSphere );

	m_pTestLight->Reconstruct( m_pVBOriginalFunction, NUM_RECONSTRUCT_SAMPLES, m_ReconstructToSphere );
}



//-----------------------------------------------------------------------------
// Name: LoadMesh()
// Desc: Load the mesh and ensure the mesh has normals, and optimize
//       the mesh for this graphics card's vertex cache so the triangle list
//       inside in the mesh will cache miss less which improves perf. 
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::LoadMesh( TCHAR* strFileName, LPD3DXMESH* ppMesh )
{
    LPD3DXMESH pMesh = NULL;
    HRESULT hr;

    if( ppMesh == NULL )
        return E_INVALIDARG;


    hr = D3DXLoadMeshFromX(strFileName, D3DXMESH_MANAGED, 
                           m_pd3dDevice, NULL, NULL, NULL, NULL, &pMesh);
    if( FAILED(hr) || (pMesh == NULL) )
        return DXTRACE_ERR( TEXT("D3DXLoadMeshFromX"), hr );

    DWORD *rgdwAdjacency = NULL;

	//-----------------------------------------------------------------------
	// Redefine Mesh
	//-----------------------------------------------------------------------
		D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE] = 
		{
			{0,  0,  D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
			{0,  12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
			{0,  24, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 0},
			D3DDECL_END()
		};

        LPD3DXMESH pTempMesh;
        hr = pMesh->CloneMesh( pMesh->GetOptions(), decl, 
                                  m_pd3dDevice, &pTempMesh );
        if( FAILED(hr) )
            return DXTRACE_ERR( TEXT("CloneMeshFVF"), hr );

        D3DXComputeNormals( pTempMesh, NULL );

        SAFE_RELEASE( pMesh );
        pMesh = pTempMesh;
	//-----------------------------------------------------------------------
	//
	//-----------------------------------------------------------------------

	UINT uStride = pMesh->GetNumBytesPerVertex();
	UINT VertexCount = pMesh->GetNumVertices();

	BYTE* pV = NULL;
	WORD* pI = NULL;
	pMesh->LockVertexBuffer( 0, (void**) &pV );
	pMesh->LockIndexBuffer( 0, (void**) &pI );

		float* pBuffer = (float*)pV;

		for ( UINT n = 0; n < VertexCount; n++ )
		{
			pBuffer[6] = 1.0f;
			pBuffer += uStride / 4;
		}

	pMesh->UnlockIndexBuffer();
	pMesh->UnlockVertexBuffer();



    // Optimize the mesh for this graphics card's vertex cache 
    // so when rendering the mesh's triangle list the vertices will 
    // cache hit more often so it won't have to re-execute the vertex shader 
    // on those vertices so it will improves perf.     
    rgdwAdjacency = new DWORD[pMesh->GetNumFaces() * 3];
    pMesh->ConvertPointRepsToAdjacency(NULL, rgdwAdjacency);
    pMesh->OptimizeInplace( D3DXMESHOPT_VERTEXCACHE, rgdwAdjacency, NULL, NULL, NULL);
    delete[] rgdwAdjacency;

    *ppMesh = pMesh;

    return S_OK;
}



//-----------------------------------------------------------------------------
// Name: RestoreDeviceObjects()
// Desc: Paired with InvalidateDeviceObjects()
//       The device exists, but may have just been Reset().  Resources in
//       D3DPOOL_DEFAULT and any other device state that persists during
//       rendering should be set here.  Render states, matrices, textures,
//       etc., that don't change during rendering can be set once here to
//       avoid redundant state setting during Render() or FrameMove().
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::RestoreDeviceObjects()
{

    if( m_pFont )
        m_pFont->OnResetDevice();
    if( m_pEffect )
        m_pEffect->OnResetDevice();

    // To read or write to D3DX effect variables we can use the string name 
    // instead of using handles, however it improves perf to use handles since then 
    // D3DX won't have to spend time doing string compares
    m_hTechniqueRenderScene         = m_pEffect->GetTechniqueByName( "RenderScene" );
    m_hTime                         = m_pEffect->GetParameterByName( NULL, "g_fTime" );
    m_hWorld                        = m_pEffect->GetParameterByName( NULL, "g_mWorld" );
    m_hWorldViewProjection          = m_pEffect->GetParameterByName( NULL, "g_mWorldViewProjection" );
    m_hMeshTexture                  = m_pEffect->GetParameterByName( NULL, "g_MeshTexture" );

    return D3D_OK;
}




//-----------------------------------------------------------------------------
// Name: FrameMove()
// Desc: Called once per frame, the call is the entry point for animating
//       the scene.
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::FrameMove()
{
    // Update the camera's postion based on user input 
    m_Camera.FrameMove( m_fElapsedTime );

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Called once per frame, the call is the entry point for 3d
//       rendering. This function sets up render states, clears the
//       viewport, and renders the scene.
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::Render()
{
    D3DXMATRIXA16 mWorld;
    D3DXMATRIXA16 mView;
    D3DXMATRIXA16 mProj;
    D3DXMATRIXA16 mWorldViewProjection;
    UINT iPass, cPasses;

	if ( m_WireFrame )
		m_pd3dDevice->SetRenderState( D3DRS_FILLMODE, 2 );
	else
		m_pd3dDevice->SetRenderState( D3DRS_FILLMODE, 3 );

    // Clear the render target and the zbuffer 
    m_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, 0x00005F5F, 1.0f, 0);

    // Render the scene
    if( SUCCEEDED( m_pd3dDevice->BeginScene() ) )
    {
        // Set world drawing technique
        m_pEffect->SetTechnique( m_hTechniqueRenderScene );

        // Get the projection & view matrix from the camera class
        mWorld = *m_Camera.GetWorldMatrix();       
        mProj = *m_Camera.GetProjMatrix();       
        mView = *m_Camera.GetViewMatrix();
		
		D3DXMATRIX mInverseWMatrix;
		float det = D3DXMatrixDeterminant( &mWorld );
		D3DXMatrixInverse( &mInverseWMatrix, &det, &mWorld );

		float s = 0.2f;
		mWorld._11 *= s; mWorld._12 *= s; mWorld._13 *= s;
		mWorld._22 *= s; mWorld._21 *= s; mWorld._23 *= s;
		mWorld._33 *= s; mWorld._31 *= s; mWorld._32 *= s;
		mWorld._42 = -1.5f;
        mWorldViewProjection = mWorld * mView * mProj;

        // Update the effect's variables 
        m_pEffect->SetMatrix( m_hWorldViewProjection, &mWorldViewProjection );
        m_pEffect->SetMatrix( m_hWorld, &mWorld );
        m_pEffect->SetFloat( m_hTime, m_fTime );
        m_pEffect->SetTexture( m_hMeshTexture, m_pMeshTexture);

		D3DXVECTOR4 LightObjectSpace4 = LightDir;
		D3DXVec3Transform( &LightObjectSpace4, &LightDir, &mInverseWMatrix );
		D3DXVECTOR3 LightObjectSpace3 = LightObjectSpace4;
		D3DXVec3Normalize( &LightObjectSpace3, &LightObjectSpace3 );

		m_pEffect->SetVector( "g_LightDir", &D3DXVECTOR4( LightObjectSpace3.x,
														  LightObjectSpace3.y,
														  LightObjectSpace3.z, 0.0f ) );

		m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );

		m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, false );

		//-----------------------------------------------------------------------
		// Draw the teapot (set the VertexDeclaration)
		//-----------------------------------------------------------------------
        m_pEffect->Begin(&cPasses, 0);
        for (iPass = 0; iPass < cPasses; iPass++)
        {
            m_pEffect->BeginPass(iPass);

				m_pMesh->DrawSubset(0);

			m_pEffect->EndPass();
        }
        m_pEffect->End();



		//-----------------------------------------------------------------------
		// Draw Matrix-Rotated function
		//-----------------------------------------------------------------------
        mWorld = *m_Camera.GetWorldMatrix();
		mWorld._41 = -2.1f;
        mWorldViewProjection = mWorld * mView * mProj;
		
		m_pEffect->SetMatrix( m_hWorldViewProjection, &mWorldViewProjection );
		m_pEffect->SetMatrix( m_hWorld, &mWorld );

        m_pEffect->Begin(&cPasses, 0);
        for (iPass = 0; iPass < cPasses; iPass++)
        {
            m_pEffect->BeginPass(iPass);

			m_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, NUM_RECONSTRUCT_SAMPLES*NUM_RECONSTRUCT_SAMPLES*2, m_pVertexBufferStaticSH, 28 );

			m_pEffect->EndPass();
        }
        m_pEffect->End();


		//-----------------------------------------------------------------------
		// Draw SH-rotated function
		//-----------------------------------------------------------------------
		TMatrix3x3 rotMatrix(	mWorld._11, mWorld._12, mWorld._13,
										mWorld._21, mWorld._22, mWorld._23,
										mWorld._31, mWorld._32, mWorld._33 );

		SphericalHarmonics::SHRotate( m_SHRotateMatrix, rotMatrix );
		m_SHRotateMatrix.Transform( m_InitialCoeffs, m_pFunction->m_Coeffs );

		D3DXMatrixIdentity( &mWorld );
		mWorld._41 = 2.1f;
        mWorldViewProjection = mWorld * mView * mProj;

		m_pEffect->SetMatrix( m_hWorldViewProjection, &mWorldViewProjection );

		D3DXVECTOR4 LightObjectSpace4D = LightDir;
		m_pEffect->SetVector( "g_LightDir", &LightObjectSpace4D );

        m_pEffect->Begin(&cPasses, 0);
        for (iPass = 0; iPass < cPasses; iPass++)
        {
			m_pEffect->BeginPass(iPass);

			m_pFunction->Reconstruct( m_pVertexBufferDynamicSH, NUM_RECONSTRUCT_SAMPLES, m_ReconstructToSphere );

			m_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, NUM_RECONSTRUCT_SAMPLES*NUM_RECONSTRUCT_SAMPLES*2, m_pVertexBufferDynamicSH, 28 );

			m_pEffect->EndPass();
        }
        m_pEffect->End();


		//-----------------------------------------------------------------------
		// Draw the original uncompressed function
		//-----------------------------------------------------------------------
		mWorld = *m_Camera.GetWorldMatrix();

		mWorldViewProjection = mWorld * mView * mProj;

		m_pEffect->SetMatrix( m_hWorldViewProjection, &mWorldViewProjection );

		m_pEffect->SetVector( "g_LightDir", &D3DXVECTOR4( LightObjectSpace3.x,
														  LightObjectSpace3.y,
														  LightObjectSpace3.z, 0.0f ) );

		m_pEffect->Begin(&cPasses, 0);
		for (iPass = 0; iPass < cPasses; iPass++)
		{
			m_pEffect->BeginPass(iPass);

				m_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, NUM_RECONSTRUCT_SAMPLES*NUM_RECONSTRUCT_SAMPLES*2, m_pVBOriginalFunction, 28 );

			m_pEffect->EndPass();
		}
		m_pEffect->End();


		RenderText();

        m_pd3dDevice->EndScene();
    }

    return S_OK;
}


//-----------------------------------------------------------------------------
// Name: RenderText()
// Desc: Draw the help & statistics for running sample
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::RenderText()
{
	HRESULT hr;
	RECT rc;

	if( NULL == m_pFont )
		return S_OK;

	// Output statistics
	int iLineY = 0;
	SetRect( &rc, 2, iLineY, 0, 0 ); iLineY += 15;
	hr = m_pFont->DrawText( NULL, m_strFrameStats, -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ));
	if( FAILED(hr) )
		return DXTRACE_ERR( TEXT("DrawText"), hr );
	SetRect( &rc, 2, iLineY, 0, 0 ); iLineY += 15;
	hr = m_pFont->DrawText( NULL, m_strDeviceStats, -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
	if( FAILED(hr) )
		return DXTRACE_ERR( TEXT("DrawText"), hr );

	// Draw help
	if( m_bShowHelp )
	{
		// Object Description
		SetRect( &rc, 2, m_d3dsdBackBuffer.Height / 4, 0, 0 );
		m_pFont->DrawText( NULL, _T("D3D Matrix Rotated SH Function:"), -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );

		SetRect( &rc, int(m_d3dsdBackBuffer.Width / 2.5), m_d3dsdBackBuffer.Height / 4, 0, 0 );
		m_pFont->DrawText( NULL, _T("Original Light Function:"), -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );

		SetRect( &rc, m_d3dsdBackBuffer.Width - 200, m_d3dsdBackBuffer.Height / 4, 0, 0 );
		m_pFont->DrawText( NULL, _T("SH Matrix Rotated SH Function:"), -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );

		iLineY = m_d3dsdBackBuffer.Height-15*6;
		SetRect( &rc, 2, iLineY, 0, 0 ); iLineY += 15;
		hr = m_pFont->DrawText( NULL, _T("Controls:"), -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 0.75f, 0.0f, 1.0f ) );
		if( FAILED(hr) )
			return DXTRACE_ERR( TEXT("DrawText"), hr );
		SetRect( &rc, 20, iLineY, 0, 0 ); 
		hr = m_pFont->DrawText( NULL, _T("Rotate model: Left mouse button\n")
			_T("Rotate light: Right mouse button\n")
			_T("Rotate camera: Middle mouse button\n")
			_T("Zoom camera: Mouse wheel scroll\n")
			_T("Hide help: F1\n"),
			-1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 0.75f, 0.0f, 1.0f ) );
		if( FAILED(hr) )
			return DXTRACE_ERR( TEXT("DrawText"), hr );

		SetRect( &rc, 250, iLineY, 0, 0 );
		hr = m_pFont->DrawText( NULL, _T("Change Device: F2\n")
			_T("View readme: F9\n")
			_T("Change function evaluation: c\n")
			_T("Toggle wireframe: w\n")
			_T("Quit: ESC"),
			-1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 0.75f, 0.0f, 1.0f ) );
		if( FAILED(hr) )
			return DXTRACE_ERR( TEXT("DrawText"), hr );
	}
	else
	{
		SetRect( &rc, 2, iLineY, 0, 0 ); iLineY += 15;
		hr = m_pFont->DrawText( NULL, _T("Press F1 to help"), 
			-1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );
		if( FAILED(hr) )
			return DXTRACE_ERR( TEXT("DrawText"), hr );
	}

	return S_OK;
}



//-----------------------------------------------------------------------------
// Name: InvalidateDeviceObjects()
// Desc: Invalidates device objects.  Paired with RestoreDeviceObjects()
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::InvalidateDeviceObjects()
{
    if( m_pFont )
        m_pFont->OnLostDevice();
    if( m_pEffect )
        m_pEffect->OnLostDevice();

    SAFE_RELEASE(m_pMeshTexture);

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DeleteDeviceObjects()
// Desc: Paired with InitDeviceObjects()
//       Called when the app is exiting, or the device is being changed,
//       this function deletes any device dependent objects.  
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::DeleteDeviceObjects()
{
    SAFE_RELEASE(m_pEffect);
    SAFE_RELEASE(m_pFont);
    SAFE_RELEASE(m_pMesh);

	delete m_pFunction;
	delete m_pTestLight;

	delete[] m_pVertexBufferStaticSH;
	delete[] m_pVertexBufferDynamicSH;
	delete[] m_pVBOriginalFunction;

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: FinalCleanup()
// Desc: Paired with OneTimeSceneInit()
//       Called before the app exits, this function gives the app the chance
//       to cleanup after itself.
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::FinalCleanup()
{
    return S_OK;
}


//-----------------------------------------------------------------------------
// Name: ConfirmDevice()
// Desc: Called during device initialization, this code checks the device
//       for some minimum set of capabilities
//-----------------------------------------------------------------------------
HRESULT CMyD3DApplication::ConfirmDevice( D3DCAPS9* pCaps, DWORD dwBehavior,
                                          D3DFORMAT adapterFormat, D3DFORMAT backBufferFormat )
{
    // Debugging vertex shaders requires either REF or software vertex processing 
    // and debugging pixel shaders requires REF.
    #ifdef DEBUG_VS
        if( pCaps->DeviceType != D3DDEVTYPE_REF && 
            (dwBehavior & D3DCREATE_SOFTWARE_VERTEXPROCESSING) == 0 )
            return E_FAIL;
    #endif

    if( dwBehavior & D3DCREATE_PUREDEVICE )
        return E_FAIL;

    // If device doesn't support 1.1 vertex shaders in HW, switch to SWVP.
    if( pCaps->VertexShaderVersion < D3DVS_VERSION(1,1) )
    {
        if( (dwBehavior & D3DCREATE_SOFTWARE_VERTEXPROCESSING ) == 0 )
        {
            return E_FAIL;
        }
    }

    return S_OK;
}



POINT LastMPos;
D3DXVECTOR2 delta( 0.0f, 0.0f );

//-----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: Overrrides the main WndProc, so the sample can do custom message
//       handling (e.g. processing mouse, keyboard, or menu commands).
//-----------------------------------------------------------------------------
LRESULT CMyD3DApplication::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    m_Camera.HandleMessages( hWnd, uMsg, wParam, lParam );


	POINT CurMPos;
	GetCursorPos( &CurMPos );
	if ( m_Camera.IsMouseRButtonDown() )
	{
		
		delta.x += float( CurMPos.x - LastMPos.x ) * 0.01f;
		delta.y -= float( CurMPos.y - LastMPos.y ) * 0.02f;
		LightDir.x = sinf( delta.y ) * cosf( delta.x );
		LightDir.y = sinf( delta.y ) * sinf( delta.x );
		LightDir.z = cosf( delta.y );

	}
	LastMPos = CurMPos;

    switch( uMsg )
    {
        case WM_COMMAND:
        {
            switch( LOWORD(wParam) )
            {
                case IDM_TOGGLEHELP:
                    m_bShowHelp = !m_bShowHelp;
                    break;

            }
			break;
        }

		case WM_KEYUP:
		{

			switch(wParam)
			{

				case 0x57:
				{
                    m_WireFrame = !m_WireFrame;
                    break;
				}

				case 0x43:
				{
                    m_ReconstructToSphere = !m_ReconstructToSphere;

					delete m_pFunction;
					m_pFunction = new ReconstructSH(m_InitialCoeffs);
					m_pFunction->Reconstruct( m_pVertexBufferStaticSH, NUM_RECONSTRUCT_SAMPLES, m_ReconstructToSphere );
					m_pTestLight->Reconstruct( m_pVBOriginalFunction, NUM_RECONSTRUCT_SAMPLES, m_ReconstructToSphere );

                    break;
				}
			}
			break;
		}
    }

    return CD3DApplication::MsgProc( hWnd, uMsg, wParam, lParam );
}