//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============ // // Purpose: // // $NoKeywords: $ //============================================================================= #include "cbase.h" // identifier was truncated to '255' characters in the debug information #pragma warning(disable: 4786) #include "ProxyEntity.h" #include "materialsystem/IMaterialVar.h" #include "materialsystem/ITexture.h" #include "bitmap/TGAWriter.h" #include "bitmap/TGALoader.h" #include "view.h" #include "materialsystem/IMaterial.h" //#include "cache_user.h" #include "vtf/vtf.h" class CCamoMaterialProxy; class CCamoTextureRegen : public ITextureRegenerator { public: CCamoTextureRegen(CCamoMaterialProxy *pProxy) : m_pProxy(pProxy) {} virtual void RegenerateTextureBits(ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pSubRect); virtual void Release() {} private: CCamoMaterialProxy *m_pProxy; }; class CCamoMaterialProxy : public CEntityMaterialProxy { public: CCamoMaterialProxy(); virtual ~CCamoMaterialProxy(); virtual bool Init(IMaterial *pMaterial, KeyValues *pKeyValues); virtual void OnBind(C_BaseEntity *pC_BaseEntity); virtual IMaterial *GetMaterial(); // Procedurally generates the camo texture... void GenerateCamoTexture(ITexture* pTexture, IVTFTexture *pVTFTexture); protected: #if 0 virtual void SetInstanceDataSize(int size); virtual void *FindInstanceData(C_BaseEntity *pEntity); virtual void *AllocateInstanceData(C_BaseEntity *pEntity); #endif private: void LoadCamoPattern(void); void GenerateRandomPointsInNormalizedCube(void); void GetColors(Vector &lighting, Vector &base, int index, const Vector &boxMin, const Vector &boxExtents, const Vector &forward, const Vector &right, const Vector &up, const Vector& entityPosition); // this needs to go in a base class private: #if 0 // stuff that needs to be in a base class. struct InstanceData_t { C_BaseEntity *pEntity; void *data; struct InstanceData_s *next; }; struct CamoInstanceData_t { int dummy; }; #endif unsigned char *m_pCamoPatternImage; #if 0 int m_InstanceDataSize; InstanceData_t *m_InstanceDataListHead; #endif IMaterial *m_pMaterial; IMaterialVar *m_pCamoTextureVar; IMaterialVar *m_pCamoPatternTextureVar; Vector *m_pointsInNormalizedBox; // [m_CamoPatternNumColors] int m_CamoPatternNumColors; int m_CamoPatternWidth; int m_CamoPatternHeight; #if 0 cache_user_t m_camoImageDataCache; #endif unsigned char m_CamoPalette[256][3]; // these represent that part of the entitiy's bounding box that we // want to cast rays through to get colors for the camo Vector m_SubBoundingBoxMin; // normalized Vector m_SubBoundingBoxMax; // normalized CCamoTextureRegen m_TextureRegen; C_BaseEntity *m_pEnt; // VXP ITexture *pCamoTexture; // VXP: Made this because of GetTextureValue cannot do shit at destructor }; void CCamoTextureRegen::RegenerateTextureBits(ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pSubRect) { pVTFTexture->ConvertImageFormat(IMAGE_FORMAT_RGB888, false); // VXP: FIXME: May this decrease performance? m_pProxy->GenerateCamoTexture(pTexture, pVTFTexture); } #pragma warning (disable:4355) CCamoMaterialProxy::CCamoMaterialProxy() : m_TextureRegen(this) { #if 0 m_InstanceDataSize = 0; #endif #if 0 memset(&m_camoImageDataCache, 0, sizeof(m_camoImageDataCache)); #endif m_pointsInNormalizedBox = NULL; #if 0 m_InstanceDataListHead = NULL; #endif m_pCamoPatternImage = NULL; m_pMaterial = NULL; m_pCamoTextureVar = NULL; m_pCamoPatternTextureVar = NULL; m_pointsInNormalizedBox = NULL; m_pEnt = NULL; } #pragma warning (default:4355) CCamoMaterialProxy::~CCamoMaterialProxy() { #if 0 InstanceData_t *curr = m_InstanceDataListHead; while (curr) { InstanceData_t *next; next = curr->next; delete curr; curr = next; } m_InstanceDataListHead = NULL; #endif // Disconnect the texture regenerator... if (m_pCamoTextureVar) { // ITexture *pCamoTexture = m_pCamoTextureVar->GetTextureValue(); if (pCamoTexture) { pCamoTexture->SetTextureRegenerator(NULL); // VXP pCamoTexture->DecrementReferenceCount(); pCamoTexture = NULL; } m_pCamoTextureVar = NULL; // VXP } if (m_pCamoPatternImage != NULL) { delete[] m_pCamoPatternImage; // VXP: Was w/o [] m_pCamoPatternImage = NULL; } if (m_pointsInNormalizedBox != NULL) { delete[] m_pointsInNormalizedBox; // VXP: Was w/o [] m_pointsInNormalizedBox = NULL; } } IMaterial *CCamoMaterialProxy::GetMaterial() { return NULL; } #if 0 void CCamoMaterialProxy::SetInstanceDataSize(int size) { m_InstanceDataSize = size; } #endif #if 0 void *CCamoMaterialProxy::FindInstanceData(C_BaseEntity *pEntity) { InstanceData_t *curr = m_InstanceDataListHead; while (curr) { if (pEntity == curr->pEntity) { return curr->data; } curr = curr->next; } return NULL; } #endif #if 0 void *CCamoMaterialProxy::AllocateInstanceData(C_BaseEntity *pEntity) { InstanceData_t *newData = new InstanceData_t; newData->pEntity = pEntity; newData->next = m_InstanceDataListHead; m_InstanceDataListHead = newData; newData->data = new unsigned char[m_InstanceDataSize]; return newData->data; } #endif bool CCamoMaterialProxy::Init(IMaterial *pMaterial, KeyValues *pKeyValues) { // return false; // hack! Need to make sure that the TGA loader has a valid filesystem before trying // to load the camo pattern. #if 0 // set how big our instance data is. SetInstanceDataSize(sizeof(CamoInstanceData_t)); #endif // remember what material we belong to. m_pMaterial = pMaterial; // get pointers to material vars. bool found; m_pCamoTextureVar = m_pMaterial->FindVar("$baseTexture", &found); if (!found) { m_pCamoTextureVar = NULL; Warning("Cannot initialize Camo because of $baseTexture\n"); return false; } // ITexture *pCamoTexture = m_pCamoTextureVar->GetTextureValue(); pCamoTexture = m_pCamoTextureVar->GetTextureValue(); if (pCamoTexture) pCamoTexture->SetTextureRegenerator(&m_TextureRegen); // Need to get the palettized texture to create the procedural texture from // somewhere. m_pCamoPatternTextureVar = m_pMaterial->FindVar("$camoPatternTexture", &found); if (!found) { m_pCamoTextureVar = NULL; Warning("Cannot initialize Camo because of $camoPatternTexture\n"); return false; } IMaterialVar *subBoundingBoxMinVar, *subBoundingBoxMaxVar; subBoundingBoxMinVar = m_pMaterial->FindVar("$camoBoundingBoxMin", &found, false); if (!found) { m_SubBoundingBoxMin = Vector(0.0f, 0.0f, 0.0f); } else { subBoundingBoxMinVar->GetVecValue(m_SubBoundingBoxMin.Base(), 3); } subBoundingBoxMaxVar = m_pMaterial->FindVar("$camoBoundingBoxMax", &found, false); if (!found) { m_SubBoundingBoxMax = Vector(1.0f, 1.0f, 1.0f); } else { subBoundingBoxMaxVar->GetVecValue(m_SubBoundingBoxMax.Base(), 3); } LoadCamoPattern(); GenerateRandomPointsInNormalizedCube(); return true; } void CCamoMaterialProxy::GetColors(Vector &diffuseColor, Vector &baseColor, int index, const Vector &boxMin, const Vector &boxExtents, const Vector &forward, const Vector &right, const Vector &up, const Vector& entityPosition) { Vector position, transformedPosition; // hack // m_pointsInNormalizedBox[index] = Vector( 0.5f, 0.5f, 1.0f ); position[0] = m_pointsInNormalizedBox[index][0] * boxExtents[0] + boxMin[0]; position[1] = m_pointsInNormalizedBox[index][1] * boxExtents[1] + boxMin[1]; position[2] = m_pointsInNormalizedBox[index][2] * boxExtents[2] + boxMin[2]; transformedPosition[0] = right[0] * position[0] + forward[0] * position[1] + up[0] * position[2]; transformedPosition[1] = right[1] * position[0] + forward[1] * position[1] + up[1] * position[2]; transformedPosition[2] = right[2] * position[0] + forward[2] * position[1] + up[2] * position[2]; transformedPosition = transformedPosition + entityPosition; Vector direction = transformedPosition - CurrentViewOrigin(); VectorNormalize(direction); direction = direction * (COORD_EXTENT * 1.74f); Vector endPoint = position + direction; // baseColor is already in gamma space // engine->TraceLineMaterialAndLighting( g_vecInstantaneousRenderOrigin, endPoint, diffuseColor, baseColor ); engine->TraceLineMaterialAndLighting(transformedPosition, endPoint, diffuseColor, baseColor); // hack - optimize! - convert from linear to gamma space - this should be hidden diffuseColor[0] = pow(diffuseColor[0], 1.0f / 2.2f); diffuseColor[1] = pow(diffuseColor[1], 1.0f / 2.2f); diffuseColor[2] = pow(diffuseColor[2], 1.0f / 2.2f); #if 0 Msg("%f %f %f\n", diffuseColor[0], diffuseColor[1], diffuseColor[2]); #endif #if 0 float max; max = diffuseColor[0]; if (diffuseColor[1] > max) { max = diffuseColor[1]; } if (diffuseColor[2] > max) { max = diffuseColor[2]; } if (max > 1.0f) { max = 1.0f / max; diffuseColor = diffuseColor * max; } #else if (diffuseColor[0] > 1.0f) { diffuseColor[0] = 1.0f; } if (diffuseColor[1] > 1.0f) { diffuseColor[1] = 1.0f; } if (diffuseColor[2] > 1.0f) { diffuseColor[2] = 1.0f; } #endif // hack //baseColor = Vector( 1.0f, 1.0f, 1.0f ); //diffuseColor = Vector( 1.0f, 1.0f, 1.0f ); } //----------------------------------------------------------------------------- // Procedurally generates the camo texture... //----------------------------------------------------------------------------- void CCamoMaterialProxy::GenerateCamoTexture(ITexture* pTexture, IVTFTexture *pVTFTexture) { if (!m_pEnt) return; #if 0 CamoInstanceData_t *pInstanceData; pInstanceData = (CamoInstanceData_t *)FindInstanceData(pEnt); if (!pInstanceData) { pInstanceData = (CamoInstanceData_t *)AllocateInstanceData(pEnt); if (!pInstanceData) { return; } // init the instance data } #endif Vector entityPosition; entityPosition = m_pEnt->GetAbsOrigin(); QAngle entityAngles; entityAngles = m_pEnt->GetAbsAngles(); // Get the bounding box for the entity Vector mins, maxs; mins = m_pEnt->WorldAlignMins(); maxs = m_pEnt->WorldAlignMaxs(); Vector traceDirection; Vector traceEnd; trace_t traceResult; Vector forward, right, up; AngleVectors(entityAngles, &forward, &right, &up); Vector position, transformedPosition; Vector maxsMinusMins = maxs - mins; Vector diffuseColor[256]; Vector baseColor; unsigned char camoPalette[256][3]; // Calculate the camo palette //Msg( "start of loop\n" ); int i; for (i = 0; i < m_CamoPatternNumColors; i++) { GetColors(diffuseColor[i], baseColor, i, mins, maxsMinusMins, forward, right, up, entityPosition); #if 1 camoPalette[i][0] = diffuseColor[i][0] * baseColor[0] * 255.0f; camoPalette[i][1] = diffuseColor[i][1] * baseColor[1] * 255.0f; camoPalette[i][2] = diffuseColor[i][2] * baseColor[2] * 255.0f; #endif #if 0 camoPalette[i][0] = baseColor[0] * 255.0f; camoPalette[i][1] = baseColor[1] * 255.0f; camoPalette[i][2] = baseColor[2] * 255.0f; #endif #if 0 camoPalette[i][0] = diffuseColor[i][0] * 255.0f; camoPalette[i][1] = diffuseColor[i][1] * 255.0f; camoPalette[i][2] = diffuseColor[i][2] * 255.0f; #endif } int width = pVTFTexture->Width(); int height = pVTFTexture->Height(); if (width != m_CamoPatternWidth || height != m_CamoPatternHeight) { Warning("Can't generate camo texture, wrong sizes\n"); return; } unsigned char *imageData = pVTFTexture->ImageData(0, 0, 0); enum ImageFormat imageFormat = pVTFTexture->Format(); if (imageFormat != IMAGE_FORMAT_RGB888) { Warning("Can't generate camo texture, need to be RGB888\n"); return; } // optimize #if 1 int x, y; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { int offset = 3 * (x + y * width); assert(offset < width * height * 3); int paletteID = m_pCamoPatternImage[x + y * width]; assert(paletteID < 256); #if 1 imageData[offset + 0] = camoPalette[paletteID][0]; imageData[offset + 1] = camoPalette[paletteID][1]; imageData[offset + 2] = camoPalette[paletteID][2]; #else imageData[offset] = 255; imageData[offset + 1] = 0; imageData[offset + 2] = 0; #endif } } #endif } //----------------------------------------------------------------------------- // Called when the texture is bound... //----------------------------------------------------------------------------- void CCamoMaterialProxy::OnBind(C_BaseEntity *pEntity) { if (!m_pCamoTextureVar) { return; } m_pEnt = pEntity; // ITexture *pCamoTexture = m_pCamoTextureVar->GetTextureValue(); if (pCamoTexture) pCamoTexture->Download(); // Mark it so it doesn't get regenerated on task switch m_pEnt = NULL; } void CCamoMaterialProxy::LoadCamoPattern(void) { #if 0 // hack - need to figure out a name to attach that isn't too long. m_pCamoPatternImage = (unsigned char *)engineCache->FindByName(&m_camoImageDataCache, "camopattern"); if (m_pCamoPatternImage) { // is already in the cache. return m_pCamoPatternImage; } #endif enum ImageFormat indexImageFormat; int indexImageSize; float dummyGamma; if (!TGALoader::GetInfo(m_pCamoPatternTextureVar->GetStringValue(), &m_CamoPatternWidth, &m_CamoPatternHeight, &indexImageFormat, &dummyGamma)) { //Warning( "Can't get tga info for hl2/materials/models/combine_elite/camo7paletted.tga for camo material\n" ); Warning("Can't get tga info for %s for camo material\n", m_pCamoPatternTextureVar->GetStringValue()); m_pCamoTextureVar = NULL; return; } if (indexImageFormat != IMAGE_FORMAT_I8) { // Warning( "Camo material texture hl2/materials/models/combine_elite/camo7paletted.tga must be 8-bit greyscale\n" ); Warning("Camo material texture %s must be 8-bit greyscale\n", m_pCamoPatternTextureVar->GetStringValue()); m_pCamoTextureVar = NULL; return; } indexImageSize = ImageLoader::GetMemRequired(m_CamoPatternWidth, m_CamoPatternHeight, 1, indexImageFormat, false); DevMsg("indexImageSize = %i\n", indexImageSize); #if 0 m_pCamoPatternImage = (unsigned char *) engineCache->Alloc(&m_camoImageDataCache, indexImageSize, "camopattern"); #endif m_pCamoPatternImage = (unsigned char *)new unsigned char[indexImageSize]; if (!m_pCamoPatternImage) { m_pCamoTextureVar = NULL; Warning("Cannot read pattern from tga for Camo proxy\n"); return; } if (!TGALoader::Load(m_pCamoPatternImage, m_pCamoPatternTextureVar->GetStringValue(), m_CamoPatternWidth, m_CamoPatternHeight, IMAGE_FORMAT_I8, dummyGamma, false)) { // Warning( "camo texture hl2/materials/models/combine_elite/camo7paletted.tga must be grey-scale" ); Warning("camo texture %s must be grey-scale\n", m_pCamoPatternTextureVar->GetStringValue()); m_pCamoTextureVar = NULL; return; } bool colorUsed[256]; int colorRemap[256]; // count the number of colors used in the image. int i; for (i = 0; i < 256; i++) { colorUsed[i] = false; } for (i = 0; i < indexImageSize; i++) { colorUsed[m_pCamoPatternImage[i]] = true; } m_CamoPatternNumColors = 0; for (i = 0; i < 256; i++) { if (colorUsed[i]) { colorRemap[i] = m_CamoPatternNumColors; m_CamoPatternNumColors++; } } // remap the color to the beginning of the palette. for (i = 0; i < indexImageSize; i++) { m_pCamoPatternImage[i] = colorRemap[m_pCamoPatternImage[i]]; // hack // m_pCamoPatternImage[i] = 0; } } void CCamoMaterialProxy::GenerateRandomPointsInNormalizedCube(void) { if (m_CamoPatternNumColors <= 0) { Warning("Cannot generate random point for Camo proxy\n"); return; } m_pointsInNormalizedBox = new Vector[m_CamoPatternNumColors]; if (!m_pointsInNormalizedBox) { m_pCamoTextureVar = NULL; return; } int i; for (i = 0; i < m_CamoPatternNumColors; i++) { m_pointsInNormalizedBox[i][0] = random->RandomFloat(m_SubBoundingBoxMin[0], m_SubBoundingBoxMax[0]); m_pointsInNormalizedBox[i][1] = random->RandomFloat(m_SubBoundingBoxMin[1], m_SubBoundingBoxMax[1]); m_pointsInNormalizedBox[i][2] = random->RandomFloat(m_SubBoundingBoxMin[2], m_SubBoundingBoxMax[2]); } } EXPOSE_INTERFACE(CCamoMaterialProxy, IMaterialProxy, "Camo" IMATERIAL_PROXY_INTERFACE_VERSION);