Example #1
0
bool CVegetableEditor::loadVegetableSet(NL3D::CTileVegetableDesc &vegetSet, std::string fileName)
{
	bool ok = true;
	NLMISC::CIFile f;

	if( f.open(fileName) )
	{
		try
		{
			// read the vegetable
			f.serial(vegetSet);
		}
		catch(NLMISC::EStream &)
		{
			ok = false;
			QMessageBox::critical(&Modules::mainWin(), "NeL Vegetable editor", QString("Failed to load file!"), QMessageBox::Ok);
		}
	}
	else
	{
		ok = false;
		QMessageBox::critical(&Modules::mainWin(), "NeL Vegetable editor", QString("Failed to load file!"), QMessageBox::Ok);
	}

	return ok;
}
// ***************************************************************************
bool CAnimationSet::loadFromFiles(const std::string &path, bool recurse /* = true*/, const char *ext /*= "anim"*/, bool wantWarningMessage /*= true*/)
{
	bool everythingOk = true;
	std::vector<std::string> anims;
	NLMISC::CPath::getPathContent(path, recurse, false, true, anims);
	for (uint k = 0; k < anims.size(); ++k)
	{
		std::string fileExt = NLMISC::CFile::getExtension(anims[k]);
		if (fileExt == ext) // an animation file ?
		{
			try
			{
				NLMISC::CIFile	iFile;
				iFile.open(anims[k]);
				std::auto_ptr<CAnimation> anim(new CAnimation);
				anim->serial(iFile);
				addAnimation(NLMISC::CFile::getFilenameWithoutExtension(anims[k]).c_str(), anim.release());
				iFile.close();

			}
			catch (NLMISC::EStream &e)
			{
				if (wantWarningMessage)
				{
					nlinfo("Unable to load an automatic animation : %s", e.what());
				}
				everythingOk = false;
			}
		}
	}
	build();
	return everythingOk;
}
Example #3
0
void TextureChooser::onFileTxtRowChanged( int row )
{
	if( row < 0 )
		return;

	QListWidgetItem *item = d_ptr->fileTextures->item( row );
	QString fn = item->text();

	std::string rfn = fn.toUtf8().constData();
	rfn = NLMISC::CPath::lookup( rfn );
	
	NLMISC::CIFile f;
	bool b = f.open( rfn );
	if( !b )
	{
		return;
	}

	NLMISC::CBitmap bm;
	uint8 depth = bm.load( f );
	f.close();
	b = bm.convertToType( NLMISC::CBitmap::RGBA );
	if( !b )
	{
		return;
	}

	setPreviewImage( bm );
}
Example #4
0
//***********************************************************************************************
void CParticleWorkspace::load()
{
	NLMISC::CIFile stream;
	stream.open(_Filename);
	NLMISC::CIXml xmlStream;
	xmlStream.init(stream);
	this->serial(xmlStream);
	clearModifiedFlag();
}
Example #5
0
// ***************************************************************************
UAnimationSet			*CDriverUser::createAnimationSet(const std::string &animationSetFile)
{
	H_AUTO( NL3D_Load_AnimationSet )

	NLMISC::CIFile	f;
	// throw exception if not found.
	std::string	path= CPath::lookup(animationSetFile);
	f.open(path);
	return _AnimationSets.insert(new CAnimationSetUser(this, f));
}
Example #6
0
void initPrimitiveBlocks()
{
	//-----------------------------
	// load pacs landscape collisions
	//-----------------------------

	static const char primitiveListFile[] = "landscape_col_prim_pacs_list.txt";
	std::string lookupPathPrimList = CPath::lookup(primitiveListFile, false, true);
	if (lookupPathPrimList.empty())
	{
		nlwarning("Unable to find the file containing pacs primitives list : %s", primitiveListFile);
		return;
	}
	//
	char tmpBuff[300];
	NLMISC::CIFile inputFile;
	if(!inputFile.open(lookupPathPrimList, false))
	{
		nlwarning("Couldn't open %s", primitiveListFile);
		return;
	}
	//
	while(!inputFile.eof())
	{
		inputFile.getline(tmpBuff, 300);
		std::string primFile = CPath::lookup(tmpBuff, false, true);
		if (!primFile.empty())
		{
			try
			{
				addPacsPrim(primFile);
			}
			catch (const NLMISC::Exception &)
			{
				nlwarning("Error while loading %s", primFile.c_str());
			}
		}
		else
		{
			nlwarning("Couldn't find %s", tmpBuff);
		}
	}
	//
	inputFile.close();
}
Example #7
0
// ***************************************************************************
UPrimitiveBlock *UPrimitiveBlock::createPrimitiveBlockFromFile(const std::string &fileName)
{

	NLMISC::CIFile input;
	if (input.open(fileName))
	{
		NLMISC::CIXml xmlInput;
		// Init
		if (xmlInput.init (input))
		{
			return createPrimitiveBlock(xmlInput);
		}
		else
		{
			throw NLMISC::Exception(std::string("Unable to init an xml input file from ") + fileName);
		}
	}
	return NULL;
}
Example #8
0
UAnimation* UAnimation::createAnimation (const char* sPath)
{
	NL3D_HAUTO_UI_ANIMATION;

	// Allocate an animation
	std::auto_ptr<CAnimation> anim (new CAnimation);

	// Read it
	NLMISC::CIFile file;
	if (file.open ( NLMISC::CPath::lookup( sPath ) ) )
	{
		// Serial the animation
		file.serial (*anim);

		// Return pointer
		CAnimation *ret=anim.release ();

		// Return the animation interface
		return ret;
	}
	else
		return NULL;
}
Example #9
0
//***********************************************************************************************
bool CParticleWorkspace::CNode::loadPS()
{	
	nlassert(_WS);
	// manually load the PS shape (so that we can deal with exceptions)
	NL3D::CShapeStream ss;
	NLMISC::CIFile inputFile;
	// collapse name
	inputFile.open(getFullPath());
	ss.serial(inputFile);
	std::auto_ptr<NL3D::CShapeBank> sb(new NL3D::CShapeBank);
	std::string shapeName = NLMISC::CFile::getFilename(_RelativePath);
	sb->add(shapeName, ss.getShapePointer());
	NL3D::CShapeBank *oldSB = NL3D::CNELU::Scene->getShapeBank();
	NL3D::CNELU::Scene->setShapeBank(sb.get());
	NL3D::CTransformShape *trs = NL3D::CNELU::Scene->createInstance(shapeName);
	if (!trs)
	{
		NL3D::CNELU::Scene->setShapeBank(oldSB);
		return false;
	}
	NL3D::CParticleSystemModel *psm = dynamic_cast<NL3D::CParticleSystemModel *>(trs);
	if (!psm)
	{
		// Not a particle system
		NL3D::CNELU::Scene->deleteInstance(trs);
		return false;
	}
	NL3D::CNELU::Scene->setShapeBank(oldSB);		
	setup(*psm);
	unload();
	// commit new values
	_PS = psm->getPS();
	_PSM = psm;
	_ShapeBank = sb.release();
	_Modified = false;
	return true; 
}
Example #10
0
void CWaterPoolEditor::OnLoadPool() 
{	
	static char BASED_CODE szFilter[] = "NeL Water Pool Files (*.wpf)|*.wpf||";
	CFileDialog fileDlg( TRUE, ".wpf", "*.wpf", OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, szFilter);
	if (fileDlg.DoModal()==IDOK)
	{				
		try
		{
			NLMISC::CIXml iXml;
			NLMISC::CIFile iF;
			if (iF.open((LPCTSTR) fileDlg.GetPathName()))
			{
				if (iXml.init (iF))
				{
					_Wpm->serial(iXml);
					iXml.release();
					iF.close();
					fillPoolList();
				}
				else
				{
					iF.close();
					MessageBox (("Unable to init xml stream from file : " + std::string((LPCTSTR) fileDlg.GetPathName())).c_str(), "NeL object viewer", MB_OK|MB_ICONEXCLAMATION);	
				}
			}
			else
			{
				MessageBox (("Unable to open file : " + std::string((LPCTSTR) fileDlg.GetPathName())).c_str(), "NeL object viewer", MB_OK|MB_ICONEXCLAMATION);
			}
		}
		catch (NLMISC::Exception& e)
		{
			MessageBox (e.what(), "NeL object viewer", MB_OK|MB_ICONEXCLAMATION);
		}
	}	
}
Example #11
0
// ***************************************************************************
int main(int argc, char **argv)
{
	uint8 algo;

	// Parse Command Line.
	//====================
	if(argc<2)
	{
		writeInstructions();
		return 0;
	}
	if(!strcmp(argv[1],"/?"))
	{
		writeInstructions();
		return 0;
	}
	if(!strcmp(argv[1],"-?"))
	{
		writeInstructions();
		return 0;
	}
	if(!parseOptions(argc, argv))
	{
		writeInstructions();
		return 0;
	}


	// Reading TGA or PNG and converting to RGBA
	//====================================
	CBitmap picTga;
	CBitmap picTga2;
	CBitmap picSrc;

	
	std::string inputFileName(argv[1]);
	if(inputFileName.find("_usercolor")<inputFileName.length())
	{
		return 0;
	}
	NLMISC::CIFile input;
	if(!input.open(inputFileName))
	{
		cerr<<"Can't open input file "<<inputFileName<<endl;
		return 1;
	}
	uint8 imageDepth = picTga.load(input);
	if(imageDepth==0)
	{
		cerr<<"Can't load file : "<<inputFileName<<endl;
		return 1;
	}
	if(imageDepth!=16 && imageDepth!=24 && imageDepth!=32 && imageDepth!=8)
	{
		cerr<<"Image not supported : "<<imageDepth<<endl;
		return 1;
	}
	input.close();
	uint32 height = picTga.getHeight();
	uint32 width= picTga.getWidth();
	picTga.convertToType (CBitmap::RGBA);


	// Output file name and algo.
	//===========================
	std::string outputFileName;
	if (!OptOutputFileName.empty())
		outputFileName = OptOutputFileName;
	else
		outputFileName = getOutputFileName(inputFileName);

	// Check dest algo
	if (OptAlgo==NOT_DEFINED)
		OptAlgo = getType (outputFileName.c_str());
	
	// Choose Algo.
	if(OptAlgo!=NOT_DEFINED)
	{
		algo= OptAlgo;
	}
	else
	{
		if(imageDepth==24)
			algo = DXT1;
		else
			algo = DXT5;
	}

	// Data check
	//===========
	if(dataCheck(inputFileName.c_str(),outputFileName.c_str(), OptAlgo, OptMipMap))
	{
		cout<<outputFileName<<" : a recent dds file already exists"<<endl;
		return 0;
	}


	// Vectors for RGBA data
	CObjectVector<uint8> RGBASrc = picTga.getPixels();
	CObjectVector<uint8> RGBASrc2;
	CObjectVector<uint8> RGBADest;
	RGBADest.resize(height*width*4);
	uint	dstRGBADestId= 0;
	



	// UserColor
	//===========
	/*
	// Checking if option "usercolor" has been used
	std::string userColorFileName;
	if(argc>4)
	{
		if(strcmp("-usercolor",argv[4])==0)
		{
			if(argc!=6)
			{
				writeInstructions();
				return;
			}
			userColorFileName = argv[5];
		}
		else
		{
			writeInstructions();
			return;
		}
	}
	*/
	// Checking if associate usercolor file  exists
	std::string userColorFileName;
	std::string::size_type pos = inputFileName.rfind(".");
	if (pos == std::string::npos)
	{
		// name whithout extension
		userColorFileName = inputFileName + "_usercolor"; 
	}
	else
	{
		// append input filename extension
		userColorFileName = inputFileName.substr(0,pos) + "_usercolor" + inputFileName.substr(pos);
	}
	
	
	// Reading second Tga for user color
	NLMISC::CIFile input2;
	if (input2.open(userColorFileName))
	{
		picTga2.load(input2);
		uint32 height2 = picTga2.getHeight();
		uint32 width2 = picTga2.getWidth();
		nlassert(width2==width);
		nlassert(height2==height);
		picTga2.convertToType (CBitmap::RGBA);



		RGBASrc2 = picTga2.getPixels();

		NLMISC::CRGBA *pRGBASrc = (NLMISC::CRGBA*)&RGBASrc[0];
		NLMISC::CRGBA *pRGBASrc2 = (NLMISC::CRGBA*)&RGBASrc2[0];
	
		for(uint32 i = 0; i<width*height; i++)
		{
			// If no UserColor, must take same RGB, and keep same Alpha from src1 !!! So texture can have both alpha
			// userColor and other alpha usage.
			if(pRGBASrc2[i].A==255) 
			{
				RGBADest[dstRGBADestId++]= pRGBASrc[i].R;
				RGBADest[dstRGBADestId++]= pRGBASrc[i].G;
				RGBADest[dstRGBADestId++]= pRGBASrc[i].B;
				RGBADest[dstRGBADestId++]= pRGBASrc[i].A;
			}
			else
			{
				// Old code.
				/*uint8 F = (uint8) ((float)pRGBASrc[i].R*0.3 + (float)pRGBASrc[i].G*0.56 + (float)pRGBASrc[i].B*0.14);
				uint8 Frgb;
				if((F*pRGBASrc2[i].A/255)==255)
					Frgb = 0;
				else
					Frgb = (255-pRGBASrc2[i].A)/(255-F*pRGBASrc2[i].A/255);
				RGBADest[dstRGBADestId++]= Frgb*pRGBASrc[i].R/255;
				RGBADest[dstRGBADestId++]= Frgb*pRGBASrc[i].G/255;
				RGBADest[dstRGBADestId++]= Frgb*pRGBASrc[i].B/255;
				RGBADest[dstRGBADestId++]= F*pRGBASrc[i].A/255;*/

				// New code: use new restrictions from IDriver.
				float	Rt, Gt, Bt, At;
				float	Lt;
				float	Rtm, Gtm, Btm, Atm;

				// read 0-1 RGB pixel.
				Rt= (float)pRGBASrc[i].R/255;
				Gt= (float)pRGBASrc[i].G/255;
				Bt= (float)pRGBASrc[i].B/255;
				Lt= Rt*0.3f + Gt*0.56f + Bt*0.14f;

				// take Alpha from userColor src.
				At= (float)pRGBASrc2[i].A/255;
				Atm= 1-Lt*(1-At);

				// If normal case.
				if(Atm>0)
				{
					Rtm= Rt*At / Atm;
					Gtm= Gt*At / Atm;
					Btm= Bt*At / Atm;
				}
				// Else special case: At==0, and Lt==1.
				else
				{
					Rtm= Gtm= Btm= 0;
				}

				// copy to buffer.
				sint	r,g,b,a;
				r= (sint)floor(Rtm*255+0.5f);
				g= (sint)floor(Gtm*255+0.5f);
				b= (sint)floor(Btm*255+0.5f);
				a= (sint)floor(Atm*255+0.5f);
				clamp(r, 0,255);
				clamp(g, 0,255);
				clamp(b, 0,255);
				clamp(a, 0,255);
				RGBADest[dstRGBADestId++]= r;
				RGBADest[dstRGBADestId++]= g;
				RGBADest[dstRGBADestId++]= b;
				RGBADest[dstRGBADestId++]= a;
			}
		}
	}
	else
		RGBADest = RGBASrc;

	// Copy to the dest bitmap.
	picSrc.resize(width, height, CBitmap::RGBA);
	picSrc.getPixels(0)= RGBADest;

	// Resize the destination bitmap ?
	while (Reduce != 0)
	{
		dividSize (picSrc);
		Reduce--;
	}

	// 8 or 16 bits TGA or PNG ?
	if ((algo == TGA16) || (algo == TGA8) || (algo == PNG16) || (algo == PNG8))
	{
		// Saving TGA or PNG file
		//=================
		NLMISC::COFile output;
		if(!output.open(outputFileName))
		{
			cerr<<"Can't open output file "<<outputFileName<<endl;
			return 1;
		}
		try 
		{
			if (algo == TGA16)
			{
				picSrc.writeTGA (output, 16);
			} 
			else if (algo == TGA8)
			{
				picSrc.convertToType(CBitmap::Luminance);
				picSrc.writeTGA (output, 8);
			}
			else if (algo == PNG16)
			{
				picSrc.writePNG (output, 16);
			} 
			else if (algo == PNG8)
			{
				picSrc.convertToType(CBitmap::Luminance);
				picSrc.writePNG (output, 8);
			}
		}
		catch(NLMISC::EWriteError &e)
		{
			cerr<<e.what()<<endl;
			return 1;
		}
	
		output.close();

	}
	else
	{
		// Compress
		//===========

		// log.
		std::string algostr;
		switch(algo)
		{
			case DXT1:
				algostr = "DXTC1";
				break;
			case DXT1A:
				algostr = "DXTC1A";
				break;
			case DXT3:
				algostr = "DXTC3";
				break;
			case DXT5:
				algostr = "DXTC5";
				break;
		}
		cout<<"compressing ("<<algostr<<") "<<inputFileName<<" to "<<outputFileName<<endl;


		// Saving compressed DDS file
		// =================
		NLMISC::COFile output;
		if(!output.open(outputFileName))
		{
			cerr<<"Can't open output file "<<outputFileName<<endl;
			return 1;
		}
		try
		{
			CS3TCCompressor		comp;
			comp.compress(picSrc, OptMipMap, algo, output);
		}
		catch(NLMISC::EWriteError &e)
		{
			cerr<<e.what()<<endl;
			return 1;
		}
		
		output.close();
	}

	return 0;
}	
Example #12
0
bool CScenarioValidator::setScenarioToLoad( const std::string& filename, CScenarioValidator::TValues& values, std::string& md5,  std::string& signature, bool checkMD5)
{

    values.clear();

    _Filename = filename;
    _Values.clear();
    // open our input file
    NLMISC::CIFile inf;
    if (!inf.open(_Filename, true) )
    {
        nlwarning("Can't load scenario %s", _Filename.c_str());
        return false;
    }

    try
    {
        static const char * header = "---- Header\n";
        static const char * slashheader = "---- /Header\n\n";
        static const char * comment = "-- ";

        static const unsigned int headerLen = (unsigned int)strlen(header);
        static const unsigned int slasheaderLen = (unsigned int)strlen(slashheader);
        static const unsigned int commentLen = (unsigned int)strlen(comment);


        NLMISC::CSString tmp;
        tmp.resize( inf.getFileSize() );
        inf.serialBuffer((uint8*)&tmp[0], (uint)tmp.size());
        _ScenarioBody = tmp.replace("\r\n", "\n");





        // Scenario without header
        if (_ScenarioBody.size() < headerLen ||_ScenarioBody.substr(0, headerLen) != header )
        {
            md5 = "";
            signature = "";
            inf.close();
            return true;
        }

        std::string::size_type endHeader = _ScenarioBody.find(slashheader, headerLen);
        if (endHeader == std::string::npos ) {
            inf.close();
            return false;
        }
        std::string::size_type startHeader = headerLen;

        std::vector<std::string> lines;
        NLMISC::splitString( _ScenarioBody.substr(startHeader, endHeader - startHeader), "'\n", lines);
        std::vector<std::string>::const_iterator firstLine(lines.begin()), lastLine(lines.end());
        std::vector<std::string> result;

        for (; firstLine != lastLine ; ++firstLine)
        {
            result.clear();
            NLMISC::splitString(*firstLine, " = '", result);
            if (result.size() == 1)
            {
                result.push_back("");
            }
            if (result.size() == 2)
            {
                if (result[0].find(comment) != std::string::npos)
                {
                    //>result[1]"\\n" => "\n"
                    NLMISC::CSString tmp = result[1];
                    tmp = tmp.replace("\\n", "\n");

                    values.push_back( std::make_pair( result[0].substr(commentLen), tmp));
                }
            }
        }

        if (values.size() >=2
                && values[0].first == "Version"
                && values[1].first == "Signature"
                && values[2].first == "HeaderMD5" && values[3].first =="BodyMD5")
        {
            std::string headerBodyMd5;
            std::string::size_type subHeader = _ScenarioBody.find("-- BodyMD5", startHeader);
            if (checkMD5)
            {
                std::string md5Id1 = NLMISC::getMD5((uint8*)(_ScenarioBody.data() + subHeader), (uint32)(endHeader - subHeader)).toString();
                if (values[2].second != md5Id1 )
                {
                    return false;
                }
                std::string md5Id2 = NLMISC::getMD5((uint8*)(_ScenarioBody.data() + endHeader + slasheaderLen), (uint32)(_ScenarioBody.size() - (endHeader + slasheaderLen))).toString();
                if (values[3].second != md5Id2)
                {
                    return false;
                }

            }
            md5 = values[2].second;
            signature = values[1].second;

        }


    }
    catch(...)
    {
        _Values = values;
        nlwarning("Can't load scenario %s", _Filename.c_str());
        return false;
    }

    _Values = values;
    // close the file
    inf.close();
    return true;

}
Example #13
0
IFileAccess::TReturnCode	CLoadFile::execute(CFileAccessManager& manager)
{
	bool	fileExists = NLMISC::CFile::fileExists(getBackupFileName(Filename));

	if (!fileExists && checkFailureMode(MajorFailureIfFileNotExists))
	{
		FailureReason = NLMISC::toString("MAJOR_FAILURE:LOAD: file '%s' doesn't not exist", Filename.c_str());
		return MajorFailure;
	}

	CBackupMsgReceiveFile outMsg;
	outMsg.RequestId = RequestId;

	NLMISC::CIFile	f;
	bool			fileOpen = (fileExists && f.open(getBackupFileName(Filename)));
	bool			fileRead = false;

	if (fileOpen)
	{
		outMsg.FileDescription.set(getBackupFileName(Filename));

		// restore filename with the provided one for the response
		outMsg.FileDescription.FileName = Filename;

		try
		{
			H_AUTO(LoadFileSerial);
  			outMsg.Data.invert();
			f.serialBuffer( outMsg.Data.bufferToFill(outMsg.FileDescription.FileSize), outMsg.FileDescription.FileSize);
			outMsg.Data.invert();
			fileRead = true;
			f.close();
		}
		catch(const NLMISC::Exception &)
		{
		}
	}

	if (!fileRead && checkFailureMode(MajorFailureIfFileUnreaddable))
	{
		FailureReason = NLMISC::toString("MAJOR_FAILURE:LOAD: can't %s file '%s'", (!fileOpen ? "open" : "read"), Filename.c_str());
		return MajorFailure;
	}

//	outMsg.send(Requester);
	switch (Requester.RequesterType)
	{
	case TRequester::rt_service:
		{
			H_AUTO(CBackupMsgReceiveFile1);
			NLNET::CMessage msgOut("bs_file");
			outMsg.serial(msgOut);
			NLNET::CUnifiedNetwork::getInstance()->send( Requester.ServiceId, msgOut );
		}
		break;
	case TRequester::rt_layer3:
		{
			NLNET::CMessage msgOut("bs_file");
			outMsg.serial(msgOut);
			Requester.Netbase->send(msgOut, Requester.From);
		}
		break;
	case TRequester::rt_module:
		{
			BS::CBackupServiceClientProxy bsc(Requester.ModuleProxy);

			bsc.loadFileResult(CBackupService::getInstance()->getBackupModule(), 
				RequestId, 
				outMsg.FileDescription.FileName, 
				outMsg.FileDescription.FileTimeStamp, 
				NLNET::TBinBuffer(outMsg.Data.buffer(), outMsg.Data.length()));
		}
	}

	// If the file read failed for any other reason than file not found then complain
	if (!fileRead && fileExists)
	{
        FailureReason = NLMISC::toString("MINOR_FAILURE:LOAD: can't %s file '%s'", (!fileOpen ? "open" : "read"), Filename.c_str());
        return MinorFailure;
	}

	if (VerboseLog)
	{
		// We can assume that this is the only case where the file read failed that hasn't already been treated above
		if (!fileExists)
		{
			nldebug("Load file Failed (but MajorFailureIfFileUnreaddable==false): file '%s' doesn't not exist", Filename.c_str());
		}
		else
		{
			nlinfo("Loaded file '%s'", Filename.c_str());
		}
	}

	return Success;
}
Example #14
0
CInstanceGroup*	CExportNel::buildInstanceGroup(const vector<INode*>& vectNode, vector<INode*>& resultInstanceNode, TimeValue tvTime)
{
	// Extract from the node the name, the transformations and the parent

	CInstanceGroup::TInstanceArray aIGArray;
	uint32 i, nNumIG;
	uint32 j,k,m;

	aIGArray.empty ();
	resultInstanceNode.empty ();
	aIGArray.resize (vectNode.size());
	resultInstanceNode.resize (vectNode.size());

	int nNbInstance = 0;
	for (i = 0; i < vectNode.size(); ++i)
	{
		INode *pNode = vectNode[i];

		int nAccelType = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_ACCEL, 32);

		if ((nAccelType&3) == 0) // If not an accelerator
		if (!RPO::isZone (*pNode, tvTime))
		if (CExportNel::isMesh (*pNode, tvTime) || CExportNel::isDummy(*pNode, tvTime))
		{
			++nNbInstance;
		}
	}

	// Check integrity of the hierarchy and set the parents
	std::vector<INode*>::const_iterator it = vectNode.begin();
	nNumIG = 0;
	for (i = 0; i < (sint)vectNode.size(); ++i, ++it)
	{
		INode *pNode = *it;

		int nAccelType = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_ACCEL, 32);

		if ((nAccelType&3) == 0) // If not an accelerator
		if (!RPO::isZone( *pNode, tvTime ))
		if (CExportNel::isMesh( *pNode, tvTime ) || CExportNel::isDummy(*pNode, tvTime))
		{
			aIGArray[nNumIG].DontAddToScene = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_DONT_ADD_TO_SCENE, 0)?true:false;
			aIGArray[nNumIG].InstanceName = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_INSTANCE_NAME, "");
			resultInstanceNode[nNumIG] = pNode;
			if (aIGArray[nNumIG].InstanceName == "") // no instance name was set, takes the node name instead
			{
				aIGArray[nNumIG].InstanceName = pNode->GetName();
			}

			// Visible? always true, but if special flag for camera collision
			sint	appDataCameraCol= CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_CAMERA_COLLISION_MESH_GENERATION, 0);
			aIGArray[nNumIG].Visible= appDataCameraCol!=3;


			INode *pParent = pNode->GetParentNode();

			// Set the DontCastShadow flag.
			aIGArray[nNumIG].DontCastShadow= pNode->CastShadows()==0;

			// Set the Special DontCastShadow flag.
			aIGArray[nNumIG].DontCastShadowForInterior= CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_LIGHT_DONT_CAST_SHADOW_INTERIOR, BST_UNCHECKED)?true:false;
			aIGArray[nNumIG].DontCastShadowForExterior= CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_LIGHT_DONT_CAST_SHADOW_EXTERIOR, BST_UNCHECKED)?true:false;

			// Is the pNode has the root node for parent ?
			if( pParent->IsRootNode() == 0 )
			{
				// Look if the parent is in the selection
				int nNumIG2 = 0;
				for (j = 0; j < vectNode.size(); ++j)
				{
					INode *pNode2 = vectNode[j];

					int nAccelType2 = CExportNel::getScriptAppData (pNode2, NEL3D_APPDATA_ACCEL, 32);
					if ((nAccelType2&3) == 0) // If not an accelerator
					if (!RPO::isZone( *pNode2, tvTime ))
					if (CExportNel::isMesh( *pNode2, tvTime ))
					{
						if (pNode2 == pParent)
							break;
						++nNumIG2;
					}
				}
				if (nNumIG2 == nNbInstance)
				{
					// The parent is not selected ! link to root
					aIGArray[nNumIG].nParent = -1;
				}
				else
				{
					aIGArray[nNumIG].nParent = nNumIG2;
				}
			}
			else
			{
				aIGArray[nNumIG].nParent = -1;
			}
			++nNumIG;
		}
	}
	aIGArray.resize( nNumIG );
	resultInstanceNode.resize( nNumIG );

	// Build the array of node
	vGlobalPos = CVector(0,0,0);
	nNumIG = 0;
	it = vectNode.begin();
	for (i = 0; i < (sint)vectNode.size(); ++i, ++it)
	{
		INode *pNode = *it;

		int nAccelType = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_ACCEL, 32);

		if ((nAccelType&3) == 0) // If not an accelerator
		if (!RPO::isZone (*pNode, tvTime))
		if (CExportNel::isMesh (*pNode, tvTime) || CExportNel::isDummy(*pNode, tvTime))
		{
			CVector vScaleTemp;
			CQuat qRotTemp;
			CVector vPosTemp;

			// Get Nel Name for the object.
			aIGArray[nNumIG].Name= CExportNel::getNelObjectName(*pNode);

			//Get the local transformation matrix
			Matrix3 nodeTM = pNode->GetNodeTM(0);
			INode *pParent = pNode->GetParentNode();
			Matrix3 parentTM = pParent->GetNodeTM(0);
			Matrix3 localTM	= nodeTM*Inverse(parentTM);

			// Extract transformations
			CExportNel::decompMatrix (vScaleTemp, qRotTemp, vPosTemp, localTM);
			aIGArray[nNumIG].Rot   = qRotTemp;
			aIGArray[nNumIG].Pos   = vPosTemp;
			aIGArray[nNumIG].Scale = vScaleTemp;
			vGlobalPos += vPosTemp;
			++nNumIG;
		}
	}
	// todo Make this work (precision):
	/*
	vGlobalPos = vGlobalPos / nNumIG;
	for (i = 0; i < nNumIG; ++i)
		aIGArray[i].Pos -= vGlobalPos;
	*/

	vGlobalPos = CVector(0,0,0); // Temporary !!!

	// Accelerator Portal/Cluster part
	//=================

	// Creation of all the clusters
	vector<CCluster> vClusters;
	it = vectNode.begin();
	for (i = 0; i < (sint)vectNode.size(); ++i, ++it)
	{
		INode *pNode = *it;

		int nAccelType = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_ACCEL, NEL3D_APPDATA_ACCEL_DEFAULT);
		bool bFatherVisible = nAccelType&NEL3D_APPDATA_ACCEL_FATHER_VISIBLE?true:false;
		bool bVisibleFromFather = nAccelType&NEL3D_APPDATA_ACCEL_VISIBLE_FROM_FATHER?true:false;
		bool bAudibleLikeVisible = (nAccelType&NEL3D_APPDATA_ACCEL_AUDIBLE_NOT_LIKE_VISIBLE)?false:true;
		bool bFatherAudible = bAudibleLikeVisible ? bFatherVisible : nAccelType&NEL3D_APPDATA_ACCEL_FATHER_AUDIBLE?true:false;
		bool bAudibleFromFather = bAudibleLikeVisible ? bVisibleFromFather : nAccelType&NEL3D_APPDATA_ACCEL_AUDIBLE_FROM_FATHER?true:false;

		if ((nAccelType&NEL3D_APPDATA_ACCEL_TYPE) == NEL3D_APPDATA_ACCEL_CLUSTER) // If cluster
		if (!RPO::isZone (*pNode, tvTime))
		if (CExportNel::isMesh(*pNode, tvTime))
		{
			CCluster clusterTemp;
			std::string temp;

			temp = CExportNel::getScriptAppData(pNode, NEL3D_APPDATA_SOUND_GROUP, "no sound");
			clusterTemp.setSoundGroup(temp != "no sound" ? temp : "");
			temp = CExportNel::getScriptAppData(pNode, NEL3D_APPDATA_ENV_FX, "no fx");
			clusterTemp.setEnvironmentFx(temp != "no fx" ? temp : "");

			CMesh::CMeshBuild *pMB;
			CMeshBase::CMeshBaseBuild *pMBB;
			pMB = createMeshBuild (*pNode, tvTime, pMBB);

			convertToWorldCoordinate( pMB, pMBB );

			for (j = 0; j < pMB->Faces.size(); ++j)
			{
				if (!clusterTemp.makeVolume (pMB->Vertices[pMB->Faces[j].Corner[0].Vertex],
											 pMB->Vertices[pMB->Faces[j].Corner[1].Vertex],
											 pMB->Vertices[pMB->Faces[j].Corner[2].Vertex]) )
				{
					// ERROR : The volume is not convex !!!
					char tam[256];
					sprintf(tam,"ERROR: The cluster %s is not convex.",vectNode[i]->GetName());
					//MessageBox(NULL,tam,"Error",MB_OK|MB_ICONERROR);
					nlwarning(tam);
				}
			}

			clusterTemp.FatherVisible = bFatherVisible;
			clusterTemp.VisibleFromFather = bVisibleFromFather;
			clusterTemp.FatherAudible = bFatherAudible;
			clusterTemp.AudibleFromFather = bAudibleFromFather;
			clusterTemp.Name = pNode->GetName();

			vClusters.push_back (clusterTemp);
			delete pMB;
			delete pMBB;
		}
	}

	// Creation of all the portals
	vector<CPortal> vPortals;
	it = vectNode.begin();
	for (i = 0; i < (sint)vectNode.size(); ++i, ++it)
	{
		INode *pNode = *it;

		int nAccelType = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_ACCEL, 32);

		if ((nAccelType&3) == 1) // If Portal
		if (!RPO::isZone (*pNode, tvTime))
		if (CExportNel::isMesh(*pNode, tvTime))
		{
			CPortal portalTemp;
			std::string temp;

			temp = CExportNel::getScriptAppData(pNode, NEL3D_APPDATA_OCC_MODEL, "no occlusion");
			portalTemp.setOcclusionModel(temp != "no occlusion" ? temp : "");
			temp = CExportNel::getScriptAppData(pNode, NEL3D_APPDATA_OPEN_OCC_MODEL, "no occlusion");
			portalTemp.setOpenOcclusionModel(temp != "no occlusion" ? temp : "");

			CMesh::CMeshBuild *pMB;
			CMeshBase::CMeshBaseBuild *pMBB;
			pMB = createMeshBuild (*pNode, tvTime, pMBB);

			convertToWorldCoordinate( pMB, pMBB );

			vector<sint32> poly;
			vector<bool> facechecked;
			facechecked.resize (pMB->Faces.size());
			for (j = 0; j < pMB->Faces.size(); ++j)
				facechecked[j] = false;

			poly.push_back(pMB->Faces[0].Corner[0].Vertex);
			poly.push_back(pMB->Faces[0].Corner[1].Vertex);
			poly.push_back(pMB->Faces[0].Corner[2].Vertex);
			facechecked[0] = true;
			for (j = 0; j < pMB->Faces.size(); ++j)
			if (!facechecked[j])
			{
				bool found = false;

				for(k = 0; k < 3; ++k)
				{
					for(m = 0; m < poly.size(); ++m)
					{
						if ((pMB->Faces[j].Corner[k].Vertex == poly[m]) &&
							(pMB->Faces[j].Corner[(k+1)%3].Vertex == poly[(m+1)%poly.size()]))
						{
							found = true;
							break;
						}
						if ((pMB->Faces[j].Corner[(k+1)%3].Vertex == poly[m]) &&
							(pMB->Faces[j].Corner[k].Vertex == poly[(m+1)%poly.size()]))
						{
							found = true;
							break;
						}
					}
					if (found)
						break;
				}
				if (found)
				{
					// insert an empty space in poly between m and m+1
					poly.resize (poly.size()+1);
					for (uint32 a = poly.size()-2; a > m; --a)
						poly[a+1] = poly[a];
					poly[m+1] = pMB->Faces[j].Corner[(k+2)%3].Vertex;
					facechecked[j] = true;
					j = 0;
				}
			}
			vector<CVector> polyv;
			polyv.resize (poly.size());
			for (j = 0; j < poly.size(); ++j)
				polyv[j] = pMB->Vertices[poly[j]];
			
			if (!portalTemp.setPoly (polyv))
			{
				// ERROR : Poly not convex, or set of vertices not plane
				char tam[256];
				sprintf(tam,"ERROR: The portal %s is not convex.",vectNode[i]->GetName());
				//MessageBox(NULL,tam,"Error",MB_OK|MB_ICONERROR);
				nlwarning(tam);
			}

			if (nAccelType&16) // is dynamic portal ?
			{
				string InstanceName = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_INSTANCE_NAME, "");
				if (!InstanceName.empty())
					portalTemp.setName (InstanceName);
				else
					portalTemp.setName (string(pNode->GetName()));
			}

			// Check if portal has 2 cluster
			int nNbCluster = 0;
			for (j = 0; j < vClusters.size(); ++j)
			{
				bool bPortalInCluster = true;
				for (k = 0; k < polyv.size(); ++k)
					if (!vClusters[j].isIn (polyv[k]) )
					{
						bPortalInCluster = false;
						break;
					}
				if (bPortalInCluster)
					++nNbCluster;
			}
			if (nNbCluster != 2)
			{
				// ERROR
				char tam[256];
				sprintf(tam,"ERROR: The portal %s has not 2 clusters but %d",vectNode[i]->GetName(), nNbCluster);
				//MessageBox(NULL,tam,"Error",MB_OK|MB_ICONERROR);
				nlwarning(tam);
			}


			vPortals.push_back (portalTemp);
			delete pMB;
			delete pMBB;
		}
	}

	// Link instance to clusters (an instance has a list of clusters)
	nNumIG = 0;
	it = vectNode.begin();
	for (i = 0; i < (sint)vectNode.size(); ++i, ++it)
	{
		INode *pNode = *it;

		int nAccelType = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_ACCEL, 32);

		if ((nAccelType&3) == 0) // If not an accelerator
		if (!RPO::isZone (*pNode, tvTime))
		if (CExportNel::isMesh (*pNode, tvTime) || CExportNel::isDummy(*pNode, tvTime))
		{
			if (nAccelType&32) // Is the flag clusterize set ?
			{
				// Test against all clusters

				// The list of vertices used to test against cluster
				std::vector<NLMISC::CVector> *testVertices;
				std::vector<NLMISC::CVector>       FXVertices;  // Used only if the obj is a fx. It contains the corners of the bbox.
				bool  buildMeshBBox = true;

				/** If it is a mesh, we build its bbox and transform in world
				  * If it is a FX, we read its bbox from its shape
				  * If we can't read it, we use the bbox of the fx helper in max
				  */
				Object *obj = pNode->EvalWorldState(tvTime).obj;
				// Check if there is an object
				if (obj)
				{
					Class_ID  clid = obj->ClassID();
					// is the object a particle system ?					
					if (clid.PartA() == NEL_PARTICLE_SYSTEM_CLASS_ID)
					{
						// build the shape from the file name
						std::string objName = CExportNel::getNelObjectName(*pNode); 						
						if (!objName.empty())
						{											
							NL3D::CShapeStream ss;
							NLMISC::CIFile iF;
							if (iF.open(objName.c_str()))
							{
								try
								{								
									iF.serial(ss);
									NL3D::CParticleSystemShape *pss = dynamic_cast<NL3D::CParticleSystemShape *>(ss.getShapePointer());
									if (!pss)
									{
										nlwarning("ERROR: Node %s shape is not a FX", CExportNel::getName(*pNode).c_str());
									}
									else
									{									
										NLMISC::CAABBox bbox;
										pss->getAABBox(bbox);
										// transform in world
										Matrix3 xForm = pNode->GetNodeTM(tvTime);
										NLMISC::CMatrix nelXForm;
										CExportNel::convertMatrix(nelXForm, xForm);									
										bbox = NLMISC::CAABBox::transformAABBox(nelXForm, bbox);
										// store vertices of the bbox in the list
										FXVertices.reserve(8);
										for(uint k = 0; k < 8; ++k)
										{
											FXVertices.push_back(CVector(((k & 1) ? 1 : -1) * bbox.getHalfSize().x + bbox.getCenter().x,
																		 ((k & 2) ? 1 : -1) * bbox.getHalfSize().y + bbox.getCenter().y,
																		 ((k & 4) ? 1 : -1) * bbox.getHalfSize().z + bbox.getCenter().z));
										}
										//
										testVertices = &FXVertices;
										buildMeshBBox = false;
									}
									delete ss.getShapePointer();
								}
								catch (NLMISC::Exception &e)
								{
									nlwarning(e.what());									
								}
							}							
							if (buildMeshBBox)
							{
								nlwarning("ERROR: Can't get bbox of a particle system from its shape, using helper bbox instead");
							}
						}
					}
				}

				CMesh::CMeshBuild *pMB = NULL;
				CMeshBase::CMeshBaseBuild *pMBB = NULL;

				if (buildMeshBBox)
				{				
					pMB = createMeshBuild (*pNode, tvTime, pMBB);
					convertToWorldCoordinate( pMB, pMBB );
					testVertices = &pMB->Vertices;
				}

				for(k = 0; k < vClusters.size(); ++k)
				{
					bool bMeshInCluster = false;

					for(j = 0; j < testVertices->size(); ++j)
					{
						if (vClusters[k].isIn ((*testVertices)[j]))
						{
							bMeshInCluster = true;
							break;
						}
					}

					if (bMeshInCluster)
					{
						aIGArray[nNumIG].Clusters.push_back (k);
					}
				}
				
				// debug purpose : to remove
				if (vClusters.size() > 0)
				if (aIGArray[nNumIG].Clusters.size() == 0)
				{
					char tam[256];
					sprintf(tam,"ERROR: Object %s is not attached to any cluster\nbut his flag clusterize is set", pNode->GetName());
					//MessageBox(NULL, tam, "Warning", MB_OK);
					nlwarning(tam);
				}
				// debug purpose : to remove

				delete pMB;
				delete pMBB;
			}
			
			++nNumIG;
		}
		// debug purpose : to remove
		/*
		if ((nAccelType&3) == 0) // If not an accelerator
		if (!(nAccelType&32))
		{
			char tam[256];
			sprintf(tam,"Object %s is not clusterized", pNode->GetName());
			MessageBox(NULL, tam, "Info", MB_OK);
		}
		*/
		// debug purpose : to remove

	}


	// PointLight part
	//=================
	bool	sunLightEnabled= false;
	sint	nNumPointLight = 0;
	vector<CPointLightNamed>	pointLights;
	pointLights.resize(vectNode.size());
	// For all nodes
	for (i = 0; i < (sint)vectNode.size(); ++i)
	{
		INode *pNode = vectNode[i];

		SLightBuild		sLightBuild;

		// If it is a Max Light.
		if ( sLightBuild.canConvertFromMaxLight(pNode, tvTime) )
		{
			// And if this light is checked to realtime export
			int		nRTExport= CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_EXPORT_REALTIME_LIGHT, BST_CHECKED);
			if(nRTExport == BST_CHECKED)
			{
				// get Max Light info.
				sLightBuild.convertFromMaxLight(pNode, tvTime);

				// Skip if LightDir
				if(sLightBuild.Type != SLightBuild::LightDir)
				{
					// Fill PointLight Info.
					NL3D::CPointLightNamed	&plNamed= pointLights[nNumPointLight];

					// Position
					plNamed.setPosition(sLightBuild.Position);
					// Attenuation
					plNamed.setupAttenuation(sLightBuild.rRadiusMin, sLightBuild.rRadiusMax);
					// Colors
					// Ensure A=255 for localAmbient to work.
					NLMISC::CRGBA	ambient= sLightBuild.Ambient;
					ambient.A= 255;
					plNamed.setDefaultAmbient(ambient);
					plNamed.setAmbient(ambient);
					plNamed.setDefaultDiffuse(sLightBuild.Diffuse);
					plNamed.setDiffuse(sLightBuild.Diffuse);
					plNamed.setDefaultSpecular(sLightBuild.Specular);
					plNamed.setSpecular(sLightBuild.Specular);

					// GroupName.
					plNamed.AnimatedLight = sLightBuild.AnimatedLight;
					plNamed.LightGroup = sLightBuild.LightGroup;

					// Which light type??
					if(sLightBuild.bAmbientOnly || sLightBuild.Type== SLightBuild::LightAmbient)
					{
						plNamed.setType(CPointLight::AmbientLight);
						// Special ambient info
						int		nRTAmbAdd= CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_REALTIME_AMBIENT_ADD_SUN, BST_UNCHECKED);
						plNamed.setAddAmbientWithSun(nRTAmbAdd==BST_CHECKED);
					}
					else if(sLightBuild.Type== SLightBuild::LightPoint)
					{
						plNamed.setType(CPointLight::PointLight);
					}
					else if(sLightBuild.Type== SLightBuild::LightSpot)
					{
						plNamed.setType(CPointLight::SpotLight);
						// Export Spot infos.
						plNamed.setupSpotDirection(sLightBuild.Direction);
						plNamed.setupSpotAngle(sLightBuild.rHotspot, sLightBuild.rFallof);
					}
					else
					{
						// What???
						nlstop;
					}


					// inc Size
					++nNumPointLight;
				}
			}

			// if this light is a directionnal and checked to export as Sun Light
			int		nExportSun= CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_EXPORT_AS_SUN_LIGHT, BST_UNCHECKED);
			if(nExportSun== BST_CHECKED)
			{
				// get Max Light info.
				sLightBuild.convertFromMaxLight(pNode, tvTime);

				// Skip if not dirLight.
				if(sLightBuild.Type == SLightBuild::LightDir)
					sunLightEnabled= true;
			}
		}
	}
	// Good size
	pointLights.resize(nNumPointLight);


	// Build the ig
	//=================

	CInstanceGroup* pIG = new CInstanceGroup;

	// Link portals and clusters and create meta cluster if one
	pIG->build (vGlobalPos,  aIGArray, vClusters, vPortals, pointLights);

	// IG touched by sun ??
	pIG->enableRealTimeSunContribution(sunLightEnabled);

	return pIG;
}
Example #15
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;
}