506 lines
15 KiB
C++
506 lines
15 KiB
C++
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
/////////////
|
|
// INCLUDE //
|
|
/////////////
|
|
#include "stdpch.h"
|
|
|
|
#include "color_slot_manager.h"
|
|
|
|
#include "nel/misc/path.h"
|
|
#include "nel/misc/common.h"
|
|
#include "nel/misc/config_file.h"
|
|
#include "nel/misc/file.h"
|
|
|
|
#include "nel/3d/u_instance_material.h"
|
|
|
|
#include <algorithm>
|
|
|
|
using namespace std;
|
|
|
|
////////////
|
|
// GLOBAL //
|
|
////////////
|
|
|
|
H_AUTO_DECL(RZ_ColorSlotManager)
|
|
|
|
NLMISC::CConfigFile ColorSlotConfigFile;
|
|
|
|
/// Manage slot and color for instances.
|
|
CColorSlotManager ColorSlotManager;
|
|
|
|
// convert an array of char * to a vector of strings
|
|
static void stringArrayToVector(const char *strArray[], uint size, CColorSlotManager::TStringVect &dest)
|
|
{
|
|
dest.resize(size);
|
|
std::copy(strArray, strArray + size, dest.begin());
|
|
}
|
|
|
|
//-----------------------------------------------
|
|
// initColorSlotManager :
|
|
// Initialize the Color slot manager for the client.
|
|
//-----------------------------------------------
|
|
void initColorSlotManager()
|
|
{
|
|
char tmpBuff[300];
|
|
uint startSlot;
|
|
|
|
static const char *skins[] = { "FY", "MA", "TR", "ZO" };
|
|
static const char *user[] = { "U1", "U2", "U3", "U4", "U5", "U6", "U7", "U8" };
|
|
static const char *eyes[] = { "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8" };
|
|
static const char *hair[] = { "H1", "H2", "H3", "H4", "H5", "H6" };
|
|
|
|
CColorSlotManager::TStringVect sv;
|
|
// skin
|
|
stringArrayToVector(skins, sizeof(skins) / sizeof(skins[0]), sv);
|
|
startSlot = ColorSlotManager.addSlot(sv);
|
|
// user
|
|
stringArrayToVector(user, sizeof(user) / sizeof(user[0]), sv);
|
|
ColorSlotManager.addSlot(sv);
|
|
// hair
|
|
stringArrayToVector(hair, sizeof(hair) / sizeof(hair[0]), sv);
|
|
ColorSlotManager.addSlot(sv);
|
|
// eyes
|
|
stringArrayToVector(eyes, sizeof(eyes) / sizeof(eyes[0]), sv);
|
|
ColorSlotManager.addSlot(sv);
|
|
|
|
|
|
string filename = NLMISC::CPath::lookup("panoply_files.txt", false, true);
|
|
if (filename.empty())
|
|
{
|
|
nlwarning("Couldn't find the color slot manager texture list");
|
|
return;
|
|
}
|
|
NLMISC::CIFile cfg;
|
|
if(cfg.open(filename, false))
|
|
{
|
|
while(!cfg.eof())
|
|
{
|
|
cfg.getline(tmpBuff, 300);
|
|
ColorSlotManager.addTexture(tmpBuff);
|
|
}
|
|
|
|
// Close the file.
|
|
cfg.close();
|
|
}
|
|
}// initColorSlotManager //
|
|
|
|
|
|
const char *CColorSlotManager::_DefaultTextureExtension[] = { "tga", "dds" };
|
|
const uint CColorSlotManager::_NumDefaultTextureExtensions = sizeof(CColorSlotManager::_DefaultTextureExtension) / sizeof(const char *);
|
|
|
|
|
|
//=======================================================================
|
|
CColorSlotManager::TIntCouple CColorSlotManager::findFileExtensionInSlot(const std::string &ext) const
|
|
{
|
|
H_AUTO_USE(RZ_ColorSlotManager)
|
|
for(TSlotVect::const_iterator it = _Slots.begin(); it != _Slots.end(); ++it)
|
|
{
|
|
TStringVect::const_iterator extIt = std::find(it->begin(), it->end(), ext);
|
|
if (extIt != it->end())
|
|
{
|
|
return std::make_pair((uint)(it - _Slots.begin()), (uint)(extIt - it->begin()));
|
|
}
|
|
}
|
|
return TIntCouple((uint) NotFound, 0);
|
|
}
|
|
|
|
//=======================================================================
|
|
CColorSlotManager::CColorSlotManager() : _Separator("_")
|
|
{
|
|
}
|
|
|
|
//=======================================================================
|
|
uint CColorSlotManager::addSlot(const TStringVect &slotDescs)
|
|
{
|
|
H_AUTO_USE(RZ_ColorSlotManager)
|
|
_Slots.push_back(slotDescs);
|
|
for(uint k = 0; k < slotDescs.size(); ++k)
|
|
{
|
|
_Slots.back()[k] = NLMISC::toUpper(_Slots.back()[k]);
|
|
}
|
|
return (uint)_Slots.size() - 1;
|
|
}
|
|
|
|
|
|
//=======================================================================
|
|
bool CColorSlotManager::addTexture(const char *texName)
|
|
{
|
|
H_AUTO_USE(RZ_ColorSlotManager)
|
|
static std::string texNameNoExtension;
|
|
static TIntCoupleVect slotIDs;
|
|
bool result = parseTexName(texName, &texNameNoExtension, &slotIDs);
|
|
if (!result) return false;
|
|
// see if not already in the map
|
|
TTex2Slots::iterator it = _TexMap.find(texNameNoExtension);
|
|
if (it == _TexMap.end())
|
|
{
|
|
// insert in map
|
|
TUIntVect &slots = _TexMap[texNameNoExtension];
|
|
slots.resize(slotIDs.size());
|
|
for(uint k = 0; k < slotIDs.size(); ++k)
|
|
{
|
|
slots[k] = slotIDs[k].first;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TUIntVect &slots = it->second;
|
|
// make sure the slots are identical
|
|
if (slots.size() != slotIDs.size())
|
|
{
|
|
nlwarning("%s number of slots is not constant", texName);
|
|
}
|
|
for(uint k = 0; k < slots.size(); ++k)
|
|
{
|
|
if (slots[k] != slotIDs[k].first)
|
|
{
|
|
nlwarning("Slots for %s are inconsistents", texName);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//=======================================================================
|
|
bool CColorSlotManager::addTexturesFromPath(const std::string &path, const char *validExtensions[] /*=_DefaultTextureExtension*/, uint numExtensions /*=_NumDefaultTextureExtensions*/)
|
|
{
|
|
H_AUTO_USE(RZ_ColorSlotManager)
|
|
if (!validExtensions || numExtensions == 0)
|
|
{
|
|
nlwarning("CColorSlotManager::addTexturesFromPath : no file extensions provided");
|
|
return false;
|
|
}
|
|
if (path.empty())
|
|
{
|
|
nlwarning("CColorSlotManager::addTexturesFromPath : empty path provided");
|
|
return false;
|
|
}
|
|
if (!NLMISC::CFile::isExists(path))
|
|
{
|
|
nlwarning(("CColorSlotManager::addTexturesFromPath : path not found : " + path).c_str());
|
|
return false;
|
|
}
|
|
std::vector<std::string> files;
|
|
NLMISC::CPath::getPathContent(path, true, false, true, files);
|
|
if (files.empty())
|
|
{
|
|
nlwarning(("CColorSlotManager::addTexturesFromPath : empty path :" + path).c_str());
|
|
return false;
|
|
}
|
|
bool everythingOk = true;
|
|
bool fileFound = false;
|
|
std::string fileExt;
|
|
for(uint k = 0; k < files.size(); ++k)
|
|
{
|
|
fileExt = NLMISC::CFile::getExtension(files[k]);
|
|
bool extFound = false;
|
|
for(uint l = 0; l < numExtensions && !extFound; ++l)
|
|
{
|
|
if (NLMISC::nlstricmp(fileExt.c_str(), validExtensions[l]) == 0)
|
|
{
|
|
fileFound = true;
|
|
if (!addTexture(files[k].c_str()))
|
|
{
|
|
everythingOk = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!fileFound)
|
|
{
|
|
nlwarning(("CColorSlotManager::addTexturesFromPath : the path " + path + " contains no texture files").c_str());
|
|
}
|
|
return everythingOk;
|
|
}
|
|
|
|
|
|
|
|
//=======================================================================
|
|
void CColorSlotManager::resetTextures()
|
|
{
|
|
H_AUTO_USE(RZ_ColorSlotManager)
|
|
_TexMap.clear();
|
|
}
|
|
|
|
|
|
//=======================================================================
|
|
void CColorSlotManager::resetSlots()
|
|
{
|
|
H_AUTO_USE(RZ_ColorSlotManager)
|
|
_Slots.clear();
|
|
}
|
|
|
|
//=======================================================================
|
|
bool CColorSlotManager::parseTexName(const char *texName, std::string *texNameWithoutExtensions,
|
|
TIntCoupleVect *destSlotsId /*=NULL*/) const
|
|
{
|
|
H_AUTO_USE(RZ_ColorSlotManager)
|
|
/** If we got a name such as "armour_red_shiny.tga", and if shiny and red are extensions, we parse
|
|
* shiny ('_' is considered has a separator), and find the slot that has 'shiny' in it (lets says slot 0),
|
|
* then 'red' is parsed (let says it is in the slot 1).
|
|
* The slotId vector will be { 0, 1 } at the end
|
|
* If no slots where found, the parsing is considered to have failed.
|
|
* If an extension is duplicated then it fails, too.
|
|
*/
|
|
typedef std::string::size_type TStrPos;
|
|
static std::string nameToParse;
|
|
static std::string currentExt;
|
|
static TIntCoupleVect slotsId;
|
|
nameToParse = NLMISC::toUpper(NLMISC::CFile::getFilenameWithoutExtension(texName));
|
|
|
|
TStrPos currPos = nameToParse.length();
|
|
|
|
slotsId.clear();
|
|
for(;;)
|
|
{
|
|
// search a separator
|
|
TStrPos extPos = nameToParse.find_last_of(_Separator, currPos);
|
|
if (extPos == std::string::npos) break;
|
|
// get the extension and see if it is in a slot
|
|
currentExt = nameToParse.substr(extPos + _Separator.length(), currPos - extPos - _Separator.length() + 1);
|
|
TIntCouple slotId = findFileExtensionInSlot(currentExt);
|
|
if (slotId.first == NotFound)
|
|
{
|
|
break;
|
|
}
|
|
for(uint k = 0; k < slotsId.size(); ++k)
|
|
{
|
|
if (slotsId[k].first == slotId.first)
|
|
{
|
|
nlwarning((std::string("Duplicated extension in texture name ") + texName).c_str());
|
|
return false;
|
|
}
|
|
}
|
|
slotsId.push_back(slotId);
|
|
if (extPos == 0) break;
|
|
currPos = extPos - 1;
|
|
}
|
|
|
|
if (slotsId.empty()) return false;
|
|
if (destSlotsId)
|
|
{
|
|
std::reverse(slotsId.begin(), slotsId.end());
|
|
*destSlotsId = slotsId; // commit changes
|
|
}
|
|
if (texNameWithoutExtensions)
|
|
{
|
|
texNameWithoutExtensions->resize(currPos + 1);
|
|
std::copy(nameToParse.begin(), nameToParse.begin() + currPos + 1, texNameWithoutExtensions->begin());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
//=======================================================================
|
|
bool CColorSlotManager::addSlotsFromConfigFile(NLMISC::CConfigFile &cf, uint &startSlotDest, uint &numSlots)
|
|
{
|
|
H_AUTO_USE(RZ_ColorSlotManager)
|
|
NLMISC::CConfigFile::CVar *mask_extensions;
|
|
// get list of masks / slots
|
|
try
|
|
{
|
|
mask_extensions = &cf.getVar("mask_extensions");
|
|
}
|
|
catch (const NLMISC::EUnknownVar &)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint startSlot = (uint)_Slots.size();
|
|
|
|
_Slots.resize(_Slots.size() + mask_extensions->size());
|
|
/// For each kind of mask, build a slot
|
|
for (uint k = 0; k < (uint) mask_extensions->size(); ++k)
|
|
{
|
|
try
|
|
{
|
|
NLMISC::CConfigFile::CVar &extensions = cf.getVar(mask_extensions->asString(k) +"_color_id");
|
|
_Slots[k + startSlot].resize(extensions.size());
|
|
for(sint l = 0; l < (sint) extensions.size(); ++l)
|
|
{
|
|
_Slots[k + startSlot][l] = extensions.asString(l);
|
|
}
|
|
}
|
|
catch (const NLMISC::EUnknownVar &)
|
|
{
|
|
_Slots.resize(startSlot);
|
|
nlwarning(("CColorSlotManager::addSlotsFromConfigFile : invalid config file, variable not found :" + mask_extensions->asString(k) + "_color_id").c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
_Separator = cf.getVar("default_separator").asString();
|
|
}
|
|
catch (const NLMISC::EUnknownVar &)
|
|
{
|
|
_Separator = "_";
|
|
}
|
|
|
|
startSlotDest = startSlot;
|
|
numSlots = (uint)_Slots.size() - startSlot;
|
|
return true;
|
|
}
|
|
|
|
|
|
//=======================================================================
|
|
bool CColorSlotManager::changeTexName(std::string &texName, TIntCouple *slotIDs, uint numValues, bool &everythingOk) const
|
|
{
|
|
H_AUTO_USE(RZ_ColorSlotManager)
|
|
static std::string texNameNoExt;
|
|
static std::string texNameBuild;
|
|
static std::string texExt;
|
|
static TIntCoupleVect srcSlotIDs;
|
|
|
|
everythingOk = true;
|
|
texNameNoExt = NLMISC::toUpper(NLMISC::CFile::getFilenameWithoutExtension(texName));
|
|
texExt = NLMISC::CFile::getExtension(texName);
|
|
TTex2Slots::const_iterator texIt = _TexMap.find(texNameNoExt);
|
|
if (texIt != _TexMap.end())
|
|
{
|
|
/** Texture hasn't been setup yet.
|
|
* We add all the extensions
|
|
*/
|
|
for(uint m = 0; m < texIt->second.size(); ++m)
|
|
{
|
|
uint n;
|
|
// search if there's a matching slot in the list
|
|
for(n = 0; n < numValues; ++n)
|
|
{
|
|
if (texIt->second[m] == slotIDs[n].first)
|
|
{
|
|
if (slotIDs[n].second < _Slots[slotIDs[n].first].size())
|
|
{
|
|
texNameNoExt += _Separator + _Slots[slotIDs[n].first][slotIDs[n].second];
|
|
}
|
|
else
|
|
{
|
|
nlwarning("Color slot : out of range value found");
|
|
// Use previous extension
|
|
texNameNoExt += _Separator + _Slots[srcSlotIDs[m].first][srcSlotIDs[m].second];
|
|
everythingOk = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (n == numValues) // not found
|
|
{
|
|
// Use default extension
|
|
texNameNoExt += _Separator + _Slots[texIt->second[m]][0];
|
|
}
|
|
}
|
|
texName = texNameNoExt += "." + texExt;
|
|
return true;
|
|
}
|
|
else // Extensions are already present in the tex name, or simply, there are no slots for this texture
|
|
{
|
|
/** The texture has been setup yet, or it isn't a texture that has slots.
|
|
* So we just try to parse it, and we change slots
|
|
*/
|
|
if (parseTexName(texNameNoExt.c_str(), &texNameBuild, &srcSlotIDs))
|
|
{
|
|
// ok, texture has slots
|
|
for(uint m = 0; m < srcSlotIDs.size(); ++m)
|
|
{
|
|
uint n;
|
|
// search if there's a matching slot in the list
|
|
for(n = 0; n < numValues; ++n)
|
|
{
|
|
if (srcSlotIDs[m].first == slotIDs[n].first)
|
|
{
|
|
if (slotIDs[n].second < _Slots[slotIDs[n].first].size())
|
|
{
|
|
texNameBuild += _Separator + _Slots[slotIDs[n].first][slotIDs[n].second];
|
|
}
|
|
else
|
|
{
|
|
nlwarning("Color slot : out of range value found");
|
|
// Use previous extension
|
|
texNameBuild += _Separator + _Slots[srcSlotIDs[m].first][srcSlotIDs[m].second];
|
|
everythingOk = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (n == numValues) // not found
|
|
{
|
|
// Use previous extension
|
|
texNameBuild += _Separator + _Slots[srcSlotIDs[m].first][srcSlotIDs[m].second];
|
|
}
|
|
}
|
|
texName = texNameBuild += "." + texExt;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// this texture has no slots
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//=======================================================================
|
|
bool CColorSlotManager::setInstanceSlot(NL3D::UInstance instance,TIntCouple *slotIDs, uint numValues) const
|
|
{
|
|
H_AUTO_USE(RZ_ColorSlotManager)
|
|
nlassert(!instance.empty());
|
|
bool everythingOk = true;
|
|
bool everythingOkForCurrent;
|
|
uint numMat = instance.getNumMaterials();
|
|
static std::string texName;
|
|
for(uint k = 0; k < numMat; ++k)
|
|
{
|
|
NL3D::UInstanceMaterial mat = instance.getMaterial(k);
|
|
sint numTex = mat.getLastTextureStage();
|
|
for(sint l = 0; l <= numTex; ++l)
|
|
{
|
|
if (mat.isTextureFile(l)) // one texture from a file ?
|
|
{
|
|
// if the texture hasn't been setup yet, we should find it in the map
|
|
texName = mat.getTextureFileName(l);
|
|
if (changeTexName(texName, slotIDs, numValues, everythingOkForCurrent))
|
|
{
|
|
mat.setTextureFileName(texName, l);
|
|
}
|
|
if (!everythingOkForCurrent)
|
|
{
|
|
everythingOk = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return everythingOk;
|
|
}
|
|
|
|
|
|
//=======================================================================
|
|
bool CColorSlotManager::setInstanceSlot(NL3D::UInstance instance, uint slotID, uint value) const
|
|
{
|
|
H_AUTO_USE(RZ_ColorSlotManager)
|
|
TIntCouple valuePair(slotID, value);
|
|
return setInstanceSlot(instance, &valuePair, 1);
|
|
}
|