// ***************************************************************************
void	CIgLighterLib::lightIg(CInstanceLighter &instanceLighter,
	const CInstanceGroup &igIn, CInstanceGroup &igOut, CInstanceLighter::CLightDesc &lightDesc, 
	CSurfaceLightingInfo &slInfo, const char *igName)
{
	sint				i;


	// Setup.
	//=======
	// Init
	instanceLighter.init();

	// For interiors ig, disable Sun contrib according to ig.
	lightDesc.DisableSunContribution= !igIn.getRealTimeSunContribution();
	// Copy it to igOut, just to keep same setup data for in and out.
	igOut.enableRealTimeSunContribution(!lightDesc.DisableSunContribution);


	// Add obstacles.
	std::vector<CInstanceLighter::CTriangle>	obstacles;
	// only if Shadowing On.
	if(lightDesc.Shadow)
	{
		// Map of shape to load
		std::map<string, IShape*> shapeMap;

		// For all instances of igIn.
		for(i=0; i<(sint)igIn.getNumInstance();i++)
		{
			// progress
			instanceLighter.progress("Loading Shapes obstacles", float(i)/igIn.getNumInstance());

			// Skip it?? IgLighterLib use the DontCastShadowForInterior flag. See doc of this flag
			if(igIn.getInstance(i).DontCastShadow || igIn.getInstance(i).DontCastShadowForInterior)
				continue;

			// Get the instance shape name
			string name= igIn.getShapeName(i);
			bool	shapeFound= true;

			// Try to find the shape in the UseShapeMap.
			std::map<string, IShape*>::const_iterator iteMap= lightDesc.UserShapeMap.find (name);

			// If not found in userShape map, try to load it from the temp loaded ShapeBank.
			if( iteMap == lightDesc.UserShapeMap.end() )
			{
				// Add a .shape at the end ?
				if (name.find('.') == std::string::npos)
					name += ".shape";

				// Lookup the file
				string nameLookup = CPath::lookup (name, false, false);
				if (!nameLookup.empty())
					name = nameLookup;

				// Find the shape in the bank
				iteMap= shapeMap.find (name);
				if (iteMap==shapeMap.end())
				{
					// Input file
					CIFile inputFile;

					if (inputFile.open (name))
					{
						// Load it
						CShapeStream stream;
						stream.serial (inputFile);

						// Get the pointer
						iteMap=shapeMap.insert (std::map<string, IShape*>::value_type (name, stream.getShapePointer ())).first;
					}
					else
					{
						// Error
						nlwarning ("WARNING can't load shape %s\n", name.c_str());
						shapeFound= false;
					}
				}
			}

			if(shapeFound)
			{
				CMatrix		matInst;
				matInst.setPos(igIn.getInstancePos(i));
				matInst.setRot(igIn.getInstanceRot(i));
				matInst.scale(igIn.getInstanceScale(i));
				// Add triangles of this shape
				CInstanceLighter::addTriangles(*iteMap->second, matInst, obstacles, i);
			}

		}

		// Clean Up shapes.
		//-----------
		std::map<string, IShape*>::iterator iteMap;
		iteMap= shapeMap.begin();
		while(iteMap!= shapeMap.end())
		{
			// delte shape
			delete	iteMap->second;
			// delete entry in map
			shapeMap.erase(iteMap);
			// next
			iteMap= shapeMap.begin();
		}
	}

	// Add pointLights of the IG.
	for(i=0; i<(sint)igIn.getPointLightList().size();i++)
	{
		instanceLighter.addStaticPointLight( igIn.getPointLightList()[i], igName );
	}


	// Setup a CIGSurfaceLightBuild if needed.
	//=======
	CIGSurfaceLightBuild	*igSurfaceLightBuild= NULL;
	CGlobalRetriever		*globalRetriever= slInfo.GlobalRetriever;
	CRetrieverBank			*retrieverBank= slInfo.RetrieverBank;
	float	cellSurfaceLightSize= slInfo.CellSurfaceLightSize;
	if(retrieverBank && globalRetriever)
	{
		igSurfaceLightBuild= new CIGSurfaceLightBuild;
		igSurfaceLightBuild->CellSize= cellSurfaceLightSize;
		// col Identifier.
		string	colIdent= slInfo.ColIdentifierPrefix + slInfo.IgFileName + slInfo.ColIdentifierSuffix;

		// For any retreiverInstance with this identifier.
		//----------------
		uint	numInstances= (uint)globalRetriever->getInstances().size();
		for(uint instanceId=0; instanceId<numInstances; instanceId++)
		{
			const CRetrieverInstance	&instance= globalRetriever->getInstance(instanceId);
			// If this instance is an interior
			if ( instance.getType() == CLocalRetriever::Interior )
			{
				uint					localRetrieverId= instance.getRetrieverId();
				const CLocalRetriever	&localRetriever= retrieverBank->getRetriever(localRetrieverId);
				// get the identifer of this localRetriever
				string	retIdent= localRetriever.getIdentifier();

				// Match the ident??
				if( retIdent.find(colIdent)!=string::npos )
				{
					// check CRetrieverLightGrid not already present
					CIGSurfaceLightBuild::ItRetrieverGridMap	itRgm;
					itRgm= igSurfaceLightBuild->RetrieverGridMap.find(localRetrieverId);
					if( itRgm != igSurfaceLightBuild->RetrieverGridMap.end() )
					{
						nlwarning ("ERROR Found 2 different collision retriever with same identifier: '%s'. The 2nd is discared\n", retIdent.c_str());
					}
					else
					{
						// Append CRetrieverLightGrid.
						itRgm= igSurfaceLightBuild->RetrieverGridMap.insert(
							make_pair(localRetrieverId, CIGSurfaceLightBuild::CRetrieverLightGrid() ) ).first;
						CIGSurfaceLightBuild::CRetrieverLightGrid	&rlg= itRgm->second;

						// Resize Grids.
						uint	numSurfaces= (uint)localRetriever.getSurfaces().size();
						rlg.Grids.resize( numSurfaces );

						// Compute the bbox for all surfaces. (NB: local to the localRetriever).
						vector<CAABBox>		surfaceBBoxes;
						localRetriever.buildInteriorSurfaceBBoxes(surfaceBBoxes);

						// For each surface, compute it.
						for(uint surfaceId=0; surfaceId<numSurfaces; surfaceId++)
						{
							// Progress.
							char	stmp[256];
							sprintf(stmp, "Sample surfaces of %s", retIdent.c_str());
							instanceLighter.progress(stmp, surfaceId / float(numSurfaces));

							// Compute surface and size of the grid.
							CIGSurfaceLightBuild::CSurface		&surfDst= rlg.Grids[surfaceId];

							// Snap Origin on cellSize
							surfDst.Origin= surfaceBBoxes[surfaceId].getMin();
							surfDst.Origin.x= floorf(surfDst.Origin.x/cellSurfaceLightSize) * cellSurfaceLightSize;
							surfDst.Origin.y= floorf(surfDst.Origin.y/cellSurfaceLightSize) * cellSurfaceLightSize;

							// Snap Width / Height on cellSize.
							float	sizex= surfaceBBoxes[surfaceId].getMax().x - surfDst.Origin.x;
							float	sizey= surfaceBBoxes[surfaceId].getMax().y - surfDst.Origin.y;
							surfDst.Width= (uint)floorf(sizex/cellSurfaceLightSize) + 2;
							surfDst.Height= (uint)floorf(sizey/cellSurfaceLightSize) + 2;
							// Get Zcenter.
							float	zCenter= surfaceBBoxes[surfaceId].getCenter().z;

							// Allocate elements.
							surfDst.Cells.resize(surfDst.Width * surfDst.Height);

							// For all elements
							for(sint yCell=0; yCell<(sint)surfDst.Height; yCell++)
							{
								for(sint xCell=0; xCell<(sint)surfDst.Width; xCell++)
								{
									// compute pos of the cell.
									ULocalPosition	localPos;
									localPos.Estimation.x= surfDst.Origin.x + xCell*cellSurfaceLightSize;
									localPos.Estimation.y= surfDst.Origin.y + yCell*cellSurfaceLightSize;
									localPos.Estimation.z= zCenter;

									// snap the pos to the surface.
									localPos.Surface= surfaceId;
									bool	snapped;
									localRetriever.snapToInteriorGround(localPos, snapped);

									// if snapped then this point is IN the surface.
									CIGSurfaceLightBuild::CCellCorner	&cell= 
										surfDst.Cells[yCell * surfDst.Width + xCell];
									cell.InSurface= snapped;

									// If ok, retrieve the global (ie world) position
									if(snapped)
									{
										// build a valid globalPosition.
										UGlobalPosition	globalPos;
										globalPos.InstanceId= instanceId;
										globalPos.LocalPosition= localPos;
										// retrieve from globalRetriever.
										cell.CenterPos= globalRetriever->getGlobalPosition(globalPos);
										// Add a delta to simulate entity center
										cell.CenterPos.z+= slInfo.CellRaytraceDeltaZ;

										// OverSample
										if(lightDesc.OverSampling==0)
										{
											// No OverSample, just add CenterPos to the samples.
											cell.NumOverSamples= 1;
											cell.OverSamples[0]= cell.CenterPos;
										}
										else
										{
											// OverSample.
											overSampleCell(cell, lightDesc.OverSampling, localRetriever, 
												*globalRetriever, instanceId, localPos, cellSurfaceLightSize, 
												slInfo.CellRaytraceDeltaZ);
											// it is possible that no samples lies in surfaces (small surface).
											// In this case, just copy CenterPos into samples.
											if(cell.NumOverSamples==0)
											{
												cell.NumOverSamples= 1;
												cell.OverSamples[0]= cell.CenterPos;
											}
										}
									}
									else
									{
										// For debug mesh only, get an approximate pos.
										cell.CenterPos= localPos.Estimation + instance.getOrigin();
										cell.CenterPos.z+= slInfo.CellRaytraceDeltaZ;
									}

									// Init cell defaults
									cell.Dilated= false;
									cell.SunContribution= 0;
								}
							}
						}
					}
				}
			}
		}

	}


	// Run.
	//=======
	instanceLighter.light(igIn, igOut, lightDesc, obstacles, NULL, igSurfaceLightBuild);

	// Output a debug mesh??
	if(igSurfaceLightBuild && slInfo.BuildDebugSurfaceShape && !igSurfaceLightBuild->RetrieverGridMap.empty() )
	{
		// Do it for the sun and point lights.
		for(uint i=0;i<2;i++)
		{
			// compute
			CMesh::CMeshBuild			meshBuild;
			CMeshBase::CMeshBaseBuild	meshBaseBuild;
			CVector	deltaPos= CVector::Null;
			deltaPos.z= - slInfo.CellRaytraceDeltaZ + 0.1f;
			// What kind of debug?
			if( i==0 )
				igSurfaceLightBuild->buildSunDebugMesh(meshBuild, meshBaseBuild, deltaPos);
			else
				igSurfaceLightBuild->buildPLDebugMesh(meshBuild, meshBaseBuild, deltaPos, igOut);

			// build
			CMesh	mesh;
			mesh.build(meshBaseBuild, meshBuild);

			// Save.
			CShapeStream	shapeStream;
			shapeStream.setShapePointer(&mesh);
			COFile		file;
			if( i==0 )
				file.open(slInfo.DebugSunName);
			else
				file.open(slInfo.DebugPLName);
			shapeStream.serial(file);
		}
	}


	// Clean.
	//=======
	if(igSurfaceLightBuild)
		delete igSurfaceLightBuild;
}
Exemplo n.º 2
0
// ---------------------------------------------------------------------------
// 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 (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::strlwr(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::strlwr(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 (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 (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 (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 (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::strlwr(getBaseName(pTF->getFileName()));
										string sTexNameMoved = NLMISC::strlwr(getBaseName(AllLightmapNames[i]));
										if (sTexName == sTexNameMoved)
										{
											// We must remap the name and indicate to remap uvs
											bMustRemapUV = true;
											//string sNewTexName = NLMISC::strlwr(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::strlwr(getBaseName(pTF->getFileName()));
										string sTexNameMoved = NLMISC::strlwr(getBaseName(AllLightmapNames[i]));
										if (sTexName == sTexNameMoved)
										{
											string sNewTexName = NLMISC::strlwr(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 (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;
}