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


//-----------------------------------------------------------------------------
// File: SHLighting.cpp
//
// Desc: This sample shows an example of Spherical Harmonic Lighting 
//		 using the ZFXMath::SphericalHarmonics::Device.
//		 The main idea of this sample is the usage of a cube map as a
//		 lighting function to be able to compute the entire radiance transfer on the GPU.
//-----------------------------------------------------------------------------
#include 
#include 
#include 
#include "resource.h"
#include 
#include 
#include 

#include 

#include "CubeMapFunction.h"

using namespace ZFXMath;

typedef float																	SHPrecisionType;
typedef TVector3D														SHFuncValueType;

typedef TCubeMapFunction< SHPrecisionType, SHFuncValueType >					CubeMapLightFunction;

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

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


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:

	double						 m_Time;
    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
	LPD3DXEFFECT           		 m_pEnvSphereEffect;
	LPD3DXEFFECT           		 m_pMeshToCubeMapEffect;

    LPD3DXMESH             		 m_pMesh;              			// Mesh object
    float*						 m_pMeshPosNormal;
	float*						 m_pMeshCoeff;

	LPD3DXMESH             		 m_pEnvSphere;              	// Mesh object

    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 

	// SH STUFF //
	int							m_NumBands;
	int							m_NumSamples;

	// SH-Tool
	SHDevice*					m_pSHDevice;

	// Unrotated SH coefficients
	Coeffs						m_LightingCoeffs;
	// Rotated SH coefficients
	Coeffs						m_ObjectLightingCoeffs;
	D3DXVECTOR4*				m_D3DLightingCoeff;

	// Function containing coeff rotation destination
	CubeMapLightFunction*		m_pLight;

	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();
	void	PRTForTheMesh( int CubeMapWidth );
	void	PutPRTIntoTheMesh();

    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(1000),
	m_LightingCoeffs(m_NumBands),
	m_ObjectLightingCoeffs(m_NumBands),
	m_D3DLightingCoeff(NULL),
	m_pLight(NULL),
	m_SHRotateMatrix(m_NumBands)
{
    m_strWindowTitle = _T("SHLighting");
    m_d3dEnumeration.AppUsesDepthBuffer = TRUE;
    m_dwCreationWidth  = 800;
    m_dwCreationHeight = 600;
    m_pFont = NULL;
	m_WireFrame = false;
    m_pEffect = NULL;
	m_pEnvSphereEffect = NULL;
	m_pMeshToCubeMapEffect = NULL;

	m_Time = 0.0;

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

    m_pMesh = NULL;
	m_pMeshPosNormal = NULL;
	m_pMeshCoeff = NULL;

	m_pEnvSphere = 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( "../BasicSH/teapot.x", &m_pMesh );
	//LoadMesh( "Head_Big_Ears.x", &m_pMesh );
	LoadMesh( "sphere0.x", &m_pEnvSphere );

    // Read the D3DX effect file
    hr = D3DXCreateEffectFromFile( m_pd3dDevice, "SHLighting.fx", NULL, NULL, 0, NULL, &m_pEffect, NULL );
	hr = D3DXCreateEffectFromFile( m_pd3dDevice, "MeshToCubeMap.fx", NULL, NULL, 0, NULL, &m_pMeshToCubeMapEffect, NULL );
	hr = D3DXCreateEffectFromFile( m_pd3dDevice, "EnvSphere.fx", NULL, NULL, 0, NULL, &m_pEnvSphereEffect, 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()
{
	int CubeMapWidth = 64;
	int CubeMapFunctionSamples = CubeMapWidth * 2;

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

	// Load our lighting environment
	LPDIRECT3DCUBETEXTURE9 pCubeMap = NULL;
	D3DXCreateCubeTextureFromFile( m_pd3dDevice, "LobbyCube.dds", &pCubeMap );

	// create a spherical function
	m_pLight = new CubeMapLightFunction( m_pd3dDevice, CubeMapFunctionSamples );
	m_pLight->EvaluateCubeMap( pCubeMap );
	pCubeMap->Release();

	// Project the lighting environment onto SH coefficients
	m_pSHDevice->Project( *m_pLight, m_LightingCoeffs );

/*	for ( int n = 0; n < m_NumBands * m_NumBands; n++ )
	{
		m_LightingCoeffs(n) *= 1.0f / PI;
	}
*/
	m_Time = timeGetTime();

	PRTForTheMesh( CubeMapWidth );

	m_Time = timeGetTime() - m_Time;

	// SHDevice and the CubeMapFuncion are no longer required
	delete m_pSHDevice;
	delete m_pLight;

	PutPRTIntoTheMesh();

}

void CMyD3DApplication::PRTForTheMesh( int CubeMapWidth )
{
	Coeffs LightingCoeffs(m_NumBands);

	// Create a cube map for incoming lighting
	LPDIRECT3DCUBETEXTURE9 pCubeMap = NULL;
	D3DXCreateCubeTexture( m_pd3dDevice, CubeMapWidth, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &pCubeMap );

	D3DXMATRIX WVP;
	D3DXMATRIX World;

	D3DXMATRIX Proj;
	float farplane = 100.0f;
	D3DXMatrixPerspectiveFovLH( &Proj, D3DX_PI / 2.0f, 1.0f, 0.0000001f, farplane );
	Proj._43 = 0.0f;

	D3DXMatrixScaling( &World, farplane, farplane, farplane );

	m_pEnvSphereEffect->SetTechnique( "RenderScene" );
	m_pMeshToCubeMapEffect->SetTechnique( "RenderScene" );


	DWORD time = timeGetTime();
	DWORD vps = 0; // vertices Processed

	// for each vertex
	for( DWORD v = 0; v < m_pMesh->GetNumVertices(); v++ )
	{
		D3DXMATRIX View[6];
		D3DXVECTOR3 viewPos( m_pMeshPosNormal[v * 6 + 0], m_pMeshPosNormal[v * 6 + 1], m_pMeshPosNormal[v * 6 + 2] );
		viewPos += D3DXVECTOR3( m_pMeshPosNormal[v * 6 + 3], m_pMeshPosNormal[v * 6 + 4], m_pMeshPosNormal[v * 6 + 5] ) * 0.001f;
		D3DXVECTOR3  ViewDir[6];
		ViewDir[0] = viewPos + D3DXVECTOR3(  1,  0,  0); D3DXMatrixLookAtLH( &View[0], &viewPos, &ViewDir[0], &D3DXVECTOR3( 0, 1, 0) ); // +X
		ViewDir[1] = viewPos + D3DXVECTOR3( -1,  0,  0); D3DXMatrixLookAtLH( &View[1], &viewPos, &ViewDir[1], &D3DXVECTOR3( 0, 1, 0) ); // -X
		ViewDir[2] = viewPos + D3DXVECTOR3(  0,  1,  0); D3DXMatrixLookAtLH( &View[2], &viewPos, &ViewDir[2], &D3DXVECTOR3( 0, 0,-1) ); // +Y
		ViewDir[3] = viewPos + D3DXVECTOR3(  0, -1,  0); D3DXMatrixLookAtLH( &View[3], &viewPos, &ViewDir[3], &D3DXVECTOR3( 0, 0, 1) ); // -Y
		ViewDir[4] = viewPos + D3DXVECTOR3(  0,  0,  1); D3DXMatrixLookAtLH( &View[4], &viewPos, &ViewDir[4], &D3DXVECTOR3( 0, 1, 0) ); // +Z
		ViewDir[5] = viewPos + D3DXVECTOR3(  0,  0, -1); D3DXMatrixLookAtLH( &View[5], &viewPos, &ViewDir[5], &D3DXVECTOR3( 0, 1, 0) ); // -Z

		LPDIRECT3DSURFACE9 pBackBuffer = NULL;
		LPDIRECT3DSURFACE9 pDepthStencil = NULL;

		m_pd3dDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer );
		m_pd3dDevice->GetDepthStencilSurface( &pDepthStencil );

		m_pd3dDevice->SetDepthStencilSurface( NULL );
		m_pd3dDevice->SetTexture( 0, NULL );

		for ( int n = 0; n < 6; n++ )
		{
			LPDIRECT3DSURFACE9 pCubeMapSurface =  NULL;
			pCubeMap->GetCubeMapSurface( D3DCUBEMAP_FACES(n), 0, &pCubeMapSurface );
			
			m_pd3dDevice->SetRenderTarget( 0, pCubeMapSurface );
			pCubeMapSurface->Release();
			//m_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, 0xFFFFFFFF, 1.0f, 0);

			m_pd3dDevice->BeginScene();

				WVP = World * View[n] * Proj;
				m_pEnvSphereEffect->SetMatrix( "g_mWorldViewProjection", &WVP );
				m_pEnvSphereEffect->SetVector( "g_MeshVertexNormal", &D3DXVECTOR4( m_pMeshPosNormal[v * 6 + 3], m_pMeshPosNormal[v * 6 + 4], m_pMeshPosNormal[v * 6 + 5], 0.0f ) );
				
				// render Sphere
				UINT Passes;
				m_pEnvSphereEffect->Begin( &Passes, D3DXFX_DONOTSAVESTATE );

					m_pEnvSphereEffect->BeginPass(0);

						m_pEnvSphere->DrawSubset(0);

					m_pEnvSphereEffect->EndPass();

				m_pEnvSphereEffect->End();

				m_pd3dDevice->SetPixelShader( NULL );

				// render Mesh
				WVP = View[n] * Proj;
				m_pMeshToCubeMapEffect->SetMatrix( "g_mWorldViewProjection", &WVP );

				m_pMeshToCubeMapEffect->Begin( &Passes, D3DXFX_DONOTSAVESTATE );
				
					m_pMeshToCubeMapEffect->BeginPass(0);

						m_pMesh->DrawSubset(0);

					m_pMeshToCubeMapEffect->EndPass();
				
				m_pMeshToCubeMapEffect->End();

			m_pd3dDevice->EndScene();
		}

		m_pd3dDevice->SetRenderTarget( 0, pBackBuffer );
		m_pd3dDevice->SetDepthStencilSurface( pDepthStencil );

		pBackBuffer->Release();
		pDepthStencil->Release();

		m_pLight->EvaluateCubeMap( pCubeMap );

		// Project the lighting environment onto SH coefficients
		m_pSHDevice->Project( *m_pLight, LightingCoeffs );

		for ( int n = 0; n < m_NumBands * m_NumBands; n++ )
		{
			m_pMeshCoeff[ v * m_NumBands * m_NumBands + n ] = LightingCoeffs(n).r / float(PI);
		}

		DWORD threshold = 1000;
		DWORD timedelta = timeGetTime() - time;
		if( timedelta > threshold )
		{
			RECT rc;

			m_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, 0x00000000, 1.0f, 0);

			m_pd3dDevice->BeginScene();

				std::ostringstream oss;
				std::string str;
				float VPS = float( v - vps ) * ( 1000.0f / timedelta );
				DWORD min = DWORD( ( m_pMesh->GetNumVertices() - v ) / VPS ) / 60;
				oss << "passed vertices: " << v << " / " << m_pMesh->GetNumVertices() << " (" << float(v * 100) / float(m_pMesh->GetNumVertices()) << "%}\n" <<
						VPS << " vertices per second\n" <<
						min << " min " << DWORD( ( m_pMesh->GetNumVertices() - v ) / VPS ) - min * 60 << " sec remain";
				str = oss.str();

				SetRect( &rc, 2, 2, 0, 0 );
				m_pFont->DrawText( NULL, str.c_str(), -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );

			m_pd3dDevice->EndScene();

			m_pd3dDevice->Present( NULL, NULL, NULL, NULL );

			vps = v;
			time = timeGetTime();
		}
	
	}

	pCubeMap->Release();
}

