home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Game Programming for Teens / VBGPFT.cdr / DirectX8 / dx8a_sdk.exe / samples / multimedia / direct3d / pick / pick.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-11-04  |  17.8 KB  |  541 lines

  1. //-----------------------------------------------------------------------------
  2. // File: Pick.cpp
  3. //
  4. // Desc: Example code showing how to do picking in D3D.
  5. //
  6. // Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #define STRICT
  9. #include <math.h>
  10. #include <stdio.h>
  11. #include <D3DX8.h>
  12. #include "D3DApp.h"
  13. #include "D3DFont.h"
  14. #include "D3DFile.h"
  15. #include "D3DUtil.h"
  16. #include "DXUtil.h"
  17.  
  18.  
  19. struct D3DVERTEX
  20. {
  21.     D3DXVECTOR3 p;
  22.     D3DXVECTOR3 n;
  23.     FLOAT       tu, tv;
  24. };
  25.  
  26. #define D3DFVF_VERTEX (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1)
  27.  
  28.  
  29.  
  30.  
  31. //-----------------------------------------------------------------------------
  32. // Name: class CMyD3DApplication
  33. // Desc: Application class. The base class (CD3DApplication) provides the 
  34. //       generic functionality needed in all Direct3D samples. CMyD3DApplication 
  35. //       adds functionality specific to this sample program.
  36. //-----------------------------------------------------------------------------
  37. class CMyD3DApplication : public CD3DApplication
  38. {
  39.     CD3DFont*   m_pFont;                      // Font for drawing text
  40.     CD3DMesh*   m_pObject;                    // Object to render
  41.     DWORD       m_dwNumPickedTriangles;       // Picking information
  42.     FLOAT       m_fPickT, m_fPickU, m_fPickV; // Picking results
  43.     LPDIRECT3DVERTEXBUFFER8 m_pVB;            // VB for picked triangles
  44.     LPDIRECT3DSURFACE8      m_pCursorBitmap;  // Cursor image
  45.  
  46.     // Internal member functions
  47.     HRESULT Pick();
  48.     BOOL    IntersectTriangle( const D3DXVECTOR3& orig, const D3DXVECTOR3& dir,
  49.                                D3DXVECTOR3& v0, D3DXVECTOR3& v1, D3DXVECTOR3& v2,
  50.                                FLOAT* t, FLOAT* u, FLOAT* v );
  51.     HRESULT ConfirmDevice( D3DCAPS8*, DWORD, D3DFORMAT );
  52.  
  53. protected:
  54.     HRESULT OneTimeSceneInit();
  55.     HRESULT InitDeviceObjects();
  56.     HRESULT RestoreDeviceObjects();
  57.     HRESULT InvalidateDeviceObjects();
  58.     HRESULT DeleteDeviceObjects();
  59.     HRESULT Render();
  60.     HRESULT FrameMove();
  61.     HRESULT FinalCleanup();
  62.  
  63. public:
  64.     LRESULT MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
  65.     CMyD3DApplication();
  66. };
  67.  
  68.  
  69.  
  70.  
  71. //-----------------------------------------------------------------------------
  72. // Name: WinMain()
  73. // Desc: Entry point to the program. Initializes everything, and goes into a
  74. //       message-processing loop. Idle time is used to render the scene.
  75. //-----------------------------------------------------------------------------
  76. INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
  77. {
  78.     CMyD3DApplication d3dApp;
  79.  
  80.     if( FAILED( d3dApp.Create( hInst ) ) )
  81.         return 0;
  82.  
  83.     return d3dApp.Run();
  84. }
  85.  
  86.  
  87.  
  88.  
  89. //-----------------------------------------------------------------------------
  90. // Name: CMyD3DApplication()
  91. // Desc: Application constructor. Sets attributes for the app.
  92. //-----------------------------------------------------------------------------
  93. CMyD3DApplication::CMyD3DApplication()
  94. {
  95.     m_strWindowTitle    = _T("Pick: D3D Picking Sample");
  96.     m_bUseDepthBuffer   = TRUE;
  97.     m_bShowCursorWhenFullscreen = TRUE;
  98.  
  99.     m_pFont           = new CD3DFont( _T("Arial"), 12, D3DFONT_BOLD );
  100.     m_pObject         = new CD3DMesh();
  101.     m_pCursorBitmap   = NULL;
  102.     m_pVB             = NULL;
  103. }
  104.  
  105.  
  106.  
  107.  
  108. //-----------------------------------------------------------------------------
  109. // Name: OneTimeSceneInit()
  110. // Desc: Called during initial app startup, this function performs all the
  111. //       permanent initialization.
  112. //-----------------------------------------------------------------------------
  113. HRESULT CMyD3DApplication::OneTimeSceneInit()
  114. {
  115.     return S_OK;
  116. }
  117.  
  118.  
  119.  
  120.  
  121. //-----------------------------------------------------------------------------
  122. // Name: Pick()
  123. // Desc: Checks if mouse point hits geometry
  124. //       the scene.
  125. //-----------------------------------------------------------------------------
  126. HRESULT CMyD3DApplication::Pick()
  127. {
  128.     D3DXVECTOR3 vPickRayDir;
  129.     D3DXVECTOR3 vPickRayOrig;
  130.  
  131.     m_dwNumPickedTriangles = 0L;
  132.  
  133.     // Get the pick ray from the mouse position
  134.     if( GetCapture() )
  135.     {
  136.         D3DXMATRIX matProj;
  137.         m_pd3dDevice->GetTransform( D3DTS_PROJECTION, &matProj );
  138.  
  139.         POINT ptCursor;
  140.         GetCursorPos( &ptCursor );
  141.         ScreenToClient( m_hWnd, &ptCursor );
  142.  
  143.         // Compute the vector of the pick ray in screen space
  144.         D3DXVECTOR3 v;
  145.         v.x =  ( ( ( 2.0f * ptCursor.x ) / m_d3dsdBackBuffer.Width  ) - 1 ) / matProj._11;
  146.         v.y = -( ( ( 2.0f * ptCursor.y ) / m_d3dsdBackBuffer.Height ) - 1 ) / matProj._22;
  147.         v.z =  1.0f;
  148.  
  149.         // Get the inverse view matrix
  150.         D3DXMATRIX matView, m;
  151.         m_pd3dDevice->GetTransform( D3DTS_VIEW, &matView );
  152.         D3DXMatrixInverse( &m, NULL, &matView );
  153.  
  154.         // Transform the screen space pick ray into 3D space
  155.         vPickRayDir.x  = v.x*m._11 + v.y*m._21 + v.z*m._31;
  156.         vPickRayDir.y  = v.x*m._12 + v.y*m._22 + v.z*m._32;
  157.         vPickRayDir.z  = v.x*m._13 + v.y*m._23 + v.z*m._33;
  158.         vPickRayOrig.x = m._41;
  159.         vPickRayOrig.y = m._42;
  160.         vPickRayOrig.z = m._43;
  161.     }
  162.  
  163.     // Get the picked triangle
  164.     if( GetCapture() )
  165.     {
  166.         LPDIRECT3DVERTEXBUFFER8 pVB;
  167.         LPDIRECT3DINDEXBUFFER8 pIB;
  168.         m_pObject->GetLocalMesh()->GetVertexBuffer( &pVB );
  169.         m_pObject->GetLocalMesh()->GetIndexBuffer( &pIB );
  170.  
  171.         struct VERTEX { D3DXVECTOR3 p, n; FLOAT tu; FLOAT tv; };
  172.         DWORD dwFVF = m_pObject->GetLocalMesh()->GetFVF();
  173.  
  174.         WORD*      pIndices;
  175.         VERTEX*    pVertices;
  176.         DWORD      dwNumFaces = m_pObject->GetLocalMesh()->GetNumFaces();
  177.         pIB->Lock( 0,0,(BYTE**)&pIndices, 0 );
  178.         pVB->Lock( 0,0,(BYTE**)&pVertices, 0 );
  179.  
  180.         for( DWORD i=0; i<dwNumFaces; i++ )
  181.         {
  182.             D3DXVECTOR3 v0 = pVertices[pIndices[3*i+0]].p;
  183.             D3DXVECTOR3 v1 = pVertices[pIndices[3*i+1]].p;
  184.             D3DXVECTOR3 v2 = pVertices[pIndices[3*i+2]].p;
  185.  
  186.             // Check if the pick ray passes through this point
  187.             if( IntersectTriangle( vPickRayOrig, vPickRayDir, v0, v1, v2,
  188.                                    &m_fPickT, &m_fPickU, &m_fPickV ) )
  189.             {
  190.                 VERTEX* v;
  191.                 m_pVB->Lock( 0, 0, (BYTE**)&v, 0 );
  192.                 v[3*m_dwNumPickedTriangles+0] = pVertices[pIndices[3*i+0]];
  193.                 v[3*m_dwNumPickedTriangles+1] = pVertices[pIndices[3*i+1]];
  194.                 v[3*m_dwNumPickedTriangles+2] = pVertices[pIndices[3*i+2]];
  195.                 m_pVB->Unlock();
  196.  
  197.                 m_dwNumPickedTriangles++;
  198.             }
  199.         }
  200.  
  201.         pVB->Unlock();
  202.         pIB->Unlock();
  203.  
  204.         pVB->Release();
  205.         pIB->Release();
  206.     }
  207.  
  208.     return S_OK;
  209. }
  210.  
  211.  
  212.  
  213.  
  214. //-----------------------------------------------------------------------------
  215. // Name: FrameMove()
  216. // Desc: Called once per frame, the call is the entry point for animating
  217. //       the scene.
  218. //-----------------------------------------------------------------------------
  219. HRESULT CMyD3DApplication::FrameMove()
  220. {
  221.     // Rotate the camera about the y-axis
  222.     D3DXVECTOR3 vFromPt   = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
  223.     D3DXVECTOR3 vLookatPt = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
  224.     D3DXVECTOR3 vUpVec    = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
  225.     vFromPt.x = -cosf(m_fTime/3.0f) * 4.0f;
  226.     vFromPt.y = 1.0f;
  227.     vFromPt.z =  sinf(m_fTime/3.0f) * 4.0f;
  228.  
  229.     D3DXMATRIX matView;
  230.     D3DXMatrixLookAtLH( &matView, &vFromPt, &vLookatPt, &vUpVec );
  231.     m_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
  232.  
  233.     return S_OK;
  234. }
  235.  
  236.  
  237.  
  238.  
  239. //-----------------------------------------------------------------------------
  240. // Name: Render()
  241. // Desc: Called once per frame, the call is the entry point for 3d
  242. //       rendering. This function sets up render states, clears the
  243. //       viewport, and renders the scene.
  244. //-----------------------------------------------------------------------------
  245. HRESULT CMyD3DApplication::Render()
  246. {
  247.     // Set up the cursor
  248.     POINT ptCursor;
  249.     GetCursorPos( &ptCursor );
  250.     ScreenToClient( m_hWnd, &ptCursor );
  251.     m_pd3dDevice->SetCursorPosition( ptCursor.x, ptCursor.y, 0L );
  252.  
  253.     // Check for picked triangles
  254.     Pick();
  255.  
  256.     // Clear the viewport
  257.     m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
  258.                          0x000000ff, 1.0f, 0L );
  259.  
  260.     // Begin the scene
  261.     if( SUCCEEDED( m_pd3dDevice->BeginScene() ) )
  262.     {
  263.         // Set render mode to lit, solid triangles
  264.         m_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
  265.         m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
  266.  
  267.         // If a triangle is picked, draw it
  268.         if( m_dwNumPickedTriangles )
  269.         {
  270.             // Draw the picked triangle
  271.             m_pd3dDevice->SetVertexShader( D3DFVF_VERTEX );
  272.             m_pd3dDevice->SetStreamSource( 0, m_pVB, sizeof(D3DVERTEX) );
  273.             m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST,
  274.                                          0, m_dwNumPickedTriangles );
  275.  
  276.             // Set render mode to unlit, wireframe triangles
  277.             m_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
  278.             m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
  279.         }
  280.  
  281.         m_pObject->Render( m_pd3dDevice );
  282.  
  283.         // Output statistics
  284.         m_pFont->DrawText( 2,  0, D3DCOLOR_ARGB(255,255,255,0), m_strFrameStats );
  285.         m_pFont->DrawText( 2, 20, D3DCOLOR_ARGB(255,255,255,0), m_strDeviceStats );
  286.  
  287.         // Output text
  288.         CHAR strBuffer[90];
  289.         if( m_dwNumPickedTriangles )
  290.             sprintf( strBuffer, _T("t=%2.2f, u=%2.2f, v=%2.2f"),
  291.                                 m_fPickT, m_fPickU, m_fPickV );
  292.         else
  293.             sprintf( strBuffer, _T("Use mouse to pick a polygon") );
  294.         m_pFont->DrawText( 2, 40, D3DCOLOR_ARGB(255,255,255,0), strBuffer );
  295.  
  296.         // End the scene.
  297.         m_pd3dDevice->EndScene();
  298.     }
  299.  
  300.     return S_OK;
  301. }
  302.  
  303.  
  304.  
  305.  
  306. //-----------------------------------------------------------------------------
  307. // Name: InitDeviceObjects()
  308. // Desc: Initialize scene objects.
  309. //-----------------------------------------------------------------------------
  310. HRESULT CMyD3DApplication::InitDeviceObjects()
  311. {
  312.     // Restore the font
  313.     m_pFont->InitDeviceObjects( m_pd3dDevice );
  314.  
  315.     if( FAILED( m_pObject->Create( m_pd3dDevice, _T("Tiger.x") ) ) )
  316.         return D3DAPPERR_MEDIANOTFOUND;
  317.     m_pObject->SetFVF( m_pd3dDevice, D3DFVF_VERTEX );
  318.  
  319.     // Create and fill the vertex buffer
  320.     DWORD dwNumVertices = m_pObject->GetSysMemMesh()->GetNumVertices();
  321.     if( FAILED( m_pd3dDevice->CreateVertexBuffer( dwNumVertices*sizeof(D3DVERTEX),
  322.                                                   D3DUSAGE_WRITEONLY, D3DFVF_VERTEX,
  323.                                                   D3DPOOL_MANAGED, &m_pVB ) ) )
  324.         return E_FAIL;
  325.  
  326.     return S_OK;
  327. }
  328.  
  329.  
  330.  
  331.  
  332. //-----------------------------------------------------------------------------
  333. // Name: RestoreDeviceObjects()
  334. // Desc: Initialize scene objects.
  335. //-----------------------------------------------------------------------------
  336. HRESULT CMyD3DApplication::RestoreDeviceObjects()
  337. {
  338.     m_pFont->RestoreDeviceObjects();
  339.  
  340.     // Restore device-memory objects for the mesh
  341.     m_pObject->RestoreDeviceObjects( m_pd3dDevice );
  342.  
  343.     // Set up the textures
  344.     m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  345.     m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
  346.     m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
  347.     m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
  348.     m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
  349.  
  350.     // Set miscellaneous render states
  351.     m_pd3dDevice->SetRenderState( D3DRS_DITHERENABLE,   FALSE );
  352.     m_pd3dDevice->SetRenderState( D3DRS_SPECULARENABLE, FALSE );
  353.     m_pd3dDevice->SetRenderState( D3DRS_ZENABLE,        TRUE );
  354.     m_pd3dDevice->SetRenderState( D3DRS_AMBIENT,        0x00444444 );
  355.  
  356.     // Set the world matrix
  357.     D3DXMATRIX matIdentity;
  358.     D3DXMatrixIdentity( &matIdentity );
  359.     m_pd3dDevice->SetTransform( D3DTS_WORLD,  &matIdentity );
  360.  
  361.     // Set the projection matrix
  362.     D3DXMATRIX matProj;
  363.     FLOAT fAspect = ((FLOAT)m_d3dsdBackBuffer.Width) / m_d3dsdBackBuffer.Height;
  364.     D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, fAspect, 1.0f, 100.0f );
  365.     m_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
  366.  
  367.     // Setup a material
  368.     D3DMATERIAL8 mtrl;
  369.     D3DUtil_InitMaterial( mtrl, 1.0f, 1.0f, 1.0f, 1.0f );
  370.     m_pd3dDevice->SetMaterial( &mtrl );
  371.  
  372.     // Set up lighting states
  373.     D3DLIGHT8 light;
  374.     D3DUtil_InitLight( light, D3DLIGHT_DIRECTIONAL, 0.1f, -1.0f, 0.1f );
  375.     m_pd3dDevice->SetLight( 0, &light );
  376.     m_pd3dDevice->LightEnable( 0, TRUE );
  377.     m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
  378.  
  379.     return S_OK;
  380. }
  381.  
  382.  
  383.  
  384.  
  385. //-----------------------------------------------------------------------------
  386. // Name: InvalidateDeviceObjects()
  387. // Desc:
  388. //-----------------------------------------------------------------------------
  389. HRESULT CMyD3DApplication::InvalidateDeviceObjects()
  390. {
  391.     m_pFont->InvalidateDeviceObjects();
  392.     m_pObject->InvalidateDeviceObjects();
  393.  
  394.     SAFE_RELEASE( m_pCursorBitmap );
  395.  
  396.     return S_OK;
  397. }
  398.  
  399.  
  400.  
  401.  
  402. //-----------------------------------------------------------------------------
  403. // Name: DeleteDeviceObjects()
  404. // Desc: Called when the app is exiting, or the device is being changed,
  405. //       this function deletes any device dependent objects.
  406. //-----------------------------------------------------------------------------
  407. HRESULT CMyD3DApplication::DeleteDeviceObjects()
  408. {
  409.     m_pFont->DeleteDeviceObjects();
  410.     m_pObject->Destroy();
  411.  
  412.     SAFE_RELEASE( m_pVB );
  413.     SAFE_RELEASE( m_pCursorBitmap );
  414.  
  415.     return S_OK;
  416. }
  417.  
  418.  
  419.  
  420.  
  421. //-----------------------------------------------------------------------------
  422. // Name: FinalCleanup()
  423. // Desc: Called before the app exits, this function gives the app the chance
  424. //       to cleanup after itself.
  425. //-----------------------------------------------------------------------------
  426. HRESULT CMyD3DApplication::FinalCleanup()
  427. {
  428.     SAFE_DELETE( m_pFont );
  429.  
  430.     return S_OK;
  431. }
  432.  
  433.  
  434.  
  435.  
  436. //-----------------------------------------------------------------------------
  437. // Name: ConfirmDevice()
  438. // Desc: Called during device intialization, this code checks the device
  439. //       for some minimum set of capabilities
  440. //-----------------------------------------------------------------------------
  441. HRESULT CMyD3DApplication::ConfirmDevice( D3DCAPS8* pCaps, DWORD dwBehavior,
  442.                                           D3DFORMAT Format )
  443. {
  444.     if( dwBehavior & D3DCREATE_PUREDEVICE )
  445.         return E_FAIL; // GetTransform doesn't work on PUREDEVICE
  446.  
  447.     // If this is a TnL device, make sure it supports directional lights
  448.     if( (dwBehavior & D3DCREATE_HARDWARE_VERTEXPROCESSING ) ||
  449.         (dwBehavior & D3DCREATE_MIXED_VERTEXPROCESSING ) )
  450.     {
  451.         if( !(pCaps->VertexProcessingCaps & D3DVTXPCAPS_DIRECTIONALLIGHTS ) )
  452.             return E_FAIL;
  453.     }
  454.  
  455.     return S_OK;
  456. }
  457.  
  458.  
  459.  
  460.  
  461. //-----------------------------------------------------------------------------
  462. // Name: MsgProc()
  463. // Desc: Overrrides the main WndProc, so the sample can do custom message
  464. //       handling (e.g. processing mouse, keyboard, or menu commands).
  465. //-----------------------------------------------------------------------------
  466. LRESULT CMyD3DApplication::MsgProc( HWND hWnd, UINT msg, WPARAM wParam,
  467.                                     LPARAM lParam )
  468. {
  469.     switch( msg )
  470.     {
  471.         case WM_LBUTTONDOWN:
  472.             // User pressed left mouse button
  473.             SetCapture( hWnd );
  474.             break;
  475.  
  476.         case WM_LBUTTONUP:
  477.             // The user released the left mouse button
  478.             ReleaseCapture();
  479.             break;
  480.     }
  481.  
  482.     return CD3DApplication::MsgProc( hWnd, msg, wParam, lParam );
  483. }
  484.  
  485.  
  486.  
  487.  
  488. //-----------------------------------------------------------------------------
  489. // Name: IntersectTriangle()
  490. // Desc: Given a ray origin (orig) and direction (dir), and three vertices of
  491. //       of a triangle, this function returns TRUE and the interpolated texture
  492. //       coordinates if the ray intersects the triangle
  493. //-----------------------------------------------------------------------------
  494. BOOL CMyD3DApplication::IntersectTriangle( const D3DXVECTOR3& orig,
  495.                                        const D3DXVECTOR3& dir, D3DXVECTOR3& v0,
  496.                                        D3DXVECTOR3& v1, D3DXVECTOR3& v2,
  497.                                        FLOAT* t, FLOAT* u, FLOAT* v )
  498. {
  499.     // Find vectors for two edges sharing vert0
  500.     D3DXVECTOR3 edge1 = v1 - v0;
  501.     D3DXVECTOR3 edge2 = v2 - v0;
  502.  
  503.     // Begin calculating determinant - also used to calculate U parameter
  504.     D3DXVECTOR3 pvec;
  505.     D3DXVec3Cross( &pvec, &dir, &edge2 );
  506.  
  507.     // If determinant is near zero, ray lies in plane of triangle
  508.     FLOAT det = D3DXVec3Dot( &edge1, &pvec );
  509.     if( det < 0.0001f )
  510.         return FALSE;
  511.  
  512.     // Calculate distance from vert0 to ray origin
  513.     D3DXVECTOR3 tvec = orig - v0;
  514.  
  515.     // Calculate U parameter and test bounds
  516.     *u = D3DXVec3Dot( &tvec, &pvec );
  517.     if( *u < 0.0f || *u > det )
  518.         return FALSE;
  519.  
  520.     // Prepare to test V parameter
  521.     D3DXVECTOR3 qvec;
  522.     D3DXVec3Cross( &qvec, &tvec, &edge1 );
  523.  
  524.     // Calculate V parameter and test bounds
  525.     *v = D3DXVec3Dot( &dir, &qvec );
  526.     if( *v < 0.0f || *u + *v > det )
  527.         return FALSE;
  528.  
  529.     // Calculate t, scale parameters, ray intersects triangle
  530.     *t = D3DXVec3Dot( &edge2, &qvec );
  531.     FLOAT fInvDet = 1.0f / det;
  532.     *t *= fInvDet;
  533.     *u *= fInvDet;
  534.     *v *= fInvDet;
  535.  
  536.     return TRUE;
  537. }
  538.  
  539.  
  540.  
  541.