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