/* ** Command & Conquer Generals(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : W3DAssetManager * * * * $Archive:: $* * * * Original Author:: Hector Yee (Enbassetmgr) * Added/Modified:: Mark Wilczynski * * * $Author:: $* * * * $Modtime:: $* * * * $Revision:: $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include #include "W3DDevice/GameClient/W3DAssetManager.h" #include "proto.h" #include "rendobj.h" #include #include "mesh.h" #include "hlod.h" #include "matinfo.h" #include "meshmdl.h" #include "part_emt.h" #include "vertmaterial.h" #include "dx8wrapper.h" #include "texture.h" #include "surfaceclass.h" #include "textureloader.h" #include "ww3dformat.h" #include "colorspace.h" #include #include "wwmemlog.h" #include "ffactory.h" #include "font3d.h" #include "render2dsentence.h" #include #include "W3DDevice/GameClient/W3DGranny.h" #include "Common/PerfTimer.h" #include "Common/GlobalData.h" #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif //--------------------------------------------------------------------- // Constants //--------------------------------------------------------------------- const float ident_scale(1.0f); const float scale_epsilon(0.01f); const Vector3 ident_HSV(0,0,0); const float H_epsilon(1.0f); const float S_epsilon(0.01f); const float V_epsilon(0.01f); //--------------------------------------------------------------------- // Externs defined somewhere in W3D. //--------------------------------------------------------------------- unsigned int PixelSize(const SurfaceClass::SurfaceDescription &sd); void Convert_Pixel(Vector3 &rgb, const SurfaceClass::SurfaceDescription &sd, const unsigned char * pixel); void Convert_Pixel(unsigned char * pixel,const SurfaceClass::SurfaceDescription &sd, const Vector3 &rgb); //--------------------------------------------------------------------- // W3DPrototype //--------------------------------------------------------------------- //--------------------------------------------------------------------- class W3DPrototypeClass : public MemoryPoolObject, public PrototypeClass { MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( W3DPrototypeClass, "W3DPrototypeClass" ) public: W3DPrototypeClass(RenderObjClass * proto, const AsciiString& name); virtual const char* Get_Name(void) const { return Name.str(); } virtual int Get_Class_ID(void) const { return Proto->Class_ID(); } virtual RenderObjClass * Create(void); virtual void DeleteSelf() { deleteInstance(); } protected: //virtual ~W3DPrototypeClass(void); private: RenderObjClass * Proto; AsciiString Name; }; //--------------------------------------------------------------------- W3DPrototypeClass::W3DPrototypeClass(RenderObjClass * proto, const AsciiString& name) : Proto(proto), Name(name) { assert(Proto); Proto->Add_Ref(); } //--------------------------------------------------------------------- W3DPrototypeClass::~W3DPrototypeClass(void) { if (Proto) { Proto->Release_Ref(); Proto = NULL; } } //--------------------------------------------------------------------- RenderObjClass * W3DPrototypeClass::Create(void) { return (RenderObjClass *)( SET_REF_OWNER( Proto->Clone() ) ); } //--------------------------------------------------------------------- // W3DAssetManager //--------------------------------------------------------------------- //--------------------------------------------------------------------- W3DAssetManager::W3DAssetManager(void) { #ifdef INCLUDE_GRANNY_IN_BUILD m_GrannyAnimManager = NEW GrannyAnimManagerClass; #endif } //--------------------------------------------------------------------- W3DAssetManager::~W3DAssetManager(void) { #ifdef INCLUDE_GRANNY_IN_BUILD delete m_GrannyAnimManager; #endif } #ifdef DUMP_PERF_STATS __int64 Total_Get_Texture_Time=0; #endif //--------------------------------------------------------------------- TextureClass *W3DAssetManager::Get_Texture( const char * filename, TextureClass::MipCountType mip_level_count, WW3DFormat texture_format, bool allow_compression ) { #ifdef DUMP_PERF_STATS __int64 startTime64,endTime64; GetPrecisionTimer(&startTime64); #endif WWPROFILE( "WW3DAssetManager::Get_Texture 1" ); /* ** Bail if the user isn't really asking for anything */ if (!filename || !*filename) { #ifdef DUMP_PERF_STATS GetPrecisionTimer(&endTime64); Total_Get_Texture_Time += endTime64-startTime64; #endif return NULL; } StringClass lower_case_name(filename,true); _strlwr(lower_case_name.Peek_Buffer()); /* ** See if the texture has already been loaded. */ TextureClass* tex = TextureHash.Get(lower_case_name); if (tex && texture_format != WW3D_FORMAT_UNKNOWN) { WWASSERT_PRINT(tex->Get_Texture_Format() == texture_format, ("Texture %s has already been loaded with different format",filename)); } /* ** Didn't have it so we have to create a new texture */ if (!tex) { tex = NEW_REF(TextureClass, (lower_case_name, NULL, mip_level_count, texture_format, allow_compression)); TextureHash.Insert(tex->Get_Texture_Name(),tex); // if (TheGlobalData->m_preloadAssets) // { // extern std::vector preloadTextureNamesGlobalHack; // preloadTextureNamesGlobalHack.push_back(tex->Get_Texture_Name()); // } #if defined(_DEBUG) || defined(_INTERNAL) if (TheGlobalData->m_preloadReport) { //loading a new asset and app is requesting a log of all loaded assets. FILE *logfile=fopen("PreloadedAssets.txt","a+"); //append to log if (logfile) { fprintf(logfile,"TX: %s\n",tex->Get_Texture_Name()); fclose(logfile); } } #endif } tex->Add_Ref(); #ifdef DUMP_PERF_STATS GetPrecisionTimer(&endTime64); Total_Get_Texture_Time += endTime64-startTime64; #endif return tex; } //--------------------------------------------------------------------- RenderObjClass * W3DAssetManager::Create_Render_Obj(const char* name) { return WW3DAssetManager::Create_Render_Obj(name); } //--------------------------------------------------------------------- /** 'Generals' specific munging to encode team color and scale in model name */ static inline void Munge_Render_Obj_Name(char *newname, const char *oldname, float scale, const int color, const char *textureName) { char lower_case_name[255]; strcpy(lower_case_name, oldname); _strlwr(lower_case_name); if (!textureName) textureName = ""; sprintf(newname,"#%d!%g!%s#%s",color,scale,textureName,lower_case_name); } //--------------------------------------------------------------------- static inline void Munge_Texture_Name(char *newname, const char *oldname, const int color) { char lower_case_name[255]; strcpy(lower_case_name, oldname); _strlwr(lower_case_name); sprintf(newname,"#%d#%s", color, lower_case_name); } //--------------------------------------------------------------------- int W3DAssetManager::replaceAssetTexture(RenderObjClass *robj, TextureClass *oldTex, TextureClass *newTex) { switch (robj->Class_ID()) { case RenderObjClass::CLASSID_MESH: return replaceMeshTexture(robj, oldTex, newTex); break; case RenderObjClass::CLASSID_HLOD: return replaceHLODTexture(robj, oldTex, newTex); break; } return 0; } //--------------------------------------------------------------------- Int W3DAssetManager::replaceHLODTexture(RenderObjClass *robj, TextureClass *oldTex, TextureClass *newTex) { int didReplace=0; int num_sub = robj->Get_Num_Sub_Objects(); for(int i = 0; i < num_sub; i++) { RenderObjClass *sub_obj = robj->Get_Sub_Object(i); didReplace |= replaceAssetTexture(sub_obj, oldTex, newTex); REF_PTR_RELEASE(sub_obj); } return didReplace; } //--------------------------------------------------------------------- Int W3DAssetManager::replaceMeshTexture(RenderObjClass *robj, TextureClass *oldTex, TextureClass *newTex) { int i; int didReplace=0; MeshClass *mesh=(MeshClass*) robj; MeshModelClass * model = mesh->Get_Model(); MaterialInfoClass *material = mesh->Get_Material_Info(); for (i=0; iTexture_Count(); i++) { if (material->Peek_Texture(i) == oldTex) { model->Replace_Texture(oldTex,newTex); material->Replace_Texture(i,newTex); didReplace=1; } } REF_PTR_RELEASE(material); REF_PTR_RELEASE(model); return didReplace; } //--------------------------------------------------------------------- /** Replaces all references to old texture with new texture. Operation is performed on asset prototype so it will affect most instances of this object. Objects which have been customized with house color will not be affected unless they are created after this function is called. */ int W3DAssetManager::replacePrototypeTexture(RenderObjClass *robj, const char * oldname, const char * newname) { //search model for old texture TextureClass *oldTex=Get_Texture(oldname); TextureClass *newTex=Get_Texture(newname); int retCode=replaceAssetTexture(robj,oldTex,newTex); REF_PTR_RELEASE(oldTex); REF_PTR_RELEASE(newTex); return retCode; } //--------------------------------------------------------------------- /** Generals specific version that looks for a texture which has been house-color tinted. Don't use this unless you really need it. For normal textures, use Get_Texture(). */ TextureClass * W3DAssetManager::Find_Texture(const char * name, const int color) { char newname[512]; Munge_Texture_Name(newname, name, color); // see if we have a cached copy TextureClass *newtex = TextureHash.Get(newname); if (newtex) { newtex->Add_Ref(); } return newtex; } //--------------------------------------------------------------------- TextureClass * W3DAssetManager::Recolor_Texture(TextureClass *texture, const int color) { const char *name=texture->Get_Texture_Name(); TextureClass *newtex = Find_Texture(name, color); if (newtex) { return newtex; } return Recolor_Texture_One_Time(texture, color); } //--------------------------------------------------------------------- const Int TEAM_COLOR_PALETTE_SIZE = 16; const UnsignedShort houseColorScale[TEAM_COLOR_PALETTE_SIZE] = { 255,239,223,211,195,174,167,151,135,123,107,91,79,63,47,35 }; //--------------------------------------------------------------------- static void remapPalette16Bit(SurfaceClass::SurfaceDescription *sd, UnsignedShort *palette, unsigned int color) { UnsignedShort pal[TEAM_COLOR_PALETTE_SIZE]; Vector3 rgb,v_color((float)((color>>16)&0xff)/255.0f/255.0f,(float)((color>>8)&0xff)/255.0f/255.0f,(float)(color&0xff)/255.0f/255.0f); //Generate a new color gradient palette based on reference color for (Int y=0; y>16)&0xff)/255.0f/255.0f,(float)((color>>8)&0xff)/255.0f/255.0f,(float)(color&0xff)/255.0f/255.0f); //Generate a new color gradient palette based on reference color for (Int y=0; y>16)&0xff)/255.0f,(float)((color>>8)&0xff)/255.0f,(float)(color&0xff)/255.0f); Int x,y; #ifdef DO_HUE_SHIFT Vector3 hsv; Vector3 hsv_color; RGB_To_HSV(hsv_color,v_color); #endif for (y=0; y>12); //get alpha for house color if (pixelAlpha) { //some house color needs to show through ///@todo: optimize this alpha blend to use fixed point math. #ifdef DO_HUE_SHIFT RGB_To_HSV(hsv,Vector3(((pixel>>8)&0xf)/15.0f,((pixel>>4)&0xf)/15.0f,(pixel &0xf)/15.0f)); hsv.X=hsv_color.X; hsv.Y*=hsv_color.Y; HSV_To_RGB(rgb,hsv); #else fpixelAlpha=pixelAlpha/15.0f; fpixelAlphaInv=1.0f-fpixelAlpha; rgb.X=fpixelAlpha * v_color.X + fpixelAlphaInv*(Real)((pixel>>8)&0xf)/15.0f; //red rgb.Y=fpixelAlpha * v_color.Y + fpixelAlphaInv*(Real)((pixel>>4)&0xf)/15.0f; //green rgb.Z=fpixelAlpha * v_color.Z + fpixelAlphaInv*(Real)(pixel&0xf)/15.0f; //blue #endif data[x] = REAL_TO_INT(rgb.X*15.0f)<<8 | REAL_TO_INT(rgb.Y*15.0f)<<4 | REAL_TO_INT(rgb.Z*15.0f); } data[x] |= 0xf000; //force alpha to opaque. } data += pitch; } } //--------------------------------------------------------------------- static void remapPalette32Bit(SurfaceClass::SurfaceDescription *sd, UnsignedInt *palette, unsigned int color) { UnsignedInt pal[TEAM_COLOR_PALETTE_SIZE]; Vector3 rgb,v_color((float)((color>>16)&0xff)/255.0f/255.0f,(float)((color>>8)&0xff)/255.0f/255.0f,(float)(color&0xff)/255.0f/255.0f); //Generate a new color gradient palette based on reference color for (Int y=0; y>16)&0xff)/255.0f/255.0f,(float)((color>>8)&0xff)/255.0f/255.0f,(float)(color&0xff)/255.0f/255.0f); //Generate a new color gradient palette based on reference color for (Int y=0; y>16)&0xff)/255.0f,(float)((color>>8)&0xff)/255.0f,(float)(color&0xff)/255.0f); Int x,y; #ifdef DO_HUE_SHIFT Vector3 hsv; Vector3 hsv_color; RGB_To_HSV(hsv_color,v_color); #endif for (y=0; y>24); //get alpha for house color if (pixelAlpha) { //some house color needs to show through #ifdef DO_HUE_SHIFT RGB_To_HSV(hsv,Vector3(((pixel>>16)&0xff)/255.0f,((pixel>>8)&0xff)/255.0f,(pixel &0xff)/255.0f)); hsv.X=hsv_color.X; hsv.Y*=hsv_color.Y; HSV_To_RGB(rgb,hsv); #else ///@todo: optimize this alpha blend to use fixed point math. fpixelAlpha=pixelAlpha/255.0f; fpixelAlphaInv=1.0f-fpixelAlpha; rgb.X=fpixelAlpha * v_color.X + fpixelAlphaInv*(Real)((pixel>>16)&0xff)/255.0f; //red rgb.Y=fpixelAlpha * v_color.Y + fpixelAlphaInv*(Real)((pixel>>8)&0xff)/255.0f; //green rgb.Z=fpixelAlpha * v_color.Z + fpixelAlphaInv*(Real)(pixel&0xff)/255.0f; //blue #endif data[x] = REAL_TO_INT(rgb.X*255.0f)<<16 | REAL_TO_INT(rgb.Y*255.0f)<<8 | REAL_TO_INT(rgb.Z*255.0f); } data[x] |= 0xff000000; //force alpha to opaque. } data += pitch; } } //--------------------------------------------------------------------- /** Surface is assumed to come in the following format: First 16 pixels are a palette composed of 24-Bit RGB values. Any pixels in remainder of image that use these 24-bit values will be remapped using a pre-defined formula. */ void W3DAssetManager::Remap_Palette(SurfaceClass *surface, const int color, Bool doPaletteOnly, Bool useAlpha) { // unsigned int x; SurfaceClass::SurfaceDescription sd; surface->Get_Description(sd); int pitch,size; // UnsignedInt newPalette[TEAM_COLOR_PALETTE_SIZE]; size=PixelSize(sd); unsigned char *bits=(unsigned char*) surface->Lock(&pitch); if (doPaletteOnly) { //only recolor the palette which is stored in top row. Model only references these pixels. if (size == 2) remapPalette16Bit(&sd, (UnsignedShort *)bits, color); else if (size == 4) remapPalette32Bit(&sd, (UnsignedInt *)bits,color); } else { if (useAlpha) { if (size == 2) remapAlphaTexture16Bit(sd.Width, sd.Height, pitch>>1, &sd, (UnsignedShort *)bits,color); else if (size == 4) remapAlphaTexture32Bit(sd.Width, sd.Height, pitch>>2, &sd, (UnsignedInt *)bits,color); } else { //Recolor the image using the palette stored in top row if (size == 2) remapTexture16Bit(sd.Width, sd.Height-1, pitch>>1, &sd, (UnsignedShort *)bits, (UnsignedShort *)(bits+pitch), color); else if (size == 4) remapTexture32Bit(sd.Width, sd.Height-1, pitch>>2, &sd, (UnsignedInt *)bits, (UnsignedInt *)(bits+pitch),color); } } surface->Unlock(); } //--------------------------------------------------------------------- TextureClass * W3DAssetManager::Recolor_Texture_One_Time(TextureClass *texture, const int color) { const char *name=texture->Get_Texture_Name(); // if texture is procedural return NULL if (name && name[0]=='!') return NULL; // make sure texture is loaded if (!texture->Is_Initialized()) TextureLoader::Request_High_Priority_Loading(texture, (TextureClass::MipCountType)texture->Get_Mip_Level_Count()); SurfaceClass::SurfaceDescription desc; SurfaceClass *newsurf, *oldsurf; texture->Get_Level_Description(desc); Int psize; psize=PixelSize(desc); DEBUG_ASSERTCRASH( psize == 2 || psize == 4, ("Can't Recolor Texture %s", name) ); oldsurf=texture->Get_Surface_Level(); newsurf=NEW_REF(SurfaceClass,(desc.Width,desc.Height,desc.Format)); newsurf->Copy(0,0,0,0,desc.Width,desc.Height,oldsurf); if (*(name+3) == 'D' || *(name+3) == 'd') Remap_Palette(newsurf,color, true, false ); //texture only contains a palette stored in top row. else if (*(name+3) == 'A' || *(name+3) == 'a') Remap_Palette(newsurf,color, false, true ); //texture only contains a palette stored in top row. TextureClass * newtex=NEW_REF(TextureClass,(newsurf,(TextureClass::MipCountType)texture->Get_Mip_Level_Count())); newtex->Set_Mag_Filter(texture->Get_Mag_Filter()); newtex->Set_Min_Filter(texture->Get_Min_Filter()); newtex->Set_Mip_Mapping(texture->Get_Mip_Mapping()); newtex->Set_U_Addr_Mode(texture->Get_U_Addr_Mode()); newtex->Set_V_Addr_Mode(texture->Get_V_Addr_Mode()); char newname[512]; Munge_Texture_Name(newname, name, color); newtex->Set_Texture_Name(newname); TextureHash.Insert(newtex->Get_Texture_Name(), newtex); newtex->Add_Ref(); REF_PTR_RELEASE(oldsurf); REF_PTR_RELEASE(newsurf); return newtex; } #ifdef DUMP_PERF_STATS __int64 Total_Create_Render_Obj_Time=0; #endif //--------------------------------------------------------------------- /** Generals specific code to generate customized render objects for each team color Scale==1.0, color==0x00000000, and oldTexure==NULL are defaults that do nothing. */ RenderObjClass * W3DAssetManager::Create_Render_Obj( const char * name, float scale, const int color, const char *oldTexture, const char *newTexture ) { #ifdef DUMP_PERF_STATS __int64 startTime64,endTime64; GetPrecisionTimer(&startTime64); #endif #ifdef INCLUDE_GRANNY_IN_BUILD Bool isGranny = false; char *pext=strrchr(name,'.'); //find file extension if (pext) isGranny=(strnicmp(pext,".GR2",4) == 0); #endif Bool reallyscale = (WWMath::Fabs(scale - ident_scale) > scale_epsilon); Bool reallycolor = (color & 0xFFFFFF) != 0; //black is not a valid color and assumes no custom coloring. Bool reallytexture = (oldTexture != NULL && newTexture != NULL); // base case, no scale or color if (!reallyscale && !reallycolor && !reallytexture) { RenderObjClass *robj=WW3DAssetManager::Create_Render_Obj(name); #ifdef DUMP_PERF_STATS GetPrecisionTimer(&endTime64); Total_Create_Render_Obj_Time += endTime64-startTime64; #endif return robj; } char newname[512]; Munge_Render_Obj_Name(newname, name, scale, color, newTexture); // see if we got a cached version RenderObjClass *rendobj = NULL; #ifdef INCLUDE_GRANNY_IN_BUILD if (isGranny) { //Granny objects share the same prototype since they allow instance scaling. strcpy(newname,name); //use same name for all granny objects at any scale. } #endif Set_WW3D_Load_On_Demand(false); // munged name will never be found in a file. rendobj = WW3DAssetManager::Create_Render_Obj(newname); if (rendobj) { //store the color that we used to create asset so we can read it back out //when we need to save this render object to a file. Used during saving //of fog of war ghost objects. rendobj->Set_ObjectColor(color); #ifdef INCLUDE_GRANNY_IN_BUILD if (isGranny) ///@todo Granny objects are realtime scaled - fix to scale like W3D. rendobj->Set_ObjectScale(scale); #endif Set_WW3D_Load_On_Demand(true); // Auto Load. #ifdef DUMP_PERF_STATS GetPrecisionTimer(&endTime64); Total_Create_Render_Obj_Time += endTime64-startTime64; #endif return rendobj; } // create a new one based on exisiting prototype WWPROFILE( "WW3DAssetManager::Create_Render_Obj" ); WWMEMLOG(MEM_GEOMETRY); // Try to find a prototype PrototypeClass * proto = Find_Prototype(name); Set_WW3D_Load_On_Demand(true); // Auto Load. if (WW3D_Load_On_Demand && proto == NULL) { // If we didn't find one, try to load on demand char filename [MAX_PATH]; char *mesh_name = ::strchr (name, '.'); if (mesh_name != NULL) { ::lstrcpyn(filename, name, ((int)mesh_name) - ((int)name) + 1); #ifdef INCLUDE_GRANNY_IN_BUILD if (isGranny) ::lstrcat(filename, ".gr2"); else #endif ::lstrcat(filename, ".w3d"); } else { sprintf( filename, "%s.w3d", name); } // If we can't find it, try the parent directory if ( Load_3D_Assets( filename ) == false ) { StringClass new_filename = StringClass("..\\") + filename; if (Load_3D_Assets( new_filename ) == false) { #ifdef INCLUDE_GRANNY_IN_BUILD char *mesh_name = ::strchr (filename, '.'); ::lstrcpyn (mesh_name, ".gr2",5); Load_3D_Assets( filename ); isGranny=true; #endif } } proto = Find_Prototype(name); // try again } if (proto == NULL) { static int warning_count = 0; if (++warning_count <= 20) { WWDEBUG_SAY(("WARNING: Failed to create Render Object: %s\r\n",name)); } #ifdef DUMP_PERF_STATS GetPrecisionTimer(&endTime64); Total_Create_Render_Obj_Time += endTime64-startTime64; #endif return NULL; // Failed to find a prototype } rendobj = proto->Create(); if (!rendobj) { #ifdef DUMP_PERF_STATS GetPrecisionTimer(&endTime64); Total_Create_Render_Obj_Time += endTime64-startTime64; #endif return NULL; } #ifdef INCLUDE_GRANNY_IN_BUILD if (!isGranny) #endif { Make_Unique(rendobj,reallyscale,reallycolor); if (reallytexture) { TextureClass *oldTex = Get_Texture(oldTexture); TextureClass *newTex = Get_Texture(newTexture); replaceAssetTexture(rendobj,oldTex,newTex); REF_PTR_RELEASE(newTex); REF_PTR_RELEASE(oldTex); } if (reallyscale) rendobj->Scale(scale); if (reallycolor) Recolor_Asset(rendobj,color); } #ifdef INCLUDE_GRANNY_IN_BUILD else { ///@todo Granny objects are realtime scaled - fix to scale like W3D. rendobj->Set_ObjectScale(scale); return rendobj; } #endif W3DPrototypeClass *w3dproto = newInstance(W3DPrototypeClass)(rendobj, newname); rendobj->Release_Ref(); Add_Prototype(w3dproto); rendobj = w3dproto->Create(); rendobj->Set_ObjectColor(color); #ifdef DUMP_PERF_STATS GetPrecisionTimer(&endTime64); Total_Create_Render_Obj_Time += endTime64-startTime64; #endif return rendobj; } //--------------------------------------------------------------------- /** Generals specific code to generate customized render objects for each team color */ int W3DAssetManager::Recolor_Asset(RenderObjClass *robj, const int color) { switch (robj->Class_ID()) { case RenderObjClass::CLASSID_MESH: return Recolor_Mesh(robj,color); break; case RenderObjClass::CLASSID_HLOD: return Recolor_HLOD(robj,color); break; } return 0; } //--------------------------------------------------------------------- /** Generals specific code to generate customized render objects for each team color */ int W3DAssetManager::Recolor_Mesh(RenderObjClass *robj, const int color) { int i; int didRecolor=0; const char *meshName; MeshClass *mesh=(MeshClass*) robj; MeshModelClass * model = mesh->Get_Model(); MaterialInfoClass *material = mesh->Get_Material_Info(); // recolor vertex material (assuming mesh is housecolor) if ( (( (meshName=strchr(mesh->Get_Name(),'.') ) != 0 && *(meshName++)) || ( (meshName=mesh->Get_Name()) != NULL)) && _strnicmp(meshName,"HOUSECOLOR", 10) == 0) { for (i=0; iVertex_Material_Count(); i++) Recolor_Vertex_Material(material->Peek_Vertex_Material(i),color); didRecolor=1; } // recolor textures TextureClass *newtex,*oldtex; for (i=0; iTexture_Count(); i++) { oldtex=material->Peek_Texture(i); if (_strnicmp(oldtex->Get_Texture_Name(),"ZHC", 3) == 0) { //This texture needs to be adjusted for housecolor newtex=Recolor_Texture(oldtex,color); if (newtex) { model->Replace_Texture(oldtex,newtex); material->Replace_Texture(i,newtex); REF_PTR_RELEASE(newtex); didRecolor=1; } } } REF_PTR_RELEASE(material); REF_PTR_RELEASE(model); return didRecolor; } //--------------------------------------------------------------------- /** Generals specific code to generate customized render objects for each team color */ int W3DAssetManager::Recolor_HLOD(RenderObjClass *robj, const int color) { int didRecolor=0; int num_sub = robj->Get_Num_Sub_Objects(); for(int i = 0; i < num_sub; i++) { RenderObjClass *sub_obj = robj->Get_Sub_Object(i); didRecolor |= Recolor_Asset(sub_obj,color); REF_PTR_RELEASE(sub_obj); } return didRecolor; } //--------------------------------------------------------------------- /** Generals specific code to generate customized render objects for each team color */ void W3DAssetManager::Recolor_Vertex_Material(VertexMaterialClass *vmat, const int color) { Vector3 rgb,rgb2; rgb.X = (Real)((color >> 16) & 0xff )/255.0f; rgb.Y = (Real)((color >> 8) & 0xff )/255.0f; rgb.Z = (Real)(color & 0xff)/255.0f; //We ignore the existing ambinent/diffuse and assume they were 1.0. We can change //to scaling them if required. // vmat->Get_Ambient(&rgb2); // Recolor(rgb,hsv_shift); rgb2.X = rgb.X; //scale colors rgb2.Y = rgb.Y; //scale colors rgb2.Z = rgb.Z; //scale colors vmat->Set_Ambient(rgb2); // vmat->Get_Diffuse(&rgb2); // Recolor(rgb,hsv_shift); rgb2.X = rgb.X; //scale colors rgb2.Y = rgb.Y; //scale colors rgb2.Z = rgb.Z; //scale colors vmat->Set_Diffuse(rgb2); } #ifdef DUMP_PERF_STATS __int64 Total_Load_3D_Assets=0; static Load_3D_Asset_Recursions=0; #endif //--------------------------------------------------------------------- bool W3DAssetManager::Load_3D_Assets( const char * filename ) { #ifdef DUMP_PERF_STATS Load_3D_Asset_Recursions++; __int64 startTime64,endTime64; GetPrecisionTimer(&startTime64); #endif #ifdef INCLUDE_GRANNY_IN_BUILD Bool isGranny = false; char *pext=strrchr(filename,'.'); //find file extension if (pext) isGranny=(strnicmp(pext,".GR2",4) == 0); if (!isGranny) #endif // Try to find an existing prototype char basename[512]; strcpy(basename, filename); char *pext = strrchr(basename, '.'); //find file extension if (pext) *pext = '\0'; //drop the extension PrototypeClass * proto = Find_Prototype(basename); if (proto) { #ifdef DUMP_PERF_STATS if (Load_3D_Asset_Recursions == 1) { GetPrecisionTimer(&endTime64); Total_Load_3D_Assets += endTime64-startTime64; } Load_3D_Asset_Recursions--; #endif return TRUE; //this file has already been loaded. } bool result = WW3DAssetManager::Load_3D_Assets(filename); #if defined(_DEBUG) || defined(_INTERNAL) if (result && TheGlobalData->m_preloadReport) { //loading a new asset and app is requesting a log of all loaded assets. FILE *logfile=fopen("PreloadedAssets.txt","a+"); //append to log if (logfile) { StringClass lower_case_name(filename,true); _strlwr(lower_case_name.Peek_Buffer()); fprintf(logfile,"3D: %s\n",lower_case_name.Peek_Buffer()); fclose(logfile); } } #endif #ifdef DUMP_PERF_STATS if (Load_3D_Asset_Recursions == 1) { GetPrecisionTimer(&endTime64); Total_Load_3D_Assets += endTime64-startTime64; } Load_3D_Asset_Recursions--; #endif return result; #ifdef INCLUDE_GRANNY_IN_BUILD //Loading assets for Granny File PrototypeClass * newproto = NULL; newproto = _GrannyLoader.Load_W3D(filename); /* ** Now, see if the prototype that we loaded has a duplicate ** name with any of our currently loaded prototypes (can't have that!) */ if (newproto != NULL) { if (!Render_Obj_Exists(newproto->Get_Name())) { /* ** Add the new, unique prototype to our list */ Add_Prototype(newproto); } else { /* ** Warn the user about a name collision with this prototype ** and dump it */ WWDEBUG_SAY(("Render Object Name Collision: %s\r\n",newproto->Get_Name())); delete newproto; newproto = NULL; return false; } } else { /* ** Warn user that a prototype was not generated from this ** chunk type */ WWDEBUG_SAY(("Could not generate Granny prototype! File = %d\r\n",filename)); return false; } return true; #endif } #ifdef DUMP_PERF_STATS __int64 Total_Get_HAnim_Time=0; static HAnim_Recursions=0; #endif //--------------------------------------------------------------------- HAnimClass * W3DAssetManager::Get_HAnim(const char * name) { #ifdef DUMP_PERF_STATS HAnim_Recursions++; __int64 startTime64,endTime64; GetPrecisionTimer(&startTime64); #endif WWPROFILE( "WW3DAssetManager::Get_HAnim" ); #ifdef INCLUDE_GRANNY_IN_BUILD Bool isGranny = false; char *pext=strrchr(name,'.'); //find file extension if (pext) isGranny=(strnicmp(pext,".GR2",4) == 0); if (!isGranny) #endif { HAnimClass *anim=WW3DAssetManager::Get_HAnim(name); //we only do custom granny processing. #ifdef DUMP_PERF_STATS if (HAnim_Recursions == 1) { GetPrecisionTimer(&endTime64); Total_Get_HAnim_Time += endTime64-startTime64; } HAnim_Recursions--; #endif return anim; } #ifdef INCLUDE_GRANNY_IN_BUILD // Try to find the hanim HAnimClass * anim = m_GrannyAnimManager->Get_Anim(name); if (WW3D_Load_On_Demand && anim == NULL) { // If we didn't find it, try to load on demand if ( !m_GrannyAnimManager->Is_Missing( name ) ) { // if this is NOT a known missing anim // If we can't find it, try the parent directory if ( m_GrannyAnimManager->Load_Anim(name) == 1 ) { StringClass new_filename = StringClass("..\\") + name; m_GrannyAnimManager->Load_Anim( new_filename ); } anim = m_GrannyAnimManager->Get_Anim(name); // Try again if (anim == NULL) { // WWDEBUG_SAY(("WARNING: Animation %s not found!\n", name)); m_GrannyAnimManager->Register_Missing( name ); // This is now a KNOWN missing anim } } } return anim; #endif } //--------------------------------------------------------------------- // Uniqing //--------------------------------------------------------------------- //--------------------------------------------------------------------- /** Generals specific code to generate customized render objects for each team color */ void W3DAssetManager::Make_HLOD_Unique(RenderObjClass *robj, Bool geometry, Bool colors) { int num_sub = robj->Get_Num_Sub_Objects(); for(int i = 0; i < num_sub; i++) { RenderObjClass *sub_obj = robj->Get_Sub_Object(i); Make_Unique(sub_obj, geometry, colors); REF_PTR_RELEASE(sub_obj); } } //--------------------------------------------------------------------- /** Generals specific code to generate customized render objects for each team color */ void W3DAssetManager::Make_Unique(RenderObjClass *robj, Bool geometry, Bool colors) { switch (robj->Class_ID()) { case RenderObjClass::CLASSID_MESH: Make_Mesh_Unique(robj,geometry,colors); break; case RenderObjClass::CLASSID_HLOD: Make_HLOD_Unique(robj,geometry,colors); break; } } //--------------------------------------------------------------------- /** Determine what method is used to apply house color to this mesh (if any) */ static Bool getMeshColorMethods(MeshClass *mesh, Bool &vertexColor, Bool &textureColor) { vertexColor = false; textureColor = false; //Check if mesh is using custom texture containing house color MaterialInfoClass *material = mesh->Get_Material_Info(); if (material) { for (int j=0; jTexture_Count(); j++) if (_strnicmp(material->Peek_Texture(j)->Get_Texture_Name(),"ZHC",3) == 0) { textureColor = true; break; } REF_PTR_RELEASE(material); } //Check if mesh is using a custom mesh which contains house color in material. //Meshes which are part of another model have names in the form "name.name" while //isolated meshes are just "name". We check for both starting with "HOUSECOLOR". const char *meshName; if ( ( (meshName=strchr(mesh->Get_Name(),'.') ) != 0 && *(meshName++)) || ( (meshName=mesh->Get_Name()) != NULL) ) { //Check if this object has housecolors on mesh if ( _strnicmp(meshName,"HOUSECOLOR", 10) == 0) vertexColor = true; } return (vertexColor || textureColor); } //--------------------------------------------------------------------- /** Generals specific code to generate customized render objects for each team color */ void W3DAssetManager::Make_Mesh_Unique(RenderObjClass *robj, Bool geometry, Bool colors) { int i; MeshClass *mesh=(MeshClass*) robj; Bool isVertexColor, isTextureColor; //figure out what type of coloring this mesh requires (if any) if ((colors && getMeshColorMethods(mesh,isVertexColor,isTextureColor)) || geometry) { //mesh has some house color applied so make those components unique to mesh. //Create unique data for this mesh mesh->Make_Unique(); MeshModelClass * model = mesh->Get_Model(); if (colors && isVertexColor) { MaterialInfoClass *material=mesh->Get_Material_Info(); for (i=0; iVertex_Material_Count(); i++) material->Peek_Vertex_Material(i)->Make_Unique(); REF_PTR_RELEASE(material); } if (geometry) { // make geometry unique model->Make_Geometry_Unique(); } REF_PTR_RELEASE(model); } } //--------------------------------------------------------------------- /**Report prototypes that have all assets with reference count equal to 1*/ void W3DAssetManager::Report_Used_Prototypes(void) { int count = Prototypes.Count(); while (count-- > 0) { PrototypeClass * proto = Prototypes[count]; if (proto->Get_Class_ID() == RenderObjClass::CLASSID_HLOD || proto->Get_Class_ID() == RenderObjClass::CLASSID_MESH) { DEBUG_LOG(("**Unfreed Prototype On Map Reset: %s\n",proto->Get_Name())); } } } //--------------------------------------------------------------------- /**Report any assets with reference counts > 1. This means they are still referenced by something besides the asset manager.*/ void W3DAssetManager::Report_Used_Assets(void) { Report_Used_Prototypes(); ///@todo: Report unfreed skeletons and animations // HAnimManager.Free_All_Anims(); // HTreeManager.Free_All_Trees(); Report_Used_Textures(); Report_Used_Font3DDatas(); Report_Used_FontChars(); } //--------------------------------------------------------------------- void W3DAssetManager::Report_Used_FontChars(void) { Int count=FontCharsList.Count(); while (count-- > 0) { if (FontCharsList[count]->Num_Refs() >= 1) { DEBUG_LOG(("**Unfreed FontChar On Map Reset: %s\n",FontCharsList[count]->Get_Name())); //FontCharsList[count]->Release_Ref(); //FontCharsList.Delete(count); } } } //--------------------------------------------------------------------- /**Report all textures with refcounts >= 1*/ void W3DAssetManager::Report_Used_Textures(void) { /* ** for each texture in the list, get it, check it's refcount, and and release ref it if the ** refcount is one. */ // unsigned count=0; // TextureClass* temp_textures[256]; HashTemplateIterator ite(TextureHash); for (ite.First();!ite.Is_Done();ite.Next()) { TextureClass* tex=ite.Peek_Value(); if (tex->Num_Refs() <= 1) { /* temp_textures[count++]=tex; if (count==256) { for (unsigned i=0;i<256;++i) { TextureHash.Remove(temp_textures[i]->Get_Texture_Name()); temp_textures[i]->Release_Ref(); } count=0; ite.First(); // iterator doesn't support modifying the hash table while iterating, so start from the // beginning. }*/ } else { DEBUG_LOG(("**Texture \"%s\" referenced %d times on map reset\n",tex->Get_Texture_Name(),tex->Num_Refs()-1)); } } /* for (unsigned i=0;iGet_Texture_Name()); temp_textures[i]->Release_Ref(); }*/ } //--------------------------------------------------------------------- /**Report all used fonts*/ void W3DAssetManager::Report_Used_Font3DDatas( void ) { /* ** for each font data in the list, get it, check it's refcount, and and release ref it if the ** refcount is one. */ SLNode *node, * next; for ( node = Font3DDatas.Head(); node; node = next) { next = node->Next(); Font3DDataClass *font = node->Data(); if (font->Num_Refs() == 1) { /* Font3DDatas.Remove(font); font->Release_Ref();*/ } else { DEBUG_LOG(("**Unfreed Font3DDatas On Map Reset: %s\n",font->Name)); } } } //--------------------------------------------------------------------- // E&B Coloring Code //--------------------------------------------------------------------- /* //--------------------------------------------------------------------- // Uniqing //--------------------------------------------------------------------- void W3DAssetManager::Make_Mesh_Unique(RenderObjClass *robj, Bool geometry, Bool colors) { int i; MeshClass *mesh=(MeshClass*) robj; mesh->Make_Unique(); MeshModelClass * model = mesh->Get_Model(); if (colors) { // make all vertex materials unique MaterialInfoClass *material = mesh->Get_Material_Info(); for (i=0; iVertex_Material_Count(); i++) material->Peek_Vertex_Material(i)->Make_Unique(); REF_PTR_RELEASE(material); // make all color arrays unique model->Make_Color_Array_Unique(0); model->Make_Color_Array_Unique(1); // do not do textures yet // because we want to do the color conversion // for the top mip level and then // mip filter instead of converting all mip levels } if (geometry) { // make geometry unique model->Make_Geometry_Unique(); } REF_PTR_RELEASE(model); } void W3DAssetManager::Make_HLOD_Unique(RenderObjClass *robj, Bool geometry, Bool colors) { int num_sub = robj->Get_Num_Sub_Objects(); for(int i = 0; i < num_sub; i++) { RenderObjClass *sub_obj = robj->Get_Sub_Object(i); Make_Unique(sub_obj,geometry,colors); REF_PTR_RELEASE(sub_obj); } } void W3DAssetManager::Make_Unique(RenderObjClass *robj, Bool geometry, Bool colors) { switch (robj->Class_ID()) { case RenderObjClass::CLASSID_MESH: Make_Mesh_Unique(robj,geometry,colors); break; case RenderObjClass::CLASSID_HLOD: Make_HLOD_Unique(robj,geometry,colors); break; } } static inline void Munge_Render_Obj_Name(char *newname, const char *oldname, float scale, const Vector3 &hsv_shift) { char lower_case_name[255]; strcpy(lower_case_name, oldname); _strlwr(lower_case_name); sprintf(newname,"#%s!%gH%gS%gV%g", lower_case_name, scale, hsv_shift.X, hsv_shift.Y, hsv_shift.Z); } static inline void Munge_Texture_Name(char *newname, const char *oldname, const Vector3 &hsv_shift) { char lower_case_name[255]; strcpy(lower_case_name, oldname); _strlwr(lower_case_name); sprintf(newname,"#%s!H%gS%gV%g", lower_case_name, hsv_shift.X, hsv_shift.Y, hsv_shift.Z); } RenderObjClass * W3DAssetManager::Create_Render_Obj(const char * name,float scale, const Vector3 &hsv_shift) { Bool isGranny = false; #ifdef INCLUDE_GRANNY_IN_BUILD char *pext=strrchr(name,'.'); //find file extension if (pext) isGranny=(strnicmp(pext,".GR2",4) == 0); #endif Bool reallyscale = (WWMath::Fabs(scale - ident_scale) > scale_epsilon); Bool reallyhsv_shift = (WWMath::Fabs(hsv_shift.X - ident_HSV.X) > H_epsilon || WWMath::Fabs(hsv_shift.Y - ident_HSV.Y) > S_epsilon || WWMath::Fabs(hsv_shift.Z - ident_HSV.Z) > V_epsilon); // base case, no scale or hue shifting if (!reallyscale && !reallyhsv_shift) return WW3DAssetManager::Create_Render_Obj(name); char newname[512]; Munge_Render_Obj_Name(newname, name, scale, hsv_shift); // see if we got a cached version RenderObjClass *rendobj=NULL; if (isGranny) { //Granny objects share the same prototype since they allow instance scaling. strcpy(newname,name); //use same name for all granny objects at any scale. } Set_WW3D_Load_On_Demand(false); // munged name will never be found in a file. rendobj=WW3DAssetManager::Create_Render_Obj(newname); if (rendobj) { if (isGranny) ///@todo Granny objects are realtime scaled - fix to scale like W3D. rendobj->Set_ObjectScale(scale); Set_WW3D_Load_On_Demand(true); // Auto Load. return rendobj; } // create a new one based on // exisiting prototype WWPROFILE( "WW3DAssetManager::Create_Render_Obj" ); WWMEMLOG(MEM_GEOMETRY); // Try to find a prototype PrototypeClass * proto = Find_Prototype(name); Set_WW3D_Load_On_Demand(true); // Auto Load. if (WW3D_Load_On_Demand && proto == NULL) { // If we didn't find one, try to load on demand char filename [MAX_PATH]; char *mesh_name = ::strchr (name, '.'); if (mesh_name != NULL) { ::lstrcpyn (filename, name, ((int)mesh_name) - ((int)name) + 1); if (isGranny) ::lstrcat (filename, ".gr2"); else ::lstrcat (filename, ".w3d"); } else { sprintf( filename, "%s.w3d", name); } // If we can't find it, try the parent directory if ( Load_3D_Assets( filename ) == false ) { StringClass new_filename = StringClass("..\\") + filename; if (Load_3D_Assets( new_filename ) == false) { char *mesh_name = ::strchr (filename, '.'); ::lstrcpyn (mesh_name, ".gr2",5); Load_3D_Assets( filename ); isGranny=true; } } proto = Find_Prototype(name); // try again } if (proto == NULL) { static int warning_count = 0; if (++warning_count <= 20) { WWDEBUG_SAY(("WARNING: Failed to create Render Object: %s\r\n",name)); } return NULL; // Failed to find a prototype } rendobj=proto->Create(); if (!rendobj) return NULL; if (!isGranny) { Make_Unique(rendobj,reallyscale,reallyhsv_shift); if (reallyscale) rendobj->Scale(scale); if (reallyhsv_shift) Recolor_Asset(rendobj,hsv_shift); } else { ///@todo Granny objects are realtime scaled - fix to scale like W3D. rendobj->Set_ObjectScale(scale); return rendobj; } W3DPrototypeClass *w3dproto=NEW W3DPrototypeClass(rendobj,newname); rendobj->Release_Ref(); Add_Prototype(w3dproto); return w3dproto->Create(); } TextureClass * W3DAssetManager::Get_Texture_With_HSV_Shift(const char * filename, const Vector3 &hsv_shift, TextureClass::MipCountType mip_level_count) { WWPROFILE( "W3DAssetManager::Get_Texture with HSV shift" ); Bool is_hsv_shift = (WWMath::Fabs(hsv_shift.X - ident_HSV.X) > H_epsilon || WWMath::Fabs(hsv_shift.Y - ident_HSV.Y) > S_epsilon || WWMath::Fabs(hsv_shift.Z - ident_HSV.Z) > V_epsilon); if (!is_hsv_shift) { return Get_Texture(filename, mip_level_count); } else { // // Bail if the user isn't really asking for anything // if ((filename == NULL) || (strlen(filename) == 0)) { return NULL; } TextureClass *newtex = Find_Texture(filename, hsv_shift); if (!newtex) { // No cached texture - need to create char lower_case_name[255]; strcpy(lower_case_name, filename); _strlwr(lower_case_name); TextureClass *oldtex = TextureHash.Get(lower_case_name); if (!oldtex) { oldtex = NEW_REF(TextureClass,(lower_case_name, NULL, mip_level_count)); TextureHash.Insert(oldtex->Get_Texture_Name(), oldtex); } newtex = Recolor_Texture_One_Time(oldtex, hsv_shift); // If the recolorization failed, return the original texture if (!newtex) { newtex = oldtex; newtex->Add_Ref(); } } return newtex; } } void W3DAssetManager::Recolor_Vertex_Material(VertexMaterialClass *vmat, const Vector3 &hsv_shift) { Vector3 rgb; vmat->Get_Ambient(&rgb); Recolor(rgb,hsv_shift); vmat->Set_Ambient(rgb); vmat->Get_Diffuse(&rgb); Recolor(rgb,hsv_shift); vmat->Set_Diffuse(rgb); vmat->Get_Emissive(&rgb); Recolor(rgb,hsv_shift); vmat->Set_Emissive(rgb); vmat->Get_Specular(&rgb); Recolor(rgb,hsv_shift); vmat->Set_Specular(rgb); } void W3DAssetManager::Recolor_Vertices(unsigned int *color, int count, const Vector3 &hsv_shift) { int i; Vector4 rgba; for (i=0; i(rgba),hsv_shift); color[i]=DX8Wrapper::Convert_Color_Clamp(rgba); } } TextureClass * W3DAssetManager::Recolor_Texture(TextureClass *texture, const Vector3 &hsv_shift) { const char *name=texture->Get_Texture_Name(); TextureClass *newtex = Find_Texture(name, hsv_shift); if (newtex) { return newtex; } return Recolor_Texture_One_Time(texture, hsv_shift); } TextureClass * W3DAssetManager::Recolor_Texture_One_Time(TextureClass *texture, const Vector3 &hsv_shift) { const char *name=texture->Get_Texture_Name(); // if texture is procedural return NULL if (name && name[0]=='!') return NULL; // make sure texture is loaded if (!texture->Is_Initialized()) TextureLoader::Request_High_Priority_Loading(texture, (TextureClass::MipCountType)texture->Get_Mip_Level_Count()); SurfaceClass::SurfaceDescription desc; SurfaceClass *newsurf, *oldsurf, *smallsurf; texture->Get_Level_Description(desc); // if texture is monochrome and no value shifting // return NULL smallsurf=texture->Get_Surface_Level((TextureClass::MipCountType)texture->Get_Mip_Level_Count()-1); if (hsv_shift.Z==0.0f && smallsurf->Is_Monochrome()) { REF_PTR_RELEASE(smallsurf); return NULL; } REF_PTR_RELEASE(smallsurf); oldsurf=texture->Get_Surface_Level(); newsurf=NEW_REF(SurfaceClass,(desc.Width,desc.Height,desc.Format)); newsurf->Copy(0,0,0,0,desc.Width,desc.Height,oldsurf); newsurf->Hue_Shift(hsv_shift); TextureClass * newtex=NEW_REF(TextureClass,(newsurf,(TextureClass::MipCountType)texture->Get_Mip_Level_Count())); newtex->Set_Mag_Filter(texture->Get_Mag_Filter()); newtex->Set_Min_Filter(texture->Get_Min_Filter()); newtex->Set_Mip_Mapping(texture->Get_Mip_Mapping()); newtex->Set_U_Addr_Mode(texture->Get_U_Addr_Mode()); newtex->Set_V_Addr_Mode(texture->Get_V_Addr_Mode()); char newname[512]; Munge_Texture_Name(newname, name, hsv_shift); newtex->Set_Texture_Name(newname); TextureHash.Insert(newtex->Get_Texture_Name(), newtex); newtex->Add_Ref(); REF_PTR_RELEASE(oldsurf); REF_PTR_RELEASE(newsurf); return newtex; } TextureClass * W3DAssetManager::Find_Texture(const char * name, const Vector3 &hsv_shift) { char newname[512]; Munge_Texture_Name(newname, name, hsv_shift); // see if we have a cached copy TextureClass *newtex = TextureHash.Get(newname); if (newtex) { newtex->Add_Ref(); } return newtex; } void W3DAssetManager::Recolor_Mesh(RenderObjClass *robj, const Vector3 &hsv_shift) { int i; MeshClass *mesh=(MeshClass*) robj; MeshModelClass * model = mesh->Get_Model(); MaterialInfoClass *material = mesh->Get_Material_Info(); // recolor vertex material for (i=0; iVertex_Material_Count(); i++) Recolor_Vertex_Material(material->Peek_Vertex_Material(i),hsv_shift); // recolor color arrays unsigned int * color; color=model->Get_Color_Array(0,false); if (color) Recolor_Vertices(color,model->Get_Vertex_Count(),hsv_shift); color=model->Get_Color_Array(1,false); if (color) Recolor_Vertices(color,model->Get_Vertex_Count(),hsv_shift); // recolor textures TextureClass *newtex,*oldtex; for (i=0; iTexture_Count(); i++) { oldtex=material->Peek_Texture(i); newtex=Recolor_Texture(oldtex,hsv_shift); if (newtex) { model->Replace_Texture(oldtex,newtex); material->Replace_Texture(i,newtex); REF_PTR_RELEASE(newtex); } } REF_PTR_RELEASE(material); REF_PTR_RELEASE(model); } void W3DAssetManager::Recolor_HLOD(RenderObjClass *robj, const Vector3 &hsv_shift) { int num_sub = robj->Get_Num_Sub_Objects(); for(int i = 0; i < num_sub; i++) { RenderObjClass *sub_obj = robj->Get_Sub_Object(i); Recolor_Asset(sub_obj,hsv_shift); REF_PTR_RELEASE(sub_obj); } } void W3DAssetManager::Recolor_ParticleEmitter(RenderObjClass *robj, const Vector3 &hsv_shift) { unsigned int i; ParticleEmitterClass* emit=(ParticleEmitterClass*) robj; ParticlePropertyStruct colors; emit->Get_Color_Key_Frames(colors); Recolor(colors.Start,hsv_shift); Recolor(colors.Rand,hsv_shift); for (i=0; iReset_Colors(colors); delete colors.Values; delete colors.KeyTimes; TextureClass *tex=emit->Get_Texture(); TextureClass *newtex=Recolor_Texture(tex,hsv_shift); if (newtex) { emit->Set_Texture(newtex); REF_PTR_RELEASE(newtex); } REF_PTR_RELEASE(tex); } void W3DAssetManager::Recolor_Asset(RenderObjClass *robj, const Vector3 &hsv_shift) { switch (robj->Class_ID()) { case RenderObjClass::CLASSID_MESH: Recolor_Mesh(robj,hsv_shift); break; case RenderObjClass::CLASSID_HLOD: Recolor_HLOD(robj,hsv_shift); break; case RenderObjClass::CLASSID_PARTICLEEMITTER: Recolor_ParticleEmitter(robj,hsv_shift); break; } } */