예제 #1
0
//-----------------------------------------------------------------------------
// Computes lighting for the static props.
// Must be after all other surface lighting has been computed for the indirect sampling.
//-----------------------------------------------------------------------------
void CVradStaticPropMgr::ComputeLighting( int iThread )
{
	// illuminate them all
	int count = m_StaticProps.Count();
	if ( !count )
	{
		// nothing to do
		return;
	}

	StartPacifier( "Computing static prop lighting : " );

	// ensure any traces against us are ignored because we have no inherit lighting contribution
	m_bIgnoreStaticPropTrace = true;

	for (int i = 0; i < count; ++i)
	{
		UpdatePacifier( (float)i / (float)count );
		ComputeLighting( m_StaticProps[i], iThread, i );
	}

	// restore default
	m_bIgnoreStaticPropTrace = false;

	// save data to bsp
	SerializeLighting();

	EndPacifier( true );
}
예제 #2
0
//-----------------------------------------------------------------------------
// Computes lighting for the detail props
//-----------------------------------------------------------------------------
void ComputeDetailPropLighting( int iThread )
{
	// illuminate them all
	DetailObjectLump_t* pProps;
	int count = UnserializeDetailProps( pProps );
	if (!count)
		return;

	// unserialize the lump that we aren't computing.
	if( g_bHDR )
	{
		UnserializeDetailPropLighting( GAMELUMP_DETAIL_PROP_LIGHTING, GAMELUMP_DETAIL_PROP_LIGHTING_VERSION, s_DetailPropLightStyleLumpLDR );
	}
	else
	{
		UnserializeDetailPropLighting( GAMELUMP_DETAIL_PROP_LIGHTING_HDR, GAMELUMP_DETAIL_PROP_LIGHTING_HDR_VERSION, s_DetailPropLightStyleLumpHDR );
	}

	StartPacifier("Computing detail prop lighting : ");

	for (int i = 0; i < count; ++i)
	{
		UpdatePacifier( (float)i / (float)count );
		ComputeLighting( pProps[i], iThread );
	}

	// Write detail prop lightstyle lump...
	WriteDetailLightingLumps();
	EndPacifier( true );
}
예제 #3
0
/*
=============
RunThreadsOn
=============
*/
void RunThreadsOn( int workcnt, qboolean showpacifier, RunThreadsFn fn, void *pUserData )
{
    int		start, end;

    start = Plat_FloatTime();
    dispatch = 0;
    workcount = workcnt;
    StartPacifier("");
    pacifier = showpacifier;

#ifdef _PROFILE
    threaded = false;
    (*func)( 0 );
    return;
#endif


    RunThreads_Start( fn, pUserData );
    RunThreads_End();


    end = Plat_FloatTime();
    if (pacifier)
    {
        EndPacifier(false);
        printf (" (%i)\n", end-start);
    }
}
예제 #4
0
void ComputePerLeafAmbientLighting()
{
	// Figure out which lights should go in the per-leaf ambient cubes.
	int nInAmbientCube = 0;
	int nSurfaceLights = 0;
	for ( int i=0; i < *pNumworldlights; i++ )
	{
		dworldlight_t *wl = &dworldlights[i];
		
		if ( IsLeafAmbientSurfaceLight( wl ) )
			wl->flags |= DWL_FLAGS_INAMBIENTCUBE;
		else
			wl->flags &= ~DWL_FLAGS_INAMBIENTCUBE;
	
		if ( wl->type == emit_surface )
			++nSurfaceLights;

		if ( wl->flags & DWL_FLAGS_INAMBIENTCUBE )
			++nInAmbientCube;
	}

	Msg( "%d of %d (%d%% of) surface lights went in leaf ambient cubes.\n", nInAmbientCube, nSurfaceLights, nSurfaceLights ? ((nInAmbientCube*100) / nSurfaceLights) : 0 );

	g_pLeafAmbientLighting->SetCount( numleafs );

	StartPacifier( "ComputePerLeafAmbientLighting: " );
	
	for ( int leafID = 0; leafID < numleafs; leafID++ )
	{
		dleaf_t *pLeaf = &dleafs[leafID];

		Vector cube[6];
		Vector center = ( Vector( pLeaf->mins[0], pLeaf->mins[1], pLeaf->mins[2] ) + Vector( pLeaf->maxs[0], pLeaf->maxs[1], pLeaf->maxs[2] ) ) * 0.5f;
#if MAKE_LIGHT_BELOW_WATER_MATCH_LIGHT_ABOVE_WATER
		if (pLeaf->contents & CONTENTS_WATER)
		{
			center.z=pLeaf->maxs[2]+1;
			int above_leaf=PointLeafnum( center);
			dleaf_t *pLeaf = &dleafs[above_leaf];
			
			center = ( Vector( pLeaf->mins[0], pLeaf->mins[1], pLeaf->mins[2] ) + Vector( pLeaf->maxs[0], pLeaf->maxs[1], pLeaf->maxs[2] ) ) * 0.5f;
		}
#endif
		
		ComputeAmbientFromSphericalSamples( center, cube );
		for ( int i = 0; i < 6; i++ )
		{
			VectorToColorRGBExp32( cube[i], (*g_pLeafAmbientLighting)[leafID].m_Color[i] );
		}

		UpdatePacifier( (float)leafID / numleafs );
	}

	EndPacifier( true );
}
예제 #5
0
void RunMPIBuildVisLeafs()
{
    g_CPUTime.Init();
    
    Msg( "%-20s ", "BuildVisLeafs  :" );
	if ( g_bMPIMaster )
	{
		StartPacifier("");
	}

	memset( g_VMPIVisLeafsData, 0, sizeof( g_VMPIVisLeafsData ) );
	if ( !g_bMPIMaster || VMPI_GetActiveWorkUnitDistributor() == k_eWorkUnitDistributor_SDK )
	{
		// Allocate space for the transfers for each thread.
		for ( int i=0; i < numthreads; i++ )
		{
			g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers = BuildVisLeafs_Start();
		}
	}

	//
	// Slaves ask for work via GetMPIBuildVisLeafWork()
	// Results are returned in BuildVisRow()
	//
	VMPI_SetCurrentStage( "RunMPIBuildVisLeafs" );
	
	double elapsed = DistributeWork( 
		dvis->numclusters, 
		VMPI_DISTRIBUTEWORK_PACKETID,
		MPI_ProcessVisLeafs, 
		MPI_ReceiveVisLeafsResults );

	// Free the transfers from each thread.
	for ( int i=0; i < numthreads; i++ )
	{
		if ( g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers )
			BuildVisLeafs_End( g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers );
	}

	if ( g_bMPIMaster )
	{
		EndPacifier(false);
		Msg( " (%d)\n", (int)elapsed );
	}
	else
	{
		if ( g_iVMPIVerboseLevel >= 1 )
			Msg( "%.1f%% CPU utilization during PortalFlow\n", (g_CPUTime.GetSeconds() * 100.0f / elapsed) / numthreads );
	}
}
예제 #6
0
void RunMPIBuildFacelights()
{
	g_CPUTime.Init();

    Msg( "%-20s ", "BuildFaceLights:" );
	if ( g_bMPIMaster )
	{
		StartPacifier("");
	}

	VMPI_SetCurrentStage( "RunMPIBuildFaceLights" );
	double elapsed = DistributeWork( 
		numfaces, 
		VMPI_DISTRIBUTEWORK_PACKETID,
		MPI_ProcessFaces, 
		MPI_ReceiveFaceResults );

	if ( g_bMPIMaster )
	{
		EndPacifier(false);
		Msg( " (%d)\n", (int)elapsed );
	}

	if ( g_bMPIMaster )
	{
		//
		// BuildPatchLights is normally called from BuildFacelights(),
		// but in MPI mode we have the master do the calculation
		// We might be able to speed this up by doing while the master
		// is idling in the above loop. Wouldn't want to slow down the
		// handing out of work - maybe another thread?
		//
		for ( int i=0; i < numfaces; ++i )
		{
			BuildPatchLights(i);
		}
	}
	else
	{
		if ( g_iVMPIVerboseLevel >= 1 )
			Msg( "\n\n%.1f%% CPU utilization during BuildFaceLights\n\n", ( g_CPUTime.GetSeconds() * 100 / elapsed ) );
	}
}
예제 #7
0
//-----------------------------------------
//
// Run PortalFlow across all available processing nodes
//
void RunMPIPortalFlow()
{
    Msg( "%-20s ", "MPIPortalFlow:" );
	if ( g_bMPIMaster )
		StartPacifier("");

	// Workers wait until we get the MC socket address.
	g_PortalMCThreadUniqueID = StatsDB_GetUniqueJobID();
	if ( g_bMPIMaster )
	{
		CCycleCount cnt;
		cnt.Sample();
		CUniformRandomStream randomStream;
		randomStream.SetSeed( cnt.GetMicroseconds() );

		g_PortalMCAddr.port = randomStream.RandomInt( 22000, 25000 ); // Pulled out of something else.
		g_PortalMCAddr.ip[0] = (unsigned char)RandomInt( 225, 238 );
		g_PortalMCAddr.ip[1] = (unsigned char)RandomInt( 0, 255 );
		g_PortalMCAddr.ip[2] = (unsigned char)RandomInt( 0, 255 );
		g_PortalMCAddr.ip[3] = (unsigned char)RandomInt( 3, 255 );

		g_pPortalMCSocket = CreateIPSocket();
		int i=0;
		for ( i; i < 5; i++ )
		{
			if ( g_pPortalMCSocket->BindToAny( randomStream.RandomInt( 20000, 30000 ) ) )
				break;
		}
		if ( i == 5 )
		{
			Error( "RunMPIPortalFlow: can't open a socket to multicast on." );
		}

		char cPacketID[2] = { VMPI_VVIS_PACKET_ID, VMPI_SUBPACKETID_MC_ADDR };
		VMPI_Send2Chunks( cPacketID, sizeof( cPacketID ), &g_PortalMCAddr, sizeof( g_PortalMCAddr ), VMPI_PERSISTENT );
	}
	else
	{
		VMPI_SetCurrentStage( "wait for MC address" );

		while ( !g_bGotMCAddr )
		{
			VMPI_DispatchNextMessage();
		}

		// Open our multicast receive socket.
		g_pPortalMCSocket = CreateMulticastListenSocket( g_PortalMCAddr );
		if ( !g_pPortalMCSocket )
		{
			char err[512];
			IP_GetLastErrorString( err, sizeof( err ) );
			Error( "RunMPIPortalFlow: CreateMulticastListenSocket failed. (%s).", err );
		}

		// Make a thread to listen for the data on the multicast socket.
		DWORD dwDummy = 0;
		g_MCThreadExitEvent.Init( false, false );

		// Make sure we kill the MC thread if the app exits ungracefully.
		CmdLib_AtCleanup( MCThreadCleanupFn );
		
		g_hMCThread = CreateThread( 
			NULL,
			0,
			PortalMCThreadFn,
			NULL,
			0,
			&dwDummy );

		if ( !g_hMCThread )
		{
			Error( "RunMPIPortalFlow: CreateThread failed for multicast receive thread." );
		}			
	}

	VMPI_SetCurrentStage( "RunMPIBasePortalFlow" );


	g_pDistributeWorkCallbacks = &g_VisDistributeWorkCallbacks;

	g_CPUTime.Init();
	double elapsed = DistributeWork( 
		g_numportals * 2,		// # work units
		VMPI_DISTRIBUTEWORK_PACKETID,	// packet ID
		ProcessPortalFlow,		// Worker function to process work units
		ReceivePortalFlow		// Master function to receive work results
		);
		
	g_pDistributeWorkCallbacks = NULL;

	CheckExitedEarly();

	// Stop the multicast stuff.
	VMPI_DeletePortalMCSocket();

	if( !g_bMPIMaster )
	{
		if ( g_iVMPIVerboseLevel >= 1 )
		{
			Msg( "Received %d (out of %d) portals from multicast.\n", g_nMulticastPortalsReceived, g_numportals * 2 );
			Msg( "%.1f%% CPU utilization during PortalFlow\n", (g_CPUTime.GetSeconds() * 100.0f / elapsed) / numthreads );
		}

		Msg( "VVIS worker finished. Over and out.\n" );
		VMPI_SetCurrentStage( "worker done" );

		CmdLib_Exit( 0 );
	}

	if ( g_bMPIMaster )
	{
		EndPacifier( false );
		Msg( " (%d)\n", (int)elapsed );
	}
}
예제 #8
0
//-----------------------------------------
//
// Run BasePortalVis across all available processing nodes
// Then collect and redistribute the results.
//
void RunMPIBasePortalVis()
{
	int i;

	Msg( "\n\nportalbytes: %d\nNum Work Units: %d\nTotal data size: %d\n", portalbytes, g_numportals*2, portalbytes*g_numportals*2 );
    Msg("%-20s ", "BasePortalVis:");
	if ( g_bMPIMaster )
		StartPacifier("");


	VMPI_SetCurrentStage( "RunMPIBasePortalVis" );

	// Note: we're aiming for about 1500 portals in a map, so about 3000 work units.
	g_CPUTime.Init();
	double elapsed = DistributeWork( 
		g_numportals * 2,		// # work units
		VMPI_DISTRIBUTEWORK_PACKETID,	// packet ID
		ProcessBasePortalVis,	// Worker function to process work units
		ReceiveBasePortalVis	// Master function to receive work results
		);

	if ( g_bMPIMaster )
	{
		EndPacifier( false );
		Msg( " (%d)\n", (int)elapsed );
	}
	
	//
	// Distribute the results to all the workers.
	//
	if ( g_bMPIMaster )
	{
		if ( !fastvis )
		{
			VMPI_SetCurrentStage( "SendPortalResults" );

			// Store all the portal results in a temp file and multicast that to the workers.
			CUtlVector<char> allPortalData;
			allPortalData.SetSize( g_numportals * 2 * portalbytes * 2 );

			char *pOut = allPortalData.Base();
			for ( i=0; i < g_numportals * 2; i++) 
			{
				portal_t *p = &portals[i];
				
				memcpy( pOut, p->portalfront, portalbytes );
				pOut += portalbytes;
				
				memcpy( pOut, p->portalflood, portalbytes );
				pOut += portalbytes;
			}

			const char *pVirtualFilename = "--portal-results--";
			VMPI_FileSystem_CreateVirtualFile( pVirtualFilename, allPortalData.Base(), allPortalData.Count() );

			char cPacketID[2] = { VMPI_VVIS_PACKET_ID, VMPI_BASEPORTALVIS_RESULTS };
			VMPI_Send2Chunks( cPacketID, sizeof( cPacketID ), pVirtualFilename, strlen( pVirtualFilename ) + 1, VMPI_PERSISTENT );
		}
	}
	else
	{
		VMPI_SetCurrentStage( "RecvPortalResults" );

		// Wait until we've received the filename from the master.
		while ( g_BasePortalVisResultsFilename.Count() == 0 )
		{
			VMPI_DispatchNextMessage();
		}

		// Open 
		FileHandle_t fp = g_pFileSystem->Open( g_BasePortalVisResultsFilename.Base(), "rb", VMPI_VIRTUAL_FILES_PATH_ID );
		if ( !fp )
			Error( "Can't open '%s' to read portal info.", g_BasePortalVisResultsFilename.Base() );

		for ( i=0; i < g_numportals * 2; i++) 
		{
			portal_t *p = &portals[i];

			p->portalfront = (byte*)malloc (portalbytes);
			g_pFileSystem->Read( p->portalfront, portalbytes, fp );
			
			p->portalflood = (byte*)malloc (portalbytes);
			g_pFileSystem->Read( p->portalflood, portalbytes, fp );
		
			p->portalvis = (byte*)malloc (portalbytes);
			memset (p->portalvis, 0, portalbytes);
		
			p->nummightsee = CountBits (p->portalflood, g_numportals*2);
		}

		g_pFileSystem->Close( fp );
	}

	
	if ( !g_bMPIMaster )
	{
		if ( g_iVMPIVerboseLevel >= 1 )
			Msg( "\n%% worker CPU utilization during BasePortalVis: %.1f\n", (g_CPUTime.GetSeconds() * 100.0f / elapsed) / numthreads );
	}
}
//-----------------------------------------------------------------------------
// Places Detail Objects in the level
//-----------------------------------------------------------------------------
void EmitDetailModels()
{
	StartPacifier("Placing detail props : ");

	// Place stuff on each face
	dface_t* pFace = dfaces;
	for (int j = 0; j < numfaces; ++j)
	{
		UpdatePacifier( (float)j / (float)numfaces );

		// Get at the material associated with this face
		texinfo_t* pTexInfo = &texinfo[pFace[j].texinfo];
		dtexdata_t* pTexData = GetTexData( pTexInfo->texdata );

		// Try to get at the material
		bool found;
		MaterialSystemMaterial_t handle = 
			FindOriginalMaterial( TexDataStringTable_GetString( pTexData->nameStringTableID ), 
						  &found, false );
		if (!found)
			continue;

		// See if its got any detail objects on it
		const char* pDetailType = GetMaterialVar( handle, "%detailtype" );
		if (!pDetailType)
			continue;

		// Get the detail type...
		DetailObject_t search;
		search.m_Name = pDetailType;
		int objectType = s_DetailObjectDict.Find(search);
		if (objectType < 0)
		{
			Warning("Material %s uses unknown detail object type %s!\n",	
				TexDataStringTable_GetString( pTexData->nameStringTableID ),
				pDetailType);
			continue;
		}

		// Emit objects on a particular face
		DetailObject_t& detail = s_DetailObjectDict[objectType];

		if (pFace[j].dispinfo < 0)
		{
			EmitDetailObjectsOnFace( &pFace[j], detail );
		}
		else
		{
			// Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
			mapdispinfo_t *pMapDisp = &mapdispinfo[pFace[j].dispinfo];
			CCoreDispInfo coreDispInfo;
			DispMapToCoreDispInfo( pMapDisp, &coreDispInfo, &pFace[j] );

			EmitDetailObjectsOnDisplacementFace( &pFace[j], detail, coreDispInfo );
		}
	}

	// Emit specifically specified detail props
	Vector origin;
	QAngle angles;
	Vector2D pos[2];
	Vector2D tex[2];
	for (int i = 0; i < num_entities; ++i)
	{
		char* pEntity = ValueForKey(&entities[i], "classname");
		if (!strcmp(pEntity, "detail_prop") || !strcmp(pEntity, "prop_detail"))
		{
			GetVectorForKey( &entities[i], "origin", origin );
			GetAnglesForKey( &entities[i], "angles", angles );
			char* pModelName = ValueForKey( &entities[i], "model" );
			int nOrientation = IntForKey( &entities[i], "detailOrientation" );

			AddDetailToLump( pModelName, origin, angles, nOrientation );

			// strip this ent from the .bsp file
			entities[i].epairs = 0;
			continue;
		}

		if (!strcmp(pEntity, "prop_detail_sprite"))
		{
			GetVectorForKey( &entities[i], "origin", origin );
			GetAnglesForKey( &entities[i], "angles", angles );
			int nOrientation = IntForKey( &entities[i], "detailOrientation" );
			GetVector2DForKey( &entities[i], "position_ul", pos[0] );
			GetVector2DForKey( &entities[i], "position_lr", pos[1] );
			GetVector2DForKey( &entities[i], "tex_ul", tex[0] );
			GetVector2DForKey( &entities[i], "tex_size", tex[1] );
			float flTextureSize = FloatForKey( &entities[i], "tex_total_size" );

			tex[1].x += tex[0].x - 0.5f;
			tex[1].y += tex[0].y - 0.5f;
			tex[0].x += 0.5f;
			tex[0].y += 0.5f;
			tex[0] /= flTextureSize;
			tex[1] /= flTextureSize;

			AddDetailSpriteToLump( origin, angles, nOrientation, pos, tex, 1.0f );

			// strip this ent from the .bsp file
			entities[i].epairs = 0;
			continue;
		}
	}

	EndPacifier( true );
}
예제 #10
0
void EmitDispLMAlphaAndNeighbors()
{
	int i;

	Msg( "Finding displacement neighbors...\n" );

	// Do lightmap alpha.
    g_DispLightmapAlpha.RemoveAll();

	// Build the CCoreDispInfos.
	CUtlVector<dface_t*> faces;

	// Create the core dispinfos and init them for use as CDispUtilsHelpers.
	g_CoreDispInfos.SetSize( nummapdispinfo );
	for ( i=0; i < nummapdispinfo; i++ )
	{
		g_CoreDispInfos[i].SetDispUtilsHelperInfo( g_CoreDispInfos.Base(), nummapdispinfo );
	}

	faces.SetSize( nummapdispinfo );

	for( i = 0; i < numfaces; i++ )
	{
        dface_t *pFace = &dfaces[i];

		if( pFace->dispinfo == -1 )
			continue;

		mapdispinfo_t *pMapDisp = &mapdispinfo[pFace->dispinfo];
		
		// Set the displacement's face index.
		ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
		pDisp->m_iMapFace = i;

		// Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
		CCoreDispInfo *pCoreDispInfo = &g_CoreDispInfos[pFace->dispinfo];
		DispMapToCoreDispInfo( pMapDisp, pCoreDispInfo, pFace );
		
		faces[pFace->dispinfo] = pFace;
	}

	
	// Generate and export neighbor data.
	ExportNeighborData( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );

	// Generate and export the active vert lists.
	ExportAllowedVertLists( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );

	Msg( "Finding lightmap sample positions...\n" );
	for ( i=0; i < nummapdispinfo; i++ )
	{
		dface_t *pFace = faces[i];
		ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
		CCoreDispInfo *pCoreDispInfo = &g_CoreDispInfos[i];

		pDisp->m_iLightmapSamplePositionStart = g_DispLightmapSamplePositions.Count();

		CalculateLightmapSamplePositions( 
			pCoreDispInfo, 
			pFace,
			g_DispLightmapSamplePositions );
	}


	StartPacifier( "Displacement Alpha : ");

	// Build lightmap alphas.
	int dispCount = 0;	// How many we've processed.
	for( i = 0; i < nummapdispinfo; i++ )
	{
        dface_t *pFace = faces[i];

		Assert( pFace->dispinfo == i );
		mapdispinfo_t *pMapDisp = &mapdispinfo[pFace->dispinfo];
		ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];

		CCoreDispInfo *pCoreDispInfo = &g_CoreDispInfos[i];

		// Allocate space for the alpha values.
		pDisp->m_iLightmapAlphaStart = g_DispLightmapAlpha.Count();
		int nLuxelsToAdd = (pFace->m_LightmapTextureSizeInLuxels[0]+1) * (pFace->m_LightmapTextureSizeInLuxels[1]+1);
		g_DispLightmapAlpha.AddMultipleToTail( nLuxelsToAdd );

		DispUpdateLightmapAlpha( 
			g_CoreDispInfos.Base(),
			i,
			(float)dispCount     / g_dispinfo.Count(),
			(float)(dispCount+1) / g_dispinfo.Count(),
			pDisp, 
			pFace->m_LightmapTextureSizeInLuxels[0],
			pFace->m_LightmapTextureSizeInLuxels[1] );
	
		++dispCount;
	}

	EndPacifier();
}
예제 #11
0
void EmitDispLMAlphaAndNeighbors()
{
	int i;

	Msg( "Finding displacement neighbors...\n" );

	// Build the CCoreDispInfos.
	CUtlVector<dface_t*> faces;

	// Create the core dispinfos and init them for use as CDispUtilsHelpers.
	for ( int iDisp = 0; iDisp < nummapdispinfo; ++iDisp )
	{
		CCoreDispInfo *pDisp = new CCoreDispInfo;
		if ( !pDisp )
		{
			g_CoreDispInfos.Purge();
			return;
		}

		int nIndex = g_CoreDispInfos.AddToTail();
		pDisp->SetListIndex( nIndex );
		g_CoreDispInfos[nIndex] = pDisp;
	}

	for ( i=0; i < nummapdispinfo; i++ )
	{
		g_CoreDispInfos[i]->SetDispUtilsHelperInfo( g_CoreDispInfos.Base(), nummapdispinfo );
	}

	faces.SetSize( nummapdispinfo );

	for( i = 0; i < numfaces; i++ )
	{
        dface_t *pFace = &dfaces[i];

		if( pFace->dispinfo == -1 )
			continue;

		mapdispinfo_t *pMapDisp = &mapdispinfo[pFace->dispinfo];
		
		// Set the displacement's face index.
		ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
		pDisp->m_iMapFace = i;

		// Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
		CCoreDispInfo *pCoreDispInfo = g_CoreDispInfos[pFace->dispinfo];
		DispMapToCoreDispInfo( pMapDisp, pCoreDispInfo, pFace );
		
		faces[pFace->dispinfo] = pFace;
	}

	
	// Generate and export neighbor data.
	ExportNeighborData( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );

	// Generate and export the active vert lists.
	ExportAllowedVertLists( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );

	
	// Now that we know which vertices are actually going to be around, snap the ones that won't
	// be around onto the slightly-reduced mesh. This is so the engine's ray test code and 
	// overlay code works right.
	SnapRemainingVertsToSurface( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );

	Msg( "Finding lightmap sample positions...\n" );
	for ( i=0; i < nummapdispinfo; i++ )
	{
		dface_t *pFace = faces[i];
		ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
		CCoreDispInfo *pCoreDispInfo = g_CoreDispInfos[i];

		pDisp->m_iLightmapSamplePositionStart = g_DispLightmapSamplePositions.Count();

		CalculateLightmapSamplePositions( pCoreDispInfo, pFace, g_DispLightmapSamplePositions );
	}

	StartPacifier( "Displacement Alpha : ");

	// Build lightmap alphas.
	int dispCount = 0;	// How many we've processed.
	for( i = 0; i < nummapdispinfo; i++ )
	{
        dface_t *pFace = faces[i];

		Assert( pFace->dispinfo == i );
		mapdispinfo_t *pMapDisp = &mapdispinfo[pFace->dispinfo];
		ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];

		CCoreDispInfo *pCoreDispInfo = g_CoreDispInfos[i];

		// Allocate space for the alpha values.
		pDisp->m_iLightmapAlphaStart = 0; // not used anymore
		
		++dispCount;
	}

	EndPacifier();
}