TerrainEngine.cpp
上传用户:sycq158
上传日期:2008-10-22
资源大小:15361k
文件大小:56k
源码类别:

游戏

开发平台:

Visual C++

  1. //-----------------------------------------------------------------------------
  2. // File: TerrianEngine.cpp
  3. //
  4. // Copyright (C) Microsoft Corporation. All Rights Reserved.
  5. //-----------------------------------------------------------------------------
  6. #include "stdafx.h"
  7. //-----------------------------------------------------------------------------
  8. // Name: CTerrainEngine
  9. // Desc: 
  10. //-----------------------------------------------------------------------------
  11. CTerrainEngine::CTerrainEngine( CMyApplication* pApp )
  12. {
  13.     m_pApp          = pApp;
  14.     m_MeshArray     = NULL;
  15.     m_dwObjectCount = 0;
  16.     m_dwEnemyCount  = 0;
  17. }
  18. //-----------------------------------------------------------------------------
  19. // Name: ~CTerrainEngine
  20. // Desc: 
  21. //-----------------------------------------------------------------------------
  22. CTerrainEngine::~CTerrainEngine()
  23. {
  24.     FinalCleanup();
  25. }
  26. //-----------------------------------------------------------------------------
  27. // Name: OneTimeSceneInit
  28. // Desc: 
  29. //-----------------------------------------------------------------------------
  30. HRESULT CTerrainEngine::OneTimeSceneInit( CThemeStyle* pTheme )
  31. {
  32.     HRESULT hr;
  33.     int iX, iZ;
  34.     m_pTheme = pTheme;
  35.     m_dwWorldWidth  = g_Profile.nWorldWidth;
  36.     m_dwWorldHeight = g_Profile.nWorldHeight;
  37.     m_MeshArray = new MESH_NODE[ m_dwWorldWidth * m_dwWorldHeight ];
  38.     if( NULL == m_MeshArray )
  39.         return E_OUTOFMEMORY;
  40.     ZeroMemory( m_MeshArray, m_dwWorldWidth * m_dwWorldHeight * sizeof(MESH_NODE) );
  41.     for( iX = 0; iX<m_dwWorldWidth; iX++ )
  42.     {
  43.         for( iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  44.         {
  45.             m_MeshArray[iX + m_dwWorldWidth*iZ].pMesh = new CTerrainMesh( this );
  46.             if( m_MeshArray[iX + m_dwWorldWidth*iZ].pMesh == NULL )
  47.                 return E_OUTOFMEMORY;
  48.         }
  49.     }
  50.     for( iX = 0; iX<m_dwWorldWidth; iX++ )
  51.     {
  52.         for( iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  53.         {
  54.             int iNorth  = (iZ+1) % m_dwWorldHeight;
  55.             int iEast   = (iX+1) % m_dwWorldWidth;
  56.             CTerrainMesh* pNorth      = m_MeshArray[   iX+ m_dwWorldWidth*iNorth].pMesh;
  57.             CTerrainMesh* pEast       = m_MeshArray[iEast+ m_dwWorldWidth*iZ].pMesh;
  58.             CTerrainMesh* pNorthEast  = m_MeshArray[iEast+ m_dwWorldWidth*iNorth].pMesh;
  59.             CZoneStyleParameter* pLandType = &m_pTheme->aZoneStyles[ rand() % m_pTheme->nNumZoneStyles ];
  60.             
  61.             hr = m_MeshArray[iX + m_dwWorldWidth*iZ].pMesh->OneTimeSceneInit( pLandType,
  62.                                                               (float)ZONE_WIDTH*iX, (float)ZONE_HEIGHT*iZ,
  63.                                                               pNorth, pEast, pNorthEast );
  64.             if( FAILED(hr) )
  65.                 return hr;
  66.         }
  67.     }
  68.     return S_OK;
  69. }
  70. //-----------------------------------------------------------------------------
  71. // Name: InitDeviceObjects
  72. // Desc: 
  73. //-----------------------------------------------------------------------------
  74. HRESULT CTerrainEngine::InitDeviceObjects( D3DFORMAT fmtTexture )
  75. {
  76.     m_fmtTexture = fmtTexture;
  77.     HRESULT hr;
  78.     int iX, iZ;
  79.     for( iX = 0; iX<m_dwWorldWidth; iX++ )
  80.     {
  81.         for( iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  82.         {
  83.             hr = m_MeshArray[iX + m_dwWorldWidth*iZ].pMesh->InitDeviceObjects( m_fmtTexture );
  84.             if( FAILED(hr) )
  85.                 return hr;
  86.         }
  87.     }
  88.     for( iX = 0; iX<m_dwWorldWidth; iX++ )
  89.     {
  90.         for( iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  91.         {
  92.             hr = m_MeshArray[iX + m_dwWorldWidth*iZ].pMesh->SmoothMap();
  93.             if( FAILED(hr) )
  94.                 return hr;
  95.         }
  96.     }
  97.     for( iX = 0; iX<m_dwWorldWidth; iX++ )
  98.     {
  99.         for( iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  100.         {
  101.             // The smooth processes uses the bordering meshs to smooth the mesh.
  102.             // So we need lock down the results of the first step in a 2nd pass
  103.             // otherwise while smoothing a mesh the border meshs will either be already
  104.             // smoothed or not yet smoothed (depending if that mesh was already processed)
  105.             // so it creates an gfx inconsistancy unless use all unsmoothed meshes 
  106.             hr = m_MeshArray[iX + m_dwWorldWidth*iZ].pMesh->FinalizeSmooth();
  107.             if( FAILED(hr) )
  108.                 return hr;
  109.         }
  110.     }  
  111.     return S_OK;
  112. }
  113. //-----------------------------------------------------------------------------
  114. // Name: RestoreDeviceObjects
  115. // Desc: 
  116. //-----------------------------------------------------------------------------
  117. HRESULT CTerrainEngine::RestoreDeviceObjects( HWND hWndMain )
  118. {
  119.     HRESULT hr;
  120.     int iX, iZ;
  121.     for( iX = 0; iX<m_dwWorldWidth; iX++ )
  122.     {
  123.         for( iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  124.         {
  125.             MESH_NODE* pMeshNode = &m_MeshArray[iX + m_dwWorldWidth*iZ];
  126.             CDisplayObject* pObject = pMeshNode->pDisplayList;
  127.             while( pObject )
  128.             {
  129.                 pObject->RestoreDeviceObjects();
  130.                 pObject = pObject->m_pNext;
  131.             }
  132.             hr = pMeshNode->pMesh->RestoreDeviceObjects( hWndMain );
  133.             if( FAILED(hr) )
  134.                 return hr;
  135.         }
  136.     }
  137.     for( iX = 0; iX<m_dwWorldWidth; iX++ )
  138.     {
  139.         for( iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  140.         {
  141.             hr = m_MeshArray[iX + m_dwWorldWidth*iZ].pMesh->GenerateMipMaps();
  142.             if( FAILED(hr) )
  143.                 return hr;
  144.         }
  145.     }
  146.     if( m_dwWorldWidth != 1 && m_dwWorldHeight != 1 ) // Can't correct edge if its 1xN or Nx1
  147.     {
  148.         for( iX = 0; iX<m_dwWorldWidth; iX++ )
  149.         {
  150.             for( iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  151.             {
  152.                 hr = m_MeshArray[iX + m_dwWorldWidth*iZ].pMesh->CorrectTextureEdge();
  153.                 if( FAILED(hr) )
  154.                     return hr;
  155.             }
  156.         }
  157.     }
  158.     return S_OK;
  159. }
  160. //-----------------------------------------------------------------------------
  161. // Name: AddDisplayObject
  162. // Desc: 
  163. //-----------------------------------------------------------------------------
  164. HRESULT CTerrainEngine::AddDisplayObject( CDisplayObject* pObject )
  165. {
  166.     if( NULL == pObject )
  167.         return E_INVALIDARG;
  168.     int iX, iZ;
  169.     GetZoneFromPt( pObject->m_vPos.x, pObject->m_vPos.z, &iX, &iZ );
  170.     MESH_NODE* pMeshNode = &m_MeshArray[iX + m_dwWorldWidth*iZ];
  171.     if( pMeshNode->pDisplayList )
  172.     {
  173.         pObject->m_pNext = pMeshNode->pDisplayList;
  174.         pObject->m_pPrev = NULL;
  175.         pMeshNode->pDisplayList->m_pPrev = pObject;
  176.         pMeshNode->pDisplayList = pObject;
  177.     }
  178.     else
  179.     {
  180.         pMeshNode->pDisplayList = pObject;
  181.         pObject->m_pNext = NULL;
  182.         pObject->m_pPrev = NULL;
  183.     }
  184.     pObject->m_pMeshNode = pMeshNode;
  185.     if( pObject->m_ObjectType == OBJ_ENEMY )
  186.         m_dwEnemyCount++;
  187.     m_dwObjectCount++;
  188.     return S_OK;
  189. }
  190. //-----------------------------------------------------------------------------
  191. // Name: RemoveDisplayObject
  192. // Desc: 
  193. //-----------------------------------------------------------------------------
  194. HRESULT CTerrainEngine::RemoveDisplayObject( CDisplayObject* pObject )
  195. {
  196.     if( NULL == pObject )
  197.         return E_INVALIDARG;
  198.     MESH_NODE* pMeshNode = pObject->m_pMeshNode;
  199.     if( pMeshNode )
  200.     {
  201.         if( pMeshNode->pDisplayList == pObject )
  202.         {
  203.             pMeshNode->pDisplayList = pObject->m_pNext;
  204.             if( pMeshNode->pDisplayList )
  205.                 pMeshNode->pDisplayList->m_pPrev = NULL;
  206.         }
  207.         else
  208.         {
  209.             if( pObject->m_pPrev )
  210.                 pObject->m_pPrev->m_pNext = pObject->m_pNext;
  211.             if( pObject->m_pNext )
  212.                 pObject->m_pNext->m_pPrev = pObject->m_pPrev;
  213.         }
  214.     }
  215.     pObject->m_pMeshNode = NULL;
  216.     pObject->m_pNext = NULL;
  217.     pObject->m_pPrev = NULL;
  218.     if( pObject->m_ObjectType == OBJ_ENEMY )
  219.         m_dwEnemyCount--;
  220.     m_dwObjectCount--;
  221.     return S_OK;
  222. }
  223. //-----------------------------------------------------------------------------
  224. // Name: ClearStrayObjects
  225. // Desc: 
  226. //-----------------------------------------------------------------------------
  227. HRESULT CTerrainEngine::ClearStrayObjects()
  228. {
  229.     CPlayerShip* pPlayerShip = g_pApp->GetPlayerShip();
  230.     for( int iX = 0; iX<m_dwWorldWidth; iX++ )
  231.     {
  232.         for( int iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  233.         {
  234.             MESH_NODE* pMeshNode = &m_MeshArray[iX + m_dwWorldWidth*iZ];
  235.             CDisplayObject* pObject = pMeshNode->pDisplayList;
  236.             CDisplayObject* pObjectNext;
  237.             while( pObject )
  238.             {
  239.                 pObjectNext = pObject->m_pNext;
  240.                 if( pObject != pPlayerShip )
  241.                 {
  242.                     RemoveDisplayObject( pObject );
  243.                     pObject->InvalidateDeviceObjects();
  244.                     pObject->DeleteDeviceObjects();
  245.                     pObject->FinalCleanup();
  246.                     SAFE_DELETE( pObject );
  247.                 }
  248.                 pObject = pObjectNext;
  249.             }
  250.         }
  251.     }
  252.     return S_OK;
  253. }
  254. //-----------------------------------------------------------------------------
  255. // Name: FrameMove
  256. // Desc: 
  257. //-----------------------------------------------------------------------------
  258. HRESULT CTerrainEngine::FrameMove( const float fElapsedTime )
  259. {
  260.     HRESULT hr;
  261.     int iX;
  262.     for( iX = 0; iX<m_dwWorldWidth; iX++ )
  263.     {
  264.         for( int iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  265.         {
  266.             MESH_NODE* pMeshNode = &m_MeshArray[iX + m_dwWorldWidth*iZ];
  267.             CDisplayObject* pObject = pMeshNode->pDisplayList;
  268.             while( pObject )
  269.             {
  270.                 if( pObject->m_bActive )
  271.                 {
  272.                     if( !g_pApp->m_bPaused || 
  273.                         (g_pApp->m_bPaused && g_pApp->m_bDebugMode && pObject == g_pApp->GetPlayerShip()) )
  274.                     {
  275.                         pObject->FrameMove( fElapsedTime );
  276.                         HandleNearbyObjects( fElapsedTime, pObject, iX, iZ );
  277.                     }
  278.                 }
  279.                 pObject = pObject->m_pNext;
  280.             }
  281.             if( pMeshNode->pMesh )
  282.             {
  283.                 // The terrian mesh itself could animate
  284.                 if( FAILED( hr = pMeshNode->pMesh->FrameMove( fElapsedTime ) ) )
  285.                     return hr;
  286.             }
  287.         }
  288.     }
  289.     // Make sure all the objects are in the right zone after moving around
  290.     for( iX = 0; iX<m_dwWorldWidth; iX++ )
  291.     {
  292.         for( int iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  293.         {
  294.             MESH_NODE* pMeshNode = &m_MeshArray[iX + m_dwWorldWidth*iZ];
  295.             int iCurZoneX, iCurZoneZ;
  296.             CDisplayObject* pObject = pMeshNode->pDisplayList;
  297.             CDisplayObject* pObjectNext;
  298.             while( pObject )
  299.             {
  300.                 pObjectNext = pObject->m_pNext;
  301.                 if( pObject->m_bActive )
  302.                 {
  303.                     pObject->FrameMoveFinalize( fElapsedTime );
  304.                     GetZoneFromPt( pObject->m_vPos.x, pObject->m_vPos.z, 
  305.                                 &iCurZoneX, &iCurZoneZ );
  306.                     if( iCurZoneX != iX ||
  307.                         iCurZoneZ != iZ )
  308.                     {
  309.                         RemoveDisplayObject( pObject );
  310.                         AddDisplayObject( pObject );
  311.                     }
  312.                 }
  313.                 else
  314.                 {
  315.                     RemoveDisplayObject( pObject );
  316.                     pObject->InvalidateDeviceObjects();
  317.                     pObject->DeleteDeviceObjects();
  318.                     pObject->FinalCleanup();
  319.                     SAFE_DELETE( pObject );
  320.                 }
  321.                 pObject = pObjectNext;
  322.             }
  323.         }
  324.     }
  325.     return S_OK;
  326. }
  327. //-----------------------------------------------------------------------------
  328. // Name: HandleNearbyObjects
  329. // Desc: 
  330. //-----------------------------------------------------------------------------
  331. HRESULT CTerrainEngine::HandleNearbyObjects( const float fElapsedTime, CDisplayObject* pObject1, 
  332.                                              int iX, int iZ )
  333. {
  334.     MESH_NODE* pMeshNode = &m_MeshArray[iX + m_dwWorldWidth*iZ];
  335.     CDisplayObject* pObject2 = pMeshNode->pDisplayList;
  336.     while( pObject2 )
  337.     {
  338.         // Skip object2 if its that same as object1
  339.         if( pObject2 != pObject1 )
  340.         {
  341.             if( pObject2->m_bActive )
  342.                 pObject1->HandleNearbyObject( fElapsedTime, pObject2 );
  343.         }
  344.         pObject2 = pObject2->m_pNext;
  345.     }
  346.     return S_OK;
  347. }
  348. //-----------------------------------------------------------------------------
  349. // Name: DrawDotOnBuffer
  350. // Desc: 
  351. //-----------------------------------------------------------------------------
  352. void CTerrainEngine::DrawDotOnBuffer( int nCenterX, int nCenterY, D3DLOCKED_RECT* plockedRect, D3DSURFACE_DESC* pdesc, WORD wColor )
  353. {
  354.     int nSize = 5;
  355.     int nX = nCenterX;
  356.     int nY = nCenterY;
  357.     for( nY = nCenterY-nSize; nY < nCenterY+nSize; nY++ )
  358.     {
  359.         for( nX = nCenterX-nSize; nX < nCenterX+nSize; nX++ )
  360.         {
  361.             if( nX >= 0 && nY >= 0 &&
  362.                 nX < (int) pdesc->Width &&
  363.                 nY < (int) pdesc->Height )
  364.             {
  365.                 WORD* pBuffer = (WORD*) ((BYTE*)plockedRect->pBits + plockedRect->Pitch * nY + nX*sizeof(WORD)); 
  366.                 *pBuffer = wColor;
  367.             }
  368.         }
  369.     }
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Name: RenderRadar
  373. // Desc: 
  374. //-----------------------------------------------------------------------------
  375. HRESULT CTerrainEngine::RenderRadar( LPDIRECT3DTEXTURE9 pRadarTexture, LPDIRECT3DTEXTURE9 pTempRadarTexture )
  376. {
  377.     HRESULT hr;
  378.     
  379.     if( g_bDebugIsZoneRenderFroze )
  380.         return S_OK; // debugging: don't render radar if camera pos froze
  381.     D3DSURFACE_DESC desc;
  382.     pTempRadarTexture->GetLevelDesc( 0, &desc ); 
  383.     D3DLOCKED_RECT lockedRect;
  384.     hr = pTempRadarTexture->LockRect( 0, &lockedRect, NULL, 0 );
  385.     if( SUCCEEDED(hr) )
  386.     {
  387.         memset( lockedRect.pBits, 0x00, 256*lockedRect.Pitch );
  388.         float fObjectX;
  389.         float fObjectY;
  390.         float fPlayerX;
  391.         float fPlayerY;
  392.         float fX;
  393.         float fY;
  394.         float fNewX;
  395.         float fNewY;
  396.         float fR;
  397.         float fAngle;
  398.         float iCopyX;
  399.         float iCopyY;
  400.         int nX = 0;
  401.         int nY = 0;
  402.         int iPlayerZoneX;
  403.         int iPlayerZoneZ;
  404.         int iPlayerWorldX;
  405.         int iPlayerWorldZ;
  406.         int iObjectZoneX;
  407.         int iObjectZoneZ;
  408.         int iObjectWorldX;
  409.         int iObjectWorldZ;
  410.         CPlayerShip* pPlayerShip = g_pApp->GetPlayerShip();
  411.         g_pTerrain->GetZoneFromPt( pPlayerShip->m_vPos.x, pPlayerShip->m_vPos.z, &iPlayerZoneX, &iPlayerZoneZ );
  412.         g_pTerrain->GetWorldFromPt( pPlayerShip->m_vPos.x, pPlayerShip->m_vPos.z, &iPlayerWorldX, &iPlayerWorldZ );
  413.         D3DXVECTOR3 vAheadLocal = D3DXVECTOR3(0.0,0.0f,1.0f);
  414.         D3DXVECTOR3 vAheadWorld;
  415.         D3DXVec3TransformNormal( &vAheadWorld, &vAheadLocal, &pPlayerShip->m_pSource->m_mOrientation ); 
  416.         float fAheadAngle = atan2f( vAheadWorld.x, vAheadWorld.z );
  417.         // Loop through every mesh in world
  418.         for( int iX = 0; iX<m_dwWorldWidth; iX++ )
  419.         {
  420.             for( int iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  421.             {
  422.                 MESH_NODE* pMeshNode = &m_MeshArray[iX + m_dwWorldWidth*iZ];
  423.                 CDisplayObject* pObject = pMeshNode->pDisplayList;
  424.                 // For every object in every mesh
  425.                 while( pObject )
  426.                 {
  427.                     if( pObject->m_ObjectType == OBJ_ENEMY )
  428.                     {       
  429.                         g_pTerrain->GetZoneFromPt( pObject->m_vPos.x, pObject->m_vPos.z, &iObjectZoneX, &iObjectZoneZ );
  430.                         g_pTerrain->GetWorldFromPt( pObject->m_vPos.x, pObject->m_vPos.z, &iObjectWorldX, &iObjectWorldZ );
  431.                         // Get coords of player & object between <0,0> and <1,1>
  432.                         fPlayerX = pPlayerShip->m_vPos.x / (float) (ZONE_WIDTH*g_pTerrain->m_dwWorldWidth);
  433.                         fPlayerY = pPlayerShip->m_vPos.z / (float) (ZONE_HEIGHT*g_pTerrain->m_dwWorldHeight);
  434.                         fObjectX = pObject->m_vPos.x / (float) (ZONE_WIDTH*g_pTerrain->m_dwWorldWidth);
  435.                         fObjectY = pObject->m_vPos.z / (float) (ZONE_HEIGHT*g_pTerrain->m_dwWorldHeight);
  436.                         assert( fPlayerX >= 0.0f && fPlayerX <= 1.0f );
  437.                         assert( fPlayerY >= 0.0f && fPlayerY <= 1.0f );
  438.                         assert( fObjectX >= 0.0f && fObjectX <= 1.0f );
  439.                         assert( fObjectY >= 0.0f && fObjectY <= 1.0f );
  440.                         // Calc position of object with player at <0,0>
  441.                         fX = fObjectX - fPlayerX;
  442.                         fY = fObjectY - fPlayerY;
  443.                         // Ship is now at (0,0) and object is between <-1,-1> and <1,1>
  444.                         assert( fX >= -1.0f && fX <= 1.0f );
  445.                         assert( fY >= -1.0f && fY <= 1.0f );
  446.                         // Since the world wraps, we might need to draw the same object 
  447.                         // multiple times, so find the min and loop to draw them all
  448.                         // Find the smallest fX,fY that's above -1.0f, -1.0f
  449.                         while( fX >= -1.0f && fX <= 1.0f ) 
  450.                                 fX -= 1.0f;
  451.                         while( fY >= -1.0f && fY <= 1.0f ) 
  452.                                 fY -= 1.0f;
  453.                         fX += 1.0f;
  454.                         fY += 1.0f;
  455.                         for( iCopyY=fY; iCopyY<= 1.0f; iCopyY += 1.0f )
  456.                         {
  457.                             for( iCopyX=fX; iCopyX<= 1.0f; iCopyX += 1.0f )
  458.                             {
  459.                                 // Get radius and angle of object point
  460.                                 fR = sqrtf( iCopyX*iCopyX + iCopyY*iCopyY );
  461.                                 fAngle = atan2f( iCopyY, iCopyX );
  462.                                 // Rotation the point based on the ship's angle 
  463.                                 fAngle += fAheadAngle;
  464.                                 // Figure out the new enemy pt
  465.                                 fNewX = cosf(fAngle) * fR;
  466.                                 fNewY = sinf(fAngle) * fR;
  467.                                 // New range is between -sqrt(2), sqrt(2)
  468.                                 assert( fNewX >= -1.42f && fNewX <= 1.42f );
  469.                                 assert( fNewY >= -1.42f && fNewY <= 1.42f );
  470.                                 // Make the range to between -1 & 1
  471.                                 fNewX /= 1.414213562373095048801688f;
  472.                                 fNewY /= 1.414213562373095048801688f;
  473.                                 assert( fNewX >= -1.0f && fNewX <= 1.0f );
  474.                                 assert( fNewY >= -1.0f && fNewY <= 1.0f );
  475.                                 // Flip the Y coord so it looks right on the overhead map
  476.                                 fNewY = -fNewY;
  477.                                 // Make new range between -0.5 & 0.5
  478.                                 fNewX /= 2.0f;
  479.                                 fNewY /= 2.0f;
  480.                                 assert( fNewX >= -0.5f && fNewX <= 0.5f );
  481.                                 assert( fNewY >= -0.5f && fNewY <= 0.5f );
  482.                                 // Make new range between 0 & 1
  483.                                 fNewX += 0.5f;
  484.                                 fNewY += 0.5f;
  485.                                 float fRangeX = (g_pTerrain->m_dwWorldWidth/2.0f);
  486.                                 float fRangeY = (g_pTerrain->m_dwWorldHeight/2.0f);
  487.                                 // Make new range between 0 & X
  488.                                 fNewX *= fRangeX;
  489.                                 fNewY *= fRangeY;
  490.                                 // Make new range between -X/2 & X/2
  491.                                 fNewX -= fRangeX/2.0f;
  492.                                 fNewY -= fRangeY/2.0f;
  493.                                 // Only allow dots close to ship, -0.5 & 0.5
  494.                                 if( fNewX >= -0.5f && fNewX <= 0.5f &&
  495.                                     fNewY >= -0.5f && fNewY <= 0.5f )
  496.                                 {
  497.                                     // Get radius and angle of enemy point
  498.                                     fR = sqrtf( fNewX*fNewX + fNewY*fNewY );
  499.                                     fAngle = atan2f( fNewY, fNewX );
  500.                                     float fAngleMin = -4.0f*D3DX_PI/4.0f;
  501.                                     float fAngleMax = fAngleMin+1.2f*D3DX_PI/4.0f;
  502.                                     // Limit the dots to a radius, and make a blind spot between fAngleMin & fAngleMax
  503.                                     if( fR < 0.45f && 
  504.                                         (!(fAngle > fAngleMin && fAngle < fAngleMax) || fR < 0.12f) )
  505.                                     {
  506.                                         // Make new range between 0 & 1
  507.                                         fNewX += 0.5f;
  508.                                         fNewY += 0.5f;
  509.                                         nX = (int) (fNewX * 256.0f);
  510.                                         nY = (int) (fNewY * 256.0f);
  511.                                         FLOAT fColor = 0.45f - fR;
  512.                                         fColor /= 0.45f;
  513.                                         BYTE nAlpha = (BYTE)(fColor * 30);
  514.                                         if( nAlpha > 0xF )
  515.                                             nAlpha = 0xF;
  516.                                         WORD wColor = (WORD) ( (nAlpha << 12) + 0x0FFF );
  517.                                         DrawDotOnBuffer( nX, nY, &lockedRect, &desc, wColor );
  518.                                     }
  519.                                 }
  520.                             }
  521.                         }
  522.                     }
  523.                     pObject = pObject->m_pNext;
  524.                 }
  525.             }
  526.         }
  527.         pTempRadarTexture->UnlockRect( 0 );
  528.     }
  529.     IDirect3DSurface9* pSurfaceSrc = NULL;
  530.     IDirect3DSurface9* pSurfaceDest = NULL;
  531.     pRadarTexture->GetSurfaceLevel( 0, &pSurfaceDest );
  532.     pTempRadarTexture->GetSurfaceLevel( 0, &pSurfaceSrc );
  533.     if( pSurfaceSrc  != NULL && 
  534.         pSurfaceDest != NULL )
  535.     {
  536.         D3DXLoadSurfaceFromSurface( pSurfaceDest, NULL, NULL, 
  537.                                     pSurfaceSrc, NULL, NULL, 
  538.                                     D3DX_FILTER_POINT, 0 );
  539.         SAFE_RELEASE( pSurfaceDest );
  540.         SAFE_RELEASE( pSurfaceSrc );
  541.     }
  542.     return S_OK;
  543. }
  544. //-----------------------------------------------------------------------------
  545. // Name: RenderFrame
  546. // Desc: 
  547. //-----------------------------------------------------------------------------
  548. HRESULT CTerrainEngine::RenderFrame( DWORD* pdwNumVerts, CD3DCamera* pCam )
  549. {
  550.     m_dwVertsRendered = 0;
  551.     m_dwNumZonesInView = 0;
  552.     switch( g_Profile.nRenderTerrianStyle )
  553.     {
  554.         case 0:
  555.         {
  556.             // Render zones (0..x,0..y)
  557.             for( int iX = 0; iX<m_dwWorldWidth; iX++ )
  558.             {
  559.                 for( int iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  560.                 {
  561.                     RenderZone( 0, 0, iX, iZ, TRUE, TRUE, FALSE );
  562.                     m_dwNumZonesInView++;
  563.                 }
  564.             }
  565.             break;
  566.         }
  567.         case 1:
  568.         {
  569.             // Render zones near camera (and behind camera)
  570.             D3DXVECTOR3 vCamEyePt    = pCam->GetEyePt();
  571.             D3DXVECTOR3 vCamLookAtPt = pCam->GetLookatPt();
  572.             D3DXVECTOR3 vX    = vCamLookAtPt - vCamEyePt;
  573.             D3DXVECTOR3 vXPerp = D3DXVECTOR3( -vX.z, vX.y, vX.x );
  574.             D3DXVec3Normalize( &vXPerp, &vXPerp );
  575.     
  576.             FLOAT       fXLen = D3DXVec3Length( &vX );
  577.             FLOAT       fYLen = (float) ( tan( pCam->GetFOV() / 2.0f ) * fXLen );
  578.             D3DXVECTOR3 vYPlus = vX + vXPerp * fYLen;
  579.             D3DXVECTOR3 vYNeg  = vX - vXPerp * fYLen;
  580.             int nArea=2;
  581.             for( int iX=-nArea; iX<=nArea; iX++ )
  582.             {
  583.                 for( int iZ=-nArea; iZ<=nArea; iZ++ )
  584.                 {
  585.                     int iZoneX, iZoneZ;
  586.                     int iWorldX, iWorldZ;
  587.                     float fZoneWrapPosX = vCamEyePt.x + iX*ZONE_WIDTH;
  588.                     float fZoneWrapPosZ = vCamEyePt.z + iZ*ZONE_HEIGHT;
  589.                     GetZoneFromPt( fZoneWrapPosX, fZoneWrapPosZ, &iZoneX, &iZoneZ );
  590.                     GetWorldFromPt( fZoneWrapPosX, fZoneWrapPosZ, &iWorldX, &iWorldZ );
  591.                     RenderZone( iWorldX, iWorldZ, iZoneX, iZoneZ, TRUE, TRUE, FALSE ); 
  592.                     m_dwNumZonesInView++;
  593.                 }
  594.             }
  595.             break;
  596.         }
  597.         
  598.         case 2:
  599.         {
  600.             // Render only the zones ahead of camera but this is 
  601.             // kind of complex since the world wraps
  602.             static D3DXVECTOR3 s_vLastCamEyePt;
  603.             static D3DXVECTOR3 s_vLastCamLookAtPt;
  604.             static CULLINFO s_vLastCullInfo;
  605.             D3DXVECTOR3 vCamEyePt    = pCam->GetEyePt();
  606.             D3DXVECTOR3 vCamLookAtPt = pCam->GetLookatPt();
  607.             if( g_bDebugFreezeZoneRender )
  608.             {
  609.                 // For debugging: allows camera for zone rendering to be frozen,
  610.                 // but allows the real camera to continue to move so the 
  611.                 // zone's that were being rendered can be inspected
  612.                 if( !g_bDebugIsZoneRenderFroze )
  613.                 {
  614.                     s_vLastCamEyePt    = vCamEyePt;
  615.                     s_vLastCamLookAtPt = vCamLookAtPt;
  616.                     s_vLastCullInfo  = g_pApp->m_cullinfo;
  617.                 }
  618.                 g_bDebugIsZoneRenderFroze = !g_bDebugIsZoneRenderFroze;
  619.                 g_bDebugFreezeZoneRender = FALSE;
  620.             }
  621.             if( g_bDebugIsZoneRenderFroze )
  622.             {
  623.                 vCamEyePt    = s_vLastCamEyePt;
  624.                 vCamLookAtPt = s_vLastCamLookAtPt;
  625.                 g_pApp->m_cullinfo = s_vLastCullInfo;
  626.             }
  627.             // Get the worlds coords for the zone where the camera is
  628.             int iCenterWorldX, iCenterWorldZ;
  629.             int iCenterZoneX, iCenterZoneZ;
  630.             GetZoneFromPt(  vCamEyePt.x, vCamEyePt.z, &iCenterZoneX, &iCenterZoneZ );
  631.             GetWorldFromPt( vCamEyePt.x, vCamEyePt.z, &iCenterWorldX, &iCenterWorldZ );
  632.             float fCenterZoneX = (float) iCenterWorldX * (ZONE_WIDTH  * m_dwWorldWidth) + iCenterZoneX*ZONE_WIDTH;
  633.             float fCenterZoneZ = (float) iCenterWorldZ * (ZONE_HEIGHT * m_dwWorldHeight) + iCenterZoneZ*ZONE_HEIGHT;
  634.             // The world wraps so track which zones are in view using m_aZoneView
  635.             ZeroMemory( m_aZoneView, sizeof(VIEW_NODE)*MAX_VIEW_ZONE_X*MAX_VIEW_ZONE_Z );
  636.             // CheckIfZoneInView() recursively checks neighbor zones to see if they are in view 
  637.             // and updates m_aZoneView as it goes and along draw the terrain for each zone in view. 
  638.             // The camera is always the center zone at m_aZoneView[MAX_VIEW_ZONE_X/2][MAX_VIEW_ZONE_Z/2]
  639.             CheckIfZoneInView( fCenterZoneX, fCenterZoneZ, MAX_VIEW_ZONE_X/2, MAX_VIEW_ZONE_Z/2 );
  640.             // Then draw the objects for each zone in view since objects may have alpha 
  641.             // and the terrian needs to be rendered first
  642.             // Render only objects in zones that are visable
  643.             for( int iX = MAX_VIEW_ZONE_X-1; iX>=0; iX-- )
  644.             {
  645.                 for( int iZ = MAX_VIEW_ZONE_Z-1; iZ>=0; iZ-- )
  646.                 {
  647.                     if( m_aZoneView[iX][iZ].bInView )
  648.                     {
  649.                         RenderZone( m_aZoneView[iX][iZ].iWorldX, m_aZoneView[iX][iZ].iWorldZ, 
  650.                                     m_aZoneView[iX][iZ].iZoneX, m_aZoneView[iX][iZ].iZoneZ, 
  651.                                     TRUE, FALSE, TRUE );
  652.                     }
  653.                 }
  654.             }
  655.             break;
  656.         }
  657.     }
  658.     *pdwNumVerts = m_dwVertsRendered;
  659.     return S_OK;
  660. }
  661. //-----------------------------------------------------------------------------
  662. // Name: CheckIfZoneInView
  663. // Desc: 
  664. //-----------------------------------------------------------------------------
  665. BOOL CTerrainEngine::CheckIfZoneInView( const float fZonePosX, const float fZonePosZ, 
  666.                                         const int iZoneIndexX, const int iZoneIndexZ )
  667. {
  668.     if( iZoneIndexX < 0 || iZoneIndexX >= MAX_VIEW_ZONE_X )
  669.         return FALSE;
  670.     if( iZoneIndexZ < 0 || iZoneIndexZ >= MAX_VIEW_ZONE_Z )
  671.         return FALSE;
  672.     
  673.     // Return if this zone point that has already been visited
  674.     VIEW_NODE* pViewNode = &m_aZoneView[iZoneIndexX][iZoneIndexZ];
  675.     if( pViewNode->bVisited )
  676.         return FALSE;
  677.     pViewNode->bVisited = TRUE;
  678.     // Update the viewnode's bounding box state if it has changed since last time
  679.     if( !pViewNode->bValid ||
  680.         pViewNode->fZonePosX != fZonePosX ||
  681.         pViewNode->fZonePosZ != fZonePosZ )
  682.     {
  683.         // Mark that this viewnode refers to the zone at (fZonePosX,fZonePosZ)
  684.         pViewNode->bValid = true;
  685.         m_aZoneView[iZoneIndexX][iZoneIndexZ].fZonePosX = fZonePosX;
  686.         m_aZoneView[iZoneIndexX][iZoneIndexZ].fZonePosZ = fZonePosZ;
  687.         // Set max & min vectors for the bound box of this zone in world coords
  688.         // Set the upper & lower height to extremes so that even if the camera 
  689.         // is looking down or up the zone is rendered
  690.         D3DXVECTOR3 vMin = D3DXVECTOR3( fZonePosX, -10.0f, fZonePosZ );
  691.         D3DXVECTOR3 vMax = D3DXVECTOR3( fZonePosX + ZONE_WIDTH, MAX_HEIGHT_OF_MAP, fZonePosZ + ZONE_HEIGHT );
  692.         // Set the bounding box for this zone in world coords
  693.         pViewNode->vecBoundsWorld[0] = D3DXVECTOR3( vMin.x, vMin.y, vMin.z ); // xyz
  694.         pViewNode->vecBoundsWorld[1] = D3DXVECTOR3( vMax.x, vMin.y, vMin.z ); // Xyz
  695.         pViewNode->vecBoundsWorld[2] = D3DXVECTOR3( vMin.x, vMax.y, vMin.z ); // xYz
  696.         pViewNode->vecBoundsWorld[3] = D3DXVECTOR3( vMax.x, vMax.y, vMin.z ); // XYz
  697.         pViewNode->vecBoundsWorld[4] = D3DXVECTOR3( vMin.x, vMin.y, vMax.z ); // xyZ
  698.         pViewNode->vecBoundsWorld[5] = D3DXVECTOR3( vMax.x, vMin.y, vMax.z ); // XyZ
  699.         pViewNode->vecBoundsWorld[6] = D3DXVECTOR3( vMin.x, vMax.y, vMax.z ); // xYZ
  700.         pViewNode->vecBoundsWorld[7] = D3DXVECTOR3( vMax.x, vMax.y, vMax.z ); // XYZ
  701.         // Calc the planes of the bounding box
  702.         D3DXPlaneFromPoints( &pViewNode->planeBoundsWorld[0], &pViewNode->vecBoundsWorld[0], &pViewNode->vecBoundsWorld[1], &pViewNode->vecBoundsWorld[2] ); // Near
  703.         D3DXPlaneFromPoints( &pViewNode->planeBoundsWorld[1], &pViewNode->vecBoundsWorld[6], &pViewNode->vecBoundsWorld[7], &pViewNode->vecBoundsWorld[5] ); // Far
  704.         D3DXPlaneFromPoints( &pViewNode->planeBoundsWorld[2], &pViewNode->vecBoundsWorld[2], &pViewNode->vecBoundsWorld[6], &pViewNode->vecBoundsWorld[4] ); // Left
  705.         D3DXPlaneFromPoints( &pViewNode->planeBoundsWorld[3], &pViewNode->vecBoundsWorld[7], &pViewNode->vecBoundsWorld[3], &pViewNode->vecBoundsWorld[5] ); // Right
  706.         D3DXPlaneFromPoints( &pViewNode->planeBoundsWorld[4], &pViewNode->vecBoundsWorld[2], &pViewNode->vecBoundsWorld[3], &pViewNode->vecBoundsWorld[6] ); // Top
  707.         D3DXPlaneFromPoints( &pViewNode->planeBoundsWorld[5], &pViewNode->vecBoundsWorld[1], &pViewNode->vecBoundsWorld[0], &pViewNode->vecBoundsWorld[4] ); // Bottom
  708.     }
  709.     // Cull the zone based on its bounding box 
  710.     pViewNode->cullstate = CullObject( &g_pApp->m_cullinfo, pViewNode->vecBoundsWorld, pViewNode->planeBoundsWorld );
  711.     // Only render this zone if its inside the camera's view
  712.     if( pViewNode->cullstate == CS_INSIDE )
  713.     {
  714.         m_aZoneView[iZoneIndexX][iZoneIndexZ].bInView = TRUE;   
  715.         m_dwNumZonesInView++;
  716.         // Since the world wraps, recursively check to see if the 8 neighbor zones are in view
  717.         CheckIfZoneInView( fZonePosX - ZONE_WIDTH, fZonePosZ              , iZoneIndexX-1, iZoneIndexZ+0 );
  718.         CheckIfZoneInView( fZonePosX + ZONE_WIDTH, fZonePosZ              , iZoneIndexX+1, iZoneIndexZ+0 );
  719.         CheckIfZoneInView( fZonePosX             , fZonePosZ - ZONE_HEIGHT, iZoneIndexX+0, iZoneIndexZ-1 );
  720.         CheckIfZoneInView( fZonePosX             , fZonePosZ + ZONE_HEIGHT, iZoneIndexX+0, iZoneIndexZ+1 );
  721.         CheckIfZoneInView( fZonePosX - ZONE_WIDTH, fZonePosZ - ZONE_HEIGHT, iZoneIndexX-1, iZoneIndexZ-1 );
  722.         CheckIfZoneInView( fZonePosX - ZONE_WIDTH, fZonePosZ + ZONE_HEIGHT, iZoneIndexX-1, iZoneIndexZ+1 );
  723.         CheckIfZoneInView( fZonePosX + ZONE_WIDTH, fZonePosZ - ZONE_HEIGHT, iZoneIndexX+1, iZoneIndexZ-1 );
  724.         CheckIfZoneInView( fZonePosX + ZONE_WIDTH, fZonePosZ + ZONE_HEIGHT, iZoneIndexX+1, iZoneIndexZ+1 );
  725.         GetZoneFromPt( fZonePosX+ZONE_WIDTH/2, fZonePosZ+ZONE_HEIGHT/2, &m_aZoneView[iZoneIndexX][iZoneIndexZ].iZoneX, &m_aZoneView[iZoneIndexX][iZoneIndexZ].iZoneZ );
  726.         GetWorldFromPt( fZonePosX+ZONE_WIDTH/2, fZonePosZ+ZONE_HEIGHT/2, &m_aZoneView[iZoneIndexX][iZoneIndexZ].iWorldX, &m_aZoneView[iZoneIndexX][iZoneIndexZ].iWorldZ );
  727.         // Render this zone
  728.         RenderZone( m_aZoneView[iZoneIndexX][iZoneIndexZ].iWorldX, m_aZoneView[iZoneIndexX][iZoneIndexZ].iWorldZ,
  729.                     m_aZoneView[iZoneIndexX][iZoneIndexZ].iZoneX, m_aZoneView[iZoneIndexX][iZoneIndexZ].iZoneZ,
  730.                     FALSE, TRUE, TRUE ); 
  731.         return TRUE;
  732.     }
  733.     return FALSE;
  734. }
  735. //-----------------------------------------------------------------------------
  736. // Name: CullObject()
  737. // Desc: Determine the cullstate for an object bounding box (OBB).  
  738. //       The algorithm is:
  739. //       1) If any OBB corner pt is inside the frustum, return CS_INSIDE
  740. //       2) Else if all OBB corner pts are outside a single frustum plane, 
  741. //          return CS_OUTSIDE
  742. //       3) Else if any frustum edge penetrates a face of the OBB, return 
  743. //          CS_INSIDE
  744. //       4) Else if any OBB edge penetrates a face of the frustum, return
  745. //          CS_INSIDE
  746. //       5) Else if any point in the frustum is outside any plane of the 
  747. //          OBB, return CS_OUTSIDE
  748. //       6) Else return CS_INSIDE
  749. //-----------------------------------------------------------------------------
  750. CULLSTATE CTerrainEngine::CullObject( const CULLINFO* pCullInfo, const D3DXVECTOR3* pVecBounds, 
  751.                                       const D3DXPLANE* pPlaneBounds )
  752. {
  753.     BYTE bOutside[8];
  754.     ZeroMemory( &bOutside, sizeof(bOutside) );
  755.     // Check boundary vertices against all 6 frustum planes, 
  756.     // and store result (1 if outside) in a bitfield
  757.     for( int iPoint = 0; iPoint < 8; iPoint++ )
  758.     {
  759.         for( int iPlane = 0; iPlane < 6; iPlane++ )
  760.         {
  761.             if( pCullInfo->planeFrustum[iPlane].a * pVecBounds[iPoint].x +
  762.                 pCullInfo->planeFrustum[iPlane].b * pVecBounds[iPoint].y +
  763.                 pCullInfo->planeFrustum[iPlane].c * pVecBounds[iPoint].z +
  764.                 pCullInfo->planeFrustum[iPlane].d < 0)
  765.             {
  766.                 bOutside[iPoint] |= (1 << iPlane);
  767.             }
  768.         }
  769.         // If any point is inside all 6 frustum planes, it is inside
  770.         // the frustum, so the object must be rendered.
  771.         if( bOutside[iPoint] == 0 )
  772.             return CS_INSIDE;
  773.     }
  774.     // If all points are outside any single frustum plane, the object is
  775.     // outside the frustum
  776.     if( (bOutside[0] & bOutside[1] & bOutside[2] & bOutside[3] & 
  777.         bOutside[4] & bOutside[5] & bOutside[6] & bOutside[7]) != 0 )
  778.     {
  779.         return CS_OUTSIDE;
  780.     }
  781.     // Now see if any of the frustum edges penetrate any of the faces of
  782.     // the bounding box
  783.     D3DXVECTOR3 edge[12][2] = 
  784.     {
  785.         pCullInfo->vecFrustum[0], pCullInfo->vecFrustum[1], // front bottom
  786.         pCullInfo->vecFrustum[2], pCullInfo->vecFrustum[3], // front top
  787.         pCullInfo->vecFrustum[0], pCullInfo->vecFrustum[2], // front left
  788.         pCullInfo->vecFrustum[1], pCullInfo->vecFrustum[3], // front right
  789.         pCullInfo->vecFrustum[4], pCullInfo->vecFrustum[5], // back bottom
  790.         pCullInfo->vecFrustum[6], pCullInfo->vecFrustum[7], // back top
  791.         pCullInfo->vecFrustum[4], pCullInfo->vecFrustum[6], // back left
  792.         pCullInfo->vecFrustum[5], pCullInfo->vecFrustum[7], // back right
  793.         pCullInfo->vecFrustum[0], pCullInfo->vecFrustum[4], // left bottom
  794.         pCullInfo->vecFrustum[2], pCullInfo->vecFrustum[6], // left top
  795.         pCullInfo->vecFrustum[1], pCullInfo->vecFrustum[5], // right bottom
  796.         pCullInfo->vecFrustum[3], pCullInfo->vecFrustum[7], // right top
  797.     };
  798.     D3DXVECTOR3 face[6][4] =
  799.     {
  800.         pVecBounds[0], pVecBounds[2], pVecBounds[3], pVecBounds[1], // front
  801.         pVecBounds[4], pVecBounds[5], pVecBounds[7], pVecBounds[6], // back
  802.         pVecBounds[0], pVecBounds[4], pVecBounds[6], pVecBounds[2], // left
  803.         pVecBounds[1], pVecBounds[3], pVecBounds[7], pVecBounds[5], // right
  804.         pVecBounds[2], pVecBounds[6], pVecBounds[7], pVecBounds[3], // top
  805.         pVecBounds[0], pVecBounds[4], pVecBounds[5], pVecBounds[1], // bottom
  806.     };
  807.     D3DXVECTOR3* pEdge;
  808.     D3DXVECTOR3* pFace;
  809.     pEdge = &edge[0][0];
  810.     for( INT iEdge = 0; iEdge < 12; iEdge++ )
  811.     {
  812.         pFace = &face[0][0];
  813.         for( INT iFace = 0; iFace < 6; iFace++ )
  814.         {
  815.             if( EdgeIntersectsFace( pEdge, pFace, &pPlaneBounds[iFace] ) )
  816.             {
  817.                 return CS_INSIDE; // slow
  818.             }
  819.             pFace += 4;
  820.         }
  821.         pEdge += 2;
  822.     }
  823.     // Now see if any of the bounding box edges penetrate any of the faces of
  824.     // the frustum
  825.     D3DXVECTOR3 edge2[12][2] = 
  826.     {
  827.         pVecBounds[0], pVecBounds[1], // front bottom
  828.         pVecBounds[2], pVecBounds[3], // front top
  829.         pVecBounds[0], pVecBounds[2], // front left
  830.         pVecBounds[1], pVecBounds[3], // front right
  831.         pVecBounds[4], pVecBounds[5], // back bottom
  832.         pVecBounds[6], pVecBounds[7], // back top
  833.         pVecBounds[4], pVecBounds[6], // back left
  834.         pVecBounds[5], pVecBounds[7], // back right
  835.         pVecBounds[0], pVecBounds[4], // left bottom
  836.         pVecBounds[2], pVecBounds[6], // left top
  837.         pVecBounds[1], pVecBounds[5], // right bottom
  838.         pVecBounds[3], pVecBounds[7], // right top
  839.     };
  840.     D3DXVECTOR3 face2[6][4] =
  841.     {
  842.         pCullInfo->vecFrustum[0], pCullInfo->vecFrustum[2], pCullInfo->vecFrustum[3], pCullInfo->vecFrustum[1], // front
  843.         pCullInfo->vecFrustum[4], pCullInfo->vecFrustum[5], pCullInfo->vecFrustum[7], pCullInfo->vecFrustum[6], // back
  844.         pCullInfo->vecFrustum[0], pCullInfo->vecFrustum[4], pCullInfo->vecFrustum[6], pCullInfo->vecFrustum[2], // left
  845.         pCullInfo->vecFrustum[1], pCullInfo->vecFrustum[3], pCullInfo->vecFrustum[7], pCullInfo->vecFrustum[5], // right
  846.         pCullInfo->vecFrustum[2], pCullInfo->vecFrustum[6], pCullInfo->vecFrustum[7], pCullInfo->vecFrustum[3], // top
  847.         pCullInfo->vecFrustum[0], pCullInfo->vecFrustum[4], pCullInfo->vecFrustum[5], pCullInfo->vecFrustum[1], // bottom
  848.     };
  849.     pEdge = &edge2[0][0];
  850.     for( iEdge = 0; iEdge < 12; iEdge++ )
  851.     {
  852.         pFace = &face2[0][0];
  853.         for( INT iFace = 0; iFace < 6; iFace++ )
  854.         {
  855.             if( EdgeIntersectsFace( pEdge, pFace, &pCullInfo->planeFrustum[iFace] ) )
  856.             {
  857.                 return CS_INSIDE; // slow
  858.             }
  859.             pFace += 4;
  860.         }
  861.         pEdge += 2;
  862.     }
  863.     // Now see if frustum is contained in bounding box
  864.     // If any frustum corner point is outside any plane of the bounding box,
  865.     // the frustum is not contained in the bounding box, so the object
  866.     // is outside the frustum
  867.     for( INT iPlane = 0; iPlane < 6; iPlane++ )
  868.     {
  869.         if( pPlaneBounds[iPlane].a * pCullInfo->vecFrustum[0].x +
  870.             pPlaneBounds[iPlane].b * pCullInfo->vecFrustum[0].y +
  871.             pPlaneBounds[iPlane].c * pCullInfo->vecFrustum[0].z +
  872.             pPlaneBounds[iPlane].d  < 0 )
  873.         {
  874.             return CS_OUTSIDE; // slow 
  875.         }
  876.     }
  877.     // Bounding box must contain the frustum, so render the object
  878.     return CS_INSIDE; // slow
  879. }
  880. //-----------------------------------------------------------------------------
  881. // Name: EdgeIntersectsFace()
  882. // Desc: Determine if the edge bounded by the two vectors in pEdges intersects
  883. //       the quadrilateral described by the four vectors in pFacePoints.  
  884. //       Note: pPlane could be derived from pFacePoints using 
  885. //       D3DXPlaneFromPoints, but it is precomputed in advance for greater
  886. //       speed.
  887. //-----------------------------------------------------------------------------
  888. BOOL CTerrainEngine::EdgeIntersectsFace( const D3DXVECTOR3* const pEdges, const D3DXVECTOR3* const pFacePoints, 
  889.                                          const D3DXPLANE* const pPlane )
  890. {
  891.     // If both edge points are on the same side of the plane, the edge does
  892.     // not intersect the face
  893.     FLOAT fDist1;
  894.     FLOAT fDist2;
  895.     fDist1 = pPlane->a * pEdges[0].x + pPlane->b * pEdges[0].y +
  896.              pPlane->c * pEdges[0].z + pPlane->d;
  897.     fDist2 = pPlane->a * pEdges[1].x + pPlane->b * pEdges[1].y +
  898.              pPlane->c * pEdges[1].z + pPlane->d;
  899.     if( fDist1 > 0 && fDist2 > 0 ||
  900.         fDist1 < 0 && fDist2 < 0 )
  901.     {
  902.         return FALSE;
  903.     }
  904.     // Find point of intersection between edge and face plane (if they're
  905.     // parallel, edge does not intersect face and D3DXPlaneIntersectLine 
  906.     // returns NULL)
  907.     D3DXVECTOR3 ptIntersection;
  908.     if( NULL == D3DXPlaneIntersectLine( &ptIntersection, pPlane, &pEdges[0], &pEdges[1] ) )
  909.         return FALSE;
  910.     // Project onto a 2D plane to make the pt-in-poly test easier
  911.     FLOAT fAbsA = (pPlane->a > 0 ? pPlane->a : -pPlane->a);
  912.     FLOAT fAbsB = (pPlane->b > 0 ? pPlane->b : -pPlane->b);
  913.     FLOAT fAbsC = (pPlane->c > 0 ? pPlane->c : -pPlane->c);
  914.     D3DXVECTOR2 facePoints[4];
  915.     D3DXVECTOR2 point;
  916.     if( fAbsA > fAbsB && fAbsA > fAbsC )
  917.     {
  918.         // Plane is mainly pointing along X axis, so use Y and Z
  919.         for( INT i = 0; i < 4; i++)
  920.         {
  921.             facePoints[i].x = pFacePoints[i].y;
  922.             facePoints[i].y = pFacePoints[i].z;
  923.         }
  924.         point.x = ptIntersection.y;
  925.         point.y = ptIntersection.z;
  926.     }
  927.     else if( fAbsB > fAbsA && fAbsB > fAbsC )
  928.     {
  929.         // Plane is mainly pointing along Y axis, so use X and Z
  930.         for( INT i = 0; i < 4; i++)
  931.         {
  932.             facePoints[i].x = pFacePoints[i].x;
  933.             facePoints[i].y = pFacePoints[i].z;
  934.         }
  935.         point.x = ptIntersection.x;
  936.         point.y = ptIntersection.z;
  937.     }
  938.     else
  939.     {
  940.         // Plane is mainly pointing along Z axis, so use X and Y
  941.         for( INT i = 0; i < 4; i++)
  942.         {
  943.             facePoints[i].x = pFacePoints[i].x;
  944.             facePoints[i].y = pFacePoints[i].y;
  945.         }
  946.         point.x = ptIntersection.x;
  947.         point.y = ptIntersection.y;
  948.     }
  949.     // If point is on the outside of any of the face edges, it is
  950.     // outside the face.  
  951.     // We can do this by taking the determinant of the following matrix:
  952.     // | x0 y0 1 |
  953.     // | x1 y1 1 |
  954.     // | x2 y2 1 |
  955.     // where (x0,y0) and (x1,y1) are points on the face edge and (x2,y2) 
  956.     // is our test point.  If this value is positive, the test point is
  957.     // "to the left" of the line.  To determine whether a point needs to
  958.     // be "to the right" or "to the left" of the four lines to qualify as
  959.     // inside the face, we need to see if the faces are specified in 
  960.     // clockwise or counter-clockwise order (it could be either, since the
  961.     // edge could be penetrating from either side).  To determine this, we
  962.     // do the same test to see if the third point is "to the right" or 
  963.     // "to the left" of the line formed by the first two points.
  964.     // See http://forum.swarthmore.edu/dr.math/problems/scott5.31.96.html
  965.     FLOAT x0, x1, x2, y0, y1, y2;
  966.     x0 = facePoints[0].x;
  967.     y0 = facePoints[0].y;
  968.     x1 = facePoints[1].x;
  969.     y1 = facePoints[1].y;
  970.     x2 = facePoints[2].x;
  971.     y2 = facePoints[2].y;
  972.     BOOL bClockwise = FALSE;
  973.     if( x1*y2 - y1*x2 - x0*y2 + y0*x2 + x0*y1 - y0*x1 < 0 )
  974.         bClockwise = TRUE;
  975.     x2 = point.x;
  976.     y2 = point.y;
  977.     for( INT i = 0; i < 4; i++ )
  978.     {
  979.         x0 = facePoints[i].x;
  980.         y0 = facePoints[i].y;
  981.         if( i < 3 )
  982.         {
  983.             x1 = facePoints[i+1].x;
  984.             y1 = facePoints[i+1].y;
  985.         }
  986.         else
  987.         {
  988.             x1 = facePoints[0].x;
  989.             y1 = facePoints[0].y;
  990.         }
  991.         if( ( x1*y2 - y1*x2 - x0*y2 + y0*x2 + x0*y1 - y0*x1 > 0 ) == bClockwise )
  992.             return FALSE;
  993.     }
  994.     // If we get here, the point is inside all four face edges, 
  995.     // so it's inside the face.
  996.     return TRUE;
  997. }
  998. //-----------------------------------------------------------------------------
  999. // Name: GetZoneFromPt
  1000. // Desc: 
  1001. //-----------------------------------------------------------------------------
  1002. VOID CTerrainEngine::GetZoneFromPt( const float fX, const float fZ, int* pnZoneX, int* pnZoneZ )
  1003. {
  1004.     int iZoneX;
  1005.     int iZoneZ;
  1006.     
  1007.     if( fX >= 0.0f )
  1008.         iZoneX = (int) (fX / ZONE_WIDTH);
  1009.     else
  1010.         iZoneX = (int) (fX / ZONE_WIDTH) - 1;
  1011.     if( fZ >= 0.0f )
  1012.         iZoneZ = (int) (fZ / ZONE_HEIGHT);
  1013.     else
  1014.         iZoneZ = (int) (fZ / ZONE_HEIGHT) - 1;
  1015.     while( iZoneX < 0 )
  1016.         iZoneX += m_dwWorldWidth;
  1017.     while( iZoneZ < 0 )
  1018.         iZoneZ += m_dwWorldHeight;
  1019.     *pnZoneX = iZoneX % m_dwWorldWidth;
  1020.     *pnZoneZ = iZoneZ % m_dwWorldHeight;
  1021. }
  1022. //-----------------------------------------------------------------------------
  1023. // Name: GetWorldFromPt
  1024. // Desc: 
  1025. //-----------------------------------------------------------------------------
  1026. VOID CTerrainEngine::GetWorldFromPt( const float fX, const float fZ, int* pnWorldX, int* pnWorldZ )
  1027. {
  1028.     int iWorldX, iWorldZ;
  1029.     
  1030.     if( fX >= 0.0f )
  1031.         iWorldX = (int) ( fX / (ZONE_WIDTH  * m_dwWorldWidth) );
  1032.     else
  1033.         iWorldX = (int) ( fX / (ZONE_WIDTH  * m_dwWorldWidth) ) - 1;
  1034.     if( fZ >= 0.0f )
  1035.         iWorldZ = (int) ( fZ / (ZONE_HEIGHT * m_dwWorldHeight) );
  1036.     else
  1037.         iWorldZ = (int) ( fZ / (ZONE_HEIGHT * m_dwWorldHeight) ) - 1;
  1038.     *pnWorldX = iWorldX;
  1039.     *pnWorldZ = iWorldZ;
  1040. }
  1041. //-----------------------------------------------------------------------------
  1042. // Name: GetWorldFromUniversal
  1043. // Desc: output coords that are between <0,0> and 
  1044. //       <ZONE_WIDTH*m_dwWorldWidth,ZONE_HEIGHT*m_dwWorldHeight>
  1045. //-----------------------------------------------------------------------------
  1046. VOID CTerrainEngine::GetWorldFromUniversal( float fUniversalX, float fUniversalZ, 
  1047.                                             float* pfWorldX, float* pfWorldZ )
  1048. {
  1049.     // Wrap if needed
  1050.     while( fUniversalX < 0.0f )
  1051.         fUniversalX += ZONE_WIDTH*m_dwWorldWidth;
  1052.     while( fUniversalX >= ZONE_WIDTH*m_dwWorldWidth )
  1053.         fUniversalX -= ZONE_WIDTH*m_dwWorldWidth;
  1054.     while( fUniversalZ < 0.0f )
  1055.         fUniversalZ += ZONE_HEIGHT*m_dwWorldHeight;
  1056.     while( fUniversalZ >= ZONE_HEIGHT*m_dwWorldHeight )
  1057.         fUniversalZ -= ZONE_HEIGHT*m_dwWorldHeight;
  1058.     *pfWorldX = fUniversalX;
  1059.     *pfWorldZ = fUniversalZ;
  1060. }
  1061. //-----------------------------------------------------------------------------
  1062. // Name: RenderZone
  1063. // Desc: 
  1064. //-----------------------------------------------------------------------------
  1065. VOID CTerrainEngine::RenderZone( int iWorldX, int iWorldZ, int iZoneX, int iZoneZ, 
  1066.                                  BOOL bRenderObjects, BOOL bRenderTerrain, BOOL bCullObjects )
  1067. {
  1068.     float fWrapOffsetX = (float) iWorldX * (ZONE_WIDTH  * m_dwWorldWidth);
  1069.     float fWrapOffsetZ = (float) iWorldZ * (ZONE_HEIGHT * m_dwWorldHeight);
  1070.     MESH_NODE* pMeshNode = &m_MeshArray[iZoneX + m_dwWorldWidth*iZoneZ];
  1071.     if( bRenderTerrain && pMeshNode->pMesh )
  1072.     {
  1073.         DWORD dwNumTerrainVerts;
  1074.         // Set either wireframe or solid
  1075.         if( g_pApp->m_bDebugMode && g_pApp->m_bWireMode )
  1076.             g_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
  1077.         else
  1078.             g_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
  1079.         pMeshNode->pMesh->RenderFrame( fWrapOffsetX, fWrapOffsetZ, &dwNumTerrainVerts );  
  1080.         g_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
  1081.         m_dwVertsRendered += dwNumTerrainVerts;
  1082.     }
  1083.     if( bRenderObjects )
  1084.     {
  1085.         DWORD dwNumModelVerts = 0;
  1086.         CDisplayObject* pObject = pMeshNode->pDisplayList;
  1087.         while( pObject )
  1088.         {
  1089.             if( pObject->m_bActive )
  1090.             {
  1091.                 bool bRender = true;
  1092.                 if( bCullObjects )
  1093.                 {
  1094.                     // Only render if m_cullstate == CS_INSIDE
  1095.                     pObject->CullObject( fWrapOffsetX, fWrapOffsetZ, &g_pApp->m_cullinfo );
  1096.                     bRender = (pObject->m_cullstate == CS_INSIDE );
  1097.                 }
  1098.                 if( bRender )
  1099.                     pObject->Render( fWrapOffsetX, fWrapOffsetZ, &dwNumModelVerts );
  1100.             }
  1101.             m_dwVertsRendered += dwNumModelVerts;
  1102.             pObject = pObject->m_pNext;
  1103.         }
  1104.     }
  1105. }
  1106. //-----------------------------------------------------------------------------
  1107. // Name: InvalidateDeviceObjects
  1108. // Desc: 
  1109. //-----------------------------------------------------------------------------
  1110. HRESULT CTerrainEngine::InvalidateDeviceObjects()
  1111. {
  1112.     HRESULT hr;
  1113.     for( int iX = 0; iX<m_dwWorldWidth; iX++ )
  1114.     {
  1115.         for( int iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  1116.         {
  1117.             MESH_NODE* pMeshNode = &m_MeshArray[iX + m_dwWorldWidth*iZ];
  1118.             CDisplayObject* pObject = pMeshNode->pDisplayList;
  1119.             while( pObject )
  1120.             {
  1121.                 pObject->InvalidateDeviceObjects();
  1122.                 pObject = pObject->m_pNext;
  1123.             }
  1124.             hr = pMeshNode->pMesh->InvalidateDeviceObjects();
  1125.             if( FAILED(hr) )
  1126.                 return hr;
  1127.         }
  1128.     }
  1129.     return S_OK;
  1130. }
  1131. //-----------------------------------------------------------------------------
  1132. // Name: DeleteDeviceObjects
  1133. // Desc: 
  1134. //-----------------------------------------------------------------------------
  1135. HRESULT CTerrainEngine::DeleteDeviceObjects()
  1136. {
  1137.     HRESULT hr;
  1138.     for( int iX = 0; iX<m_dwWorldWidth; iX++ )
  1139.     {
  1140.         for( int iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  1141.         {
  1142.             MESH_NODE* pMeshNode = &m_MeshArray[iX + m_dwWorldWidth*iZ];
  1143.             CDisplayObject* pObject = pMeshNode->pDisplayList;
  1144.             while( pObject )
  1145.             {
  1146.                 pObject->DeleteDeviceObjects();
  1147.                 pObject = pObject->m_pNext;
  1148.             }
  1149.             hr = pMeshNode->pMesh->DeleteDeviceObjects();
  1150.             if( FAILED(hr) )
  1151.                 return hr;
  1152.         }
  1153.     }
  1154.     return S_OK;
  1155. }
  1156. //-----------------------------------------------------------------------------
  1157. // Name: FinalCleanup
  1158. // Desc: 
  1159. //-----------------------------------------------------------------------------
  1160. VOID CTerrainEngine::FinalCleanup()
  1161. {
  1162.     if( m_MeshArray )
  1163.     {
  1164.         for( int iX = 0; iX<m_dwWorldWidth; iX++ )
  1165.         {
  1166.             for( int iZ = 0; iZ<m_dwWorldHeight; iZ++ )
  1167.             {
  1168.                 MESH_NODE* pMeshNode = &m_MeshArray[iX + m_dwWorldWidth*iZ];
  1169.                 CDisplayObject* pObject = pMeshNode->pDisplayList;
  1170.                 CDisplayObject* pObjectDelete;
  1171.                 while( pObject )
  1172.                 {
  1173.                     pObjectDelete = pObject;
  1174.                     pObject = pObject->m_pNext;
  1175.                     SAFE_DELETE( pObjectDelete );
  1176.                 }
  1177.                 pMeshNode->pDisplayList = NULL;
  1178.                 if( m_MeshArray[iX + m_dwWorldWidth*iZ].pMesh )
  1179.                     m_MeshArray[iX + m_dwWorldWidth*iZ].pMesh->FinalCleanup();
  1180.                 SAFE_DELETE( m_MeshArray[iX + m_dwWorldWidth*iZ].pMesh );
  1181.             }
  1182.         }
  1183.         SAFE_DELETE_ARRAY( m_MeshArray );
  1184.     }
  1185. }
  1186. //-----------------------------------------------------------------------------
  1187. // Name: GetHeight
  1188. // Desc: returns the height of the ground at this point in universial coords 
  1189. //       Since the terrian engine is simple, there can only be one 1 ground height
  1190. //       at every point (no tunnels/etc)
  1191. //-----------------------------------------------------------------------------
  1192. FLOAT CTerrainEngine::GetHeight( float x, float z ) 
  1193.     // Doing this:
  1194.     // x = -0.0000001
  1195.     // x += 256.0f
  1196.     // x will equal 256.0f (it will loose bits of percision) which will throw 
  1197.     // off the calculations below so round off the percision while maintaining
  1198.     // the sign value
  1199.     x *= 10000.0f;
  1200.     if( x < 0 )
  1201.         x = floorf(x)/10000.0f;
  1202.     else
  1203.         x = ceilf(x)/10000.0f;
  1204.     z *= 10000.0f;
  1205.     if( z < 0 )
  1206.         z = floorf(z)/10000.0f;
  1207.     else
  1208.         z = ceilf(z)/10000.0f;
  1209.     // Figure out which zone this point is in
  1210.     int iX = (int)( (int)(x) / (int)ZONE_WIDTH );
  1211.     int iZ = (int)( (int)(z) / (int)ZONE_HEIGHT );
  1212.     // Compensate for negative values since iX = -10.0f / 64.0f = 0 
  1213.     // and we want iX=-1 if x=-10.0f
  1214.     if( x < 0.0f ) iX--;
  1215.     if( z < 0.0f ) iZ--;
  1216.     // Wrap the values around the world if needed
  1217.     while( iX < 0 )
  1218.     {
  1219.         iX += m_dwWorldWidth;
  1220.         x += ZONE_WIDTH*m_dwWorldWidth;
  1221.     }
  1222.     while( iX >= m_dwWorldWidth )
  1223.     {
  1224.         iX -= m_dwWorldWidth;
  1225.         x -= ZONE_WIDTH*m_dwWorldWidth;
  1226.     }
  1227.     while( iZ < 0 )
  1228.     {
  1229.         iZ += m_dwWorldHeight;
  1230.         z += ZONE_HEIGHT*m_dwWorldHeight;
  1231.     }   
  1232.     while( iZ >= m_dwWorldHeight )
  1233.     {
  1234.         iZ -= m_dwWorldHeight;
  1235.         z -= ZONE_HEIGHT*m_dwWorldHeight;
  1236.     }
  1237.     CTerrainMesh* pMesh = m_MeshArray[iX + m_dwWorldWidth*iZ].pMesh;
  1238.     // Ensure that the mesh owns this point
  1239.     assert( FALSE == pMesh->IsOutOfRangeWorld( x, z ) );       
  1240.     // Now that we know the mesh index, ask the mesh what the value is at this point
  1241.     return pMesh->GetHeightWorld( x, z ); 
  1242. };
  1243. //-----------------------------------------------------------------------------
  1244. // Name: GetTexelColor
  1245. // Desc: same as above but returns the texel color at this point
  1246. //-----------------------------------------------------------------------------
  1247. DWORD CTerrainEngine::GetTexelColor( DWORD iLevel, float x, float z )
  1248.     x *= 10000.0f;
  1249.     if( x < 0 )
  1250.         x = floorf(x)/10000.0f;
  1251.     else
  1252.         x = ceilf(x)/10000.0f;
  1253.     z *= 10000.0f;
  1254.     if( z < 0 )
  1255.         z = floorf(z)/10000.0f;
  1256.     else
  1257.         z = ceilf(z)/10000.0f;
  1258.     int iX = (int)( x / (float) ZONE_WIDTH );
  1259.     int iZ = (int)( z / (float) ZONE_HEIGHT );
  1260.     if( x < 0.0f ) iX--;
  1261.     if( z < 0.0f ) iZ--;
  1262.     while( iX < 0 )
  1263.     {
  1264.         iX += m_dwWorldWidth;
  1265.         x += ZONE_WIDTH*m_dwWorldWidth;
  1266.     }
  1267.     while( iX >= m_dwWorldWidth )
  1268.     {
  1269.         iX -= m_dwWorldWidth;
  1270.         x -= ZONE_WIDTH*m_dwWorldWidth;
  1271.     }
  1272.     while( iZ < 0 )
  1273.     {
  1274.         iZ += m_dwWorldHeight;
  1275.         z += ZONE_HEIGHT*m_dwWorldHeight;
  1276.     }   
  1277.     while( iZ >= m_dwWorldHeight )
  1278.     {
  1279.         iZ -= m_dwWorldHeight;
  1280.         z -= ZONE_HEIGHT*m_dwWorldHeight;
  1281.     }
  1282.         
  1283.     return m_MeshArray[iX + m_dwWorldWidth*iZ].pMesh->GetTexelColor( iLevel, x, z ); 
  1284. }