Files
Jedi-Academy/tools/ModView/skins.cpp
2013-04-04 14:32:05 -07:00

1128 lines
31 KiB
C++

// Filename:- script.cpp
//
// file to control model-loading scripts (scripts are purely a viewer convenience for multiple-bolt stuff)
//
#include "stdafx.h"
#include "includes.h"
#include "files.h"
#include "r_common.h"
//#include "ModViewTreeView.h"
//#include "glm_code.h"
//#include "R_Model.h"
//#include "R_Surface.h"
#include "textures.h"
//#include "TEXT.H"
//#include "sequence.h"
//#include "model.h"
#include "GenericParser2.h"
#include "skins.h"
#include "stl.h"
SkinSets_t CurrentSkins;
SkinSetsSurfacePrefs_t CurrentSkinsSurfacePrefs;
typedef map <string, map<string, int> > G2SkinModelPrefs_t; // <skinfile name, <modelname, junk>> to see if a file contains a model's name
G2SkinModelPrefs_t G2SkinModelPrefs;
// called from Media_Delete...
//
void Skins_KillPreCacheInfo(void)
{
G2SkinModelPrefs.clear();
//
// these 2 aren't really needed here, but wtf?
//
CurrentSkins.clear();
CurrentSkinsSurfacePrefs.clear();
}
/*
prefs
{
models
{
1 "snow"
}
surfaces_off
{
name1 "head_side_r_avmed_off"
name2 "head_side_l_avmed_off"
name3 "head_side_r_off"
name4 "head_side_l_off"
}
surfaces_on
{
name1 "head_bck_uppr_r_avmed_off"
name2 "head_bck_uppr_l_avmed_off"
name3 "head_bck_lwr_r_avmed_off"
name4 "head_bck_lwr_l_avmed_off"
name5 "head_bck_lwr_r"
name6 "head_bck_lwr_l"
}
}
material
{
name "face"
group
{
name "white"
shader1 "models/characters/face_average/face"
}
group
{
name "black"
texture1 "models/characters/average_face/f_marine_b1"
}
}
material
{
name "face_2sided"
group
{
name "white"
shader1 "models/characters/face_average/face_2sided"
}
group
{
name "black"
texture1 "models/characters/average_face/f_marine_b1"
}
}
material
{
name "head"
group
{
name "white"
shader1 "models/characters/face_average/head"
}
group
{
name "black"
texture1 "models/characters/average_face/h_marine_b1"
}
}
material
{
name "arms"
group
{
name "white"
shader1 "models/characters/average_sleeves/arms"
}
group
{
name "black"
texture1 "models/characters/average_sleeves/a_marine_b1"
}
}
*/
#define sSKINKEYWORD_MATERIAL "material"
#define sSKINKEYWORD_GROUP "group"
#define sSKINKEYWORD_NAME "name"
#define sSKINKEYWORD_PREFS "prefs"
#define sSKINKEYWORD_MODELSUSING "models"
#define sSKINKEYWORD_SURFACES_ON "surfaces_on"
#define sSKINKEYWORD_SURFACES_OFF "surfaces_off"
#define sSKINKEYWORD_SURFACES_OFFNOCHILDREN "surfaces_offnochildren"
static LPCSTR Skins_Parse(string strThisSkinFileName, CGPGroup *pFileGroup, CGPGroup *pParseGroup_Prefs)
{
LPCSTR psError = NULL;
// read any optional surface on/off blocks...
//
for (int iSurfaceOnOffType = 0; iSurfaceOnOffType<3; iSurfaceOnOffType++)
{
CGPGroup *pSurfaceParseGroup = NULL;
switch (iSurfaceOnOffType)
{
case 0: pSurfaceParseGroup = pParseGroup_Prefs->FindSubGroup(sSKINKEYWORD_SURFACES_ON); break;
case 1: pSurfaceParseGroup = pParseGroup_Prefs->FindSubGroup(sSKINKEYWORD_SURFACES_OFF); break;
case 2: pSurfaceParseGroup = pParseGroup_Prefs->FindSubGroup(sSKINKEYWORD_SURFACES_OFFNOCHILDREN); break;
default: assert(0); break;
}
if (pSurfaceParseGroup)
{
CGPValue *pValue = pSurfaceParseGroup->GetPairs();
while (pValue)
{
// string str1 = (*it).first; // junk, eg "name1"
string str2 = pValue->GetTopValue();
switch (iSurfaceOnOffType)
{
case 0: CurrentSkinsSurfacePrefs[strThisSkinFileName].vSurfacesOn.push_back(str2); break;
case 1: CurrentSkinsSurfacePrefs[strThisSkinFileName].vSurfacesOff.push_back(str2); break;
case 2: CurrentSkinsSurfacePrefs[strThisSkinFileName].vSurfacesOffNoChildren.push_back(str2); break;
default: assert(0); break;
}
pValue = pValue->GetNext();
}
}
}
// find all the materials and add them to the skin set...
//
int iMaterialDeclarationIndex = 0;
for (CGPGroup *pMaterialGroup = pFileGroup->GetSubGroups(); pMaterialGroup; pMaterialGroup = pMaterialGroup->GetNext(), iMaterialDeclarationIndex++)
{
string strKeyWord = pMaterialGroup->GetName();
if (strKeyWord == sSKINKEYWORD_MATERIAL)
{
string strMaterialName(pMaterialGroup->FindPairValue(sSKINKEYWORD_NAME,""));
if (strMaterialName == "")
{
psError = va("%s[%d] had no \"%s\" field!\n",sSKINKEYWORD_MATERIAL, iMaterialDeclarationIndex, sSKINKEYWORD_NAME);
return psError;
}
// now iterate through the ethnic group variants of this material...
//
int iEthnicGroupIndex = 0;
for (CGPGroup *pEthnicGroup = pMaterialGroup->GetSubGroups(); pEthnicGroup; pEthnicGroup = pEthnicGroup->GetNext(), iEthnicGroupIndex++)
{
strKeyWord = pEthnicGroup->GetName();
if (strKeyWord == sSKINKEYWORD_GROUP)
{
string strEthnicGroupName(pEthnicGroup->FindPairValue(sSKINKEYWORD_NAME,""));
if (strEthnicGroupName == "")
{
psError = va("%s[%d] %s[%d] had no \"%s\" field!\n",sSKINKEYWORD_MATERIAL, iMaterialDeclarationIndex, sSKINKEYWORD_GROUP, iEthnicGroupIndex, sSKINKEYWORD_NAME);
return psError;
}
// now iterate through the shader variants for this ethnic version of this material... (is anyone reading this...?)
//
int iAlternateShaderIndex = 0;
for (CGPValue *pValue = pEthnicGroup->GetPairs(); pValue; pValue = pValue->GetNext())
{
string strField(pValue->GetName());
if (strField != sSKINKEYWORD_NAME)
{
// ... then it should be a shader...
//
string strShader(pValue->GetTopValue());
CurrentSkins[strThisSkinFileName][strEthnicGroupName][strMaterialName].push_back(strShader);
}
}
}
}
}
}
return psError;
}
// returns NULL if error, else usable (local) path...
//
static LPCSTR Skins_ModelNameToSkinPath(LPCSTR psModelFilename)
{
static CString str;
str = psModelFilename;
str.MakeLower();
while (str.Replace('\\','/')){}
// special case for anything in the "objects" dir...
//
int i = str.Find("models/objects/");
if (i>=0)
{
str = str.Left(i+strlen("models/objects/"));
str+= "skins";
return (LPCSTR) str;
}
// or anything else...
//
i = str.Find("models/");
if (i>=0)
{
str = str.Left(i+strlen("models/"));
str+= "characters/skins";
return (LPCSTR) str;
}
return NULL;
}
// (params should both be const, but GP1 has crap proto args)
//
static bool Skins_ParseThisFile(CGenericParser2 &SkinParser,
/*const*/char *psSkinFileData, string &strThisModelBaseName,
CGPGroup *&pFileGroup, CGPGroup *&pParseGroup_Prefs,
LPCSTR psSkinFileName, G2SkinModelPrefs_t &G2SkinModelPrefs
)
{
bool bParseThisSkinFile = false;
char *psDataPtr = psSkinFileData;
if (SkinParser.Parse(&psDataPtr, true))
{
pFileGroup = SkinParser.GetBaseParseGroup();
if (pFileGroup)
{
pParseGroup_Prefs = pFileGroup->FindSubGroup(sSKINKEYWORD_PREFS);//, true);
if (pParseGroup_Prefs)
{
// see if our current model is amongst those that use this skin...
//
// look for a "models" field which says who uses this skin file...
//
CGPGroup *pParseGroup_ModelsUsing = pParseGroup_Prefs->FindSubGroup(sSKINKEYWORD_MODELSUSING);
if (pParseGroup_ModelsUsing)
{
// ok, now search this subgroup for our model name...
//
for (CGPValue *pValue = pParseGroup_ModelsUsing->GetPairs(); pValue; pValue = pValue->GetNext())
{
string str1 = pValue->GetName();
string str2 = pValue->GetTopValue();
if (str2 == strThisModelBaseName)
{
bParseThisSkinFile = true;
//break; // do NOT stop scanning now
}
// record this model entry...
//
G2SkinModelPrefs[ psSkinFileName ][ str2 ] = 1; // any old junk, as long as we make an entry
}
}
}
}
}
else
{
ErrorBox(va("{} - Brace mismatch error in file \"%s\"!",psSkinFileName));
}
return bParseThisSkinFile;
}
typedef struct FT_s
{
FILETIME ft;
bool bValid;
FT_s()
{
bValid = false;
}
} FT_t;
typedef map <string,FT_t> SkinFileTimeDates_t;
static SkinFileTimeDates_t SkinFileTimeDates;
// returns true if at least one set of skin data was read, else false...
//
static bool Skins_Read(LPCSTR psModelFilename)
{
LPCSTR psError = NULL;
CWaitCursor;
LPCSTR psSkinsPath = Skins_ModelNameToSkinPath(psModelFilename); // eg "models/characters/skins"
if (psSkinsPath)
{
string strThisModelBaseName(String_ToLower(Filename_WithoutExt(Filename_WithoutPath(psModelFilename))));
char **ppsSkinFiles;
int iSkinFiles;
// scan for skin files...
//
ppsSkinFiles = //ri.FS_ListFiles( "shaders", ".shader", &iSkinFiles );
Sys_ListFiles( va("%s%s",gamedir,psSkinsPath),// const char *directory,
".g2skin", // const char *extension,
NULL, // char *filter,
&iSkinFiles,// int *numfiles,
qfalse // qboolean wantsubs
);
if ( !ppsSkinFiles || !iSkinFiles )
{
CurrentSkins.clear();
CurrentSkinsSurfacePrefs.clear();
return false;
}
if ( iSkinFiles > MAX_SKIN_FILES )
{
WarningBox(va("%d skin files found, capping to %d\n\n(tell me if this ever happens -Ste)", iSkinFiles, MAX_SKIN_FILES ));
iSkinFiles = MAX_SKIN_FILES;
}
// load and parse skin files...
//
// for now, I just scan each file and if it's out of date then I invalidate it's model-prefs info...
//
extern bool GetFileTime(LPCSTR psFileName, FILETIME &ft);
for (int i=0; i<iSkinFiles; i++)
{
bool bReParseThisFile = false;
char sFileName[MAX_QPATH];
LPCSTR psFileName = ppsSkinFiles[i];
Com_sprintf( sFileName, sizeof( sFileName ), "%s/%s", psSkinsPath, psFileName );
psFileName = &sFileName[0];
// have a go at getting this time/date stamp if not already present...
//
if (!SkinFileTimeDates[psFileName].bValid)
{
FILETIME ft;
if (GetFileTime(psFileName, ft))
{
SkinFileTimeDates[psFileName].ft = ft;
SkinFileTimeDates[psFileName].bValid = true;
}
}
// now see if there's a valid time-stamp, and use it if so, else give up and re-scan all files...
//
if (SkinFileTimeDates[psFileName].bValid)
{
FILETIME ft;
if (GetFileTime(psFileName, ft))
{
LONG l = CompareFileTime( &SkinFileTimeDates[psFileName].ft, &ft);
bReParseThisFile = (l<0);
}
else
{
bReParseThisFile = true;
}
}
else
{
bReParseThisFile = true;
}
if (bReParseThisFile)
{
G2SkinModelPrefs[sFileName].clear();
}
}
if (1)//bReParseSkinFiles || !CurrentSkins.size())
{
CurrentSkins.clear();
CurrentSkinsSurfacePrefs.clear();
char *buffers[MAX_SKIN_FILES]={0};
// long iTotalBytesLoaded = 0;
for ( int i=0; i<iSkinFiles && !psError; i++ )
{
char sFileName[MAX_QPATH];
string strThisSkinFile(ppsSkinFiles[i]);
Com_sprintf( sFileName, sizeof( sFileName ), "%s/%s", psSkinsPath, strThisSkinFile.c_str() );
StatusMessage( va("Scanning skin %d/%d: \"%s\"...",i+1,iSkinFiles,sFileName));
//ri.Printf( PRINT_ALL, "...loading '%s'\n", sFileName );
bool _bDiskLoadOccured = false; // debug use only, but wtf?
#define LOAD_SKIN_FILE \
/*iTotalBytesLoaded += */ ri.FS_ReadFile( sFileName, (void **)&buffers[i] ); \
if ( !buffers[i] ) \
{ \
CurrentSkins.clear(); \
CurrentSkinsSurfacePrefs.clear(); \
\
ri.Error( ERR_DROP, "Couldn't load %s", sFileName );\
} \
_bDiskLoadOccured = true;
// see if we should pay attention to the contents of this file...
//
CGPGroup *pFileGroup = NULL;
CGPGroup *pParseGroup_Prefs = NULL;
CGenericParser2 SkinParser;
//
bool bParseThisFile = false;
//
// if we have any information about this skin file as regards what models it refers to, use the info...
//
if (G2SkinModelPrefs[sFileName].size())
{
map<string, int>::iterator it = G2SkinModelPrefs[sFileName].find( strThisModelBaseName );
if (it != G2SkinModelPrefs[sFileName].end())
{
// this skin file contains this entry, so just check that we can setup the parse groups ok...
//
LOAD_SKIN_FILE;
char *psDataPtr = buffers[i];
if (SkinParser.Parse(&psDataPtr, true))
{
pFileGroup = SkinParser.GetBaseParseGroup();
if (pFileGroup)
{
pParseGroup_Prefs = pFileGroup->FindSubGroup(sSKINKEYWORD_PREFS);//, true);
if (pParseGroup_Prefs)
{
bParseThisFile = true;
}
}
}
else
{
ErrorBox(va("{} - Brace mismatch error in file \"%s\"!",sFileName));
}
}
}
else
{
// no size info for this file, so check it manually...
//
LOAD_SKIN_FILE;
if (Skins_ParseThisFile(SkinParser, buffers[i], strThisModelBaseName, pFileGroup, pParseGroup_Prefs,
sFileName, G2SkinModelPrefs)
)
{
bParseThisFile = true;
}
}
if (bParseThisFile)
{
psError = Skins_Parse( strThisSkinFile, pFileGroup, pParseGroup_Prefs);
if (psError)
{
ErrorBox(va("Skins_Read(): Error reading file \"%s\"!\n\n( Skins will be ignored for this model )\n\nError was:\n\n",sFileName,psError));
}
}
else
{
//OutputDebugString(va("Skipping parse of file \"%s\" %s\n",sFileName, _bDiskLoadOccured?"":"( and no load! )"));
}
}
//
// free loaded skin files...
//
for ( i=0; i<iSkinFiles; i++ )
{
if (buffers[i])
{
ri.FS_FreeFile( buffers[i] );
}
}
}
StatusMessage(NULL);
Sys_FreeFileList( ppsSkinFiles );
}
else
{
CurrentSkins.clear();
CurrentSkinsSurfacePrefs.clear();
}
if (psError)
{
return false;
}
return !!(CurrentSkins.size());
}
// ask if there are any skins available for the supplied GLM model path...
//
bool Skins_FilesExist(LPCSTR psModelFilename)
{
return Skins_Read(psModelFilename);
}
static Skins_ApplyVariant(ModelContainer_t *pContainer, SkinSet_t::iterator itEthnic, ShadersForMaterial_t::iterator itMaterialShaders, string strMaterialName, int iVariant)
{
if (itMaterialShaders != (*itEthnic).second.end())
{
LPCSTR psShaderName = (*itMaterialShaders).second[iVariant].c_str(); // shader name to load from skinset
pContainer->MaterialShaders[strMaterialName] = psShaderName;
LPCSTR psLocalTexturePath = R_FindShader( psShaderName ); // shader->texture name
if (psLocalTexturePath && strlen(psLocalTexturePath))
{
TextureHandle_t hTexture = TextureHandle_ForName( psLocalTexturePath );
if (hTexture == -1)
{
hTexture = Texture_Load(psLocalTexturePath);
}
GLuint uiBind = Texture_GetGLBind( hTexture );
pContainer->MaterialBinds[strMaterialName] = uiBind;
}
}
}
static void Skins_SetSurfacePrefs(ModelContainer_t *pContainer, StringVector_t &SurfaceNames, SurfaceOnOff_t eStatus)
{
bool bFailureOccured = false;
for (int i=0; i<SurfaceNames.size() && !bFailureOccured; i++)
{
string strSurfaceName = SurfaceNames[i];
if (!Model_GLMSurface_SetStatus( pContainer->hModel, strSurfaceName.c_str(), eStatus))
bFailureOccured = true;
}
}
// this fills in a modelcontainer's "MaterialBinds" and "MaterialShaders" fields (registering textures etc)
// based on the skinset pointed at by pContainer->SkinSets and strSkinFile
//
bool Skins_ApplySkinFile(ModelContainer_t *pContainer, string strSkinFile, string strEthnic,
bool bApplySurfacePrefs, bool bDefaultSurfaces,
string strMaterial, int iVariant) // optional params, else "" and -1
{
CWaitCursor wait;
bool bReturn = true;
// new bit, if surface prefs active, then enable/disable model surfaces...
//
if (bApplySurfacePrefs || bDefaultSurfaces)
{
Model_GLMSurfaces_DefaultAll(pContainer->hModel);
}
if (bApplySurfacePrefs)
{
// now go through surface prefs list and turn them on/off accordingly...
//
SkinSetsSurfacePrefs_t::iterator itSurfPrefs = pContainer->SkinSetsSurfacePrefs.find(strSkinFile.c_str());
if (itSurfPrefs != pContainer->SkinSetsSurfacePrefs.end())
{
SurfaceOnOffPrefs_t &prefs = (*itSurfPrefs).second;
Skins_SetSurfacePrefs(pContainer, prefs.vSurfacesOn, SURF_ON);
Skins_SetSurfacePrefs(pContainer, prefs.vSurfacesOff, SURF_OFF);
Skins_SetSurfacePrefs(pContainer, prefs.vSurfacesOffNoChildren, SURF_NO_DESCENDANTS);
}
}
// setup a map of material names first, which we can then read from and lookup shaders for...
//
if (iVariant != -1)
{
// just clear this one...
//
assert(!strMaterial.empty());
pContainer->MaterialShaders [strMaterial] = "";
pContainer->MaterialBinds [strMaterial] = (GLuint) 0;
}
else
{
pContainer->strCurrentSkinFile = strSkinFile;
pContainer->strCurrentSkinEthnic= strEthnic;
pContainer->MaterialBinds.clear();
pContainer->MaterialShaders.clear();
for (int iSurface = 0; iSurface<pContainer->iNumSurfaces; iSurface++)
{
// when we're at this point we know it's GLM model, and that the shader name is in fact a material name...
//
LPCSTR psMaterialName = GLMModel_GetSurfaceShaderName( pContainer->hModel, iSurface );
pContainer->MaterialShaders [psMaterialName] = ""; // just insert the key for now, so the map<> is legit.
pContainer->MaterialBinds [psMaterialName] = (GLuint) 0; // default to gl-white-notfound texture
}
}
SkinSets_t::iterator itSkins = pContainer->SkinSets.find(strSkinFile);
if (itSkins != pContainer->SkinSets.end())
{
SkinSet_t::iterator itEthnic = (*itSkins).second.find(strEthnic);
// new bit, auto-hack the ethnic-variant if we don't find it to use the first one (so lazy NPC->MVS code works)...
//
if (itEthnic == (*itSkins).second.end())
{
itEthnic = (*itSkins).second.begin();
string strNewEthnic = (*itEthnic).first;
pContainer->strCurrentSkinEthnic= strNewEthnic;
}
if (itEthnic != (*itSkins).second.end())
{
// ok, we've found the ethnic skin set specified, now use it to look up the shaders for the materials...
//
// query these materials, and find the appropriate skinfile/ethnic shader (variant 0 for now)...
//
if (iVariant != -1)
{
ShadersForMaterial_t::iterator itMaterialShaders = (*itEthnic).second.find(strMaterial);
Skins_ApplyVariant(pContainer, itEthnic, itMaterialShaders, strMaterial, iVariant);
}
else
{
for (MappedString_t::iterator itMaterial = pContainer->MaterialShaders.begin(); itMaterial != pContainer->MaterialShaders.end(); ++itMaterial)
{
LPCSTR psMaterialName = (*itMaterial).first.c_str();
// find this material in the skinset...
//
ShadersForMaterial_t::iterator itMaterialShaders = (*itEthnic).second.find((*itMaterial).first);
Skins_ApplyVariant(pContainer, itEthnic, itMaterialShaders, psMaterialName, 0);
}
}
}
}
return bReturn;
}
// sets up valid skin tables based on first entries loaded, also registers/binds appropriate textures...
//
void Skins_ApplyDefault(ModelContainer_t *pContainer)
{
for (SkinSets_t::iterator itSkins = pContainer->SkinSets.begin(); itSkins != pContainer->SkinSets.end(); ++itSkins)
{
string strCurrentSkinFile = (*itSkins).first;
for (SkinSet_t::iterator itEthnic = (*itSkins).second.begin(); itEthnic != (*itSkins).second.end(); ++itEthnic)
{
string strCurrentEthnic = (*itEthnic).first; // eg "white"
Skins_ApplySkinFile(pContainer, strCurrentSkinFile, strCurrentEthnic, true, true);
return; // :-)
}
}
}
// returns true if at least one set of skin data was read, else false...
//
bool Skins_Read(LPCSTR psModelFilename, ModelContainer_t *pContainer)
{
if (Skins_Read(psModelFilename))
{
pContainer->SkinSets = CurrentSkins; // huge stl-nested-map copy
pContainer->SkinSetsSurfacePrefs = CurrentSkinsSurfacePrefs;
return true;
}
return false;
}
typedef map <string, StringVector_t> EthnicMaterials_t;
typedef map < string, EthnicMaterials_t > SkinFileMaterialsMissing_t; // [skinfile][ethnic][missing materials]
static void SkinSet_Validate_BuildList(StringSet_t &strUniqueSkinShaders, SkinSets_t::iterator itSkins, StringSet_t &MaterialsPresentInModel, SkinFileMaterialsMissing_t &SkinFileMaterialsMissing)
{
string strSkinName((*itSkins).first); // eg "thug1"
// race variants...
//
for (SkinSet_t::iterator itEthnic = (*itSkins).second.begin(); itEthnic != (*itSkins).second.end(); ++itEthnic)
{
string strEthnicName((*itEthnic).first); // eg "white"
map <string, int> MaterialsDefinedInThisEthnicVariant;
// materials...
//
for (ShadersForMaterial_t::iterator itMaterial = (*itEthnic).second.begin(); itMaterial != (*itEthnic).second.end(); ++itMaterial)
{
string strMaterialName((*itMaterial).first); // eg "face"
// keep a note of all materials defined in this file...
//
MaterialsDefinedInThisEthnicVariant[strMaterialName]=1; // 1 = whatever
// available shader variants for this material...
//
for (int iShaderVariantIndex=0; iShaderVariantIndex<(*itMaterial).second.size(); iShaderVariantIndex++)
{
string strShaderVariantName((*itMaterial).second[iShaderVariantIndex]); // eg "models/characters/average_sleeves/face"
strUniqueSkinShaders.insert(strUniqueSkinShaders.end(),strShaderVariantName);
}
}
// now scan through the materials defined in this ethnic variant, and see if there were any materials
// mentioned/referenced by the model which weren't defined here...
//
for (StringSet_t::iterator itMaterialsReferenced = MaterialsPresentInModel.begin(); itMaterialsReferenced != MaterialsPresentInModel.end(); ++itMaterialsReferenced)
{
string strModelMaterial = (*itMaterialsReferenced);
if (!strModelMaterial.empty() && stricmp(strModelMaterial.c_str(),"[NoMaterial]"))
{
if (MaterialsDefinedInThisEthnicVariant.find(strModelMaterial) == MaterialsDefinedInThisEthnicVariant.end())
{
SkinFileMaterialsMissing[strSkinName][strEthnicName].push_back(strModelMaterial);
//
// OutputDebugString(va("strModelMaterial = %s\n",strModelMaterial.c_str()));
}
}
}
}
}
// UI-code...
//
bool Skins_FileHasSurfacePrefs(ModelContainer_t *pContainer, LPCSTR psSkin)
{
SkinSetsSurfacePrefs_t::iterator itSurfPrefs = pContainer->SkinSetsSurfacePrefs.find(psSkin);
if (itSurfPrefs != pContainer->SkinSetsSurfacePrefs.end())
{
SurfaceOnOffPrefs_t &prefs = (*itSurfPrefs).second;
return prefs.vSurfacesOn.size() ||
prefs.vSurfacesOff.size() ||
prefs.vSurfacesOffNoChildren.size()
;
}
return false;
}
bool Skins_ApplyEthnic( ModelContainer_t *pContainer, LPCSTR psSkin, LPCSTR psEthnic, bool bApplySurfacePrefs, bool bDefaultSurfaces)
{
return Skins_ApplySkinFile(pContainer, psSkin, psEthnic, bApplySurfacePrefs, bDefaultSurfaces);
}
bool Skins_ApplySkinShaderVariant(ModelContainer_t *pContainer, LPCSTR psSkin, LPCSTR psEthnic, LPCSTR psMaterial, int iVariant )
{
return Skins_ApplySkinFile(pContainer, psSkin, psEthnic, false, false, psMaterial, iVariant );
}
// returns false for "at least one shader or material missing", else true for all ok...
//
extern bool g_bReportImageLoadErrors;
bool Skins_Validate( ModelContainer_t *pContainer, int iSkinNumber )
{
bool bReturn = true;
bool bPREV_bReportImageLoadErrors = g_bReportImageLoadErrors;
g_bReportImageLoadErrors = false;
bool bCheckMissingMaterials = true;//GetYesNo("Check for materials referenced by model but missing in skinfile(s)?\n\n( Note: This can give false alarms for skins which don't use (eg) \"scarf\" )");
// first, build up a list of all model materials...
//
StringSet_t MaterialsPresentInModel;
for (int iSurface = 0; iSurface < pContainer->iNumSurfaces; iSurface++)
{
bool bOnOff = GLMModel_SurfaceIsON(pContainer->hModel, iSurface);
if (bOnOff)
{
LPCSTR psMaterial = GLMModel_GetSurfaceShaderName( pContainer->hModel, iSurface);
MaterialsPresentInModel.insert(MaterialsPresentInModel.end(),psMaterial);
}
}
// build up a list of shaders used...
//
StringSet_t UniqueSkinShaders;
SkinFileMaterialsMissing_t SkinFileMaterialsMissing;
int iThisSkinIndex = 0;
for (SkinSets_t::iterator itSkins = pContainer->SkinSets.begin(); itSkins != pContainer->SkinSets.end(); ++itSkins, iThisSkinIndex++)
{
if (iSkinNumber == iThisSkinIndex || iSkinNumber == -1)
{
SkinSet_Validate_BuildList(UniqueSkinShaders, itSkins, MaterialsPresentInModel, SkinFileMaterialsMissing);
}
}
// now process the unique list we've just built...
//
CWaitCursor wait;
string strFoundList;
string strNotFoundList;
int iUniqueIndex = 0;
for (StringSet_t::iterator it = UniqueSkinShaders.begin(); it != UniqueSkinShaders.end(); ++it, iUniqueIndex++)
{
string strShader(*it);
StatusMessage(va("Processing shader %d/%d: \"%s\"\n",iUniqueIndex,UniqueSkinShaders.size(),strShader.c_str()));
OutputDebugString(va("Unique: \"%s\"... ",strShader.c_str()));
int iTextureHandle = Texture_Load(strShader.c_str(), true); // bInhibitStatus
GLuint uiGLBind = Texture_GetGLBind( iTextureHandle );
if (uiGLBind == 0)
{
OutputDebugString("NOT FOUND\n");
strNotFoundList += strShader;
strNotFoundList += "\n";
}
else
{
OutputDebugString("found\n");
strFoundList += strShader;
strFoundList += "\n";
}
}
StatusMessage(NULL);
// see if we were missing any model materials in these skins...
//
CString strModelMaterialsMissing;
if (SkinFileMaterialsMissing.size())
{
for (SkinFileMaterialsMissing_t::iterator itSkinFileMaterialsMissing = SkinFileMaterialsMissing.begin(); itSkinFileMaterialsMissing != SkinFileMaterialsMissing.end(); ++itSkinFileMaterialsMissing)
{
string strSkinFileName((*itSkinFileMaterialsMissing).first);
if (iSkinNumber == -1)
{
strModelMaterialsMissing += va("\nSkin \"%s\":\n",strSkinFileName.c_str());
}
for (EthnicMaterials_t::iterator itSkinFile = (*itSkinFileMaterialsMissing).second.begin(); itSkinFile != (*itSkinFileMaterialsMissing).second.end(); ++itSkinFile)
{
string strEthnicFileName((*itSkinFile).first);
strModelMaterialsMissing += va("Ethnic \"%s\": ",strEthnicFileName.c_str());
StringVector_t& MaterialStrings = (*itSkinFile).second;
for (int iMaterial = 0; iMaterial != MaterialStrings.size(); ++iMaterial)
{
string strMaterial(MaterialStrings[iMaterial]);
strModelMaterialsMissing += va("%s\"%s\"",(iMaterial==0)?"":", ",strMaterial.c_str());
}
strModelMaterialsMissing += "\n";
}
}
}
if (!strModelMaterialsMissing.IsEmpty())
{
if (iSkinNumber == -1)
{
strModelMaterialsMissing.Insert(0, "One or more skin files are missing some material definitions referenced by this model's currently-active surfaces.\nList follows...\n\n");
}
else
{
strModelMaterialsMissing.Insert(0, "This skin file is missing one or more material definitions referenced by this model's currently-active surfaces.\nList follows...\n\n");
}
}
if (!strModelMaterialsMissing.IsEmpty())
{
if (bCheckMissingMaterials)
{
WarningBox(va("Summary Part 1: Missing materials\n\n%s",(LPCSTR)strModelMaterialsMissing));
}
}
// Now output results...
// If too many lines to fit on screen (which is now happening), send 'em to notepad instead...
//
// ( tacky way of counting lines...)
CString strTackyCount(strNotFoundList.c_str());
strTackyCount += strFoundList.c_str();
int iLines = strTackyCount.Replace('\n','?'); // :-)
#define MAX_BOX_LINES_HERE 50
if (strNotFoundList.empty())
{
if (iLines > MAX_BOX_LINES_HERE)
{
if (GetYesNo(va("All shaders found... :-)\n\nList has > %d entries, send to Notepad?",MAX_BOX_LINES_HERE)))
{
SendStringToNotepad(va("All shaders found... :-)\n\nList follows:\n\n%s",strFoundList.c_str()),"found_shaders.txt");
}
}
else
{
InfoBox(va("All shaders found... :-)\n\nList follows:\n\n%s",strFoundList.c_str()));
}
}
else
{
if (iLines > MAX_BOX_LINES_HERE)
{
if (GetYesNo(va("Some missing shader, some found, but list is > %d entries, send to Notepad?",MAX_BOX_LINES_HERE)))
{
SendStringToNotepad(va("Missing shaders:\n\n%s\n\nFound shaders:\n\n%s",strNotFoundList.c_str(),strFoundList.c_str()),"found_shaders.txt");
}
}
else
{
WarningBox(va("Missing shaders:\n\n%s\n\nFound shaders:\n\n%s",strNotFoundList.c_str(),strFoundList.c_str()));
}
bReturn = false;
}
g_bReportImageLoadErrors = bPREV_bReportImageLoadErrors;
return bReturn;
}
bool Skins_ApplyToTree (HTREEITEM hTreeItem_Parent, ModelContainer_t *pContainer)
{
bool bReturn = false;
if (pContainer->SkinSets.size())
{
bReturn = true;
TreeItemData_t TreeItemData={0};
TreeItemData.iItemType = TREEITEMTYPE_SKINSHEADER;
TreeItemData.iModelHandle = pContainer->hModel;
HTREEITEM hTreeItem_SkinsHeader = ModelTree_InsertItem("G2Skins available", hTreeItem_Parent, TreeItemData.uiData);
// skins...
//
int iSkinNumber = 0;
for (SkinSets_t::iterator itSkins = pContainer->SkinSets.begin(); itSkins != pContainer->SkinSets.end(); ++itSkins, iSkinNumber++)
{
string strSkinName((*itSkins).first); // eg "thug1"
TreeItemData.iItemNumber = iSkinNumber;
TreeItemData.iItemType = TREEITEMTYPE_SKIN;
HTREEITEM hTreeItem_ThisSkin = ModelTree_InsertItem(strSkinName.c_str(), hTreeItem_SkinsHeader, TreeItemData.uiData);
// race variants...
//
int iEthnicNumber = 0;
for (SkinSet_t::iterator itEthnic = (*itSkins).second.begin(); itEthnic != (*itSkins).second.end(); ++itEthnic, iEthnicNumber++)
{
string strEthnicName((*itEthnic).first); // eg "white"
TreeItemData.iItemNumber = iEthnicNumber;
TreeItemData.iItemType = TREEITEMTYPE_SKINETHNIC;
HTREEITEM hTreeItem_ThisEthnic = ModelTree_InsertItem(strEthnicName.c_str(), hTreeItem_ThisSkin, TreeItemData.uiData);
// materials...
//
int iMaterialNumber = 0;
for (ShadersForMaterial_t::iterator itMaterial = (*itEthnic).second.begin(); itMaterial != (*itEthnic).second.end(); ++itMaterial, iMaterialNumber++)
{
string strMaterialName((*itMaterial).first); // eg "face"
TreeItemData.iItemNumber = iMaterialNumber;
TreeItemData.iItemType = TREEITEMTYPE_SKINMATERIAL;
HTREEITEM hTreeItem_ThisMaterial = ModelTree_InsertItem(strMaterialName.c_str(), hTreeItem_ThisEthnic, TreeItemData.uiData);
// available shader variants for this material...
//
for (int iShaderVariantIndex=0; iShaderVariantIndex<(*itMaterial).second.size(); iShaderVariantIndex++)
{
string strShaderVariantName((*itMaterial).second[iShaderVariantIndex]); // eg "models/characters/average_sleeves/face"
TreeItemData.iItemNumber = iShaderVariantIndex;
TreeItemData.iItemType = TREEITEMTYPE_SKINMATERIALSHADER;
HTREEITEM hTreeItem_ThisShaderVariant = ModelTree_InsertItem(strShaderVariantName.c_str(), hTreeItem_ThisMaterial, TreeItemData.uiData);
}
}
}
}
}
return bReturn;
}
// called directly from renderer...
//
extern ModelContainer_t* gpContainerBeingRendered;
GLuint AnySkin_GetGLBind( ModelHandle_t hModel, LPCSTR psMaterialName, LPCSTR psSurfaceName )
{
ModelContainer_t *pContainer = gpContainerBeingRendered; // considerably faster during rendering process :-)
if (pContainer == NULL)
{
pContainer = ModelContainer_FindFromModelHandle(hModel);
}
if (pContainer)
{
if (pContainer->SkinSets.size())
{
return pContainer->MaterialBinds[psMaterialName];
}
return OldSkins_GetGLBind(pContainer,psSurfaceName);
}
return (GLuint) 0;
}
/////////////////// eof /////////////////