1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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>
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/>.
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"
44 #endif // NL_OS_WINDOWS
46 // ***************************************************************************
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
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)
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 // ***************************************************************************
71 using namespace NLMISC
;
73 using namespace NLGEORGES
;
74 using namespace NLLIGO
;
76 // ***************************************************************************
78 // ***************************************************************************
80 #define SELECTION_EPSILON 0.1f
84 const char *progressbar
[BAR_LENGTH
]=
101 "[............... ]",
102 "[................ ]",
103 "[................. ]",
104 "[.................. ]",
105 "[................... ]",
106 "[....................]"
109 // ***************************************************************************
112 class CMyCallback
: public IProgressCallback
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
);
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
]);
131 for (i
=(uint
)strlen(msg
); i
<79; i
++)
142 // ***************************************************************************
144 sint
getXFromZoneName (const string
&ZoneName
)
148 while (ZoneName
[i
] != '_')
150 yStr
+= ZoneName
[i
]; ++i
;
151 if (i
== ZoneName
.size())
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
)
168 std::string
zoneName(name
);
171 std::string::size_type ind1
= zoneName
.find("_");
172 if(ind1
== std::string::npos
)
174 nlwarning("bad file name");
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");
187 NLMISC::fromString(ystr
, y
);
192 uint ind2
= (uint
)zoneName
.length();
195 nlwarning("x code size is not a 2 characters code");
198 std::string xstr
= zoneName
.substr(ind1
+1,ind2
-ind1
-1);
199 for(i
=0; i
<xstr
.length(); i
++)
201 if (isalpha(xstr
[i
]))
204 x
+= (tolower(xstr
[i
])-'a');
208 nlwarning("invalid");
215 // ***************************************************************************
217 sint
getYFromZoneName (const string
&ZoneName
)
221 while (ZoneName
[i
] != '_')
223 yStr
+= ZoneName
[i
]; ++i
;
224 if (i
== ZoneName
.size())
228 while (i
< ZoneName
.size())
230 xStr
+= ZoneName
[i
]; ++i
;
234 NLMISC::fromString(yStr
, y
);
239 // ***************************************************************************
241 void outString (const string
&sText
)
244 InfoLog
->displayRaw(sText
.c_str());
247 // ***************************************************************************
249 string
getPrimitiveName (const IPrimitive
&primitive
)
252 primitive
.getPropertyByName ("name", 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
)
269 nlwarning("zone index too high");
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))
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.
304 c
= -(p0
.x
*a
+ p0
.y
*b
);
305 if( (a
*pos0
.x
+ b
*pos0
.y
+ c
) < 0) return false;
309 c
= -(p1
.x
*a
+ p1
.y
*b
);
310 if( (a
*pos0
.x
+ b
*pos0
.y
+ c
) < 0) return false;
314 c
= -(p2
.x
*a
+ p2
.y
*b
);
315 if( (a
*pos0
.x
+ b
*pos0
.y
+ c
) < 0) return false;
320 // ***************************************************************************
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
;
339 bool loadcf (NLMISC::CConfigFile
&cf
);
342 // ***************************************************************************
344 CExportOptions::CExportOptions ()
349 // ***************************************************************************
351 bool CExportOptions::loadcf (CConfigFile
&cf
)
354 CConfigFile::CVar
&cvOutIGDir
= cf
.getVar("OutIGDir");
355 OutIGDir
= cvOutIGDir
.asString();
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");
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();
386 // ***************************************************************************
397 // Additionnal parameters
398 class CAdditionnalParam
401 CAdditionnalParam (uint snapLayer
, const string
&primitiveName
, const string
&primitiveFile
, bool snap
) : SnapLayer (snapLayer
), PrimitiveName (primitiveName
), PrimitiveFile (primitiveFile
)
406 // Snap over the landscape
413 string PrimitiveName
;
416 string PrimitiveFile
;
425 // Update date if new date is more recent
426 void updateDate (uint32 newDate
)
433 CInstanceGroup::TInstanceArray Instances
;
435 // Additionnal information
436 vector
<CAdditionnalParam
> AdditionnalInfo
;
442 // Init the container
443 void init (sint minx
, sint maxx
, sint miny
, sint maxy
, const char *zoneDir
)
450 Width
= maxx
- minx
+ 1;
454 IGS
.resize (Width
*(maxy
-miny
+1));
457 string dir
= CPath::standardizePath (zoneDir
, true);
460 for (sint y
=miny
; y
<=maxy
; y
++)
461 for (sint x
=minx
; x
<=maxx
; x
++)
465 getZoneNameByCoord ((sint16
)x
, (sint16
)y
, zoneFilename
);
466 zoneFilename
= dir
+ zoneFilename
+ ".zonew";
469 if (CFile::fileExists (zoneFilename
))
470 get (x
, y
).Date
= CFile::getFileModificationDate (zoneFilename
);
475 CIG
&get (sint x
, sint y
)
477 return IGS
[(x
-Minx
)+(y
-Miny
)*Width
];
491 // ***************************************************************************
500 CValue (CSmartPtr
<UForm
> ptr
, uint32 date
) : Ptr (ptr
), Date (date
) {};
503 CSmartPtr
<UForm
> Ptr
;
513 _FormLoader
= UFormLoader::createLoader ();
519 UFormLoader::releaseLoader (_FormLoader
);
522 // The form container
523 const UForm
*loadForm (const char *formName
, uint32
&formDate
)
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);
539 form
= _FormLoader
->loadForm (path
.c_str ());
543 set
<string
> dependencies
;
544 form
->getDependencies (dependencies
);
546 // Get dependencies dates
548 set
<string
>::const_iterator ite
= dependencies
.begin ();
549 while (ite
!= dependencies
.end ())
552 string path
= CPath::lookup (*ite
, false, false, false);
556 uint32 date
= CFile::getFileModificationDate (path
);
568 _FormMap
.insert (map
<string
, CValue
>::value_type (formShortName
, CValue (form
, formDate
)));
573 nlwarning ("Error : Can't load the form (%s)", path
.c_str ());
579 form
= ite
->second
.Ptr
;
580 formDate
= ite
->second
.Date
;
583 // Return the form or NULL
590 UFormLoader
*_FormLoader
;
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
);
605 // Get the class name
607 if (point
->getPropertyByName ("class", className
))
610 if (className
== "prim")
612 // Get its plant name
613 string plantFilename
;
614 if (point
->getPropertyByName ("form", plantFilename
))
617 if (NLMISC::toLowerAscii(CFile::getExtension (plantFilename
)) != "plant")
618 plantFilename
+= ".plant";
622 const UForm
*form
= formContainer
.loadForm (plantFilename
.c_str (), formDate
);
625 // Get the parameters
627 if (form
->getRootNode ().getValueByName (shape
, "3D.Shape"))
630 CVector position
= point
->Point
;
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
) )
646 if (point
->getPropertyByName ("depth", text
))
648 layer
= atoi (text
.c_str ());
656 if (point
->getPropertyByName ("snap", text
))
657 snap
= text
!= "false";
660 if (!snap
&& point
->getPropertyByName ("height", text
))
661 NLMISC::fromString(text
, position
.z
);
663 // *** Add the instance
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
688 getZoneNameByCoord (x
, y
, zoneFilename
);
689 zoneFilename
= CPath::standardizePath (options
.InLandscapeDir
, true) + zoneFilename
+ ".zonew";
690 // todo hulud needed ? instances.updateDate (zoneFilename);
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
);
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
);
710 nlwarning ("Error : in file (%s), the primitive (%s) has no plant file.", primFilename
, getPrimitiveName (primitive
).c_str ());
717 uint numChildren
= primitive
.getNumChildren ();
718 for (uint i
=0; i
<numChildren
; i
++)
721 callback
.progress ((float)i
/(float)numChildren
);
722 callback
.pushCropedValues ((float)i
/(float)numChildren
, (float)(i
+1)/(float)numChildren
);
725 const IPrimitive
*child
;
726 nlverify (primitive
.getChild (child
, i
));
727 addPointPrimitive (landscape
, primFilename
, primFileDate
, *child
, igs
, options
, formContainer
, callback
);
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");
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");
766 // Load the config file
767 CExportOptions options
;
769 string sTmp
= string("loading cfg file : ") + string(argv
[1]) + "\n";
774 if (!options
.loadcf(cf
))
776 sTmp
= "Error : options not loaded from config file\n";
782 // *** Add pathes in the search path for georges forms
784 CPath::addSearchPath (options
.FormDir
, true, true);
785 CPath::addSearchPath (options
.WorldEditorFiles
, true, true);
789 string path
= CPath::lookup ("world_editor_classes.xml", false, false, false);
791 nlwarning ("Error : File world_editor_classes.xml not found");
793 config
.readPrimitiveClass (path
.c_str(), false);
795 // *** Load the landscape
797 // Init the landscape
798 CLandscape landscape
;
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;
812 CMyCallback callback
;
814 // For each zone files
817 nlwarning ("Error : no zonew files found. Abort.");
822 for (i
=0; i
<files
.size (); i
++)
825 callback
.DisplayString
= "Loading zones";
826 callback
.progress ((float)i
/(float)files
.size ());
829 if (NLMISC::toLowerAscii(CFile::getExtension (files
[i
])) == "zonew")
839 if (inFile
.open (files
[i
]))
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
);
863 nlwarning ("Error : can't open the file (%s) for reading", files
[i
].c_str ());
866 catch(const Exception
&e
)
869 nlwarning ("Error loading zone file (%s) : %s", files
[i
].c_str (), e
.what ());
874 // *** Create the 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
885 for (i
=0; i
<options
.PrimDirs
.size(); i
++)
886 CPath::getPathContent (options
.PrimDirs
[i
], true, false, true, files
);
889 uint fileCount
= (uint
)files
.size ();
890 for (i
=0; i
<fileCount
; i
++)
893 if (NLMISC::toLowerAscii(CFile::getExtension (files
[i
])) == "primitive")
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
);
906 if (inFile
.open (files
[i
]))
908 // Open an xml stream
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
);
922 nlwarning ("Error : can't read the primitive file %s", files
[i
].c_str ());
928 nlwarning ("Error : can't open the file (%s) for reading", files
[i
].c_str ());
931 catch(const Exception
&e
)
934 nlwarning ("Error loading primitive file (%s) : %s", files
[i
].c_str (), e
.what ());
938 callback
.popCropedValues ();
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
);
953 // Get the final filename
955 getZoneNameByCoord (x
, y
, igFilename
);
956 igFilename
= CPath::standardizePath (options
.OutIGDir
, true) + igFilename
+ ".ig";
958 // Something in the ig ?
959 if (!instance
.Instances
.empty ())
962 if (CFile::fileExists (igFilename
) && (instance
.Date
< CFile::getFileModificationDate (igFilename
)))
964 outString ("SKIP " + CFile::getFilename (igFilename
) + " \n");
968 // *** Snap to ground
971 const CZone
*zone
= landscape
.getZone (getZoneId (x
, y
));
975 nlassert (instance
.Instances
.size () == instance
.AdditionnalInfo
.size ());
977 // For each instances
979 for (i
=0; i
<instance
.Instances
.size (); i
++)
982 if (instance
.AdditionnalInfo
[i
].Snap
)
985 callback
.DisplayString
= "Snap to ground " + CFile::getFilename (igFilename
);
986 callback
.progress ((float)i
/(float)instance
.Instances
.size ());
989 CAABBoxExt zoneBBox
= zone
->getZoneBB ();
991 // The bbox used to select triangles
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
;
1004 for (j
=0; j
<triangles
.size (); j
++)
1007 const CTriangle
&triangle
= triangles
[j
];
1009 // Test this triangle
1010 if (triangleIntersect2DGround (triangle
, position
))
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
);
1023 uint layer
= instance
.AdditionnalInfo
[i
].SnapLayer
;
1025 // Found some triangles ?
1026 const uint setSize
= (uint
)selectedHeight
.size ();
1029 // Look for the triangle in the good layer
1030 uint currentLayer
= 0;
1033 if (layer
>= setSize
)
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);
1044 // Invert the layer number
1045 layer
= setSize
- layer
- 1;
1047 set
<float>::iterator ite
= selectedHeight
.begin ();
1048 while (ite
!= selectedHeight
.end ())
1051 if (currentLayer
== layer
)
1060 nlassert (ite
!= selectedHeight
.end ());
1062 // Get the final height
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 ());
1077 nlwarning ("Error : No landscape for the zone (%s)", CFile::getFilename (igFilename
).c_str ());
1080 // Build an instance group
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
1093 if (outFile
.open (igFilename
))
1095 ig
.serial (outFile
);
1098 outString ("OK " + CFile::getFilename (igFilename
) + " \n");
1103 nlwarning ("Error : can't open the file (%s) for writing.", igFilename
.c_str ());
1106 catch (const Exception
&e
)
1109 nlwarning ("Error writing the file (%s) : %s", igFilename
.c_str (), e
.what ());
1116 if (CFile::fileExists (igFilename
))
1119 outString ("REMOVE " + CFile::getFilename (igFilename
) + " \n");
1122 if (!CFile::deleteFile(igFilename
))
1125 nlwarning ("Error : Can't remove the file (%s)", igFilename
.c_str ());
1133 catch (const Exception
&e
)
1135 string sTmp
= string("ERROR : ") + e
.what();
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);