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; }
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; }
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; }
// --------------------------------------------------------------------------- // 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; }