1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2021 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 #include "../zone_lib/zone_utility.h"
20 #include <nel/misc/types_nl.h>
21 #include <nel/misc/file.h>
22 #include <nel/misc/i_xml.h>
23 #include <nel/misc/common.h>
24 #include <nel/misc/cmd_args.h>
25 #include <nel/misc/bitmap.h>
26 //#include <nel/3d/quad_tree.h>
27 #include <nel/3d/zone.h>
28 //#include <nel/3d/landscape.h>
29 //#include <nel/3d/zone_smoother.h>
30 //#include <nel/3d/zone_tgt_smoother.h>
31 //#include <nel/3d/zone_corner_smoother.h>
32 #include <nel/ligo/zone_region.h>
37 using namespace NLMISC
;
38 using namespace NLLIGO
;
41 namespace /* anonymous */
44 sint32 s_ZoneMinX
, s_ZoneMinY
, s_ZoneMaxX
, s_ZoneMaxY
;
45 float s_CellSize
= 160.0f
;
48 std::string s_SourceDir
; /* R:\reference\2008_july\data\r2_desert */
49 std::string s_ReferenceDir
; /* R:\pipeline\export\continents\r2_desert\zone_weld */
51 std::string s_OutputPy
;
53 std::string s_SourceExt
= "zonel";
54 std::string s_ReferenceExt
= "zonenhw";
56 CZoneRegion s_Land
; /* "R:\graphics\landscape\ligo\desert\r2_desert.land" */
60 // unbuild_elevation --land "R:\graphics\landscape\ligo\desert\r2_desert.land" "Y:\temp\r2_desert_elevation.Py" "R:\reference\2008_july\data\r2_desert" "R:\pipeline\export\continents\r2_desert\zone_weld"
61 // --land "R:\graphics\landscape\ligo\jungle\zorai.land" --referenceext zonenhw "X:\wsl\big_zorai.py" "R:\reference\2008_july\data\zorai_zones" "R:\pipeline\export\continents\zorai\zone_weld"
63 // --land "R:\graphics\landscape\ligo\desert\r2_desert.land" "X:\wsl\big_r2_desert.py" "R:\reference\2008_july\data\r2_desert" "R:\pipeline\export\continents\r2_desert\zone_weld" --extendcoords
64 // --land "R:\graphics\landscape\ligo\jungle\r2_jungle.land" "X:\wsl\big_r2_jungle.py" "R:\reference\2008_july\data\r2_jungle" "R:\pipeline\export\continents\r2_jungle\zone_weld" --extendcoords
65 // --land "R:\graphics\landscape\ligo\jungle\r2_forest.land" "X:\wsl\big_r2_forest.py" "R:\reference\2008_july\data\r2_forest" "R:\pipeline\export\continents\r2_forest\zone_weld" --extendcoords
66 // --land "R:\graphics\landscape\ligo\lacustre\r2_lakes.land" "X:\wsl\big_r2_lakes.py" "R:\reference\2008_july\data\r2_lakes" "R:\pipeline\export\continents\r2_lakes\zone_weld" --extendcoords
67 // --land "R:\graphics\landscape\ligo\primes_racines\r2_roots.land" "X:\wsl\big_r2_roots.py" "R:\reference\2008_july\data\r2_roots" "R:\pipeline\export\continents\r2_roots\zone_weld" --extendcoords
69 // --land "R:\graphics\landscape\ligo\desert\r2_desert.land" --referenceext zonew "X:\wsl\check_r2_desert.py" "R:\reference\2008_july\data\r2_desert" "R:\pipeline\export\continents\r2_desert\zone_weld" --extendcoords
70 // --land "R:\graphics\landscape\ligo\jungle\r2_jungle.land" --referenceext zonew "X:\wsl\check_r2_jungle.py" "R:\reference\2008_july\data\r2_jungle" "R:\pipeline\export\continents\r2_jungle\zone_weld" --extendcoords
71 // --land "R:\graphics\landscape\ligo\primes_racines\r2_roots.land" --referenceext zonew "X:\wsl\check_r2_roots.py" "R:\reference\2008_july\data\r2_roots" "R:\pipeline\export\continents\r2_roots\zone_weld" --extendcoords
73 bool loadLand(const string
&filename
)
78 if (fileIn
.open (filename
))
81 nlverify(xml
.init(fileIn
));
86 nlwarning("Can't open the land file: %s", filename
.c_str());
90 catch (const Exception
& e
)
92 nlwarning("Error in land file: %s", e
.what());
98 bool getXYFromZoneName(sint32
&x
, sint32
&y
, const string
&zoneName
)
102 while (zoneName
[i
] != '_')
106 if (i
== zoneName
.size())
109 if (!NLMISC::fromString(yStr
, y
))
113 while (i
< zoneName
.size())
118 if (xStr
.size() != 2)
120 xStr
= NLMISC::toUpperAscii(xStr
);
121 x
= ((xStr
[0] - 'A') * 26 + (xStr
[1] - 'A'));
129 void getBitmapSize(int &w
, int &h
)
131 sint32 sizeX
= s_ZoneMaxX
- s_ZoneMinX
+ 1;
132 sint32 sizeY
= s_ZoneMaxY
- s_ZoneMinY
+ 1;
143 void getBitmapCoord(float &xc
, float &yc
, float x
, float y
)
145 float deltaZ
= 0.0f
, deltaZ2
= 0.0f
;
147 sint32 sizeX
= s_ZoneMaxX
- s_ZoneMinX
+ 1;
148 sint32 sizeY
= s_ZoneMaxY
- s_ZoneMinY
+ 1;
150 xc
= (x
- s_CellSize
* s_ZoneMinX
) / (s_CellSize
* sizeX
);
151 yc
= 1.0f
- ((y
- s_CellSize
* s_ZoneMinY
) / (s_CellSize
* sizeY
));
157 xc
-= .5f
/ (float)w
;
158 yc
-= .5f
/ (float)h
;
159 xc
= xc
* (float)(w
+ 1) / (float)w
;
160 yc
= yc
* (float)(h
+ 1) / (float)h
;
164 bool processZone(std::vector
<NLMISC::CVector
> &output
, const std::string
&sourceFile
, const std::string
&referenceFile
)
166 std::string zone
= CFile::getFilenameWithoutExtension(referenceFile
);
168 std::vector
<CVector
> sourceVertices
;
169 std::vector
<CVector
> referenceVertices
;
175 CIFile
f(sourceFile
);
179 zone
.retrieve(zoneInfo
);
181 for (ptrdiff_t i
= 0; i
< (ptrdiff_t)zoneInfo
.Patchs
.size(); ++i
)
183 sourceVertices
.push_back(zoneInfo
.Patchs
[i
].Patch
.Vertices
[0]);
184 sourceVertices
.push_back(zoneInfo
.Patchs
[i
].Patch
.Vertices
[1]);
185 sourceVertices
.push_back(zoneInfo
.Patchs
[i
].Patch
.Vertices
[2]);
186 sourceVertices
.push_back(zoneInfo
.Patchs
[i
].Patch
.Vertices
[3]);
194 CIFile
f(referenceFile
);
198 zone
.retrieve(zoneInfo
);
200 for (ptrdiff_t i
= 0; i
< (ptrdiff_t)zoneInfo
.Patchs
.size(); ++i
)
202 referenceVertices
.push_back(zoneInfo
.Patchs
[i
].Patch
.Vertices
[0]);
203 referenceVertices
.push_back(zoneInfo
.Patchs
[i
].Patch
.Vertices
[1]);
204 referenceVertices
.push_back(zoneInfo
.Patchs
[i
].Patch
.Vertices
[2]);
205 referenceVertices
.push_back(zoneInfo
.Patchs
[i
].Patch
.Vertices
[3]);
208 printf("Source vertices: %i, reference vertices: %i\n", (int)sourceVertices
.size(), (int)referenceVertices
.size());
209 if (sourceVertices
.size() != referenceVertices
.size())
211 nlwarning("Mismatching vertex count in zone %s, source vertices: %i, reference vertices: %i", zone
.c_str(), (int)sourceVertices
.size(), (int)referenceVertices
.size());
215 std::vector
<bool> processedSourceIndices
;
216 std::vector
<bool> processedReferenceIndices
;
217 processedSourceIndices
.resize(sourceVertices
.size());
218 processedReferenceIndices
.resize(referenceVertices
.size());
220 int referenceProcessedCount
= 0;
221 int sourceProcessedCount
= 0;
223 for (ptrdiff_t i
= 0; i
< (ptrdiff_t)referenceVertices
.size(); ++i
)
225 if (processedReferenceIndices
[i
])
228 float referenceHeight
= referenceVertices
[i
].z
;
229 int referenceHeightDiv
= 1;
230 ++referenceProcessedCount
;
232 for (ptrdiff_t j
= i
+ 1; j
< (ptrdiff_t)referenceVertices
.size(); ++j
)
234 if (processedReferenceIndices
[j
])
237 if (abs(referenceVertices
[i
].x
- referenceVertices
[j
].x
) < 0.1f
238 && abs(referenceVertices
[i
].y
- referenceVertices
[j
].y
) < 0.1f
)
240 processedReferenceIndices
[j
] = true;
241 referenceHeight
+= referenceVertices
[j
].z
;
242 ++referenceHeightDiv
;
243 ++referenceProcessedCount
;
247 float sourceHeight
= 0.f
;
248 int sourceHeightDiv
= 0;
250 for (ptrdiff_t j
= 0; j
< (ptrdiff_t)sourceVertices
.size(); ++j
)
252 if (processedSourceIndices
[j
])
255 if (abs(referenceVertices
[i
].x
- sourceVertices
[j
].x
) < 0.1f
256 && abs(referenceVertices
[i
].y
- sourceVertices
[j
].y
) < 0.1f
)
258 processedSourceIndices
[j
] = true;
259 sourceHeight
+= sourceVertices
[j
].z
;
261 ++sourceProcessedCount
;
265 if (!sourceHeightDiv
)
267 nlwarning("No matching vertices in source for zone %s, x: %f, y: %f", zone
.c_str(), referenceVertices
[i
].x
, referenceVertices
[i
].y
);
272 if (referenceHeightDiv
!= sourceHeightDiv
)
274 nlwarning("Mismatching vertex count for zone %s, x: %f, y: %f (reference: %i, source: %i)", zone
.c_str(), referenceVertices
[i
].x
, referenceVertices
[i
].y
, referenceHeightDiv
, sourceHeightDiv
);
279 referenceHeight
/= (float)referenceHeightDiv
;
280 sourceHeight
/= (float)sourceHeightDiv
;
282 output
.push_back(NLMISC::CVector(referenceVertices
[i
].x
, referenceVertices
[i
].y
, sourceHeight
- referenceHeight
));
288 bool unbuildElevation()
290 std::vector
<std::string
> referenceZones
;
291 CPath::getPathContent(s_ReferenceDir
, true, false, true, referenceZones
);
294 std::vector
<NLMISC::CVector
> output
;
296 for (std::vector
<std::string
>::iterator
it(referenceZones
.begin()), end(referenceZones
.end()); it
!= end
; ++it
)
298 if (CFile::getExtension(*it
) != s_ReferenceExt
)
301 std::string zone
= CFile::getFilenameWithoutExtension(*it
);
302 std::string sourceZone
= s_SourceDir
+ "/" + CFile::getFilenameWithoutExtension(*it
) + "." + s_SourceExt
;
304 if (!CFile::fileExists(sourceZone
))
307 if (zone
== "137_JK") // Bad zone
310 printf("%s\n", nlUtf8ToMbcs(zone
));
313 if (!processZone(output
, sourceZone
, *it
))
319 printf("Total zones: %i\n", totalZones
);
323 std::vector
<bool> processedOutput
;
324 processedOutput
.resize(output
.size());
325 std::vector
<NLMISC::CVector
> reducedOutput
;
326 for (ptrdiff_t i
= 0; i
< (ptrdiff_t)output
.size(); ++i
)
328 if (processedOutput
[i
])
331 CVector v
= output
[i
];
334 for (ptrdiff_t j
= i
+ 1; j
< (ptrdiff_t)output
.size(); ++j
)
336 if (processedOutput
[j
])
339 if (abs(output
[i
].x
- output
[j
].x
) < 0.1f
340 && abs(output
[i
].y
- output
[j
].y
) < 0.1f
)
342 processedOutput
[j
] = true;
349 reducedOutput
.push_back(v
);
352 printf("Reduced vertex count from %i to %i\n", (int)output
.size(), (int)reducedOutput
.size());
356 std::vector
<NLMISC::CVector
> reducedOutput
= output
;
364 FILE *fo
= fopen(nlUtf8ToMbcs(s_OutputPy
), "w");
368 fprintf(fo
, "import naturalneighbor # https://github.com/innolitics/natural-neighbor-interpolation - pip3 install naturalneighbor\n");
369 fprintf(fo
, "import numpy as np\n");
370 fprintf(fo
, "import png # pip3 install pypng\n");
371 fprintf(fo
, "grid_ranges = [[0, 1, %i%s], [0, 1, %i%s], [-1, 1, 1j]]\n", w
, "j", h
, "j");
373 fprintf(fo
, "points = np.array([\n");
375 for (ptrdiff_t i
= 0; i
< (ptrdiff_t)reducedOutput
.size(); ++i
)
378 getBitmapCoord(x
, y
, reducedOutput
[i
].x
, reducedOutput
[i
].y
);
379 fprintf(fo
, "\t[ %f, %f, 0 ], # %f, %f\n", x
, y
, reducedOutput
[i
].x
, reducedOutput
[i
].y
);
384 fprintf(fo
, "values = np.array([\n");
386 for (ptrdiff_t i
= 0; i
< (ptrdiff_t)reducedOutput
.size(); ++i
)
388 fprintf(fo
, "\t%f,\n", reducedOutput
[i
].z
);
393 fprintf(fo
, "nn_interpolated_values = naturalneighbor.griddata(points, values, grid_ranges)\n");
394 fprintf(fo
, "img = []\n");
395 fprintf(fo
, "for y in range(0, %i):\n", h
);
396 fprintf(fo
, "\tline = []\n");
397 fprintf(fo
, "\tfor x in range(0, %i):\n", w
);
398 fprintf(fo
, "\t\tline.append(np.round(nn_interpolated_values[x][y][0]).astype(int) + 127)\n");
399 fprintf(fo
, "\timg.append(line)\n");
400 fprintf(fo
, "with open('%s.png', 'wb') as f:\n", CFile::getFilenameWithoutExtension(s_OutputPy
).c_str());
401 fprintf(fo
, "\tw = png.Writer(%i, %i, greyscale=True)\n", w
, h
);
402 fprintf(fo
, "\tw.write(f, img)\n");
407 printf("Warnings: %i\n", s_Warnings
);
411 bool unbuildElevation(NLMISC::CCmdArgs
&args
)
413 s_OutputPy
= args
.getAdditionalArg("output")[0];
414 s_SourceDir
= args
.getAdditionalArg("source")[0];
415 s_ReferenceDir
= args
.getAdditionalArg("reference")[0];
417 if (args
.haveLongArg("zonemin") && args
.haveLongArg("zonemax"))
419 sint32 zoneMinX
, zoneMinY
;
420 sint32 zoneMaxX
, zoneMaxY
;
421 if (!getXYFromZoneName(zoneMinX
, zoneMinY
, args
.getLongArg("zonemin")[0])
422 || !getXYFromZoneName(zoneMaxX
, zoneMaxY
, args
.getLongArg("zonemax")[0]))
426 s_ZoneMinX
= min(zoneMinX
, zoneMaxX
);
427 s_ZoneMaxX
= max(zoneMinX
, zoneMaxX
);
428 s_ZoneMinY
= min(zoneMinY
, zoneMaxY
);
429 s_ZoneMaxY
= max(zoneMinY
, zoneMaxY
);
431 else if (args
.haveLongArg("land"))
433 if (!loadLand(args
.getLongArg("land")[0]))
435 s_ZoneMinX
= s_Land
.getMinX();
436 s_ZoneMaxX
= s_Land
.getMaxX();
437 s_ZoneMinY
= s_Land
.getMinY();
438 s_ZoneMaxY
= s_Land
.getMaxY();
442 nlwarning("Must have either both 'zonemin' and 'zonemax', or 'land' specified");
446 if (args
.haveLongArg("cellsize"))
448 if (!NLMISC::fromString(args
.getLongArg("cellsize")[0], s_CellSize
))
452 s_ExtendCoords
= args
.haveLongArg("extendcoords");
454 if (args
.haveLongArg("sourceext"))
456 s_SourceExt
= args
.getLongArg("sourceext")[0];
459 if (args
.haveLongArg("referenceext"))
461 s_ReferenceExt
= args
.getLongArg("referenceext")[0];
464 return unbuildElevation();
467 } /* anonymous namespace */
469 int main(int argc
, char **argv
)
471 NLMISC::CApplicationContext myApplicationContext
;
473 NLMISC::CCmdArgs args
;
475 args
.addAdditionalArg("output", "Output Python file path");
476 args
.addAdditionalArg("source", "Input folder with zones at the right height");
477 args
.addAdditionalArg("reference", "Input folder with zones at the wrong height");
479 args
.addArg("", "sourceext", "extension", "Source zone extension (default: zonel)");
480 args
.addArg("", "referenceext", "extension", "Reference zone extension (default: zonew)");
482 args
.addArg("", "land", "land", "Ligo land file (either specify this or the boundaries)");
483 args
.addArg("", "zonemin", "zone", "Zone boundary");
484 args
.addArg("", "zonemax", "zone", "Zone boundary");
485 args
.addArg("", "cellsize", "meters", "Zone cell size (default: 160)");
486 args
.addArg("", "extendcoords", "flag", "Extend coordinates to edge of bitmap pixels");
488 if (!args
.parse(argc
, argv
))
493 if (!unbuildElevation(args
))