2013-10-23 17:08:09 +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"
# include "windows.h"
# include <vector>
# include <string>
// ---------------------------------------------------------------------------
using namespace std ;
using namespace NL3D ;
// ---------------------------------------------------------------------------
char sExeDir [ MAX_PATH ] ;
void outString ( const string & sText )
{
char sCurDir [ MAX_PATH ] ;
GetCurrentDirectory ( MAX_PATH , sCurDir ) ;
SetCurrentDirectory ( sExeDir ) ;
NLMISC : : createDebug ( ) ;
NLMISC : : InfoLog - > displayRaw ( sText . c_str ( ) ) ;
SetCurrentDirectory ( sCurDir ) ;
}
// ---------------------------------------------------------------------------
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 ) ;
hFind = FindFirstFile ( sFilter . 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 ) ;
}
// ---------------------------------------------------------------------------
bool fileExist ( const std : : string & sFileName )
{
HANDLE hFile = CreateFile ( sFileName . c_str ( ) , GENERIC_READ , FILE_SHARE_READ | FILE_SHARE_WRITE , NULL ,
OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL ) ;
if ( hFile = = INVALID_HANDLE_VALUE )
return false ;
CloseHandle ( hFile ) ;
return true ;
}
// -----------------------------------------------------------------------------------------------
// 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 ] ;
GetCurrentDirectory ( MAX_PATH , sExeDir ) ;
// Get absolute directory for lightmaps
if ( ! SetCurrentDirectory ( ppArgs [ 1 ] ) )
{
outString ( string ( " ERROR : directory " ) + ppArgs [ 1 ] + " do not exists \n " ) ;
return - 1 ;
}
GetCurrentDirectory ( MAX_PATH , sLMPDir ) ;
SetCurrentDirectory ( sExeDir ) ;
// Get absolute directory for shapes
if ( ! SetCurrentDirectory ( ppArgs [ 2 ] ) )
{
outString ( string ( " ERROR : directory " ) + ppArgs [ 2 ] + " do not exists \n " ) ;
return - 1 ;
}
GetCurrentDirectory ( 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 )
{
SetCurrentDirectory ( sExeDir ) ;
if ( ! SetCurrentDirectory ( 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
SetCurrentDirectory ( 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 " ;
SetCurrentDirectory ( 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
// ---------------------------------------------------
SetCurrentDirectory ( 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 ;
}
}
}
SetCurrentDirectory ( 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 )
{
SetCurrentDirectory ( 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 ;
}