Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / ryzom / tools / leveldesign / prim_export / main.cpp
blob4d219ba783ac271d6c6a3e2dd63c915e1a624ba1
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) 2010-2020 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 <stdio.h>
22 #include <string>
23 #include <sstream>
25 #include "nel/misc/config_file.h"
26 #include "nel/misc/file.h"
27 #include "nel/misc/i_xml.h"
28 #include "nel/misc/path.h"
29 #include "nel/misc/progress_callback.h"
31 #include "nel/ligo/primitive.h"
32 #include "nel/ligo/ligo_config.h"
34 #include "nel/georges/u_form.h"
35 #include "nel/georges/u_form_elm.h"
36 #include "nel/georges/u_form_loader.h"
38 #include "nel/3d/zone.h"
39 #include "nel/3d/landscape.h"
40 #include "nel/3d/scene_group.h"
42 #ifdef NL_OS_WINDOWS
43 #include <windows.h>
44 #endif // NL_OS_WINDOWS
46 // ***************************************************************************
49 Documentation
51 This tool generates ig from primitive files
53 - Load the landscape zone. Load all the *.zonew found in the InLandscapeDir and add them in a landscape
54 - For each primitive files found in PrimDirs
55 - Look for points with the "prim" primitive class
56 - Get the good height on the landscape
57 - Get the .plant georges file associed with the point
58 - Add an entry in the good ig file
59 - Set the shape filename
60 - Set the plant name
61 - Set the final position (x and y come from the primitive and z from height test with the landscape, the rotation comes from the primitive)
62 - Set the ig date
63 - The date is the most recent date between the .zonew .plant .primitive files associed with the ig.
64 - Snap to ground only modified or created ig
65 - Save the modified or created ig files
68 // ***************************************************************************
70 using namespace std;
71 using namespace NLMISC;
72 using namespace NL3D;
73 using namespace NLGEORGES;
74 using namespace NLLIGO;
76 // ***************************************************************************
77 // Utility functions
78 // ***************************************************************************
80 #define SELECTION_EPSILON 0.1f
82 #define BAR_LENGTH 21
84 const char *progressbar[BAR_LENGTH]=
86 "[ ]",
87 "[. ]",
88 "[.. ]",
89 "[... ]",
90 "[.... ]",
91 "[..... ]",
92 "[...... ]",
93 "[....... ]",
94 "[........ ]",
95 "[......... ]",
96 "[.......... ]",
97 "[........... ]",
98 "[............ ]",
99 "[............. ]",
100 "[.............. ]",
101 "[............... ]",
102 "[................ ]",
103 "[................. ]",
104 "[.................. ]",
105 "[................... ]",
106 "[....................]"
109 // ***************************************************************************
111 // Progress bar
112 class CMyCallback : public IProgressCallback
114 public:
115 void progress (float progress)
117 // Delta time, update max all the 300 ms
118 static sint64 time = CTime::getLocalTime ();
119 sint64 currentTime = CTime::getLocalTime ();
120 if ((currentTime - time) > 300)
122 // Crop the progress bar value
123 progress = getCropedValue (progress);
125 // Progress bar
126 char msg[512];
127 uint pgId= (uint)(progress*(float)BAR_LENGTH);
128 pgId= min(pgId, (uint)(BAR_LENGTH-1));
129 sprintf (msg, "\r%s: %s", DisplayString.c_str (), progressbar[pgId]);
130 uint i;
131 for (i=(uint)strlen(msg); i<79; i++)
132 msg[i]=' ';
133 msg[i]=0;
134 printf ("%s", msg);
135 printf ("\r");
137 time = currentTime;
142 // ***************************************************************************
144 sint getXFromZoneName (const string &ZoneName)
146 string xStr, yStr;
147 uint32 i = 0;
148 while (ZoneName[i] != '_')
150 yStr += ZoneName[i]; ++i;
151 if (i == ZoneName.size())
152 return -1;
154 ++i;
155 while (i < ZoneName.size())
157 xStr += ZoneName[i]; ++i;
159 return ((xStr[0] - 'A')*26 + (xStr[1] - 'A'));
162 // ***************************************************************************
164 bool getZoneCoordByName(const char * name, uint16& x, uint16& y)
166 uint i;
168 std::string zoneName(name);
170 // y
171 std::string::size_type ind1 = zoneName.find("_");
172 if(ind1 == std::string::npos)
174 nlwarning("bad file name");
175 return false;
177 std::string ystr = zoneName.substr(0,ind1);
178 for(i=0; i<ystr.length(); i++)
180 if(!isdigit(ystr[i]))
182 nlwarning("y code size is not a 2 characters code");
183 return false;
187 NLMISC::fromString(ystr, y);
188 y = -y;
190 // x
191 x = 0;
192 uint ind2 = (uint)zoneName.length();
193 if((ind2-ind1-1)!=2)
195 nlwarning("x code size is not a 2 characters code");
196 return false;
198 std::string xstr = zoneName.substr(ind1+1,ind2-ind1-1);
199 for(i=0; i<xstr.length(); i++)
201 if (isalpha(xstr[i]))
203 x *= 26;
204 x += (tolower(xstr[i])-'a');
206 else
208 nlwarning("invalid");
209 return false;
212 return true;
215 // ***************************************************************************
217 sint getYFromZoneName (const string &ZoneName)
219 string xStr, yStr;
220 uint32 i = 0;
221 while (ZoneName[i] != '_')
223 yStr += ZoneName[i]; ++i;
224 if (i == ZoneName.size())
225 return 1;
227 ++i;
228 while (i < ZoneName.size())
230 xStr += ZoneName[i]; ++i;
233 sint y = 0;
234 NLMISC::fromString(yStr, y);
236 return -y;
239 // ***************************************************************************
241 void outString (const string &sText)
243 createDebug ();
244 InfoLog->displayRaw(sText.c_str());
247 // ***************************************************************************
249 string getPrimitiveName (const IPrimitive &primitive)
251 string name;
252 primitive.getPropertyByName ("name", name);
253 return name;
256 // ***************************************************************************
258 uint16 getZoneId (sint x, sint y)
260 return (uint16)((((-y)-1)<<8) + x);
263 // ***************************************************************************
265 void getLettersFromNum(uint16 num, std::string& code)
267 if(num>26*26)
269 nlwarning("zone index too high");
270 return;
272 code.resize(0);
273 uint16 remainder = num%26;
274 code += 'A' + num/26;
275 code += 'A' + remainder;
278 // ***************************************************************************
280 void getZoneNameByCoord(sint16 x, sint16 y, std::string& zoneName)
282 if ((y>0) || (y<-255) || (x<0) || (x>255))
283 return;
284 zoneName = toString(-y) + "_";
285 zoneName += ('A' + (x/26));
286 zoneName += ('A' + (x%26));
289 // ***************************************************************************
291 bool triangleIntersect2DGround (const CTriangle &tri, const CVector &pos0)
293 const CVector &p0= tri.V0;
294 const CVector &p1= tri.V1;
295 const CVector &p2= tri.V2;
297 // Test if the face enclose the pos in X/Y plane.
298 // NB: compute and using a BBox to do a rapid test is not a very good idea, since it will
299 // add an overhead which is NOT negligeable compared to the following test.
300 float a,b,c; // 2D cartesian coefficients of line in plane X/Y.
301 // Line p0-p1.
302 a= -(p1.y-p0.y);
303 b= (p1.x-p0.x);
304 c= -(p0.x*a + p0.y*b);
305 if( (a*pos0.x + b*pos0.y + c) < 0) return false;
306 // Line p1-p2.
307 a= -(p2.y-p1.y);
308 b= (p2.x-p1.x);
309 c= -(p1.x*a + p1.y*b);
310 if( (a*pos0.x + b*pos0.y + c) < 0) return false;
311 // Line p2-p0.
312 a= -(p0.y-p2.y);
313 b= (p0.x-p2.x);
314 c= -(p2.x*a + p2.y*b);
315 if( (a*pos0.x + b*pos0.y + c) < 0) return false;
317 return true;
320 // ***************************************************************************
321 // CExportOptions
322 // ***************************************************************************
324 struct CExportOptions
326 std::string InLandscapeDir; // Directory where to get .zonew files
327 std::string OutIGDir; // Directory where to put IG
328 std::string LandBankFile; // The .smallbank file associated with the landscape
329 std::string LandFarBankFile; // The .farbank file
330 float CellSize; // Typically 160.0
331 std::string LandTileNoiseDir; // Directory where to get displacement map
333 std::vector<std::string> PrimDirs; // Directory to parse for .flora and .prim associated
334 // This is here we get continent.cfg file
335 std::string FormDir; // Directory to get georges dfn
336 std::string WorldEditorFiles;
338 CExportOptions ();
339 bool loadcf (NLMISC::CConfigFile &cf);
342 // ***************************************************************************
344 CExportOptions::CExportOptions ()
346 CellSize = 160.0f;
349 // ***************************************************************************
351 bool CExportOptions::loadcf (CConfigFile &cf)
353 // Out
354 CConfigFile::CVar &cvOutIGDir = cf.getVar("OutIGDir");
355 OutIGDir = cvOutIGDir.asString();
357 // In
358 CConfigFile::CVar &cvInLandscapeDir = cf.getVar("ZoneWDir");
359 InLandscapeDir = cvInLandscapeDir.asString();
361 CConfigFile::CVar &cvLandBankFile = cf.getVar("SmallBank");
362 LandBankFile = cvLandBankFile.asString();
363 CConfigFile::CVar &cvLandFarBankFile = cf.getVar("FarBank");
364 LandFarBankFile = cvLandFarBankFile.asString();
365 CConfigFile::CVar &cvLandTileNoiseDir = cf.getVar("DisplaceDir");
366 LandTileNoiseDir = cvLandTileNoiseDir.asString();
368 CConfigFile::CVar &cvCellSize = cf.getVar("CellSize");
369 CellSize = cvCellSize.asFloat();
371 CConfigFile::CVar &cvPrimDir = cf.getVar("PrimDirs");
372 uint i;
373 PrimDirs.resize (cvPrimDir.size());
374 for (i=0; i<(uint)cvPrimDir.size(); i++)
375 PrimDirs[i] = cvPrimDir.asString(i);
377 CConfigFile::CVar &cvFormDir = cf.getVar("FormDir");
378 FormDir = cvFormDir.asString();
380 CConfigFile::CVar &cvWorldEditorFiles = cf.getVar("WorldEditorFiles");
381 WorldEditorFiles = cvWorldEditorFiles.asString();
383 return true;
386 // ***************************************************************************
388 // Array of ig
389 class CIgContainer
391 public:
393 // An ig
394 class CIG
396 public:
397 // Additionnal parameters
398 class CAdditionnalParam
400 public:
401 CAdditionnalParam (uint snapLayer, const string &primitiveName, const string &primitiveFile, bool snap) : SnapLayer (snapLayer), PrimitiveName (primitiveName), PrimitiveFile (primitiveFile)
403 Snap = snap;
406 // Snap over the landscape
407 bool Snap;
409 // Snap layer
410 uint SnapLayer;
412 // Primitive name
413 string PrimitiveName;
415 // Primitive file
416 string PrimitiveFile;
419 // Default ctor
420 CIG ()
422 Date = 0;
425 // Update date if new date is more recent
426 void updateDate (uint32 newDate)
428 if (newDate > Date)
429 Date = newDate;
432 // The ig
433 CInstanceGroup::TInstanceArray Instances;
435 // Additionnal information
436 vector<CAdditionnalParam> AdditionnalInfo;
438 // The ig date
439 uint32 Date;
442 // Init the container
443 void init (sint minx, sint maxx, sint miny, sint maxy, const char *zoneDir)
445 // Save the values
446 Minx = minx;
447 Miny = miny;
448 Maxx = maxx+1;
449 Maxy = maxy+1;
450 Width = maxx - minx + 1;
452 // Resize the array
453 IGS.clear ();
454 IGS.resize (Width*(maxy-miny+1));
456 // Directory
457 string dir = CPath::standardizePath (zoneDir, true);
459 // For each zone
460 for (sint y=miny; y<=maxy; y++)
461 for (sint x=minx; x<=maxx; x++)
463 // The zone name
464 string zoneFilename;
465 getZoneNameByCoord ((sint16)x, (sint16)y, zoneFilename);
466 zoneFilename = dir + zoneFilename + ".zonew";
468 // Get the date
469 if (CFile::fileExists (zoneFilename))
470 get (x, y).Date = CFile::getFileModificationDate (zoneFilename);
474 // Get the ig
475 CIG &get (sint x, sint y)
477 return IGS[(x-Minx)+(y-Miny)*Width];
480 // Size and position
481 uint Width;
482 sint Minx;
483 sint Miny;
484 sint Maxx;
485 sint Maxy;
487 // The ig vector
488 vector<CIG> IGS;
491 // ***************************************************************************
493 // Array of ig
494 class CFormContainer
496 private:
497 // The map value
498 struct CValue
500 CValue (CSmartPtr<UForm> ptr, uint32 date) : Ptr (ptr), Date (date) {};
502 // The form pointer
503 CSmartPtr<UForm> Ptr;
505 // Its date
506 uint32 Date;
508 public:
510 // Default ctor
511 CFormContainer ()
513 _FormLoader = UFormLoader::createLoader ();
516 // Dtor
517 ~CFormContainer ()
519 UFormLoader::releaseLoader (_FormLoader);
522 // The form container
523 const UForm *loadForm (const char *formName, uint32 &formDate)
525 // The form
526 UForm *form = NULL;
527 formDate = 0;
529 // In the map ?
530 string formShortName = NLMISC::toLowerAscii(CFile::getFilename (formName));
531 map<string, CValue >::iterator ite = _FormMap.find (formShortName);
532 if (ite == _FormMap.end ())
534 // Look for this plant file
535 string path = CPath::lookup (formName, false, false, false);
536 if (!path.empty ())
538 // Load it !
539 form = _FormLoader->loadForm (path.c_str ());
540 if (form)
542 // Get dependencies
543 set<string> dependencies;
544 form->getDependencies (dependencies);
546 // Get dependencies dates
547 formDate = 0;
548 set<string>::const_iterator ite = dependencies.begin ();
549 while (ite != dependencies.end ())
551 // Get the path name
552 string path = CPath::lookup (*ite, false, false, false);
553 if (!path.empty ())
555 // Get the file date
556 uint32 date = CFile::getFileModificationDate (path);
558 // Update date
559 if (date > formDate)
560 formDate = date;
563 // Next dependency
564 ite++;
567 // Add it
568 _FormMap.insert (map<string, CValue >::value_type (formShortName, CValue (form, formDate)));
570 else
572 // Error in the log
573 nlwarning ("Error : Can't load the form (%s)", path.c_str ());
577 else
579 form = ite->second.Ptr;
580 formDate = ite->second.Date;
583 // Return the form or NULL
584 return form;
587 private:
589 // The form loader
590 UFormLoader *_FormLoader;
592 // The form map
593 map<string, CValue > _FormMap;
596 // ***************************************************************************
598 void addPointPrimitive (CLandscape &landscape, const char *primFilename, uint32 primFileDate, const IPrimitive &primitive, CIgContainer &igs,
599 const CExportOptions &options, CFormContainer &formContainer, IProgressCallback &callback)
601 // Is this primitive a point ?
602 const CPrimPoint *point = dynamic_cast<const CPrimPoint *>(&primitive);
603 if (point)
605 // Get the class name
606 string className;
607 if (point->getPropertyByName ("class", className))
609 // Is it a plant ?
610 if (className == "prim")
612 // Get its plant name
613 string plantFilename;
614 if (point->getPropertyByName ("form", plantFilename))
616 // Add an extension
617 if (NLMISC::toLowerAscii(CFile::getExtension (plantFilename)) != "plant")
618 plantFilename += ".plant";
620 // Load this form
621 uint32 formDate;
622 const UForm *form = formContainer.loadForm (plantFilename.c_str (), formDate);
623 if (form)
625 // Get the parameters
626 string shape;
627 if (form->getRootNode ().getValueByName (shape, "3D.Shape"))
629 // Get the position
630 CVector position = point->Point;
632 // Get the scale
633 string scaleText;
634 float scale = 1;
635 if (point->getPropertyByName ("scale", scaleText))
636 NLMISC::fromString(scaleText, scale);
638 // Get zone coordinates
639 sint x = (sint)floor (position.x / options.CellSize);
640 sint y = (sint)floor (position.y / options.CellSize);
641 if ( (x >= igs.Minx) && (x < igs.Maxx) && (y >= igs.Miny) && (y < igs.Maxy) )
643 // Get its layer
644 string text;
645 uint layer = 0;
646 if (point->getPropertyByName ("depth", text))
648 layer = atoi (text.c_str ());
651 // Not snap yet
652 position.z = 0;
654 // Snap flag
655 bool snap = true;
656 if (point->getPropertyByName ("snap", text))
657 snap = text != "false";
659 // Get height
660 if (!snap && point->getPropertyByName ("height", text))
661 NLMISC::fromString(text, position.z);
663 // *** Add the instance
665 // Create it
666 CInstanceGroup::CInstance instance;
667 instance.Pos = position;
668 instance.Rot = CQuat(CVector::K, point->Angle);
669 instance.Scale = CVector (scale, scale, scale);
670 instance.nParent = -1;
671 instance.Name = shape;
672 instance.InstanceName = NLMISC::toLowerAscii(CFile::getFilename (plantFilename));
674 // Get the instance group ref
675 CIgContainer::CIG &instances = igs.get (x, y);
676 instances.Instances.push_back (instance);
677 instances.AdditionnalInfo.push_back (CIgContainer::CIG::CAdditionnalParam (layer,
678 getPrimitiveName (primitive), primFilename, snap));
680 // Update the date with the primitive filename
681 instances.updateDate (primFileDate);
683 // Update the date with the plant filename
684 instances.updateDate (formDate);
686 // Update the date with the zone filename
687 string zoneFilename;
688 getZoneNameByCoord (x, y, zoneFilename);
689 zoneFilename = CPath::standardizePath (options.InLandscapeDir, true) + zoneFilename + ".zonew";
690 // todo hulud needed ? instances.updateDate (zoneFilename);
693 else
695 // Error in the log
696 nlwarning ("Error : Can't get a shape name in the form (%s) for the primitive (%s) in the file (%s)",
697 plantFilename.c_str (), getPrimitiveName (primitive).c_str (), primFilename);
700 else
702 // Error in the log
703 nlwarning ("Error : can't load the file (%s) used by the primitive (%s) in the file (%s).", plantFilename.c_str (),
704 getPrimitiveName (primitive).c_str (), primFilename);
707 else
709 // Error in the log
710 nlwarning ("Error : in file (%s), the primitive (%s) has no plant file.", primFilename, getPrimitiveName (primitive).c_str ());
716 // Look in children
717 uint numChildren = primitive.getNumChildren ();
718 for (uint i=0; i<numChildren; i++)
720 // Progress bar
721 callback.progress ((float)i/(float)numChildren);
722 callback.pushCropedValues ((float)i/(float)numChildren, (float)(i+1)/(float)numChildren);
724 // Get the child
725 const IPrimitive *child;
726 nlverify (primitive.getChild (child, i));
727 addPointPrimitive (landscape, primFilename, primFileDate, *child, igs, options, formContainer, callback);
729 // Progress bar
730 callback.popCropedValues ();
734 // ***************************************************************************
736 int main (int argc, char**argv)
738 new NLMISC::CApplicationContext;
740 // Filter addSearchPath
741 NLMISC::createDebug();
742 InfoLog->addNegativeFilter ("addSearchPath");
744 // Register ligo
745 NLLIGO::Register ();
747 if (argc != 2)
749 printf ("Use : prim_export configfile.cfg\n");
750 printf ("\nExample of config.cfg\n\n");
752 printf ("\n// Export Options\n");
753 printf ("OutIGDir = \"c:/temp/outIG\";\n");
754 printf ("ZoneWDir = \"c:/temp/inZoneW\";\n");
755 printf ("SmallBank = \"//amiga/3d/database/landscape/_texture_tiles/jungle/jungle.bank\";\n");
756 printf ("FarBank = \"//amiga/3d/database/landscape/_texture_tiles/jungle/jungle.farbank\";\n");
757 printf ("DisplaceDir = \"//amiga/3d/database/landscape/_texture_tiles/displace\";\n");
758 printf ("CellSize = 160.0;\n");
759 printf ("PrimDirs = {\"//server/leveldesign/world/fyros\"};\n");
761 return -1;
766 // Load the config file
767 CExportOptions options;
769 string sTmp = string("loading cfg file : ") + string(argv[1]) + "\n";
770 // outString(sTmp);
772 CConfigFile cf;
773 cf.load (argv[1]);
774 if (!options.loadcf(cf))
776 sTmp = "Error : options not loaded from config file\n";
777 outString (sTmp);
778 return -1;
782 // *** Add pathes in the search path for georges forms
784 CPath::addSearchPath (options.FormDir, true, true);
785 CPath::addSearchPath (options.WorldEditorFiles, true, true);
787 // Ligo config
788 CLigoConfig config;
789 string path = CPath::lookup ("world_editor_classes.xml", false, false, false);
790 if (path.empty())
791 nlwarning ("Error : File world_editor_classes.xml not found");
792 else
793 config.readPrimitiveClass (path.c_str(), false);
795 // *** Load the landscape
797 // Init the landscape
798 CLandscape landscape;
799 landscape.init ();
801 // Get file list
802 vector<string> files;
803 CPath::getPathContent (options.InLandscapeDir, false, false, true, files);
805 // Landscape bounding box
806 sint minx = 0x7fffffff;
807 sint miny = 0x7fffffff;
808 sint maxx = 0x80000000;
809 sint maxy = 0x80000000;
811 // The callback
812 CMyCallback callback;
814 // For each zone files
815 if (files.empty())
817 nlwarning ("Error : no zonew files found. Abort.");
819 else
821 uint i;
822 for (i=0; i<files.size (); i++)
824 // Progress
825 callback.DisplayString = "Loading zones";
826 callback.progress ((float)i/(float)files.size ());
828 // Zonew ?
829 if (NLMISC::toLowerAscii(CFile::getExtension (files[i])) == "zonew")
831 // Load it
834 // The zone
835 CZone zone;
837 // Load
838 CIFile inFile;
839 if (inFile.open (files[i]))
841 // Read the zone
842 zone.serial (inFile);
844 // Add the zone in the landscape
845 landscape.addZone (zone);
847 // Extand the bounding box
848 string name = CFile::getFilenameWithoutExtension (files[i]);
849 sint x = getXFromZoneName (name);
850 sint y = getYFromZoneName (name);
851 if (x<minx)
852 minx = x;
853 if (y<miny)
854 miny = y;
855 if (x>maxx)
856 maxx = x;
857 if (y>maxy)
858 maxy = y;
860 else
862 // Error in the log
863 nlwarning ("Error : can't open the file (%s) for reading", files[i].c_str ());
866 catch(const Exception &e)
868 // Error in the log
869 nlwarning ("Error loading zone file (%s) : %s", files[i].c_str (), e.what ());
874 // *** Create the igs
875 CIgContainer igs;
876 igs.init (minx, maxx, miny, maxy, options.InLandscapeDir.c_str ());
878 // *** Create a form container
879 CFormContainer formContainer;
881 // *** For each primitive files
883 // Get the primitive files liste
884 files.clear ();
885 for (i=0; i<options.PrimDirs.size(); i++)
886 CPath::getPathContent (options.PrimDirs[i], true, false, true, files);
888 // For each
889 uint fileCount = (uint)files.size ();
890 for (i=0; i<fileCount; i++)
892 // Primitive file ?
893 if (NLMISC::toLowerAscii(CFile::getExtension (files[i])) == "primitive")
895 // Progress bar
896 nlinfo (files[i].c_str());
897 callback.DisplayString = "Add primitives from "+CFile::getFilename(files[i]);
898 callback.progress ((float)i/(float)fileCount);
899 callback.pushCropedValues ((float)i/(float)fileCount, (float)(i+1)/(float)fileCount);
901 // Load it
904 // Load it
905 CIFile inFile;
906 if (inFile.open (files[i]))
908 // Open an xml stream
909 CIXml inXml;
910 inXml.init (inFile);
912 // Read it
913 CPrimitives primitives;
914 if (primitives.read (inXml.getRootNode (), files[i].c_str (), config))
916 // Look for primitives
917 addPointPrimitive (landscape, files[i].c_str (), CFile::getFileModificationDate (files[i]), *primitives.RootNode, igs, options, formContainer, callback);
919 else
921 // Error in the log
922 nlwarning ("Error : can't read the primitive file %s", files[i].c_str ());
925 else
927 // Error in the log
928 nlwarning ("Error : can't open the file (%s) for reading", files[i].c_str ());
931 catch(const Exception &e)
933 // Error in the log
934 nlwarning ("Error loading primitive file (%s) : %s", files[i].c_str (), e.what ());
937 // Progress bar
938 callback.popCropedValues ();
942 // *** Save igs
944 for (sint y=miny; y<=maxy; y++)
945 for (sint x=minx; x<=maxx; x++)
947 // Get the instance ref
948 CIgContainer::CIG &instance = igs.get (x, y);
950 // Good date ?
951 if (instance.Date)
953 // Get the final filename
954 string igFilename;
955 getZoneNameByCoord (x, y, igFilename);
956 igFilename = CPath::standardizePath (options.OutIGDir, true) + igFilename + ".ig";
958 // Something in the ig ?
959 if (!instance.Instances.empty ())
961 // Check date
962 if (CFile::fileExists (igFilename) && (instance.Date < CFile::getFileModificationDate (igFilename)))
964 outString ("SKIP " + CFile::getFilename (igFilename) + " \n");
966 else
968 // *** Snap to ground
970 // Get the zone bbox
971 const CZone *zone = landscape.getZone (getZoneId (x, y));
972 if (zone)
974 // Checks
975 nlassert (instance.Instances.size () == instance.AdditionnalInfo.size ());
977 // For each instances
978 uint i;
979 for (i=0; i<instance.Instances.size (); i++)
981 // Have to snap it ?
982 if (instance.AdditionnalInfo[i].Snap)
984 // Progress bar
985 callback.DisplayString = "Snap to ground " + CFile::getFilename (igFilename);
986 callback.progress ((float)i/(float)instance.Instances.size ());
988 // Get the zone bbox
989 CAABBoxExt zoneBBox = zone->getZoneBB ();
991 // The bbox used to select triangles
992 CAABBox bbox;
993 CVector &position = instance.Instances[i].Pos;
994 bbox.setCenter (CVector (position.x + SELECTION_EPSILON, position.y + SELECTION_EPSILON, zoneBBox.getMax ().z + SELECTION_EPSILON));
995 bbox.extend (CVector (position.x - SELECTION_EPSILON, position.y - SELECTION_EPSILON, zoneBBox.getMin ().z - SELECTION_EPSILON));
997 // Select some triangles
998 vector<CTrianglePatch> triangles;
999 landscape.buildTrianglesInBBox (bbox, triangles, 0);
1001 // Ray trace triangles
1002 set<float> selectedHeight;
1003 uint j;
1004 for (j=0; j<triangles.size (); j++)
1006 // Ref on triangle
1007 const CTriangle &triangle = triangles[j];
1009 // Test this triangle
1010 if (triangleIntersect2DGround (triangle, position))
1012 // build the plane
1013 CPlane plane;
1014 plane.make (triangle.V0, triangle.V1, triangle.V2);
1016 // Get the final height
1017 CVector intersect = plane.intersect (bbox.getMin (), bbox.getMax ());
1018 selectedHeight.insert (intersect.z);
1022 // Get its layer
1023 uint layer = instance.AdditionnalInfo[i].SnapLayer;
1025 // Found some triangles ?
1026 const uint setSize = (uint)selectedHeight.size ();
1027 if (setSize)
1029 // Look for the triangle in the good layer
1030 uint currentLayer = 0;
1032 // Layer exist ?
1033 if (layer >= setSize)
1035 // Error in the log
1036 nlwarning ("Error : Layer %d used by the primitive (%s) in the file (%s) doesn't exist. Select layer %d instead.",
1037 layer, instance.AdditionnalInfo[i].PrimitiveName.c_str (),
1038 instance.AdditionnalInfo[i].PrimitiveFile.c_str (), setSize-1);
1040 // New layer
1041 layer = setSize-1;
1044 // Invert the layer number
1045 layer = setSize - layer - 1;
1047 set<float>::iterator ite = selectedHeight.begin ();
1048 while (ite != selectedHeight.end ())
1050 // Good layer ?
1051 if (currentLayer == layer)
1052 break;
1054 // Next layer
1055 currentLayer++;
1056 ite++;
1059 // Should be found
1060 nlassert (ite != selectedHeight.end ());
1062 // Get the final height
1063 position.z = *ite;
1065 else
1067 // Error in the log
1068 nlwarning ("Error : No landscape under the primitive (%s) in the file (%s).",
1069 instance.AdditionnalInfo[i].PrimitiveName.c_str (), instance.AdditionnalInfo[i].PrimitiveFile.c_str ());
1074 else
1076 // Error in the log
1077 nlwarning ("Error : No landscape for the zone (%s)", CFile::getFilename (igFilename).c_str ());
1080 // Build an instance group
1081 CInstanceGroup ig;
1082 CVector vGlobalPos = CVector::Null;
1083 vector<CCluster> Portals;
1084 vector<CPortal> Clusters;
1085 ig.build (vGlobalPos, instance.Instances, Portals, Clusters);
1087 // *** Save the ig file
1089 try
1091 // The file
1092 COFile outFile;
1093 if (outFile.open (igFilename))
1095 ig.serial (outFile);
1097 // Done
1098 outString ("OK " + CFile::getFilename (igFilename) + " \n");
1100 else
1102 // Error in the log
1103 nlwarning ("Error : can't open the file (%s) for writing.", igFilename.c_str ());
1106 catch (const Exception &e)
1108 // Error in the log
1109 nlwarning ("Error writing the file (%s) : %s", igFilename.c_str (), e.what ());
1113 else
1115 // File exist ?
1116 if (CFile::fileExists (igFilename))
1118 // Done
1119 outString ("REMOVE " + CFile::getFilename (igFilename) + " \n");
1121 // Remove it
1122 if (!CFile::deleteFile(igFilename))
1124 // Error in the log
1125 nlwarning ("Error : Can't remove the file (%s)", igFilename.c_str ());
1133 catch (const Exception &e)
1135 string sTmp = string("ERROR : ") + e.what();
1136 outString (sTmp);
1139 return 1;
1159 // *** Snap to the ground
1161 // Get zone coordinates
1162 sint x = (sint)floor (position.x / options.CellSize);
1163 sint y = (sint)floor (position.y / options.CellSize);