//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $Revision: $ // $NoKeywords: $ //=============================================================================// #include "cmdlib.h" #include "mathlib.h" #include "bsplib.h" #include "zip_utils.h" #include "scriplib.h" #include "UtlLinkedList.h" #include "BSPTreeData.h" #include "CModel.h" #include "GameBSPFile.h" #include "UtlBuffer.h" #include "UtlRBTree.h" #include "UtlSymbol.h" #include "checksum_crc.h" #include "tier0/dbg.h" #include "lumpfiles.h" //============================================================================= // "-hdr" tells us to use the HDR fields (if present) on the light sources. Also, tells us to write // out the HDR lumps for lightmaps, ambient leaves, and lights sources. bool g_bHDR = false; uint32 g_LevelFlags = 0; int nummodels; dmodel_t dmodels[MAX_MAP_MODELS]; int visdatasize; byte dvisdata[MAX_MAP_VISIBILITY]; dvis_t *dvis = (dvis_t *)dvisdata; CUtlVector<byte> dlightdataHDR; CUtlVector<byte> dlightdataLDR; CUtlVector<byte> *pdlightdata = &dlightdataLDR; CUtlVector<char> dentdata; int numleafs; dleaf_t dleafs[MAX_MAP_LEAFS]; CUtlVector<CompressedLightCube> g_LeafAmbientLightingLDR; CUtlVector<CompressedLightCube> g_LeafAmbientLightingHDR; CUtlVector<CompressedLightCube> *g_pLeafAmbientLighting = NULL; // &g_LeafAmbientLightingLDR; unsigned short g_LeafMinDistToWater[MAX_MAP_LEAFS]; int numplanes; dplane_t dplanes[MAX_MAP_PLANES]; int numvertexes; dvertex_t dvertexes[MAX_MAP_VERTS]; int g_numvertnormalindices; // dfaces reference these. These index g_vertnormals. unsigned short g_vertnormalindices[MAX_MAP_VERTNORMALS]; int g_numvertnormals; Vector g_vertnormals[MAX_MAP_VERTNORMALS]; int numnodes; dnode_t dnodes[MAX_MAP_NODES]; CUtlVector<texinfo_t> texinfo( MAX_MAP_TEXINFO ); int numtexdata; dtexdata_t dtexdata[MAX_MAP_TEXDATA]; // // displacement map bsp file info: dispinfo // CUtlVector<ddispinfo_t> g_dispinfo; CUtlVector<CDispVert> g_DispVerts; CUtlVector<CDispTri> g_DispTris; CUtlVector<unsigned char> g_DispLightmapSamplePositions; // LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS int numorigfaces; dface_t dorigfaces[MAX_MAP_FACES]; int g_numprimitives = 0; dprimitive_t g_primitives[MAX_MAP_PRIMITIVES]; int g_numprimverts = 0; dprimvert_t g_primverts[MAX_MAP_PRIMVERTS]; int g_numprimindices = 0; unsigned short g_primindices[MAX_MAP_PRIMINDICES]; int numfaces; dface_t dfaces[MAX_MAP_FACES]; int numfaces_hdr; dface_t dfaces_hdr[MAX_MAP_FACES]; int numedges; dedge_t dedges[MAX_MAP_EDGES]; int numleaffaces; unsigned short dleaffaces[MAX_MAP_LEAFFACES]; int numleafbrushes; unsigned short dleafbrushes[MAX_MAP_LEAFBRUSHES]; int numsurfedges; int dsurfedges[MAX_MAP_SURFEDGES]; int numbrushes; dbrush_t dbrushes[MAX_MAP_BRUSHES]; int numbrushsides; dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES]; int numareas; darea_t dareas[MAX_MAP_AREAS]; int numareaportals; dareaportal_t dareaportals[MAX_MAP_AREAPORTALS]; int numworldlightsLDR; dworldlight_t dworldlightsLDR[MAX_MAP_WORLDLIGHTS]; int numworldlightsHDR; dworldlight_t dworldlightsHDR[MAX_MAP_WORLDLIGHTS]; int *pNumworldlights = &numworldlightsLDR; dworldlight_t *dworldlights = dworldlightsLDR; int numportals = 0; dportal_t dportals[MAX_MAP_PORTALS]; int numclusters = 0; dcluster_t dclusters[MAX_MAP_CLUSTERS]; int numleafwaterdata = 0; dleafwaterdata_t dleafwaterdata[MAX_MAP_LEAFWATERDATA]; int numportalverts = 0; unsigned short dportalverts[MAX_MAP_PORTALVERTS]; int numclusterportals; unsigned short dclusterportals[MAX_MAP_PORTALS*2]; CUtlVector<CFaceMacroTextureInfo> g_FaceMacroTextureInfos; Vector g_ClipPortalVerts[MAX_MAP_PORTALVERTS]; int g_nClipPortalVerts; dcubemapsample_t g_CubemapSamples[MAX_MAP_CUBEMAPSAMPLES]; int g_nCubemapSamples = 0; int g_nOverlayCount; doverlay_t g_Overlays[MAX_MAP_OVERLAYS]; int g_nWaterOverlayCount; dwateroverlay_t g_WaterOverlays[MAX_MAP_WATEROVERLAYS]; CUtlVector<char> g_TexDataStringData; CUtlVector<int> g_TexDataStringTable; byte *g_pPhysCollide = NULL; int g_PhysCollideSize = 0; int g_MapRevision = 0; CUtlVector<doccluderdata_t> g_OccluderData( 256, 256 ); CUtlVector<doccluderpolydata_t> g_OccluderPolyData( 1024, 1024 ); CUtlVector<int> g_OccluderVertexIndices( 2048, 2048 ); CUtlVector<dlightmappage_t> g_dLightmapPages; CUtlVector<dlightmappageinfo_t> g_dLightmapPageInfos; static void AddLump (int lumpnum, void *data, int len, int version = 0 ); dheader_t *header; FileHandle_t wadfile; dheader_t outheader; struct { void *pLumps[ HEADER_LUMPS ]; int lumpParsed[ HEADER_LUMPS ]; int size[ HEADER_LUMPS ]; } g_Lumps; //----------------------------------------------------------------------------- // Game lump memory storage //----------------------------------------------------------------------------- // NOTE: This is not optimal at all; since I expect client lumps to // not be accessed all that often. struct GameLump_t { GameLumpId_t m_Id; unsigned short m_Flags; unsigned short m_Version; CUtlMemory< unsigned char > m_Memory; }; static CUtlLinkedList< GameLump_t, GameLumpHandle_t > s_GameLumps; //----------------------------------------------------------------------------- // Purpose: // Singlegon instance // Output : CPakFile& //----------------------------------------------------------------------------- IZip* GetPakFile( void ) { return zip_utils; } //----------------------------------------------------------------------------- // Purpose: Set the sector alignment for all subsequent zip operations //----------------------------------------------------------------------------- void ForceAlignment( bool bAlign, unsigned int sectorSize ) { GetPakFile()->ForceAlignment( bAlign, sectorSize ); } //----------------------------------------------------------------------------- // Purpose: Store data back out to .bsp file //----------------------------------------------------------------------------- static void WritePakFileLump( void ) { CUtlBuffer buf( 0, 0 ); GetPakFile()->SaveToBuffer( buf ); unsigned int align = GetPakFile()->GetAlignment(); if ( align ) { // must repsect pak file alignment // pad up and ensure lump starts on same aligned boundary int filePos = g_pFileSystem->Tell(wadfile); int size = ((filePos + align - 1) & ~(align-1)) - filePos; if ( size ) { char *pData = (char *)malloc( size ); memset( pData, 0, size ); SafeWrite(wadfile, pData, size ); free( pData ); } } // Now store final buffer out to file AddLump( LUMP_PAKFILE, buf.Base(), buf.TellPut() ); } //----------------------------------------------------------------------------- // Purpose: Store data back out to .bsp file //----------------------------------------------------------------------------- static void WriteXZPPakFileLump( char* xzpFilename ) { unsigned int align = 512; // HACK! if ( align ) { // must repsect pak file alignment // pad up and ensure lump starts on same aligned boundary int filePos = g_pFileSystem->Tell(wadfile); int size = ((filePos + align - 1) & ~(align-1)) - filePos; if ( size ) { char *pData = (char *)malloc( size ); memset( pData, 0, size ); SafeWrite(wadfile, pData, size ); free( pData ); } } FILE* hXZP = fopen(xzpFilename, "rb"); fseek(hXZP,0,SEEK_END); int length = ftell(hXZP); fseek(hXZP,0,SEEK_SET); void* buffer = malloc( length ); fread(buffer,1,length,hXZP); fclose(hXZP); // Now store final buffer out to file AddLump( LUMP_XZIPPAKFILE, buffer, length ); free(buffer); } //----------------------------------------------------------------------------- // Purpose: Remove all entries //----------------------------------------------------------------------------- void ClearPackFile( void ) { GetPakFile()->Reset(); } //----------------------------------------------------------------------------- // Purpose: Add file from disk to .bsp PAK lump // Input : *relativename - // *fullpath - //----------------------------------------------------------------------------- void AddFileToPack( const char *relativename, const char *fullpath ) { GetPakFile()->AddFileToZip( relativename, fullpath ); } //----------------------------------------------------------------------------- // Purpose: Add buffer to .bsp PAK lump as named file // Input : *relativename - // *data - // length - //----------------------------------------------------------------------------- void AddBufferToPack( const char *pRelativeName, void *data, int length, bool bTextMode ) { GetPakFile()->AddBufferToZip( pRelativeName, data, length, bTextMode ); } //----------------------------------------------------------------------------- // Purpose: Check if a file already exists in the pack file. // Input : *relativename - //----------------------------------------------------------------------------- bool FileExistsInPack( const char *pRelativeName ) { return GetPakFile()->FileExistsInZip( pRelativeName ); } //----------------------------------------------------------------------------- // Read a file from the pack file //----------------------------------------------------------------------------- bool ReadFileFromPack( const char *pRelativeName, bool bTextMode, CUtlBuffer &buf ) { return GetPakFile()->ReadFileFromZip( pRelativeName, bTextMode, buf ); } //----------------------------------------------------------------------------- // Purpose: Remove file from .bsp PAK lump // Input : *relativename - //----------------------------------------------------------------------------- void RemoveFileFromPack( const char *relativename ) { GetPakFile()->RemoveFileFromZip( relativename ); } //----------------------------------------------------------------------------- // Purpose: Get next filename in directory // Input : id, -1 to start, returns next id, or -1 at list conclusion //----------------------------------------------------------------------------- int GetNextFilename( int id, char *pBuffer, int bufferSize, int &fileSize ) { return GetPakFile()->GetNextFilename( id, pBuffer, bufferSize, fileSize ); } //----------------------------------------------------------------------------- // Convert four-CC code to a handle + back //----------------------------------------------------------------------------- GameLumpHandle_t GetGameLumpHandle( GameLumpId_t id ) { // NOTE: I'm also expecting game lump id's to be four-CC codes Assert( id > HEADER_LUMPS ); for (int i = s_GameLumps.Count(); --i >= 0; ) { if (s_GameLumps[i].m_Id == id) return i; } return InvalidGameLump(); } GameLumpId_t GetGameLumpId( GameLumpHandle_t handle ) { return s_GameLumps[handle].m_Id; } int GetGameLumpFlags( GameLumpHandle_t handle ) { return s_GameLumps[handle].m_Flags; } int GetGameLumpVersion( GameLumpHandle_t handle ) { return s_GameLumps[handle].m_Version; } //----------------------------------------------------------------------------- // Game lump accessor methods //----------------------------------------------------------------------------- void* GetGameLump( GameLumpHandle_t id ) { return s_GameLumps[id].m_Memory.Base(); } int GameLumpSize( GameLumpHandle_t id ) { return s_GameLumps[id].m_Memory.NumAllocated(); } //----------------------------------------------------------------------------- // Game lump iteration methods //----------------------------------------------------------------------------- GameLumpHandle_t FirstGameLump() { return (s_GameLumps.Count()) ? 0 : InvalidGameLump(); } GameLumpHandle_t NextGameLump( GameLumpHandle_t handle ) { ++handle; return (handle < s_GameLumps.Count()) ? handle : InvalidGameLump(); } GameLumpHandle_t InvalidGameLump() { return 0xFFFF; } //----------------------------------------------------------------------------- // Game lump creation/destruction method //----------------------------------------------------------------------------- GameLumpHandle_t CreateGameLump( GameLumpId_t id, int size, int flags, int version ) { Assert( GetGameLumpHandle(id) == InvalidGameLump() ); GameLumpHandle_t handle = s_GameLumps.AddToTail(); s_GameLumps[handle].m_Id = id; s_GameLumps[handle].m_Flags = flags; s_GameLumps[handle].m_Version = version; s_GameLumps[handle].m_Memory.EnsureCapacity( size ); return handle; } void DestroyGameLump( GameLumpHandle_t handle ) { s_GameLumps.Remove( handle ); } void DestroyAllGameLumps() { s_GameLumps.RemoveAll(); } //----------------------------------------------------------------------------- // String table methods //----------------------------------------------------------------------------- const char *TexDataStringTable_GetString( int stringID ) { return &g_TexDataStringData[g_TexDataStringTable[stringID]]; } int TexDataStringTable_AddOrFindString( const char *pString ) { int i; // garymcthack: Make this use an RBTree! for( i = 0; i < g_TexDataStringTable.Count(); i++ ) { if( stricmp( pString, &g_TexDataStringData[g_TexDataStringTable[i]] ) == 0 ) { return i; } } int len = strlen( pString ); int outOffset = g_TexDataStringData.AddMultipleToTail( len+1, pString ); int outIndex = g_TexDataStringTable.AddToTail( outOffset ); return outIndex; } //----------------------------------------------------------------------------- // Compute file size and clump count //----------------------------------------------------------------------------- static void ComputeGameLumpSizeAndCount( int& size, int& clumpCount ) { // Figure out total size of the client lumps size = 0; clumpCount = 0; GameLumpHandle_t h; for( h = FirstGameLump(); h != InvalidGameLump(); h = NextGameLump( h ) ) { ++clumpCount; size += GameLumpSize( h ); } // Add on headers size += sizeof( dgamelumpheader_t ) + clumpCount * sizeof( dgamelump_t ); } //----------------------------------------------------------------------------- // Adds all game lumps into one big block //----------------------------------------------------------------------------- static void AddGameLumps( ) { // Figure out total size of the client lumps int size, clumpCount; ComputeGameLumpSizeAndCount( size, clumpCount ); // Set up the main lump dictionary entry g_Lumps.size[LUMP_GAME_LUMP] = 0; // mark it written lump_t* lump = &header->lumps[LUMP_GAME_LUMP]; lump->fileofs = LittleLong( g_pFileSystem->Tell(wadfile) ); lump->filelen = LittleLong(size); // write header dgamelumpheader_t header; header.lumpCount = clumpCount; SafeWrite (wadfile, &header, sizeof(header)); // write dictionary dgamelump_t dict; int offset = lump->fileofs + sizeof(header) + clumpCount * sizeof(dgamelump_t); GameLumpHandle_t h; for( h = FirstGameLump(); h != InvalidGameLump(); h = NextGameLump( h ) ) { dict.id = GetGameLumpId(h); dict.version = GetGameLumpVersion(h); dict.flags = GetGameLumpFlags(h); dict.fileofs = offset; dict.filelen = GameLumpSize( h ); SafeWrite (wadfile, &dict, sizeof(dict)); offset += dict.filelen; } // write lumps.. for( h = FirstGameLump(); h != InvalidGameLump(); h = NextGameLump( h ) ) { SafeWrite (wadfile, GetGameLump(h), GameLumpSize(h)); } // align to doubleword int totSize = (lump->filelen + 3) & ~0x3; totSize -= lump->filelen; if (totSize > 0) { char buf[3] = { 0, 0, 0 }; SafeWrite (wadfile, buf, totSize); } } //----------------------------------------------------------------------------- // Game lump file I/O //----------------------------------------------------------------------------- static void ParseGameLump( dheader_t* pHeader ) { DestroyAllGameLumps(); g_Lumps.lumpParsed[LUMP_GAME_LUMP] = 1; // mark it parsed int length = pHeader->lumps[LUMP_GAME_LUMP].filelen; int ofs = pHeader->lumps[LUMP_GAME_LUMP].fileofs; if (length > 0) { // Read dictionary... dgamelumpheader_t* pGameLumpHeader = (dgamelumpheader_t*)((byte *)pHeader + ofs); dgamelump_t* pGameLump = (dgamelump_t*)(pGameLumpHeader + 1); for (int i = 0; i < pGameLumpHeader->lumpCount; ++i ) { int length = pGameLump[i].filelen; GameLumpHandle_t lump = CreateGameLump( pGameLump[i].id, length, pGameLump[i].flags, pGameLump[i].version ); memcpy (GetGameLump(lump), (byte *)pHeader + pGameLump[i].fileofs, length); } } } //----------------------------------------------------------------------------- // Adds the occluder lump... //----------------------------------------------------------------------------- static void AddOcclusionLump( ) { g_Lumps.size[LUMP_OCCLUSION] = 0; // mark it written int nOccluderCount = g_OccluderData.Count(); int nOccluderPolyDataCount = g_OccluderPolyData.Count(); int nOccluderVertexIndices = g_OccluderVertexIndices.Count(); int nLumpLength = nOccluderCount * sizeof(doccluderdata_t) + nOccluderPolyDataCount * sizeof(doccluderpolydata_t) + nOccluderVertexIndices * sizeof(int) + 3 * sizeof(int); lump_t *lump = &header->lumps[LUMP_OCCLUSION]; lump->fileofs = LittleLong( g_pFileSystem->Tell(wadfile) ); lump->filelen = LittleLong( nLumpLength ); lump->version = LittleLong( LUMP_OCCLUSION_VERSION ); lump->fourCC[0] = ( char )0; lump->fourCC[1] = ( char )0; lump->fourCC[2] = ( char )0; lump->fourCC[3] = ( char )0; SafeWrite( wadfile, &nOccluderCount, 4 ); SafeWrite( wadfile, g_OccluderData.Base(), nOccluderCount * sizeof(doccluderdata_t) ); SafeWrite( wadfile, &nOccluderPolyDataCount, 4 ); SafeWrite( wadfile, g_OccluderPolyData.Base(), nOccluderPolyDataCount * sizeof(doccluderpolydata_t) ); SafeWrite( wadfile, &nOccluderVertexIndices, 4 ); SafeWrite( wadfile, g_OccluderVertexIndices.Base(), nOccluderVertexIndices * sizeof(int) ); } //----------------------------------------------------------------------------- // Loads the occluder lump... //----------------------------------------------------------------------------- static void UnserializeOcclusionLumpV2( CUtlBuffer &buf ) { int nCount = buf.GetInt(); if ( nCount ) { g_OccluderData.SetCount( nCount ); buf.Get( g_OccluderData.Base(), nCount * sizeof(g_OccluderData[0]) ); } nCount = buf.GetInt(); if ( nCount ) { g_OccluderPolyData.SetCount( nCount ); buf.Get( g_OccluderPolyData.Base(), nCount * sizeof(g_OccluderPolyData[0]) ); } nCount = buf.GetInt(); if ( nCount ) { g_OccluderVertexIndices.SetCount( nCount ); buf.Get( g_OccluderVertexIndices.Base(), nCount * sizeof(g_OccluderVertexIndices[0]) ); } } static void LoadOcclusionLump() { g_OccluderData.RemoveAll(); g_OccluderPolyData.RemoveAll(); g_OccluderVertexIndices.RemoveAll(); int length, ofs; g_Lumps.lumpParsed[LUMP_OCCLUSION] = 1; // mark it parsed length = header->lumps[LUMP_OCCLUSION].filelen; ofs = header->lumps[LUMP_OCCLUSION].fileofs; CUtlBuffer buf( (byte *)header + ofs, length, CUtlBuffer::READ_ONLY ); switch (header->lumps[LUMP_OCCLUSION].version) { case 2: UnserializeOcclusionLumpV2( buf ); break; case 0: break; default: Error("Unknown occlusion lump version!\n"); break; } } /* =============== CompressVis =============== */ int CompressVis (byte *vis, byte *dest) { int j; int rep; int visrow; byte *dest_p; dest_p = dest; // visrow = (r_numvisleafs + 7)>>3; visrow = (dvis->numclusters + 7)>>3; for (j=0 ; j<visrow ; j++) { *dest_p++ = vis[j]; if (vis[j]) continue; rep = 1; for ( j++; j<visrow ; j++) if (vis[j] || rep == 255) break; else rep++; *dest_p++ = rep; j--; } return dest_p - dest; } /* =================== DecompressVis =================== */ void DecompressVis (byte *in, byte *decompressed) { int c; byte *out; int row; // row = (r_numvisleafs+7)>>3; row = (dvis->numclusters+7)>>3; out = decompressed; do { if (*in) { *out++ = *in++; continue; } c = in[1]; if (!c) Error ("DecompressVis: 0 repeat"); in += 2; if ((out - decompressed) + c > row) { c = row - (out - decompressed); Warning( "warning: Vis decompression overrun\n" ); } while (c) { *out++ = 0; c--; } } while (out - decompressed < row); } //============================================================================= int LumpVersion( int lump ) { return header->lumps[lump].version; } bool HasLump( int lump ) { return header->lumps[lump].filelen > 0; } int CopyLump (int lump, void *dest, int size, int forceVersion = -1) { int length, ofs; g_Lumps.lumpParsed[lump] = 1; // mark it parsed length = header->lumps[lump].filelen; ofs = header->lumps[lump].fileofs; if (length % size) { Error ("LoadBSPFile: odd size for lump %d", lump ); } if ( forceVersion >= 0 && forceVersion != header->lumps[lump].version ) { Error ("LoadBSPFile: old version for lump %d in map!", lump ); } memcpy (dest, (byte *)header + ofs, length); return length / size; } template< class T > void CopyLump ( int lump, CUtlVector<T> &dest, int forceVersion = -1 ) { int length, ofs; g_Lumps.lumpParsed[lump] = 1; // mark it parsed length = header->lumps[lump].filelen; ofs = header->lumps[lump].fileofs; if (length % sizeof(T)) { Error ("LoadBSPFile: odd size for lump %d", lump ); } if ( forceVersion >= 0 && forceVersion != header->lumps[lump].version ) { Error ("LoadBSPFile: old version for lump %d in map!", lump ); } dest.SetSize( length / sizeof(T) ); memcpy( dest.Base(), (byte *)header + ofs, length ); } template< class T > void CopyOptionalLump( int lump, CUtlVector<T> &dest, int forceVersion = -1 ) { int length, ofs; g_Lumps.lumpParsed[lump] = 1; // mark it parsed length = header->lumps[lump].filelen; ofs = header->lumps[lump].fileofs; if (length % sizeof(T)) { Error ("LoadBSPFile: odd size for lump %d", lump ); } // not fatal if not present if ( length && forceVersion >= 0 && forceVersion != header->lumps[lump].version ) { Error ("LoadBSPFile: old version for lump %d in map!", lump ); } dest.SetSize( length / sizeof(T) ); memcpy( dest.Base(), (byte *)header + ofs, length ); } int CopyVariableLump( int lump, void **dest, int size, int forceVersion = -1 ) { int length, ofs; g_Lumps.lumpParsed[lump] = 1; // mark it parsed length = header->lumps[lump].filelen; ofs = header->lumps[lump].fileofs; if (length % size) { Error ("LoadBSPFile: odd lump size"); } if ( forceVersion >= 0 && forceVersion != header->lumps[lump].version ) { Error ("LoadBSPFile: old version map!"); } *dest = malloc( length ); memcpy (*dest, (byte *)header + ofs, length); return length / size; } void Lumps_Init( void ) { memset( &g_Lumps, 0, sizeof(g_Lumps) ); } void Lumps_Parse( void ) { int i; for ( i = 0; i < HEADER_LUMPS; i++ ) { if ( !g_Lumps.lumpParsed[i] && header->lumps[i].filelen ) { g_Lumps.size[i] = CopyVariableLump( i, &g_Lumps.pLumps[i], 1 ); Msg("Reading unknown lump #%d (%d bytes)\n", i, g_Lumps.size[i] ); } } } void Lumps_Write( void ) { int i; for ( i = 0; i < HEADER_LUMPS; i++ ) { if ( g_Lumps.size[i] ) { Msg("Writing unknown lump #%d (%d bytes)\n", i, g_Lumps.size[i] ); AddLump( i, g_Lumps.pLumps[i], g_Lumps.size[i] ); } if ( g_Lumps.pLumps[i] ) { free( g_Lumps.pLumps[i] ); g_Lumps.pLumps[i] = NULL; } } } int LoadLeafs( void ) { switch( LumpVersion( LUMP_LEAFS ) ) { case 0: { int length = header->lumps[LUMP_LEAFS].filelen; g_Lumps.lumpParsed[LUMP_LEAFS] = 1; // mark it parsed int size = sizeof( dleaf_version_0_t ); if( length % size ) { Error( "odd size for LUMP_LEAFS\n" ); } int count = length / size; dleaf_version_0_t *pSrc = ( dleaf_version_0_t * )( ( byte * )header + header->lumps[LUMP_LEAFS].fileofs ); dleaf_t *pDst = dleafs; Assert( g_pLeafAmbientLighting ); g_pLeafAmbientLighting->SetCount( count ); CompressedLightCube *pDstLeafAmbientLighting = &(*g_pLeafAmbientLighting)[0]; int i; for( i = 0; i < count; i++ ) { // pDst is a subset of pSrc; *pDst = *( ( dleaf_t * )( void * )pSrc ); *pDstLeafAmbientLighting = pSrc->m_AmbientLighting; pDst++; pSrc++; pDstLeafAmbientLighting++; } return count; } break; case 1: return CopyLump (LUMP_LEAFS, dleafs, sizeof(dleaf_t)); break; default: Assert( 0 ); Error( "Unknown LUMP_LEAFS version\n" ); return 0; break; } } /* ============= OpenBSPFile Low level BSP opener for external parsing. Parses headers, but nothing else. You must close the BSP, via CloseBSPFile(). ============= */ void OpenBSPFile (char *filename) { int i; Lumps_Init(); // load the file header LoadFile (filename, (void **)&header); // swap the header for (i=0 ; i< sizeof(dheader_t)/4 ; i++) ((int *)header)[i] = LittleLong ( ((int *)header)[i]); if (header->ident != IDBSPHEADER) Error ("%s is not a IBSP file", filename); if (header->version < MINBSPVERSION || header->version > BSPVERSION) Error ("%s is version %i, not %i", filename, header->version, BSPVERSION); g_MapRevision = header->mapRevision; } /* ============= CloseBSPFile ============= */ void CloseBSPFile ( void ) { free (header); } /* ============= LoadBSPFile ============= */ void LoadBSPFile (char *filename) { OpenBSPFile( filename ); nummodels = CopyLump (LUMP_MODELS, dmodels, sizeof(dmodel_t)); numvertexes = CopyLump (LUMP_VERTEXES, dvertexes, sizeof(dvertex_t)); numplanes = CopyLump (LUMP_PLANES, dplanes, sizeof(dplane_t)); numleafs = LoadLeafs(); numnodes = CopyLump (LUMP_NODES, dnodes, sizeof(dnode_t)); CopyLump (LUMP_TEXINFO, texinfo); numtexdata = CopyLump (LUMP_TEXDATA, dtexdata, sizeof(dtexdata_t)); CopyLump( LUMP_DISPINFO, g_dispinfo ); CopyLump( LUMP_DISP_VERTS, g_DispVerts ); CopyLump( LUMP_DISP_TRIS, g_DispTris ); CopyLump( LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS, g_DispLightmapSamplePositions ); CopyLump( LUMP_FACE_MACRO_TEXTURE_INFO, g_FaceMacroTextureInfos ); numfaces = CopyLump (LUMP_FACES, dfaces, sizeof(dface_t), LUMP_FACES_VERSION); if (HasLump( LUMP_FACES_HDR )) numfaces_hdr = CopyLump (LUMP_FACES_HDR, dfaces_hdr, sizeof(dface_t), LUMP_FACES_VERSION); else numfaces_hdr = 0; g_numprimitives = CopyLump (LUMP_PRIMITIVES, g_primitives, sizeof( dprimitive_t ) ); g_numprimverts = CopyLump (LUMP_PRIMVERTS, g_primverts, sizeof( dprimvert_t ) ); g_numprimindices = CopyLump (LUMP_PRIMINDICES, g_primindices, sizeof( unsigned short ) ); numorigfaces = CopyLump( LUMP_ORIGINALFACES, dorigfaces, sizeof( dface_t ) ); // original faces numleaffaces = CopyLump (LUMP_LEAFFACES, dleaffaces, sizeof(dleaffaces[0])); numleafbrushes = CopyLump (LUMP_LEAFBRUSHES, dleafbrushes, sizeof(dleafbrushes[0])); numsurfedges = CopyLump (LUMP_SURFEDGES, dsurfedges, sizeof(dsurfedges[0])); numedges = CopyLump (LUMP_EDGES, dedges, sizeof(dedge_t)); numbrushes = CopyLump (LUMP_BRUSHES, dbrushes, sizeof(dbrush_t)); numbrushsides = CopyLump (LUMP_BRUSHSIDES, dbrushsides, sizeof(dbrushside_t)); numareas = CopyLump (LUMP_AREAS, dareas, sizeof(darea_t)); numareaportals = CopyLump (LUMP_AREAPORTALS, dareaportals, sizeof(dareaportal_t)); visdatasize = CopyLump (LUMP_VISIBILITY, dvisdata, 1); CopyOptionalLump(LUMP_LIGHTING, dlightdataLDR, LUMP_LIGHTING_VERSION ); CopyOptionalLump(LUMP_LIGHTING_HDR, dlightdataHDR, LUMP_LIGHTING_VERSION ); CopyOptionalLump(LUMP_LEAF_AMBIENT_LIGHTING, g_LeafAmbientLightingLDR ); CopyOptionalLump(LUMP_LEAF_AMBIENT_LIGHTING_HDR, g_LeafAmbientLightingHDR ); CopyLump (LUMP_ENTITIES, dentdata); numworldlightsLDR = CopyLump (LUMP_WORLDLIGHTS, dworldlightsLDR, sizeof(dworldlight_t) ); numworldlightsHDR = CopyLump (LUMP_WORLDLIGHTS_HDR, dworldlightsHDR, sizeof(dworldlight_t) ); // not present on pc, not an error CopyOptionalLump( LUMP_LIGHTMAPPAGES, g_dLightmapPages, LUMP_LIGHTMAPPAGES_VERSION ); CopyOptionalLump( LUMP_LIGHTMAPPAGEINFOS, g_dLightmapPageInfos, LUMP_LIGHTMAPPAGEINFOS_VERSION ); numportals = CopyLump (LUMP_PORTALS, dportals, sizeof(dportal_t)); numclusters = CopyLump (LUMP_CLUSTERS, dclusters, sizeof(dcluster_t)); numleafwaterdata = CopyLump( LUMP_LEAFWATERDATA, dleafwaterdata, sizeof( dleafwaterdata_t ) ); numportalverts = CopyLump (LUMP_PORTALVERTS, dportalverts, sizeof(unsigned short)); numclusterportals = CopyLump (LUMP_CLUSTERPORTALS, dclusterportals, sizeof(unsigned short)); g_PhysCollideSize = CopyVariableLump( LUMP_PHYSCOLLIDE, (void**)&g_pPhysCollide, 1 ); g_numvertnormals = CopyLump (LUMP_VERTNORMALS, g_vertnormals, sizeof( g_vertnormals[0] )); g_numvertnormalindices = CopyLump (LUMP_VERTNORMALINDICES, g_vertnormalindices, sizeof( g_vertnormalindices[0] )); g_nClipPortalVerts = CopyLump( LUMP_CLIPPORTALVERTS, g_ClipPortalVerts, sizeof( g_ClipPortalVerts[0] ) ); g_nCubemapSamples = CopyLump( LUMP_CUBEMAPS, g_CubemapSamples, sizeof( g_CubemapSamples[0] ) ); CopyLump( LUMP_TEXDATA_STRING_DATA, g_TexDataStringData ); CopyLump( LUMP_TEXDATA_STRING_TABLE, g_TexDataStringTable ); g_nOverlayCount = CopyLump( LUMP_OVERLAYS, g_Overlays, sizeof( g_Overlays[0] ) ); g_nWaterOverlayCount = CopyLump( LUMP_WATEROVERLAYS, g_WaterOverlays, sizeof( g_WaterOverlays[0] ) ); dflagslump_t flags_lump; if (HasLump( LUMP_MAP_FLAGS )) CopyLump ( LUMP_MAP_FLAGS, &flags_lump, sizeof( flags_lump ) ); else memset(&flags_lump, 0, sizeof( flags_lump ) ); // default flags to 0 g_LevelFlags = flags_lump.m_LevelFlags; LoadOcclusionLump(); CopyLump( LUMP_LEAFMINDISTTOWATER, g_LeafMinDistToWater, sizeof( g_LeafMinDistToWater[0] ) ); /* int crap; for( crap = 0; crap < g_nBSPStringTable; crap++ ) { Msg( "stringtable %d", ( int )crap ); Msg( " %d:", ( int )g_BSPStringTable[crap] ); puts( &g_BSPStringData[g_BSPStringTable[crap]] ); puts( "\n" ); } */ // Load PAK file lump into appropriate data structure { byte *pakbuffer = NULL; int paksize = CopyVariableLump( LUMP_PAKFILE, ( void ** )&pakbuffer, 1 ); if ( paksize > 0 ) { GetPakFile()->ParseFromBuffer( pakbuffer, paksize ); } else { GetPakFile()->Reset(); } free( pakbuffer ); } ParseGameLump( header ); // NOTE: Do NOT call CopyLump after Lumps_Parse() it parses all un-Copyied lumps Lumps_Parse(); // parse any additional lumps CloseBSPFile(); // everything has been copied out } /* ============= LoadBSPFileFilesystemOnly ============= */ void LoadBSPFile_FileSystemOnly (char *filename) { int i; Lumps_Init(); // // load the file header // LoadFile (filename, (void **)&header); // swap the header for (i=0 ; i< sizeof(dheader_t)/4 ; i++) ((int *)header)[i] = LittleLong ( ((int *)header)[i]); if (header->ident != IDBSPHEADER) Error ("%s is not a IBSP file", filename); if (header->version < MINBSPVERSION || header->version > BSPVERSION) Error ("%s is version %i, not %i", filename, header->version, BSPVERSION); // Load PAK file lump into appropriate data structure { byte *pakbuffer = NULL; int paksize = CopyVariableLump( LUMP_PAKFILE, ( void ** )&pakbuffer, 1 ); if ( paksize > 0 ) { GetPakFile()->ParseFromBuffer( pakbuffer, paksize ); } else { GetPakFile()->Reset(); } free( pakbuffer ); } free (header); // everything has been copied out } void ExtractZipFileFromBSP( char *pBSPFileName, char *pZipFileName ) { Lumps_Init(); // // load the file header // LoadFile ( pBSPFileName, (void **)&header); // swap the header int i; for (i=0 ; i< sizeof(dheader_t)/4 ; i++) ((int *)header)[i] = LittleLong ( ((int *)header)[i]); if (header->ident != IDBSPHEADER) Error ("%s is not a IBSP file", pBSPFileName); if (header->version < MINBSPVERSION || BSPVERSION > BSPVERSION) Error ("%s is version %i, not %i", pBSPFileName, header->version, BSPVERSION); byte *pakbuffer = NULL; int paksize = CopyVariableLump( LUMP_PAKFILE, ( void ** )&pakbuffer, 1 ); if ( paksize > 0 ) { byte *pakbuffer = NULL; int paksize = CopyVariableLump( LUMP_PAKFILE, ( void ** )&pakbuffer, 1 ); FILE *fp; fp = fopen( pZipFileName, "wb" ); if( !fp ) { fprintf( stderr, "can't open %s\n", pZipFileName ); return; } fwrite( pakbuffer, paksize, 1, fp ); fclose( fp ); } else { int paksize = CopyVariableLump( LUMP_XZIPPAKFILE, ( void ** )&pakbuffer, 1 ); if ( paksize > 0 ) { byte *pakbuffer = NULL; int paksize = CopyVariableLump( LUMP_XZIPPAKFILE, ( void ** )&pakbuffer, 1 ); FILE *fp; fp = fopen( pZipFileName, "wb" ); if( !fp ) { fprintf( stderr, "can't open %s\n", pZipFileName ); return; } fwrite( pakbuffer, paksize, 1, fp ); fclose( fp ); } else { fprintf( stderr, "zip file is zero length!\n" ); } } } /* ============= LoadBSPFileTexinfo Only loads the texinfo lump, so qdata can scan for textures ============= */ void LoadBSPFileTexinfo (char *filename) { int i; FILE *f; int length, ofs; header = (dheader_t*)malloc(sizeof(dheader_t)); f = fopen (filename, "rb"); fread (header, sizeof(dheader_t), 1, f); // swap the header for (i=0 ; i< sizeof(dheader_t)/4 ; i++) ((int *)header)[i] = LittleLong ( ((int *)header)[i]); if (header->ident != IDBSPHEADER) { Error ("%s is not a IBSP file", filename); } if (header->version < MINBSPVERSION || header->version > BSPVERSION) { Error ("%s is version %i, not %i", filename, header->version, BSPVERSION); } length = header->lumps[LUMP_TEXINFO].filelen; ofs = header->lumps[LUMP_TEXINFO].fileofs; int nCount = length / sizeof(texinfo_t); texinfo.Purge(); texinfo.AddMultipleToTail( nCount ); fseek (f, ofs, SEEK_SET); fread (texinfo.Base(), length, 1, f); fclose (f); free (header); // everything has been copied out } //============================================================================ static void AddLump (int lumpnum, void *data, int len, int version ) { lump_t *lump; g_Lumps.size[lumpnum] = 0; // mark it written lump = &header->lumps[lumpnum]; lump->fileofs = LittleLong( g_pFileSystem->Tell(wadfile) ); lump->filelen = LittleLong(len); lump->version= LittleLong( version ); lump->fourCC[0] = ( char )0; lump->fourCC[1] = ( char )0; lump->fourCC[2] = ( char )0; lump->fourCC[3] = ( char )0; SafeWrite (wadfile, data, (len+3)&~3); } template< class T > static void AddLump( int lumpnum, CUtlVector<T> &data, int version = 0 ) { AddLump( lumpnum, data.Base(), data.Count() * sizeof(T), version ); } /* ============= WriteBSPFile Swaps the bsp file in place, so it should not be referenced again ============= */ void WriteBSPFile (char *filename, char* xzpLumpFilename ) { if ( texinfo.Count() > MAX_MAP_TEXINFO ) { Error( "Map has too many texinfos (has %d, can have at most %d)\n", texinfo.Count(), MAX_MAP_TEXINFO ); return; } header = &outheader; memset (header, 0, sizeof(dheader_t)); header->ident = LittleLong (IDBSPHEADER); header->version = LittleLong (BSPVERSION); header->mapRevision = LittleLong( g_MapRevision ); wadfile = SafeOpenWrite (filename); SafeWrite (wadfile, header, sizeof(dheader_t)); // overwritten later AddLump (LUMP_PLANES, dplanes, numplanes*sizeof(dplane_t)); AddLump (LUMP_LEAFS, dleafs, numleafs*sizeof(dleaf_t), LUMP_LEAFS_VERSION); // Make ambient lighting of zero so that the rest of the code can assume that this lump is here. if ( !g_bHDR && g_LeafAmbientLightingLDR.Count() == 0 ) { g_LeafAmbientLightingLDR.SetCount( numleafs ); memset( g_LeafAmbientLightingLDR.Base(), 0, g_LeafAmbientLightingLDR.Count()*sizeof(CompressedLightCube )); } AddLump (LUMP_LEAF_AMBIENT_LIGHTING, g_LeafAmbientLightingLDR, g_LeafAmbientLightingLDR.Count()*sizeof(CompressedLightCube)); // Make ambient lighting of zero so that the rest of the code can assume that this lump is here. if ( g_bHDR && g_LeafAmbientLightingHDR.Count() == 0 ) { g_LeafAmbientLightingHDR.SetCount( numleafs ); memset( g_LeafAmbientLightingHDR.Base(), 0, g_LeafAmbientLightingHDR.Count()*sizeof(CompressedLightCube )); } AddLump (LUMP_LEAF_AMBIENT_LIGHTING_HDR, g_LeafAmbientLightingHDR, g_LeafAmbientLightingHDR.Count()*sizeof(CompressedLightCube)); AddLump (LUMP_VERTEXES, dvertexes, numvertexes*sizeof(dvertex_t)); AddLump (LUMP_NODES, dnodes, numnodes*sizeof(dnode_t)); AddLump (LUMP_TEXINFO, texinfo); AddLump (LUMP_TEXDATA, dtexdata, numtexdata*sizeof(dtexdata_t)); AddLump (LUMP_DISPINFO, g_dispinfo ); AddLump (LUMP_DISP_VERTS, g_DispVerts ); AddLump (LUMP_DISP_TRIS, g_DispTris ); AddLump (LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS, g_DispLightmapSamplePositions ); AddLump (LUMP_FACE_MACRO_TEXTURE_INFO, g_FaceMacroTextureInfos ); AddLump (LUMP_PRIMITIVES, g_primitives, g_numprimitives * sizeof( dprimitive_t ) ); AddLump (LUMP_PRIMVERTS, g_primverts, g_numprimverts * sizeof( dprimvert_t ) ); AddLump (LUMP_PRIMINDICES, g_primindices, g_numprimindices * sizeof( unsigned short ) ); AddLump (LUMP_FACES, dfaces, numfaces*sizeof(dface_t), LUMP_FACES_VERSION); if (numfaces_hdr) AddLump (LUMP_FACES_HDR, dfaces_hdr, numfaces_hdr*sizeof(dface_t), LUMP_FACES_VERSION); AddLump (LUMP_ORIGINALFACES, dorigfaces, numorigfaces*sizeof( dface_t ) ); // original faces lump AddLump (LUMP_BRUSHES, dbrushes, numbrushes*sizeof(dbrush_t)); AddLump (LUMP_BRUSHSIDES, dbrushsides, numbrushsides*sizeof(dbrushside_t)); AddLump (LUMP_LEAFFACES, dleaffaces, numleaffaces*sizeof(dleaffaces[0])); AddLump (LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes*sizeof(dleafbrushes[0])); AddLump (LUMP_SURFEDGES, dsurfedges, numsurfedges*sizeof(dsurfedges[0])); AddLump (LUMP_EDGES, dedges, numedges*sizeof(dedge_t)); AddLump (LUMP_MODELS, dmodels, nummodels*sizeof(dmodel_t)); AddLump (LUMP_AREAS, dareas, numareas*sizeof(darea_t)); AddLump (LUMP_AREAPORTALS, dareaportals, numareaportals*sizeof(dareaportal_t)); AddLump (LUMP_LIGHTING, dlightdataLDR, LUMP_LIGHTING_VERSION); AddLump (LUMP_LIGHTING_HDR, dlightdataHDR, LUMP_LIGHTING_VERSION); AddLump (LUMP_VISIBILITY, dvisdata, visdatasize); AddLump (LUMP_ENTITIES, dentdata); AddLump (LUMP_WORLDLIGHTS, dworldlightsLDR, numworldlightsLDR*sizeof(dworldlight_t)); AddLump (LUMP_WORLDLIGHTS_HDR, dworldlightsHDR, numworldlightsHDR*sizeof(dworldlight_t)); AddLump (LUMP_LEAFWATERDATA, dleafwaterdata, numleafwaterdata*sizeof(dleafwaterdata_t)); AddOcclusionLump(); dflagslump_t flags_lump; flags_lump.m_LevelFlags = g_LevelFlags; AddLump( LUMP_MAP_FLAGS, &flags_lump, sizeof( flags_lump) ); // NOTE: This is just for debugging, so it is disabled in release maps #if 0 // add the vis portals to the BSP for visualization AddLump (LUMP_PORTALS, dportals, numportals*sizeof(dportal_t)); AddLump (LUMP_CLUSTERS, dclusters, numclusters*sizeof(dcluster_t)); AddLump (LUMP_PORTALVERTS, dportalverts, numportalverts*sizeof(unsigned short)); AddLump (LUMP_CLUSTERPORTALS, dclusterportals, numclusterportals*sizeof(unsigned short)); #endif AddLump (LUMP_CLIPPORTALVERTS, g_ClipPortalVerts, g_nClipPortalVerts*sizeof(g_ClipPortalVerts[0])); AddLump (LUMP_CUBEMAPS, g_CubemapSamples, g_nCubemapSamples * sizeof( g_CubemapSamples[0] ) ); AddLump (LUMP_TEXDATA_STRING_DATA, g_TexDataStringData ); AddLump (LUMP_TEXDATA_STRING_TABLE, g_TexDataStringTable ); AddLump (LUMP_OVERLAYS, g_Overlays, g_nOverlayCount * sizeof( g_Overlays[0] ) ); AddLump (LUMP_WATEROVERLAYS, g_WaterOverlays, g_nWaterOverlayCount * sizeof( g_WaterOverlays[0] ) ); if ( g_pPhysCollide ) { AddLump (LUMP_PHYSCOLLIDE, g_pPhysCollide, g_PhysCollideSize); } AddLump (LUMP_VERTNORMALS, g_vertnormals, g_numvertnormals * sizeof( g_vertnormals[0] ) ); AddLump (LUMP_VERTNORMALINDICES, g_vertnormalindices, g_numvertnormalindices * sizeof( g_vertnormalindices[0] ) ); AddLump (LUMP_LEAFMINDISTTOWATER, g_LeafMinDistToWater, numleafs * sizeof( g_LeafMinDistToWater[0] ) ); AddLump (LUMP_LIGHTMAPPAGES, g_dLightmapPages, LUMP_LIGHTMAPPAGES_VERSION); AddLump (LUMP_LIGHTMAPPAGEINFOS, g_dLightmapPageInfos, LUMP_LIGHTMAPPAGEINFOS_VERSION); AddGameLumps(); /* { GetPakFile()->AddFileToPack( "cfg/config.cfg", "d:\\tf2\\tf2\\cfg\\config.cfg" ); } */ // Write pakfile lump to disk if( xzpLumpFilename ) { WriteXZPPakFileLump( xzpLumpFilename ); } else { WritePakFileLump(); } // NOTE: Do NOT call AddLump after Lumps_Write() it writes all un-Added lumps Lumps_Write(); // write any additional lumps g_pFileSystem->Seek (wadfile, 0, FILESYSTEM_SEEK_HEAD); SafeWrite (wadfile, header, sizeof(dheader_t)); g_pFileSystem->Close (wadfile); } // Generate the next clear lump filename for the bsp file bool GenerateNextLumpFileName( const char *bspfilename, char *lumpfilename, int buffsize ) { for (int i = 0; i < MAX_LUMPFILES; i++) { GenerateLumpFileName( bspfilename, lumpfilename, buffsize, i ); if ( !g_pFileSystem->FileExists( lumpfilename ) ) return true; } return false; } void WriteLumpToFile( char *filename, int lump ) { if ( !HasLump(lump) ) return; char lumppre[MAX_PATH]; if ( !GenerateNextLumpFileName( filename, lumppre, MAX_PATH ) ) { Warning( "Failed to find valid lump filename for bsp %s.\n", filename ); return; } // Open the file FileHandle_t lumpfile = g_pFileSystem->Open(lumppre, "wb"); if ( !lumpfile ) { Error ("Error opening %s! (Check for write enable)\n",filename); return; } int ofs = header->lumps[lump].fileofs; int length = header->lumps[lump].filelen; // Write the header lumpfileheader_t lumpHeader; lumpHeader.lumpID = lump; lumpHeader.lumpVersion = LumpVersion(lump); lumpHeader.lumpLength = length; lumpHeader.mapRevision = LittleLong( g_MapRevision ); lumpHeader.lumpOffset = sizeof(lumpfileheader_t); // Lump starts after the header SafeWrite (lumpfile, &lumpHeader, sizeof(lumpfileheader_t)); // Write the lump SafeWrite (lumpfile, (byte *)header + ofs, length); } //============================================================================ #define ENTRIES(a) (sizeof(a)/sizeof(*(a))) #define ENTRYSIZE(a) (sizeof(*(a))) int ArrayUsage( char *szItem, int items, int maxitems, int itemsize ) { float percentage = maxitems ? items * 100.0 / maxitems : 0.0; Msg("%-17.17s %8i/%-8i %8i/%-8i (%4.1f%%) ", szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage ); if ( percentage > 80.0 ) Msg( "VERY FULL!\n" ); else if ( percentage > 95.0 ) Msg( "SIZE DANGER!\n" ); else if ( percentage > 99.9 ) Msg( "SIZE OVERFLOW!!!\n" ); else Msg( "\n" ); return items * itemsize; } int GlobUsage( char *szItem, int itemstorage, int maxstorage ) { float percentage = maxstorage ? itemstorage * 100.0 / maxstorage : 0.0; Msg("%-17.17s [variable] %8i/%-8i (%4.1f%%) ", szItem, itemstorage, maxstorage, percentage ); if ( percentage > 80.0 ) Msg( "VERY FULL!\n" ); else if ( percentage > 95.0 ) Msg( "SIZE DANGER!\n" ); else if ( percentage > 99.9 ) Msg( "SIZE OVERFLOW!!!\n" ); else Msg( "\n" ); return itemstorage; } /* ============= PrintBSPFileSizes Dumps info about current file ============= */ void PrintBSPFileSizes (void) { int totalmemory = 0; // if (!num_entities) // ParseEntities (); Msg("\n"); Msg( "%-17s %16s %16s %9s \n", "Object names", "Objects/Maxobjs", "Memory / Maxmem", "Fullness" ); Msg( "%-17s %16s %16s %9s \n", "------------", "---------------", "---------------", "--------" ); totalmemory += ArrayUsage( "models", nummodels, ENTRIES(dmodels), ENTRYSIZE(dmodels) ); totalmemory += ArrayUsage( "brushes", numbrushes, ENTRIES(dbrushes), ENTRYSIZE(dbrushes) ); totalmemory += ArrayUsage( "brushsides", numbrushsides, ENTRIES(dbrushsides), ENTRYSIZE(dbrushsides) ); totalmemory += ArrayUsage( "planes", numplanes, ENTRIES(dplanes), ENTRYSIZE(dplanes) ); totalmemory += ArrayUsage( "vertexes", numvertexes, ENTRIES(dvertexes), ENTRYSIZE(dvertexes) ); totalmemory += ArrayUsage( "nodes", numnodes, ENTRIES(dnodes), ENTRYSIZE(dnodes) ); totalmemory += ArrayUsage( "texinfos", texinfo.Count(),MAX_MAP_TEXINFO, sizeof(texinfo_t) ); totalmemory += ArrayUsage( "texdata", numtexdata, ENTRIES(dtexdata), ENTRYSIZE(dtexdata) ); totalmemory += ArrayUsage( "dispinfos", g_dispinfo.Count(), 0, sizeof( ddispinfo_t ) ); totalmemory += ArrayUsage( "disp_verts", g_DispVerts.Count(), 0, sizeof( g_DispVerts[0] ) ); totalmemory += ArrayUsage( "disp_tris", g_DispTris.Count(), 0, sizeof( g_DispTris[0] ) ); totalmemory += ArrayUsage( "disp_lmsamples",g_DispLightmapSamplePositions.Count(),0,sizeof( g_DispLightmapSamplePositions[0] ) ); totalmemory += ArrayUsage( "faces", numfaces, ENTRIES(dfaces), ENTRYSIZE(dfaces) ); totalmemory += ArrayUsage( "hdr faces", numfaces_hdr, ENTRIES(dfaces_hdr), ENTRYSIZE(dfaces_hdr) ); totalmemory += ArrayUsage( "origfaces", numorigfaces, ENTRIES(dorigfaces), ENTRYSIZE(dorigfaces) ); // original faces totalmemory += ArrayUsage( "leaves", numleafs, ENTRIES(dleafs), ENTRYSIZE(dleafs) ); totalmemory += ArrayUsage( "leaffaces", numleaffaces, ENTRIES(dleaffaces), ENTRYSIZE(dleaffaces) ); totalmemory += ArrayUsage( "leafbrushes", numleafbrushes, ENTRIES(dleafbrushes), ENTRYSIZE(dleafbrushes) ); totalmemory += ArrayUsage( "areas", numareas, ENTRIES(dareas), ENTRYSIZE(dareas) ); totalmemory += ArrayUsage( "surfedges", numsurfedges, ENTRIES(dsurfedges), ENTRYSIZE(dsurfedges) ); totalmemory += ArrayUsage( "edges", numedges, ENTRIES(dedges), ENTRYSIZE(dedges) ); totalmemory += ArrayUsage( "LDR worldlights", numworldlightsLDR, ENTRIES(dworldlightsLDR), ENTRYSIZE(dworldlightsLDR) ); totalmemory += ArrayUsage( "HDR worldlights", numworldlightsHDR, ENTRIES(dworldlightsHDR), ENTRYSIZE(dworldlightsHDR) ); totalmemory += ArrayUsage( "waterstrips", g_numprimitives,ENTRIES(g_primitives), ENTRYSIZE(g_primitives) ); totalmemory += ArrayUsage( "waterverts", g_numprimverts, ENTRIES(g_primverts), ENTRYSIZE(g_primverts) ); totalmemory += ArrayUsage( "waterindices", g_numprimindices,ENTRIES(g_primindices),ENTRYSIZE(g_primindices) ); totalmemory += ArrayUsage( "cubemapsamples", g_nCubemapSamples,ENTRIES(g_CubemapSamples),ENTRYSIZE(g_CubemapSamples) ); totalmemory += ArrayUsage( "overlays", g_nOverlayCount, ENTRIES(g_Overlays), ENTRYSIZE(g_Overlays) ); totalmemory += GlobUsage( "LDR lightdata", dlightdataLDR.Count(), 0 ); totalmemory += GlobUsage( "HDR lightdata", dlightdataHDR.Count(), 0 ); totalmemory += GlobUsage( "visdata", visdatasize, sizeof(dvisdata) ); totalmemory += GlobUsage( "entdata", dentdata.Count(), 384*1024 ); // goal is <384K totalmemory += ArrayUsage( "LDR leaf ambient lighting", g_LeafAmbientLightingLDR.Count(), MAX_MAP_LEAFS, sizeof( g_LeafAmbientLightingLDR[0] ) ); totalmemory += ArrayUsage( "HDR leaf ambient lighting", g_LeafAmbientLightingHDR.Count(), MAX_MAP_LEAFS, sizeof( g_LeafAmbientLightingHDR[0] ) ); totalmemory += ArrayUsage( "occluders", g_OccluderData.Count(), 0, sizeof( g_OccluderData[0] ) ); totalmemory += ArrayUsage( "occluder polygons", g_OccluderPolyData.Count(), 0, sizeof( g_OccluderPolyData[0] ) ); totalmemory += ArrayUsage( "occluder vert ind",g_OccluderVertexIndices.Count(),0, sizeof( g_OccluderVertexIndices[0] ) ); GameLumpHandle_t h = GetGameLumpHandle( GAMELUMP_DETAIL_PROPS ); if (h != InvalidGameLump()) totalmemory += GlobUsage( "detail props", 1, GameLumpSize(h) ); h = GetGameLumpHandle( GAMELUMP_DETAIL_PROP_LIGHTING ); if (h != InvalidGameLump()) totalmemory += GlobUsage( "dtl prp lght", 1, GameLumpSize(h) ); h = GetGameLumpHandle( GAMELUMP_DETAIL_PROP_LIGHTING_HDR ); if (h != InvalidGameLump()) totalmemory += GlobUsage( "HDR dtl prp lght", 1, GameLumpSize(h) ); h = GetGameLumpHandle( GAMELUMP_STATIC_PROPS ); if (h != InvalidGameLump()) totalmemory += GlobUsage( "static props", 1, GameLumpSize(h) ); totalmemory += GlobUsage( "pakfile", GetPakFile()->EstimateSize(), 0 ); Msg( "\nLevel flags = %x\n", g_LevelFlags ); Msg( "\nWin32 Specific Data:\n" ); int totalWin32Specificmemory = 0; // HACKHACK: Set physics limit at 4MB, in reality this is totally dynamic totalWin32Specificmemory += GlobUsage( "physics", g_PhysCollideSize, 4*1024*1024 ); Msg( "==== Total Win32 BSP file data space used: %d bytes ====\n", totalmemory + totalWin32Specificmemory ); #ifdef _XBOX Msg( "\nXBox Specific Data:\n" ); int totalXBoxSpecificmemory = 0; totalXBoxSpecificmemory += GlobUsage( "lightmap pages", g_dLightmapPages.Count()*sizeof(dlightmappage_t), 0 ); totalXBoxSpecificmemory += GlobUsage( "lightmap infos", g_dLightmapPageInfos.Count()*sizeof(dlightmappageinfo_t), 0 ); Msg( "==== Total XBox BSP file data space used: %d bytes ====\n", totalmemory + totalXBoxSpecificmemory ); #endif // _XBOX Msg( "\n" ); int triangleCount = 0; for ( int i = 0; i < numfaces; i++ ) { // face tris = numedges - 2 triangleCount += dfaces[i].numedges - 2; } Msg("Total triangle count: %d\n", triangleCount ); // UNDONE: // areaportals, portals, texdata, clusters, worldlights, portalverts } /* ============= PrintBSPPackDirectory Dumps a list of files stored in the bsp pack. ============= */ void PrintBSPPackDirectory( void ) { GetPakFile()->PrintDirectory(); } //============================================ int num_entities; entity_t entities[MAX_MAP_ENTITIES]; void StripTrailing (char *e) { char *s; s = e + strlen(e)-1; while (s >= e && *s <= 32) { *s = 0; s--; } } /* ================= ParseEpair ================= */ epair_t *ParseEpair (void) { epair_t *e; e = (epair_t*)malloc (sizeof(epair_t)); memset (e, 0, sizeof(epair_t)); if (strlen(token) >= MAX_KEY-1) Error ("ParseEpar: token too long"); e->key = copystring(token); GetToken (false); if (strlen(token) >= MAX_VALUE-1) Error ("ParseEpar: token too long"); e->value = copystring(token); // strip trailing spaces StripTrailing (e->key); StripTrailing (e->value); return e; } /* ================ ParseEntity ================ */ qboolean ParseEntity (void) { epair_t *e; entity_t *mapent; if (!GetToken (true)) return false; if (strcmp (token, "{") ) Error ("ParseEntity: { not found"); if (num_entities == MAX_MAP_ENTITIES) Error ("num_entities == MAX_MAP_ENTITIES"); mapent = &entities[num_entities]; num_entities++; do { if (!GetToken (true)) Error ("ParseEntity: EOF without closing brace"); if (!strcmp (token, "}") ) break; e = ParseEpair (); e->next = mapent->epairs; mapent->epairs = e; } while (1); return true; } /* ================ ParseEntities Parses the dentdata string into entities ================ */ void ParseEntities (void) { num_entities = 0; ParseFromMemory (dentdata.Base(), dentdata.Count()); while (ParseEntity ()) { } } /* ================ UnparseEntities Generates the dentdata string from all the entities ================ */ void UnparseEntities (void) { epair_t *ep; char line[2048]; int i; char key[1024], value[1024]; CUtlBuffer buffer(0,0,true); buffer.EnsureCapacity( 256 * 1024 ); for (i=0 ; i<num_entities ; i++) { ep = entities[i].epairs; if (!ep) continue; // ent got removed buffer.PutString( "{\n" ); for (ep = entities[i].epairs ; ep ; ep=ep->next) { strcpy (key, ep->key); StripTrailing (key); strcpy (value, ep->value); StripTrailing (value); sprintf(line, "\"%s\" \"%s\"\n", key, value); buffer.PutString( line ); } buffer.PutString("}\n"); } int entdatasize = buffer.TellPut()+1; dentdata.SetSize( entdatasize ); memcpy( dentdata.Base(), buffer.Base(), entdatasize-1 ); dentdata[entdatasize-1] = 0; } void PrintEntity (entity_t *ent) { epair_t *ep; Msg ("------- entity %p -------\n", ent); for (ep=ent->epairs ; ep ; ep=ep->next) { Msg ("%s = %s\n", ep->key, ep->value); } } void SetKeyValue(entity_t *ent, const char *key, const char *value) { epair_t *ep; for (ep=ent->epairs ; ep ; ep=ep->next) if (!strcmp (ep->key, key) ) { free (ep->value); ep->value = copystring(value); return; } ep = (epair_t*)malloc (sizeof(*ep)); ep->next = ent->epairs; ent->epairs = ep; ep->key = copystring(key); ep->value = copystring(value); } char *ValueForKey (entity_t *ent, char *key) { epair_t *ep; for (ep=ent->epairs ; ep ; ep=ep->next) if (!strcmp (ep->key, key) ) return ep->value; return ""; } vec_t FloatForKey (entity_t *ent, char *key) { char *k; k = ValueForKey (ent, key); return atof(k); } int IntForKey (entity_t *ent, char *key) { char *k; k = ValueForKey (ent, key); return atol(k); } void GetVectorForKey (entity_t *ent, char *key, Vector& vec) { char *k; double v1, v2, v3; k = ValueForKey (ent, key); // scanf into doubles, then assign, so it is vec_t size independent v1 = v2 = v3 = 0; sscanf (k, "%lf %lf %lf", &v1, &v2, &v3); vec[0] = v1; vec[1] = v2; vec[2] = v3; } void GetVector2DForKey (entity_t *ent, char *key, Vector2D& vec) { char *k; double v1, v2; k = ValueForKey (ent, key); // scanf into doubles, then assign, so it is vec_t size independent v1 = v2 = 0; sscanf (k, "%lf %lf", &v1, &v2); vec[0] = v1; vec[1] = v2; } void GetAnglesForKey (entity_t *ent, char *key, QAngle& angle) { char *k; double v1, v2, v3; k = ValueForKey (ent, key); // scanf into doubles, then assign, so it is vec_t size independent v1 = v2 = v3 = 0; sscanf (k, "%lf %lf %lf", &v1, &v2, &v3); angle[0] = v1; angle[1] = v2; angle[2] = v3; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BuildFaceCalcWindingData( dface_t *pFace, int *points ) { for( int i = 0; i < pFace->numedges; i++ ) { int eIndex = dsurfedges[pFace->firstedge+i]; if( eIndex < 0 ) { points[i] = dedges[-eIndex].v[1]; } else { points[i] = dedges[eIndex].v[0]; } } } void TriStripToTriList( unsigned short const *pTriStripIndices, int nTriStripIndices, unsigned short **pTriListIndices, int *pnTriListIndices ) { int nMaxTriListIndices = (nTriStripIndices - 2) * 3; *pTriListIndices = new unsigned short[ nMaxTriListIndices ]; *pnTriListIndices = 0; for( int i=0; i < nTriStripIndices - 2; i++ ) { if( pTriStripIndices[i] == pTriStripIndices[i+1] || pTriStripIndices[i] == pTriStripIndices[i+2] || pTriStripIndices[i+1] == pTriStripIndices[i+2] ) { } else { // Flip odd numbered tris.. if( i & 1 ) { (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+2]; (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+1]; (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i]; } else { (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i]; (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+1]; (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+2]; } } } } void CalcTextureCoordsAtPoints( float const texelsPerWorldUnits[2][4], int const subtractOffset[2], Vector const *pPoints, int const nPoints, Vector2D *pCoords ) { for( int i=0; i < nPoints; i++ ) { for( int iCoord=0; iCoord < 2; iCoord++ ) { float *pDestCoord = &pCoords[i][iCoord]; *pDestCoord = 0; for( int iDot=0; iDot < 3; iDot++ ) *pDestCoord += pPoints[i][iDot] * texelsPerWorldUnits[iCoord][iDot]; *pDestCoord += texelsPerWorldUnits[iCoord][3]; *pDestCoord -= subtractOffset[iCoord]; } } } /* ================ CalcFaceExtents Fills in s->texmins[] and s->texsize[] ================ */ void CalcFaceExtents(dface_t *s, int lightmapTextureMinsInLuxels[2], int lightmapTextureSizeInLuxels[2]) { vec_t mins[2], maxs[2], val=0; int i,j, e=0; dvertex_t *v=NULL; texinfo_t *tex=NULL; mins[0] = mins[1] = 1e24; maxs[0] = maxs[1] = -1e24; tex = &texinfo[s->texinfo]; for (i=0 ; i<s->numedges ; i++) { e = dsurfedges[s->firstedge+i]; if (e >= 0) v = dvertexes + dedges[e].v[0]; else v = dvertexes + dedges[-e].v[1]; for (j=0 ; j<2 ; j++) { val = v->point[0] * tex->lightmapVecsLuxelsPerWorldUnits[j][0] + v->point[1] * tex->lightmapVecsLuxelsPerWorldUnits[j][1] + v->point[2] * tex->lightmapVecsLuxelsPerWorldUnits[j][2] + tex->lightmapVecsLuxelsPerWorldUnits[j][3]; if (val < mins[j]) mins[j] = val; if (val > maxs[j]) maxs[j] = val; } } int nMaxLightmapDim = (s->dispinfo == -1) ? MAX_LIGHTMAP_DIM_WITHOUT_BORDER : MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER; for (i=0 ; i<2 ; i++) { mins[i] = ( float )floor( mins[i] ); maxs[i] = ( float )ceil( maxs[i] ); lightmapTextureMinsInLuxels[i] = ( int )mins[i]; lightmapTextureSizeInLuxels[i] = ( int )( maxs[i] - mins[i] ); if( lightmapTextureSizeInLuxels[i] > nMaxLightmapDim + 1 ) { Vector point = vec3_origin; for (int j=0 ; j<s->numedges ; j++) { e = dsurfedges[s->firstedge+j]; v = (e<0)?dvertexes + dedges[-e].v[1] : dvertexes + dedges[e].v[0]; point += v->point; Warning( "Bad surface extents point: %f %f %f\n", v->point.x, v->point.y, v->point.z ); } point *= 1.0f/s->numedges; Error( "Bad surface extents - surface is too big to have a lightmap\n\tmaterial %s around point (%.1f %.1f %.1f)\n\t(dimension: %d, %d>%d)\n", TexDataStringTable_GetString( dtexdata[texinfo[s->texinfo].texdata].nameStringTableID ), point.x, point.y, point.z, ( int )i, ( int )lightmapTextureSizeInLuxels[i], ( int )( nMaxLightmapDim + 1 ) ); } } } void UpdateAllFaceLightmapExtents() { for( int i=0; i < numfaces; i++ ) { dface_t *pFace = &dfaces[i]; if ( texinfo[pFace->texinfo].flags & (SURF_SKY|SURF_NOLIGHT) ) continue; // non-lit texture CalcFaceExtents( pFace, pFace->m_LightmapTextureMinsInLuxels, pFace->m_LightmapTextureSizeInLuxels ); } } //----------------------------------------------------------------------------- // // Helper class to iterate over leaves, used by tools // //----------------------------------------------------------------------------- #define TEST_EPSILON (0.03125) class CToolBSPTree : public ISpatialQuery { public: // Returns the number of leaves int LeafCount() const; // Enumerates the leaves along a ray, box, etc. bool EnumerateLeavesAtPoint( Vector const& pt, ISpatialLeafEnumerator* pEnum, int context ); bool EnumerateLeavesInBox( Vector const& mins, Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context ); bool EnumerateLeavesInSphere( Vector const& center, float radius, ISpatialLeafEnumerator* pEnum, int context ); bool EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context ); }; //----------------------------------------------------------------------------- // Returns the number of leaves //----------------------------------------------------------------------------- int CToolBSPTree::LeafCount() const { return numleafs; } //----------------------------------------------------------------------------- // Enumerates the leaves at a point //----------------------------------------------------------------------------- bool CToolBSPTree::EnumerateLeavesAtPoint( Vector const& pt, ISpatialLeafEnumerator* pEnum, int context ) { int node = 0; while( node >= 0 ) { dnode_t* pNode = &dnodes[node]; dplane_t* pPlane = &dplanes[pNode->planenum]; if (DotProduct( pPlane->normal, pt ) <= pPlane->dist) { node = pNode->children[1]; } else { node = pNode->children[0]; } } return pEnum->EnumerateLeaf( - node - 1, context ); } //----------------------------------------------------------------------------- // Enumerates the leaves in a box //----------------------------------------------------------------------------- static bool EnumerateLeavesInBox_R( int node, Vector const& mins, Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context ) { Vector cornermin, cornermax; while( node >= 0 ) { dnode_t* pNode = &dnodes[node]; dplane_t* pPlane = &dplanes[pNode->planenum]; // Arbitrary split plane here for (int i = 0; i < 3; ++i) { if (pPlane->normal[i] >= 0) { cornermin[i] = mins[i]; cornermax[i] = maxs[i]; } else { cornermin[i] = maxs[i]; cornermax[i] = mins[i]; } } if ( (DotProduct( pPlane->normal, cornermax ) - pPlane->dist) <= -TEST_EPSILON ) { node = pNode->children[1]; } else if ( (DotProduct( pPlane->normal, cornermin ) - pPlane->dist) >= TEST_EPSILON ) { node = pNode->children[0]; } else { if (!EnumerateLeavesInBox_R( pNode->children[0], mins, maxs, pEnum, context )) { return false; } return EnumerateLeavesInBox_R( pNode->children[1], mins, maxs, pEnum, context ); } } return pEnum->EnumerateLeaf( - node - 1, context ); } bool CToolBSPTree::EnumerateLeavesInBox( Vector const& mins, Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context ) { return EnumerateLeavesInBox_R( 0, mins, maxs, pEnum, context ); } //----------------------------------------------------------------------------- // Enumerate leaves within a sphere //----------------------------------------------------------------------------- static bool EnumerateLeavesInSphere_R( int node, Vector const& origin, float radius, ISpatialLeafEnumerator* pEnum, int context ) { while( node >= 0 ) { dnode_t* pNode = &dnodes[node]; dplane_t* pPlane = &dplanes[pNode->planenum]; if (DotProduct( pPlane->normal, origin ) + radius - pPlane->dist <= -TEST_EPSILON ) { node = pNode->children[1]; } else if (DotProduct( pPlane->normal, origin ) - radius - pPlane->dist >= TEST_EPSILON ) { node = pNode->children[0]; } else { if (!EnumerateLeavesInSphere_R( pNode->children[0], origin, radius, pEnum, context )) { return false; } return EnumerateLeavesInSphere_R( pNode->children[1], origin, radius, pEnum, context ); } } return pEnum->EnumerateLeaf( - node - 1, context ); } bool CToolBSPTree::EnumerateLeavesInSphere( Vector const& center, float radius, ISpatialLeafEnumerator* pEnum, int context ) { return EnumerateLeavesInSphere_R( 0, center, radius, pEnum, context ); } //----------------------------------------------------------------------------- // Enumerate leaves along a ray //----------------------------------------------------------------------------- static bool EnumerateLeavesAlongRay_R( int node, Ray_t const& ray, Vector const& start, Vector const& end, ISpatialLeafEnumerator* pEnum, int context ) { float front,back; while (node >= 0) { dnode_t* pNode = &dnodes[node]; dplane_t* pPlane = &dplanes[pNode->planenum]; if ( pPlane->type <= PLANE_Z ) { front = start[pPlane->type] - pPlane->dist; back = end[pPlane->type] - pPlane->dist; } else { front = DotProduct(start, pPlane->normal) - pPlane->dist; back = DotProduct(end, pPlane->normal) - pPlane->dist; } if (front <= -TEST_EPSILON && back <= -TEST_EPSILON) { node = pNode->children[1]; } else if (front >= TEST_EPSILON && back >= TEST_EPSILON) { node = pNode->children[0]; } else { // test the front side first bool side = front < 0; // Compute intersection point based on the original ray float splitfrac; float denom = DotProduct( ray.m_Delta, pPlane->normal ); if ( denom == 0.0f ) { splitfrac = 1.0f; } else { splitfrac = ( pPlane->dist - DotProduct( ray.m_Start, pPlane->normal ) ) / denom; if (splitfrac < 0) splitfrac = 0; else if (splitfrac > 1) splitfrac = 1; } // Compute the split point Vector split; VectorMA( ray.m_Start, splitfrac, ray.m_Delta, split ); bool r = EnumerateLeavesAlongRay_R (pNode->children[side], ray, start, split, pEnum, context ); if (!r) return r; return EnumerateLeavesAlongRay_R (pNode->children[!side], ray, split, end, pEnum, context); } } return pEnum->EnumerateLeaf( - node - 1, context ); } bool CToolBSPTree::EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context ) { if (!ray.m_IsSwept) { Vector mins, maxs; VectorAdd( ray.m_Start, ray.m_Extents, maxs ); VectorSubtract( ray.m_Start, ray.m_Extents, mins ); return EnumerateLeavesInBox_R( 0, mins, maxs, pEnum, context ); } // FIXME: Extruded ray not implemented yet Assert( ray.m_IsRay ); Vector end; VectorAdd( ray.m_Start, ray.m_Delta, end ); return EnumerateLeavesAlongRay_R( 0, ray, ray.m_Start, end, pEnum, context ); } //----------------------------------------------------------------------------- // Singleton accessor //----------------------------------------------------------------------------- ISpatialQuery* ToolBSPTree() { static CToolBSPTree s_ToolBSPTree; return &s_ToolBSPTree; } //----------------------------------------------------------------------------- // Enumerates nodes in front to back order... //----------------------------------------------------------------------------- // FIXME: Do we want this in the IBSPTree interface? static bool EnumerateNodesAlongRay_R( int node, Ray_t const& ray, float start, float end, IBSPNodeEnumerator* pEnum, int context ) { float front, back; float startDotN, deltaDotN; while (node >= 0) { dnode_t* pNode = &dnodes[node]; dplane_t* pPlane = &dplanes[pNode->planenum]; if ( pPlane->type <= PLANE_Z ) { startDotN = ray.m_Start[pPlane->type]; deltaDotN = ray.m_Delta[pPlane->type]; } else { startDotN = DotProduct( ray.m_Start, pPlane->normal ); deltaDotN = DotProduct( ray.m_Delta, pPlane->normal ); } front = startDotN + start * deltaDotN - pPlane->dist; back = startDotN + end * deltaDotN - pPlane->dist; if (front <= -TEST_EPSILON && back <= -TEST_EPSILON) { node = pNode->children[1]; } else if (front >= TEST_EPSILON && back >= TEST_EPSILON) { node = pNode->children[0]; } else { // test the front side first bool side = front < 0; // Compute intersection point based on the original ray float splitfrac; if ( deltaDotN == 0.0f ) { splitfrac = 1.0f; } else { splitfrac = ( pPlane->dist - startDotN ) / deltaDotN; if (splitfrac < 0.0f) splitfrac = 0.0f; else if (splitfrac > 1.0f) splitfrac = 1.0f; } bool r = EnumerateNodesAlongRay_R (pNode->children[side], ray, start, splitfrac, pEnum, context ); if (!r) return r; // Visit the node... if (!pEnum->EnumerateNode( node, ray, splitfrac, context )) return false; return EnumerateNodesAlongRay_R (pNode->children[!side], ray, splitfrac, end, pEnum, context); } } // Visit the leaf... return pEnum->EnumerateLeaf( - node - 1, ray, start, end, context ); } bool EnumerateNodesAlongRay( Ray_t const& ray, IBSPNodeEnumerator* pEnum, int context ) { Vector end; VectorAdd( ray.m_Start, ray.m_Delta, end ); return EnumerateNodesAlongRay_R( 0, ray, 0.0f, 1.0f, pEnum, context ); } //----------------------------------------------------------------------------- // Helps us find all leaves associated with a particular cluster //----------------------------------------------------------------------------- CUtlVector<clusterlist_t> g_ClusterLeaves; void BuildClusterTable( void ) { int i, j; int leafCount; int leafList[MAX_MAP_LEAFS]; g_ClusterLeaves.SetCount( dvis->numclusters ); for ( i = 0; i < dvis->numclusters; i++ ) { leafCount = 0; for ( j = 0; j < numleafs; j++ ) { if ( dleafs[j].cluster == i ) { leafList[ leafCount ] = j; leafCount++; } } g_ClusterLeaves[i].leafCount = leafCount; if ( leafCount ) { g_ClusterLeaves[i].leafs.SetCount( leafCount ); memcpy( g_ClusterLeaves[i].leafs.Base(), leafList, sizeof(int) * leafCount ); } } } // There's a version of this in host.cpp!!! Make sure that they match. void GetPlatformMapPath( const char *pMapPath, char *pPlatformMapPath, int dxlevel, int maxLength ) { Q_StripExtension( pMapPath, pPlatformMapPath, maxLength ); // if( dxlevel <= 60 ) // { // Q_strncat( pPlatformMapPath, "_dx60", maxLength, COPY_ALL_CHARACTERS ); // } Q_strncat( pPlatformMapPath, ".bsp", maxLength, COPY_ALL_CHARACTERS ); } void SetHDRMode( bool bHDR ) { g_bHDR = bHDR; if( bHDR ) { pdlightdata = &dlightdataHDR; g_pLeafAmbientLighting = &g_LeafAmbientLightingHDR; pNumworldlights = &numworldlightsHDR; dworldlights = dworldlightsHDR; #ifdef VRAD extern void VRadDetailProps_SetHDRMode( bool bHDR ); VRadDetailProps_SetHDRMode( bHDR ); #endif } else { pdlightdata = &dlightdataLDR; g_pLeafAmbientLighting = &g_LeafAmbientLightingLDR; pNumworldlights = &numworldlightsLDR; dworldlights = dworldlightsLDR; #ifdef VRAD extern void VRadDetailProps_SetHDRMode( bool bHDR ); VRadDetailProps_SetHDRMode( bHDR ); #endif } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 5821 | Knut Wikstrom |
Added Valve Source code. This is NOT to be commited to other than new code from Valve. |