home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / Direct3D / VertexBlend / vertexblend.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  18.0 KB  |  514 lines

  1. //-----------------------------------------------------------------------------
  2. // File: VertexBlend.cpp
  3. //
  4. // Desc: Example code showing how to do a skinning effect, using the vertex
  5. //       blending feature of Direct3D. Normally, Direct3D transforms each
  6. //       vertex through the world matrix. The vertex blending feature,
  7. //       however, uses mulitple world matrices and a per-vertex blend factor
  8. //       to transform each vertex.
  9. //
  10. // Copyright (c) 1997-2001 Microsoft Corporation. All rights reserved.
  11. //-----------------------------------------------------------------------------
  12. #define STRICT
  13. #include <math.h>
  14. #include <stdio.h>
  15. #include <D3DX8.h>
  16. #include "D3DApp.h"
  17. #include "D3DFile.h"
  18. #include "D3DFont.h"
  19. #include "D3DUtil.h"
  20. #include "DXUtil.h"
  21. #include "resource.h"
  22.  
  23.  
  24.  
  25.  
  26. //-----------------------------------------------------------------------------
  27. // Name: struct BLENDVERTEX
  28. // Desc: Custom vertex which includes a blending factor
  29. //-----------------------------------------------------------------------------
  30. struct BLENDVERTEX
  31. {
  32.     D3DXVECTOR3 v;       // Referenced as v0 in the vertex shader
  33.     FLOAT       blend;   // Referenced as v1.x in the vertex shader
  34.     D3DXVECTOR3 n;       // Referenced as v3 in the vertex shader
  35.     FLOAT       tu, tv;  // Referenced as v7 in the vertex shader
  36. };
  37.  
  38. #define D3DFVF_BLENDVERTEX (D3DFVF_XYZB1|D3DFVF_NORMAL|D3DFVF_TEX1)
  39.  
  40.  
  41.  
  42.  
  43. //-----------------------------------------------------------------------------
  44. // Name: class CMyD3DApplication
  45. // Desc: Application class. The base class (CD3DApplication) provides the 
  46. //       generic functionality needed in all Direct3D samples. CMyD3DApplication 
  47. //       adds functionality specific to this sample program.
  48. //-----------------------------------------------------------------------------
  49. class CMyD3DApplication : public CD3DApplication
  50. {
  51.     CD3DFont*    m_pFont;
  52.     CD3DMesh*    m_pObject;           // Object to use for vertex blending
  53.     DWORD        m_dwNumVertices;
  54.     DWORD        m_dwNumFaces;
  55.     LPDIRECT3DVERTEXBUFFER8 m_pVB;    
  56.     LPDIRECT3DINDEXBUFFER8  m_pIB;
  57.  
  58.     D3DXMATRIX   m_matUpperArm;       // Vertex blending matrices
  59.     D3DXMATRIX   m_matLowerArm;
  60.  
  61.     DWORD        m_dwVertexShader;    // Vertex shader
  62.     BOOL         m_bUseVertexShader;
  63.  
  64. protected:
  65.     HRESULT OneTimeSceneInit();
  66.     HRESULT InitDeviceObjects();
  67.     HRESULT RestoreDeviceObjects();
  68.     HRESULT InvalidateDeviceObjects();
  69.     HRESULT DeleteDeviceObjects();
  70.     HRESULT Render();
  71.     HRESULT FrameMove();
  72.     HRESULT FinalCleanup();
  73.     HRESULT ConfirmDevice( D3DCAPS8*, DWORD, D3DFORMAT );
  74.     LRESULT MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
  75.  
  76. public:
  77.     CMyD3DApplication();
  78. };
  79.  
  80.  
  81.  
  82.  
  83. //-----------------------------------------------------------------------------
  84. // Name: WinMain()
  85. // Desc: Entry point to the program. Initializes everything, and goes into a
  86. //       message-processing loop. Idle time is used to render the scene.
  87. //-----------------------------------------------------------------------------
  88. INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
  89. {
  90.     CMyD3DApplication d3dApp;
  91.     
  92.     if( FAILED( d3dApp.Create( hInst ) ) )
  93.         return 0;
  94.  
  95.     return d3dApp.Run();
  96. }
  97.  
  98.  
  99.  
  100.  
  101. //-----------------------------------------------------------------------------
  102. // Name: CMyD3DApplication()
  103. // Desc: Application constructor. Sets attributes for the app.
  104. //-----------------------------------------------------------------------------
  105. CMyD3DApplication::CMyD3DApplication()
  106. {
  107.     m_strWindowTitle    = _T("VertexBlend: Surface Skinning Example");
  108.     m_bUseDepthBuffer   = TRUE;
  109.  
  110.     m_pFont             = new CD3DFont( _T("Arial"), 12, D3DFONT_BOLD );
  111.     m_pObject           = new CD3DMesh();
  112.     m_pVB               = NULL;
  113.     m_pIB               = NULL;
  114.     m_dwVertexShader    = 0L;
  115.     m_bUseVertexShader  = FALSE;
  116. }
  117.  
  118.  
  119.  
  120.  
  121. //-----------------------------------------------------------------------------
  122. // Name: OneTimeSceneInit()
  123. // Desc: Called during initial app startup, this function performs all the
  124. //       permanent initialization.
  125. //-----------------------------------------------------------------------------
  126. HRESULT CMyD3DApplication::OneTimeSceneInit()
  127. {
  128.     return S_OK;
  129. }
  130.  
  131.  
  132.  
  133.  
  134. //-----------------------------------------------------------------------------
  135. // Name: FrameMove()
  136. // Desc: Called once per frame, the call is the entry point for animating
  137. //       the scene.
  138. //-----------------------------------------------------------------------------
  139. HRESULT CMyD3DApplication::FrameMove()
  140. {
  141.     // Set the vertex blending matrices for this frame
  142.     D3DXVECTOR3 vAxis( 2+sinf(m_fTime*3.1f), 2+sinf(m_fTime*3.3f), sinf(m_fTime*3.5f) ); 
  143.     D3DXMatrixRotationAxis( &m_matLowerArm, &vAxis, sinf(3*m_fTime) );
  144.     D3DXMatrixIdentity( &m_matUpperArm );
  145.  
  146.     // Set the vertex shader constants. Note: outside of the blend matrices,
  147.     // most of these values don't change, so don't need to really be set every
  148.     // frame. It's just done here for clarity
  149.     if( m_bUseVertexShader )
  150.     {
  151.         // Some basic constants
  152.         D3DXVECTOR4 vZero(0,0,0,0);
  153.         D3DXVECTOR4 vOne(1,1,1,1);
  154.  
  155.         // Lighting vector (normalized) and material colors. (Use red light
  156.         // to show difference from non-vertex shader case.)
  157.         D3DXVECTOR4 vLight( 0.5f, 1.0f, -1.0f, 0.0f );
  158.         D3DXVec4Normalize( &vLight, &vLight );
  159.         FLOAT       fDiffuse[] = { 1.00f, 1.00f, 0.00f, 0.00f };
  160.         FLOAT       fAmbient[] = { 0.25f, 0.25f, 0.25f, 0.25f };
  161.  
  162.         // Vertex shader operations use transposed matrices
  163.         D3DXMATRIX matWorld0Transpose, matWorld1Transpose;
  164.         D3DXMATRIX matView, matProj, matViewProj, matViewProjTranspose;
  165.         m_pd3dDevice->GetTransform( D3DTS_VIEW,       &matView );
  166.         m_pd3dDevice->GetTransform( D3DTS_PROJECTION, &matProj );
  167.         D3DXMatrixMultiply( &matViewProj, &matView, &matProj );
  168.         D3DXMatrixTranspose( &matWorld0Transpose, &m_matUpperArm );
  169.         D3DXMatrixTranspose( &matWorld1Transpose, &m_matLowerArm );
  170.         D3DXMatrixTranspose( &matViewProjTranspose, &matViewProj );
  171.  
  172.         // Set the vertex shader constants
  173.         m_pd3dDevice->SetVertexShaderConstant(  0, &vZero,    1 );
  174.         m_pd3dDevice->SetVertexShaderConstant(  1, &vOne,     1 );
  175.         m_pd3dDevice->SetVertexShaderConstant(  4, &matWorld0Transpose,   4 );
  176.         m_pd3dDevice->SetVertexShaderConstant(  8, &matWorld1Transpose,   4 );
  177.         m_pd3dDevice->SetVertexShaderConstant( 12, &matViewProjTranspose, 4 );
  178.         m_pd3dDevice->SetVertexShaderConstant( 20, &vLight,   1 );
  179.         m_pd3dDevice->SetVertexShaderConstant( 21, &fDiffuse, 1 );
  180.         m_pd3dDevice->SetVertexShaderConstant( 22, &fAmbient, 1 );
  181.     }
  182.  
  183.     return S_OK;
  184. }
  185.  
  186.  
  187.  
  188.  
  189. //-----------------------------------------------------------------------------
  190. // Name: Render()
  191. // Desc: Called once per frame, the call is the entry point for 3d
  192. //       rendering. This function sets up render states, clears the
  193. //       viewport, and renders the scene.
  194. //-----------------------------------------------------------------------------
  195. HRESULT CMyD3DApplication::Render()
  196. {
  197.     // Clear the backbuffer
  198.     m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, 
  199.                          0x000000ff, 1.0f, 0L );
  200.  
  201.     // Begin the scene
  202.     if( SUCCEEDED( m_pd3dDevice->BeginScene() ) )
  203.     {
  204.         if( m_bUseVertexShader )        
  205.         {
  206.             m_pd3dDevice->SetVertexShader( m_dwVertexShader );
  207.             m_pd3dDevice->SetStreamSource( 0, m_pVB, sizeof(BLENDVERTEX) );
  208.             m_pd3dDevice->SetIndices( m_pIB, 0 );
  209.             m_pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, m_dwNumVertices,
  210.                                                 0, m_dwNumFaces );
  211.         }
  212.         else
  213.         {
  214.             // Enable vertex blending using API
  215.             m_pd3dDevice->SetTransform( D3DTS_WORLD,  &m_matUpperArm );
  216.             m_pd3dDevice->SetTransform( D3DTS_WORLD1, &m_matLowerArm );
  217.             m_pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND, D3DVBF_1WEIGHTS );
  218.  
  219.             // Display the object
  220.             m_pObject->Render( m_pd3dDevice );
  221.         }
  222.  
  223.         // Output statistics
  224.         m_pFont->DrawText( 2,  0, D3DCOLOR_ARGB(255,255,255,0), m_strFrameStats );
  225.         m_pFont->DrawText( 2, 20, D3DCOLOR_ARGB(255,255,255,0), m_strDeviceStats );
  226.  
  227.         if( m_bUseVertexShader )
  228.             m_pFont->DrawText( 2, 40, D3DCOLOR_ARGB(255,255,255,255), _T("Using vertex shader") );
  229.         else
  230.             m_pFont->DrawText( 2, 40, D3DCOLOR_ARGB(255,255,255,255), _T("Using D3DRS_VERTEXBLEND") );
  231.  
  232.         // End the scene.
  233.         m_pd3dDevice->EndScene();
  234.     }
  235.  
  236.     return S_OK;
  237. }
  238.  
  239.  
  240.  
  241.  
  242. //-----------------------------------------------------------------------------
  243. // Name: InitDeviceObjects()
  244. // Desc: Initialize scene objects.
  245. //-----------------------------------------------------------------------------
  246. HRESULT CMyD3DApplication::InitDeviceObjects()
  247. {
  248.     // Initialize the font's internal textures
  249.     m_pFont->InitDeviceObjects( m_pd3dDevice );
  250.  
  251.     // Load an object to render
  252.     if( FAILED( m_pObject->Create( m_pd3dDevice, _T("mslogo.x") ) ) )
  253.         return D3DAPPERR_MEDIANOTFOUND;
  254.  
  255.     if( ( ( m_dwCreateFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING ) ||
  256.           ( m_dwCreateFlags & D3DCREATE_MIXED_VERTEXPROCESSING ) ) &&
  257.         m_d3dCaps.VertexShaderVersion < D3DVS_VERSION(1,0) )
  258.     {
  259.         // No VS available, so don't try to use it or allow user to
  260.         // switch to it
  261.         m_bUseVertexShader = FALSE;
  262.         EnableMenuItem( GetMenu( m_hWnd ), IDM_USEVERTEXSHADER, MF_GRAYED );
  263.     }
  264.     else if( m_d3dCaps.MaxVertexBlendMatrices < 2 )
  265.     {
  266.         // No blend matrices available, so don't try to use them or 
  267.         // allow user to switch to them
  268.         m_bUseVertexShader = TRUE;
  269.         EnableMenuItem( GetMenu( m_hWnd ), IDM_USEVERTEXSHADER, MF_GRAYED );
  270.     }
  271.     else
  272.     {
  273.         // Both techniques available, so default to blend matrices and 
  274.         // allow the user to switch techniques
  275.         m_bUseVertexShader = FALSE;
  276.         EnableMenuItem( GetMenu( m_hWnd ), IDM_USEVERTEXSHADER, MF_ENABLED );
  277.     }
  278.  
  279.     // Set a custom FVF for the mesh
  280.     m_pObject->SetFVF( m_pd3dDevice, D3DFVF_BLENDVERTEX );
  281.  
  282.     // Add blending weights to the mesh
  283.     {
  284.         // Gain acces to the mesh's vertices
  285.         LPDIRECT3DVERTEXBUFFER8 pVB;
  286.         BLENDVERTEX* pVertices;
  287.         DWORD        dwNumVertices = m_pObject->GetSysMemMesh()->GetNumVertices();
  288.         m_pObject->GetSysMemMesh()->GetVertexBuffer( &pVB );
  289.         pVB->Lock( 0, 0, (BYTE**)&pVertices, 0 );
  290.  
  291.         // Calculate the min/max z values for all the vertices
  292.         FLOAT fMinX =  1e10f;
  293.         FLOAT fMaxX = -1e10f;
  294.  
  295.         for( DWORD i=0; i<dwNumVertices; i++ )
  296.         {
  297.             if( pVertices[i].v.x < fMinX ) 
  298.                 fMinX = pVertices[i].v.x;
  299.             if( pVertices[i].v.x > fMaxX ) 
  300.                 fMaxX = pVertices[i].v.x;
  301.         }
  302.  
  303.         for( i=0; i<dwNumVertices; i++ )
  304.         {
  305.             // Set the blend factors for the vertices
  306.             FLOAT a = ( pVertices[i].v.x - fMinX ) / ( fMaxX - fMinX );
  307.             pVertices[i].blend = 1.0f-sinf(a*D3DX_PI*1.0f);
  308.         }
  309.  
  310.         // Done with the mesh's vertex buffer data
  311.         pVB->Unlock();
  312.         pVB->Release();
  313.     }
  314.  
  315.     return S_OK;
  316. }
  317.  
  318.  
  319.  
  320.  
  321. //-----------------------------------------------------------------------------
  322. // Name: RestoreDeviceObjects()
  323. // Desc: Restore device-memory objects and state after a device is created or
  324. //       resized.
  325. //-----------------------------------------------------------------------------
  326. HRESULT CMyD3DApplication::RestoreDeviceObjects()
  327. {
  328.     m_pFont->RestoreDeviceObjects();
  329.  
  330.     // Restore mesh's local memory objects
  331.     m_pObject->RestoreDeviceObjects( m_pd3dDevice );
  332.  
  333.     // Get access to the mesh vertex and index buffers
  334.     m_pObject->GetLocalMesh()->GetVertexBuffer( &m_pVB );
  335.     m_pObject->GetLocalMesh()->GetIndexBuffer( &m_pIB );
  336.     m_dwNumVertices = m_pObject->GetLocalMesh()->GetNumVertices();
  337.     m_dwNumFaces    = m_pObject->GetLocalMesh()->GetNumFaces();
  338.  
  339.     if( ( m_dwCreateFlags & D3DCREATE_SOFTWARE_VERTEXPROCESSING ) ||
  340.         m_d3dCaps.VertexShaderVersion >= D3DVS_VERSION(1,0) )
  341.     {
  342.         // Setup the vertex declaration
  343.         DWORD adwDecl[50];
  344.         D3DXDeclaratorFromFVF( D3DFVF_BLENDVERTEX, adwDecl );
  345.  
  346.         // Create vertex shader from a file
  347.         if( FAILED( D3DUtil_CreateVertexShader( m_pd3dDevice, 
  348.                                                 _T("Blend.vsh"), adwDecl,
  349.                                                 &m_dwVertexShader ) ) )
  350.         {
  351.             return E_FAIL;
  352.         }
  353.     }
  354.     // Set miscellaneous render states
  355.     m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
  356.     m_pd3dDevice->SetRenderState( D3DRS_AMBIENT, 0x00404040 );
  357.  
  358.     // Set the projection matrix
  359.     D3DXMATRIX matProj;
  360.     FLOAT fAspect = m_d3dsdBackBuffer.Width / (FLOAT)m_d3dsdBackBuffer.Height;
  361.     D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, fAspect, 1.0f, 10000.0f );
  362.     m_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
  363.  
  364.     // Set the app view matrix for normal viewing
  365.     D3DXVECTOR3 vEyePt    = D3DXVECTOR3( 0.0f,-5.0f,-10.0f );
  366.     D3DXVECTOR3 vLookatPt = D3DXVECTOR3( 0.0f, 0.0f,  0.0f );
  367.     D3DXVECTOR3 vUpVec    = D3DXVECTOR3( 0.0f, 1.0f,  0.0f );
  368.     D3DXMATRIX matView;
  369.     D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
  370.     m_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
  371.  
  372.     // Create a directional light. (Use yellow light to distinguish from
  373.     // vertex shader case.)
  374.     D3DLIGHT8 light;
  375.     D3DUtil_InitLight( light, D3DLIGHT_DIRECTIONAL, -0.5f, -1.0f, 1.0f );
  376.     light.Diffuse.r = 1.0f;
  377.     light.Diffuse.g = 1.0f;
  378.     light.Diffuse.b = 0.0f;
  379.     m_pd3dDevice->SetLight( 0, &light );
  380.     m_pd3dDevice->LightEnable( 0, TRUE );
  381.     m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
  382.  
  383.     return S_OK;
  384. }
  385.  
  386.  
  387.  
  388.  
  389. //-----------------------------------------------------------------------------
  390. // Name: InvalidateDeviceObjects()
  391. // Desc: Called when the device-dependent objects are about to be lost.
  392. //-----------------------------------------------------------------------------
  393. HRESULT CMyD3DApplication::InvalidateDeviceObjects()
  394. {
  395.     m_pFont->InvalidateDeviceObjects();
  396.     m_pObject->InvalidateDeviceObjects();
  397.  
  398.     if( m_dwVertexShader != 0 )
  399.         m_pd3dDevice->DeleteVertexShader( m_dwVertexShader );
  400.  
  401.     SAFE_RELEASE( m_pVB );
  402.     SAFE_RELEASE( m_pIB );
  403.  
  404.     return S_OK;
  405. }
  406.  
  407.  
  408.  
  409.  
  410. //-----------------------------------------------------------------------------
  411. // Name: DeleteDeviceObjects()
  412. // Desc: Called when the app is exiting, or the device is being changed,
  413. //       this function deletes any device dependent objects.
  414. //-----------------------------------------------------------------------------
  415. HRESULT CMyD3DApplication::DeleteDeviceObjects()
  416. {
  417.     m_pFont->DeleteDeviceObjects();
  418.     m_pObject->Destroy();
  419.  
  420.     return S_OK;
  421. }
  422.  
  423.  
  424.  
  425.  
  426. //-----------------------------------------------------------------------------
  427. // Name: FinalCleanup()
  428. // Desc: Called before the app exits, this function gives the app the chance
  429. //       to cleanup after itself.
  430. //-----------------------------------------------------------------------------
  431. HRESULT CMyD3DApplication::FinalCleanup()
  432. {
  433.     SAFE_DELETE( m_pFont );
  434.     SAFE_DELETE( m_pObject );
  435.  
  436.     return S_OK;
  437. }
  438.  
  439.  
  440.  
  441.  
  442. //-----------------------------------------------------------------------------
  443. // Name: ConfirmDevice()
  444. // Desc: Called during device intialization, this code checks the device
  445. //       for some minimum set of capabilities
  446. //-----------------------------------------------------------------------------
  447. HRESULT CMyD3DApplication::ConfirmDevice( D3DCAPS8* pCaps, DWORD dwBehavior, 
  448.                                           D3DFORMAT Format )
  449. {
  450.     if( dwBehavior & D3DCREATE_PUREDEVICE )
  451.         return E_FAIL; // GetTransform doesn't work on PUREDEVICE
  452.  
  453.     // Check that the device supports at least one of the two techniques
  454.     // used in this sample: either a vertex shader, or at least two blend
  455.     // matrices and a directional light.
  456.  
  457.     if( (dwBehavior & D3DCREATE_HARDWARE_VERTEXPROCESSING ) ||
  458.         (dwBehavior & D3DCREATE_MIXED_VERTEXPROCESSING ) )
  459.     {
  460.         if( pCaps->VertexShaderVersion >= D3DVS_VERSION(1,0) )
  461.             return S_OK;
  462.     }
  463.     else
  464.     {
  465.         // Software vertex processing always supports vertex shaders
  466.         return S_OK;
  467.     }
  468.  
  469.     // Check that the device can blend vertices with at least two matrices
  470.     // (Software can always do up to 4 blend matrices)
  471.     if( pCaps->MaxVertexBlendMatrices < 2 )
  472.         return E_FAIL;
  473.  
  474.     // If this is a TnL device, make sure it supports directional lights
  475.     if( (dwBehavior & D3DCREATE_HARDWARE_VERTEXPROCESSING ) ||
  476.         (dwBehavior & D3DCREATE_MIXED_VERTEXPROCESSING ) )
  477.     {
  478.         if( !(pCaps->VertexProcessingCaps & D3DVTXPCAPS_DIRECTIONALLIGHTS ) )
  479.             return E_FAIL;
  480.     }
  481.  
  482.     return S_OK;
  483. }
  484.  
  485.  
  486.  
  487.  
  488. //-----------------------------------------------------------------------------
  489. // Name: MsgProc()
  490. // Desc: Message proc function to handle key and menu input
  491. //-----------------------------------------------------------------------------
  492. LRESULT CMyD3DApplication::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam,
  493.                                     LPARAM lParam )
  494. {
  495.     // Handle menu commands
  496.     if( WM_COMMAND == uMsg )
  497.     {
  498.         switch( LOWORD(wParam) )
  499.         {
  500.             case IDM_USEVERTEXSHADER:
  501.                 m_bUseVertexShader = !m_bUseVertexShader;
  502.                 CheckMenuItem( GetMenu(hWnd), IDM_USEVERTEXSHADER,
  503.                                m_bUseVertexShader ? MF_CHECKED : MF_UNCHECKED );
  504.                 break;
  505.         }
  506.     }
  507.  
  508.     // Pass remaining messages to default handler
  509.     return CD3DApplication::MsgProc( hWnd, uMsg, wParam, lParam );
  510. }
  511.  
  512.  
  513.  
  514.