void CMyD3DApplication::PutPRTIntoTheMesh()
{
	//-----------------------------------------------------------------------
	// 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_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 0},
		{0,  40, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 1},
		{0,  56, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 2},
		{0,  72, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 3},
		D3DDECL_END()
	};

	LPD3DXMESH pTempMesh;
	m_pMesh->CloneMesh( m_pMesh->GetOptions(), decl, 
						m_pd3dDevice, &pTempMesh );

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

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

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

	float* pBuffer = (float*)pV;

	for ( UINT n = 0; n < VertexCount; n++ )
	{
		for ( int c = 0; c < m_NumBands * m_NumBands; c++ )
		{
			pBuffer[c + 6] = m_pMeshCoeff[ n * m_NumBands * m_NumBands + c ];
		}
		pBuffer += uStride / 4;
	}

	m_pMesh->UnlockIndexBuffer();
	m_pMesh->UnlockVertexBuffer();
}


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

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

	//-----------------------------------------------------------------------
	//
	//-----------------------------------------------------------------------

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

	bool firstMesh = true;
	if ( m_pMeshPosNormal ) firstMesh = false;

	if ( firstMesh )
	{
		m_pMeshPosNormal = new float[ VertexCount * 6 ]; // Stride = 6
		m_pMeshCoeff = new float[ VertexCount * m_NumBands * m_NumBands ];
		memset( m_pMeshCoeff, 0, VertexCount * m_NumBands * m_NumBands * sizeof(float) );
	}

	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++ )
		{
			if( firstMesh )
			{
				m_pMeshPosNormal[n * 6 + 0] = pBuffer[0];
				m_pMeshPosNormal[n * 6 + 1] = pBuffer[1];
				m_pMeshPosNormal[n * 6 + 2] = pBuffer[2];
				m_pMeshPosNormal[n * 6 + 3] = pBuffer[3];
				m_pMeshPosNormal[n * 6 + 4] = pBuffer[4];
				m_pMeshPosNormal[n * 6 + 5] = pBuffer[5];
			}
			pBuffer += uStride / 4;
		}

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

    *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();
	if( m_pEnvSphereEffect )
		m_pEnvSphereEffect->OnResetDevice();
	if( m_pMeshToCubeMapEffect )
		m_pMeshToCubeMapEffect->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_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 );

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

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



		D3DXQUATERNION q( LightDir.x, LightDir.y, LightDir.z, 1.0f );
		D3DXQuaternionNormalize( &q, &q );
		D3DXMATRIX qmatrix;
		D3DXMatrixRotationQuaternion( &qmatrix , &q );

		qmatrix *= mInverseWMatrix;

		TMatrix3x3 rotMatrix(	qmatrix._11, qmatrix._12, qmatrix._13,
										qmatrix._21, qmatrix._22, qmatrix._23,
										qmatrix._31, qmatrix._32, qmatrix._33 );

		SphericalHarmonics::SHRotate( m_SHRotateMatrix, rotMatrix );
		m_SHRotateMatrix.Transform( m_LightingCoeffs, m_ObjectLightingCoeffs );

		SAFE_DELETE_ARRAY( m_D3DLightingCoeff );
		m_D3DLightingCoeff = new D3DXVECTOR4[m_NumBands * m_NumBands];
		for ( int n = 0; n < m_NumBands * m_NumBands; n++ )
		{
			m_D3DLightingCoeff[n] = D3DXVECTOR4( m_ObjectLightingCoeffs(n).r, m_ObjectLightingCoeffs(n).g, m_ObjectLightingCoeffs(n).b, 0.0f );
		}

		m_pEffect->SetVectorArray( "g_LightingCoeff", m_D3DLightingCoeff, m_NumBands * m_NumBands );

		m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );

		m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, false );

		m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, true );
		m_pd3dDevice->SetRenderState( D3DRS_ZWRITEENABLE, true );

		//-----------------------------------------------------------------------
		// Draw the teapot
		//-----------------------------------------------------------------------
        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();


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


	std::ostringstream oss;
	std::string str;
	oss << "computation time: " << m_Time << " ms";
	str = oss.str();

	SetRect( &rc, 2, iLineY, 0, 0 ); iLineY += 15;
	m_pFont->DrawText( NULL, str.c_str(), -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );


	// Draw help
	if( m_bShowHelp )
	{
		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("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();
	if( m_pEnvSphereEffect )
		m_pEnvSphereEffect->OnLostDevice();
	if( m_pMeshToCubeMapEffect )
		m_pMeshToCubeMapEffect->OnLostDevice();

    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_pEnvSphereEffect);
	SAFE_RELEASE(m_pMeshToCubeMapEffect);
    SAFE_RELEASE(m_pFont);
    SAFE_RELEASE(m_pMesh);
	SAFE_RELEASE(m_pEnvSphere);
	SAFE_DELETE_ARRAY(m_pMeshPosNormal);
	SAFE_DELETE_ARRAY(m_pMeshCoeff);

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

			}
			break;
		}
    }

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