Example #1
0
//-----------------------------------------------------------------------------
// Loads a new scene from the given scene file.
//-----------------------------------------------------------------------------
void SceneManager::LoadScene( char *name, char *path )
{
	// Create the lists of objects used in the scene. The dynamic object list
	// is persistent across scene changes so it doesn't need to be created.
	m_occludingObjects = new LinkedList< SceneOccluder >;
	m_visibleOccluders = new LinkedList< SceneOccluder >;
	m_playerSpawnPoints = new LinkedList< SceneObject >;
	m_objectSpawners = new LinkedList< SpawnerObject >;

	// Load the script for the scene.
	Script *script = new Script( name, path );

	// Store the name of the scene.
	m_name = new char[strlen( script->GetStringData( "name" ) ) + 1];
	memcpy( m_name, script->GetStringData( "name" ), ( strlen( script->GetStringData( "name" ) ) + 1 ) * sizeof( char ) );

	// Store the scene's gravity vector.
	m_gravity = *script->GetVectorData( "gravity" ) / m_scale;

	// Create the sun light source.
	D3DLIGHT9 sun;
	sun.Type = D3DLIGHT_DIRECTIONAL;
	sun.Diffuse.r = 1.0f;
	sun.Diffuse.g = 1.0f;
	sun.Diffuse.b = 1.0f;
	sun.Diffuse.a = 1.0f;
	sun.Specular = sun.Diffuse;
	sun.Ambient.r = script->GetColourData( "ambient_light" )->r;
	sun.Ambient.g = script->GetColourData( "ambient_light" )->g;
	sun.Ambient.b = script->GetColourData( "ambient_light" )->b;
	sun.Ambient.a = script->GetColourData( "ambient_light" )->a;
	sun.Direction = D3DXVECTOR3( script->GetVectorData( "sun_direction" )->x, script->GetVectorData( "sun_direction" )->y, script->GetVectorData( "sun_direction" )->z );
	sun.Range = 0.0f;

	// Switch lighting on, enable the sun light, and specular highlights.
	g_engine->GetDevice()->SetRenderState( D3DRS_LIGHTING, true );
	g_engine->GetDevice()->SetLight( 0, &sun );
	g_engine->GetDevice()->LightEnable( 0, true );
	g_engine->GetDevice()->SetRenderState( D3DRS_SPECULARENABLE, true );

	// Set up the fog.
	float density = *script->GetFloatData( "fog_density" ) * m_scale;
	g_engine->GetDevice()->SetRenderState( D3DRS_FOGENABLE, true );
	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 ) );
	g_engine->GetDevice()->SetRenderState( D3DRS_FOGVERTEXMODE, D3DFOG_EXP2 );
	g_engine->GetDevice()->SetRenderState( D3DRS_FOGDENSITY, *(unsigned long*)&density );

	// Store the constraints used for creating the scene.
	m_maxFaces = *script->GetNumberData( "max_faces" );
	m_maxHalfSize = *script->GetFloatData( "max_half_size" );

	// Load the scene's mesh.
	m_mesh = g_engine->GetMeshManager()->Add( script->GetStringData( "mesh" ), script->GetStringData( "mesh_path" ) );

	// Destory the scene's script as it is no longer needed.
	SAFE_DELETE( script );

	// Set a new projection matrix so the view frustum will fit the scene.
	D3DDISPLAYMODE *display;
	display = g_engine->GetDisplayMode();
	D3DXMATRIX projection;
	D3DXMatrixPerspectiveFovLH( &projection, D3DX_PI / 4, (float)display->Width / (float)display->Height, 0.1f / m_scale, m_mesh->GetBoundingSphere()->radius * 2.0f );
	g_engine->GetDevice()->SetTransform( D3DTS_PROJECTION, &projection );

	// Set the view frustum's projection matrix.
	m_viewFrustum.SetProjectionMatrix( projection );

	// Create the list of render caches.
	m_renderCaches = new LinkedList< RenderCache >;

	// Search the mesh for unique materials.
	for( unsigned long m = 0; m < m_mesh->GetStaticMesh()->NumMaterials; m++ )
	{
		// Flag to determine if the material has been found already.
		bool found = false;

		// Ensure the new material is valid.
		if( m_mesh->GetStaticMesh()->materials[m] == NULL )
			continue;

		// Iterate through the already existing render caches.
		m_renderCaches->Iterate( true );
		while( m_renderCaches->Iterate() )
		{
			// Check if the new material already exists.
			if( m_renderCaches->GetCurrent()->GetMaterial() == m_mesh->GetStaticMesh()->materials[m] )
			{
				found = true;
				break;
			}
		}

		// Add the material if it wasn't found and not set to ignore faces.
		if( found == false && m_mesh->GetStaticMesh()->materials[m]->GetIgnoreFace() == false )
			m_renderCaches->Add( new RenderCache( g_engine->GetDevice(), m_mesh->GetStaticMesh()->materials[m] ) );
	}

	// Get a pointer to the mesh's frame list.
	LinkedList< Frame > *frames = m_mesh->GetFrameList();

	// Iterate through the frame list.
	frames->Iterate( true );
	while( frames->Iterate() != NULL )
	{
		// Check if this frame contains an occluder.
		if( strncmp( "oc_", frames->GetCurrent()->Name, 3 ) == 0 )
		{
			// If so, load the occluder, and continue to the next frame.
			m_occludingObjects->Add( new SceneOccluder( frames->GetCurrent()->GetTranslation(), ( (MeshContainer*)frames->GetCurrent()->pMeshContainer )->originalMesh, &frames->GetCurrent()->finalTransformationMatrix ) );
			continue;
		}

		// Check if this frame is a spawn point.
		if( strncmp( "sp_", frames->GetCurrent()->Name, 3 ) == 0 )
		{
			// Get the actual name of the spawner object at this spawn point.
			char *firstDash = strpbrk( frames->GetCurrent()->Name, "_" );
			firstDash++;
			char *lastDash = strrchr( firstDash, '_' );
			unsigned long length = lastDash - firstDash;
			char *name = new char[length + 5];
			ZeroMemory( name, sizeof( char ) * ( length + 5 ) );
			strncpy( name, firstDash, length );
			strcat( name, ".txt" );

			// Check if it is a player spawn point.
			if( stricmp( name, "player.txt" ) == 0 )
			{
				// Get the name of the player spawn point's radius frame.
				char *radiusName = new char[strlen( firstDash ) + 8];
				ZeroMemory( radiusName, sizeof( char ) * ( strlen( firstDash ) + 8 ) );
				strncpy( radiusName, firstDash, strlen( firstDash ) );
				strcat( radiusName, "_radius" );

				// Find the player spawn point's radius frame.
				Frame *radiusFrame = frames->GetFirst();
				while( radiusFrame != NULL )
				{
					if( stricmp( radiusFrame->Name, radiusName ) == 0 )
						break;

					radiusFrame = frames->GetNext( radiusFrame );
				}

				// Destroy the string buffer for the radius frame's name.
				SAFE_DELETE_ARRAY( radiusName );

				// Find the distance between the two points (the radius).
				float radius = 0.0f;
				if( radiusFrame != NULL )
					radius = D3DXVec3Length( &( radiusFrame->GetTranslation() - frames->GetCurrent()->GetTranslation() ) );

				// Create the player spawn point.
				SceneObject *point = new SceneObject( NULL, NULL );
				point->SetTranslation( frames->GetCurrent()->GetTranslation() );
				point->SetBoundingSphere( D3DXVECTOR3( 0.0f, 0.0f, 0.0f ), radius );
				point->SetVisible( false );
				point->SetGhost( true );
				point->Update( 0.0f );
				m_dynamicObjects->Add( m_playerSpawnPoints->Add( point ) );
			}
			else
			{
				// Create the application specific spawner object.
				SpawnerObject *spawner = new SpawnerObject( name, m_spawnerPath );
				spawner->SetTranslation( frames->GetCurrent()->GetTranslation() );
				spawner->Update( 0.0f );
				m_dynamicObjects->Add( m_objectSpawners->Add( spawner ) );
			}

			// Destroy the string buffer used to create the spawner's name.
			SAFE_DELETE_ARRAY( name );
		}
	}

	// A list of valid faces (those with a valid material).
	bool *validFaces = new bool[m_mesh->GetStaticMesh()->originalMesh->GetNumFaces()];
	ZeroMemory( validFaces, sizeof( bool ) * m_mesh->GetStaticMesh()->originalMesh->GetNumFaces() );

	// These are used for locking the vertex, index, and attribute buffers of
	// the meshes. They act as pointers into the data returned by the locks.
	Vertex *vertices = NULL;
	unsigned short *indices = NULL;
	unsigned long *attributes = NULL;

	// Lock the mesh's vertex, index, and attribute buffers.
	m_mesh->GetStaticMesh()->originalMesh->LockVertexBuffer( D3DLOCK_READONLY, (void**)&vertices );
	m_mesh->GetStaticMesh()->originalMesh->LockIndexBuffer( D3DLOCK_READONLY, (void**)&indices );
	m_mesh->GetStaticMesh()->originalMesh->LockAttributeBuffer( D3DLOCK_READONLY, &attributes );

	// Count the faces in the scene that have a valid material.
	for( unsigned long f = 0; f < m_mesh->GetStaticMesh()->originalMesh->GetNumFaces(); f++ )
	{
		m_renderCaches->Iterate( true );
		while( m_renderCaches->Iterate() )
		{
			if( m_renderCaches->GetCurrent()->GetMaterial() == m_mesh->GetStaticMesh()->materials[attributes[f]] )
			{
				m_totalFaces++;
				validFaces[f] = true;
				break;
			}
		}
	}

	// Create the array of faces.
	m_faces = new SceneFace[m_totalFaces];

	// Set the number of vertices.
	m_totalVertices = m_totalFaces * 3;

	// Create the vertex buffer that will hold all the vertices in the scene.
	g_engine->GetDevice()->CreateVertexBuffer( m_totalVertices * VERTEX_FVF_SIZE, D3DUSAGE_WRITEONLY, VERTEX_FVF, D3DPOOL_MANAGED, &m_sceneVertexBuffer, NULL );

	// Used for temporary storage of the final vertices that make the scene.
	Vertex *tempVertices = new Vertex[m_totalVertices];

	// Lock the scene's vertex buffer
	m_sceneVertexBuffer->Lock( 0, 0, (void**)&m_vertices, 0 );

	// Go through all of the valid faces in the mesh and store their details.
	for( f = 0; f < m_totalFaces; f++ )
	{
		// Ensure this face is valid.
		if( validFaces[f] == false )
		{
			// Advance the indices pointer to skip this face.
			indices += 3;
			continue;
		}

		// Store the index pointer for each vertex in the face.
		m_faces[f].vertex0 = *indices++;
		m_faces[f].vertex1 = *indices++;
		m_faces[f].vertex2 = *indices++;

		// Find the render cache this face belongs to.
		m_renderCaches->Iterate( true );
		while( m_renderCaches->Iterate() )
		{
			if( m_renderCaches->GetCurrent()->GetMaterial() == m_mesh->GetStaticMesh()->materials[attributes[f]] )
			{
				m_faces[f].renderCache = m_renderCaches->GetCurrent();
				m_renderCaches->GetCurrent()->AddFace();
				break;
			}
		}

		// Take of temporary copy of these vertices.
		tempVertices[m_faces[f].vertex0] = vertices[m_faces[f].vertex0];
		tempVertices[m_faces[f].vertex1] = vertices[m_faces[f].vertex1];
		tempVertices[m_faces[f].vertex2] = vertices[m_faces[f].vertex2];
	}

	// Copy the final vertices (that make up the scene) into the vertex buffer.
	memcpy( m_vertices, tempVertices, m_totalVertices * VERTEX_FVF_SIZE );

	// Unlock the scene's vertex buffer.
	m_sceneVertexBuffer->Unlock();

	// Destroy the temporary vertices array.
	SAFE_DELETE_ARRAY( tempVertices );

	// Unlock the mesh's vertex, index, and attribute buffers.
	m_mesh->GetStaticMesh()->originalMesh->UnlockAttributeBuffer();
	m_mesh->GetStaticMesh()->originalMesh->UnlockIndexBuffer();
	m_mesh->GetStaticMesh()->originalMesh->UnlockVertexBuffer();

	// Destroy the array of valid faces.
	SAFE_DELETE_ARRAY( validFaces );

	// Create the first scene leaf (the largest leaf that encloses the scene).
	m_firstLeaf = new SceneLeaf();

	// Recursively build the scene, starting with the first leaf.
	RecursiveSceneBuild( m_firstLeaf, m_mesh->GetBoundingSphere()->centre, m_mesh->GetBoundingBox()->halfSize );

	// Allow the render caches to prepare themselves.
	m_renderCaches->Iterate( true );
	while( m_renderCaches->Iterate() )
		m_renderCaches->GetCurrent()->Prepare( m_totalVertices );

	// Indicate that the scene is now loaded.
	m_loaded = true;
}