khanat-opennel-code/code/nel/tools/3d/lightmap_optimizer/main.cpp

1012 lines
29 KiB
C++
Raw Normal View History

2014-12-22 13:21:54 +00:00
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// 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/>.
// lightmap_optimizer
// ------------------
// the goal is to regroup lightmap of a level into lightmap with a higher level
#include "nel/misc/common.h"
#include "nel/misc/file.h"
#include "nel/misc/bitmap.h"
#include "nel/misc/log.h"
#include "nel/misc/path.h"
#include "nel/3d/mesh_base.h"
#include "nel/3d/mesh.h"
#include "nel/3d/mesh_mrm.h"
#include "nel/3d/mesh_multi_lod.h"
#include "nel/3d/vertex_buffer.h"
#include "nel/3d/texture_file.h"
#include "nel/3d/register_3d.h"
#ifdef NL_OS_WINDOWS
# include <windows.h>
#else
# define strnicmp NLMISC::strnicmp
# include <dirent.h> /* for directories functions */
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h> /* getcwd, chdir -- replacement for getCurDiretory & setCurDirectory on windows */
#endif
#include <vector>
#include <string>
// ---------------------------------------------------------------------------
using namespace std;
using namespace NL3D;
void outString (const string &sText) ;
// ---------------------------------------------------------------------------
#ifdef NL_OS_WINDOWS // win32 code
void GetCWD (int length,char *dir)
{
GetCurrentDirectoryA (length, dir);
}
bool ChDir(const char *path)
{
return SetCurrentDirectoryA (path);
}
void dir (const std::string &sFilter, std::vector<std::string> &sAllFiles, bool bFullPath)
{
WIN32_FIND_DATA findData;
HANDLE hFind;
char sCurDir[MAX_PATH];
sAllFiles.clear ();
GetCurrentDirectory (MAX_PATH, sCurDir);
std::string sFilterAsx = std::string("*") + sFilter;
hFind = FindFirstFile (sFilterAsx.c_str(), &findData);
while (hFind != INVALID_HANDLE_VALUE)
{
DWORD res = GetFileAttributes(findData.cFileName);
if (res != INVALID_FILE_ATTRIBUTES && !(res&FILE_ATTRIBUTE_DIRECTORY))
{
if (bFullPath)
sAllFiles.push_back(string(sCurDir) + "\\" + findData.cFileName);
else
sAllFiles.push_back(findData.cFileName);
}
if (FindNextFile (hFind, &findData) == 0)
break;
}
FindClose (hFind);
}
#else // posix version of the void dir(...) function.
void GetCWD (int length, char* directory)
{
getcwd (directory,length);
}
bool ChDir(const char *path)
{
return ( chdir (path) == 0 ? true : false );
}
void dir (const string &sFilter, vector<string> &sAllFiles, bool bFullPath)
{
char sCurDir[MAX_PATH];
DIR* dp = NULL;
struct dirent *dirp= NULL;
GetCWD ( MAX_PATH,sCurDir ) ;
sAllFiles.clear ();
if ( (dp = opendir( sCurDir )) == NULL)
{
string sTmp = string("ERROR : Can't open the dir : \"")+string(sCurDir)+string("\"") ;
outString ( sTmp ) ;
return ;
}
while ( (dirp = readdir(dp)) != NULL)
{
std:string sFileName = std::string(dirp->d_name) ;
if (sFileName.substr((sFileName.length()-sFilter.length()),sFilter.length()).find(sFilter)!= std::string::npos )
{
if (bFullPath)
sAllFiles.push_back(string(sCurDir) + "/" + sFileName);
else
sAllFiles.push_back(sFileName);
}
}
closedir(dp);
}
bool DeleteFile(const char* filename){
if ( int res = unlink (filename) == -1 )
return false;
return true;
}
#endif
// ---------------------------------------------------------------------------
char sExeDir[MAX_PATH];
void outString (const string &sText)
{
char sCurDir[MAX_PATH];
GetCWD (MAX_PATH, sCurDir);
ChDir (sExeDir);
NLMISC::createDebug ();
NLMISC::InfoLog->displayRaw(sText.c_str());
ChDir (sCurDir);
}
// ---------------------------------------------------------------------------
bool fileExist (const std::string &sFileName)
{
return NLMISC::CFile::isExists(sFileName) ;
}
// -----------------------------------------------------------------------------------------------
// Try all position to put pSrc in pDst
bool tryAllPos (NLMISC::CBitmap *pSrc, NLMISC::CBitmap *pDst, sint32 &x, sint32 &y)
{
uint32 i, j;
NLMISC::CObjectVector<uint8> &rSrcPix = pSrc->getPixels();
NLMISC::CObjectVector<uint8> &rDstPix = pDst->getPixels();
// Recalculate real size of the source (without padding to power of 2)
uint32 nSrcWidth = 0, nSrcHeight = 0;
for (j = 0; j < pSrc->getHeight(); ++j)
for (i = 0; i < pSrc->getWidth(); ++i)
{
if (rSrcPix[4*(i+j*pSrc->getWidth())+3] != 0)
{
if ((i+1) > nSrcWidth)
nSrcWidth = i+1;
if ((j+1) > nSrcHeight)
nSrcHeight= j+1;
}
}
if (nSrcWidth > pDst->getWidth() ) return false;
if (nSrcHeight > pDst->getHeight() ) return false;
// For all position test if the Src plane can be put in
for (j = 0; j < (pDst->getHeight() - nSrcHeight); ++j)
for (i = 0; i < (pDst->getWidth() - nSrcWidth); ++i)
{
x = i; y = j;
uint32 a, b;
bool bCanPut = true;
for (b = 0; b < nSrcHeight; ++b)
{
for (a = 0; a < nSrcWidth; ++a)
{
if (rSrcPix[4*(a+b*pSrc->getWidth())+3] != 0)
{
if (rDstPix[4*((x+a)+(y+b)*pDst->getWidth())+3] != 0 )
{
bCanPut = false;
break;
}
}
}
if (bCanPut == false)
break;
}
if (bCanPut)
return true;
}
return false;
}
// -----------------------------------------------------------------------------------------------
bool putIn (NLMISC::CBitmap *pSrc, NLMISC::CBitmap *pDst, sint32 x, sint32 y)
{
uint32 a, b;
NLMISC::CObjectVector<uint8> &rSrcPix = pSrc->getPixels();
NLMISC::CObjectVector<uint8> &rDstPix = pDst->getPixels();
for (b = 0; b < pSrc->getHeight(); ++b)
for (a = 0; a < pSrc->getWidth(); ++a)
if (rSrcPix[4*(a+b*pSrc->getWidth())+3] != 0)
{
if (rDstPix[4*((x+a)+(y+b)*pDst->getWidth())+3] != 0)
return false;
rDstPix[4*((x+a)+(y+b)*pDst->getWidth())+0] = rSrcPix[4*(a+b*pSrc->getWidth())+0];
rDstPix[4*((x+a)+(y+b)*pDst->getWidth())+1] = rSrcPix[4*(a+b*pSrc->getWidth())+1];
rDstPix[4*((x+a)+(y+b)*pDst->getWidth())+2] = rSrcPix[4*(a+b*pSrc->getWidth())+2];
rDstPix[4*((x+a)+(y+b)*pDst->getWidth())+3] = rSrcPix[4*(a+b*pSrc->getWidth())+3];
}
return true;
}
// ---------------------------------------------------------------------------
string getBaseName (const string &fullname)
{
string sTmp2;
string::size_type pos = fullname.rfind('_');
if (pos != string::npos)
sTmp2 = fullname.substr(0, pos+1);
return sTmp2;
}
// ---------------------------------------------------------------------------
uint8 getLayerNb (const string &fullname)
{
uint8 nRet = 0;
string::size_type beg = fullname.rfind('_');
string::size_type end = fullname.rfind('.');
if (beg != string::npos)
{
string sTmp2 = fullname.substr(beg+1, end-beg-1);
NLMISC::fromString(sTmp2, nRet);
}
return nRet;
}
// Flag all vertices linked to face with material m
// ---------------------------------------------------------------------------
void FlagVertices (CMeshGeom &mg, uint InMatID, vector<bool> &verticesNeedRemap)
{
CVertexBuffer &vertexBuffer = const_cast<CVertexBuffer&>(mg.getVertexBuffer());
// For each matrix block
uint matrixBlock;
uint nbMatrixBlock=mg.getNbMatrixBlock();
for (matrixBlock=0; matrixBlock<nbMatrixBlock; matrixBlock++)
{
// For each render pass
uint renderPass;
uint numRenderPass=mg.getNbRdrPass(matrixBlock);
for (renderPass=0; renderPass<numRenderPass; renderPass++)
{
// Render pass material
uint32 matId=mg.getRdrPassMaterial(matrixBlock, renderPass);
if (matId == InMatID) // Same Material -> Flag all vertices of this pass
{
// Get primitives
const CIndexBuffer &primitiveBlock=mg.getRdrPassPrimitiveBlock(matrixBlock,renderPass);
CIndexBufferRead iba;
primitiveBlock.lock (iba);
// Set of vertex to remap
std::set<uint> vertexToRemap;
// Remap triangles
uint index;
if (iba.getFormat() == CIndexBuffer::Indices32)
{
const uint32 *indexPtr=(const uint32 *)iba.getPtr();
uint32 numIndex=primitiveBlock.getNumIndexes();
for (index=0; index<numIndex; index++)
vertexToRemap.insert (indexPtr[index]);
}
else
{
const uint16 *indexPtr=(const uint16 *)iba.getPtr();
uint32 numIndex=primitiveBlock.getNumIndexes();
for (index=0; index<numIndex; index++)
vertexToRemap.insert (indexPtr[index]);
}
// Remap the vertex
std::set<uint>::iterator iteRemap=vertexToRemap.begin();
while (iteRemap!=vertexToRemap.end())
{
// Remap the vertex
verticesNeedRemap[*iteRemap] = true;
// Next vertex
iteRemap++;
}
}
}
}
}
void FlagVerticesMRM (CMeshMRMGeom &mg, uint InMatID, vector<bool> &verticesNeedRemap)
{
CVertexBuffer &vertexBuffer = const_cast<CVertexBuffer&>(mg.getVertexBuffer());
// For each matrix block
uint matrixBlock;
uint nbMatrixBlock=1;//mg.getNbMatrixBlock(); // ASK YOYO
for (matrixBlock=0; matrixBlock<nbMatrixBlock; matrixBlock++)
{
// For each render pass
uint renderPass;
uint numRenderPass=mg.getNbRdrPass(matrixBlock);
for (renderPass=0; renderPass<numRenderPass; renderPass++)
{
// Render pass material
uint32 matId=mg.getRdrPassMaterial(matrixBlock, renderPass);
if (matId == InMatID) // Same Material -> Flag all vertices of this pass
{
// Get primitives
const CIndexBuffer &primitiveBlock=mg.getRdrPassPrimitiveBlock(matrixBlock,renderPass);
CIndexBufferRead iba;
primitiveBlock.lock (iba);
// Set of vertex to remap
std::set<uint> vertexToRemap;
// Remap triangles
uint index;
if (iba.getFormat() == CIndexBuffer::Indices32)
{
const uint32 *indexPtr=(const uint32 *)iba.getPtr();
uint32 numIndex=primitiveBlock.getNumIndexes();
for (index=0; index<numIndex; index++)
vertexToRemap.insert (indexPtr[index]);
}
else
{
const uint16 *indexPtr=(const uint16 *)iba.getPtr();
uint32 numIndex=primitiveBlock.getNumIndexes();
for (index=0; index<numIndex; index++)
vertexToRemap.insert (indexPtr[index]);
}
// Remap the vertex
std::set<uint>::iterator iteRemap=vertexToRemap.begin();
while (iteRemap!=vertexToRemap.end())
{
// Remap the vertex
verticesNeedRemap[*iteRemap] = true;
// Next vertex
iteRemap++;
}
}
}
}
}
// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------
int main(int nNbArg, char **ppArgs)
{
if (nNbArg <3 || nNbArg >5)
{
outString ("ERROR : Wrong number of arguments\n");
outString ("USAGE : lightmap_optimizer <path_lightmaps> <path_shapes> [path_tags] [path_flag8bit]\n");
return -1;
}
vector<string> AllShapeNames;
vector<CMeshBase*> AllShapes;
std::vector<std::string> tags;
char sLMPDir[MAX_PATH];
char sSHPDir[MAX_PATH];
GetCWD (MAX_PATH, sExeDir);
// Get absolute directory for lightmaps
if (!ChDir(ppArgs[1]))
{
outString (string("ERROR : directory ") + ppArgs[1] + " do not exists or access is denied\n");
return -1;
}
GetCWD (MAX_PATH, sLMPDir);
ChDir (sExeDir);
// Get absolute directory for shapes
if (!ChDir(ppArgs[2]))
{
outString (string("ERROR : directory ") + ppArgs[2] + " do not exists or access is denied\n");
return -1;
}
GetCWD (MAX_PATH, sSHPDir);
dir (".shape", AllShapeNames, false);
registerSerial3d ();
for (uint32 nShp = 0; nShp < AllShapeNames.size(); ++nShp)
{
try
{
CShapeStream mesh;
NLMISC::CIFile meshfile (AllShapeNames[nShp]);
meshfile.serial( mesh );
meshfile.close();
// Add the shape to the map.
CMeshBase *pMB = dynamic_cast<CMeshBase*>(mesh.getShapePointer());
AllShapes.push_back (pMB);
}
catch (const NLMISC::EPathNotFound &e)
{
outString(string("ERROR: shape not found ")+AllShapeNames[nShp]+" - "+e.what());
return -1;
}
}
if (nNbArg > 3 && ppArgs[3] && strlen(ppArgs[3]) > 0)
{
ChDir (sExeDir);
if (!ChDir(ppArgs[3]))
{
outString (string("ERROR : directory ") + ppArgs[3] + " do not exists\n");
return -1;
}
dir (".tag", tags, false);
for(uint k = 0; k < tags.size(); ++k)
{
std::string::size_type pos = tags[k].find('.');
if (pos != std::string::npos)
{
tags[k] = tags[k].substr(0, pos);
}
}
}
// **** Parse all mesh loaded, to flag each lightmap if 8 bit or not (NB: all layers should be same mode)
std::set<string> setLM8Bit;
for(uint i=0;i<AllShapes.size();i++)
{
CMeshBase *pMB= AllShapes[i];
if(!pMB)
continue;
uint32 nbMat= pMB->getNbMaterial();
for (uint32 m = 0; m < nbMat; ++m)
{
CMaterial& rMat = const_cast<CMaterial&>(pMB->getMaterial (m));
if (rMat.getShader() == CMaterial::LightMap)
{
// Begin with stage 0
uint8 stage = 0;
while (rMat.getLightMap(stage) != NULL)
{
ITexture *pIT = rMat.getLightMap (stage);
CTextureFile *pTF = dynamic_cast<CTextureFile*>(pIT);
if (pTF != NULL)
{
string sTexName = NLMISC::toLower(pTF->getFileName());
if(pTF->getUploadFormat()==ITexture::Luminance)
setLM8Bit.insert(sTexName);
}
++stage;
}
}
}
}
// **** Parse all lightmaps, sorted by layer, and 8 or 16 bit mode
ChDir (sExeDir);
for (uint32 lmc8bitMode = 0; lmc8bitMode < 2; ++lmc8bitMode)
for (uint32 nNbLayer = 0; nNbLayer < 256; ++nNbLayer)
{
// Get all lightmaps with same number of layer == nNbLayer
// merge lightmaps only if they are in same mode (8bits or 16 bits)
vector<string> AllLightmapNames;
vector<sint> AllLightmapTags;
vector<NLMISC::CBitmap*> AllLightmaps;
sint32 i, j, k, m, n;
string sFilter;
// **** Get All Lightmaps that have this number of layer, and this mode
sFilter = "_" + NLMISC::toString(nNbLayer) + ".tga";
ChDir (sLMPDir);
dir (sFilter, AllLightmapNames, false);
// filter by layer
vector<string> tmpLMs;
tmpLMs.reserve(AllLightmapNames.size());
for (i = 0; i < (sint32)AllLightmapNames.size(); ++i)
{
string sTmp2 = getBaseName (AllLightmapNames[i]);
sTmp2 += NLMISC::toString(nNbLayer+1) + ".tga";
// if not More layer than expected, ok
if (!fileExist(sTmp2))
{
tmpLMs.push_back(AllLightmapNames[i]);
}
}
AllLightmapNames= tmpLMs;
// filter by 8bit or not mode.
tmpLMs.clear();
for (i = 0; i < (sint32)AllLightmapNames.size(); ++i)
{
bool lm8Bit= setLM8Bit.find( NLMISC::toLower(AllLightmapNames[i]) ) !=setLM8Bit.end();
// if same mode
if( lm8Bit == (lmc8bitMode==1) )
{
tmpLMs.push_back(AllLightmapNames[i]);
}
}
AllLightmapNames= tmpLMs;
// **** Build tag info
/*
for(uint k = 0; k < tags.size(); ++k)
{
nlinfo("tag %d = %s", (int) k, tags[k].c_str());
}
*/
AllLightmapTags.resize(AllLightmapNames.size());
for(uint k = 0; k < AllLightmapNames.size(); ++k)
{
nlinfo("k = %d", (int) k);
AllLightmapTags[k] = -1;
// search for longest tag that match
uint bestLength = 0;
for(uint l = 0; l < tags.size(); ++l)
{
if (AllLightmapNames[k].size() > tags[l].size())
{
if (tags[l].size() > bestLength)
{
std::string start = AllLightmapNames[k].substr(0, tags[l].size());
if (NLMISC::nlstricmp(start, tags[l]) == 0)
{
bestLength = (uint)tags[l].size();
// the tag matchs
AllLightmapTags[k] = l;
}
}
}
}
if (AllLightmapTags[k] == -1)
{
nlinfo(NLMISC::toString("Lightmap %s has no tag", AllLightmapNames[k].c_str()).c_str());
}
else
{
nlinfo(NLMISC::toString("Lightmap %s has tag %d : %s", AllLightmapNames[k].c_str(), (int) AllLightmapTags[k], tags[AllLightmapTags[k]].c_str()).c_str());
}
}
// Check if all layer of the same lightmap has the same size
if (nNbLayer > 0)
for (i = 0; i < (sint32)AllLightmapNames.size(); ++i)
{
string sTmp2;
sTmp2 = getBaseName (AllLightmapNames[i]) + "0.tga";
uint32 wRef, hRef;
try
{
NLMISC::CIFile inFile;
inFile.open(sTmp2);
CBitmap::loadSize(inFile, wRef, hRef);
}
catch (const NLMISC::Exception &e)
{
outString (string("ERROR :") + e.what());
return -1;
}
bool bFound = false;
for (k = 1; k <= (sint32)nNbLayer; ++k)
{
string sTmp3 = getBaseName (AllLightmapNames[i]) + NLMISC::toString(k) + ".tga";
uint32 wCur = wRef, hCur = hRef;
try
{
NLMISC::CIFile inFile;
inFile.open(sTmp3);
CBitmap::loadSize(inFile, wCur, hCur);
}
catch (const NLMISC::Exception &)
{
}
if ((wCur != wRef) || (hCur != hRef))
{
bFound = true;
break;
}
}
// Should delete all layers of this lightmap (in fact in lightmapnames list we have
// only the name of the current layer)
if (bFound)
{
sTmp2 = getBaseName (AllLightmapNames[i]);
outString(string("ERROR: lightmaps ")+sTmp2+"*.tga not all the same size\n");
for (k = 0; k < (sint32)AllLightmapNames.size(); ++k)
{
if (strnicmp(AllLightmapNames[k].c_str(), sTmp2.c_str(), sTmp2.size()) == 0)
{
for (j = k+1; j < (sint32)AllLightmapNames.size(); ++j)
{
AllLightmapNames[j-1] = AllLightmapNames[j];
AllLightmapTags[j - 1] = AllLightmapTags[j];
}
AllLightmapNames.resize (AllLightmapNames.size()-1);
AllLightmapTags.resize(AllLightmapTags.size() - 1);
k = -1;
i = -1;
}
}
}
}
if (AllLightmapNames.size() == 0)
continue;
// Load all the lightmaps
AllLightmaps.resize (AllLightmapNames.size());
for (i = 0; i < (sint32)AllLightmaps.size(); ++i)
{
try
{
NLMISC::CBitmap *pBtmp = new NLMISC::CBitmap;
NLMISC::CIFile inFile;
inFile.open(AllLightmapNames[i]);
pBtmp->load(inFile);
AllLightmaps[i] = pBtmp;
}
catch (const NLMISC::Exception &e)
{
outString (string("ERROR :") + e.what());
return -1;
}
}
// Sort all lightmaps by decreasing size
for (i = 0; i < (sint32)(AllLightmaps.size()-1); ++i)
for (j = i+1; j < (sint32)AllLightmaps.size(); ++j)
{
NLMISC::CBitmap *pBI = AllLightmaps[i];
NLMISC::CBitmap *pBJ = AllLightmaps[j];
if ((pBI->getWidth()*pBI->getHeight()) < (pBJ->getWidth()*pBJ->getHeight()))
{
NLMISC::CBitmap *pBTmp = AllLightmaps[i];
AllLightmaps[i] = AllLightmaps[j];
AllLightmaps[j] = pBTmp;
string sTmp = AllLightmapNames[i];
AllLightmapNames[i] = AllLightmapNames[j];
AllLightmapNames[j] = sTmp;
sint tagTmp = AllLightmapTags[i];
AllLightmapTags[i] = AllLightmapTags[j];
AllLightmapTags[j] = tagTmp;
}
}
nlassert(AllLightmapTags.size() == AllLightmapNames.size());
for (i = 0; i < (sint32)AllLightmapNames.size(); ++i)
{
outString(NLMISC::toString("%d / %d\n", (int) i, (int) AllLightmapNames.size()));
bool bAssigned = false;
for (j = 0; j < i; ++j)
{
// Tags of both textures must match. We don't want to spread lightmap chunk in bitmap whose other part aren't used by current ig lightmaps (this wastes vram for nothing)
if (AllLightmapTags[i] != AllLightmapTags[j]) continue;
// Try to place the texture i into the texture j
// This can be done only if texture was exported from the same zone. To ensure that, check
NLMISC::CBitmap *pBI = AllLightmaps[i];
NLMISC::CBitmap *pBJ = AllLightmaps[j];
sint32 x, y;
if (tryAllPos (pBI, pBJ, x, y))
{
bAssigned = true;
if (!putIn (pBI, pBJ, x, y))
{
outString (string("ERROR : cannot put reference lightmap ")+AllLightmapNames[i]+
" in "+AllLightmapNames[j]);
return -1;
}
// Put texture i into texture j for all layers of the lightmap !
for (k = 0; k <= (sint32)nNbLayer; ++k)
{
string sTexNameI = getBaseName (AllLightmapNames[i]) + NLMISC::toString(k) + ".tga";
string sTexNameJ = getBaseName (AllLightmapNames[j]) + NLMISC::toString(k) + ".tga";
NLMISC::CBitmap BitmapI;
NLMISC::CBitmap BitmapJ;
NLMISC::CIFile inFile;
outString (NLMISC::toString("INFO : Transfering %s (tag = %d) in %s (tag = %d)",
sTexNameI.c_str(), (int) AllLightmapTags[i],
sTexNameJ.c_str(), (int) AllLightmapTags[j]) +
" at ("+NLMISC::toString(x)+","+NLMISC::toString(y)+")\n");
try
{
inFile.open (sTexNameI);
BitmapI.load (inFile);
inFile.close ();
inFile.open (sTexNameJ);
BitmapJ.load (inFile);
inFile.close ();
}
catch (const NLMISC::Exception &e)
{
outString (string("ERROR :") + e.what());
return -1;
}
if (!putIn (&BitmapI, &BitmapJ, x, y))
{
outString (string("ERROR : cannot put lightmap ")+sTexNameI+" in "+sTexNameJ+"\n");
return -1;
}
// Delete File
DeleteFile (sTexNameI.c_str());
outString (string("INFO : Deleting file ")+sTexNameI+"\n");
// Save destination image
NLMISC::COFile outFile;
outFile.open (sTexNameJ);
BitmapJ.writeTGA (outFile, 32);
outString (string("INFO : Saving file ")+sTexNameJ+"\n");
}
// Change shapes uvs related and names to the lightmap
// ---------------------------------------------------
ChDir (sSHPDir);
for (k = 0; k < (sint32)AllShapes.size(); ++k)
{
CMeshBase *pMB = AllShapes[k];
if (!pMB)
continue;
uint nNbMat = pMB->getNbMaterial ();
vector< vector<bool> > VerticesNeedRemap;
bool bMustSave = false;
// Initialize all VerticesNeedRemap
CMesh *pMesh = dynamic_cast<CMesh*>(pMB);
CMeshMRM *pMeshMRM = dynamic_cast<CMeshMRM*>(pMB);
CMeshMultiLod *pMeshML = dynamic_cast<CMeshMultiLod*>(pMB);
if (pMesh != NULL)
{
VerticesNeedRemap.resize(1); // Only one meshgeom
vector<bool> &rVNR = VerticesNeedRemap[0];
rVNR.resize (pMesh->getMeshGeom().getVertexBuffer().getNumVertices(), false);
}
else if (pMeshMRM != NULL)
{
VerticesNeedRemap.resize(1); // Only one meshmrmgeom
vector<bool> &rVNR = VerticesNeedRemap[0];
rVNR.resize (pMeshMRM->getMeshGeom().getVertexBuffer().getNumVertices(), false);
}
else if (pMeshML != NULL)
{
sint32 nNumSlot = pMeshML->getNumSlotMesh();
VerticesNeedRemap.resize(nNumSlot);
for (m = 0; m < nNumSlot; ++m)
{
vector<bool> &rVNR = VerticesNeedRemap[m];
const CMeshGeom *pMG = dynamic_cast<const CMeshGeom*>(&pMeshML->getMeshGeom(m));
if (pMG != NULL)
rVNR.resize (pMG->getVertexBuffer().getNumVertices(), false);
else
rVNR.resize(0);
}
}
else continue; // Next mesh
// All materials must have the lightmap names changed
for (m = 0; m < (sint32)nNbMat; ++m)
{
bool bMustRemapUV = false;
CMaterial& rMat = const_cast<CMaterial&>(pMB->getMaterial (m));
if (rMat.getShader() == CMaterial::LightMap)
{
// Begin with stage 0
uint8 stage = 0;
while (rMat.getLightMap(stage) != NULL)
{
ITexture *pIT = rMat.getLightMap (stage);
CTextureFile *pTF = dynamic_cast<CTextureFile*>(pIT);
if (pTF != NULL)
{
string sTexName = NLMISC::toLower(getBaseName(pTF->getFileName()));
string sTexNameMoved = NLMISC::toLower(getBaseName(AllLightmapNames[i]));
if (sTexName == sTexNameMoved)
{
// We must remap the name and indicate to remap uvs
bMustRemapUV = true;
//string sNewTexName = NLMISC::toLower(getBaseName(AllLightmapNames[j]));
//sNewTexName += NLMISC::toString(getLayerNb(pTF->getFileName())) + ".tga";
//pTF->setFileName (sNewTexName);
}
}
++stage;
}
}
// We have to remap the uvs of this mesh for this material
if (bMustRemapUV) // Flaggage of the vertices to remap
{
if (pMesh != NULL)
{
// Flag all vertices linked to face with material m
FlagVertices (const_cast<CMeshGeom&>(pMesh->getMeshGeom()), m, VerticesNeedRemap[0]);
}
else if (pMeshMRM != NULL)
{
FlagVerticesMRM (const_cast<CMeshMRMGeom&>(pMeshMRM->getMeshGeom()), m, VerticesNeedRemap[0]);
}
else if (pMeshML != NULL)
{
sint32 nNumSlot = pMeshML->getNumSlotMesh();
for (n = 0; n < nNumSlot; ++n)
{
// Get the mesh geom
CMeshGeom *pMG = const_cast<CMeshGeom*>(dynamic_cast<const CMeshGeom*>(&pMeshML->getMeshGeom(n)));
if (pMG)
{
// Flag the vertices
FlagVertices (*pMG, m, VerticesNeedRemap[n]);
}
else
{
// Get the mesh MRM geom
CMeshMRMGeom *pMMRMG = const_cast<CMeshMRMGeom*>(dynamic_cast<const CMeshMRMGeom*>(&pMeshML->getMeshGeom(n)));
if (pMMRMG)
{
// Flag the vertices
FlagVerticesMRM (*pMMRMG, m, VerticesNeedRemap[n]);
}
}
}
}
}
}
// Change lightmap names
for (m = 0; m < (sint32)nNbMat; ++m)
{
CMaterial& rMat = const_cast<CMaterial&>(pMB->getMaterial (m));
if (rMat.getShader() == CMaterial::LightMap)
{
// Begin with stage 0
uint8 stage = 0;
while (rMat.getLightMap(stage) != NULL)
{
ITexture *pIT = rMat.getLightMap (stage);
CTextureFile *pTF = dynamic_cast<CTextureFile*>(pIT);
if (pTF != NULL)
{
string sTexName = NLMISC::toLower(getBaseName(pTF->getFileName()));
string sTexNameMoved = NLMISC::toLower(getBaseName(AllLightmapNames[i]));
if (sTexName == sTexNameMoved)
{
string sNewTexName = NLMISC::toLower(getBaseName(AllLightmapNames[j]));
sNewTexName += NLMISC::toString(getLayerNb(pTF->getFileName())) + ".tga";
pTF->setFileName (sNewTexName);
}
}
++stage;
}
}
}
// We have now the list of vertices to remap for all material that have been changed
// So parse this list and apply the transformation : (uv * TexSizeI + decalXY) / TexSizeJ
for (m = 0; m < (sint32)VerticesNeedRemap.size(); ++m)
{
CVertexBuffer *pVB;
if (pMesh != NULL)
{
pVB = const_cast<CVertexBuffer*>(&pMesh->getMeshGeom().getVertexBuffer());
}
else if (pMeshMRM != NULL)
{
pVB = const_cast<CVertexBuffer*>(&pMeshMRM->getMeshGeom().getVertexBuffer());
}
else if (pMeshML != NULL)
{
const CMeshGeom *pMG = dynamic_cast<const CMeshGeom*>(&pMeshML->getMeshGeom(m));
pVB = const_cast<CVertexBuffer*>(&pMG->getVertexBuffer());
}
CVertexBufferReadWrite vba;
pVB->lock (vba);
vector<bool> &rVNR = VerticesNeedRemap[m];
for (n = 0; n < (sint32)rVNR.size(); ++n)
if (rVNR[n])
{
CUV *pUV = (CUV*)vba.getTexCoordPointer (n,1);
pUV->U = (pUV->U*pBI->getWidth() + x) / pBJ->getWidth();
pUV->V = (pUV->V*pBI->getHeight() + y) / pBJ->getHeight();
bMustSave = true;
}
}
if (bMustSave)
{
try
{
if (AllShapes[k])
{
CShapeStream mesh;
mesh.setShapePointer (AllShapes[k]);
NLMISC::COFile meshfile (AllShapeNames[k]);
meshfile.serial (mesh);
meshfile.close ();
}
}
catch (const NLMISC::EPathNotFound &e)
{
outString(string("ERROR: cannot save shape ")+AllShapeNames[k]+" - "+e.what());
return -1;
}
}
}
ChDir (sLMPDir);
// Get out of the j loop
break;
}
}
// if assigned to another bitmap -> delete the bitmap i
if (bAssigned)
{
// Delete Names && tags
for (j = i+1; j < (sint32)AllLightmapNames.size(); ++j)
{
AllLightmapNames[j-1] = AllLightmapNames[j];
AllLightmapTags[j-1] = AllLightmapTags[j];
}
AllLightmapNames.resize (AllLightmapNames.size()-1);
AllLightmapTags.resize (AllLightmapTags.size()-1);
// Delete Lightmaps
delete AllLightmaps[i];
for (j = i+1; j < (sint32)AllLightmaps.size(); ++j)
AllLightmaps[j-1] = AllLightmaps[j];
AllLightmaps.resize (AllLightmaps.size()-1);
i = i - 1;
}
}
}
// **** Additionally, output or clear a "flag file" in a dir to info if a 8bit lihgtmap or not
if (nNbArg >=5 && ppArgs[4] && strlen(ppArgs[4]) > 0)
{
ChDir (sExeDir);
// out a text file, with list of
FILE *out= fopen(ppArgs[4], "wt");
if(!out)
{
outString(string("ERROR: cannot save ")+ppArgs[4]);
}
set<string>::iterator it(setLM8Bit.begin()), end(setLM8Bit.end());
for(;it!=end;it++)
{
string temp= (*it);
temp+= "\n";
fputs(temp.c_str(), out);
}
fclose(out);
}
return 0;
}