// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/> // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. #include "export.h" #include "formFlora.h" #include "formPlant.h" #ifdef NL_OS_WINDOWS #define NOMINMAX #include <windows.h> #endif // NL_OS_WINDOWS #include "nel/ligo/zone_region.h" #include "nel/ligo/primitive.h" #include "nel/georges/u_form_loader.h" #include "nel/georges/u_form.h" #include "nel/georges/u_form_elm.h" #include "nel/3d/zone.h" #include "nel/3d/landscape.h" #include "nel/3d/scene_group.h" #include "nel/3d/visual_collision_manager.h" #include "nel/3d/visual_collision_entity.h" #include "nel/misc/o_xml.h" #include "nel/misc/i_xml.h" #include "nel/misc/path.h" #include "nel/misc/file.h" #include "tools.h" #include "../master/ContinentCfg.h" using namespace std; using namespace NLMISC; using namespace NL3D; using namespace NLLIGO; using namespace NLGEORGES; #define MAX_SYS_DIR 6 const char *gExportSysDir[MAX_SYS_DIR] = { ".", "..", "ZoneBitmaps", "ZoneLigos", "dfn", "tmp" }; // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------- // Segment line intersection P1P2 and P3P4 bool CExport::segmentIntersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { double denominator = (y4-y3)*(x2-x1) - (x4-x3)*(y2-y1); if( denominator == 0.0 ) return false; // The segment are colinear double k = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3) ) / denominator; if( (k<=0.0) || (k>=1.0) ) return false; k = ( (x2-x1)*(y1-y3) - (y2-y1)*(x1-x3) ) / denominator; if( (k<=0.0) || (k>=1.0) ) return false; return true; } // --------------------------------------------------------------------------- void CExport::delIGZone (sint32 x, sint32 y) { string sZoneName = CExport::getZoneNameFromXY (x, y); sZoneName += ".ig"; if (CFile::deleteFile(sZoneName)) { CTools::chdir (_ExeDir); string sTmp = string(" zone ") + sZoneName + " deleted"; if (_ExportCB != NULL) _ExportCB->dispInfo(sTmp); } } // --------------------------------------------------------------------------- void CExport::delAllIGZoneUnderPoint (float fCellSize, CPrimPoint *pPoint, const string &sIGOutputDir) { if (pPoint == NULL) return; sint32 nX, nY; CTools::chdir (sIGOutputDir); nX = (sint32) floor (pPoint->Point.x / fCellSize); nY = (sint32) floor (pPoint->Point.y / fCellSize); delIGZone (nX, nY); CTools::chdir (sIGOutputDir); } // --------------------------------------------------------------------------- void CExport::delAllIGZoneUnderPath (float fCellSize, CPrimPath *pPath, const string &sIGOutputDir) { if (pPath == NULL) return; if (pPath->VPoints.size() == 0) return; uint32 i, j; CVector vMin, vMax; CTools::chdir (sIGOutputDir); vMin = vMax = pPath->VPoints[0]; for (i = 0; i < pPath->VPoints.size(); ++i) { if (vMin.x > pPath->VPoints[i].x) vMin.x = pPath->VPoints[i].x; if (vMin.y > pPath->VPoints[i].y) vMin.y = pPath->VPoints[i].y; if (vMin.z > pPath->VPoints[i].z) vMin.z = pPath->VPoints[i].z; if (vMax.x < pPath->VPoints[i].x) vMax.x = pPath->VPoints[i].x; if (vMax.y < pPath->VPoints[i].y) vMax.y = pPath->VPoints[i].y; if (vMax.z < pPath->VPoints[i].z) vMax.z = pPath->VPoints[i].z; } sint32 x, y; sint32 nMinX, nMinY, nMaxX, nMaxY; nMinX = (sint32) floor (vMin.x / fCellSize); nMinY = (sint32) floor (vMin.y / fCellSize); nMaxX = (sint32) floor (vMax.x / fCellSize); nMaxY = (sint32) floor (vMax.y / fCellSize); for (x = nMinX; x <= nMaxX; ++x) for (y = nMinY; y <= nMaxY; ++y) { // Does the zone (x,y) is under the patah ? CVector vSquare[4]; vSquare[0].x = x * fCellSize; vSquare[0].y = y * fCellSize; vSquare[0].z = 0.0f; vSquare[1].x = (x+1) * fCellSize; vSquare[1].y = y * fCellSize; vSquare[1].z = 0.0f; vSquare[2].x = (x+1) * fCellSize; vSquare[2].y = (y+1) * fCellSize; vSquare[2].z = 0.0f; vSquare[3].x = x * fCellSize; vSquare[3].y = (y+1) * fCellSize; vSquare[3].z = 0.0f; // Is a vertex of the path inside the zone ? for (i = 0; i < pPath->VPoints.size(); ++i) { if ((pPath->VPoints[i].x >= (x*fCellSize)) && (pPath->VPoints[i].x <= ((x+1)*fCellSize)) && (pPath->VPoints[i].y <= (y*fCellSize)) && (pPath->VPoints[i].y <= ((y+1)*fCellSize))) { delIGZone (x, y); CTools::chdir (sIGOutputDir); } } // Is an segment of the path cut an edge of the patat ? for (i = 0; i < pPath->VPoints.size()-1; ++i) for (j = 0; j < 4; ++j) { double x1 = vSquare[j].x; double y1 = vSquare[j].y; double x2 = vSquare[(j+1)%4].x; double y2 = vSquare[(j+1)%4].y; double x3 = pPath->VPoints[i].x; double y3 = pPath->VPoints[i].y; double x4 = pPath->VPoints[i+1].x; double y4 = pPath->VPoints[i+1].y; if (segmentIntersection(x1, y1, x2, y2, x3, y3, x4, y4)) { delIGZone (x, y); CTools::chdir (sIGOutputDir); } } } } // --------------------------------------------------------------------------- void CExport::delAllIGZoneUnderPatat (float fCellSize, CPrimZone *pPatat, const string &sIGOutputDir) { if (pPatat == NULL) return; if (pPatat->VPoints.size() == 0) return; uint32 i, j; CVector vMin, vMax; CTools::chdir (sIGOutputDir); vMin = vMax = pPatat->VPoints[0]; for (i = 0; i < pPatat->VPoints.size(); ++i) { if (vMin.x > pPatat->VPoints[i].x) vMin.x = pPatat->VPoints[i].x; if (vMin.y > pPatat->VPoints[i].y) vMin.y = pPatat->VPoints[i].y; if (vMin.z > pPatat->VPoints[i].z) vMin.z = pPatat->VPoints[i].z; if (vMax.x < pPatat->VPoints[i].x) vMax.x = pPatat->VPoints[i].x; if (vMax.y < pPatat->VPoints[i].y) vMax.y = pPatat->VPoints[i].y; if (vMax.z < pPatat->VPoints[i].z) vMax.z = pPatat->VPoints[i].z; } sint32 x, y; sint32 nMinX, nMinY, nMaxX, nMaxY; nMinX = (sint32) floor (vMin.x / fCellSize); nMinY = (sint32) floor (vMin.y / fCellSize); nMaxX = (sint32) floor (vMax.x / fCellSize); nMaxY = (sint32) floor (vMax.y / fCellSize); for (x = nMinX; x <= nMaxX; ++x) for (y = nMinY; y <= nMaxY; ++y) { // Does the zone (x,y) is under the patat ? // Is a vertex of the zone in the patat ? CVector vSquare[4]; vSquare[0].x = x * fCellSize; vSquare[0].y = y * fCellSize; vSquare[0].z = 0.0f; vSquare[1].x = (x+1) * fCellSize; vSquare[1].y = y * fCellSize; vSquare[1].z = 0.0f; vSquare[2].x = (x+1) * fCellSize; vSquare[2].y = (y+1) * fCellSize; vSquare[2].z = 0.0f; vSquare[3].x = x * fCellSize; vSquare[3].y = (y+1) * fCellSize; vSquare[3].z = 0.0f; for (i = 0; i < 4; ++i) { if (pPatat->contains(vSquare[i])) { delIGZone (x, y); CTools::chdir (sIGOutputDir); } } // Is a vertex of the patat inside the zone ? for (i = 0; i < pPatat->VPoints.size(); ++i) { if ((pPatat->VPoints[i].x >= (x*fCellSize)) && (pPatat->VPoints[i].x <= ((x+1)*fCellSize)) && (pPatat->VPoints[i].y <= (y*fCellSize)) && (pPatat->VPoints[i].y <= ((y+1)*fCellSize))) { delIGZone (x, y); CTools::chdir (sIGOutputDir); } } // Is an edge of the zone cut an edge of the patat ? for (i = 0; i < pPatat->VPoints.size(); ++i) for (j = 0; j < 4; ++j) { double x1 = vSquare[j].x; double y1 = vSquare[j].y; double x2 = vSquare[(j+1)%4].x; double y2 = vSquare[(j+1)%4].y; double x3 = pPatat->VPoints[i].x; double y3 = pPatat->VPoints[i].y; double x4 = pPatat->VPoints[(i+1)%pPatat->VPoints.size()].x; double y4 = pPatat->VPoints[(i+1)%pPatat->VPoints.size()].y; if (segmentIntersection(x1, y1, x2, y2, x3, y3, x4, y4)) { delIGZone (x, y); CTools::chdir (sIGOutputDir); } } } } // --------------------------------------------------------------------------- // A patat needs an update if there is at least one zone under itself that is not present bool CExport::isPatatNeedUpdate (float fCellSize, CPrimZone *pPatat, const string &sIGOutputDir) { uint32 i, j; CVector vMin, vMax; CTools::chdir (sIGOutputDir); if (pPatat->VPoints.size() == 0) return false; vMin = vMax = pPatat->VPoints[0]; for (i = 0; i < pPatat->VPoints.size(); ++i) { if (vMin.x > pPatat->VPoints[i].x) vMin.x = pPatat->VPoints[i].x; if (vMin.y > pPatat->VPoints[i].y) vMin.y = pPatat->VPoints[i].y; if (vMin.z > pPatat->VPoints[i].z) vMin.z = pPatat->VPoints[i].z; if (vMax.x < pPatat->VPoints[i].x) vMax.x = pPatat->VPoints[i].x; if (vMax.y < pPatat->VPoints[i].y) vMax.y = pPatat->VPoints[i].y; if (vMax.z < pPatat->VPoints[i].z) vMax.z = pPatat->VPoints[i].z; } sint32 x, y; sint32 nMinX, nMinY, nMaxX, nMaxY; nMinX = (sint32) floor (vMin.x / fCellSize); nMinY = (sint32) floor (vMin.y / fCellSize); nMaxX = (sint32) floor (vMax.x / fCellSize); nMaxY = (sint32) floor (vMax.y / fCellSize); for (x = nMinX; x <= nMaxX; ++x) for (y = nMinY; y <= nMaxY; ++y) { // Does the zone (x,y) is under the patat ? bool bZoneUnderPatat = false; // Is a vertex of the zone in the patat ? CVector vSquare[4]; vSquare[0].x = x * fCellSize; vSquare[0].y = y * fCellSize; vSquare[0].z = 0.0f; vSquare[1].x = (x+1) * fCellSize; vSquare[1].y = y * fCellSize; vSquare[1].z = 0.0f; vSquare[2].x = (x+1) * fCellSize; vSquare[2].y = (y+1) * fCellSize; vSquare[2].z = 0.0f; vSquare[3].x = x * fCellSize; vSquare[3].y = (y+1) * fCellSize; vSquare[3].z = 0.0f; for (i = 0; i < 4; ++i) { if (pPatat->contains(vSquare[i])) { string sTmp = CExport::getZoneNameFromXY(x,y) + ".ig"; if (!CTools::fileExist(sTmp)) // If the file does not exist return true; // need update } } // Is a vertex of the patat inside the zone ? for (i = 0; i < pPatat->VPoints.size(); ++i) { if ((pPatat->VPoints[i].x >= (x*fCellSize)) && (pPatat->VPoints[i].x <= ((x+1)*fCellSize)) && (pPatat->VPoints[i].y >= (y*fCellSize)) && (pPatat->VPoints[i].y <= ((y+1)*fCellSize))) { string sTmp = CExport::getZoneNameFromXY(x,y) + ".ig"; if (!CTools::fileExist(sTmp)) // If the file does not exist return true; // need update } } // Is an edge of the zone cut an edge of the patat ? for (i = 0; i < pPatat->VPoints.size(); ++i) for (j = 0; j < 4; ++j) { double x1 = vSquare[j].x; double y1 = vSquare[j].y; double x2 = vSquare[(j+1)%4].x; double y2 = vSquare[(j+1)%4].y; double x3 = pPatat->VPoints[i].x; double y3 = pPatat->VPoints[i].y; double x4 = pPatat->VPoints[(i+1)%pPatat->VPoints.size()].x; double y4 = pPatat->VPoints[(i+1)%pPatat->VPoints.size()].y; if (segmentIntersection(x1, y1, x2, y2, x3, y3, x4, y4)) { string sTmp = CExport::getZoneNameFromXY (x, y) + ".ig"; if (!CTools::fileExist(sTmp)) // If the file does not exist return true; // need update } } } return false; } // --------------------------------------------------------------------------- // A path needs an update if there is at least one zone under itself that is not present bool CExport::isPathNeedUpdate (float fCellSize, CPrimPath *pPath, const string &sIGOutputDir) { uint32 i, j; CVector vMin, vMax; CTools::chdir (sIGOutputDir); if (pPath->VPoints.size() == 0) return false; vMin = vMax = pPath->VPoints[0]; for (i = 0; i < pPath->VPoints.size(); ++i) { if (vMin.x > pPath->VPoints[i].x) vMin.x = pPath->VPoints[i].x; if (vMin.y > pPath->VPoints[i].y) vMin.y = pPath->VPoints[i].y; if (vMin.z > pPath->VPoints[i].z) vMin.z = pPath->VPoints[i].z; if (vMax.x < pPath->VPoints[i].x) vMax.x = pPath->VPoints[i].x; if (vMax.y < pPath->VPoints[i].y) vMax.y = pPath->VPoints[i].y; if (vMax.z < pPath->VPoints[i].z) vMax.z = pPath->VPoints[i].z; } sint32 x, y; sint32 nMinX, nMinY, nMaxX, nMaxY; nMinX = (sint32) floor (vMin.x / fCellSize); nMinY = (sint32) floor (vMin.y / fCellSize); nMaxX = (sint32) floor (vMax.x / fCellSize); nMaxY = (sint32) floor (vMax.y / fCellSize); for (x = nMinX; x <= nMaxX; ++x) for (y = nMinY; y <= nMaxY; ++y) { // Does the zone (x,y) is under the patat ? bool bZoneUnderPatat = false; // Is a vertex of the zone in the patat ? CVector vSquare[4]; vSquare[0].x = x * fCellSize; vSquare[0].y = y * fCellSize; vSquare[0].z = 0.0f; vSquare[1].x = (x+1) * fCellSize; vSquare[1].y = y * fCellSize; vSquare[1].z = 0.0f; vSquare[2].x = (x+1) * fCellSize; vSquare[2].y = (y+1) * fCellSize; vSquare[2].z = 0.0f; vSquare[3].x = x * fCellSize; vSquare[3].y = (y+1) * fCellSize; vSquare[3].z = 0.0f; // Is a vertex of the path inside the zone ? for (i = 0; i < pPath->VPoints.size(); ++i) { if ((pPath->VPoints[i].x >= (x*fCellSize)) && (pPath->VPoints[i].x <= ((x+1)*fCellSize)) && (pPath->VPoints[i].y >= (y*fCellSize)) && (pPath->VPoints[i].y <= ((y+1)*fCellSize))) { string sTmp = CExport::getZoneNameFromXY(x,y) + ".ig"; if (!CTools::fileExist(sTmp)) // If the file does not exist return true; // need update } } // Is an edge of the zone cut an edge of the patat ? for (i = 0; i < (pPath->VPoints.size()-1); ++i) for (j = 0; j < 4; ++j) { double x1 = vSquare[j].x; double y1 = vSquare[j].y; double x2 = vSquare[(j+1)%4].x; double y2 = vSquare[(j+1)%4].y; double x3 = pPath->VPoints[i].x; double y3 = pPath->VPoints[i].y; double x4 = pPath->VPoints[i+1].x; double y4 = pPath->VPoints[i+1].y; if (segmentIntersection(x1, y1, x2, y2, x3, y3, x4, y4)) { string sTmp = CExport::getZoneNameFromXY (x, y) + ".ig"; if (!CTools::fileExist(sTmp)) // If the file does not exist return true; // need update } } } return false; } // --------------------------------------------------------------------------- // A path needs an update if there is at least one zone under itself that is not present bool CExport::isPointNeedUpdate (float fCellSize, CPrimPoint *pPoint, const string &sIGOutputDir) { CTools::chdir (sIGOutputDir); sint32 x, y; x = (sint32) floor (pPoint->Point.x / fCellSize); y = (sint32) floor (pPoint->Point.y / fCellSize); // Does the zone (x,y) is under the patat ? bool bZoneUnderPatat = false; // Is a vertex of the zone in the patat ? CVector vSquare[4]; vSquare[0].x = x * fCellSize; vSquare[0].y = y * fCellSize; vSquare[0].z = 0.0f; vSquare[1].x = (x+1) * fCellSize; vSquare[1].y = y * fCellSize; vSquare[1].z = 0.0f; vSquare[2].x = (x+1) * fCellSize; vSquare[2].y = (y+1) * fCellSize; vSquare[2].z = 0.0f; vSquare[3].x = x * fCellSize; vSquare[3].y = (y+1) * fCellSize; vSquare[3].z = 0.0f; // Is the vertex inside the zone ? if ((pPoint->Point.x >= (x*fCellSize)) && (pPoint->Point.x <= ((x+1)*fCellSize)) && (pPoint->Point.y >= (y*fCellSize)) && (pPoint->Point.y <= ((y+1)*fCellSize))) { string sTmp = CExport::getZoneNameFromXY(x,y) + ".ig"; if (!CTools::fileExist(sTmp)) // If the file does not exist return true; // need update } return false; } // --------------------------------------------------------------------------- // SExportOptions // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- SExportOptions::SExportOptions () { CellSize = 160.0f; } // --------------------------------------------------------------------------- bool SExportOptions::loadcf (CConfigFile &cf) { // Out CConfigFile::CVar &cvOutIGDir = cf.getVar("EXP_OutIGDir"); OutIGDir = cvOutIGDir.asString(); // In CConfigFile::CVar &cvInLandscapeDir = cf.getVar("EXP_ZoneWDir"); InLandscapeDir = cvInLandscapeDir.asString(); CConfigFile::CVar &cvLandBankFile = cf.getVar("EXP_SmallBank"); LandBankFile = cvLandBankFile.asString(); CConfigFile::CVar &cvLandFarBankFile = cf.getVar("EXP_FarBank"); LandFarBankFile = cvLandFarBankFile.asString(); CConfigFile::CVar &cvLandTileNoiseDir = cf.getVar("EXP_DisplaceDir"); LandTileNoiseDir = cvLandTileNoiseDir.asString(); CConfigFile::CVar &cvCellSize = cf.getVar("EXP_CellSize"); CellSize = cvCellSize.asFloat(); CConfigFile::CVar &cvPrimFloraDir = cf.getVar("EXP_PrimFloraDir"); PrimFloraDir = cvPrimFloraDir.asString(); return true; } // --------------------------------------------------------------------------- bool SExportOptions::save (FILE *f) { fprintf (f,"\n// Export Options\n"); fprintf (f, "EXP_OutIGDir = \"%s\";\n", OutIGDir); fprintf (f, "EXP_ZoneWDir = \"%s\";\n", InLandscapeDir); fprintf (f, "EXP_SmallBank = \"%s\";\n", LandBankFile); fprintf (f, "EXP_FarBank = \"%s\";\n", LandFarBankFile); fprintf (f, "EXP_DisplaceDir = \"%s\";\n", LandTileNoiseDir); fprintf (f, "EXP_CellSize = %f;\n", CellSize); fprintf (f, "EXP_PrimFloraDir = \"%s\";\n", PrimFloraDir); return true; } // --------------------------------------------------------------------------- // SExportPrimitive // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- bool SExportPrimitive::operator == (const SExportPrimitive &rRightArg) { if (strcmp(this->FullPrimName.c_str(), rRightArg.FullPrimName.c_str()) == 0) if (strcmp(this->PrimitiveName.c_str(), rRightArg.PrimitiveName.c_str()) == 0) return true; return false; } // --------------------------------------------------------------------------- // CExport // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- CExport::CExport() { _Landscape = NULL; _VCM = NULL; _VCE = NULL; _ExportCB = NULL; _Options = NULL; } // --------------------------------------------------------------------------- CExport::~CExport() { if (_Landscape != NULL) delete _Landscape; if (_VCM != NULL) { _VCM->deleteEntity (_VCE); delete _VCM; } } // --------------------------------------------------------------------------- bool CExport::newExport (SExportOptions &opt, IExportCB *expCB) { _ExeDir = CTools::pwd(); _Options = &opt; _ExportCB = expCB; string sTmp; uint32 i, j, k, m; // First of all read the CFG string sContinentDir = _Options->PrimFloraDir; sContinentDir = CTools::normalizePath (sContinentDir); CTools::chdir (sContinentDir.c_str()); // Relative to absolute path sContinentDir = CTools::pwd () + "\\"; CTools::chdir (_ExeDir); // Read continent.cfg SContinentCfg ContinentCFG; { string sTmp = sContinentDir + "continent.cfg"; if (!ContinentCFG.load(sTmp.c_str())) throw Exception("Cannot load continent.cfg"); } // Ok set parameters from options first and with CFG if no options set if (_Options->OutIGDir == "") _OutIGDir = CTools::normalizePath (ContinentCFG.OutIGDir); else _OutIGDir = CTools::normalizePath (_Options->OutIGDir); if (_Options->LandFile == "") _LandFile = ContinentCFG.LandFile; else _LandFile = _Options->LandFile; if (_Options->DfnDir == "") _DfnDir = ContinentCFG.DfnDir; else _DfnDir = _Options->DfnDir; if (_Options->GameElemDir == "") _GameElemDir = ContinentCFG.GameElemDir; else _GameElemDir = _Options->GameElemDir; if (_Options->InLandscapeDir == "") _InLandscapeDir = ContinentCFG.LandZoneWDir; // Directory where to get .zonew files else _InLandscapeDir = _Options->InLandscapeDir; if (_Options->LandFarBankFile == "") _LandBankFile = ContinentCFG.LandBankFile; // The .smallbank file associated with the landscape else _LandBankFile = _Options->LandBankFile; if (_Options->LandFarBankFile == "") _LandFarBankFile = ContinentCFG.LandFarBankFile; // The .farbank file else _LandFarBankFile = _Options->LandFarBankFile; if (_Options->LandTileNoiseDir == "") _LandTileNoiseDir = ContinentCFG.LandTileNoiseDir; // Directory where to get displacement map else _LandTileNoiseDir = _Options->LandTileNoiseDir; // Now create output directory CTools::mkdir (_OutIGDir); CTools::chdir (_OutIGDir.c_str()); _OutIGDir = CTools::pwd () + "\\"; CTools::chdir (_ExeDir); CPath::addSearchPath (_DfnDir, true, true); CPath::addSearchPath (_GameElemDir, true, true); CPath::addSearchPath (_LandTileNoiseDir, true, true); // Get all regions vector<string> vRegions; WIN32_FIND_DATA findData; HANDLE hFind; CTools::chdir (sContinentDir); hFind = FindFirstFile ("*.*", &findData); while (hFind != INVALID_HANDLE_VALUE) { if (GetFileAttributes(findData.cFileName)&FILE_ATTRIBUTE_DIRECTORY) { // Look if the name is a system directory bool bFound = false; for (i = 0; i < MAX_SYS_DIR; ++i) if (stricmp (findData.cFileName, gExportSysDir[i]) == 0) { bFound = true; break; } if (!bFound) // No, ok lets recurse it { vRegions.push_back (findData.cFileName); } } if (FindNextFile (hFind, &findData) == 0) break; } FindClose (hFind); // Process all regions uint32 nRegion; for (nRegion = 0; nRegion < vRegions.size(); ++nRegion) { CTools::chdir (sContinentDir + vRegions[nRegion]); // Process if a flora has been modified vector<SFloraToUpdate> vFloraToUpdate; uint32 nFloraFile; // Fill the structure for update { // Get all new flora of the region vector<string> vFloraFiles; CTools::dirSub ("*.flora", vFloraFiles, true); for (nFloraFile = 0; nFloraFile < vFloraFiles.size(); ++nFloraFile) { // Compare the date with the old file stored in the IG output directory CTools::chdir (sContinentDir + vRegions[nRegion]); string sBaseName = vFloraFiles[nFloraFile].substr(sContinentDir.size()); sBaseName = sBaseName.substr(0,sBaseName.rfind('\\')); for (i = 0; i < sBaseName.size(); ++i) if (sBaseName[i] == '\\') sBaseName[i] = '-'; sBaseName += '-'; string sOldFloraName = _OutIGDir + sBaseName + vFloraFiles[nFloraFile].substr(vFloraFiles[nFloraFile].rfind('\\')+1); if (CTools::fileExist(sOldFloraName)) { if (CTools::fileDateCmp(vFloraFiles[nFloraFile], sOldFloraName) > 0) { // Delete zones from the 2 files SFloraToUpdate ftuTmp; // Delete zones from the newest file ftuTmp.FloraFile = vFloraFiles[nFloraFile]; CTools::chdir (vFloraFiles[nFloraFile].substr(0,vFloraFiles[nFloraFile].rfind('\\'))); CTools::dir ("*.prim", ftuTmp.PrimFile, true); vFloraToUpdate.push_back (ftuTmp); // Delete zones from the oldest file CTools::chdir (_OutIGDir); ftuTmp.FloraFile = sOldFloraName; CTools::dir (sBaseName + "*.prim", ftuTmp.PrimFile, true); vFloraToUpdate.push_back (ftuTmp); CTools::chdir (_ExeDir); sTmp = vFloraFiles[nFloraFile] + " has been modified."; if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp); } else { CTools::chdir (_ExeDir); sTmp = vFloraFiles[nFloraFile] + string(" up to date."); if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp); } } else { // Delete zone from newest file SFloraToUpdate ftuTmp; ftuTmp.FloraFile = vFloraFiles[nFloraFile]; CTools::chdir (vFloraFiles[nFloraFile].substr(0,vFloraFiles[nFloraFile].rfind('\\'))); CTools::dir ("*.prim", ftuTmp.PrimFile, true); vFloraToUpdate.push_back (ftuTmp); CTools::chdir (_ExeDir); sTmp = vFloraFiles[nFloraFile] + " not generated."; if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp); } } // Get all old flora of the current region CTools::chdir (_OutIGDir); CTools::dir (vRegions[nRegion] + "-*.flora", vFloraFiles, true); for (nFloraFile = 0; nFloraFile < vFloraFiles.size(); ++nFloraFile) { CTools::chdir (_OutIGDir); // if the old flora file still exist but the new one not -> delete zone for the oldest file string sNewFloraName = vFloraFiles[nFloraFile].substr(vFloraFiles[nFloraFile].rfind('\\')+1); string sBaseName = sNewFloraName.substr (0, vFloraFiles[nFloraFile].rfind('-')); for (i = 0; i < sNewFloraName.size(); ++i) if (sNewFloraName[i] == '-') sNewFloraName[i] = '\\'; sNewFloraName = sContinentDir + sNewFloraName; if (!CTools::fileExist(sNewFloraName)) { SFloraToUpdate ftuTmp; // Delete zones from the oldest file ftuTmp.FloraFile = vFloraFiles[nFloraFile]; CTools::dir (sBaseName + "*.prim", ftuTmp.PrimFile, true); vFloraToUpdate.push_back (ftuTmp); CTools::chdir (_ExeDir); sTmp = vFloraFiles[nFloraFile] + " not needed anymore."; if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp); } } } // End of fill the structure for update // Interpret the FloraToUpdate for (nFloraFile = 0; nFloraFile < vFloraToUpdate.size(); ++nFloraFile) { // Get all patats referenced by the flora and suppress zones under // Load the .flora file SFormFlora FormFlora; { // Get a loader UFormLoader *loader = UFormLoader::createLoader (); // Load the form CSmartPtr<UForm> form = loader->loadForm (vFloraToUpdate[nFloraFile].FloraFile.c_str ()); if (form) FormFlora.build (form->getRootNode ()); // Release the loader UFormLoader::releaseLoader (loader); } // Load all the .prim files that can be referenced by the flora vector<CPrimRegion> vPrimRegions; { vector<string> &rPrimFiles = vFloraToUpdate[nFloraFile].PrimFile; for (i = 0; i < rPrimFiles.size(); ++i) { CPrimRegion tmpPrimRegion; CIFile fileIn; fileIn.open (rPrimFiles[i]); CIXml input; input.init (fileIn); tmpPrimRegion.serial (input); vPrimRegions.push_back (tmpPrimRegion); } } // Delete zones under the old prims referenced by the old zone for (i = 0; i < FormFlora.IncludePatats.size(); ++i) { // Get the patat CPrimZone *pPatat = NULL; CPrimPoint *pPoint = NULL; CPrimPath *pPath = NULL; for (j = 0; j < vPrimRegions.size(); ++j) { for (k = 0; k < vPrimRegions[j].VZones.size(); ++k) if (vPrimRegions[j].VZones[k].getName() == FormFlora.IncludePatats[i]) pPatat = &vPrimRegions[j].VZones[k]; for (k = 0; k < vPrimRegions[j].VPoints.size(); ++k) if (vPrimRegions[j].VPoints[k].getName() == FormFlora.IncludePatats[i]) pPoint = &vPrimRegions[j].VPoints[k]; for (k = 0; k < vPrimRegions[j].VPaths.size(); ++k) if (vPrimRegions[j].VPaths[k].getName() == FormFlora.IncludePatats[i]) pPath = &vPrimRegions[j].VPaths[k]; } if ((pPatat == NULL) && (pPoint == NULL) && (pPath == NULL)) { CTools::chdir (_ExeDir); sTmp = "WARNING : Cannot find " + FormFlora.IncludePatats[i]; if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp); continue; } if ((pPatat != NULL) && (pPatat->VPoints.size() <= 2)) { CTools::chdir (_ExeDir); sTmp = "Patat " + pPatat->getName() + " has less than 3 points"; if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp); continue; } if ((pPath != NULL) && (pPath->VPoints.size() < 2)) { CTools::chdir (_ExeDir); sTmp = "Patat " + pPatat->getName() + " has less than 2 points"; if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp); continue; } if (pPatat != NULL) delAllIGZoneUnderPatat (160.0f, pPatat, _OutIGDir); if (pPoint != NULL) delAllIGZoneUnderPoint (160.0f, pPoint, _OutIGDir); if (pPath != NULL) delAllIGZoneUnderPath (160.0f, pPath, _OutIGDir); } } // End of Process if a flora has been modified // Process if a prim has been modified // Fill the structure for update CTools::chdir (sContinentDir + vRegions[nRegion]); vector<SPrimToUpdate> vPrimToUpdate; { uint32 nPrimFile; vector<string> vPrimFiles; // Get all new prim of the region CTools::dirSub ("*.prim", vPrimFiles, true); for (nPrimFile = 0; nPrimFile < vPrimFiles.size(); ++nPrimFile) { CTools::chdir (sContinentDir + vRegions[nRegion]); // Compare the date with the old file stored in the IG output directory string sBaseName = vPrimFiles[nPrimFile].substr(sContinentDir.size()); sBaseName = sBaseName.substr(0,sBaseName.rfind('\\')); for (i = 0; i < sBaseName.size(); ++i) if (sBaseName[i] == '\\') sBaseName[i] = '-'; sBaseName += '-'; string sOldPrimName = _OutIGDir + sBaseName + vPrimFiles[nPrimFile].substr(vPrimFiles[nPrimFile].rfind('\\')+1); if (CTools::fileExist(sOldPrimName)) { if (CTools::fileDateCmp(vPrimFiles[nPrimFile], sOldPrimName) > 0) { // Delete zones from the 2 files SPrimToUpdate ptuTmp; // Delete zones from the newest file ptuTmp.PrimFile = vPrimFiles[nPrimFile]; CTools::chdir (vPrimFiles[nPrimFile].substr(0,vPrimFiles[nPrimFile].rfind('\\'))); CTools::dir ("*.flora", ptuTmp.FloraFile, true); vPrimToUpdate.push_back (ptuTmp); // Delete zones from the oldest file CTools::chdir (_OutIGDir); ptuTmp.PrimFile = sOldPrimName; CTools::dir (sBaseName + "*.flora", ptuTmp.FloraFile, true); vPrimToUpdate.push_back (ptuTmp); CTools::chdir (_ExeDir); sTmp = vPrimFiles[nPrimFile] + " has been modified."; if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp); } else { CTools::chdir (_ExeDir); sTmp = vPrimFiles[nPrimFile] + " up to date."; if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp); } } else { // Delete zone from newest file SPrimToUpdate ptuTmp; ptuTmp.PrimFile = vPrimFiles[nPrimFile]; CTools::chdir (vPrimFiles[nPrimFile].substr(0,vPrimFiles[nPrimFile].rfind('\\'))); CTools::dir ("*.flora", ptuTmp.FloraFile, true); vPrimToUpdate.push_back (ptuTmp); CTools::chdir (_ExeDir); sTmp = vPrimFiles[nPrimFile] + " not generated."; if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp); } } // Get all old prim of the current region CTools::chdir (_OutIGDir); CTools::dir (vRegions[nRegion] + "-*.prim", vPrimFiles, false); for (nPrimFile = 0; nPrimFile < vPrimFiles.size(); ++nPrimFile) { CTools::chdir (_OutIGDir); // if the old prim file still exist but the new one not -> delete zone for the oldest file string sNewPrimName = vPrimFiles[nPrimFile].substr(vPrimFiles[nPrimFile].rfind('\\')+1); string sBaseName = sNewPrimName.substr (0, vPrimFiles[nPrimFile].rfind('-')); for (i = 0; i < sNewPrimName.size(); ++i) if (sNewPrimName[i] == '-') sNewPrimName[i] = '\\'; sNewPrimName = sContinentDir + sNewPrimName; if (!CTools::fileExist(sNewPrimName)) { // Delete zones from the oldest file CPrimRegion PrimRegion; { CIFile fileIn; fileIn.open (vPrimFiles[nPrimFile]); CIXml input; input.init (fileIn); PrimRegion.serial (input); } for (j = 0; j < PrimRegion.VZones.size(); ++j) delAllIGZoneUnderPatat (_Options->CellSize, &PrimRegion.VZones[j], _OutIGDir); for (j = 0; j < PrimRegion.VPaths.size(); ++j) delAllIGZoneUnderPath (_Options->CellSize, &PrimRegion.VPaths[j], _OutIGDir); for (j = 0; j < PrimRegion.VPoints.size(); ++j) delAllIGZoneUnderPoint (_Options->CellSize, &PrimRegion.VPoints[j], _OutIGDir); CTools::chdir (_ExeDir); sTmp = vPrimFiles[nPrimFile] + " not needed anymore."; if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp); } } } // End of fill the structure for update // Interpretation of structure for update for (i = 0; i < vPrimToUpdate.size(); ++i) { // Load all the .flora file vector<SFormFlora> vFormFloras; for (j = 0; j < vPrimToUpdate[i].FloraFile.size(); ++j) { // Create the loader UFormLoader *loader = UFormLoader::createLoader (); // Load the form CSmartPtr<UForm> form = loader->loadForm (vPrimToUpdate[i].FloraFile[j].c_str ()); if (form) { SFormFlora FormFloraTmp; FormFloraTmp.build (form->getRootNode ()); vFormFloras.push_back (FormFloraTmp); } // Release the loader UFormLoader::releaseLoader (loader); } // Load all the .prim files that can be referenced by the flora CPrimRegion PrimRegion; { CIFile fileIn; fileIn.open (vPrimToUpdate[i].PrimFile); CIXml input; input.init (fileIn); PrimRegion.serial (input); } // Delete zones under the prims that has been referenced by a flora for (j = 0; j < PrimRegion.VZones.size(); ++j) { // Get the patat CPrimZone *pPatat = NULL; // Check if the current patat is referenced by a flora for (k = 0; k < vFormFloras.size(); ++k) for (m = 0; m < vFormFloras[k].IncludePatats.size(); ++m) if (PrimRegion.VZones[j].getName() == vFormFloras[k].IncludePatats[m]) pPatat = &PrimRegion.VZones[j]; if ((pPatat == NULL) || (pPatat->VPoints.size() <= 2)) { continue; } delAllIGZoneUnderPatat (_Options->CellSize, pPatat, _OutIGDir); } for (j = 0; j < PrimRegion.VPoints.size(); ++j) { // Get the point CPrimPoint *pPoint = NULL; // Check if the current point is referenced by a flora for (k = 0; k < vFormFloras.size(); ++k) for (m = 0; m < vFormFloras[k].IncludePatats.size(); ++m) if (PrimRegion.VPoints[j].getName() == vFormFloras[k].IncludePatats[m]) pPoint = &PrimRegion.VPoints[j]; delAllIGZoneUnderPoint (_Options->CellSize, pPoint, _OutIGDir); } for (j = 0; j < PrimRegion.VPaths.size(); ++j) { // Get the path CPrimPath *pPath = NULL; // Check if the current path is referenced by a flora for (k = 0; k < vFormFloras.size(); ++k) for (m = 0; m < vFormFloras[k].IncludePatats.size(); ++m) if (PrimRegion.VPaths[j].getName() == vFormFloras[k].IncludePatats[m]) pPath = &PrimRegion.VPaths[j]; delAllIGZoneUnderPath (_Options->CellSize, pPath, _OutIGDir); } } // End of Process if a prim has been modified } // End of process all regions // Check all patat to export (a patat that has no zone under itself (deleted or not present)) vector<SExportPrimitive> vExportPrimitives; vector<string> vAllPrimFiles; // All prim files of a continent CTools::chdir (sContinentDir); CTools::dirSub ("*.prim", vAllPrimFiles, true); for (i = 0; i < vAllPrimFiles.size(); ++i) { vAllPrimFiles[i] = strlwr(vAllPrimFiles[i]); // Load the primfile CPrimRegion PrimRegion; { CIFile fileIn; fileIn.open (vAllPrimFiles[i]); CIXml input; input.init (fileIn); PrimRegion.serial (input); } for (j = 0; j < PrimRegion.VZones.size(); ++j) { // Check all zones to know if this patat must be updated if (isPatatNeedUpdate(_Options->CellSize, &PrimRegion.VZones[j], _OutIGDir)) { SExportPrimitive epTmp; epTmp.FullPrimName = vAllPrimFiles[i]; epTmp.PrimitiveName = PrimRegion.VZones[j].getName(); vExportPrimitives.push_back (epTmp); } } for (j = 0; j < PrimRegion.VPaths.size(); ++j) { // Check all pathes to know if some must be updated (if no zone under) if (isPathNeedUpdate(_Options->CellSize, &PrimRegion.VPaths[j], _OutIGDir)) { SExportPrimitive epTmp; epTmp.FullPrimName = vAllPrimFiles[i]; epTmp.PrimitiveName = PrimRegion.VPaths[j].getName(); vExportPrimitives.push_back (epTmp); } } for (j = 0; j < PrimRegion.VPoints.size(); ++j) { // Check all points to know if some must be updated (if no zone under) if (isPointNeedUpdate(_Options->CellSize, &PrimRegion.VPoints[j], _OutIGDir)) { SExportPrimitive epTmp; epTmp.FullPrimName = vAllPrimFiles[i]; epTmp.PrimitiveName = PrimRegion.VPoints[j].getName(); vExportPrimitives.push_back (epTmp); } } } // Export CTools::chdir (_ExeDir); _Options->PrimFloraDir = sContinentDir; sTmp = "Exporting"; if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp); doExport (*_Options, _ExportCB, &vExportPrimitives); // Copy new files for incremental purpose CTools::chdir (_ExeDir); sTmp = "Incrementing"; if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp); for (nRegion = 0; nRegion < vRegions.size(); ++nRegion) { CTools::chdir (sContinentDir + vRegions[nRegion]); vector<string> vFiles; CTools::dirSub ("*.prim", vFiles, true); for (i = 0; i < vFiles.size(); ++i) { string sDst = vFiles[i].substr (sContinentDir.size()); for (j = 0; j < sDst.size(); ++j) if (sDst[j] == '\\') sDst[j] = '-'; sDst = _OutIGDir + sDst; CTools::copy (sDst, vFiles[i]); } CTools::chdir (sContinentDir + vRegions[nRegion]); CTools::dirSub ("*.flora", vFiles, true); for (i = 0; i < vFiles.size(); ++i) { string sDst = vFiles[i].substr (sContinentDir.size()); for (j = 0; j < sDst.size(); ++j) if (sDst[j] == '\\') sDst[j] = '-'; sDst = _OutIGDir + sDst; CTools::copy (sDst, vFiles[i]); } // ------------------------------------------------------------------- // If the new file do not exists anymore but the old file exists -> delete old file CTools::chdir (_OutIGDir); CTools::dir (vRegions[nRegion] + "-*.prim", vFiles, false); for (i = 0; i < vFiles.size(); ++i) { // Get the name of the recent file string sNewName = vFiles[i]; for (j = 0; j < sNewName.size(); ++j) if (sNewName[j] == '-') sNewName[j] = '\\'; sNewName = sContinentDir + sNewName; if (!CTools::fileExist(sNewName)) { // Delete the oldest file CFile::deleteFile(vFiles[i]); } } // If the new file do not exists anymore but the old file exists -> delete old file CTools::dir (vRegions[nRegion] + "-*.flora", vFiles, false); for (i = 0; i < vFiles.size(); ++i) { // Get the name of the recent file string sNewName = vFiles[i]; for (j = 0; j < sNewName.size(); ++j) if (sNewName[j] == '-') sNewName[j] = '\\'; sNewName = sContinentDir + sNewName; if (!CTools::fileExist(sNewName)) { // Delete the oldest file CFile::deleteFile(vFiles[i]); } } } CTools::chdir (_ExeDir); return true; } // --------------------------------------------------------------------------- bool CExport::doExport (SExportOptions &opt, IExportCB *expCB, vector<SExportPrimitive> *selection) { char sTmp[MAX_PATH]; GetCurrentDirectory (MAX_PATH, sTmp); _Options = &opt; _ExportCB = expCB; // Does we have something to export if ((selection != NULL) && (selection->size() == 0)) { if (_ExportCB) _ExportCB->dispInfo ("Nothing to export"); return true; } // If we want to generate flora then we have to load the landscape uint32 i; if (_ExportCB) _ExportCB->dispPass ("Load Landscape"); if (_Landscape == NULL) { _Landscape = new CLandscape; _Landscape->init (); _VCM = new CVisualCollisionManager; _VCE = _VCM->createEntity (); _VCM->setLandscape (_Landscape); _VCE->setSnapToRenderedTesselation (false); try { CIFile bankFile (_LandBankFile); _Landscape->TileBank.serial (bankFile); CIFile farbankFile (_LandFarBankFile); _Landscape->TileFarBank.serial (farbankFile); _Landscape->TileBank.makeAllPathRelative (); _Landscape->TileBank.setAbsPath (""); _Landscape->TileBank.makeAllExtensionDDS (); _Landscape->initTileBanks (); loadLandscape (_LandFile); } catch (const Exception &/*e*/) { if (_ExportCB) _ExportCB->dispError ("Cannot load banks files"); } } _FloraInsts.clear (); if (_ExportCB) _ExportCB->dispPass ("Generate Flora"); vector<string> allFloraFiles; SetCurrentDirectory (_Options->PrimFloraDir.c_str()); getAllFiles (".Flora", allFloraFiles); SetCurrentDirectory (sTmp); for (i = 0; i < allFloraFiles.size(); ++i) { generateIGFromFlora (allFloraFiles[i], selection); } writeFloraIG (_LandFile, (selection != NULL)); // If selection != NULL then test for writing SetCurrentDirectory (sTmp); if (_ExportCB) _ExportCB->dispPass ("Finished"); return true; } // --------------------------------------------------------------------------- void CExport::getAllFiles (const string &ext, vector<string> &files) { char sCurDir[MAX_PATH]; GetCurrentDirectory (MAX_PATH, sCurDir); WIN32_FIND_DATA findData; HANDLE hFind = FindFirstFile ("*.*", &findData); while (hFind != INVALID_HANDLE_VALUE) { if (!((strcmp (findData.cFileName, ".") == 0) || (strcmp (findData.cFileName, "..") == 0))) { if (findData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) { string sNewDir = sCurDir; sNewDir += string("\\") + findData.cFileName; SetCurrentDirectory (sNewDir.c_str()); getAllFiles (ext, files); SetCurrentDirectory (sCurDir); } else { if (strlen(findData.cFileName) > strlen(ext.c_str())) if (stricmp(&findData.cFileName[strlen(findData.cFileName)-strlen(ext.c_str())], ext.c_str()) == 0) { string fullName = sCurDir; fullName += string("\\") + findData.cFileName; fullName = strlwr (fullName); files.push_back (fullName); } } } if (FindNextFile (hFind, &findData) == 0) break; } FindClose (hFind); SetCurrentDirectory (sCurDir); } // --------------------------------------------------------------------------- bool CExport::searchFile (const std::string &plantName, std::string &dir) { char sCurDir[MAX_PATH]; bool bFound = false; GetCurrentDirectory (MAX_PATH, sCurDir); WIN32_FIND_DATA findData; HANDLE hFind = FindFirstFile ("*.*", &findData); while (hFind != INVALID_HANDLE_VALUE) { string filename = findData.cFileName; filename = strlwr(filename); if (!((filename == ".") || (filename == ".."))) { if (findData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) { string sNewDir = sCurDir; sNewDir += string("\\") + filename; SetCurrentDirectory (sNewDir.c_str()); if (searchFile (plantName, dir)) { bFound = true; break; } SetCurrentDirectory (sCurDir); } else { if (strlwr(plantName) == filename) { dir = sCurDir; bFound = true; break; } } } if (FindNextFile (hFind, &findData) == 0) break; } FindClose (hFind); SetCurrentDirectory (sCurDir); return bFound; } // --------------------------------------------------------------------------- bool CExport::generateIGFromFlora (const std::string &SrcFile, std::vector<SExportPrimitive> *selection) { uint32 i, j, k, l, m; if (_ExportCB) _ExportCB->dispPass ("Generating From " + SrcFile); // Load all .prim vector<CPrimRegion> allPrimRegion; vector<string> allPrimFiles; { char sCurDir[MAX_PATH]; GetCurrentDirectory (MAX_PATH, sCurDir); SetCurrentDirectory (_Options->PrimFloraDir.c_str()); getAllFiles (".prim", allPrimFiles); SetCurrentDirectory (sCurDir); for (i = 0; i < allPrimFiles.size(); ++i) { try { CPrimRegion tmpPrimRegion; CIFile fileIn; fileIn.open (allPrimFiles[i]); CIXml input; input.init (fileIn); tmpPrimRegion.serial (input); allPrimRegion.push_back (tmpPrimRegion); } catch (const Exception &/*e*/) { if (_ExportCB != NULL) _ExportCB->dispWarning (string("Cant load ") + allPrimFiles[i]); CPrimRegion tmpPrimRegion; allPrimRegion.push_back (tmpPrimRegion); } } } // Load the .Flora file (georges file) and load all associated .plant SFormFlora formFlora; map<string, SFormPlant> Plants; { // Create a loader UFormLoader *loader = UFormLoader::createLoader (); // CPath::addSearchPath (_Options->DfnDir, true, true); // CPath::addSearchPath (_Options->GameElemDir, true, true); // CPath::addSearchPath (_Options->LandTileNoiseDir, true, true); // Load the form CSmartPtr<UForm> form = loader->loadForm (SrcFile.c_str ()); if (form == NULL) { if (_ExportCB != NULL) _ExportCB->dispError (string("Cant load ") + SrcFile ); } else { // Read .Flora formFlora.build (form->getRootNode ()); // Load the .plant associated for (i = 0; i < formFlora.PlantInstances.size(); ++i) { const string &plantName = formFlora.PlantInstances[i].Name; map<string, SFormPlant>::iterator it = Plants.find (plantName); if (it != Plants.end()) // Already here ?! continue; // Zap it char sCurDir[MAX_PATH]; GetCurrentDirectory (MAX_PATH, sCurDir); try { SetCurrentDirectory (_GameElemDir.c_str()); string dir; if (searchFile (plantName, dir)) { string tmpName = dir + string("\\") + plantName; CSmartPtr<UForm> form2 = loader->loadForm (tmpName.c_str()); if (form2) { SFormPlant plantTmp; plantTmp.build (form2->getRootNode ()); Plants.insert (map<string, SFormPlant>::value_type(plantName, plantTmp)); } else { if (_ExportCB != NULL) _ExportCB->dispWarning (string("Cant load ") + plantName); } } else { if (_ExportCB != NULL) _ExportCB->dispWarning (string("Cant load ") + plantName); } SetCurrentDirectory (sCurDir); } catch (const Exception &e) { SetCurrentDirectory (sCurDir); if (_ExportCB != NULL) _ExportCB->dispWarning (string("Cant load ") + plantName + "(" + e.what() + ")" ); } } } } // Sort PlantInstances by biggest radius first if (formFlora.PlantInstances.size() > 1) for (i = 0; i < (formFlora.PlantInstances.size()-1); ++i) for (j = i+1; j < formFlora.PlantInstances.size(); ++j) { SPlantInstance &rPlantI = formFlora.PlantInstances[i]; SPlantInstance &rPlantJ = formFlora.PlantInstances[j]; map<string, SFormPlant>::iterator it = Plants.find (rPlantI.Name); if (it == Plants.end()) continue; SFormPlant &rFormPlantI = it->second; it = Plants.find (rPlantJ.Name); if (it == Plants.end()) continue; SFormPlant &rFormPlantJ = it->second; if (rFormPlantI.BoundingRadius < rFormPlantJ.BoundingRadius) { SPlantInstance pi = formFlora.PlantInstances[i]; formFlora.PlantInstances[i] = formFlora.PlantInstances[j]; formFlora.PlantInstances[j] = pi; } } // Generating float jitter = formFlora.JitterPos; clamp (jitter, 0.0f, 1.0f); srand (formFlora.RandomSeed); for (i = 0; i < formFlora.IncludePatats.size(); ++i) { uint32 nCurPlant = 0; CVector vMin, vMax; if (_ExportCB) _ExportCB->dispPass ("IncludePatats("+toString(i+1)+"/"+toString(formFlora.IncludePatats.size())+")"); // Get the patat CPrimZone *pPatat = NULL; CPrimPoint *pPoint = NULL; CPrimPath *pPath = NULL; // Look if this is a patat for (j = 0; j < allPrimRegion.size(); ++j) { for (k = 0; k < allPrimRegion[j].VZones.size(); ++k) { if (allPrimRegion[j].VZones[k].getName() == formFlora.IncludePatats[i]) { if (selection != NULL) { SExportPrimitive epTmp; epTmp.FullPrimName = allPrimFiles[j]; epTmp.PrimitiveName = allPrimRegion[j].VZones[k].getName(); for (m = 0; m < selection->size(); ++m) if (selection->operator[](m) == epTmp) { pPatat = &allPrimRegion[j].VZones[k]; break; } } else { pPatat = &allPrimRegion[j].VZones[k]; } } } } //Look if this is a point for (j = 0; j < allPrimRegion.size(); ++j) { for (k = 0; k < allPrimRegion[j].VPoints.size(); ++k) { if (allPrimRegion[j].VPoints[k].getName() == formFlora.IncludePatats[i]) { if (selection != NULL) { SExportPrimitive epTmp; epTmp.FullPrimName = allPrimFiles[j]; epTmp.PrimitiveName = allPrimRegion[j].VPoints[k].getName(); for (m = 0; m < selection->size(); ++m) if (selection->operator[](m) == epTmp) { pPoint = &allPrimRegion[j].VPoints[k]; break; } } else { pPoint = &allPrimRegion[j].VPoints[k]; } } } } //Look if this is a path for (j = 0; j < allPrimRegion.size(); ++j) { for (k = 0; k < allPrimRegion[j].VPaths.size(); ++k) { if (allPrimRegion[j].VPaths[k].getName() == formFlora.IncludePatats[i]) { if (selection != NULL) { SExportPrimitive epTmp; epTmp.FullPrimName = allPrimFiles[j]; epTmp.PrimitiveName = allPrimRegion[j].VPaths[k].getName(); for (m = 0; m < selection->size(); ++m) if (selection->operator[](m) == epTmp) { pPath = &allPrimRegion[j].VPaths[k]; break; } } else { pPath = &allPrimRegion[j].VPaths[k]; } } } } if ((pPatat == NULL) && (pPoint == NULL) && (pPath == NULL)) { if (selection == NULL) if (_ExportCB) _ExportCB->dispWarning ("Cannot find " + formFlora.IncludePatats[i]); continue; } if ((pPatat != NULL) && (pPatat->VPoints.size() <= 2)) { if (_ExportCB) _ExportCB->dispWarning ("Patat " + pPatat->getName() + " has less than 3 points"); continue; } if ((pPath != NULL) && (pPath->VPoints.size() <= 1)) { if (_ExportCB) _ExportCB->dispWarning ("Path " + pPath->getName() + " has less than 2 points"); continue; } // Generate for a patat if (pPatat != NULL) { vMin = vMax = pPatat->VPoints[0]; for (j = 0; j < pPatat->VPoints.size(); ++j) { if (vMin.x > pPatat->VPoints[j].x) vMin.x = pPatat->VPoints[j].x; if (vMin.y > pPatat->VPoints[j].y) vMin.y = pPatat->VPoints[j].y; if (vMin.z > pPatat->VPoints[j].z) vMin.z = pPatat->VPoints[j].z; if (vMax.x < pPatat->VPoints[j].x) vMax.x = pPatat->VPoints[j].x; if (vMax.y < pPatat->VPoints[j].y) vMax.y = pPatat->VPoints[j].y; if (vMax.z < pPatat->VPoints[j].z) vMax.z = pPatat->VPoints[j].z; } for (j = 0; j < formFlora.PlantInstances.size(); ++j) { SPlantInstance &rPlant = formFlora.PlantInstances[j]; map<string, SFormPlant>::iterator it = Plants.find (rPlant.Name); if (it == Plants.end()) { if (_ExportCB) _ExportCB->dispWarning ("Cannot find " + rPlant.Name); continue; } SFormPlant &rFormPlant = it->second; float squareLength = (float)sqrt (Pi*rFormPlant.BoundingRadius*rFormPlant.BoundingRadius / rPlant.Density); uint32 nNbPlantX = 1+(int)floor ((vMax.x-vMin.x) / squareLength); uint32 nNbPlantY = 1+(int)floor ((vMax.y-vMin.y) / squareLength); for (l = 0; l < nNbPlantY; ++l) for (k = 0; k < nNbPlantX; ++k) { if (_ExportCB) _ExportCB->dispPassProgress (((float)(k+l*nNbPlantX))/((float)(nNbPlantX*nNbPlantY))); bool bExists = false; CVector pos; float scaleTmp; for (m = 0; m < 32; ++m) { pos.x = vMin.x + squareLength * k + (frand(2.0f)-1.0f) * jitter * 0.5f * squareLength; pos.y = vMin.y + squareLength * l + (frand(2.0f)-1.0f) * jitter * 0.5f * squareLength; pos.z = 0.0f; scaleTmp = (formFlora.ScaleMax-formFlora.ScaleMin)*frand(1.0)+formFlora.ScaleMin; if (pPatat->contains(pos)) { if (isWellPlaced(pos, rPlant, rFormPlant, scaleTmp)) { // Testt finally with the exclude patats ... bExists = true; for (uint32 expat = 0; expat < formFlora.ExcludePatats.size(); ++expat) { CPrimZone *pExPatat = NULL; for (uint32 epj = 0; epj < allPrimRegion.size(); ++epj) for (uint32 epk = 0; epk < allPrimRegion[epj].VZones.size(); ++epk) if (allPrimRegion[epj].VZones[epk].getName() == formFlora.ExcludePatats[expat]) { pExPatat = &allPrimRegion[epj].VZones[epk]; break; } if (pExPatat != NULL) { if (pExPatat->contains(pos)) { bExists = false; break; } } } if (bExists) break; } } } if (!bExists) continue; SFloraInst vi; vi.ShapeName = rFormPlant.Shape; vi.PlantName = rPlant.Name; vi.Scale = scaleTmp; vi.Radius = rFormPlant.BoundingRadius * vi.Scale; vi.Rot = (float)Pi * frand (1.0); if (formFlora.PutOnWater) { if (pos.z < formFlora.WaterHeight) pos.z = formFlora.WaterHeight; } vi.Pos = pos; _FloraInsts.push_back (vi); } // End of for all position of a plant x,y check if we cant put it } // End of for all plant instances } // End of Generate for a patat // Generate for a point if (pPoint != NULL) { // Choose a plant float total = 0.0f; for (j = 0; j < formFlora.PlantInstances.size(); ++j) { total += formFlora.PlantInstances[j].Density; } float posf = total * frand(1.0); total = 0.0f; for (j = 0; j < formFlora.PlantInstances.size(); ++j) { total += formFlora.PlantInstances[j].Density; if (posf < total) break; } if (j == formFlora.PlantInstances.size()) j = (uint32)formFlora.PlantInstances.size()-1; SPlantInstance &rPlant = formFlora.PlantInstances[j]; map<string, SFormPlant>::iterator it = Plants.find (rPlant.Name); if (it == Plants.end()) { if (_ExportCB) _ExportCB->dispWarning ("Cannot find " + rPlant.Name); continue; } SFormPlant &rFormPlant = it->second; SFloraInst vi; vi.ShapeName = rFormPlant.Shape; vi.PlantName = rPlant.Name; vi.Scale = (formFlora.ScaleMax-formFlora.ScaleMin)*frand(1.0)+formFlora.ScaleMin; vi.Radius = rFormPlant.BoundingRadius * vi.Scale; vi.Rot = (float)Pi * frand (1.0); CVector pos; pos.x = pPoint->Point.x; pos.y = pPoint->Point.y; pos.z = getZFromXY (pos.x, pos.y); if (formFlora.PutOnWater) { if (pos.z < formFlora.WaterHeight) pos.z = formFlora.WaterHeight; } vi.Pos = pos; if (pos.z > -90000.0f) _FloraInsts.push_back (vi); } // End of Generate for a point // Generate for a path if (pPath != NULL) { float rLength = 0.0f; // Total length of the broken line for (j = 0; j < pPath->VPoints.size()-1; ++j) { rLength += (pPath->VPoints[j]-pPath->VPoints[j+1]).norm(); } for (j = 0; j < formFlora.PlantInstances.size(); ++j) { SPlantInstance &rPlant = formFlora.PlantInstances[j]; map<string, SFormPlant>::iterator it = Plants.find (rPlant.Name); if (it == Plants.end()) { if (_ExportCB) _ExportCB->dispWarning ("Cannot find " + rPlant.Name); continue; } SFormPlant &rFormPlant = it->second; float squareLength = (float)(2*rFormPlant.BoundingRadius / rPlant.Density); uint32 nNbPlant = 1+(int)floor (rLength / squareLength); for (k = 0; k < nNbPlant; ++k) { if (_ExportCB) _ExportCB->dispPassProgress (((float)(k))/((float)(nNbPlant))); bool bExists = false; CVector pos; float scaleTmp; for (m = 0; m < 32; ++m) { // Calculate the curviline abscisse float curvAbs = squareLength * k + (frand(2.0f)-1.0f) * jitter * 0.5f * squareLength; float TempLength = 0.0f; // Convert to a real point along the curve (broken line) for (l = 0; l < pPath->VPoints.size()-1; ++l) { float newSize = (pPath->VPoints[l]-pPath->VPoints[l+1]).norm(); if (curvAbs < (TempLength+newSize)) { curvAbs -= TempLength; break; } TempLength += newSize; } if (l == (pPath->VPoints.size()-1)) { l = (uint32)pPath->VPoints.size()-2; curvAbs = (pPath->VPoints[l]-pPath->VPoints[l+1]).norm(); } // Calculate the coord curvAbs = curvAbs / (pPath->VPoints[l]-pPath->VPoints[l+1]).norm(); pos = pPath->VPoints[l] + (pPath->VPoints[l+1]-pPath->VPoints[l])*curvAbs; pos.z = 0.0f; scaleTmp = (formFlora.ScaleMax-formFlora.ScaleMin)*frand(1.0)+formFlora.ScaleMin; if (isWellPlaced(pos, rPlant, rFormPlant, scaleTmp)) { // Test finally with the exclude patats ... bExists = true; for (uint32 expat = 0; expat < formFlora.ExcludePatats.size(); ++expat) { CPrimZone *pExPatat = NULL; for (uint32 epj = 0; epj < allPrimRegion.size(); ++epj) for (uint32 epk = 0; epk < allPrimRegion[epj].VZones.size(); ++epk) if (allPrimRegion[epj].VZones[epk].getName() == formFlora.ExcludePatats[expat]) { pExPatat = &allPrimRegion[epj].VZones[epk]; break; } if (pExPatat != NULL) { if (pExPatat->contains(pos)) { bExists = false; break; } } } if (bExists) break; } } if (!bExists) continue; SFloraInst vi; vi.ShapeName = rFormPlant.Shape; vi.PlantName = rPlant.Name; vi.Scale = scaleTmp; vi.Radius = rFormPlant.BoundingRadius * vi.Scale; vi.Rot = (float)Pi * frand (1.0); if (formFlora.PutOnWater) { if (pos.z < formFlora.WaterHeight) pos.z = formFlora.WaterHeight; } vi.Pos = pos; _FloraInsts.push_back (vi); } // End of for all position of a plant x,y check if we cant put it } // End of for all plant instances } // End of Generate for a path } // End of for all IncludePatats return true; } // --------------------------------------------------------------------------- float CExport::getZFromXY (float x, float y) { CVector pos = CVector(x, y, 0); CVector normal; float z, zmin, zmax; // Approximate the z with patch bounding boxes sint32 zoneX = (sint32)floor (x/_Options->CellSize); sint32 zoneY = (sint32)floor (-y/_Options->CellSize); sint32 zoneId = zoneY * 256 + zoneX; CZone *pZone = _Landscape->getZone (zoneId); if (pZone == NULL) return -100000.0f; CAABBoxExt bb = pZone->getZoneBB(); zmin = bb.getMin().z; zmax = bb.getMax().z; pos.z = zmin; z = zmin; while (z < zmax) { if (_VCE->snapToGround(pos, normal)) break; z += CVisualCollisionEntity::BBoxRadiusZ / 2.0f; // Super sampling due to max frequency on radiosity pos.z = z; } if (z >= zmax) return -100000.0f; return pos.z; } // --------------------------------------------------------------------------- bool CExport::isWellPlaced (CVector &pos, SPlantInstance &rPI, SFormPlant &rFP, float scale) { uint32 i; // Look if this Flora intersect with one of the current ones for (i = 0; i < _FloraInsts.size(); ++i) { CVector temp = _FloraInsts[i].Pos - pos; temp.z = 0.0f; if (temp.norm() < (_FloraInsts[i].Radius + scale*rFP.BoundingRadius)) return false; } // Get the real Z pos.z = getZFromXY (pos.x, pos.y); if (pos.z < -90000.0f) return false; // Get some Z around to see if we can put the Flora on the ground uint32 nNbSamples = 8; // Const to be put somewhere vector<CVector> base; base.resize (nNbSamples); for (i = 0; i < nNbSamples; ++i) { base[i] = pos; base[i].x += scale * rFP.CollisionRadius * cosf((2.0f*(float)Pi*i)/(float)nNbSamples); base[i].y += scale * rFP.CollisionRadius * sinf((2.0f*(float)Pi*i)/(float)nNbSamples); base[i].z = getZFromXY (base[i].x, base[i].y); if (fabs(base[i].z-pos.z) > 0.8f) return false; } return true; } // --------------------------------------------------------------------------- void CExport::writeFloraIG (const string &LandFile, bool bTestForWriting) { sint32 i, j, k; if (_FloraInsts.size() == 0) return; CZoneRegion zoneRegion; CIFile inFile; if (inFile.open (LandFile)) { CIXml xml(true); xml.init (inFile); zoneRegion.serial (xml); inFile.close(); } else { nlwarning ("Can't open the file %s", LandFile.c_str()); } // Load all zone for (j = zoneRegion.getMinY(); j <= zoneRegion.getMaxY(); ++j) for (i = zoneRegion.getMinX(); i <= zoneRegion.getMaxX(); ++i) { if ((zoneRegion.getName(i,j) == STRING_OUT_OF_BOUND) || (zoneRegion.getName(i,j) == STRING_UNUSED)) continue; vector<int> vegZone; // Take all Flora instances in the zone (i,j) for (k = 0; k < (sint32)_FloraInsts.size(); ++k) { if (((i*_Options->CellSize) < _FloraInsts[k].Pos.x) && (_FloraInsts[k].Pos.x < ((i+1)*_Options->CellSize)) && ((j*_Options->CellSize) < _FloraInsts[k].Pos.y) && (_FloraInsts[k].Pos.y < ((j+1)*_Options->CellSize))) { vegZone.push_back (k); } } // Make the .IG string ZoneName; ZoneName += NLMISC::toString(-j) + "_"; ZoneName += 'a' + (i/26); ZoneName += 'a' + (i%26); CVector vGlobalPos = CVector (0.0f, 0.0f, 0.0f); CInstanceGroup::TInstanceArray Instances; vector<CCluster> Portals; vector<CPortal> Clusters; Instances.resize (vegZone.size()); for (k = 0; k < (sint32)vegZone.size(); ++k) { //vGlobalPos += _FloraInsts[vegZone[k]].Pos; Instances[k].Pos = _FloraInsts[vegZone[k]].Pos; Instances[k].Rot = CQuat(CVector::K, _FloraInsts[vegZone[k]].Rot); Instances[k].Scale = CVector(_FloraInsts[vegZone[k]].Scale, _FloraInsts[vegZone[k]].Scale, _FloraInsts[vegZone[k]].Scale); Instances[k].nParent = -1; Instances[k].Name = _FloraInsts[vegZone[k]].ShapeName; Instances[k].InstanceName = _FloraInsts[vegZone[k]].PlantName; /*Instances[k].InstanceName = "Flora_"; // see if it works Instances[k].InstanceName += ZoneName + "_"; Instances[k].InstanceName += '0' + ((k/1000)%10); Instances[k].InstanceName += '0' + ((k/100) %10); Instances[k].InstanceName += '0' + ((k/10) %10); Instances[k].InstanceName += '0' + ( k %10);*/ } // \todo trap -> look why it dont seems to work with a global positionning //vGlobalPos /= (float)vegZone.size(); //for (k = 0; k < (sint32)vegZone.size(); ++k) // Instances[k].Pos -= vGlobalPos; CInstanceGroup IG; IG.build (vGlobalPos, Instances, Portals, Clusters); ZoneName = _OutIGDir + "\\" + ZoneName; ZoneName += ".ig"; CIFile inFile; // If file already exists and we have selection... if (bTestForWriting) if (inFile.open(ZoneName)) { inFile.close(); continue; } try { COFile outFile (ZoneName); IG.serial (outFile); if (_ExportCB != NULL) _ExportCB->dispInfo (ZoneName + " generated"); } catch (const Exception &e) { if (_ExportCB != NULL) _ExportCB->dispWarning ("Cant write " + ZoneName + " (" + e.what() + ")"); } } } // --------------------------------------------------------------------------- void CExport::loadLandscape (const string &LandFile) { CZoneRegion zoneRegion; CIFile inFile; try { if (inFile.open (LandFile)) { CIXml xml(true); xml.init (inFile); zoneRegion.serial (xml); inFile.close(); } else { if (_ExportCB != NULL) _ExportCB->dispWarning (string("Can't open file ") + LandFile); } } catch (const Exception &e) { if (_ExportCB != NULL) _ExportCB->dispWarning (string("Cant load ") + LandFile + " : " + e.what()); } // Load all zone sint32 nTotalFile = (1 + zoneRegion.getMaxY() - zoneRegion.getMinY()) * (1 + zoneRegion.getMaxX() - zoneRegion.getMinX()); sint32 nCurrentFile = 0; for (sint32 j = zoneRegion.getMinY(); j <= zoneRegion.getMaxY(); ++j) for (sint32 i = zoneRegion.getMinX(); i <= zoneRegion.getMaxX(); ++i) { ++nCurrentFile; if (_ExportCB != NULL) _ExportCB->dispPassProgress(((float)nCurrentFile)/((float)nTotalFile)); if ((zoneRegion.getName(i,j) == STRING_OUT_OF_BOUND) || (zoneRegion.getName(i,j) == STRING_UNUSED)) continue; // Generate zone name string ZoneName = getZoneNameFromXY (i, j); ZoneName = _InLandscapeDir + string("\\") + ZoneName; //if (_ExportCB != NULL) // _ExportCB->dispInfo (string("Loading ") + ZoneName); try { CZone zone; if (!inFile.open (ZoneName + string(".zonew"))) inFile.open (ZoneName + string(".zonel")); zone.serial (inFile); inFile.close (); _Landscape->addZone (zone); } catch(const Exception &/*e*/) { if (_ExportCB != NULL) _ExportCB->dispWarning (string("Cant load ") + ZoneName + string(".zone(l,w)")); } if ((_ExportCB != NULL) && (_ExportCB->isCanceled())) return; } } // Public Helpers // ************** // --------------------------------------------------------------------------- string CExport::getZoneNameFromXY (sint32 x, sint32 y) { string tmp; if ((y>0) || (y<-255) || (x<0) || (x>255)) return "NOT VALID"; tmp = toString(-y) + "_"; tmp += ('A' + (x/26)); tmp += ('A' + (x%26)); return tmp; } // --------------------------------------------------------------------------- sint32 CExport::getXFromZoneName (const string &ZoneName) { string xStr, yStr; uint32 i = 0; while (ZoneName[i] != '_') { yStr += ZoneName[i]; ++i; if (i == ZoneName.size()) return -1; } ++i; while (i < ZoneName.size()) { xStr += ZoneName[i]; ++i; } return ((xStr[0] - 'A')*26 + (xStr[1] - 'A')); } // --------------------------------------------------------------------------- sint32 CExport::getYFromZoneName (const string &ZoneName) { string xStr, yStr; uint32 i = 0; while (ZoneName[i] != '_') { yStr += ZoneName[i]; ++i; if (i == ZoneName.size()) return 1; } ++i; while (i < ZoneName.size()) { xStr += ZoneName[i]; ++i; } return -atoi(yStr.c_str()); }