Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / ryzom / tools / leveldesign / export / export.cpp
blobaed92db319b7151a6b921ebb2cb92b8ee7d809ae
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010 Matt RAYKOWSKI (sfb) <matt.raykowski@gmail.com>
6 // Copyright (C) 2016 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "export.h"
22 #include "formFlora.h"
23 #include "formPlant.h"
24 #ifdef NL_OS_WINDOWS
25 #define NOMINMAX
26 #include <windows.h>
27 #endif // NL_OS_WINDOWS
29 #include "nel/ligo/zone_region.h"
30 #include "nel/ligo/primitive.h"
32 #include "nel/georges/u_form_loader.h"
33 #include "nel/georges/u_form.h"
34 #include "nel/georges/u_form_elm.h"
36 #include "nel/3d/zone.h"
37 #include "nel/3d/landscape.h"
38 #include "nel/3d/scene_group.h"
39 #include "nel/3d/visual_collision_manager.h"
40 #include "nel/3d/visual_collision_entity.h"
42 #include "nel/misc/o_xml.h"
43 #include "nel/misc/i_xml.h"
44 #include "nel/misc/path.h"
45 #include "nel/misc/file.h"
47 #include "tools.h"
48 #include "../master/ContinentCfg.h"
50 using namespace std;
51 using namespace NLMISC;
52 using namespace NL3D;
53 using namespace NLLIGO;
54 using namespace NLGEORGES;
56 #define MAX_SYS_DIR 6
57 const char *gExportSysDir[MAX_SYS_DIR] =
59 ".",
60 "..",
61 "ZoneBitmaps",
62 "ZoneLigos",
63 "dfn",
64 "tmp"
68 // ---------------------------------------------------------------------------
69 // ---------------------------------------------------------------------------
70 // ---------------------------------------------------------------------------
71 // ---------------------------------------------------------------------------
73 // -----------------------------------------------------------------------------------------------
74 // Segment line intersection P1P2 and P3P4
75 bool CExport::segmentIntersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
77 double denominator = (y4-y3)*(x2-x1) - (x4-x3)*(y2-y1);
78 if( denominator == 0.0 )
79 return false; // The segment are colinear
80 double k = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3) ) / denominator;
81 if( (k<=0.0) || (k>=1.0) ) return false;
82 k = ( (x2-x1)*(y1-y3) - (y2-y1)*(x1-x3) ) / denominator;
83 if( (k<=0.0) || (k>=1.0) ) return false;
84 return true;
87 // ---------------------------------------------------------------------------
88 void CExport::delIGZone (sint32 x, sint32 y)
90 string sZoneName = CExport::getZoneNameFromXY (x, y);
91 sZoneName += ".ig";
92 if (CFile::deleteFile(sZoneName))
94 CTools::chdir (_ExeDir);
95 string sTmp = string(" zone ") + sZoneName + " deleted";
96 if (_ExportCB != NULL)
97 _ExportCB->dispInfo(sTmp);
101 // ---------------------------------------------------------------------------
102 void CExport::delAllIGZoneUnderPoint (float fCellSize, CPrimPoint *pPoint, const string &sIGOutputDir)
104 if (pPoint == NULL) return;
105 sint32 nX, nY;
106 CTools::chdir (sIGOutputDir);
107 nX = (sint32) floor (pPoint->Point.x / fCellSize);
108 nY = (sint32) floor (pPoint->Point.y / fCellSize);
109 delIGZone (nX, nY);
110 CTools::chdir (sIGOutputDir);
113 // ---------------------------------------------------------------------------
114 void CExport::delAllIGZoneUnderPath (float fCellSize, CPrimPath *pPath, const string &sIGOutputDir)
116 if (pPath == NULL) return;
117 if (pPath->VPoints.empty()) return;
118 uint32 i, j;
119 CVector vMin, vMax;
121 CTools::chdir (sIGOutputDir);
122 vMin = vMax = pPath->VPoints[0];
123 for (i = 0; i < pPath->VPoints.size(); ++i)
125 if (vMin.x > pPath->VPoints[i].x) vMin.x = pPath->VPoints[i].x;
126 if (vMin.y > pPath->VPoints[i].y) vMin.y = pPath->VPoints[i].y;
127 if (vMin.z > pPath->VPoints[i].z) vMin.z = pPath->VPoints[i].z;
128 if (vMax.x < pPath->VPoints[i].x) vMax.x = pPath->VPoints[i].x;
129 if (vMax.y < pPath->VPoints[i].y) vMax.y = pPath->VPoints[i].y;
130 if (vMax.z < pPath->VPoints[i].z) vMax.z = pPath->VPoints[i].z;
133 sint32 x, y;
134 sint32 nMinX, nMinY, nMaxX, nMaxY;
135 nMinX = (sint32) floor (vMin.x / fCellSize);
136 nMinY = (sint32) floor (vMin.y / fCellSize);
137 nMaxX = (sint32) floor (vMax.x / fCellSize);
138 nMaxY = (sint32) floor (vMax.y / fCellSize);
140 for (x = nMinX; x <= nMaxX; ++x)
141 for (y = nMinY; y <= nMaxY; ++y)
143 // Does the zone (x,y) is under the patah ?
145 CVector vSquare[4];
146 vSquare[0].x = x * fCellSize;
147 vSquare[0].y = y * fCellSize;
148 vSquare[0].z = 0.0f;
150 vSquare[1].x = (x+1) * fCellSize;
151 vSquare[1].y = y * fCellSize;
152 vSquare[1].z = 0.0f;
154 vSquare[2].x = (x+1) * fCellSize;
155 vSquare[2].y = (y+1) * fCellSize;
156 vSquare[2].z = 0.0f;
158 vSquare[3].x = x * fCellSize;
159 vSquare[3].y = (y+1) * fCellSize;
160 vSquare[3].z = 0.0f;
162 // Is a vertex of the path inside the zone ?
163 for (i = 0; i < pPath->VPoints.size(); ++i)
165 if ((pPath->VPoints[i].x >= (x*fCellSize)) &&
166 (pPath->VPoints[i].x <= ((x+1)*fCellSize)) &&
167 (pPath->VPoints[i].y <= (y*fCellSize)) &&
168 (pPath->VPoints[i].y <= ((y+1)*fCellSize)))
170 delIGZone (x, y);
171 CTools::chdir (sIGOutputDir);
175 // Is an segment of the path cut an edge of the patat ?
176 for (i = 0; i < pPath->VPoints.size()-1; ++i)
177 for (j = 0; j < 4; ++j)
179 double x1 = vSquare[j].x;
180 double y1 = vSquare[j].y;
181 double x2 = vSquare[(j+1)%4].x;
182 double y2 = vSquare[(j+1)%4].y;
184 double x3 = pPath->VPoints[i].x;
185 double y3 = pPath->VPoints[i].y;
186 double x4 = pPath->VPoints[i+1].x;
187 double y4 = pPath->VPoints[i+1].y;
189 if (segmentIntersection(x1, y1, x2, y2, x3, y3, x4, y4))
191 delIGZone (x, y);
192 CTools::chdir (sIGOutputDir);
198 // ---------------------------------------------------------------------------
199 void CExport::delAllIGZoneUnderPatat (float fCellSize, CPrimZone *pPatat, const string &sIGOutputDir)
201 if (pPatat == NULL) return;
202 if (pPatat->VPoints.empty()) return;
203 uint32 i, j;
204 CVector vMin, vMax;
206 CTools::chdir (sIGOutputDir);
207 vMin = vMax = pPatat->VPoints[0];
208 for (i = 0; i < pPatat->VPoints.size(); ++i)
210 if (vMin.x > pPatat->VPoints[i].x) vMin.x = pPatat->VPoints[i].x;
211 if (vMin.y > pPatat->VPoints[i].y) vMin.y = pPatat->VPoints[i].y;
212 if (vMin.z > pPatat->VPoints[i].z) vMin.z = pPatat->VPoints[i].z;
213 if (vMax.x < pPatat->VPoints[i].x) vMax.x = pPatat->VPoints[i].x;
214 if (vMax.y < pPatat->VPoints[i].y) vMax.y = pPatat->VPoints[i].y;
215 if (vMax.z < pPatat->VPoints[i].z) vMax.z = pPatat->VPoints[i].z;
218 sint32 x, y;
219 sint32 nMinX, nMinY, nMaxX, nMaxY;
220 nMinX = (sint32) floor (vMin.x / fCellSize);
221 nMinY = (sint32) floor (vMin.y / fCellSize);
222 nMaxX = (sint32) floor (vMax.x / fCellSize);
223 nMaxY = (sint32) floor (vMax.y / fCellSize);
225 for (x = nMinX; x <= nMaxX; ++x)
226 for (y = nMinY; y <= nMaxY; ++y)
228 // Does the zone (x,y) is under the patat ?
230 // Is a vertex of the zone in the patat ?
231 CVector vSquare[4];
232 vSquare[0].x = x * fCellSize;
233 vSquare[0].y = y * fCellSize;
234 vSquare[0].z = 0.0f;
236 vSquare[1].x = (x+1) * fCellSize;
237 vSquare[1].y = y * fCellSize;
238 vSquare[1].z = 0.0f;
240 vSquare[2].x = (x+1) * fCellSize;
241 vSquare[2].y = (y+1) * fCellSize;
242 vSquare[2].z = 0.0f;
244 vSquare[3].x = x * fCellSize;
245 vSquare[3].y = (y+1) * fCellSize;
246 vSquare[3].z = 0.0f;
248 for (i = 0; i < 4; ++i)
250 if (pPatat->contains(vSquare[i]))
252 delIGZone (x, y);
253 CTools::chdir (sIGOutputDir);
257 // Is a vertex of the patat inside the zone ?
258 for (i = 0; i < pPatat->VPoints.size(); ++i)
260 if ((pPatat->VPoints[i].x >= (x*fCellSize)) &&
261 (pPatat->VPoints[i].x <= ((x+1)*fCellSize)) &&
262 (pPatat->VPoints[i].y <= (y*fCellSize)) &&
263 (pPatat->VPoints[i].y <= ((y+1)*fCellSize)))
265 delIGZone (x, y);
266 CTools::chdir (sIGOutputDir);
270 // Is an edge of the zone cut an edge of the patat ?
271 for (i = 0; i < pPatat->VPoints.size(); ++i)
272 for (j = 0; j < 4; ++j)
274 double x1 = vSquare[j].x;
275 double y1 = vSquare[j].y;
276 double x2 = vSquare[(j+1)%4].x;
277 double y2 = vSquare[(j+1)%4].y;
279 double x3 = pPatat->VPoints[i].x;
280 double y3 = pPatat->VPoints[i].y;
281 double x4 = pPatat->VPoints[(i+1)%pPatat->VPoints.size()].x;
282 double y4 = pPatat->VPoints[(i+1)%pPatat->VPoints.size()].y;
284 if (segmentIntersection(x1, y1, x2, y2, x3, y3, x4, y4))
286 delIGZone (x, y);
287 CTools::chdir (sIGOutputDir);
293 // ---------------------------------------------------------------------------
294 // A patat needs an update if there is at least one zone under itself that is not present
295 bool CExport::isPatatNeedUpdate (float fCellSize, CPrimZone *pPatat, const string &sIGOutputDir)
297 uint32 i, j;
298 CVector vMin, vMax;
300 CTools::chdir (sIGOutputDir);
301 if (pPatat->VPoints.empty())
302 return false;
303 vMin = vMax = pPatat->VPoints[0];
304 for (i = 0; i < pPatat->VPoints.size(); ++i)
306 if (vMin.x > pPatat->VPoints[i].x) vMin.x = pPatat->VPoints[i].x;
307 if (vMin.y > pPatat->VPoints[i].y) vMin.y = pPatat->VPoints[i].y;
308 if (vMin.z > pPatat->VPoints[i].z) vMin.z = pPatat->VPoints[i].z;
309 if (vMax.x < pPatat->VPoints[i].x) vMax.x = pPatat->VPoints[i].x;
310 if (vMax.y < pPatat->VPoints[i].y) vMax.y = pPatat->VPoints[i].y;
311 if (vMax.z < pPatat->VPoints[i].z) vMax.z = pPatat->VPoints[i].z;
314 sint32 x, y;
315 sint32 nMinX, nMinY, nMaxX, nMaxY;
316 nMinX = (sint32) floor (vMin.x / fCellSize);
317 nMinY = (sint32) floor (vMin.y / fCellSize);
318 nMaxX = (sint32) floor (vMax.x / fCellSize);
319 nMaxY = (sint32) floor (vMax.y / fCellSize);
321 for (x = nMinX; x <= nMaxX; ++x)
322 for (y = nMinY; y <= nMaxY; ++y)
324 // Does the zone (x,y) is under the patat ?
325 bool bZoneUnderPatat = false;
326 // Is a vertex of the zone in the patat ?
327 CVector vSquare[4];
328 vSquare[0].x = x * fCellSize;
329 vSquare[0].y = y * fCellSize;
330 vSquare[0].z = 0.0f;
332 vSquare[1].x = (x+1) * fCellSize;
333 vSquare[1].y = y * fCellSize;
334 vSquare[1].z = 0.0f;
336 vSquare[2].x = (x+1) * fCellSize;
337 vSquare[2].y = (y+1) * fCellSize;
338 vSquare[2].z = 0.0f;
340 vSquare[3].x = x * fCellSize;
341 vSquare[3].y = (y+1) * fCellSize;
342 vSquare[3].z = 0.0f;
344 for (i = 0; i < 4; ++i)
346 if (pPatat->contains(vSquare[i]))
348 string sTmp = CExport::getZoneNameFromXY(x,y) + ".ig";
349 if (!CTools::fileExist(sTmp)) // If the file does not exist
350 return true; // need update
354 // Is a vertex of the patat inside the zone ?
355 for (i = 0; i < pPatat->VPoints.size(); ++i)
357 if ((pPatat->VPoints[i].x >= (x*fCellSize)) &&
358 (pPatat->VPoints[i].x <= ((x+1)*fCellSize)) &&
359 (pPatat->VPoints[i].y >= (y*fCellSize)) &&
360 (pPatat->VPoints[i].y <= ((y+1)*fCellSize)))
362 string sTmp = CExport::getZoneNameFromXY(x,y) + ".ig";
363 if (!CTools::fileExist(sTmp)) // If the file does not exist
364 return true; // need update
368 // Is an edge of the zone cut an edge of the patat ?
369 for (i = 0; i < pPatat->VPoints.size(); ++i)
370 for (j = 0; j < 4; ++j)
372 double x1 = vSquare[j].x;
373 double y1 = vSquare[j].y;
374 double x2 = vSquare[(j+1)%4].x;
375 double y2 = vSquare[(j+1)%4].y;
377 double x3 = pPatat->VPoints[i].x;
378 double y3 = pPatat->VPoints[i].y;
379 double x4 = pPatat->VPoints[(i+1)%pPatat->VPoints.size()].x;
380 double y4 = pPatat->VPoints[(i+1)%pPatat->VPoints.size()].y;
382 if (segmentIntersection(x1, y1, x2, y2, x3, y3, x4, y4))
384 string sTmp = CExport::getZoneNameFromXY (x, y) + ".ig";
385 if (!CTools::fileExist(sTmp)) // If the file does not exist
386 return true; // need update
390 return false;
393 // ---------------------------------------------------------------------------
394 // A path needs an update if there is at least one zone under itself that is not present
395 bool CExport::isPathNeedUpdate (float fCellSize, CPrimPath *pPath, const string &sIGOutputDir)
397 uint32 i, j;
398 CVector vMin, vMax;
400 CTools::chdir (sIGOutputDir);
401 if (pPath->VPoints.empty())
402 return false;
403 vMin = vMax = pPath->VPoints[0];
404 for (i = 0; i < pPath->VPoints.size(); ++i)
406 if (vMin.x > pPath->VPoints[i].x) vMin.x = pPath->VPoints[i].x;
407 if (vMin.y > pPath->VPoints[i].y) vMin.y = pPath->VPoints[i].y;
408 if (vMin.z > pPath->VPoints[i].z) vMin.z = pPath->VPoints[i].z;
409 if (vMax.x < pPath->VPoints[i].x) vMax.x = pPath->VPoints[i].x;
410 if (vMax.y < pPath->VPoints[i].y) vMax.y = pPath->VPoints[i].y;
411 if (vMax.z < pPath->VPoints[i].z) vMax.z = pPath->VPoints[i].z;
414 sint32 x, y;
415 sint32 nMinX, nMinY, nMaxX, nMaxY;
416 nMinX = (sint32) floor (vMin.x / fCellSize);
417 nMinY = (sint32) floor (vMin.y / fCellSize);
418 nMaxX = (sint32) floor (vMax.x / fCellSize);
419 nMaxY = (sint32) floor (vMax.y / fCellSize);
421 for (x = nMinX; x <= nMaxX; ++x)
422 for (y = nMinY; y <= nMaxY; ++y)
424 // Does the zone (x,y) is under the patat ?
425 bool bZoneUnderPatat = false;
426 // Is a vertex of the zone in the patat ?
427 CVector vSquare[4];
428 vSquare[0].x = x * fCellSize;
429 vSquare[0].y = y * fCellSize;
430 vSquare[0].z = 0.0f;
432 vSquare[1].x = (x+1) * fCellSize;
433 vSquare[1].y = y * fCellSize;
434 vSquare[1].z = 0.0f;
436 vSquare[2].x = (x+1) * fCellSize;
437 vSquare[2].y = (y+1) * fCellSize;
438 vSquare[2].z = 0.0f;
440 vSquare[3].x = x * fCellSize;
441 vSquare[3].y = (y+1) * fCellSize;
442 vSquare[3].z = 0.0f;
444 // Is a vertex of the path inside the zone ?
445 for (i = 0; i < pPath->VPoints.size(); ++i)
447 if ((pPath->VPoints[i].x >= (x*fCellSize)) &&
448 (pPath->VPoints[i].x <= ((x+1)*fCellSize)) &&
449 (pPath->VPoints[i].y >= (y*fCellSize)) &&
450 (pPath->VPoints[i].y <= ((y+1)*fCellSize)))
452 string sTmp = CExport::getZoneNameFromXY(x,y) + ".ig";
453 if (!CTools::fileExist(sTmp)) // If the file does not exist
454 return true; // need update
458 // Is an edge of the zone cut an edge of the patat ?
459 for (i = 0; i < (pPath->VPoints.size()-1); ++i)
460 for (j = 0; j < 4; ++j)
462 double x1 = vSquare[j].x;
463 double y1 = vSquare[j].y;
464 double x2 = vSquare[(j+1)%4].x;
465 double y2 = vSquare[(j+1)%4].y;
467 double x3 = pPath->VPoints[i].x;
468 double y3 = pPath->VPoints[i].y;
469 double x4 = pPath->VPoints[i+1].x;
470 double y4 = pPath->VPoints[i+1].y;
472 if (segmentIntersection(x1, y1, x2, y2, x3, y3, x4, y4))
474 string sTmp = CExport::getZoneNameFromXY (x, y) + ".ig";
475 if (!CTools::fileExist(sTmp)) // If the file does not exist
476 return true; // need update
480 return false;
483 // ---------------------------------------------------------------------------
484 // A path needs an update if there is at least one zone under itself that is not present
485 bool CExport::isPointNeedUpdate (float fCellSize, CPrimPoint *pPoint, const string &sIGOutputDir)
487 CTools::chdir (sIGOutputDir);
489 sint32 x, y;
490 x = (sint32) floor (pPoint->Point.x / fCellSize);
491 y = (sint32) floor (pPoint->Point.y / fCellSize);
493 // Does the zone (x,y) is under the patat ?
494 bool bZoneUnderPatat = false;
495 // Is a vertex of the zone in the patat ?
496 CVector vSquare[4];
497 vSquare[0].x = x * fCellSize;
498 vSquare[0].y = y * fCellSize;
499 vSquare[0].z = 0.0f;
501 vSquare[1].x = (x+1) * fCellSize;
502 vSquare[1].y = y * fCellSize;
503 vSquare[1].z = 0.0f;
505 vSquare[2].x = (x+1) * fCellSize;
506 vSquare[2].y = (y+1) * fCellSize;
507 vSquare[2].z = 0.0f;
509 vSquare[3].x = x * fCellSize;
510 vSquare[3].y = (y+1) * fCellSize;
511 vSquare[3].z = 0.0f;
513 // Is the vertex inside the zone ?
514 if ((pPoint->Point.x >= (x*fCellSize)) &&
515 (pPoint->Point.x <= ((x+1)*fCellSize)) &&
516 (pPoint->Point.y >= (y*fCellSize)) &&
517 (pPoint->Point.y <= ((y+1)*fCellSize)))
519 string sTmp = CExport::getZoneNameFromXY(x,y) + ".ig";
520 if (!CTools::fileExist(sTmp)) // If the file does not exist
521 return true; // need update
524 return false;
527 // ---------------------------------------------------------------------------
528 // SExportOptions
529 // ---------------------------------------------------------------------------
531 // ---------------------------------------------------------------------------
532 SExportOptions::SExportOptions ()
534 CellSize = 160.0f;
537 // ---------------------------------------------------------------------------
538 bool SExportOptions::loadcf (CConfigFile &cf)
540 // Out
541 CConfigFile::CVar &cvOutIGDir = cf.getVar("EXP_OutIGDir");
542 OutIGDir = cvOutIGDir.asString();
544 // In
545 CConfigFile::CVar &cvInLandscapeDir = cf.getVar("EXP_ZoneWDir");
546 InLandscapeDir = cvInLandscapeDir.asString();
548 CConfigFile::CVar &cvLandBankFile = cf.getVar("EXP_SmallBank");
549 LandBankFile = cvLandBankFile.asString();
550 CConfigFile::CVar &cvLandFarBankFile = cf.getVar("EXP_FarBank");
551 LandFarBankFile = cvLandFarBankFile.asString();
552 CConfigFile::CVar &cvLandTileNoiseDir = cf.getVar("EXP_DisplaceDir");
553 LandTileNoiseDir = cvLandTileNoiseDir.asString();
555 CConfigFile::CVar &cvCellSize = cf.getVar("EXP_CellSize");
556 CellSize = cvCellSize.asFloat();
558 CConfigFile::CVar &cvPrimFloraDir = cf.getVar("EXP_PrimFloraDir");
559 PrimFloraDir = cvPrimFloraDir.asString();
561 return true;
564 // ---------------------------------------------------------------------------
565 bool SExportOptions::save (FILE *f)
567 fprintf (f,"\n// Export Options\n");
568 fprintf (f, "EXP_OutIGDir = \"%s\";\n", OutIGDir.c_str());
569 fprintf (f, "EXP_ZoneWDir = \"%s\";\n", InLandscapeDir.c_str());
570 fprintf (f, "EXP_SmallBank = \"%s\";\n", LandBankFile.c_str());
571 fprintf (f, "EXP_FarBank = \"%s\";\n", LandFarBankFile.c_str());
572 fprintf (f, "EXP_DisplaceDir = \"%s\";\n", LandTileNoiseDir.c_str());
573 fprintf (f, "EXP_CellSize = %f;\n", CellSize);
574 fprintf (f, "EXP_PrimFloraDir = \"%s\";\n", PrimFloraDir.c_str());
575 return true;
578 // ---------------------------------------------------------------------------
579 // SExportPrimitive
580 // ---------------------------------------------------------------------------
582 // ---------------------------------------------------------------------------
583 bool SExportPrimitive::operator == (const SExportPrimitive &rRightArg)
585 if (strcmp(this->FullPrimName.c_str(), rRightArg.FullPrimName.c_str()) == 0)
586 if (strcmp(this->PrimitiveName.c_str(), rRightArg.PrimitiveName.c_str()) == 0)
587 return true;
588 return false;
591 // ---------------------------------------------------------------------------
592 // CExport
593 // ---------------------------------------------------------------------------
595 // ---------------------------------------------------------------------------
596 CExport::CExport()
598 _Landscape = NULL;
599 _VCM = NULL;
600 _VCE = NULL;
601 _ExportCB = NULL;
602 _Options = NULL;
605 // ---------------------------------------------------------------------------
606 CExport::~CExport()
608 if (_Landscape != NULL)
609 delete _Landscape;
610 if (_VCM != NULL)
612 _VCM->deleteEntity (_VCE);
613 delete _VCM;
619 // ---------------------------------------------------------------------------
620 bool CExport::newExport (SExportOptions &opt, IExportCB *expCB)
622 _ExeDir = CTools::pwd();
623 _Options = &opt;
624 _ExportCB = expCB;
626 string sTmp;
627 uint32 i, j, k, m;
631 // First of all read the CFG
632 string sContinentDir = _Options->PrimFloraDir;
634 sContinentDir = CTools::normalizePath (sContinentDir);
635 CTools::chdir (sContinentDir.c_str()); // Relative to absolute path
636 sContinentDir = CTools::pwd () + "\\";
637 CTools::chdir (_ExeDir);
639 // Read continent.cfg
640 SContinentCfg ContinentCFG;
642 string sTmp = sContinentDir + "continent.cfg";
643 if (!ContinentCFG.load(sTmp.c_str()))
644 throw Exception("Cannot load continent.cfg");
647 // Ok set parameters from options first and with CFG if no options set
649 if (_Options->OutIGDir.empty())
650 _OutIGDir = CTools::normalizePath (ContinentCFG.OutIGDir);
651 else
652 _OutIGDir = CTools::normalizePath (_Options->OutIGDir);
654 if (_Options->LandFile.empty())
655 _LandFile = ContinentCFG.LandFile;
656 else
657 _LandFile = _Options->LandFile;
659 if (_Options->DfnDir.empty())
660 _DfnDir = ContinentCFG.DfnDir;
661 else
662 _DfnDir = _Options->DfnDir;
664 if (_Options->GameElemDir.empty())
665 _GameElemDir = ContinentCFG.GameElemDir;
666 else
667 _GameElemDir = _Options->GameElemDir;
669 if (_Options->InLandscapeDir.empty())
670 _InLandscapeDir = ContinentCFG.LandZoneWDir; // Directory where to get .zonew files
671 else
672 _InLandscapeDir = _Options->InLandscapeDir;
674 if (_Options->LandFarBankFile.empty())
675 _LandBankFile = ContinentCFG.LandBankFile; // The .smallbank file associated with the landscape
676 else
677 _LandBankFile = _Options->LandBankFile;
679 if (_Options->LandFarBankFile.empty())
680 _LandFarBankFile = ContinentCFG.LandFarBankFile; // The .farbank file
681 else
682 _LandFarBankFile = _Options->LandFarBankFile;
684 if (_Options->LandTileNoiseDir.empty())
685 _LandTileNoiseDir = ContinentCFG.LandTileNoiseDir; // Directory where to get displacement map
686 else
687 _LandTileNoiseDir = _Options->LandTileNoiseDir;
690 // Now create output directory
691 CTools::mkdir (_OutIGDir);
693 CTools::chdir (_OutIGDir.c_str());
694 _OutIGDir = CTools::pwd () + "\\";
695 CTools::chdir (_ExeDir);
698 CPath::addSearchPath (_DfnDir, true, true);
699 CPath::addSearchPath (_GameElemDir, true, true);
700 CPath::addSearchPath (_LandTileNoiseDir, true, true);
703 // Get all regions
704 vector<string> vRegions;
705 WIN32_FIND_DATA findData;
706 HANDLE hFind;
708 CTools::chdir (sContinentDir);
709 hFind = FindFirstFile ("*.*", &findData);
710 while (hFind != INVALID_HANDLE_VALUE)
712 if (GetFileAttributes(findData.cFileName)&FILE_ATTRIBUTE_DIRECTORY)
714 // Look if the name is a system directory
715 bool bFound = false;
716 for (i = 0; i < MAX_SYS_DIR; ++i)
717 if (stricmp (findData.cFileName, gExportSysDir[i]) == 0)
719 bFound = true;
720 break;
722 if (!bFound) // No, ok lets recurse it
724 vRegions.push_back (findData.cFileName);
727 if (FindNextFile (hFind, &findData) == 0)
728 break;
730 FindClose (hFind);
733 // Process all regions
734 uint32 nRegion;
735 for (nRegion = 0; nRegion < vRegions.size(); ++nRegion)
737 CTools::chdir (sContinentDir + vRegions[nRegion]);
739 // Process if a flora has been modified
740 vector<SFloraToUpdate> vFloraToUpdate;
741 uint32 nFloraFile;
743 // Fill the structure for update
745 // Get all new flora of the region
746 vector<string> vFloraFiles;
747 CTools::dirSub ("*.flora", vFloraFiles, true);
748 for (nFloraFile = 0; nFloraFile < vFloraFiles.size(); ++nFloraFile)
750 // Compare the date with the old file stored in the IG output directory
751 CTools::chdir (sContinentDir + vRegions[nRegion]);
752 string sBaseName = vFloraFiles[nFloraFile].substr(sContinentDir.size());
753 sBaseName = sBaseName.substr(0,sBaseName.rfind('\\'));
754 for (i = 0; i < sBaseName.size(); ++i)
755 if (sBaseName[i] == '\\')
756 sBaseName[i] = '-';
757 sBaseName += '-';
758 string sOldFloraName = _OutIGDir + sBaseName + vFloraFiles[nFloraFile].substr(vFloraFiles[nFloraFile].rfind('\\')+1);
760 if (CTools::fileExist(sOldFloraName))
762 if (CTools::fileDateCmp(vFloraFiles[nFloraFile], sOldFloraName) > 0)
764 // Delete zones from the 2 files
765 SFloraToUpdate ftuTmp;
766 // Delete zones from the newest file
767 ftuTmp.FloraFile = vFloraFiles[nFloraFile];
768 CTools::chdir (vFloraFiles[nFloraFile].substr(0,vFloraFiles[nFloraFile].rfind('\\')));
769 CTools::dir ("*.prim", ftuTmp.PrimFile, true);
770 vFloraToUpdate.push_back (ftuTmp);
771 // Delete zones from the oldest file
772 CTools::chdir (_OutIGDir);
773 ftuTmp.FloraFile = sOldFloraName;
774 CTools::dir (sBaseName + "*.prim", ftuTmp.PrimFile, true);
775 vFloraToUpdate.push_back (ftuTmp);
776 CTools::chdir (_ExeDir);
777 sTmp = vFloraFiles[nFloraFile] + " has been modified.";
778 if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp);
780 else
782 CTools::chdir (_ExeDir);
783 sTmp = vFloraFiles[nFloraFile] + string(" up to date.");
784 if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp);
787 else
789 // Delete zone from newest file
790 SFloraToUpdate ftuTmp;
791 ftuTmp.FloraFile = vFloraFiles[nFloraFile];
792 CTools::chdir (vFloraFiles[nFloraFile].substr(0,vFloraFiles[nFloraFile].rfind('\\')));
793 CTools::dir ("*.prim", ftuTmp.PrimFile, true);
794 vFloraToUpdate.push_back (ftuTmp);
795 CTools::chdir (_ExeDir);
796 sTmp = vFloraFiles[nFloraFile] + " not generated.";
797 if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp);
801 // Get all old flora of the current region
802 CTools::chdir (_OutIGDir);
803 CTools::dir (vRegions[nRegion] + "-*.flora", vFloraFiles, true);
804 for (nFloraFile = 0; nFloraFile < vFloraFiles.size(); ++nFloraFile)
806 CTools::chdir (_OutIGDir);
807 // if the old flora file still exist but the new one not -> delete zone for the oldest file
808 string sNewFloraName = vFloraFiles[nFloraFile].substr(vFloraFiles[nFloraFile].rfind('\\')+1);
809 string sBaseName = sNewFloraName.substr (0, vFloraFiles[nFloraFile].rfind('-'));
810 for (i = 0; i < sNewFloraName.size(); ++i)
811 if (sNewFloraName[i] == '-')
812 sNewFloraName[i] = '\\';
814 sNewFloraName = sContinentDir + sNewFloraName;
815 if (!CTools::fileExist(sNewFloraName))
817 SFloraToUpdate ftuTmp;
818 // Delete zones from the oldest file
819 ftuTmp.FloraFile = vFloraFiles[nFloraFile];
820 CTools::dir (sBaseName + "*.prim", ftuTmp.PrimFile, true);
821 vFloraToUpdate.push_back (ftuTmp);
822 CTools::chdir (_ExeDir);
823 sTmp = vFloraFiles[nFloraFile] + " not needed anymore.";
824 if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp);
828 // End of fill the structure for update
830 // Interpret the FloraToUpdate
831 for (nFloraFile = 0; nFloraFile < vFloraToUpdate.size(); ++nFloraFile)
833 // Get all patats referenced by the flora and suppress zones under
835 // Load the .flora file
836 SFormFlora FormFlora;
838 // Get a loader
839 UFormLoader *loader = UFormLoader::createLoader ();
841 // Load the form
842 CSmartPtr<UForm> form = loader->loadForm (vFloraToUpdate[nFloraFile].FloraFile.c_str ());
843 if (form)
844 FormFlora.build (form->getRootNode ());
846 // Release the loader
847 UFormLoader::releaseLoader (loader);
850 // Load all the .prim files that can be referenced by the flora
851 vector<CPrimRegion> vPrimRegions;
853 vector<string> &rPrimFiles = vFloraToUpdate[nFloraFile].PrimFile;
854 for (i = 0; i < rPrimFiles.size(); ++i)
856 CPrimRegion tmpPrimRegion;
857 CIFile fileIn;
858 fileIn.open (rPrimFiles[i]);
859 CIXml input;
860 input.init (fileIn);
861 tmpPrimRegion.serial (input);
862 vPrimRegions.push_back (tmpPrimRegion);
866 // Delete zones under the old prims referenced by the old zone
868 for (i = 0; i < FormFlora.IncludePatats.size(); ++i)
870 // Get the patat
871 CPrimZone *pPatat = NULL;
872 CPrimPoint *pPoint = NULL;
873 CPrimPath *pPath = NULL;
874 for (j = 0; j < vPrimRegions.size(); ++j)
876 for (k = 0; k < vPrimRegions[j].VZones.size(); ++k)
877 if (vPrimRegions[j].VZones[k].getName() == FormFlora.IncludePatats[i])
878 pPatat = &vPrimRegions[j].VZones[k];
879 for (k = 0; k < vPrimRegions[j].VPoints.size(); ++k)
880 if (vPrimRegions[j].VPoints[k].getName() == FormFlora.IncludePatats[i])
881 pPoint = &vPrimRegions[j].VPoints[k];
882 for (k = 0; k < vPrimRegions[j].VPaths.size(); ++k)
883 if (vPrimRegions[j].VPaths[k].getName() == FormFlora.IncludePatats[i])
884 pPath = &vPrimRegions[j].VPaths[k];
887 if ((pPatat == NULL) && (pPoint == NULL) && (pPath == NULL))
889 CTools::chdir (_ExeDir);
890 sTmp = "WARNING : Cannot find " + FormFlora.IncludePatats[i];
891 if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp);
892 continue;
895 if ((pPatat != NULL) && (pPatat->VPoints.size() <= 2))
897 CTools::chdir (_ExeDir);
898 sTmp = "Patat " + pPatat->getName() + " has less than 3 points";
899 if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp);
900 continue;
903 if ((pPath != NULL) && (pPath->VPoints.size() < 2))
905 CTools::chdir (_ExeDir);
906 sTmp = "Patat " + pPatat->getName() + " has less than 2 points";
907 if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp);
908 continue;
911 if (pPatat != NULL)
912 delAllIGZoneUnderPatat (160.0f, pPatat, _OutIGDir);
913 if (pPoint != NULL)
914 delAllIGZoneUnderPoint (160.0f, pPoint, _OutIGDir);
915 if (pPath != NULL)
916 delAllIGZoneUnderPath (160.0f, pPath, _OutIGDir);
919 // End of Process if a flora has been modified
921 // Process if a prim has been modified
922 // Fill the structure for update
923 CTools::chdir (sContinentDir + vRegions[nRegion]);
924 vector<SPrimToUpdate> vPrimToUpdate;
926 uint32 nPrimFile;
927 vector<string> vPrimFiles;
928 // Get all new prim of the region
929 CTools::dirSub ("*.prim", vPrimFiles, true);
930 for (nPrimFile = 0; nPrimFile < vPrimFiles.size(); ++nPrimFile)
932 CTools::chdir (sContinentDir + vRegions[nRegion]);
933 // Compare the date with the old file stored in the IG output directory
934 string sBaseName = vPrimFiles[nPrimFile].substr(sContinentDir.size());
935 sBaseName = sBaseName.substr(0,sBaseName.rfind('\\'));
936 for (i = 0; i < sBaseName.size(); ++i)
937 if (sBaseName[i] == '\\')
938 sBaseName[i] = '-';
939 sBaseName += '-';
940 string sOldPrimName = _OutIGDir + sBaseName + vPrimFiles[nPrimFile].substr(vPrimFiles[nPrimFile].rfind('\\')+1);
942 if (CTools::fileExist(sOldPrimName))
944 if (CTools::fileDateCmp(vPrimFiles[nPrimFile], sOldPrimName) > 0)
946 // Delete zones from the 2 files
947 SPrimToUpdate ptuTmp;
948 // Delete zones from the newest file
949 ptuTmp.PrimFile = vPrimFiles[nPrimFile];
950 CTools::chdir (vPrimFiles[nPrimFile].substr(0,vPrimFiles[nPrimFile].rfind('\\')));
951 CTools::dir ("*.flora", ptuTmp.FloraFile, true);
952 vPrimToUpdate.push_back (ptuTmp);
953 // Delete zones from the oldest file
954 CTools::chdir (_OutIGDir);
955 ptuTmp.PrimFile = sOldPrimName;
956 CTools::dir (sBaseName + "*.flora", ptuTmp.FloraFile, true);
957 vPrimToUpdate.push_back (ptuTmp);
958 CTools::chdir (_ExeDir);
959 sTmp = vPrimFiles[nPrimFile] + " has been modified.";
960 if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp);
962 else
964 CTools::chdir (_ExeDir);
965 sTmp = vPrimFiles[nPrimFile] + " up to date.";
966 if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp);
969 else
971 // Delete zone from newest file
972 SPrimToUpdate ptuTmp;
973 ptuTmp.PrimFile = vPrimFiles[nPrimFile];
974 CTools::chdir (vPrimFiles[nPrimFile].substr(0,vPrimFiles[nPrimFile].rfind('\\')));
975 CTools::dir ("*.flora", ptuTmp.FloraFile, true);
976 vPrimToUpdate.push_back (ptuTmp);
977 CTools::chdir (_ExeDir);
978 sTmp = vPrimFiles[nPrimFile] + " not generated.";
979 if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp);
983 // Get all old prim of the current region
984 CTools::chdir (_OutIGDir);
985 CTools::dir (vRegions[nRegion] + "-*.prim", vPrimFiles, false);
986 for (nPrimFile = 0; nPrimFile < vPrimFiles.size(); ++nPrimFile)
988 CTools::chdir (_OutIGDir);
989 // if the old prim file still exist but the new one not -> delete zone for the oldest file
990 string sNewPrimName = vPrimFiles[nPrimFile].substr(vPrimFiles[nPrimFile].rfind('\\')+1);
991 string sBaseName = sNewPrimName.substr (0, vPrimFiles[nPrimFile].rfind('-'));
992 for (i = 0; i < sNewPrimName.size(); ++i)
993 if (sNewPrimName[i] == '-')
994 sNewPrimName[i] = '\\';
996 sNewPrimName = sContinentDir + sNewPrimName;
997 if (!CTools::fileExist(sNewPrimName))
999 // Delete zones from the oldest file
1000 CPrimRegion PrimRegion;
1002 CIFile fileIn;
1003 fileIn.open (vPrimFiles[nPrimFile]);
1004 CIXml input;
1005 input.init (fileIn);
1006 PrimRegion.serial (input);
1009 for (j = 0; j < PrimRegion.VZones.size(); ++j)
1010 delAllIGZoneUnderPatat (_Options->CellSize, &PrimRegion.VZones[j], _OutIGDir);
1011 for (j = 0; j < PrimRegion.VPaths.size(); ++j)
1012 delAllIGZoneUnderPath (_Options->CellSize, &PrimRegion.VPaths[j], _OutIGDir);
1013 for (j = 0; j < PrimRegion.VPoints.size(); ++j)
1014 delAllIGZoneUnderPoint (_Options->CellSize, &PrimRegion.VPoints[j], _OutIGDir);
1016 CTools::chdir (_ExeDir);
1017 sTmp = vPrimFiles[nPrimFile] + " not needed anymore.";
1018 if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp);
1022 // End of fill the structure for update
1024 // Interpretation of structure for update
1025 for (i = 0; i < vPrimToUpdate.size(); ++i)
1027 // Load all the .flora file
1028 vector<SFormFlora> vFormFloras;
1029 for (j = 0; j < vPrimToUpdate[i].FloraFile.size(); ++j)
1031 // Create the loader
1032 UFormLoader *loader = UFormLoader::createLoader ();
1034 // Load the form
1035 CSmartPtr<UForm> form = loader->loadForm (vPrimToUpdate[i].FloraFile[j].c_str ());
1036 if (form)
1038 SFormFlora FormFloraTmp;
1039 FormFloraTmp.build (form->getRootNode ());
1040 vFormFloras.push_back (FormFloraTmp);
1043 // Release the loader
1044 UFormLoader::releaseLoader (loader);
1047 // Load all the .prim files that can be referenced by the flora
1048 CPrimRegion PrimRegion;
1050 CIFile fileIn;
1051 fileIn.open (vPrimToUpdate[i].PrimFile);
1052 CIXml input;
1053 input.init (fileIn);
1054 PrimRegion.serial (input);
1057 // Delete zones under the prims that has been referenced by a flora
1059 for (j = 0; j < PrimRegion.VZones.size(); ++j)
1061 // Get the patat
1062 CPrimZone *pPatat = NULL;
1064 // Check if the current patat is referenced by a flora
1065 for (k = 0; k < vFormFloras.size(); ++k)
1066 for (m = 0; m < vFormFloras[k].IncludePatats.size(); ++m)
1067 if (PrimRegion.VZones[j].getName() == vFormFloras[k].IncludePatats[m])
1068 pPatat = &PrimRegion.VZones[j];
1070 if ((pPatat == NULL) || (pPatat->VPoints.size() <= 2))
1072 continue;
1074 delAllIGZoneUnderPatat (_Options->CellSize, pPatat, _OutIGDir);
1076 for (j = 0; j < PrimRegion.VPoints.size(); ++j)
1078 // Get the point
1079 CPrimPoint *pPoint = NULL;
1081 // Check if the current point is referenced by a flora
1082 for (k = 0; k < vFormFloras.size(); ++k)
1083 for (m = 0; m < vFormFloras[k].IncludePatats.size(); ++m)
1084 if (PrimRegion.VPoints[j].getName() == vFormFloras[k].IncludePatats[m])
1085 pPoint = &PrimRegion.VPoints[j];
1086 delAllIGZoneUnderPoint (_Options->CellSize, pPoint, _OutIGDir);
1088 for (j = 0; j < PrimRegion.VPaths.size(); ++j)
1090 // Get the path
1091 CPrimPath *pPath = NULL;
1093 // Check if the current path is referenced by a flora
1094 for (k = 0; k < vFormFloras.size(); ++k)
1095 for (m = 0; m < vFormFloras[k].IncludePatats.size(); ++m)
1096 if (PrimRegion.VPaths[j].getName() == vFormFloras[k].IncludePatats[m])
1097 pPath = &PrimRegion.VPaths[j];
1098 delAllIGZoneUnderPath (_Options->CellSize, pPath, _OutIGDir);
1101 // End of Process if a prim has been modified
1104 // End of process all regions
1106 // Check all patat to export (a patat that has no zone under itself (deleted or not present))
1107 vector<SExportPrimitive> vExportPrimitives;
1108 vector<string> vAllPrimFiles; // All prim files of a continent
1109 CTools::chdir (sContinentDir);
1110 CTools::dirSub ("*.prim", vAllPrimFiles, true);
1111 for (i = 0; i < vAllPrimFiles.size(); ++i)
1113 vAllPrimFiles[i] = strlwr(vAllPrimFiles[i]);
1114 // Load the primfile
1115 CPrimRegion PrimRegion;
1117 CIFile fileIn;
1118 fileIn.open (vAllPrimFiles[i]);
1119 CIXml input;
1120 input.init (fileIn);
1121 PrimRegion.serial (input);
1124 for (j = 0; j < PrimRegion.VZones.size(); ++j)
1126 // Check all zones to know if this patat must be updated
1127 if (isPatatNeedUpdate(_Options->CellSize, &PrimRegion.VZones[j], _OutIGDir))
1129 SExportPrimitive epTmp;
1130 epTmp.FullPrimName = vAllPrimFiles[i];
1131 epTmp.PrimitiveName = PrimRegion.VZones[j].getName();
1132 vExportPrimitives.push_back (epTmp);
1136 for (j = 0; j < PrimRegion.VPaths.size(); ++j)
1138 // Check all pathes to know if some must be updated (if no zone under)
1139 if (isPathNeedUpdate(_Options->CellSize, &PrimRegion.VPaths[j], _OutIGDir))
1141 SExportPrimitive epTmp;
1142 epTmp.FullPrimName = vAllPrimFiles[i];
1143 epTmp.PrimitiveName = PrimRegion.VPaths[j].getName();
1144 vExportPrimitives.push_back (epTmp);
1148 for (j = 0; j < PrimRegion.VPoints.size(); ++j)
1150 // Check all points to know if some must be updated (if no zone under)
1151 if (isPointNeedUpdate(_Options->CellSize, &PrimRegion.VPoints[j], _OutIGDir))
1153 SExportPrimitive epTmp;
1154 epTmp.FullPrimName = vAllPrimFiles[i];
1155 epTmp.PrimitiveName = PrimRegion.VPoints[j].getName();
1156 vExportPrimitives.push_back (epTmp);
1161 // Export
1163 CTools::chdir (_ExeDir);
1164 _Options->PrimFloraDir = sContinentDir;
1165 sTmp = "Exporting";
1166 if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp);
1167 doExport (*_Options, _ExportCB, &vExportPrimitives);
1169 // Copy new files for incremental purpose
1170 CTools::chdir (_ExeDir);
1171 sTmp = "Incrementing";
1172 if (_ExportCB != NULL) _ExportCB->dispInfo (sTmp);
1173 for (nRegion = 0; nRegion < vRegions.size(); ++nRegion)
1175 CTools::chdir (sContinentDir + vRegions[nRegion]);
1176 vector<string> vFiles;
1177 CTools::dirSub ("*.prim", vFiles, true);
1178 for (i = 0; i < vFiles.size(); ++i)
1180 string sDst = vFiles[i].substr (sContinentDir.size());
1181 for (j = 0; j < sDst.size(); ++j)
1182 if (sDst[j] == '\\')
1183 sDst[j] = '-';
1184 sDst = _OutIGDir + sDst;
1185 CTools::copy (sDst, vFiles[i]);
1187 CTools::chdir (sContinentDir + vRegions[nRegion]);
1188 CTools::dirSub ("*.flora", vFiles, true);
1189 for (i = 0; i < vFiles.size(); ++i)
1191 string sDst = vFiles[i].substr (sContinentDir.size());
1192 for (j = 0; j < sDst.size(); ++j)
1193 if (sDst[j] == '\\')
1194 sDst[j] = '-';
1195 sDst = _OutIGDir + sDst;
1196 CTools::copy (sDst, vFiles[i]);
1199 // -------------------------------------------------------------------
1200 // If the new file do not exists anymore but the old file exists -> delete old file
1201 CTools::chdir (_OutIGDir);
1202 CTools::dir (vRegions[nRegion] + "-*.prim", vFiles, false);
1203 for (i = 0; i < vFiles.size(); ++i)
1205 // Get the name of the recent file
1206 string sNewName = vFiles[i];
1207 for (j = 0; j < sNewName.size(); ++j)
1208 if (sNewName[j] == '-')
1209 sNewName[j] = '\\';
1211 sNewName = sContinentDir + sNewName;
1212 if (!CTools::fileExist(sNewName))
1214 // Delete the oldest file
1215 CFile::deleteFile(vFiles[i]);
1219 // If the new file do not exists anymore but the old file exists -> delete old file
1220 CTools::dir (vRegions[nRegion] + "-*.flora", vFiles, false);
1221 for (i = 0; i < vFiles.size(); ++i)
1223 // Get the name of the recent file
1224 string sNewName = vFiles[i];
1225 for (j = 0; j < sNewName.size(); ++j)
1226 if (sNewName[j] == '-')
1227 sNewName[j] = '\\';
1229 sNewName = sContinentDir + sNewName;
1230 if (!CTools::fileExist(sNewName))
1232 // Delete the oldest file
1233 CFile::deleteFile(vFiles[i]);
1238 CTools::chdir (_ExeDir);
1239 return true;
1242 // ---------------------------------------------------------------------------
1243 bool CExport::doExport (SExportOptions &opt, IExportCB *expCB, vector<SExportPrimitive> *selection)
1245 char sTmp[MAX_PATH];
1246 GetCurrentDirectory (MAX_PATH, sTmp);
1248 _Options = &opt;
1249 _ExportCB = expCB;
1251 // Does we have something to export
1252 if ((selection != NULL) && (selection->empty()))
1254 if (_ExportCB)
1255 _ExportCB->dispInfo ("Nothing to export");
1256 return true;
1259 // If we want to generate flora then we have to load the landscape
1260 uint32 i;
1262 if (_ExportCB)
1263 _ExportCB->dispPass ("Load Landscape");
1264 if (_Landscape == NULL)
1266 _Landscape = new CLandscape;
1267 _Landscape->init ();
1268 _VCM = new CVisualCollisionManager;
1269 _VCE = _VCM->createEntity ();
1270 _VCM->setLandscape (_Landscape);
1271 _VCE->setSnapToRenderedTesselation (false);
1274 CIFile bankFile (_LandBankFile);
1275 _Landscape->TileBank.serial (bankFile);
1276 CIFile farbankFile (_LandFarBankFile);
1277 _Landscape->TileFarBank.serial (farbankFile);
1278 _Landscape->TileBank.makeAllPathRelative ();
1279 _Landscape->TileBank.setAbsPath ("");
1280 _Landscape->TileBank.makeAllExtensionDDS ();
1281 _Landscape->initTileBanks ();
1283 loadLandscape (_LandFile);
1285 catch (const Exception &/*e*/)
1287 if (_ExportCB)
1288 _ExportCB->dispError ("Cannot load banks files");
1292 _FloraInsts.clear ();
1293 if (_ExportCB)
1294 _ExportCB->dispPass ("Generate Flora");
1295 vector<string> allFloraFiles;
1296 SetCurrentDirectory (_Options->PrimFloraDir.c_str());
1297 getAllFiles (".Flora", allFloraFiles);
1298 SetCurrentDirectory (sTmp);
1299 for (i = 0; i < allFloraFiles.size(); ++i)
1301 generateIGFromFlora (allFloraFiles[i], selection);
1304 writeFloraIG (_LandFile, (selection != NULL)); // If selection != NULL then test for writing
1306 SetCurrentDirectory (sTmp);
1307 if (_ExportCB)
1308 _ExportCB->dispPass ("Finished");
1310 return true;
1313 // ---------------------------------------------------------------------------
1314 void CExport::getAllFiles (const string &ext, vector<string> &files)
1316 char sCurDir[MAX_PATH];
1317 GetCurrentDirectory (MAX_PATH, sCurDir);
1319 WIN32_FIND_DATA findData;
1320 HANDLE hFind = FindFirstFile ("*.*", &findData);
1321 while (hFind != INVALID_HANDLE_VALUE)
1323 if (!((strcmp (findData.cFileName, ".") == 0) || (strcmp (findData.cFileName, "..") == 0)))
1325 if (findData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
1327 string sNewDir = sCurDir;
1328 sNewDir += string("\\") + findData.cFileName;
1329 SetCurrentDirectory (sNewDir.c_str());
1330 getAllFiles (ext, files);
1331 SetCurrentDirectory (sCurDir);
1333 else
1335 if (strlen(findData.cFileName) > strlen(ext.c_str()))
1336 if (stricmp(&findData.cFileName[strlen(findData.cFileName)-strlen(ext.c_str())], ext.c_str()) == 0)
1338 string fullName = sCurDir;
1339 fullName += string("\\") + findData.cFileName;
1340 fullName = strlwr (fullName);
1341 files.push_back (fullName);
1346 if (FindNextFile (hFind, &findData) == 0)
1347 break;
1349 FindClose (hFind);
1351 SetCurrentDirectory (sCurDir);
1354 // ---------------------------------------------------------------------------
1355 bool CExport::searchFile (const std::string &plantName, std::string &dir)
1357 char sCurDir[MAX_PATH];
1358 bool bFound = false;
1359 GetCurrentDirectory (MAX_PATH, sCurDir);
1361 WIN32_FIND_DATA findData;
1362 HANDLE hFind = FindFirstFile ("*.*", &findData);
1363 while (hFind != INVALID_HANDLE_VALUE)
1365 string filename = findData.cFileName;
1366 filename = strlwr(filename);
1367 if (!((filename == ".") || (filename == "..")))
1369 if (findData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
1371 string sNewDir = sCurDir;
1372 sNewDir += string("\\") + filename;
1373 SetCurrentDirectory (sNewDir.c_str());
1374 if (searchFile (plantName, dir))
1376 bFound = true;
1377 break;
1379 SetCurrentDirectory (sCurDir);
1381 else
1383 if (strlwr(plantName) == filename)
1385 dir = sCurDir;
1386 bFound = true;
1387 break;
1392 if (FindNextFile (hFind, &findData) == 0)
1393 break;
1395 FindClose (hFind);
1396 SetCurrentDirectory (sCurDir);
1397 return bFound;
1400 // ---------------------------------------------------------------------------
1401 bool CExport::generateIGFromFlora (const std::string &SrcFile, std::vector<SExportPrimitive> *selection)
1403 uint32 i, j, k, l, m;
1405 if (_ExportCB)
1406 _ExportCB->dispPass ("Generating From " + SrcFile);
1408 // Load all .prim
1409 vector<CPrimRegion> allPrimRegion;
1410 vector<string> allPrimFiles;
1412 char sCurDir[MAX_PATH];
1413 GetCurrentDirectory (MAX_PATH, sCurDir);
1414 SetCurrentDirectory (_Options->PrimFloraDir.c_str());
1415 getAllFiles (".prim", allPrimFiles);
1416 SetCurrentDirectory (sCurDir);
1417 for (i = 0; i < allPrimFiles.size(); ++i)
1421 CPrimRegion tmpPrimRegion;
1422 CIFile fileIn;
1423 fileIn.open (allPrimFiles[i]);
1424 CIXml input;
1425 input.init (fileIn);
1426 tmpPrimRegion.serial (input);
1427 allPrimRegion.push_back (tmpPrimRegion);
1429 catch (const Exception &/*e*/)
1431 if (_ExportCB != NULL)
1432 _ExportCB->dispWarning (string("Cant load ") + allPrimFiles[i]);
1433 CPrimRegion tmpPrimRegion;
1434 allPrimRegion.push_back (tmpPrimRegion);
1439 // Load the .Flora file (georges file) and load all associated .plant
1440 SFormFlora formFlora;
1441 map<string, SFormPlant> Plants;
1443 // Create a loader
1444 UFormLoader *loader = UFormLoader::createLoader ();
1445 // CPath::addSearchPath (_Options->DfnDir, true, true);
1446 // CPath::addSearchPath (_Options->GameElemDir, true, true);
1447 // CPath::addSearchPath (_Options->LandTileNoiseDir, true, true);
1449 // Load the form
1450 CSmartPtr<UForm> form = loader->loadForm (SrcFile.c_str ());
1451 if (form == NULL)
1453 if (_ExportCB != NULL)
1454 _ExportCB->dispError (string("Cant load ") + SrcFile );
1456 else
1458 // Read .Flora
1459 formFlora.build (form->getRootNode ());
1461 // Load the .plant associated
1462 for (i = 0; i < formFlora.PlantInstances.size(); ++i)
1464 const string &plantName = formFlora.PlantInstances[i].Name;
1465 map<string, SFormPlant>::iterator it = Plants.find (plantName);
1466 if (it != Plants.end()) // Already here ?!
1467 continue; // Zap it
1469 char sCurDir[MAX_PATH];
1470 GetCurrentDirectory (MAX_PATH, sCurDir);
1474 SetCurrentDirectory (_GameElemDir.c_str());
1475 string dir;
1477 if (searchFile (plantName, dir))
1479 string tmpName = dir + string("\\") + plantName;
1481 CSmartPtr<UForm> form2 = loader->loadForm (tmpName.c_str());
1482 if (form2)
1484 SFormPlant plantTmp;
1485 plantTmp.build (form2->getRootNode ());
1486 Plants.insert (map<string, SFormPlant>::value_type(plantName, plantTmp));
1488 else
1490 if (_ExportCB != NULL)
1491 _ExportCB->dispWarning (string("Cant load ") + plantName);
1494 else
1496 if (_ExportCB != NULL)
1497 _ExportCB->dispWarning (string("Cant load ") + plantName);
1499 SetCurrentDirectory (sCurDir);
1501 catch (const Exception &e)
1503 SetCurrentDirectory (sCurDir);
1504 if (_ExportCB != NULL)
1505 _ExportCB->dispWarning (string("Cant load ") + plantName + "(" + e.what() + ")" );
1511 // Sort PlantInstances by biggest radius first
1512 if (formFlora.PlantInstances.size() > 1)
1513 for (i = 0; i < (formFlora.PlantInstances.size()-1); ++i)
1514 for (j = i+1; j < formFlora.PlantInstances.size(); ++j)
1516 SPlantInstance &rPlantI = formFlora.PlantInstances[i];
1517 SPlantInstance &rPlantJ = formFlora.PlantInstances[j];
1518 map<string, SFormPlant>::iterator it = Plants.find (rPlantI.Name);
1519 if (it == Plants.end())
1520 continue;
1521 SFormPlant &rFormPlantI = it->second;
1523 it = Plants.find (rPlantJ.Name);
1524 if (it == Plants.end())
1525 continue;
1526 SFormPlant &rFormPlantJ = it->second;
1527 if (rFormPlantI.BoundingRadius < rFormPlantJ.BoundingRadius)
1529 SPlantInstance pi = formFlora.PlantInstances[i];
1530 formFlora.PlantInstances[i] = formFlora.PlantInstances[j];
1531 formFlora.PlantInstances[j] = pi;
1535 // Generating
1536 float jitter = formFlora.JitterPos;
1537 clamp (jitter, 0.0f, 1.0f);
1538 srand (formFlora.RandomSeed);
1539 for (i = 0; i < formFlora.IncludePatats.size(); ++i)
1541 uint32 nCurPlant = 0;
1542 CVector vMin, vMax;
1544 if (_ExportCB)
1545 _ExportCB->dispPass ("IncludePatats("+toString(i+1)+"/"+toString(formFlora.IncludePatats.size())+")");
1547 // Get the patat
1548 CPrimZone *pPatat = NULL;
1549 CPrimPoint *pPoint = NULL;
1550 CPrimPath *pPath = NULL;
1552 // Look if this is a patat
1553 for (j = 0; j < allPrimRegion.size(); ++j)
1555 for (k = 0; k < allPrimRegion[j].VZones.size(); ++k)
1557 if (allPrimRegion[j].VZones[k].getName() == formFlora.IncludePatats[i])
1559 if (selection != NULL)
1561 SExportPrimitive epTmp;
1562 epTmp.FullPrimName = allPrimFiles[j];
1563 epTmp.PrimitiveName = allPrimRegion[j].VZones[k].getName();
1564 for (m = 0; m < selection->size(); ++m)
1565 if (selection->operator[](m) == epTmp)
1567 pPatat = &allPrimRegion[j].VZones[k];
1568 break;
1571 else
1573 pPatat = &allPrimRegion[j].VZones[k];
1579 //Look if this is a point
1580 for (j = 0; j < allPrimRegion.size(); ++j)
1582 for (k = 0; k < allPrimRegion[j].VPoints.size(); ++k)
1584 if (allPrimRegion[j].VPoints[k].getName() == formFlora.IncludePatats[i])
1586 if (selection != NULL)
1588 SExportPrimitive epTmp;
1589 epTmp.FullPrimName = allPrimFiles[j];
1590 epTmp.PrimitiveName = allPrimRegion[j].VPoints[k].getName();
1591 for (m = 0; m < selection->size(); ++m)
1592 if (selection->operator[](m) == epTmp)
1594 pPoint = &allPrimRegion[j].VPoints[k];
1595 break;
1598 else
1600 pPoint = &allPrimRegion[j].VPoints[k];
1606 //Look if this is a path
1607 for (j = 0; j < allPrimRegion.size(); ++j)
1609 for (k = 0; k < allPrimRegion[j].VPaths.size(); ++k)
1611 if (allPrimRegion[j].VPaths[k].getName() == formFlora.IncludePatats[i])
1613 if (selection != NULL)
1615 SExportPrimitive epTmp;
1616 epTmp.FullPrimName = allPrimFiles[j];
1617 epTmp.PrimitiveName = allPrimRegion[j].VPaths[k].getName();
1618 for (m = 0; m < selection->size(); ++m)
1619 if (selection->operator[](m) == epTmp)
1621 pPath = &allPrimRegion[j].VPaths[k];
1622 break;
1625 else
1627 pPath = &allPrimRegion[j].VPaths[k];
1634 if ((pPatat == NULL) && (pPoint == NULL) && (pPath == NULL))
1636 if (selection == NULL)
1637 if (_ExportCB)
1638 _ExportCB->dispWarning ("Cannot find " + formFlora.IncludePatats[i]);
1639 continue;
1642 if ((pPatat != NULL) && (pPatat->VPoints.size() <= 2))
1644 if (_ExportCB)
1645 _ExportCB->dispWarning ("Patat " + pPatat->getName() + " has less than 3 points");
1646 continue;
1649 if ((pPath != NULL) && (pPath->VPoints.size() <= 1))
1651 if (_ExportCB)
1652 _ExportCB->dispWarning ("Path " + pPath->getName() + " has less than 2 points");
1653 continue;
1656 // Generate for a patat
1657 if (pPatat != NULL)
1659 vMin = vMax = pPatat->VPoints[0];
1660 for (j = 0; j < pPatat->VPoints.size(); ++j)
1662 if (vMin.x > pPatat->VPoints[j].x) vMin.x = pPatat->VPoints[j].x;
1663 if (vMin.y > pPatat->VPoints[j].y) vMin.y = pPatat->VPoints[j].y;
1664 if (vMin.z > pPatat->VPoints[j].z) vMin.z = pPatat->VPoints[j].z;
1665 if (vMax.x < pPatat->VPoints[j].x) vMax.x = pPatat->VPoints[j].x;
1666 if (vMax.y < pPatat->VPoints[j].y) vMax.y = pPatat->VPoints[j].y;
1667 if (vMax.z < pPatat->VPoints[j].z) vMax.z = pPatat->VPoints[j].z;
1670 for (j = 0; j < formFlora.PlantInstances.size(); ++j)
1672 SPlantInstance &rPlant = formFlora.PlantInstances[j];
1673 map<string, SFormPlant>::iterator it = Plants.find (rPlant.Name);
1674 if (it == Plants.end())
1676 if (_ExportCB)
1677 _ExportCB->dispWarning ("Cannot find " + rPlant.Name);
1678 continue;
1680 SFormPlant &rFormPlant = it->second;
1682 float squareLength = (float)sqrt (Pi*rFormPlant.BoundingRadius*rFormPlant.BoundingRadius / rPlant.Density);
1683 uint32 nNbPlantX = 1+(int)floor ((vMax.x-vMin.x) / squareLength);
1684 uint32 nNbPlantY = 1+(int)floor ((vMax.y-vMin.y) / squareLength);
1685 for (l = 0; l < nNbPlantY; ++l)
1686 for (k = 0; k < nNbPlantX; ++k)
1688 if (_ExportCB)
1689 _ExportCB->dispPassProgress (((float)(k+l*nNbPlantX))/((float)(nNbPlantX*nNbPlantY)));
1691 bool bExists = false;
1692 CVector pos;
1693 float scaleTmp;
1694 for (m = 0; m < 32; ++m)
1696 pos.x = vMin.x + squareLength * k + (frand(2.0f)-1.0f) * jitter * 0.5f * squareLength;
1697 pos.y = vMin.y + squareLength * l + (frand(2.0f)-1.0f) * jitter * 0.5f * squareLength;
1698 pos.z = 0.0f;
1699 scaleTmp = (formFlora.ScaleMax-formFlora.ScaleMin)*frand(1.0)+formFlora.ScaleMin;
1700 if (pPatat->contains(pos))
1702 if (isWellPlaced(pos, rPlant, rFormPlant, scaleTmp))
1704 // Testt finally with the exclude patats ...
1705 bExists = true;
1706 for (uint32 expat = 0; expat < formFlora.ExcludePatats.size(); ++expat)
1708 CPrimZone *pExPatat = NULL;
1709 for (uint32 epj = 0; epj < allPrimRegion.size(); ++epj)
1710 for (uint32 epk = 0; epk < allPrimRegion[epj].VZones.size(); ++epk)
1711 if (allPrimRegion[epj].VZones[epk].getName() == formFlora.ExcludePatats[expat])
1713 pExPatat = &allPrimRegion[epj].VZones[epk];
1714 break;
1716 if (pExPatat != NULL)
1718 if (pExPatat->contains(pos))
1720 bExists = false;
1721 break;
1725 if (bExists)
1726 break;
1731 if (!bExists)
1732 continue;
1734 SFloraInst vi;
1735 vi.ShapeName = rFormPlant.Shape;
1736 vi.PlantName = rPlant.Name;
1737 vi.Scale = scaleTmp;
1738 vi.Radius = rFormPlant.BoundingRadius * vi.Scale;
1739 vi.Rot = (float)Pi * frand (1.0);
1741 if (formFlora.PutOnWater)
1743 if (pos.z < formFlora.WaterHeight)
1744 pos.z = formFlora.WaterHeight;
1746 vi.Pos = pos;
1747 _FloraInsts.push_back (vi);
1748 } // End of for all position of a plant x,y check if we cant put it
1749 } // End of for all plant instances
1750 } // End of Generate for a patat
1752 // Generate for a point
1753 if (pPoint != NULL)
1755 // Choose a plant
1756 float total = 0.0f;
1757 for (j = 0; j < formFlora.PlantInstances.size(); ++j)
1759 total += formFlora.PlantInstances[j].Density;
1761 float posf = total * frand(1.0);
1762 total = 0.0f;
1763 for (j = 0; j < formFlora.PlantInstances.size(); ++j)
1765 total += formFlora.PlantInstances[j].Density;
1766 if (posf < total) break;
1768 if (j == formFlora.PlantInstances.size())
1769 j = (uint32)formFlora.PlantInstances.size()-1;
1771 SPlantInstance &rPlant = formFlora.PlantInstances[j];
1772 map<string, SFormPlant>::iterator it = Plants.find (rPlant.Name);
1773 if (it == Plants.end())
1775 if (_ExportCB)
1776 _ExportCB->dispWarning ("Cannot find " + rPlant.Name);
1777 continue;
1779 SFormPlant &rFormPlant = it->second;
1781 SFloraInst vi;
1782 vi.ShapeName = rFormPlant.Shape;
1783 vi.PlantName = rPlant.Name;
1784 vi.Scale = (formFlora.ScaleMax-formFlora.ScaleMin)*frand(1.0)+formFlora.ScaleMin;
1785 vi.Radius = rFormPlant.BoundingRadius * vi.Scale;
1786 vi.Rot = (float)Pi * frand (1.0);
1788 CVector pos;
1789 pos.x = pPoint->Point.x;
1790 pos.y = pPoint->Point.y;
1791 pos.z = getZFromXY (pos.x, pos.y);
1792 if (formFlora.PutOnWater)
1794 if (pos.z < formFlora.WaterHeight)
1795 pos.z = formFlora.WaterHeight;
1797 vi.Pos = pos;
1798 if (pos.z > -90000.0f)
1799 _FloraInsts.push_back (vi);
1800 } // End of Generate for a point
1802 // Generate for a path
1803 if (pPath != NULL)
1805 float rLength = 0.0f; // Total length of the broken line
1806 for (j = 0; j < pPath->VPoints.size()-1; ++j)
1808 rLength += (pPath->VPoints[j]-pPath->VPoints[j+1]).norm();
1811 for (j = 0; j < formFlora.PlantInstances.size(); ++j)
1813 SPlantInstance &rPlant = formFlora.PlantInstances[j];
1814 map<string, SFormPlant>::iterator it = Plants.find (rPlant.Name);
1815 if (it == Plants.end())
1817 if (_ExportCB)
1818 _ExportCB->dispWarning ("Cannot find " + rPlant.Name);
1819 continue;
1821 SFormPlant &rFormPlant = it->second;
1823 float squareLength = (float)(2*rFormPlant.BoundingRadius / rPlant.Density);
1824 uint32 nNbPlant = 1+(int)floor (rLength / squareLength);
1826 for (k = 0; k < nNbPlant; ++k)
1828 if (_ExportCB)
1829 _ExportCB->dispPassProgress (((float)(k))/((float)(nNbPlant)));
1831 bool bExists = false;
1832 CVector pos;
1833 float scaleTmp;
1834 for (m = 0; m < 32; ++m)
1836 // Calculate the curviline abscisse
1837 float curvAbs = squareLength * k + (frand(2.0f)-1.0f) * jitter * 0.5f * squareLength;
1838 float TempLength = 0.0f;
1839 // Convert to a real point along the curve (broken line)
1840 for (l = 0; l < pPath->VPoints.size()-1; ++l)
1842 float newSize = (pPath->VPoints[l]-pPath->VPoints[l+1]).norm();
1843 if (curvAbs < (TempLength+newSize))
1845 curvAbs -= TempLength;
1846 break;
1848 TempLength += newSize;
1850 if (l == (pPath->VPoints.size()-1))
1852 l = (uint32)pPath->VPoints.size()-2;
1853 curvAbs = (pPath->VPoints[l]-pPath->VPoints[l+1]).norm();
1855 // Calculate the coord
1856 curvAbs = curvAbs / (pPath->VPoints[l]-pPath->VPoints[l+1]).norm();
1857 pos = pPath->VPoints[l] + (pPath->VPoints[l+1]-pPath->VPoints[l])*curvAbs;
1858 pos.z = 0.0f;
1859 scaleTmp = (formFlora.ScaleMax-formFlora.ScaleMin)*frand(1.0)+formFlora.ScaleMin;
1860 if (isWellPlaced(pos, rPlant, rFormPlant, scaleTmp))
1862 // Test finally with the exclude patats ...
1863 bExists = true;
1864 for (uint32 expat = 0; expat < formFlora.ExcludePatats.size(); ++expat)
1866 CPrimZone *pExPatat = NULL;
1867 for (uint32 epj = 0; epj < allPrimRegion.size(); ++epj)
1868 for (uint32 epk = 0; epk < allPrimRegion[epj].VZones.size(); ++epk)
1869 if (allPrimRegion[epj].VZones[epk].getName() == formFlora.ExcludePatats[expat])
1871 pExPatat = &allPrimRegion[epj].VZones[epk];
1872 break;
1874 if (pExPatat != NULL)
1876 if (pExPatat->contains(pos))
1878 bExists = false;
1879 break;
1883 if (bExists)
1884 break;
1888 if (!bExists)
1889 continue;
1891 SFloraInst vi;
1892 vi.ShapeName = rFormPlant.Shape;
1893 vi.PlantName = rPlant.Name;
1894 vi.Scale = scaleTmp;
1895 vi.Radius = rFormPlant.BoundingRadius * vi.Scale;
1896 vi.Rot = (float)Pi * frand (1.0);
1898 if (formFlora.PutOnWater)
1900 if (pos.z < formFlora.WaterHeight)
1901 pos.z = formFlora.WaterHeight;
1903 vi.Pos = pos;
1904 _FloraInsts.push_back (vi);
1905 } // End of for all position of a plant x,y check if we cant put it
1906 } // End of for all plant instances
1908 } // End of Generate for a path
1910 } // End of for all IncludePatats
1911 return true;
1914 // ---------------------------------------------------------------------------
1915 float CExport::getZFromXY (float x, float y)
1917 CVector pos = CVector(x, y, 0);
1918 CVector normal;
1919 float z, zmin, zmax;
1921 // Approximate the z with patch bounding boxes
1922 sint32 zoneX = (sint32)floor (x/_Options->CellSize);
1923 sint32 zoneY = (sint32)floor (-y/_Options->CellSize);
1924 sint32 zoneId = zoneY * 256 + zoneX;
1925 CZone *pZone = _Landscape->getZone (zoneId);
1926 if (pZone == NULL)
1927 return -100000.0f;
1929 CAABBoxExt bb = pZone->getZoneBB();
1930 zmin = bb.getMin().z;
1931 zmax = bb.getMax().z;
1932 pos.z = zmin;
1933 z = zmin;
1934 while (z < zmax)
1936 if (_VCE->snapToGround(pos, normal))
1937 break;
1938 z += CVisualCollisionEntity::BBoxRadiusZ / 2.0f; // Super sampling due to max frequency on radiosity
1939 pos.z = z;
1942 if (z >= zmax)
1943 return -100000.0f;
1945 return pos.z;
1948 // ---------------------------------------------------------------------------
1949 bool CExport::isWellPlaced (CVector &pos, SPlantInstance &rPI, SFormPlant &rFP, float scale)
1951 uint32 i;
1953 // Look if this Flora intersect with one of the current ones
1954 for (i = 0; i < _FloraInsts.size(); ++i)
1956 CVector temp = _FloraInsts[i].Pos - pos;
1957 temp.z = 0.0f;
1958 if (temp.norm() < (_FloraInsts[i].Radius + scale*rFP.BoundingRadius))
1959 return false;
1962 // Get the real Z
1963 pos.z = getZFromXY (pos.x, pos.y);
1964 if (pos.z < -90000.0f)
1965 return false;
1967 // Get some Z around to see if we can put the Flora on the ground
1968 uint32 nNbSamples = 8; // Const to be put somewhere
1969 vector<CVector> base;
1970 base.resize (nNbSamples);
1971 for (i = 0; i < nNbSamples; ++i)
1973 base[i] = pos;
1974 base[i].x += scale * rFP.CollisionRadius * cosf((2.0f*(float)Pi*i)/(float)nNbSamples);
1975 base[i].y += scale * rFP.CollisionRadius * sinf((2.0f*(float)Pi*i)/(float)nNbSamples);
1976 base[i].z = getZFromXY (base[i].x, base[i].y);
1978 if (fabs(base[i].z-pos.z) > 0.8f)
1979 return false;
1982 return true;
1985 // ---------------------------------------------------------------------------
1986 void CExport::writeFloraIG (const string &LandFile, bool bTestForWriting)
1988 sint32 i, j, k;
1990 if (_FloraInsts.empty())
1991 return;
1993 CZoneRegion zoneRegion;
1994 CIFile inFile;
1995 if (inFile.open (LandFile))
1997 CIXml xml(true);
1998 xml.init (inFile);
1999 zoneRegion.serial (xml);
2001 inFile.close();
2003 else
2005 nlwarning ("Can't open the file %s", LandFile.c_str());
2008 // Load all zone
2010 for (j = zoneRegion.getMinY(); j <= zoneRegion.getMaxY(); ++j)
2011 for (i = zoneRegion.getMinX(); i <= zoneRegion.getMaxX(); ++i)
2013 if ((zoneRegion.getName(i,j) == STRING_OUT_OF_BOUND) ||
2014 (zoneRegion.getName(i,j) == STRING_UNUSED))
2015 continue;
2017 vector<int> vegZone;
2018 // Take all Flora instances in the zone (i,j)
2019 for (k = 0; k < (sint32)_FloraInsts.size(); ++k)
2021 if (((i*_Options->CellSize) < _FloraInsts[k].Pos.x) && (_FloraInsts[k].Pos.x < ((i+1)*_Options->CellSize)) &&
2022 ((j*_Options->CellSize) < _FloraInsts[k].Pos.y) && (_FloraInsts[k].Pos.y < ((j+1)*_Options->CellSize)))
2024 vegZone.push_back (k);
2029 // Make the .IG
2030 string ZoneName;
2031 ZoneName += NLMISC::toString(-j) + "_";
2032 ZoneName += 'a' + (i/26);
2033 ZoneName += 'a' + (i%26);
2035 CVector vGlobalPos = CVector (0.0f, 0.0f, 0.0f);
2036 CInstanceGroup::TInstanceArray Instances;
2037 vector<CCluster> Portals;
2038 vector<CPortal> Clusters;
2039 Instances.resize (vegZone.size());
2041 for (k = 0; k < (sint32)vegZone.size(); ++k)
2043 //vGlobalPos += _FloraInsts[vegZone[k]].Pos;
2044 Instances[k].Pos = _FloraInsts[vegZone[k]].Pos;
2045 Instances[k].Rot = CQuat(CVector::K, _FloraInsts[vegZone[k]].Rot);
2046 Instances[k].Scale = CVector(_FloraInsts[vegZone[k]].Scale, _FloraInsts[vegZone[k]].Scale, _FloraInsts[vegZone[k]].Scale);
2047 Instances[k].nParent = -1;
2048 Instances[k].Name = _FloraInsts[vegZone[k]].ShapeName;
2049 Instances[k].InstanceName = _FloraInsts[vegZone[k]].PlantName;
2050 /*Instances[k].InstanceName = "Flora_"; // see if it works
2051 Instances[k].InstanceName += ZoneName + "_";
2052 Instances[k].InstanceName += '0' + ((k/1000)%10);
2053 Instances[k].InstanceName += '0' + ((k/100) %10);
2054 Instances[k].InstanceName += '0' + ((k/10) %10);
2055 Instances[k].InstanceName += '0' + ( k %10);*/
2058 // \todo trap -> look why it dont seems to work with a global positionning
2059 //vGlobalPos /= (float)vegZone.size();
2060 //for (k = 0; k < (sint32)vegZone.size(); ++k)
2061 // Instances[k].Pos -= vGlobalPos;
2063 CInstanceGroup IG;
2064 IG.build (vGlobalPos, Instances, Portals, Clusters);
2066 ZoneName = _OutIGDir + "\\" + ZoneName;
2067 ZoneName += ".ig";
2069 CIFile inFile; // If file already exists and we have selection...
2070 if (bTestForWriting)
2071 if (inFile.open(ZoneName))
2073 inFile.close();
2074 continue;
2079 COFile outFile (ZoneName);
2080 IG.serial (outFile);
2081 if (_ExportCB != NULL)
2082 _ExportCB->dispInfo (ZoneName + " generated");
2084 catch (const Exception &e)
2086 if (_ExportCB != NULL)
2087 _ExportCB->dispWarning ("Cant write " + ZoneName + " (" + e.what() + ")");
2092 // ---------------------------------------------------------------------------
2093 void CExport::loadLandscape (const string &LandFile)
2095 CZoneRegion zoneRegion;
2097 CIFile inFile;
2100 if (inFile.open (LandFile))
2102 CIXml xml(true);
2103 xml.init (inFile);
2104 zoneRegion.serial (xml);
2106 inFile.close();
2108 else
2110 if (_ExportCB != NULL)
2111 _ExportCB->dispWarning (string("Can't open file ") + LandFile);
2114 catch (const Exception &e)
2116 if (_ExportCB != NULL)
2117 _ExportCB->dispWarning (string("Cant load ") + LandFile + " : " + e.what());
2119 // Load all zone
2121 sint32 nTotalFile = (1 + zoneRegion.getMaxY() - zoneRegion.getMinY()) * (1 + zoneRegion.getMaxX() - zoneRegion.getMinX());
2122 sint32 nCurrentFile = 0;
2123 for (sint32 j = zoneRegion.getMinY(); j <= zoneRegion.getMaxY(); ++j)
2124 for (sint32 i = zoneRegion.getMinX(); i <= zoneRegion.getMaxX(); ++i)
2126 ++nCurrentFile;
2127 if (_ExportCB != NULL)
2128 _ExportCB->dispPassProgress(((float)nCurrentFile)/((float)nTotalFile));
2130 if ((zoneRegion.getName(i,j) == STRING_OUT_OF_BOUND) ||
2131 (zoneRegion.getName(i,j) == STRING_UNUSED))
2132 continue;
2134 // Generate zone name
2136 string ZoneName = getZoneNameFromXY (i, j);
2138 ZoneName = _InLandscapeDir + string("\\") + ZoneName;
2140 //if (_ExportCB != NULL)
2141 // _ExportCB->dispInfo (string("Loading ") + ZoneName);
2145 CZone zone;
2146 if (!inFile.open (ZoneName + string(".zonew")))
2147 inFile.open (ZoneName + string(".zonel"));
2148 zone.serial (inFile);
2149 inFile.close ();
2150 _Landscape->addZone (zone);
2152 catch(const Exception &/*e*/)
2154 if (_ExportCB != NULL)
2155 _ExportCB->dispWarning (string("Cant load ") + ZoneName + string(".zone(l,w)"));
2157 if ((_ExportCB != NULL) && (_ExportCB->isCanceled()))
2158 return;
2163 // Public Helpers
2164 // **************
2166 // ---------------------------------------------------------------------------
2167 string CExport::getZoneNameFromXY (sint32 x, sint32 y)
2169 string tmp;
2171 if ((y>0) || (y<-255) || (x<0) || (x>255))
2172 return "NOT VALID";
2173 tmp = toString(-y) + "_";
2174 tmp += ('A' + (x/26));
2175 tmp += ('A' + (x%26));
2176 return tmp;
2179 // ---------------------------------------------------------------------------
2180 sint32 CExport::getXFromZoneName (const string &ZoneName)
2182 string xStr, yStr;
2183 uint32 i = 0;
2184 while (ZoneName[i] != '_')
2186 yStr += ZoneName[i]; ++i;
2187 if (i == ZoneName.size())
2188 return -1;
2190 ++i;
2191 while (i < ZoneName.size())
2193 xStr += ZoneName[i]; ++i;
2195 return ((xStr[0] - 'A')*26 + (xStr[1] - 'A'));
2198 // ---------------------------------------------------------------------------
2199 sint32 CExport::getYFromZoneName (const string &ZoneName)
2201 string xStr, yStr;
2202 uint32 i = 0;
2203 while (ZoneName[i] != '_')
2205 yStr += ZoneName[i]; ++i;
2206 if (i == ZoneName.size())
2207 return 1;
2209 ++i;
2210 while (i < ZoneName.size())
2212 xStr += ZoneName[i]; ++i;
2214 return -atoi(yStr.c_str());