aboutsummaryrefslogtreecommitdiff
path: root/mp/src/utils/vbsp/cubemap.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/utils/vbsp/cubemap.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/utils/vbsp/cubemap.cpp')
-rw-r--r--mp/src/utils/vbsp/cubemap.cpp1992
1 files changed, 996 insertions, 996 deletions
diff --git a/mp/src/utils/vbsp/cubemap.cpp b/mp/src/utils/vbsp/cubemap.cpp
index 2415115b..aeff12f1 100644
--- a/mp/src/utils/vbsp/cubemap.cpp
+++ b/mp/src/utils/vbsp/cubemap.cpp
@@ -1,996 +1,996 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "vbsp.h"
-#include "bsplib.h"
-#include "tier1/UtlBuffer.h"
-#include "tier1/utlvector.h"
-#include "bitmap/imageformat.h"
-#include <KeyValues.h>
-#include "tier1/strtools.h"
-#include "tier1/utlsymbol.h"
-#include "vtf/vtf.h"
-#include "materialpatch.h"
-#include "materialsystem/imaterialsystem.h"
-#include "materialsystem/imaterial.h"
-#include "materialsystem/imaterialvar.h"
-
-
-/*
- Meager documentation for how the cubemaps are assigned.
-
-
- While loading the map, it calls:
- *** Cubemap_SaveBrushSides
- Builds a list of what cubemaps manually were assigned to what faces
- in s_EnvCubemapToBrushSides.
-
- Immediately after loading the map, it calls:
- *** Cubemap_FixupBrushSidesMaterials
- Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
- side referenced by an env_cubemap manually.
-
- Then it calls Cubemap_AttachDefaultCubemapToSpecularSides:
- *** Cubemap_InitCubemapSideData:
- Setup s_aCubemapSideData.bHasEnvMapInMaterial and bManuallyPickedByAnEnvCubemap for each side.
- bHasEnvMapInMaterial is set if the side's material has $envmap.
- bManuallyPickedByAnEnvCubemap is true if the side was in s_EnvCubemapToBrushSides.
-
- Then, for each bHasEnvMapInMaterial and !bManuallyPickedByAnEnvCubemap (ie: every specular surface that wasn't
- referenced by some env_cubemap), it does Cubemap_CreateTexInfo.
-*/
-
-struct PatchInfo_t
-{
- char *m_pMapName;
- int m_pOrigin[3];
-};
-
-struct CubemapInfo_t
-{
- int m_nTableId;
- bool m_bSpecular;
-};
-
-static bool CubemapLessFunc( const CubemapInfo_t &lhs, const CubemapInfo_t &rhs )
-{
- return ( lhs.m_nTableId < rhs.m_nTableId );
-}
-
-
-typedef CUtlVector<int> IntVector_t;
-static CUtlVector<IntVector_t> s_EnvCubemapToBrushSides;
-
-static CUtlVector<char *> s_DefaultCubemapNames;
-static char g_IsCubemapTexData[MAX_MAP_TEXDATA];
-
-
-struct CubemapSideData_t
-{
- bool bHasEnvMapInMaterial;
- bool bManuallyPickedByAnEnvCubemap;
-};
-
-static CubemapSideData_t s_aCubemapSideData[MAX_MAP_BRUSHSIDES];
-
-
-
-inline bool SideHasCubemapAndWasntManuallyReferenced( int iSide )
-{
- return s_aCubemapSideData[iSide].bHasEnvMapInMaterial && !s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap;
-}
-
-
-void Cubemap_InsertSample( const Vector& origin, int size )
-{
- dcubemapsample_t *pSample = &g_CubemapSamples[g_nCubemapSamples];
- pSample->origin[0] = ( int )origin[0];
- pSample->origin[1] = ( int )origin[1];
- pSample->origin[2] = ( int )origin[2];
- pSample->size = size;
- g_nCubemapSamples++;
-}
-
-static const char *FindSkyboxMaterialName( void )
-{
- for( int i = 0; i < g_MainMap->num_entities; i++ )
- {
- char* pEntity = ValueForKey(&g_MainMap->entities[i], "classname");
- if (!strcmp(pEntity, "worldspawn"))
- {
- return ValueForKey( &g_MainMap->entities[i], "skyname" );
- }
- }
- return NULL;
-}
-
-static void BackSlashToForwardSlash( char *pname )
-{
- while ( *pname ) {
- if ( *pname == '\\' )
- *pname = '/';
- pname++;
- }
-}
-
-static void ForwardSlashToBackSlash( char *pname )
-{
- while ( *pname ) {
- if ( *pname == '/' )
- *pname = '\\';
- pname++;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Finds materials that are used by a particular material
-//-----------------------------------------------------------------------------
-#define MAX_MATERIAL_NAME 512
-
-// This is the list of materialvars which are used in our codebase to look up dependent materials
-static const char *s_pDependentMaterialVar[] =
-{
- "$bottommaterial", // Used by water materials
- "$crackmaterial", // Used by shattered glass materials
- "$fallbackmaterial", // Used by all materials
-
- "", // Always must be last
-};
-
-static const char *FindDependentMaterial( const char *pMaterialName, const char **ppMaterialVar = NULL )
-{
- // FIXME: This is a terrible way of doing this! It creates a dependency
- // between vbsp and *all* code which reads dependent materials from materialvars
- // At the time of writing this function, that means the engine + studiorender.
- // We need a better way of figuring out how to do this, but for now I'm trying to do
- // the fastest solution possible since it's close to ship
-
- static char pDependentMaterialName[MAX_MATERIAL_NAME];
- for( int i = 0; s_pDependentMaterialVar[i][0]; ++i )
- {
- if ( !GetValueFromMaterial( pMaterialName, s_pDependentMaterialVar[i], pDependentMaterialName, MAX_MATERIAL_NAME - 1 ) )
- continue;
-
- if ( !Q_stricmp( pDependentMaterialName, pMaterialName ) )
- {
- Warning( "Material %s is depending on itself through materialvar %s! Ignoring...\n", pMaterialName, s_pDependentMaterialVar[i] );
- continue;
- }
-
- // Return the material var that caused the dependency
- if ( ppMaterialVar )
- {
- *ppMaterialVar = s_pDependentMaterialVar[i];
- }
-
-#ifdef _DEBUG
- // FIXME: Note that this code breaks if a material has more than 1 dependent material
- ++i;
- static char pDependentMaterialName2[MAX_MATERIAL_NAME];
- while( s_pDependentMaterialVar[i][0] )
- {
- Assert( !GetValueFromMaterial( pMaterialName, s_pDependentMaterialVar[i], pDependentMaterialName2, MAX_MATERIAL_NAME - 1 ) );
- ++i;
- }
-#endif
-
- return pDependentMaterialName;
- }
-
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Loads VTF files
-//-----------------------------------------------------------------------------
-static bool LoadSrcVTFFiles( IVTFTexture *pSrcVTFTextures[6], const char *pSkyboxMaterialBaseName,
- int *pUnionTextureFlags, bool bHDR )
-{
- const char *facingName[6] = { "rt", "lf", "bk", "ft", "up", "dn" };
- int i;
- for( i = 0; i < 6; i++ )
- {
- char srcMaterialName[1024];
- sprintf( srcMaterialName, "%s%s", pSkyboxMaterialBaseName, facingName[i] );
-
- IMaterial *pSkyboxMaterial = g_pMaterialSystem->FindMaterial( srcMaterialName, "skybox" );
- //IMaterialVar *pSkyTextureVar = pSkyboxMaterial->FindVar( bHDR ? "$hdrbasetexture" : "$basetexture", NULL ); //, bHDR ? false : true );
- IMaterialVar *pSkyTextureVar = pSkyboxMaterial->FindVar( "$basetexture", NULL ); // Since we're setting it to black anyway, just use $basetexture for HDR
- const char *vtfName = pSkyTextureVar->GetStringValue();
- char srcVTFFileName[MAX_PATH];
- Q_snprintf( srcVTFFileName, MAX_PATH, "materials/%s.vtf", vtfName );
-
- CUtlBuffer buf;
- if ( !g_pFullFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
- {
- // Try looking for a compressed HDR texture
- if ( bHDR )
- {
- /* // FIXME: We need a way to uncompress this format!
- bool bHDRCompressed = true;
-
- pSkyTextureVar = pSkyboxMaterial->FindVar( "$hdrcompressedTexture", NULL );
- vtfName = pSkyTextureVar->GetStringValue();
- Q_snprintf( srcVTFFileName, MAX_PATH, "materials/%s.vtf", vtfName );
-
- if ( !g_pFullFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
- */
- {
- return false;
- }
- }
- else
- {
- return false;
- }
- }
-
- pSrcVTFTextures[i] = CreateVTFTexture();
- if (!pSrcVTFTextures[i]->Unserialize(buf))
- {
- Warning("*** Error unserializing skybox texture: %s\n", pSkyboxMaterialBaseName );
- return false;
- }
-
- *pUnionTextureFlags |= pSrcVTFTextures[i]->Flags();
- int flagsNoAlpha = pSrcVTFTextures[i]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
- int flagsFirstNoAlpha = pSrcVTFTextures[0]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
-
- // NOTE: texture[0] is a side texture that could be 1/2 height, so allow this and also allow 4x4 faces
- if ( ( ( pSrcVTFTextures[i]->Width() != pSrcVTFTextures[0]->Width() ) && ( pSrcVTFTextures[i]->Width() != 4 ) ) ||
- ( ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height() ) && ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height()*2 ) && ( pSrcVTFTextures[i]->Height() != 4 ) ) ||
- ( flagsNoAlpha != flagsFirstNoAlpha ) )
- {
- Warning("*** Error: Skybox vtf files for %s weren't compiled with the same size texture and/or same flags!\n", pSkyboxMaterialBaseName );
- return false;
- }
-
- if ( bHDR )
- {
- pSrcVTFTextures[i]->ConvertImageFormat( IMAGE_FORMAT_RGB323232F, false );
- pSrcVTFTextures[i]->GenerateMipmaps();
- pSrcVTFTextures[i]->ConvertImageFormat( IMAGE_FORMAT_RGBA16161616F, false );
- }
- }
-
- return true;
-}
-
-void VTFNameToHDRVTFName( const char *pSrcName, char *pDest, int maxLen, bool bHDR )
-{
- Q_strncpy( pDest, pSrcName, maxLen );
- if( !bHDR )
- {
- return;
- }
- char *pDot = Q_stristr( pDest, ".vtf" );
- if( !pDot )
- {
- return;
- }
- Q_strncpy( pDot, ".hdr.vtf", maxLen - ( pDot - pDest ) );
-}
-
-#define DEFAULT_CUBEMAP_SIZE 32
-
-void CreateDefaultCubemaps( bool bHDR )
-{
- memset( g_IsCubemapTexData, 0, sizeof(g_IsCubemapTexData) );
-
- // NOTE: This implementation depends on the fact that all VTF files contain
- // all mipmap levels
- const char *pSkyboxBaseName = FindSkyboxMaterialName();
-
- if( !pSkyboxBaseName )
- {
- if( s_DefaultCubemapNames.Count() )
- {
- Warning( "This map uses env_cubemap, and you don't have a skybox, so no default env_cubemaps will be generated.\n" );
- }
- return;
- }
-
- char skyboxMaterialName[MAX_PATH];
- Q_snprintf( skyboxMaterialName, MAX_PATH, "skybox/%s", pSkyboxBaseName );
-
- IVTFTexture *pSrcVTFTextures[6];
-
- int unionTextureFlags = 0;
- if( !LoadSrcVTFFiles( pSrcVTFTextures, skyboxMaterialName, &unionTextureFlags, bHDR ) )
- {
- Warning( "Can't load skybox file %s to build the default cubemap!\n", skyboxMaterialName );
- return;
- }
- Msg( "Creating default %scubemaps for env_cubemap using skybox materials:\n %s*.vmt\n"
- " ! Run buildcubemaps in the engine to get the correct cube maps.\n", bHDR ? "HDR " : "LDR ", skyboxMaterialName );
-
- // Figure out the mip differences between the two textures
- int iMipLevelOffset = 0;
- int tmp = pSrcVTFTextures[0]->Width();
- while( tmp > DEFAULT_CUBEMAP_SIZE )
- {
- iMipLevelOffset++;
- tmp >>= 1;
- }
-
- // Create the destination cubemap
- IVTFTexture *pDstCubemap = CreateVTFTexture();
- pDstCubemap->Init( DEFAULT_CUBEMAP_SIZE, DEFAULT_CUBEMAP_SIZE, 1,
- pSrcVTFTextures[0]->Format(), unionTextureFlags | TEXTUREFLAGS_ENVMAP,
- pSrcVTFTextures[0]->FrameCount() );
-
- // First iterate over all frames
- for (int iFrame = 0; iFrame < pDstCubemap->FrameCount(); ++iFrame)
- {
- // Next iterate over all normal cube faces (we know there's 6 cause it's an envmap)
- for (int iFace = 0; iFace < 6; ++iFace )
- {
- // Finally, iterate over all mip levels in the *destination*
- for (int iMip = 0; iMip < pDstCubemap->MipCount(); ++iMip )
- {
- // Copy the bits from the source images into the cube faces
- unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, iMip + iMipLevelOffset );
- unsigned char *pDstBits = pDstCubemap->ImageData( iFrame, iFace, iMip );
- int iSize = pDstCubemap->ComputeMipSize( iMip );
- int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( iMip + iMipLevelOffset );
-
- // !!! FIXME: Set this to black until HDR cubemaps are built properly!
- memset( pDstBits, 0, iSize );
- continue;
-
- if ( ( pSrcVTFTextures[iFace]->Width() == 4 ) && ( pSrcVTFTextures[iFace]->Height() == 4 ) ) // If texture is 4x4 square
- {
- // Force mip level 2 to get the 1x1 face
- unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, 2 );
- int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( 2 );
-
- // Replicate 1x1 mip level across entire face
- //memset( pDstBits, 0, iSize );
- for ( int i = 0; i < ( iSize / iSrcMipSize ); i++ )
- {
- memcpy( pDstBits + ( i * iSrcMipSize ), pSrcBits, iSrcMipSize );
- }
- }
- else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height() ) // If texture is square
- {
- if ( iSrcMipSize != iSize )
- {
- Warning( "%s - ERROR! Cannot copy square face for default cubemap! iSrcMipSize(%d) != iSize(%d)\n", skyboxMaterialName, iSrcMipSize, iSize );
- memset( pDstBits, 0, iSize );
- }
- else
- {
- // Just copy the mip level
- memcpy( pDstBits, pSrcBits, iSize );
- }
- }
- else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height()*2 ) // If texture is rectangle 2x wide
- {
- int iMipWidth, iMipHeight, iMipDepth;
- pDstCubemap->ComputeMipLevelDimensions( iMip, &iMipWidth, &iMipHeight, &iMipDepth );
- if ( ( iMipHeight > 1 ) && ( iSrcMipSize*2 != iSize ) )
- {
- Warning( "%s - ERROR building default cube map! %d*2 != %d\n", skyboxMaterialName, iSrcMipSize, iSize );
- memset( pDstBits, 0, iSize );
- }
- else
- {
- // Copy row at a time and repeat last row
- memcpy( pDstBits, pSrcBits, iSize/2 );
- //memcpy( pDstBits + iSize/2, pSrcBits, iSize/2 );
- int nSrcRowSize = pSrcVTFTextures[iFace]->RowSizeInBytes( iMip + iMipLevelOffset );
- int nDstRowSize = pDstCubemap->RowSizeInBytes( iMip );
- if ( nSrcRowSize != nDstRowSize )
- {
- Warning( "%s - ERROR building default cube map! nSrcRowSize(%d) != nDstRowSize(%d)!\n", skyboxMaterialName, nSrcRowSize, nDstRowSize );
- memset( pDstBits, 0, iSize );
- }
- else
- {
- for ( int i = 0; i < ( iSize/2 / nSrcRowSize ); i++ )
- {
- memcpy( pDstBits + iSize/2 + i*nSrcRowSize, pSrcBits + iSrcMipSize - nSrcRowSize, nSrcRowSize );
- }
- }
- }
- }
- else
- {
- // ERROR! This code only supports square and rectangluar 2x wide
- Warning( "%s - Couldn't create default cubemap because texture res is %dx%d\n", skyboxMaterialName, pSrcVTFTextures[iFace]->Width(), pSrcVTFTextures[iFace]->Height() );
- memset( pDstBits, 0, iSize );
- return;
- }
- }
- }
- }
-
- ImageFormat originalFormat = pDstCubemap->Format();
- if( !bHDR )
- {
- // Convert the cube to format that we can apply tools to it...
- pDstCubemap->ConvertImageFormat( IMAGE_FORMAT_DEFAULT, false );
- }
-
- // Fixup the cubemap facing
- pDstCubemap->FixCubemapFaceOrientation();
-
- // Now that the bits are in place, compute the spheremaps...
- pDstCubemap->GenerateSpheremap();
-
- if( !bHDR )
- {
- // Convert the cubemap to the final format
- pDstCubemap->ConvertImageFormat( originalFormat, false );
- }
-
- // Write the puppy out!
- char dstVTFFileName[1024];
- if( bHDR )
- {
- sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.hdr.vtf", mapbase );
- }
- else
- {
- sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.vtf", mapbase );
- }
-
- CUtlBuffer outputBuf;
- if (!pDstCubemap->Serialize( outputBuf ))
- {
- Warning( "Error serializing default cubemap %s\n", dstVTFFileName );
- return;
- }
-
- IZip *pak = GetPakFile();
-
- // spit out the default one.
- AddBufferToPak( pak, dstVTFFileName, outputBuf.Base(), outputBuf.TellPut(), false );
-
- // spit out all of the ones that are attached to world geometry.
- int i;
- for( i = 0; i < s_DefaultCubemapNames.Count(); i++ )
- {
- char vtfName[MAX_PATH];
- VTFNameToHDRVTFName( s_DefaultCubemapNames[i], vtfName, MAX_PATH, bHDR );
- if( FileExistsInPak( pak, vtfName ) )
- {
- continue;
- }
- AddBufferToPak( pak, vtfName, outputBuf.Base(),outputBuf.TellPut(), false );
- }
-
- // Clean up the textures
- for( i = 0; i < 6; i++ )
- {
- DestroyVTFTexture( pSrcVTFTextures[i] );
- }
- DestroyVTFTexture( pDstCubemap );
-}
-
-void Cubemap_CreateDefaultCubemaps( void )
-{
- CreateDefaultCubemaps( false );
- CreateDefaultCubemaps( true );
-}
-
-// Builds a list of what cubemaps manually were assigned to what faces
-// in s_EnvCubemapToBrushSides.
-void Cubemap_SaveBrushSides( const char *pSideListStr )
-{
- IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[s_EnvCubemapToBrushSides.AddToTail()];
- char *pTmp = ( char * )_alloca( strlen( pSideListStr ) + 1 );
- strcpy( pTmp, pSideListStr );
- const char *pScan = strtok( pTmp, " " );
- if( !pScan )
- {
- return;
- }
- do
- {
- int brushSideID;
- if( sscanf( pScan, "%d", &brushSideID ) == 1 )
- {
- brushSidesVector.AddToTail( brushSideID );
- }
- } while( ( pScan = strtok( NULL, " " ) ) );
-}
-
-
-//-----------------------------------------------------------------------------
-// Generate patched material name
-//-----------------------------------------------------------------------------
-static void GeneratePatchedName( const char *pMaterialName, const PatchInfo_t &info, bool bMaterialName, char *pBuffer, int nMaxLen )
-{
- const char *pSeparator = bMaterialName ? "_" : "";
- int nLen = Q_snprintf( pBuffer, nMaxLen, "maps/%s/%s%s%d_%d_%d", info.m_pMapName,
- pMaterialName, pSeparator, info.m_pOrigin[0], info.m_pOrigin[1], info.m_pOrigin[2] );
-
- if ( bMaterialName )
- {
- Assert( nLen < TEXTURE_NAME_LENGTH - 1 );
- if ( nLen >= TEXTURE_NAME_LENGTH - 1 )
- {
- Error( "Generated env_cubemap patch name : %s too long! (max = %d)\n", pBuffer, TEXTURE_NAME_LENGTH );
- }
- }
-
- BackSlashToForwardSlash( pBuffer );
- Q_strlower( pBuffer );
-}
-
-
-//-----------------------------------------------------------------------------
-// Patches the $envmap for a material and all its dependents, returns true if any patching happened
-//-----------------------------------------------------------------------------
-static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, const PatchInfo_t &info, const char *pCubemapTexture )
-{
- // Do *NOT* patch the material if there is an $envmap specified and it's not 'env_cubemap'
-
- // FIXME: It's theoretically ok to patch the material if $envmap is not specified,
- // because we're using the 'replace' block, which will only add the env_cubemap if
- // $envmap is specified in the source material. But it will fail if someone adds
- // a specific non-env_cubemap $envmap to the source material at a later point. Bleah
-
- // See if we have an $envmap to patch
- bool bShouldPatchEnvCubemap = DoesMaterialHaveKeyValuePair( pMaterialName, "$envmap", "env_cubemap" );
-
- // See if we have a dependent material to patch
- bool bDependentMaterialPatched = false;
- const char *pDependentMaterialVar = NULL;
- const char *pDependentMaterial = FindDependentMaterial( pMaterialName, &pDependentMaterialVar );
- if ( pDependentMaterial )
- {
- bDependentMaterialPatched = PatchEnvmapForMaterialAndDependents( pDependentMaterial, info, pCubemapTexture );
- }
-
- // If we have neither to patch, we're done
- if ( !bShouldPatchEnvCubemap && !bDependentMaterialPatched )
- return false;
-
- // Otherwise we have to make a patched version of ourselves
- char pPatchedMaterialName[1024];
- GeneratePatchedName( pMaterialName, info, true, pPatchedMaterialName, 1024 );
-
- MaterialPatchInfo_t pPatchInfo[2];
- int nPatchCount = 0;
- if ( bShouldPatchEnvCubemap )
- {
- pPatchInfo[nPatchCount].m_pKey = "$envmap";
- pPatchInfo[nPatchCount].m_pRequiredOriginalValue = "env_cubemap";
- pPatchInfo[nPatchCount].m_pValue = pCubemapTexture;
- ++nPatchCount;
- }
-
- char pDependentPatchedMaterialName[1024];
- if ( bDependentMaterialPatched )
- {
- // FIXME: Annoying! I either have to pass back the patched dependent material name
- // or reconstruct it. Both are sucky.
- GeneratePatchedName( pDependentMaterial, info, true, pDependentPatchedMaterialName, 1024 );
- pPatchInfo[nPatchCount].m_pKey = pDependentMaterialVar;
- pPatchInfo[nPatchCount].m_pValue = pDependentPatchedMaterialName;
- ++nPatchCount;
- }
-
- CreateMaterialPatch( pMaterialName, pPatchedMaterialName, nPatchCount, pPatchInfo, PATCH_REPLACE );
-
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Finds a texinfo that has a particular
-//-----------------------------------------------------------------------------
-
-
-//-----------------------------------------------------------------------------
-// Create a VMT to override the specified texinfo which references the cubemap entity at the specified origin.
-// Returns the index of the new (or preexisting) texinfo referencing that VMT.
-//
-// Also adds the new cubemap VTF filename to s_DefaultCubemapNames so it can copy the
-// default (skybox) cubemap into this file so the cubemap doesn't have the pink checkerboard at
-// runtime before they run buildcubemaps.
-//-----------------------------------------------------------------------------
-static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] )
-{
- // Don't make cubemap tex infos for nodes
- if ( originalTexInfo == TEXINFO_NODE )
- return originalTexInfo;
-
- texinfo_t *pTexInfo = &texinfo[originalTexInfo];
- dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
- const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
- if ( g_IsCubemapTexData[pTexInfo->texdata] )
- {
- Warning("Multiple references for cubemap on texture %s!!!\n", pMaterialName );
- return originalTexInfo;
- }
-
- // Get out of here if the originalTexInfo is already a generated material for this position.
- char pStringToSearchFor[512];
- Q_snprintf( pStringToSearchFor, 512, "_%d_%d_%d", origin[0], origin[1], origin[2] );
- if ( Q_stristr( pMaterialName, pStringToSearchFor ) )
- return originalTexInfo;
-
- // Package up information needed to generate patch names
- PatchInfo_t info;
- info.m_pMapName = mapbase;
- info.m_pOrigin[0] = origin[0];
- info.m_pOrigin[1] = origin[1];
- info.m_pOrigin[2] = origin[2];
-
- // Generate the name of the patched material
- char pGeneratedTexDataName[1024];
- GeneratePatchedName( pMaterialName, info, true, pGeneratedTexDataName, 1024 );
-
- // Make sure the texdata doesn't already exist.
- int nTexDataID = FindTexData( pGeneratedTexDataName );
- bool bHasTexData = (nTexDataID != -1);
- if( !bHasTexData )
- {
- // Generate the new "$envmap" texture name.
- char pTextureName[1024];
- GeneratePatchedName( "c", info, false, pTextureName, 1024 );
-
- // Hook the texture into the material and all dependent materials
- // but if no hooking was necessary, exit out
- if ( !PatchEnvmapForMaterialAndDependents( pMaterialName, info, pTextureName ) )
- return originalTexInfo;
-
- // Store off the name of the cubemap that we need to create since we successfully patched
- char pFileName[1024];
- int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
- int id = s_DefaultCubemapNames.AddToTail();
- s_DefaultCubemapNames[id] = new char[ nLen + 1 ];
- strcpy( s_DefaultCubemapNames[id], pFileName );
-
- // Make a new texdata
- nTexDataID = AddCloneTexData( pTexData, pGeneratedTexDataName );
- g_IsCubemapTexData[nTexDataID] = true;
- }
-
- Assert( nTexDataID != -1 );
-
- texinfo_t newTexInfo;
- newTexInfo = *pTexInfo;
- newTexInfo.texdata = nTexDataID;
-
- int nTexInfoID = -1;
-
- // See if we need to make a new texinfo
- bool bHasTexInfo = false;
- if( bHasTexData )
- {
- nTexInfoID = FindTexInfo( newTexInfo );
- bHasTexInfo = (nTexInfoID != -1);
- }
-
- // Make a new texinfo if we need to.
- if( !bHasTexInfo )
- {
- nTexInfoID = texinfo.AddToTail( newTexInfo );
- }
-
- Assert( nTexInfoID != -1 );
- return nTexInfoID;
-}
-
-static int SideIDToIndex( int brushSideID )
-{
- int i;
- for( i = 0; i < g_MainMap->nummapbrushsides; i++ )
- {
- if( g_MainMap->brushsides[i].id == brushSideID )
- {
- return i;
- }
- }
- return -1;
-}
-
-
-//-----------------------------------------------------------------------------
-// Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
-// side referenced by an env_cubemap manually.
-//-----------------------------------------------------------------------------
-void Cubemap_FixupBrushSidesMaterials( void )
-{
- Msg( "fixing up env_cubemap materials on brush sides...\n" );
- Assert( s_EnvCubemapToBrushSides.Count() == g_nCubemapSamples );
-
- int cubemapID;
- for( cubemapID = 0; cubemapID < g_nCubemapSamples; cubemapID++ )
- {
- IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[cubemapID];
- int i;
- for( i = 0; i < brushSidesVector.Count(); i++ )
- {
- int brushSideID = brushSidesVector[i];
- int sideIndex = SideIDToIndex( brushSideID );
- if( sideIndex < 0 )
- {
- Warning("env_cubemap pointing at deleted brushside near (%d, %d, %d)\n",
- g_CubemapSamples[cubemapID].origin[0], g_CubemapSamples[cubemapID].origin[1], g_CubemapSamples[cubemapID].origin[2] );
-
- continue;
- }
-
- side_t *pSide = &g_MainMap->brushsides[sideIndex];
-
-#ifdef DEBUG
- if ( pSide->pMapDisp )
- {
- Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
- }
-#endif
-
- pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[cubemapID].origin );
- if ( pSide->pMapDisp )
- {
- pSide->pMapDisp->face.texinfo = pSide->texinfo;
- }
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void Cubemap_ResetCubemapSideData( void )
-{
- for ( int iSide = 0; iSide < MAX_MAP_BRUSHSIDES; ++iSide )
- {
- s_aCubemapSideData[iSide].bHasEnvMapInMaterial = false;
- s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap = false;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Returns true if the material or any of its dependents use an $envmap
-//-----------------------------------------------------------------------------
-bool DoesMaterialOrDependentsUseEnvmap( const char *pPatchedMaterialName )
-{
- const char *pOriginalMaterialName = GetOriginalMaterialNameForPatchedMaterial( pPatchedMaterialName );
- if( DoesMaterialHaveKey( pOriginalMaterialName, "$envmap" ) )
- return true;
-
- const char *pDependentMaterial = FindDependentMaterial( pOriginalMaterialName );
- if ( !pDependentMaterial )
- return false;
-
- return DoesMaterialOrDependentsUseEnvmap( pDependentMaterial );
-}
-
-
-//-----------------------------------------------------------------------------
-// Builds a list of all texdatas which need fixing up
-//-----------------------------------------------------------------------------
-void Cubemap_InitCubemapSideData( void )
-{
- // This tree is used to prevent re-parsing material vars multiple times
- CUtlRBTree<CubemapInfo_t> lookup( 0, g_MainMap->nummapbrushsides, CubemapLessFunc );
-
- // Fill in specular data.
- for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
- {
- side_t *pSide = &g_MainMap->brushsides[iSide];
- if ( !pSide )
- continue;
-
- if ( pSide->texinfo == TEXINFO_NODE )
- continue;
-
- texinfo_t *pTex = &texinfo[pSide->texinfo];
- if ( !pTex )
- continue;
-
- dtexdata_t *pTexData = GetTexData( pTex->texdata );
- if ( !pTexData )
- continue;
-
- CubemapInfo_t info;
- info.m_nTableId = pTexData->nameStringTableID;
-
- // Have we encountered this materal? If so, then copy the data we cached off before
- int i = lookup.Find( info );
- if ( i != lookup.InvalidIndex() )
- {
- s_aCubemapSideData[iSide].bHasEnvMapInMaterial = lookup[i].m_bSpecular;
- continue;
- }
-
- // First time we've seen this material. Figure out if it uses env_cubemap
- const char *pPatchedMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
- info.m_bSpecular = DoesMaterialOrDependentsUseEnvmap( pPatchedMaterialName );
- s_aCubemapSideData[ iSide ].bHasEnvMapInMaterial = info.m_bSpecular;
- lookup.Insert( info );
- }
-
- // Fill in cube map data.
- for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
- {
- IntVector_t &sideList = s_EnvCubemapToBrushSides[iCubemap];
- int nSideCount = sideList.Count();
- for ( int iSide = 0; iSide < nSideCount; ++iSide )
- {
- int nSideID = sideList[iSide];
- int nIndex = SideIDToIndex( nSideID );
- if ( nIndex < 0 )
- continue;
-
- s_aCubemapSideData[nIndex].bManuallyPickedByAnEnvCubemap = true;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-int Cubemap_FindClosestCubemap( const Vector &entityOrigin, side_t *pSide )
-{
- if ( !pSide )
- return -1;
-
- // Return a valid (if random) cubemap if there's no winding
- if ( !pSide->winding )
- return 0;
-
- // Calculate the center point.
- Vector vecCenter;
- vecCenter.Init();
-
- for ( int iPoint = 0; iPoint < pSide->winding->numpoints; ++iPoint )
- {
- VectorAdd( vecCenter, pSide->winding->p[iPoint], vecCenter );
- }
- VectorScale( vecCenter, 1.0f / pSide->winding->numpoints, vecCenter );
- vecCenter += entityOrigin;
- plane_t *pPlane = &g_MainMap->mapplanes[pSide->planenum];
-
- // Find the closest cubemap.
- int iMinCubemap = -1;
- float flMinDist = FLT_MAX;
-
- // Look for cubemaps in front of the surface first.
- for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
- {
- dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
- Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
- static_cast<float>( pSample->origin[1] ),
- static_cast<float>( pSample->origin[2] ) );
- Vector vecDelta;
- VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
- float flDist = vecDelta.NormalizeInPlace();
- float flDot = DotProduct( vecDelta, pPlane->normal );
- if ( ( flDot >= 0.0f ) && ( flDist < flMinDist ) )
- {
- flMinDist = flDist;
- iMinCubemap = iCubemap;
- }
- }
-
- // Didn't find anything in front search for closest.
- if( iMinCubemap == -1 )
- {
- for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
- {
- dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
- Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
- static_cast<float>( pSample->origin[1] ),
- static_cast<float>( pSample->origin[2] ) );
- Vector vecDelta;
- VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
- float flDist = vecDelta.Length();
- if ( flDist < flMinDist )
- {
- flMinDist = flDist;
- iMinCubemap = iCubemap;
- }
- }
- }
-
- return iMinCubemap;
-}
-
-
-//-----------------------------------------------------------------------------
-// For every specular surface that wasn't referenced by some env_cubemap, call Cubemap_CreateTexInfo.
-//-----------------------------------------------------------------------------
-void Cubemap_AttachDefaultCubemapToSpecularSides( void )
-{
- Cubemap_ResetCubemapSideData();
- Cubemap_InitCubemapSideData();
-
- // build a mapping from side to entity id so that we can get the entity origin
- CUtlVector<int> sideToEntityIndex;
- sideToEntityIndex.SetCount(g_MainMap->nummapbrushsides);
- int i;
- for ( i = 0; i < g_MainMap->nummapbrushsides; i++ )
- {
- sideToEntityIndex[i] = -1;
- }
-
- for ( i = 0; i < g_MainMap->nummapbrushes; i++ )
- {
- int entityIndex = g_MainMap->mapbrushes[i].entitynum;
- for ( int j = 0; j < g_MainMap->mapbrushes[i].numsides; j++ )
- {
- side_t *side = &g_MainMap->mapbrushes[i].original_sides[j];
- int sideIndex = side - g_MainMap->brushsides;
- sideToEntityIndex[sideIndex] = entityIndex;
- }
- }
-
- for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
- {
- side_t *pSide = &g_MainMap->brushsides[iSide];
- if ( !SideHasCubemapAndWasntManuallyReferenced( iSide ) )
- continue;
-
-
- int currentEntity = sideToEntityIndex[iSide];
-
- int iCubemap = Cubemap_FindClosestCubemap( g_MainMap->entities[currentEntity].origin, pSide );
- if ( iCubemap == -1 )
- continue;
-
-#ifdef DEBUG
- if ( pSide->pMapDisp )
- {
- Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
- }
-#endif
- pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[iCubemap].origin );
- if ( pSide->pMapDisp )
- {
- pSide->pMapDisp->face.texinfo = pSide->texinfo;
- }
- }
-}
-
-// Populate with cubemaps that were skipped
-void Cubemap_AddUnreferencedCubemaps()
-{
- char pTextureName[1024];
- char pFileName[1024];
- PatchInfo_t info;
- dcubemapsample_t *pSample;
- int i,j;
-
- for ( i=0; i<g_nCubemapSamples; ++i )
- {
- pSample = &g_CubemapSamples[i];
-
- // generate the formatted texture name based on cubemap origin
- info.m_pMapName = mapbase;
- info.m_pOrigin[0] = pSample->origin[0];
- info.m_pOrigin[1] = pSample->origin[1];
- info.m_pOrigin[2] = pSample->origin[2];
- GeneratePatchedName( "c", info, false, pTextureName, 1024 );
-
- // find or add
- for ( j=0; j<s_DefaultCubemapNames.Count(); ++j )
- {
- if ( !stricmp( s_DefaultCubemapNames[j], pTextureName ) )
- {
- // already added
- break;
- }
- }
- if ( j == s_DefaultCubemapNames.Count() )
- {
- int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
-
- int id = s_DefaultCubemapNames.AddToTail();
- s_DefaultCubemapNames[id] = new char[nLen + 1];
- strcpy( s_DefaultCubemapNames[id], pFileName );
- }
- }
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "bsplib.h"
+#include "tier1/UtlBuffer.h"
+#include "tier1/utlvector.h"
+#include "bitmap/imageformat.h"
+#include <KeyValues.h>
+#include "tier1/strtools.h"
+#include "tier1/utlsymbol.h"
+#include "vtf/vtf.h"
+#include "materialpatch.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialvar.h"
+
+
+/*
+ Meager documentation for how the cubemaps are assigned.
+
+
+ While loading the map, it calls:
+ *** Cubemap_SaveBrushSides
+ Builds a list of what cubemaps manually were assigned to what faces
+ in s_EnvCubemapToBrushSides.
+
+ Immediately after loading the map, it calls:
+ *** Cubemap_FixupBrushSidesMaterials
+ Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
+ side referenced by an env_cubemap manually.
+
+ Then it calls Cubemap_AttachDefaultCubemapToSpecularSides:
+ *** Cubemap_InitCubemapSideData:
+ Setup s_aCubemapSideData.bHasEnvMapInMaterial and bManuallyPickedByAnEnvCubemap for each side.
+ bHasEnvMapInMaterial is set if the side's material has $envmap.
+ bManuallyPickedByAnEnvCubemap is true if the side was in s_EnvCubemapToBrushSides.
+
+ Then, for each bHasEnvMapInMaterial and !bManuallyPickedByAnEnvCubemap (ie: every specular surface that wasn't
+ referenced by some env_cubemap), it does Cubemap_CreateTexInfo.
+*/
+
+struct PatchInfo_t
+{
+ char *m_pMapName;
+ int m_pOrigin[3];
+};
+
+struct CubemapInfo_t
+{
+ int m_nTableId;
+ bool m_bSpecular;
+};
+
+static bool CubemapLessFunc( const CubemapInfo_t &lhs, const CubemapInfo_t &rhs )
+{
+ return ( lhs.m_nTableId < rhs.m_nTableId );
+}
+
+
+typedef CUtlVector<int> IntVector_t;
+static CUtlVector<IntVector_t> s_EnvCubemapToBrushSides;
+
+static CUtlVector<char *> s_DefaultCubemapNames;
+static char g_IsCubemapTexData[MAX_MAP_TEXDATA];
+
+
+struct CubemapSideData_t
+{
+ bool bHasEnvMapInMaterial;
+ bool bManuallyPickedByAnEnvCubemap;
+};
+
+static CubemapSideData_t s_aCubemapSideData[MAX_MAP_BRUSHSIDES];
+
+
+
+inline bool SideHasCubemapAndWasntManuallyReferenced( int iSide )
+{
+ return s_aCubemapSideData[iSide].bHasEnvMapInMaterial && !s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap;
+}
+
+
+void Cubemap_InsertSample( const Vector& origin, int size )
+{
+ dcubemapsample_t *pSample = &g_CubemapSamples[g_nCubemapSamples];
+ pSample->origin[0] = ( int )origin[0];
+ pSample->origin[1] = ( int )origin[1];
+ pSample->origin[2] = ( int )origin[2];
+ pSample->size = size;
+ g_nCubemapSamples++;
+}
+
+static const char *FindSkyboxMaterialName( void )
+{
+ for( int i = 0; i < g_MainMap->num_entities; i++ )
+ {
+ char* pEntity = ValueForKey(&g_MainMap->entities[i], "classname");
+ if (!strcmp(pEntity, "worldspawn"))
+ {
+ return ValueForKey( &g_MainMap->entities[i], "skyname" );
+ }
+ }
+ return NULL;
+}
+
+static void BackSlashToForwardSlash( char *pname )
+{
+ while ( *pname ) {
+ if ( *pname == '\\' )
+ *pname = '/';
+ pname++;
+ }
+}
+
+static void ForwardSlashToBackSlash( char *pname )
+{
+ while ( *pname ) {
+ if ( *pname == '/' )
+ *pname = '\\';
+ pname++;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds materials that are used by a particular material
+//-----------------------------------------------------------------------------
+#define MAX_MATERIAL_NAME 512
+
+// This is the list of materialvars which are used in our codebase to look up dependent materials
+static const char *s_pDependentMaterialVar[] =
+{
+ "$bottommaterial", // Used by water materials
+ "$crackmaterial", // Used by shattered glass materials
+ "$fallbackmaterial", // Used by all materials
+
+ "", // Always must be last
+};
+
+static const char *FindDependentMaterial( const char *pMaterialName, const char **ppMaterialVar = NULL )
+{
+ // FIXME: This is a terrible way of doing this! It creates a dependency
+ // between vbsp and *all* code which reads dependent materials from materialvars
+ // At the time of writing this function, that means the engine + studiorender.
+ // We need a better way of figuring out how to do this, but for now I'm trying to do
+ // the fastest solution possible since it's close to ship
+
+ static char pDependentMaterialName[MAX_MATERIAL_NAME];
+ for( int i = 0; s_pDependentMaterialVar[i][0]; ++i )
+ {
+ if ( !GetValueFromMaterial( pMaterialName, s_pDependentMaterialVar[i], pDependentMaterialName, MAX_MATERIAL_NAME - 1 ) )
+ continue;
+
+ if ( !Q_stricmp( pDependentMaterialName, pMaterialName ) )
+ {
+ Warning( "Material %s is depending on itself through materialvar %s! Ignoring...\n", pMaterialName, s_pDependentMaterialVar[i] );
+ continue;
+ }
+
+ // Return the material var that caused the dependency
+ if ( ppMaterialVar )
+ {
+ *ppMaterialVar = s_pDependentMaterialVar[i];
+ }
+
+#ifdef _DEBUG
+ // FIXME: Note that this code breaks if a material has more than 1 dependent material
+ ++i;
+ static char pDependentMaterialName2[MAX_MATERIAL_NAME];
+ while( s_pDependentMaterialVar[i][0] )
+ {
+ Assert( !GetValueFromMaterial( pMaterialName, s_pDependentMaterialVar[i], pDependentMaterialName2, MAX_MATERIAL_NAME - 1 ) );
+ ++i;
+ }
+#endif
+
+ return pDependentMaterialName;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads VTF files
+//-----------------------------------------------------------------------------
+static bool LoadSrcVTFFiles( IVTFTexture *pSrcVTFTextures[6], const char *pSkyboxMaterialBaseName,
+ int *pUnionTextureFlags, bool bHDR )
+{
+ const char *facingName[6] = { "rt", "lf", "bk", "ft", "up", "dn" };
+ int i;
+ for( i = 0; i < 6; i++ )
+ {
+ char srcMaterialName[1024];
+ sprintf( srcMaterialName, "%s%s", pSkyboxMaterialBaseName, facingName[i] );
+
+ IMaterial *pSkyboxMaterial = g_pMaterialSystem->FindMaterial( srcMaterialName, "skybox" );
+ //IMaterialVar *pSkyTextureVar = pSkyboxMaterial->FindVar( bHDR ? "$hdrbasetexture" : "$basetexture", NULL ); //, bHDR ? false : true );
+ IMaterialVar *pSkyTextureVar = pSkyboxMaterial->FindVar( "$basetexture", NULL ); // Since we're setting it to black anyway, just use $basetexture for HDR
+ const char *vtfName = pSkyTextureVar->GetStringValue();
+ char srcVTFFileName[MAX_PATH];
+ Q_snprintf( srcVTFFileName, MAX_PATH, "materials/%s.vtf", vtfName );
+
+ CUtlBuffer buf;
+ if ( !g_pFullFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
+ {
+ // Try looking for a compressed HDR texture
+ if ( bHDR )
+ {
+ /* // FIXME: We need a way to uncompress this format!
+ bool bHDRCompressed = true;
+
+ pSkyTextureVar = pSkyboxMaterial->FindVar( "$hdrcompressedTexture", NULL );
+ vtfName = pSkyTextureVar->GetStringValue();
+ Q_snprintf( srcVTFFileName, MAX_PATH, "materials/%s.vtf", vtfName );
+
+ if ( !g_pFullFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
+ */
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ pSrcVTFTextures[i] = CreateVTFTexture();
+ if (!pSrcVTFTextures[i]->Unserialize(buf))
+ {
+ Warning("*** Error unserializing skybox texture: %s\n", pSkyboxMaterialBaseName );
+ return false;
+ }
+
+ *pUnionTextureFlags |= pSrcVTFTextures[i]->Flags();
+ int flagsNoAlpha = pSrcVTFTextures[i]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
+ int flagsFirstNoAlpha = pSrcVTFTextures[0]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
+
+ // NOTE: texture[0] is a side texture that could be 1/2 height, so allow this and also allow 4x4 faces
+ if ( ( ( pSrcVTFTextures[i]->Width() != pSrcVTFTextures[0]->Width() ) && ( pSrcVTFTextures[i]->Width() != 4 ) ) ||
+ ( ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height() ) && ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height()*2 ) && ( pSrcVTFTextures[i]->Height() != 4 ) ) ||
+ ( flagsNoAlpha != flagsFirstNoAlpha ) )
+ {
+ Warning("*** Error: Skybox vtf files for %s weren't compiled with the same size texture and/or same flags!\n", pSkyboxMaterialBaseName );
+ return false;
+ }
+
+ if ( bHDR )
+ {
+ pSrcVTFTextures[i]->ConvertImageFormat( IMAGE_FORMAT_RGB323232F, false );
+ pSrcVTFTextures[i]->GenerateMipmaps();
+ pSrcVTFTextures[i]->ConvertImageFormat( IMAGE_FORMAT_RGBA16161616F, false );
+ }
+ }
+
+ return true;
+}
+
+void VTFNameToHDRVTFName( const char *pSrcName, char *pDest, int maxLen, bool bHDR )
+{
+ Q_strncpy( pDest, pSrcName, maxLen );
+ if( !bHDR )
+ {
+ return;
+ }
+ char *pDot = Q_stristr( pDest, ".vtf" );
+ if( !pDot )
+ {
+ return;
+ }
+ Q_strncpy( pDot, ".hdr.vtf", maxLen - ( pDot - pDest ) );
+}
+
+#define DEFAULT_CUBEMAP_SIZE 32
+
+void CreateDefaultCubemaps( bool bHDR )
+{
+ memset( g_IsCubemapTexData, 0, sizeof(g_IsCubemapTexData) );
+
+ // NOTE: This implementation depends on the fact that all VTF files contain
+ // all mipmap levels
+ const char *pSkyboxBaseName = FindSkyboxMaterialName();
+
+ if( !pSkyboxBaseName )
+ {
+ if( s_DefaultCubemapNames.Count() )
+ {
+ Warning( "This map uses env_cubemap, and you don't have a skybox, so no default env_cubemaps will be generated.\n" );
+ }
+ return;
+ }
+
+ char skyboxMaterialName[MAX_PATH];
+ Q_snprintf( skyboxMaterialName, MAX_PATH, "skybox/%s", pSkyboxBaseName );
+
+ IVTFTexture *pSrcVTFTextures[6];
+
+ int unionTextureFlags = 0;
+ if( !LoadSrcVTFFiles( pSrcVTFTextures, skyboxMaterialName, &unionTextureFlags, bHDR ) )
+ {
+ Warning( "Can't load skybox file %s to build the default cubemap!\n", skyboxMaterialName );
+ return;
+ }
+ Msg( "Creating default %scubemaps for env_cubemap using skybox materials:\n %s*.vmt\n"
+ " ! Run buildcubemaps in the engine to get the correct cube maps.\n", bHDR ? "HDR " : "LDR ", skyboxMaterialName );
+
+ // Figure out the mip differences between the two textures
+ int iMipLevelOffset = 0;
+ int tmp = pSrcVTFTextures[0]->Width();
+ while( tmp > DEFAULT_CUBEMAP_SIZE )
+ {
+ iMipLevelOffset++;
+ tmp >>= 1;
+ }
+
+ // Create the destination cubemap
+ IVTFTexture *pDstCubemap = CreateVTFTexture();
+ pDstCubemap->Init( DEFAULT_CUBEMAP_SIZE, DEFAULT_CUBEMAP_SIZE, 1,
+ pSrcVTFTextures[0]->Format(), unionTextureFlags | TEXTUREFLAGS_ENVMAP,
+ pSrcVTFTextures[0]->FrameCount() );
+
+ // First iterate over all frames
+ for (int iFrame = 0; iFrame < pDstCubemap->FrameCount(); ++iFrame)
+ {
+ // Next iterate over all normal cube faces (we know there's 6 cause it's an envmap)
+ for (int iFace = 0; iFace < 6; ++iFace )
+ {
+ // Finally, iterate over all mip levels in the *destination*
+ for (int iMip = 0; iMip < pDstCubemap->MipCount(); ++iMip )
+ {
+ // Copy the bits from the source images into the cube faces
+ unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, iMip + iMipLevelOffset );
+ unsigned char *pDstBits = pDstCubemap->ImageData( iFrame, iFace, iMip );
+ int iSize = pDstCubemap->ComputeMipSize( iMip );
+ int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( iMip + iMipLevelOffset );
+
+ // !!! FIXME: Set this to black until HDR cubemaps are built properly!
+ memset( pDstBits, 0, iSize );
+ continue;
+
+ if ( ( pSrcVTFTextures[iFace]->Width() == 4 ) && ( pSrcVTFTextures[iFace]->Height() == 4 ) ) // If texture is 4x4 square
+ {
+ // Force mip level 2 to get the 1x1 face
+ unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, 2 );
+ int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( 2 );
+
+ // Replicate 1x1 mip level across entire face
+ //memset( pDstBits, 0, iSize );
+ for ( int i = 0; i < ( iSize / iSrcMipSize ); i++ )
+ {
+ memcpy( pDstBits + ( i * iSrcMipSize ), pSrcBits, iSrcMipSize );
+ }
+ }
+ else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height() ) // If texture is square
+ {
+ if ( iSrcMipSize != iSize )
+ {
+ Warning( "%s - ERROR! Cannot copy square face for default cubemap! iSrcMipSize(%d) != iSize(%d)\n", skyboxMaterialName, iSrcMipSize, iSize );
+ memset( pDstBits, 0, iSize );
+ }
+ else
+ {
+ // Just copy the mip level
+ memcpy( pDstBits, pSrcBits, iSize );
+ }
+ }
+ else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height()*2 ) // If texture is rectangle 2x wide
+ {
+ int iMipWidth, iMipHeight, iMipDepth;
+ pDstCubemap->ComputeMipLevelDimensions( iMip, &iMipWidth, &iMipHeight, &iMipDepth );
+ if ( ( iMipHeight > 1 ) && ( iSrcMipSize*2 != iSize ) )
+ {
+ Warning( "%s - ERROR building default cube map! %d*2 != %d\n", skyboxMaterialName, iSrcMipSize, iSize );
+ memset( pDstBits, 0, iSize );
+ }
+ else
+ {
+ // Copy row at a time and repeat last row
+ memcpy( pDstBits, pSrcBits, iSize/2 );
+ //memcpy( pDstBits + iSize/2, pSrcBits, iSize/2 );
+ int nSrcRowSize = pSrcVTFTextures[iFace]->RowSizeInBytes( iMip + iMipLevelOffset );
+ int nDstRowSize = pDstCubemap->RowSizeInBytes( iMip );
+ if ( nSrcRowSize != nDstRowSize )
+ {
+ Warning( "%s - ERROR building default cube map! nSrcRowSize(%d) != nDstRowSize(%d)!\n", skyboxMaterialName, nSrcRowSize, nDstRowSize );
+ memset( pDstBits, 0, iSize );
+ }
+ else
+ {
+ for ( int i = 0; i < ( iSize/2 / nSrcRowSize ); i++ )
+ {
+ memcpy( pDstBits + iSize/2 + i*nSrcRowSize, pSrcBits + iSrcMipSize - nSrcRowSize, nSrcRowSize );
+ }
+ }
+ }
+ }
+ else
+ {
+ // ERROR! This code only supports square and rectangluar 2x wide
+ Warning( "%s - Couldn't create default cubemap because texture res is %dx%d\n", skyboxMaterialName, pSrcVTFTextures[iFace]->Width(), pSrcVTFTextures[iFace]->Height() );
+ memset( pDstBits, 0, iSize );
+ return;
+ }
+ }
+ }
+ }
+
+ ImageFormat originalFormat = pDstCubemap->Format();
+ if( !bHDR )
+ {
+ // Convert the cube to format that we can apply tools to it...
+ pDstCubemap->ConvertImageFormat( IMAGE_FORMAT_DEFAULT, false );
+ }
+
+ // Fixup the cubemap facing
+ pDstCubemap->FixCubemapFaceOrientation();
+
+ // Now that the bits are in place, compute the spheremaps...
+ pDstCubemap->GenerateSpheremap();
+
+ if( !bHDR )
+ {
+ // Convert the cubemap to the final format
+ pDstCubemap->ConvertImageFormat( originalFormat, false );
+ }
+
+ // Write the puppy out!
+ char dstVTFFileName[1024];
+ if( bHDR )
+ {
+ sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.hdr.vtf", mapbase );
+ }
+ else
+ {
+ sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.vtf", mapbase );
+ }
+
+ CUtlBuffer outputBuf;
+ if (!pDstCubemap->Serialize( outputBuf ))
+ {
+ Warning( "Error serializing default cubemap %s\n", dstVTFFileName );
+ return;
+ }
+
+ IZip *pak = GetPakFile();
+
+ // spit out the default one.
+ AddBufferToPak( pak, dstVTFFileName, outputBuf.Base(), outputBuf.TellPut(), false );
+
+ // spit out all of the ones that are attached to world geometry.
+ int i;
+ for( i = 0; i < s_DefaultCubemapNames.Count(); i++ )
+ {
+ char vtfName[MAX_PATH];
+ VTFNameToHDRVTFName( s_DefaultCubemapNames[i], vtfName, MAX_PATH, bHDR );
+ if( FileExistsInPak( pak, vtfName ) )
+ {
+ continue;
+ }
+ AddBufferToPak( pak, vtfName, outputBuf.Base(),outputBuf.TellPut(), false );
+ }
+
+ // Clean up the textures
+ for( i = 0; i < 6; i++ )
+ {
+ DestroyVTFTexture( pSrcVTFTextures[i] );
+ }
+ DestroyVTFTexture( pDstCubemap );
+}
+
+void Cubemap_CreateDefaultCubemaps( void )
+{
+ CreateDefaultCubemaps( false );
+ CreateDefaultCubemaps( true );
+}
+
+// Builds a list of what cubemaps manually were assigned to what faces
+// in s_EnvCubemapToBrushSides.
+void Cubemap_SaveBrushSides( const char *pSideListStr )
+{
+ IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[s_EnvCubemapToBrushSides.AddToTail()];
+ char *pTmp = ( char * )_alloca( strlen( pSideListStr ) + 1 );
+ strcpy( pTmp, pSideListStr );
+ const char *pScan = strtok( pTmp, " " );
+ if( !pScan )
+ {
+ return;
+ }
+ do
+ {
+ int brushSideID;
+ if( sscanf( pScan, "%d", &brushSideID ) == 1 )
+ {
+ brushSidesVector.AddToTail( brushSideID );
+ }
+ } while( ( pScan = strtok( NULL, " " ) ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Generate patched material name
+//-----------------------------------------------------------------------------
+static void GeneratePatchedName( const char *pMaterialName, const PatchInfo_t &info, bool bMaterialName, char *pBuffer, int nMaxLen )
+{
+ const char *pSeparator = bMaterialName ? "_" : "";
+ int nLen = Q_snprintf( pBuffer, nMaxLen, "maps/%s/%s%s%d_%d_%d", info.m_pMapName,
+ pMaterialName, pSeparator, info.m_pOrigin[0], info.m_pOrigin[1], info.m_pOrigin[2] );
+
+ if ( bMaterialName )
+ {
+ Assert( nLen < TEXTURE_NAME_LENGTH - 1 );
+ if ( nLen >= TEXTURE_NAME_LENGTH - 1 )
+ {
+ Error( "Generated env_cubemap patch name : %s too long! (max = %d)\n", pBuffer, TEXTURE_NAME_LENGTH );
+ }
+ }
+
+ BackSlashToForwardSlash( pBuffer );
+ Q_strlower( pBuffer );
+}
+
+
+//-----------------------------------------------------------------------------
+// Patches the $envmap for a material and all its dependents, returns true if any patching happened
+//-----------------------------------------------------------------------------
+static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, const PatchInfo_t &info, const char *pCubemapTexture )
+{
+ // Do *NOT* patch the material if there is an $envmap specified and it's not 'env_cubemap'
+
+ // FIXME: It's theoretically ok to patch the material if $envmap is not specified,
+ // because we're using the 'replace' block, which will only add the env_cubemap if
+ // $envmap is specified in the source material. But it will fail if someone adds
+ // a specific non-env_cubemap $envmap to the source material at a later point. Bleah
+
+ // See if we have an $envmap to patch
+ bool bShouldPatchEnvCubemap = DoesMaterialHaveKeyValuePair( pMaterialName, "$envmap", "env_cubemap" );
+
+ // See if we have a dependent material to patch
+ bool bDependentMaterialPatched = false;
+ const char *pDependentMaterialVar = NULL;
+ const char *pDependentMaterial = FindDependentMaterial( pMaterialName, &pDependentMaterialVar );
+ if ( pDependentMaterial )
+ {
+ bDependentMaterialPatched = PatchEnvmapForMaterialAndDependents( pDependentMaterial, info, pCubemapTexture );
+ }
+
+ // If we have neither to patch, we're done
+ if ( !bShouldPatchEnvCubemap && !bDependentMaterialPatched )
+ return false;
+
+ // Otherwise we have to make a patched version of ourselves
+ char pPatchedMaterialName[1024];
+ GeneratePatchedName( pMaterialName, info, true, pPatchedMaterialName, 1024 );
+
+ MaterialPatchInfo_t pPatchInfo[2];
+ int nPatchCount = 0;
+ if ( bShouldPatchEnvCubemap )
+ {
+ pPatchInfo[nPatchCount].m_pKey = "$envmap";
+ pPatchInfo[nPatchCount].m_pRequiredOriginalValue = "env_cubemap";
+ pPatchInfo[nPatchCount].m_pValue = pCubemapTexture;
+ ++nPatchCount;
+ }
+
+ char pDependentPatchedMaterialName[1024];
+ if ( bDependentMaterialPatched )
+ {
+ // FIXME: Annoying! I either have to pass back the patched dependent material name
+ // or reconstruct it. Both are sucky.
+ GeneratePatchedName( pDependentMaterial, info, true, pDependentPatchedMaterialName, 1024 );
+ pPatchInfo[nPatchCount].m_pKey = pDependentMaterialVar;
+ pPatchInfo[nPatchCount].m_pValue = pDependentPatchedMaterialName;
+ ++nPatchCount;
+ }
+
+ CreateMaterialPatch( pMaterialName, pPatchedMaterialName, nPatchCount, pPatchInfo, PATCH_REPLACE );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a texinfo that has a particular
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Create a VMT to override the specified texinfo which references the cubemap entity at the specified origin.
+// Returns the index of the new (or preexisting) texinfo referencing that VMT.
+//
+// Also adds the new cubemap VTF filename to s_DefaultCubemapNames so it can copy the
+// default (skybox) cubemap into this file so the cubemap doesn't have the pink checkerboard at
+// runtime before they run buildcubemaps.
+//-----------------------------------------------------------------------------
+static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] )
+{
+ // Don't make cubemap tex infos for nodes
+ if ( originalTexInfo == TEXINFO_NODE )
+ return originalTexInfo;
+
+ texinfo_t *pTexInfo = &texinfo[originalTexInfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+ if ( g_IsCubemapTexData[pTexInfo->texdata] )
+ {
+ Warning("Multiple references for cubemap on texture %s!!!\n", pMaterialName );
+ return originalTexInfo;
+ }
+
+ // Get out of here if the originalTexInfo is already a generated material for this position.
+ char pStringToSearchFor[512];
+ Q_snprintf( pStringToSearchFor, 512, "_%d_%d_%d", origin[0], origin[1], origin[2] );
+ if ( Q_stristr( pMaterialName, pStringToSearchFor ) )
+ return originalTexInfo;
+
+ // Package up information needed to generate patch names
+ PatchInfo_t info;
+ info.m_pMapName = mapbase;
+ info.m_pOrigin[0] = origin[0];
+ info.m_pOrigin[1] = origin[1];
+ info.m_pOrigin[2] = origin[2];
+
+ // Generate the name of the patched material
+ char pGeneratedTexDataName[1024];
+ GeneratePatchedName( pMaterialName, info, true, pGeneratedTexDataName, 1024 );
+
+ // Make sure the texdata doesn't already exist.
+ int nTexDataID = FindTexData( pGeneratedTexDataName );
+ bool bHasTexData = (nTexDataID != -1);
+ if( !bHasTexData )
+ {
+ // Generate the new "$envmap" texture name.
+ char pTextureName[1024];
+ GeneratePatchedName( "c", info, false, pTextureName, 1024 );
+
+ // Hook the texture into the material and all dependent materials
+ // but if no hooking was necessary, exit out
+ if ( !PatchEnvmapForMaterialAndDependents( pMaterialName, info, pTextureName ) )
+ return originalTexInfo;
+
+ // Store off the name of the cubemap that we need to create since we successfully patched
+ char pFileName[1024];
+ int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
+ int id = s_DefaultCubemapNames.AddToTail();
+ s_DefaultCubemapNames[id] = new char[ nLen + 1 ];
+ strcpy( s_DefaultCubemapNames[id], pFileName );
+
+ // Make a new texdata
+ nTexDataID = AddCloneTexData( pTexData, pGeneratedTexDataName );
+ g_IsCubemapTexData[nTexDataID] = true;
+ }
+
+ Assert( nTexDataID != -1 );
+
+ texinfo_t newTexInfo;
+ newTexInfo = *pTexInfo;
+ newTexInfo.texdata = nTexDataID;
+
+ int nTexInfoID = -1;
+
+ // See if we need to make a new texinfo
+ bool bHasTexInfo = false;
+ if( bHasTexData )
+ {
+ nTexInfoID = FindTexInfo( newTexInfo );
+ bHasTexInfo = (nTexInfoID != -1);
+ }
+
+ // Make a new texinfo if we need to.
+ if( !bHasTexInfo )
+ {
+ nTexInfoID = texinfo.AddToTail( newTexInfo );
+ }
+
+ Assert( nTexInfoID != -1 );
+ return nTexInfoID;
+}
+
+static int SideIDToIndex( int brushSideID )
+{
+ int i;
+ for( i = 0; i < g_MainMap->nummapbrushsides; i++ )
+ {
+ if( g_MainMap->brushsides[i].id == brushSideID )
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
+// side referenced by an env_cubemap manually.
+//-----------------------------------------------------------------------------
+void Cubemap_FixupBrushSidesMaterials( void )
+{
+ Msg( "fixing up env_cubemap materials on brush sides...\n" );
+ Assert( s_EnvCubemapToBrushSides.Count() == g_nCubemapSamples );
+
+ int cubemapID;
+ for( cubemapID = 0; cubemapID < g_nCubemapSamples; cubemapID++ )
+ {
+ IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[cubemapID];
+ int i;
+ for( i = 0; i < brushSidesVector.Count(); i++ )
+ {
+ int brushSideID = brushSidesVector[i];
+ int sideIndex = SideIDToIndex( brushSideID );
+ if( sideIndex < 0 )
+ {
+ Warning("env_cubemap pointing at deleted brushside near (%d, %d, %d)\n",
+ g_CubemapSamples[cubemapID].origin[0], g_CubemapSamples[cubemapID].origin[1], g_CubemapSamples[cubemapID].origin[2] );
+
+ continue;
+ }
+
+ side_t *pSide = &g_MainMap->brushsides[sideIndex];
+
+#ifdef DEBUG
+ if ( pSide->pMapDisp )
+ {
+ Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
+ }
+#endif
+
+ pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[cubemapID].origin );
+ if ( pSide->pMapDisp )
+ {
+ pSide->pMapDisp->face.texinfo = pSide->texinfo;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void Cubemap_ResetCubemapSideData( void )
+{
+ for ( int iSide = 0; iSide < MAX_MAP_BRUSHSIDES; ++iSide )
+ {
+ s_aCubemapSideData[iSide].bHasEnvMapInMaterial = false;
+ s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap = false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns true if the material or any of its dependents use an $envmap
+//-----------------------------------------------------------------------------
+bool DoesMaterialOrDependentsUseEnvmap( const char *pPatchedMaterialName )
+{
+ const char *pOriginalMaterialName = GetOriginalMaterialNameForPatchedMaterial( pPatchedMaterialName );
+ if( DoesMaterialHaveKey( pOriginalMaterialName, "$envmap" ) )
+ return true;
+
+ const char *pDependentMaterial = FindDependentMaterial( pOriginalMaterialName );
+ if ( !pDependentMaterial )
+ return false;
+
+ return DoesMaterialOrDependentsUseEnvmap( pDependentMaterial );
+}
+
+
+//-----------------------------------------------------------------------------
+// Builds a list of all texdatas which need fixing up
+//-----------------------------------------------------------------------------
+void Cubemap_InitCubemapSideData( void )
+{
+ // This tree is used to prevent re-parsing material vars multiple times
+ CUtlRBTree<CubemapInfo_t> lookup( 0, g_MainMap->nummapbrushsides, CubemapLessFunc );
+
+ // Fill in specular data.
+ for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
+ {
+ side_t *pSide = &g_MainMap->brushsides[iSide];
+ if ( !pSide )
+ continue;
+
+ if ( pSide->texinfo == TEXINFO_NODE )
+ continue;
+
+ texinfo_t *pTex = &texinfo[pSide->texinfo];
+ if ( !pTex )
+ continue;
+
+ dtexdata_t *pTexData = GetTexData( pTex->texdata );
+ if ( !pTexData )
+ continue;
+
+ CubemapInfo_t info;
+ info.m_nTableId = pTexData->nameStringTableID;
+
+ // Have we encountered this materal? If so, then copy the data we cached off before
+ int i = lookup.Find( info );
+ if ( i != lookup.InvalidIndex() )
+ {
+ s_aCubemapSideData[iSide].bHasEnvMapInMaterial = lookup[i].m_bSpecular;
+ continue;
+ }
+
+ // First time we've seen this material. Figure out if it uses env_cubemap
+ const char *pPatchedMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+ info.m_bSpecular = DoesMaterialOrDependentsUseEnvmap( pPatchedMaterialName );
+ s_aCubemapSideData[ iSide ].bHasEnvMapInMaterial = info.m_bSpecular;
+ lookup.Insert( info );
+ }
+
+ // Fill in cube map data.
+ for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
+ {
+ IntVector_t &sideList = s_EnvCubemapToBrushSides[iCubemap];
+ int nSideCount = sideList.Count();
+ for ( int iSide = 0; iSide < nSideCount; ++iSide )
+ {
+ int nSideID = sideList[iSide];
+ int nIndex = SideIDToIndex( nSideID );
+ if ( nIndex < 0 )
+ continue;
+
+ s_aCubemapSideData[nIndex].bManuallyPickedByAnEnvCubemap = true;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int Cubemap_FindClosestCubemap( const Vector &entityOrigin, side_t *pSide )
+{
+ if ( !pSide )
+ return -1;
+
+ // Return a valid (if random) cubemap if there's no winding
+ if ( !pSide->winding )
+ return 0;
+
+ // Calculate the center point.
+ Vector vecCenter;
+ vecCenter.Init();
+
+ for ( int iPoint = 0; iPoint < pSide->winding->numpoints; ++iPoint )
+ {
+ VectorAdd( vecCenter, pSide->winding->p[iPoint], vecCenter );
+ }
+ VectorScale( vecCenter, 1.0f / pSide->winding->numpoints, vecCenter );
+ vecCenter += entityOrigin;
+ plane_t *pPlane = &g_MainMap->mapplanes[pSide->planenum];
+
+ // Find the closest cubemap.
+ int iMinCubemap = -1;
+ float flMinDist = FLT_MAX;
+
+ // Look for cubemaps in front of the surface first.
+ for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
+ {
+ dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
+ Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
+ static_cast<float>( pSample->origin[1] ),
+ static_cast<float>( pSample->origin[2] ) );
+ Vector vecDelta;
+ VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
+ float flDist = vecDelta.NormalizeInPlace();
+ float flDot = DotProduct( vecDelta, pPlane->normal );
+ if ( ( flDot >= 0.0f ) && ( flDist < flMinDist ) )
+ {
+ flMinDist = flDist;
+ iMinCubemap = iCubemap;
+ }
+ }
+
+ // Didn't find anything in front search for closest.
+ if( iMinCubemap == -1 )
+ {
+ for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
+ {
+ dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
+ Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
+ static_cast<float>( pSample->origin[1] ),
+ static_cast<float>( pSample->origin[2] ) );
+ Vector vecDelta;
+ VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
+ float flDist = vecDelta.Length();
+ if ( flDist < flMinDist )
+ {
+ flMinDist = flDist;
+ iMinCubemap = iCubemap;
+ }
+ }
+ }
+
+ return iMinCubemap;
+}
+
+
+//-----------------------------------------------------------------------------
+// For every specular surface that wasn't referenced by some env_cubemap, call Cubemap_CreateTexInfo.
+//-----------------------------------------------------------------------------
+void Cubemap_AttachDefaultCubemapToSpecularSides( void )
+{
+ Cubemap_ResetCubemapSideData();
+ Cubemap_InitCubemapSideData();
+
+ // build a mapping from side to entity id so that we can get the entity origin
+ CUtlVector<int> sideToEntityIndex;
+ sideToEntityIndex.SetCount(g_MainMap->nummapbrushsides);
+ int i;
+ for ( i = 0; i < g_MainMap->nummapbrushsides; i++ )
+ {
+ sideToEntityIndex[i] = -1;
+ }
+
+ for ( i = 0; i < g_MainMap->nummapbrushes; i++ )
+ {
+ int entityIndex = g_MainMap->mapbrushes[i].entitynum;
+ for ( int j = 0; j < g_MainMap->mapbrushes[i].numsides; j++ )
+ {
+ side_t *side = &g_MainMap->mapbrushes[i].original_sides[j];
+ int sideIndex = side - g_MainMap->brushsides;
+ sideToEntityIndex[sideIndex] = entityIndex;
+ }
+ }
+
+ for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
+ {
+ side_t *pSide = &g_MainMap->brushsides[iSide];
+ if ( !SideHasCubemapAndWasntManuallyReferenced( iSide ) )
+ continue;
+
+
+ int currentEntity = sideToEntityIndex[iSide];
+
+ int iCubemap = Cubemap_FindClosestCubemap( g_MainMap->entities[currentEntity].origin, pSide );
+ if ( iCubemap == -1 )
+ continue;
+
+#ifdef DEBUG
+ if ( pSide->pMapDisp )
+ {
+ Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
+ }
+#endif
+ pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[iCubemap].origin );
+ if ( pSide->pMapDisp )
+ {
+ pSide->pMapDisp->face.texinfo = pSide->texinfo;
+ }
+ }
+}
+
+// Populate with cubemaps that were skipped
+void Cubemap_AddUnreferencedCubemaps()
+{
+ char pTextureName[1024];
+ char pFileName[1024];
+ PatchInfo_t info;
+ dcubemapsample_t *pSample;
+ int i,j;
+
+ for ( i=0; i<g_nCubemapSamples; ++i )
+ {
+ pSample = &g_CubemapSamples[i];
+
+ // generate the formatted texture name based on cubemap origin
+ info.m_pMapName = mapbase;
+ info.m_pOrigin[0] = pSample->origin[0];
+ info.m_pOrigin[1] = pSample->origin[1];
+ info.m_pOrigin[2] = pSample->origin[2];
+ GeneratePatchedName( "c", info, false, pTextureName, 1024 );
+
+ // find or add
+ for ( j=0; j<s_DefaultCubemapNames.Count(); ++j )
+ {
+ if ( !stricmp( s_DefaultCubemapNames[j], pTextureName ) )
+ {
+ // already added
+ break;
+ }
+ }
+ if ( j == s_DefaultCubemapNames.Count() )
+ {
+ int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
+
+ int id = s_DefaultCubemapNames.AddToTail();
+ s_DefaultCubemapNames[id] = new char[nLen + 1];
+ strcpy( s_DefaultCubemapNames[id], pFileName );
+ }
+ }
+}