home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / Source / Chapter 10 / Engine / SceneManager.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2005-03-29  |  40.8 KB  |  1,054 lines

  1. //-----------------------------------------------------------------------------
  2. // SceneManager.h implementation.
  3. // Refer to the SceneManager.h interface for more details.
  4. //
  5. // Programming a Multiplayer First Person Shooter in DirectX
  6. // Copyright (c) 2004 Vaughan Young
  7. //-----------------------------------------------------------------------------
  8. #include "Engine.h"
  9.  
  10. //-----------------------------------------------------------------------------
  11. // The scene manager class constructor.
  12. //-----------------------------------------------------------------------------
  13. SceneManager::SceneManager( float scale, char *spawnerPath )
  14. {
  15.     m_name = NULL;
  16.     m_scale = scale;
  17.     m_gravity = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
  18.     m_loaded = false;
  19.     m_mesh = NULL;
  20.     m_maxFaces = 0;
  21.     m_maxHalfSize = 0.0f;
  22.     m_frameStamp = 0;
  23.  
  24.     m_dynamicObjects = new LinkedList< SceneObject >;
  25.     m_occludingObjects = NULL;
  26.     m_visibleOccluders = NULL;
  27.     m_playerSpawnPoints = NULL;
  28.     m_objectSpawners = NULL;
  29.     m_spawnerPath = spawnerPath;
  30.  
  31.     m_firstLeaf = NULL;
  32.  
  33.     m_sceneVertexBuffer = NULL;
  34.     m_vertices = NULL;
  35.     m_totalVertices = 0;
  36.  
  37.     m_renderCaches = NULL;
  38.  
  39.     m_totalFaces = 0;
  40.     m_faces = NULL;
  41. }
  42.  
  43. //-----------------------------------------------------------------------------
  44. // The scene manager class destructor.
  45. //-----------------------------------------------------------------------------
  46. SceneManager::~SceneManager()
  47. {
  48.     DestroyScene();
  49.  
  50.     // Destroy the dynamic objects list as it was created in the constructor.
  51.     SAFE_DELETE( m_dynamicObjects );
  52. }
  53.  
  54. //-----------------------------------------------------------------------------
  55. // Loads a new scene from the given scene file.
  56. //-----------------------------------------------------------------------------
  57. void SceneManager::LoadScene( char *name, char *path )
  58. {
  59.     // Create the lists of objects used in the scene. The dynamic object list
  60.     // is persistent across scene changes so it doesn't need to be created.
  61.     m_occludingObjects = new LinkedList< SceneOccluder >;
  62.     m_visibleOccluders = new LinkedList< SceneOccluder >;
  63.     m_playerSpawnPoints = new LinkedList< SceneObject >;
  64.     m_objectSpawners = new LinkedList< SpawnerObject >;
  65.  
  66.     // Load the script for the scene.
  67.     Script *script = new Script( name, path );
  68.  
  69.     // Store the name of the scene.
  70.     m_name = new char[strlen( script->GetStringData( "name" ) ) + 1];
  71.     memcpy( m_name, script->GetStringData( "name" ), ( strlen( script->GetStringData( "name" ) ) + 1 ) * sizeof( char ) );
  72.  
  73.     // Store the scene's gravity vector.
  74.     m_gravity = *script->GetVectorData( "gravity" ) / m_scale;
  75.  
  76.     // Create the sun light source.
  77.     D3DLIGHT9 sun;
  78.     sun.Type = D3DLIGHT_DIRECTIONAL;
  79.     sun.Diffuse.r = 1.0f;
  80.     sun.Diffuse.g = 1.0f;
  81.     sun.Diffuse.b = 1.0f;
  82.     sun.Diffuse.a = 1.0f;
  83.     sun.Specular = sun.Diffuse;
  84.     sun.Ambient.r = script->GetColourData( "ambient_light" )->r;
  85.     sun.Ambient.g = script->GetColourData( "ambient_light" )->g;
  86.     sun.Ambient.b = script->GetColourData( "ambient_light" )->b;
  87.     sun.Ambient.a = script->GetColourData( "ambient_light" )->a;
  88.     sun.Direction = D3DXVECTOR3( script->GetVectorData( "sun_direction" )->x, script->GetVectorData( "sun_direction" )->y, script->GetVectorData( "sun_direction" )->z );
  89.     sun.Range = 0.0f;
  90.  
  91.     // Switch lighting on, enable the sun light, and specular highlights.
  92.     g_engine->GetDevice()->SetRenderState( D3DRS_LIGHTING, true );
  93.     g_engine->GetDevice()->SetLight( 0, &sun );
  94.     g_engine->GetDevice()->LightEnable( 0, true );
  95.     g_engine->GetDevice()->SetRenderState( D3DRS_SPECULARENABLE, true );
  96.  
  97.     // Set up the fog.
  98.     float density = *script->GetFloatData( "fog_density" ) * m_scale;
  99.     g_engine->GetDevice()->SetRenderState( D3DRS_FOGENABLE, true );
  100.     g_engine->GetDevice()->SetRenderState( D3DRS_FOGCOLOR, D3DCOLOR_COLORVALUE( script->GetColourData( "fog_colour" )->r, script->GetColourData( "fog_colour" )->g, script->GetColourData( "fog_colour" )->b, script->GetColourData( "fog_colour" )->a ) );
  101.     g_engine->GetDevice()->SetRenderState( D3DRS_FOGVERTEXMODE, D3DFOG_EXP2 );
  102.     g_engine->GetDevice()->SetRenderState( D3DRS_FOGDENSITY, *(unsigned long*)&density );
  103.  
  104.     // Store the constraints used for creating the scene.
  105.     m_maxFaces = *script->GetNumberData( "max_faces" );
  106.     m_maxHalfSize = *script->GetFloatData( "max_half_size" );
  107.  
  108.     // Load the scene's mesh.
  109.     m_mesh = g_engine->GetMeshManager()->Add( script->GetStringData( "mesh" ), script->GetStringData( "mesh_path" ) );
  110.  
  111.     // Destory the scene's script as it is no longer needed.
  112.     SAFE_DELETE( script );
  113.  
  114.     // Set a new projection matrix so the view frustum will fit the scene.
  115.     D3DDISPLAYMODE *display;
  116.     display = g_engine->GetDisplayMode();
  117.     D3DXMATRIX projection;
  118.     D3DXMatrixPerspectiveFovLH( &projection, D3DX_PI / 4, (float)display->Width / (float)display->Height, 0.1f / m_scale, m_mesh->GetBoundingSphere()->radius * 2.0f );
  119.     g_engine->GetDevice()->SetTransform( D3DTS_PROJECTION, &projection );
  120.  
  121.     // Set the view frustum's projection matrix.
  122.     m_viewFrustum.SetProjectionMatrix( projection );
  123.  
  124.     // Create the list of render caches.
  125.     m_renderCaches = new LinkedList< RenderCache >;
  126.  
  127.     // Search the mesh for unique materials.
  128.     for( unsigned long m = 0; m < m_mesh->GetStaticMesh()->NumMaterials; m++ )
  129.     {
  130.         // Flag to determine if the material has been found already.
  131.         bool found = false;
  132.  
  133.         // Ensure the new material is valid.
  134.         if( m_mesh->GetStaticMesh()->materials[m] == NULL )
  135.             continue;
  136.  
  137.         // Iterate through the already existing render caches.
  138.         m_renderCaches->Iterate( true );
  139.         while( m_renderCaches->Iterate() )
  140.         {
  141.             // Check if the new material already exists.
  142.             if( m_renderCaches->GetCurrent()->GetMaterial() == m_mesh->GetStaticMesh()->materials[m] )
  143.             {
  144.                 found = true;
  145.                 break;
  146.             }
  147.         }
  148.  
  149.         // Add the material if it wasn't found and not set to ignore faces.
  150.         if( found == false && m_mesh->GetStaticMesh()->materials[m]->GetIgnoreFace() == false )
  151.             m_renderCaches->Add( new RenderCache( g_engine->GetDevice(), m_mesh->GetStaticMesh()->materials[m] ) );
  152.     }
  153.  
  154.     // Get a pointer to the mesh's frame list.
  155.     LinkedList< Frame > *frames = m_mesh->GetFrameList();
  156.  
  157.     // Iterate through the frame list.
  158.     frames->Iterate( true );
  159.     while( frames->Iterate() != NULL )
  160.     {
  161.         // Check if this frame contains an occluder.
  162.         if( strncmp( "oc_", frames->GetCurrent()->Name, 3 ) == 0 )
  163.         {
  164.             // If so, load the occluder, and continue to the next frame.
  165.             m_occludingObjects->Add( new SceneOccluder( frames->GetCurrent()->GetTranslation(), ( (MeshContainer*)frames->GetCurrent()->pMeshContainer )->originalMesh, &frames->GetCurrent()->finalTransformationMatrix ) );
  166.             continue;
  167.         }
  168.  
  169.         // Check if this frame is a spawn point.
  170.         if( strncmp( "sp_", frames->GetCurrent()->Name, 3 ) == 0 )
  171.         {
  172.             // Get the actual name of the spawner object at this spawn point.
  173.             char *firstDash = strpbrk( frames->GetCurrent()->Name, "_" );
  174.             firstDash++;
  175.             char *lastDash = strrchr( firstDash, '_' );
  176.             unsigned long length = lastDash - firstDash;
  177.             char *name = new char[length + 5];
  178.             ZeroMemory( name, sizeof( char ) * ( length + 5 ) );
  179.             strncpy( name, firstDash, length );
  180.             strcat( name, ".txt" );
  181.  
  182.             // Check if it is a player spawn point.
  183.             if( stricmp( name, "player.txt" ) == 0 )
  184.             {
  185.                 // Get the name of the player spawn point's radius frame.
  186.                 char *radiusName = new char[strlen( firstDash ) + 8];
  187.                 ZeroMemory( radiusName, sizeof( char ) * ( strlen( firstDash ) + 8 ) );
  188.                 strncpy( radiusName, firstDash, strlen( firstDash ) );
  189.                 strcat( radiusName, "_radius" );
  190.  
  191.                 // Find the player spawn point's radius frame.
  192.                 Frame *radiusFrame = frames->GetFirst();
  193.                 while( radiusFrame != NULL )
  194.                 {
  195.                     if( stricmp( radiusFrame->Name, radiusName ) == 0 )
  196.                         break;
  197.  
  198.                     radiusFrame = frames->GetNext( radiusFrame );
  199.                 }
  200.  
  201.                 // Destroy the string buffer for the radius frame's name.
  202.                 SAFE_DELETE_ARRAY( radiusName );
  203.  
  204.                 // Find the distance between the two points (the radius).
  205.                 float radius = 0.0f;
  206.                 if( radiusFrame != NULL )
  207.                     radius = D3DXVec3Length( &( radiusFrame->GetTranslation() - frames->GetCurrent()->GetTranslation() ) );
  208.  
  209.                 // Create the player spawn point.
  210.                 SceneObject *point = new SceneObject( NULL, NULL );
  211.                 point->SetTranslation( frames->GetCurrent()->GetTranslation() );
  212.                 point->SetBoundingSphere( D3DXVECTOR3( 0.0f, 0.0f, 0.0f ), radius );
  213.                 point->SetVisible( false );
  214.                 point->SetGhost( true );
  215.                 point->Update( 0.0f );
  216.                 m_dynamicObjects->Add( m_playerSpawnPoints->Add( point ) );
  217.             }
  218.             else
  219.             {
  220.                 // Create the application specific spawner object.
  221.                 SpawnerObject *spawner = new SpawnerObject( name, m_spawnerPath );
  222.                 spawner->SetTranslation( frames->GetCurrent()->GetTranslation() );
  223.                 spawner->Update( 0.0f );
  224.                 m_dynamicObjects->Add( m_objectSpawners->Add( spawner ) );
  225.             }
  226.  
  227.             // Destroy the string buffer used to create the spawner's name.
  228.             SAFE_DELETE_ARRAY( name );
  229.         }
  230.     }
  231.  
  232.     // A list of valid faces (those with a valid material).
  233.     bool *validFaces = new bool[m_mesh->GetStaticMesh()->originalMesh->GetNumFaces()];
  234.     ZeroMemory( validFaces, sizeof( bool ) * m_mesh->GetStaticMesh()->originalMesh->GetNumFaces() );
  235.  
  236.     // These are used for locking the vertex, index, and attribute buffers of
  237.     // the meshes. They act as pointers into the data returned by the locks.
  238.     Vertex *vertices = NULL;
  239.     unsigned short *indices = NULL;
  240.     unsigned long *attributes = NULL;
  241.  
  242.     // Lock the mesh's vertex, index, and attribute buffers.
  243.     m_mesh->GetStaticMesh()->originalMesh->LockVertexBuffer( D3DLOCK_READONLY, (void**)&vertices );
  244.     m_mesh->GetStaticMesh()->originalMesh->LockIndexBuffer( D3DLOCK_READONLY, (void**)&indices );
  245.     m_mesh->GetStaticMesh()->originalMesh->LockAttributeBuffer( D3DLOCK_READONLY, &attributes );
  246.  
  247.     // Count the faces in the scene that have a valid material.
  248.     for( unsigned long f = 0; f < m_mesh->GetStaticMesh()->originalMesh->GetNumFaces(); f++ )
  249.     {
  250.         m_renderCaches->Iterate( true );
  251.         while( m_renderCaches->Iterate() )
  252.         {
  253.             if( m_renderCaches->GetCurrent()->GetMaterial() == m_mesh->GetStaticMesh()->materials[attributes[f]] )
  254.             {
  255.                 m_totalFaces++;
  256.                 validFaces[f] = true;
  257.                 break;
  258.             }
  259.         }
  260.     }
  261.  
  262.     // Create the array of faces.
  263.     m_faces = new SceneFace[m_totalFaces];
  264.  
  265.     // Set the number of vertices.
  266.     m_totalVertices = m_totalFaces * 3;
  267.  
  268.     // Create the vertex buffer that will hold all the vertices in the scene.
  269.     g_engine->GetDevice()->CreateVertexBuffer( m_totalVertices * VERTEX_FVF_SIZE, D3DUSAGE_WRITEONLY, VERTEX_FVF, D3DPOOL_MANAGED, &m_sceneVertexBuffer, NULL );
  270.  
  271.     // Used for temporary storage of the final vertices that make the scene.
  272.     Vertex *tempVertices = new Vertex[m_totalVertices];
  273.  
  274.     // Lock the scene's vertex buffer
  275.     m_sceneVertexBuffer->Lock( 0, 0, (void**)&m_vertices, 0 );
  276.  
  277.     // Go through all of the valid faces in the mesh and store their details.
  278.     for( f = 0; f < m_totalFaces; f++ )
  279.     {
  280.         // Ensure this face is valid.
  281.         if( validFaces[f] == false )
  282.         {
  283.             // Advance the indices pointer to skip this face.
  284.             indices += 3;
  285.             continue;
  286.         }
  287.  
  288.         // Store the index pointer for each vertex in the face.
  289.         m_faces[f].vertex0 = *indices++;
  290.         m_faces[f].vertex1 = *indices++;
  291.         m_faces[f].vertex2 = *indices++;
  292.  
  293.         // Find the render cache this face belongs to.
  294.         m_renderCaches->Iterate( true );
  295.         while( m_renderCaches->Iterate() )
  296.         {
  297.             if( m_renderCaches->GetCurrent()->GetMaterial() == m_mesh->GetStaticMesh()->materials[attributes[f]] )
  298.             {
  299.                 m_faces[f].renderCache = m_renderCaches->GetCurrent();
  300.                 m_renderCaches->GetCurrent()->AddFace();
  301.                 break;
  302.             }
  303.         }
  304.  
  305.         // Take of temporary copy of these vertices.
  306.         tempVertices[m_faces[f].vertex0] = vertices[m_faces[f].vertex0];
  307.         tempVertices[m_faces[f].vertex1] = vertices[m_faces[f].vertex1];
  308.         tempVertices[m_faces[f].vertex2] = vertices[m_faces[f].vertex2];
  309.     }
  310.  
  311.     // Copy the final vertices (that make up the scene) into the vertex buffer.
  312.     memcpy( m_vertices, tempVertices, m_totalVertices * VERTEX_FVF_SIZE );
  313.  
  314.     // Unlock the scene's vertex buffer.
  315.     m_sceneVertexBuffer->Unlock();
  316.  
  317.     // Destroy the temporary vertices array.
  318.     SAFE_DELETE_ARRAY( tempVertices );
  319.  
  320.     // Unlock the mesh's vertex, index, and attribute buffers.
  321.     m_mesh->GetStaticMesh()->originalMesh->UnlockAttributeBuffer();
  322.     m_mesh->GetStaticMesh()->originalMesh->UnlockIndexBuffer();
  323.     m_mesh->GetStaticMesh()->originalMesh->UnlockVertexBuffer();
  324.  
  325.     // Destroy the array of valid faces.
  326.     SAFE_DELETE_ARRAY( validFaces );
  327.  
  328.     // Create the first scene leaf (the largest leaf that encloses the scene).
  329.     m_firstLeaf = new SceneLeaf();
  330.  
  331.     // Recursively build the scene, starting with the first leaf.
  332.     RecursiveSceneBuild( m_firstLeaf, m_mesh->GetBoundingSphere()->centre, m_mesh->GetBoundingBox()->halfSize );
  333.  
  334.     // Allow the render caches to prepare themselves.
  335.     m_renderCaches->Iterate( true );
  336.     while( m_renderCaches->Iterate() )
  337.         m_renderCaches->GetCurrent()->Prepare( m_totalVertices );
  338.  
  339.     // Indicate that the scene is now loaded.
  340.     m_loaded = true;
  341. }
  342.  
  343. //-----------------------------------------------------------------------------
  344. // Destroys the currently loaded scene.
  345. //-----------------------------------------------------------------------------
  346. void SceneManager::DestroyScene()
  347. {
  348.     // Destroy the array of polygons.
  349.     SAFE_DELETE_ARRAY( m_faces );
  350.     m_totalFaces = 0;
  351.  
  352.     // Destroy the list of render caches.
  353.     SAFE_DELETE( m_renderCaches );
  354.  
  355.     // Release the scene's vertex buffer.
  356.     SAFE_RELEASE( m_sceneVertexBuffer );
  357.     m_vertices = NULL;
  358.     m_totalVertices = 0;
  359.  
  360.     // Destroy the scene leaf hierarchy.
  361.     SAFE_DELETE( m_firstLeaf );
  362.  
  363.     // Destroy the object spawner list.
  364.     if( m_objectSpawners != NULL )
  365.         m_objectSpawners->ClearPointers();
  366.     SAFE_DELETE( m_objectSpawners );
  367.  
  368.     // Destroy the list of player spawn points.
  369.     if( m_playerSpawnPoints != NULL )
  370.         m_playerSpawnPoints->ClearPointers();
  371.     SAFE_DELETE( m_playerSpawnPoints );
  372.  
  373.     // Destroy the lists of occluding objects.
  374.     if( m_visibleOccluders != NULL )
  375.         m_visibleOccluders->ClearPointers();
  376.     SAFE_DELETE( m_visibleOccluders );
  377.     SAFE_DELETE( m_occludingObjects );
  378.  
  379.     // Empty the list of dynamic objects.
  380.     m_dynamicObjects->Empty();
  381.  
  382.     // Destroy the scene's mesh.
  383.     g_engine->GetMeshManager()->Remove( &m_mesh );
  384.  
  385.     // Destroy the scene name.
  386.     SAFE_DELETE_ARRAY( m_name );
  387.  
  388.     // Indicate that the scene is no longer loaded.
  389.     m_loaded = false;
  390. }
  391.  
  392. //-----------------------------------------------------------------------------
  393. // Returns true if the scene is loaded and ready for use.
  394. //-----------------------------------------------------------------------------
  395. bool SceneManager::IsLoaded()
  396. {
  397.     return m_loaded;
  398. }
  399.  
  400. //-----------------------------------------------------------------------------
  401. // Updates the scene and all the objects in it.
  402. //-----------------------------------------------------------------------------
  403. void SceneManager::Update( float elapsed, D3DXMATRIX *view )
  404. {
  405.     // Ensure a scene is loaded.
  406.     if( m_firstLeaf == NULL )
  407.         return;
  408.  
  409.     // Increment the frame stamp. Indicating the start of a new frame.
  410.     m_frameStamp++;
  411.  
  412.     // Update the view frustum.
  413.     m_viewFrustum.Update( view );
  414.  
  415.     // Go through all the dynamic object's and update them.
  416.     m_dynamicObjects->Iterate( true );
  417.     while( m_dynamicObjects->Iterate() )
  418.     {
  419.         // Ignore the object if it is not enabled.
  420.         if( m_dynamicObjects->GetCurrent()->GetEnabled() == false )
  421.             continue;
  422.  
  423.         // If this object is a ghost, then it cannot collided with anything.
  424.         // However, it still needs to be updated. Since objects receive their
  425.         // movement through the collision system, ghost objects will have to be
  426.         // allowed to update their movement manually.
  427.         if( m_dynamicObjects->GetCurrent()->GetGhost() == true )
  428.         {
  429.             m_dynamicObjects->GetCurrent()->Update( elapsed );
  430.             continue;
  431.         }
  432.  
  433.         // Objects without an ellipsoid radius cannot collide with anything.
  434.         if( m_dynamicObjects->GetCurrent()->GetEllipsoidRadius().x + m_dynamicObjects->GetCurrent()->GetEllipsoidRadius().y, m_dynamicObjects->GetCurrent()->GetEllipsoidRadius().z <= 0.0f )
  435.         {
  436.             m_dynamicObjects->GetCurrent()->Update( elapsed );
  437.             continue;
  438.         }
  439.  
  440.         // Build the collision data for this object.
  441.         static CollisionData collisionData;
  442.         collisionData.scale = m_scale;
  443.         collisionData.elapsed = elapsed;
  444.         collisionData.frameStamp = m_frameStamp;
  445.         collisionData.object = m_dynamicObjects->GetCurrent();
  446.         collisionData.gravity = m_gravity * elapsed;
  447.  
  448.         // Perform collision detection for this object.
  449.         PerformCollisionDetection( &collisionData, (Vertex*)m_vertices, m_faces, m_totalFaces, m_dynamicObjects );
  450.  
  451.         // Allow the object to update itself.
  452.         m_dynamicObjects->GetCurrent()->Update( elapsed, false );
  453.     }
  454. }
  455.  
  456. //-----------------------------------------------------------------------------
  457. // Renders the scene and all the objects in it.
  458. //-----------------------------------------------------------------------------
  459. void SceneManager::Render( float elapsed, D3DXVECTOR3 viewer )
  460. {
  461.     // Ensure a scene is loaded.
  462.     if( m_firstLeaf == NULL )
  463.         return;
  464.  
  465.     // Clear the list of visible occluders.
  466.     m_visibleOccluders->ClearPointers();
  467.  
  468.     // Begin the process of determining the visible leaves in the scene. The
  469.     // first step involves checking the scene leaves against the view frustum.
  470.     RecursiveSceneFrustumCheck( m_firstLeaf, viewer );
  471.  
  472.     // A list of potentially visible leaves and occluders has been determined
  473.     // after check against the view frustum. The next step is to go through and
  474.     // further refine the list of visible occluders through occlusion culling.
  475.     m_visibleOccluders->Iterate( true );
  476.     while( m_visibleOccluders->Iterate() )
  477.     {
  478.         // If the occluder's visible stamp does not not equal the current frame
  479.         // stamp then the occluder has been hidden somehow, so ignore it.
  480.         if( m_visibleOccluders->GetCurrent()->visibleStamp != m_frameStamp )
  481.             continue;
  482.  
  483.         // Build the occluder's occlusion volume.
  484.         BuildOcclusionVolume( m_visibleOccluders->GetCurrent(), viewer );
  485.  
  486.         // Iterate through the rest of the visible occluder's.
  487.         SceneOccluder *occludee = m_visibleOccluders->GetNext( m_visibleOccluders->GetCurrent() );
  488.         while( occludee != NULL )
  489.         {
  490.             // If the occludee's bounding sphere is overlapping the occluder's
  491.             // volume and the occludee's bounding box is completely enclosed by
  492.             // the occluder's volume, then the occludee is hidden.
  493.             if( IsSphereOverlappingVolume( m_visibleOccluders->GetCurrent()->planes, occludee->translation, occludee->GetBoundingSphere()->radius ) == true )
  494.                 if( IsBoxEnclosedByVolume( m_visibleOccluders->GetCurrent()->planes, occludee->GetBoundingBox()->min, occludee->GetBoundingBox()->max ) == true )
  495.                     occludee->visibleStamp--;
  496.  
  497.             occludee = m_visibleOccluders->GetNext( occludee );
  498.         }
  499.     }
  500.  
  501.     // Tell all the render caches to prepare for rendering.
  502.     m_renderCaches->Iterate( true );
  503.     while( m_renderCaches->Iterate() )
  504.         m_renderCaches->GetCurrent()->Begin();
  505.  
  506.     // Finally, check the scene's leaves against the visible occluders.
  507.     RecursiveSceneOcclusionCheck( m_firstLeaf );
  508.  
  509.     // Set an identity world transformation matrix to render around the origin.
  510.     D3DXMATRIX world;
  511.     D3DXMatrixIdentity( &world );
  512.     g_engine->GetDevice()->SetTransform( D3DTS_WORLD, &world );
  513.  
  514.     // Set the scene vertex buffer as the current device data stream.
  515.     g_engine->GetDevice()->SetStreamSource( 0, m_sceneVertexBuffer, 0, VERTEX_FVF_SIZE );
  516.     g_engine->GetDevice()->SetFVF( VERTEX_FVF );
  517.  
  518.     // Tell all the render caches to end rendering. This will cause them to
  519.     // send their faces to be render to the video card.
  520.     m_renderCaches->Iterate( true );
  521.     while( m_renderCaches->Iterate() )
  522.         m_renderCaches->GetCurrent()->End();
  523.  
  524.     // Iterate through the list of dynamic objects.
  525.     m_dynamicObjects->Iterate( true );
  526.     while( m_dynamicObjects->Iterate() )
  527.     {
  528.         // Check if the object is visible.
  529.         if( m_dynamicObjects->GetCurrent()->GetVisible() == false )
  530.             continue;
  531.  
  532.         // Check if the object's bounding sphere is inside the view frustum.
  533.         if( m_viewFrustum.IsSphereInside( m_dynamicObjects->GetCurrent()->GetBoundingSphere()->centre, m_dynamicObjects->GetCurrent()->GetBoundingSphere()->radius ) == false )
  534.             continue;
  535.  
  536.         // Iterate through the list of visible occluders.
  537.         bool occluded = false;
  538.         m_visibleOccluders->Iterate( true );
  539.         while( m_visibleOccluders->Iterate() )
  540.         {
  541.             // Ignore hidden occluders.
  542.             if( m_visibleOccluders->GetCurrent()->visibleStamp != m_frameStamp )
  543.                 continue;
  544.  
  545.             occluded = true;
  546.  
  547.             // Check the object's bounding sphere against the occlusion volume.
  548.             m_visibleOccluders->GetCurrent()->planes->Iterate( true );
  549.             while( m_visibleOccluders->GetCurrent()->planes->Iterate() )
  550.             {
  551.                 if( D3DXPlaneDotCoord( m_visibleOccluders->GetCurrent()->planes->GetCurrent(), &m_dynamicObjects->GetCurrent()->GetBoundingSphere()->centre ) < m_dynamicObjects->GetCurrent()->GetBoundingSphere()->radius )
  552.                 {
  553.                     occluded = false;
  554.                     break;
  555.                 }
  556.             }
  557.  
  558.             // Break if the object is completely hidden by this occluder.
  559.             if( occluded == true )
  560.                 break;
  561.         }
  562.  
  563.         // Ignore this object if it is occluded.
  564.         if( occluded == true )
  565.             continue;
  566.  
  567.         // Render the object.
  568.         m_dynamicObjects->GetCurrent()->Render();
  569.     }
  570. }
  571.  
  572. //-----------------------------------------------------------------------------
  573. // Adds the given object to the scene.
  574. //-----------------------------------------------------------------------------
  575. SceneObject *SceneManager::AddObject( SceneObject *object )
  576. {
  577.     return m_dynamicObjects->Add( object );
  578. }
  579.  
  580. //-----------------------------------------------------------------------------
  581. // Removes the given object from the scene.
  582. //-----------------------------------------------------------------------------
  583. void SceneManager::RemoveObject( SceneObject **object )
  584. {
  585.     m_dynamicObjects->ClearPointer( object );
  586. }
  587.  
  588. //-----------------------------------------------------------------------------
  589. // Returns a random player spawnpoint.
  590. //-----------------------------------------------------------------------------
  591. SceneObject *SceneManager::GetRandomPlayerSpawnPoint()
  592. {
  593.     // Get a random spawn point.
  594.     SceneObject *point = m_playerSpawnPoints->GetRandom();
  595.  
  596.     // If the spawner's collision stamp equals the current frame stamp, then
  597.     // something has collided with the spawner. Alternatively this spawner may
  598.     // not be enabled. In either case, return NULL to indicate that a vacent
  599.     // spawn point was not found. The caller will have to try again later.
  600.     if( point->GetCollisionStamp() != m_frameStamp && point->GetEnabled() == true )
  601.         return point;
  602.     else
  603.         return NULL;
  604. }
  605.  
  606. //-----------------------------------------------------------------------------
  607. // Returns the spawn point with the given ID (position in the linked list).
  608. //-----------------------------------------------------------------------------
  609. SceneObject *SceneManager::GetSpawnPointByID( long id )
  610. {
  611.     SceneObject *point = NULL;
  612.  
  613.     // Ensure the ID is in range.
  614.     if( id < (long)m_playerSpawnPoints->GetTotalElements() )
  615.     {
  616.         // Loop through the player spawn point list until the id is reached.
  617.         point = m_playerSpawnPoints->GetFirst();
  618.         for( long i = 0; i < id; i++ )
  619.             point = m_playerSpawnPoints->GetNext( point );
  620.     }
  621.  
  622.     return point;
  623. }
  624.  
  625. //-----------------------------------------------------------------------------
  626. // Returns the ID (position in the linked list) of the given spawn point.
  627. //-----------------------------------------------------------------------------
  628. long SceneManager::GetSpawnPointID( SceneObject *point )
  629. {
  630.     // Ensure the given spawn point is valid.
  631.     if( point == NULL )
  632.         return -1;
  633.  
  634.     long id = 0;
  635.  
  636.     // Iterate the player spawn point list looking for the given spawn point.
  637.     m_playerSpawnPoints->Iterate( true );
  638.     while( m_playerSpawnPoints->Iterate() != NULL )
  639.     {
  640.         if( m_playerSpawnPoints->GetCurrent() == point )
  641.             break;
  642.  
  643.         id++;
  644.     }
  645.  
  646.     // If the ID equals the total elements, then the spawn point was not found.
  647.     if( id == (long)m_playerSpawnPoints->GetTotalElements() )
  648.         id = -1;
  649.  
  650.     return id;
  651. }
  652.  
  653. //-----------------------------------------------------------------------------
  654. // Returns the list of object spawners in the scene.
  655. //-----------------------------------------------------------------------------
  656. LinkedList< SpawnerObject > *SceneManager::GetSpawnerObjectList()
  657. {
  658.     return m_objectSpawners;
  659. }
  660.  
  661. //-----------------------------------------------------------------------------
  662. // Returns the result of a ray intersection with the scene and all its objects.
  663. //-----------------------------------------------------------------------------
  664. bool SceneManager::RayIntersectScene( RayIntersectionResult *result, D3DXVECTOR3 rayPosition, D3DXVECTOR3 rayDirection, bool checkScene, SceneObject *thisObject, bool checkObjects )
  665. {
  666.     float hitDistance = 0.0f;
  667.  
  668.     // Check if the ray needs to check for intersection with the scene.
  669.     if( checkScene == true )
  670.     {
  671.         // Go through all the faces in the scene, check for intersection.
  672.         for( unsigned long f = 0; f < m_totalFaces; f++ )
  673.         {
  674.             // Skip this face if its material is set to ignore rays.
  675.             if( m_faces[f].renderCache->GetMaterial()->GetIgnoreRay() == true )
  676.                 continue;
  677.  
  678.             // Check the ray against this face.
  679.             if( D3DXIntersectTri( (D3DXVECTOR3*)&m_vertices[m_faces[f].vertex0], (D3DXVECTOR3*)&m_vertices[m_faces[f].vertex1], (D3DXVECTOR3*)&m_vertices[m_faces[f].vertex2], &rayPosition, &rayDirection, NULL, NULL, &hitDistance ) == TRUE )
  680.             {
  681.                 if( hitDistance < result->distance || result->material == NULL )
  682.                 {
  683.                     ( *result ).distance = hitDistance;
  684.                     ( *result ).material = m_faces[f].renderCache->GetMaterial();
  685.                 }
  686.             }
  687.         }
  688.     }
  689.  
  690.     // Check if the ray needs to check for intersection with the objects.
  691.     if( checkObjects == true )
  692.     {
  693.         // Stores the ray in model space.
  694.         D3DXVECTOR3 rp, rd;
  695.  
  696.         // Iterate all the objects in the scene, check for intersection.
  697.         SceneObject *object = m_dynamicObjects->GetFirst();
  698.         while( object != NULL )
  699.         {
  700.             // Only check this object if it is enabled, has a mesh and is not
  701.             // the calling object.
  702.             if( object->GetEnabled() == true && object->GetMesh() != NULL && object != thisObject )
  703.             {
  704.                 // Transform the ray into model space.
  705.                 D3DXMATRIX inverse;
  706.                 D3DXMatrixInverse( &inverse, NULL, object->GetWorldMatrix() );
  707.                 D3DXVec3TransformCoord( &rp, &rayPosition, &inverse );
  708.                 D3DXVec3TransformNormal( &rd, &rayDirection, &inverse );
  709.  
  710.                 // Go through the list of frames in the object's mesh.
  711.                 LinkedList< Frame > *frames = object->GetMesh()->GetFrameList();
  712.                 frames->Iterate( true );
  713.                 while( frames->Iterate() != NULL )
  714.                 {
  715.                     // Ignore this frame if it has no mesh.
  716.                     if( frames->GetCurrent()->pMeshContainer == NULL )
  717.                         continue;
  718.  
  719.                     // Check the ray against this frame's mesh.
  720.                     BOOL hit;
  721.                     D3DXIntersect( frames->GetCurrent()->pMeshContainer->MeshData.pMesh, &rp, &rd, &hit, NULL, NULL, NULL, &hitDistance, NULL, NULL );
  722.                     if( hit == TRUE && ( hitDistance < result->distance || result->material == NULL ) )
  723.                     {
  724.                         ( *result ).distance = hitDistance;
  725.                         ( *result ).material = object->GetMesh()->GetStaticMesh()->materials[0];
  726.                         ( *result ).hitObject = object;
  727.                     }
  728.                 }
  729.             }
  730.  
  731.             // Go to the next object.
  732.             object = m_dynamicObjects->GetNext( object );
  733.         }
  734.     }
  735.  
  736.     // Return false if no intersection occured.
  737.     if( result->material == NULL )
  738.         return false;
  739.  
  740.     // Calculate the point of intersection.
  741.     ( *result ).point = rayPosition + rayDirection * result->distance;
  742.  
  743.     return true;
  744. }
  745.  
  746. //-----------------------------------------------------------------------------
  747. // Builds an occlusion volume for the given occluder.
  748. //-----------------------------------------------------------------------------
  749. void SceneManager::BuildOcclusionVolume( SceneOccluder *occluder, D3DXVECTOR3 viewer )
  750. {
  751.     // Create a list of edges for the occluder's silhouette.
  752.     LinkedList< Edge > *edges = new LinkedList< Edge >;
  753.  
  754.     // Go through all the faces in the occluder's mesh.
  755.     for( unsigned long f = 0; f < occluder->totalFaces; f++ )
  756.     {
  757.         // Get the indices of this face.
  758.         unsigned short index0 = occluder->indices[3 * f + 0];
  759.         unsigned short index1 = occluder->indices[3 * f + 1];
  760.         unsigned short index2 = occluder->indices[3 * f + 2];
  761.  
  762.         // Find the angle between the face's normal and the vector point from
  763.         // viewer's position to the face's position. If the angle is less than
  764.         // 0, then the face is visible to the viewer.
  765.         if( D3DXVec3Dot( &occluder->vertices[index0].normal, &( occluder->vertices[index0].translation - viewer ) ) < 0.0f )
  766.         {
  767.             // Check if the list of edges is empty.
  768.             if( edges->GetTotalElements() == 0 )
  769.             {
  770.                 // Add all the edges for this face.
  771.                 edges->Add( new Edge( &occluder->vertices[index0], &occluder->vertices[index1] ) );
  772.                 edges->Add( new Edge( &occluder->vertices[index1], &occluder->vertices[index2] ) );
  773.                 edges->Add( new Edge( &occluder->vertices[index2], &occluder->vertices[index0] ) );
  774.             }
  775.             else
  776.             {
  777.                 Edge *found0 = NULL;
  778.                 Edge *found1 = NULL;
  779.                 Edge *found2 = NULL;
  780.  
  781.                 // Iterate through the list of edges.
  782.                 edges->Iterate( true );
  783.                 while( edges->Iterate() != NULL )
  784.                 {
  785.                     // Check if the first edge of this face already exists.
  786.                     if( ( edges->GetCurrent()->vertex0->translation == occluder->vertices[index0].translation && edges->GetCurrent()->vertex1->translation == occluder->vertices[index1].translation ) ||
  787.                         ( edges->GetCurrent()->vertex0->translation == occluder->vertices[index1].translation && edges->GetCurrent()->vertex1->translation == occluder->vertices[index0].translation ) )
  788.                         found0 = edges->GetCurrent();
  789.  
  790.                     // Check if the second edge of this face already exists.
  791.                     if( ( edges->GetCurrent()->vertex0->translation == occluder->vertices[index1].translation && edges->GetCurrent()->vertex1->translation == occluder->vertices[index2].translation ) ||
  792.                         ( edges->GetCurrent()->vertex0->translation == occluder->vertices[index2].translation && edges->GetCurrent()->vertex1->translation == occluder->vertices[index1].translation ) )
  793.                         found1 = edges->GetCurrent();
  794.  
  795.                     // Check if the third edge of this face already exists.
  796.                     if( ( edges->GetCurrent()->vertex0->translation == occluder->vertices[index2].translation && edges->GetCurrent()->vertex1->translation == occluder->vertices[index0].translation ) ||
  797.                         ( edges->GetCurrent()->vertex0->translation == occluder->vertices[index0].translation && edges->GetCurrent()->vertex1->translation == occluder->vertices[index2].translation ) )
  798.                         found2 = edges->GetCurrent();
  799.                 }
  800.  
  801.                 // If the first edge was found, remove it. Otherwise add it.
  802.                 if( found0 != NULL )
  803.                     edges->Remove( &found0 );
  804.                 else
  805.                     edges->Add( new Edge( &occluder->vertices[index0], &occluder->vertices[index1] ) );
  806.  
  807.                 // If the second edge was found, remove it. Otherwise add it.
  808.                 if( found1 != NULL )
  809.                     edges->Remove( &found1 );
  810.                 else
  811.                     edges->Add( new Edge( &occluder->vertices[index1], &occluder->vertices[index2] ) );
  812.  
  813.                 // If the thrid edge was found, remove it. Otherwise add it.
  814.                 if( found2 != NULL )
  815.                     edges->Remove( &found2 );
  816.                 else
  817.                     edges->Add( new Edge( &occluder->vertices[index2], &occluder->vertices[index0] ) );
  818.             }
  819.         }
  820.     }
  821.  
  822.     // Empty the occluder's list of planes.
  823.     occluder->planes->Empty();
  824.  
  825.     // Create the front cap plane.
  826.     D3DXPLANE *plane = new D3DXPLANE;
  827.     D3DXPlaneFromPointNormal( plane, &occluder->translation, &( occluder->translation - viewer ) );
  828.     occluder->planes->Add( plane );
  829.  
  830.     // Iterate through the list of edges.
  831.     edges->Iterate( true );
  832.     while( edges->Iterate() != NULL )
  833.     {
  834.         // Get the position of the vertices in the edge.
  835.         D3DXVECTOR3 vertex1 = edges->GetCurrent()->vertex0->translation;
  836.         D3DXVECTOR3 vertex2 = edges->GetCurrent()->vertex1->translation;
  837.  
  838.         // Calculate the position of the thrid vertex for creating the plane.
  839.         D3DXVECTOR3 dir = vertex1 - viewer;
  840.         D3DXVec3Normalize( &dir, &dir );
  841.         D3DXVECTOR3 vertex3 = vertex1 + dir;
  842.  
  843.         // Create a plane from this edge.
  844.         plane = new D3DXPLANE;
  845.         D3DXPlaneFromPoints( plane, &vertex1, &vertex2, &vertex3 );
  846.         occluder->planes->Add( plane );
  847.     }
  848.  
  849.     // Destroy the list of edges.
  850.     SAFE_DELETE( edges );
  851. }
  852.  
  853. //-----------------------------------------------------------------------------
  854. // Recursively builds the scene.
  855. //-----------------------------------------------------------------------------
  856. void SceneManager::RecursiveSceneBuild( SceneLeaf *leaf, D3DXVECTOR3 translation, float halfSize )
  857. {
  858.     // Build a bounding volume around this leaf.
  859.     leaf->SetBoundingBox( D3DXVECTOR3( translation.x - halfSize, translation.y - halfSize, translation.z - halfSize ), D3DXVECTOR3( translation.x + halfSize, translation.y + halfSize, translation.z + halfSize ) );
  860.     leaf->SetBoundingSphere( translation, (float)sqrt( halfSize * halfSize + halfSize * halfSize + halfSize * halfSize ) );
  861.  
  862.     // Count the number of face in this leaf.
  863.     unsigned long totalFaces = 0;
  864.     for( unsigned long f = 0; f < m_totalFaces; f++ )
  865.         if( IsFaceInBox( &m_vertices[m_faces[f].vertex0], &m_vertices[m_faces[f].vertex1], &m_vertices[m_faces[f].vertex2], leaf->GetBoundingBox()->min, leaf->GetBoundingBox()->max ) == true )
  866.             totalFaces++;
  867.  
  868.     // Only divide the leaf up if it is too big and contains too many faces.
  869.     if( halfSize > m_maxHalfSize && totalFaces > m_maxFaces )
  870.     {
  871.         // Go through all the child leaves.
  872.         for( char c = 0; c < 8; c++ )
  873.         {
  874.             D3DXVECTOR3 newTranslation, newMin, newMax;
  875.             float newHalfSize = halfSize / 2.0f;
  876.             float mod;
  877.  
  878.             // Calculate the translation of the new leaf on the x axis.
  879.             mod = 1.0f;
  880.             if( c % 2 < 1 )
  881.                 mod = -1.0f;
  882.             newTranslation.x = translation.x + newHalfSize * mod;
  883.  
  884.             // Calculate the translation of the new leaf on the y axis.
  885.             mod = 1.0f;
  886.             if( c % 4 < 2 )
  887.                 mod = -1.0f;
  888.             newTranslation.y = translation.y + newHalfSize * mod;
  889.  
  890.             // Calculate the translation of the new leaf on the z axis.
  891.             mod = 1.0f;
  892.             if( c % 8 < 4 )
  893.                 mod = -1.0f;
  894.             newTranslation.z = translation.z + newHalfSize * mod;
  895.  
  896.             // Calculate the bounding box around the new leaf.
  897.             newMin = D3DXVECTOR3( newTranslation.x - newHalfSize, newTranslation.y - newHalfSize, newTranslation.z - newHalfSize );
  898.             newMax = D3DXVECTOR3( newTranslation.x + newHalfSize, newTranslation.y + newHalfSize, newTranslation.z + newHalfSize );
  899.  
  900.             // Check if the new scene leaf will have at least one face in it.
  901.             for( f = 0; f < m_totalFaces; f++ )
  902.             {
  903.                 if( IsFaceInBox( &m_vertices[m_faces[f].vertex0], &m_vertices[m_faces[f].vertex1], &m_vertices[m_faces[f].vertex2], newMin, newMax ) == true )
  904.                 {
  905.                     // A face has been found that is inside the new child scene
  906.                     // leaf, so create the scene leaf. Then recurse through the
  907.                     // scene leaf's branch of the scene hierarchy.
  908.                     leaf->children[c] = new SceneLeaf;
  909.                     RecursiveSceneBuild( leaf->children[c], newTranslation, halfSize / 2.0f );
  910.  
  911.                     break;
  912.                 }
  913.             }
  914.         }
  915.  
  916.         return;
  917.     }
  918.  
  919.     // Create the leaf's array of face indices.
  920.     leaf->totalFaces = totalFaces;
  921.     leaf->faces = new unsigned long[totalFaces];
  922.  
  923.     // If any face is contained in the leaf's bounding box, then store
  924.     // the index of the face in the leaf's face index array.
  925.     totalFaces = 0;
  926.     for( f = 0; f < m_totalFaces; f++ )
  927.         if( IsFaceInBox( &m_vertices[m_faces[f].vertex0], &m_vertices[m_faces[f].vertex1], &m_vertices[m_faces[f].vertex2], leaf->GetBoundingBox()->min, leaf->GetBoundingBox()->max ) == true )
  928.             leaf->faces[totalFaces++] = f;
  929.  
  930.     // Store pointers to any occluding objects in the leaf.
  931.     m_occludingObjects->Iterate( true );
  932.     while( m_occludingObjects->Iterate() )
  933.         if( IsBoxInBox( m_occludingObjects->GetCurrent()->GetBoundingBox()->min, m_occludingObjects->GetCurrent()->GetBoundingBox()->max, leaf->GetBoundingBox()->min, leaf->GetBoundingBox()->max ) == true )
  934.             leaf->occluders->Add( m_occludingObjects->GetCurrent() );
  935. }
  936.  
  937. //-----------------------------------------------------------------------------
  938. // Recursively checks the scene's leaves against the view frustum.
  939. //-----------------------------------------------------------------------------
  940. bool SceneManager::RecursiveSceneFrustumCheck( SceneLeaf *leaf, D3DXVECTOR3 viewer )
  941. {
  942.     // Check if the leaf's bounding sphere is inside the view frustum.
  943.     if( m_viewFrustum.IsSphereInside( leaf->GetBoundingSphere()->centre, leaf->GetBoundingSphere()->radius ) == false )
  944.         return false;
  945.  
  946.     // Check if the leaf's bounding box is inside the view frustum.
  947.     if( m_viewFrustum.IsBoxInside( leaf->GetBoundingBox()->min, leaf->GetBoundingBox()->max ) == false )
  948.         return false;
  949.  
  950.     // Set the visible stamp on this leaf to the current frame stamp. This will
  951.     // indicate that the leaf may be visible this frame and may need rendering.
  952.     leaf->visibleStamp = m_frameStamp;
  953.  
  954.     // Check if any of this leaf's children are visible.
  955.     char visibleChildren = 0;
  956.     for( char c = 0; c < 8; c++ )
  957.         if( leaf->children[c] != NULL )
  958.             if( RecursiveSceneFrustumCheck( leaf->children[c], viewer ) )
  959.                 visibleChildren++;
  960.  
  961.     // If this leaf has visible children then this branch of the scene can go
  962.     // deeper. So ignore this leaf.
  963.     if( visibleChildren > 0 )
  964.         return false;
  965.  
  966.     // Iterate through all the occluders in this leaf.
  967.     leaf->occluders->Iterate( true );
  968.     while( leaf->occluders->Iterate() )
  969.     {
  970.         // Check if the occluder's bounding sphere is inside the view frustum.
  971.         if( m_viewFrustum.IsSphereInside( leaf->occluders->GetCurrent()->translation, leaf->occluders->GetCurrent()->GetBoundingSphere()->radius ) == false )
  972.             continue;
  973.  
  974.         // Check if the occluder's bounding box is inside the view frustum.
  975.         if( m_viewFrustum.IsBoxInside( leaf->occluders->GetCurrent()->GetBoundingBox()->min, leaf->occluders->GetCurrent()->GetBoundingBox()->max ) == false )
  976.             continue;
  977.  
  978.         // Calculate the distance between the occluder and the viewer.
  979.         leaf->occluders->GetCurrent()->distance = D3DXVec3Length( &( leaf->occluders->GetCurrent()->translation - viewer ) );
  980.  
  981.         // Iterate through the list of visible occluders.
  982.         m_visibleOccluders->Iterate( true );
  983.         while( m_visibleOccluders->Iterate() )
  984.         {
  985.             // If the new occluder is already in the list, don't add it agian.
  986.             if( leaf->occluders->GetCurrent() == m_visibleOccluders->GetCurrent() )
  987.                 break;
  988.  
  989.             // If the new occluder is closer to the viewer than this occluder,
  990.             // then add it to the list before this occluder.
  991.             if( leaf->occluders->GetCurrent()->distance < m_visibleOccluders->GetCurrent()->distance )
  992.             {
  993.                 m_visibleOccluders->InsertBefore( leaf->occluders->GetCurrent(), m_visibleOccluders->GetCompleteElement( m_visibleOccluders->GetCurrent() ) );
  994.                 leaf->occluders->GetCurrent()->visibleStamp = m_frameStamp;
  995.                 break;
  996.             }
  997.         }
  998.  
  999.         // If the occluder wasn't in the list or not added then add it now.
  1000.         if( leaf->occluders->GetCurrent()->visibleStamp != m_frameStamp )
  1001.         {
  1002.             m_visibleOccluders->Add( leaf->occluders->GetCurrent() );
  1003.             leaf->occluders->GetCurrent()->visibleStamp = m_frameStamp;
  1004.         }
  1005.     }
  1006.  
  1007.     return true;
  1008. }
  1009.  
  1010. //-----------------------------------------------------------------------------
  1011. // Recursively checks the scene's leaves against the occlusion volumes.
  1012. //-----------------------------------------------------------------------------
  1013. void SceneManager::RecursiveSceneOcclusionCheck( SceneLeaf *leaf )
  1014. {
  1015.     // Ignore the leaf if it is not visible this frame.
  1016.     if( leaf->visibleStamp != m_frameStamp )
  1017.         return;
  1018.  
  1019.     // Iterate through the list of visible occluders.
  1020.     m_visibleOccluders->Iterate( true );
  1021.     while( m_visibleOccluders->Iterate() )
  1022.     {
  1023.         // Ignore hidden occluders.
  1024.         if( m_visibleOccluders->GetCurrent()->visibleStamp != m_frameStamp )
  1025.             continue;
  1026.  
  1027.         // If the leaf's bounding sphere is overlapping the occluder's volume
  1028.         // and the leaf's bounding box is completely enclosed by the occluder's
  1029.         // volume, then the leaf is hidden, so ignore it.
  1030.         if( IsSphereOverlappingVolume( m_visibleOccluders->GetCurrent()->planes, leaf->GetBoundingSphere()->centre, leaf->GetBoundingSphere()->radius ) == true )
  1031.             if( IsBoxEnclosedByVolume( m_visibleOccluders->GetCurrent()->planes, leaf->GetBoundingBox()->min, leaf->GetBoundingBox()->max ) == true )
  1032.                 return;
  1033.     }
  1034.  
  1035.     // Check if any of this leaf's children are visible.
  1036.     for( char c = 0; c < 8; c++ )
  1037.         if( leaf->children[c] != NULL )
  1038.             RecursiveSceneOcclusionCheck( leaf->children[c] );
  1039.  
  1040.     // Go through all the faces in the leaf.
  1041.     for( unsigned long f = 0; f < leaf->totalFaces; f++ )
  1042.     {
  1043.         // Check this face's render stamp. If it is equal to the current frame
  1044.         // stamp, then the face has already been rendered this frame.
  1045.         if( m_faces[leaf->faces[f]].renderStamp == m_frameStamp )
  1046.             continue;
  1047.  
  1048.         // Set the face's render stamp to indicate that it has been rendered.
  1049.         m_faces[leaf->faces[f]].renderStamp = m_frameStamp;
  1050.  
  1051.         // Tell the face's render cache to render this face.
  1052.         m_faces[leaf->faces[f]].renderCache->RenderFace( m_faces[leaf->faces[f]].vertex0, m_faces[leaf->faces[f]].vertex1, m_faces[leaf->faces[f]].vertex2 );
  1053.     }
  1054. }