//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "tier1/strtools.h" #include "macro_texture.h" #include "bsplib.h" #include "cmdlib.h" #include "vtf/vtf.h" #include "tier1/utldict.h" #include "tier1/utlbuffer.h" #include "bitmap/imageformat.h" class CMacroTextureData { public: int m_Width, m_Height; CUtlMemory m_ImageData; }; CMacroTextureData *g_pGlobalMacroTextureData = NULL; // Which macro texture each map face uses. static CUtlDict g_MacroTextureLookup; // Stores a list of unique macro textures. static CUtlVector g_FaceMacroTextures; // Which macro texture each face wants to use. static Vector g_MacroWorldMins, g_MacroWorldMaxs; CMacroTextureData* FindMacroTexture(const char *pFilename) { int index = g_MacroTextureLookup.Find(pFilename); if (g_MacroTextureLookup.IsValidIndex(index)) return g_MacroTextureLookup[index]; else return NULL; } CMacroTextureData* LoadMacroTextureFile(const char *pFilename) { FileHandle_t hFile = g_pFileSystem->Open(pFilename, "rb"); if (hFile == FILESYSTEM_INVALID_HANDLE) return NULL; // Read the file in. CUtlVector tempData; tempData.SetSize(g_pFileSystem->Size(hFile)); g_pFileSystem->Read(tempData.Base(), tempData.Count(), hFile); g_pFileSystem->Close(hFile); // Now feed the data into a CUtlBuffer (great...) CUtlBuffer buf; buf.Put(tempData.Base(), tempData.Count()); // Now make a texture out of it. IVTFTexture *pTex = CreateVTFTexture(); if (!pTex->Unserialize(buf)) Error("IVTFTexture::Unserialize( %s ) failed.", pFilename); pTex->ConvertImageFormat(IMAGE_FORMAT_RGBA8888, false); // Get it in a format we like. // Now convert to a CMacroTextureData. CMacroTextureData *pData = new CMacroTextureData; pData->m_Width = pTex->Width(); pData->m_Height = pTex->Height(); pData->m_ImageData.EnsureCapacity(pData->m_Width * pData->m_Height * 4); memcpy(pData->m_ImageData.Base(), pTex->ImageData(), pData->m_Width * pData->m_Height * 4); DestroyVTFTexture(pTex); Msg("-- LoadMacroTextureFile: %s\n", pFilename); return pData; } void InitMacroTexture(const char *pBSPFilename) { // Get the world bounds (same ones used by minimaps and level designers know how to use). int i = 0; for (i; i < num_entities; ++i) { char* pEntity = ValueForKey(&entities[i], "classname"); if (!strcmp(pEntity, "worldspawn")) { GetVectorForKey(&entities[i], "world_mins", g_MacroWorldMins); GetVectorForKey(&entities[i], "world_maxs", g_MacroWorldMaxs); break; } } if (i == num_entities) { Warning("MaskOnMacroTexture: can't find worldspawn"); return; } // Load the macro texture that is mapped onto everything. char mapName[512], vtfFilename[512]; Q_FileBase(pBSPFilename, mapName, sizeof(mapName)); Q_snprintf(vtfFilename, sizeof(vtfFilename), "materials/macro/%s/base.vtf", mapName); g_pGlobalMacroTextureData = LoadMacroTextureFile(vtfFilename); // Now load the macro texture for each face. g_FaceMacroTextures.SetSize(numfaces); for (int iFace = 0; iFace < numfaces; iFace++) { g_FaceMacroTextures[iFace] = NULL; if (iFace < g_FaceMacroTextureInfos.Count()) { unsigned short stringID = g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID; if (stringID != 0xFFFF) { const char *pMacroTextureName = &g_TexDataStringData[g_TexDataStringTable[stringID]]; Q_snprintf(vtfFilename, sizeof(vtfFilename), "%smaterials/%s.vtf", gamedir, pMacroTextureName); g_FaceMacroTextures[iFace] = FindMacroTexture(vtfFilename); if (!g_FaceMacroTextures[iFace]) { g_FaceMacroTextures[iFace] = LoadMacroTextureFile(vtfFilename); if (g_FaceMacroTextures[iFace]) { g_MacroTextureLookup.Insert(vtfFilename, g_FaceMacroTextures[iFace]); } } } } } } inline Vector SampleMacroTexture(const CMacroTextureData *t, const Vector &vWorldPos) { int ix = (int)RemapVal(vWorldPos.x, g_MacroWorldMins.x, g_MacroWorldMaxs.x, 0, t->m_Width - 0.00001); int iy = (int)RemapVal(vWorldPos.y, g_MacroWorldMins.y, g_MacroWorldMaxs.y, 0, t->m_Height - 0.00001); ix = clamp(ix, 0, t->m_Width - 1); iy = t->m_Height - 1 - clamp(iy, 0, t->m_Height - 1); const unsigned char *pInputColor = &t->m_ImageData[(iy*t->m_Width + ix) * 4]; return Vector(pInputColor[0] / 255.0, pInputColor[1] / 255.0, pInputColor[2] / 255.0); } void ApplyMacroTextures(int iFace, const Vector &vWorldPos, Vector &outLuxel) { // Add the global macro texture. Vector vGlobal; if (g_pGlobalMacroTextureData) outLuxel *= SampleMacroTexture(g_pGlobalMacroTextureData, vWorldPos); // Now add the per-material macro texture. if (g_FaceMacroTextures[iFace]) outLuxel *= SampleMacroTexture(g_FaceMacroTextures[iFace], vWorldPos); }