Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / tools / ligo / unbuild_land / unbuild_land.cpp
blob534f01a122d0fcc4defc79df840f9f32513cc2d1
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2019-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
3 //
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.
8 //
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"
24 #include <iostream>
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>
38 #include <vector>
39 #include <set>
41 using namespace NL3D;
42 using namespace NLMISC;
43 using namespace NLLIGO;
44 using namespace std;
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
58 CZoneRegion s_Land;
60 bool saveLand()
62 try
64 COFile fileOut;
65 if (fileOut.open(s_LandFileOut, false, true, false))
67 COXml xml;
68 nlverify(xml.init(&fileOut));
69 s_Land.serial(xml);
71 else
73 nlwarning("Can't open the land file for writing: %s", s_LandFileOut.c_str());
74 return false;
77 catch (const Exception &e)
79 nlwarning("Error in writing land file: %s", e.what());
80 return true;
82 return true;
85 bool getXYFromZoneName(sint32 &x, sint32 &y, const string &zoneName)
87 string xStr, yStr;
88 uint32 i = 0;
89 while (zoneName[i] != '_')
91 yStr += zoneName[i];
92 ++i;
93 if (i == zoneName.size())
94 goto Fail;
96 if (!NLMISC::fromString(yStr, y))
97 goto Fail;
98 y = -y;
99 ++i;
100 while (i < zoneName.size())
102 xStr += zoneName[i];
103 ++i;
105 if (xStr.size() != 2)
106 goto Fail;
107 xStr = NLMISC::toUpperAscii(xStr);
108 x = ((xStr[0] - 'A') * 26 + (xStr[1] - 'A'));
109 return true;
110 Fail:
111 x = -1;
112 y = -1;
113 return false;
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)
120 avg += vertices[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)
124 vertices[i] -= avg;
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)
131 vertices[i] -= off;
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!)
138 float md = 0.f;
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)
144 if (ref.size())
146 int lowj = 0;
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();
151 if (v < lowv)
153 lowj = j;
154 lowv = v;
157 md += sqrtf(lowv);
158 usedref[lowj] = true;
159 // keep it! - refcpy.erase(refcpy.begin() + lowj);
161 else
163 md += zone[i].norm();
166 #if 0
167 md = 0.f;
168 std::vector<bool> usedzone;
169 usedzone.resize(zone.size(), false);
170 for (ptrdiff_t j = 0; j < ref.size(); ++j)
172 if (usedref[j])
174 if (zone.size())
176 int lowi = 0;
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();
181 if (v < lowv)
183 lowi = i;
184 lowv = v;
187 md += lowv;
188 usedzone[lowi] = true;
190 else
192 md += ref[j].norm();
196 md = 0.f;
197 int nc = 0;
198 for (ptrdiff_t i = 0; i < zone.size(); ++i)
200 if (usedzone[i])
202 if (ref.size())
204 int lowj = 0;
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();
209 if (v < lowv)
211 lowj = j;
212 lowv = v;
215 md += sqrtf(lowv);
216 usedref[lowj] = true;
217 // keep it! - refcpy.erase(refcpy.begin() + lowj);
219 else
221 md += zone[i].norm();
224 else
226 ++nc;
227 // md += 1.0f;
228 // md += 0.01;
231 for (ptrdiff_t j = 0; j < ref.size(); ++j)
233 if (!usedref[j])
235 // md += 1.0f;
236 // md += 0.01;
239 if (nc * 8 > zone.size())
240 return md + 10000;
241 #endif
242 return md;
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)
250 float rating;
251 rating = ratePoints(zoneVertices, it->second, 1.0, 0.0, 0.0, 1.0);
252 if (rating < bestPoints)
254 brick = it->first;
255 rotate = 0;
256 flip = 0;
257 bestPoints = rating;
259 rating = ratePoints(zoneVertices, it->second, 0.0, -1.0, 1.0, 0.0);
260 if (rating < bestPoints)
262 brick = it->first;
263 rotate = 1;
264 flip = 0;
265 bestPoints = rating;
267 rating = ratePoints(zoneVertices, it->second, -1.0, 0.0, 0.0, -1.0);
268 if (rating < bestPoints)
270 brick = it->first;
271 rotate = 2;
272 flip = 0;
273 bestPoints = rating;
275 rating = ratePoints(zoneVertices, it->second, 0.0, 1.0, -1.0, 0.0);
276 if (rating < bestPoints)
278 brick = it->first;
279 rotate = 3;
280 flip = 0;
282 rating = ratePoints(zoneVertices, it->second, -1.0, 0.0, 0.0, 1.0);
283 if (rating < bestPoints)
285 brick = it->first;
286 rotate = 0;
287 flip = 1;
288 bestPoints = rating;
290 rating = ratePoints(zoneVertices, it->second, 0.0, -1.0, -1.0, 0.0);
291 if (rating < bestPoints)
293 brick = it->first;
294 rotate = 1;
295 flip = 1;
296 bestPoints = rating;
298 rating = ratePoints(zoneVertices, it->second, 1.0, 0.0, 0.0, -1.0);
299 if (rating < bestPoints)
301 brick = it->first;
302 rotate = 2;
303 flip = 1;
304 bestPoints = rating;
306 rating = ratePoints(zoneVertices, it->second, 0.0, 1.0, 1.0, 0.0);
307 if (rating < bestPoints)
309 brick = it->first;
310 rotate = 3;
311 flip = 1;
312 bestPoints = rating;
315 es = bestPoints;
318 bool unbuildLand()
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")
332 continue;
334 if (NLMISC::startsWith(CFile::getFilename(*it), "converted-")) // Not a real ligo
335 continue;
337 // if (NLMISC::startsWith(CFile::getFilename(*it), "foret-moor")) // Not useful for r2_forest
338 // continue;
339 // if (NLMISC::startsWith(CFile::getFilename(*it), "goo-")) // Not useful for r2_forest
340 // continue;
341 // if (NLMISC::endsWith(CFile::getFilenameWithoutExtension(*it), "_zc")) // Not useful for r2_forest
342 // continue;
344 nldebug("Load %s", CFile::getFilenameWithoutExtension(*it).c_str());
346 CIFile zoneFile(*it);
347 CZone zone;
348 zone.serial(zoneFile);
349 zoneFile.close();
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)
368 goto SkipA;
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));
376 SkipA:;
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")
387 continue;
389 int x, y;
390 std::string zoneName = CFile::getFilenameWithoutExtension(*it);
391 if (!getXYFromZoneName(x, y, zoneName))
393 nlerrornoex("Bad zone name: %s", zoneName.c_str());
394 continue;
397 CIFile zoneFile(*it);
398 CZone zone;
399 zone.serial(zoneFile);
400 zoneFile.close();
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)
418 goto SkipB;
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));
426 SkipB:;
429 // offsetVertices(vec, x, y);
430 // centerVertices(vec);
432 std::string brick;
433 int rotate;
434 int flip;
435 float es;
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);
445 return saveLand();
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))
466 return EXIT_FAILURE;
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]))
478 goto Fail;
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))
490 goto Fail;
493 if (!unbuildLand())
494 return EXIT_FAILURE;
496 return EXIT_SUCCESS;
497 Fail:
498 args.displayHelp();
499 return EXIT_FAILURE;