1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2019-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // This utility is intended to rescue a lost .land file from existing
18 // runtime zones. It does not recover the heightmap.
20 // Author: Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
22 // #include "../../3d/zone_lib/zone_utility.h"
25 #include <nel/misc/types_nl.h>
26 #include <nel/misc/file.h>
27 #include <nel/misc/o_xml.h>
28 #include <nel/misc/common.h>
29 #include <nel/misc/cmd_args.h>
30 #include <nel/misc/bitmap.h>
31 //#include <nel/3d/quad_tree.h>
32 #include <nel/3d/zone.h>
33 //#include <nel/3d/landscape.h>
34 //#include <nel/3d/zone_smoother.h>
35 //#include <nel/3d/zone_tgt_smoother.h>
36 //#include <nel/3d/zone_corner_smoother.h>
37 #include <nel/ligo/zone_region.h>
42 using namespace NLMISC
;
43 using namespace NLLIGO
;
46 namespace /* anonymous */
49 sint32 s_ZoneMinX
, s_ZoneMinY
, s_ZoneMaxX
, s_ZoneMaxY
;
50 float s_CellSize
= 160.0f
;
52 bool s_Height
= false;
54 std::string s_ZoneBricksDirIn
; // UTF-8 path
55 std::string s_ZoneRuntimeDirIn
; // UTF-8 path
56 std::string s_LandFileOut
; // UTF-8 path
65 if (fileOut
.open(s_LandFileOut
, false, true, false))
68 nlverify(xml
.init(&fileOut
));
73 nlwarning("Can't open the land file for writing: %s", s_LandFileOut
.c_str());
77 catch (const Exception
&e
)
79 nlwarning("Error in writing land file: %s", e
.what());
85 bool getXYFromZoneName(sint32
&x
, sint32
&y
, const string
&zoneName
)
89 while (zoneName
[i
] != '_')
93 if (i
== zoneName
.size())
96 if (!NLMISC::fromString(yStr
, y
))
100 while (i
< zoneName
.size())
105 if (xStr
.size() != 2)
107 xStr
= NLMISC::toUpperAscii(xStr
);
108 x
= ((xStr
[0] - 'A') * 26 + (xStr
[1] - 'A'));
116 void centerVertices(std::vector
<CVector
> &vertices
)
118 CVector avg
= CVector(0.0f
, 0.0f
, 0.0f
);
119 for (ptrdiff_t i
= 0; i
< vertices
.size(); ++i
)
121 avg
/= (float)vertices
.size();
122 nldebug("Average: %f, %f", avg
.x
, avg
.y
);
123 for (ptrdiff_t i
= 0; i
< vertices
.size(); ++i
)
127 void offsetVertices(std::vector
<CVector
> &vertices
, int x
, int y
)
129 CVector off
= CVector((float)x
* s_CellSize
, (float)y
* s_CellSize
, 0.0f
);
130 for (ptrdiff_t i
= 0; i
< vertices
.size(); ++i
)
134 float ratePoints(const std::vector
<CVector
> &zone
, const std::vector
<CVector
> &ref
, float xx
, float xy
, float yx
, float yy
)
136 // Rudimentary distance rating of vertices (not very reliable, but good enough!)
139 // std::vector<CVector> refcpy = ref;
140 std::vector
<bool> usedref
;
141 usedref
.resize(ref
.size(), false);
142 for (ptrdiff_t i
= 0; i
< zone
.size(); ++i
)
147 float lowv
= (CVector(ref
[0].x
* xx
+ ref
[0].y
* xy
, ref
[0].x
* yx
+ ref
[0].y
* yy
, ref
[0].z
) - zone
[i
]).sqrnorm();
148 for (ptrdiff_t j
= 1; j
< ref
.size(); ++j
)
150 float v
= (CVector(ref
[j
].x
* xx
+ ref
[j
].y
* xy
, ref
[j
].x
* yx
+ ref
[j
].y
* yy
, ref
[j
].z
) - zone
[i
]).sqrnorm();
158 usedref
[lowj
] = true;
159 // keep it! - refcpy.erase(refcpy.begin() + lowj);
163 md
+= zone
[i
].norm();
168 std::vector
<bool> usedzone
;
169 usedzone
.resize(zone
.size(), false);
170 for (ptrdiff_t j
= 0; j
< ref
.size(); ++j
)
177 float lowv
= (CVector2f(ref
[j
].x
* xx
+ ref
[j
].y
* xy
, ref
[j
].x
* yx
+ ref
[j
].y
* yy
) - zone
[0]).sqrnorm();
178 for (ptrdiff_t i
= 1; i
< zone
.size(); ++i
)
180 float v
= (CVector2f(ref
[j
].x
* xx
+ ref
[j
].y
* xy
, ref
[j
].x
* yx
+ ref
[j
].y
* yy
) - zone
[i
]).sqrnorm();
188 usedzone
[lowi
] = true;
198 for (ptrdiff_t i
= 0; i
< zone
.size(); ++i
)
205 float lowv
= (CVector2f(ref
[0].x
* xx
+ ref
[0].y
* xy
, ref
[0].x
* yx
+ ref
[0].y
* yy
) - zone
[i
]).sqrnorm();
206 for (ptrdiff_t j
= 1; j
< ref
.size(); ++j
)
208 float v
= (CVector2f(ref
[j
].x
* xx
+ ref
[j
].y
* xy
, ref
[j
].x
* yx
+ ref
[j
].y
* yy
) - zone
[i
]).sqrnorm();
216 usedref
[lowj
] = true;
217 // keep it! - refcpy.erase(refcpy.begin() + lowj);
221 md
+= zone
[i
].norm();
231 for (ptrdiff_t j
= 0; j
< ref
.size(); ++j
)
239 if (nc
* 8 > zone
.size())
245 void findBestBrick(std::string
&brick
, int &rotate
, int &flip
, float &es
, std::vector
<CVector
> &zoneVertices
, const std::map
<std::string
, std::vector
<CVector
>> &brickVertices
)
247 float bestPoints
= (float)(uint32
)~0;
248 for (std::map
<std::string
, std::vector
<CVector
>>::const_iterator it
= brickVertices
.begin(), end
= brickVertices
.end(); it
!= end
; ++it
)
251 rating
= ratePoints(zoneVertices
, it
->second
, 1.0, 0.0, 0.0, 1.0);
252 if (rating
< bestPoints
)
259 rating
= ratePoints(zoneVertices
, it
->second
, 0.0, -1.0, 1.0, 0.0);
260 if (rating
< bestPoints
)
267 rating
= ratePoints(zoneVertices
, it
->second
, -1.0, 0.0, 0.0, -1.0);
268 if (rating
< bestPoints
)
275 rating
= ratePoints(zoneVertices
, it
->second
, 0.0, 1.0, -1.0, 0.0);
276 if (rating
< bestPoints
)
282 rating
= ratePoints(zoneVertices
, it
->second
, -1.0, 0.0, 0.0, 1.0);
283 if (rating
< bestPoints
)
290 rating
= ratePoints(zoneVertices
, it
->second
, 0.0, -1.0, -1.0, 0.0);
291 if (rating
< bestPoints
)
298 rating
= ratePoints(zoneVertices
, it
->second
, 1.0, 0.0, 0.0, -1.0);
299 if (rating
< bestPoints
)
306 rating
= ratePoints(zoneVertices
, it
->second
, 0.0, 1.0, 1.0, 0.0);
307 if (rating
< bestPoints
)
320 s_Land
.resize(s_ZoneMinX
, s_ZoneMaxX
, s_ZoneMinY
, s_ZoneMaxY
);
322 // float th = s_CellSize * 0.5f;
323 float th
= (s_CellSize
* 0.5f
) - (s_CellSize
* 0.2f
* 0.5f
);
325 // Read in all the bricks
326 std::vector
<std::string
> brickFiles
;
327 CPath::getPathContent(s_ZoneBricksDirIn
, false, false, true, brickFiles
);
328 std::map
<std::string
, std::vector
<CVector
>> brickVertices
;
329 for (std::vector
<std::string
>::const_iterator it
= brickFiles
.begin(), end
= brickFiles
.end(); it
!= end
; ++it
)
331 if (CFile::getExtension(*it
) != "zone")
334 if (NLMISC::startsWith(CFile::getFilename(*it
), "converted-")) // Not a real ligo
337 // if (NLMISC::startsWith(CFile::getFilename(*it), "foret-moor")) // Not useful for r2_forest
339 // if (NLMISC::startsWith(CFile::getFilename(*it), "goo-")) // Not useful for r2_forest
341 // if (NLMISC::endsWith(CFile::getFilenameWithoutExtension(*it), "_zc")) // Not useful for r2_forest
344 nldebug("Load %s", CFile::getFilenameWithoutExtension(*it
).c_str());
346 CIFile
zoneFile(*it
);
348 zone
.serial(zoneFile
);
351 // Retrieve patches and vertices
352 uint16 zoneId
= zone
.getZoneId();
353 std::vector
<CPatchInfo
> zonePatches
;
354 std::vector
<CBorderVertex
> zoneBorderVertices
;
355 zone
.retrieve(zonePatches
, zoneBorderVertices
);
357 brickVertices
[CFile::getFilenameWithoutExtension(*it
)] = std::vector
<CVector
>();
358 std::vector
<CVector
> &vec
= brickVertices
[CFile::getFilenameWithoutExtension(*it
)];
359 CVector off
= CVector(s_CellSize
* 0.5f
, s_CellSize
* 0.5f
, 0.0f
);
360 for (ptrdiff_t i
= 0; i
< zonePatches
.size(); ++i
)
362 for (ptrdiff_t j
= 0; j
< 4; ++j
)
364 float rx
= zonePatches
[i
].Patch
.Vertices
[j
].x
- off
.x
;
365 float ry
= zonePatches
[i
].Patch
.Vertices
[j
].y
- off
.y
;
366 if (rx
<= -th
|| rx
>= th
367 || ry
<= -th
|| ry
>= th
)
370 for (ptrdiff_t j
= 0; j
< 4; ++j
)
372 float rx
= zonePatches
[i
].Patch
.Vertices
[j
].x
- off
.x
;
373 float ry
= zonePatches
[i
].Patch
.Vertices
[j
].y
- off
.y
;
374 vec
.push_back(CVector(rx
, ry
, s_Height
? zonePatches
[i
].Patch
.Vertices
[j
].z
: 0.0f
));
379 // centerVertices(vec);
382 std::vector
<std::string
> runtimeFiles
;
383 CPath::getPathContent(s_ZoneRuntimeDirIn
, false, false, true, runtimeFiles
);
384 for (std::vector
<std::string
>::const_iterator it
= runtimeFiles
.begin(), end
= runtimeFiles
.end(); it
!= end
; ++it
)
386 if (CFile::getExtension(*it
) != "zonel")
390 std::string zoneName
= CFile::getFilenameWithoutExtension(*it
);
391 if (!getXYFromZoneName(x
, y
, zoneName
))
393 nlerrornoex("Bad zone name: %s", zoneName
.c_str());
397 CIFile
zoneFile(*it
);
399 zone
.serial(zoneFile
);
402 // Retrieve patches and vertices
403 uint16 zoneId
= zone
.getZoneId();
404 std::vector
<CPatchInfo
> zonePatches
;
405 std::vector
<CBorderVertex
> zoneBorderVertices
;
406 zone
.retrieve(zonePatches
, zoneBorderVertices
);
408 std::vector
<CVector
> vec
;
409 CVector off
= CVector((float)x
* s_CellSize
, (float)y
* s_CellSize
, 0.0f
) + CVector(s_CellSize
* 0.5f
, s_CellSize
* 0.5f
, 0.0f
);
410 for (ptrdiff_t i
= 0; i
< zonePatches
.size(); ++i
)
412 for (ptrdiff_t j
= 0; j
< 4; ++j
)
414 float rx
= zonePatches
[i
].Patch
.Vertices
[j
].x
- off
.x
;
415 float ry
= zonePatches
[i
].Patch
.Vertices
[j
].y
- off
.y
;
416 if (rx
<= -th
|| rx
>= th
417 || ry
<= -th
|| ry
>= th
)
420 for (ptrdiff_t j
= 0; j
< 4; ++j
)
422 float rx
= zonePatches
[i
].Patch
.Vertices
[j
].x
- off
.x
;
423 float ry
= zonePatches
[i
].Patch
.Vertices
[j
].y
- off
.y
;
424 vec
.push_back(CVector(rx
, ry
, s_Height
? zonePatches
[i
].Patch
.Vertices
[j
].z
: 0.0f
));
429 // offsetVertices(vec, x, y);
430 // centerVertices(vec);
436 findBestBrick(brick
, rotate
, flip
, es
, vec
, brickVertices
);
438 nlinfo("Zone: %s, brick: %s, rotate: %i; flip: %i, error score: %f", zoneName
.c_str(), brick
.c_str(), rotate
, flip
, es
);
440 s_Land
.setName(x
, y
, brick
);
441 s_Land
.setRot(x
, y
, rotate
);
442 s_Land
.setFlip(x
, y
, flip
);
448 } /* anonymous namespace */
450 int main(int argc
, char **argv
)
452 NLMISC::CApplicationContext myApplicationContext
;
454 NLMISC::CCmdArgs args
;
456 args
.addAdditionalArg("land", "Output ligo land file");
457 args
.addAdditionalArg("zonebricks", "Input zone bricks directory");
458 args
.addAdditionalArg("zoneruntime", "Input runtime zone directory");
459 args
.addAdditionalArg("zonemin", "Zone boundary");
460 args
.addAdditionalArg("zonemax", "Zone boundary");
461 args
.addArg("", "cellsize", "meters", "Zone cell size");
462 args
.addArg("", "height", "", "Take Z coordinate into account");
464 if (!args
.parse(argc
, argv
))
469 s_LandFileOut
= args
.getAdditionalArg("land")[0];
470 s_ZoneBricksDirIn
= args
.getAdditionalArg("zonebricks")[0];
471 s_ZoneRuntimeDirIn
= args
.getAdditionalArg("zoneruntime")[0];
473 sint32 zoneMinX
, zoneMinY
;
474 sint32 zoneMaxX
, zoneMaxY
;
475 if (!getXYFromZoneName(zoneMinX
, zoneMinY
, args
.getAdditionalArg("zonemin")[0])
476 || !getXYFromZoneName(zoneMaxX
, zoneMaxY
, args
.getAdditionalArg("zonemax")[0]))
480 s_ZoneMinX
= min(zoneMinX
, zoneMaxX
);
481 s_ZoneMaxX
= max(zoneMinX
, zoneMaxX
);
482 s_ZoneMinY
= min(zoneMinY
, zoneMaxY
);
483 s_ZoneMaxY
= max(zoneMinY
, zoneMaxY
);
485 s_Height
= args
.haveLongArg("height");
487 if (args
.haveLongArg("cellsize"))
489 if (!NLMISC::fromString(args
.getLongArg("cellsize")[0], s_CellSize
))