Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / tools / pacs / build_rbank / prim_checker.cpp
blobf8e14b88a2dad595da0c10070f02d09160e72801
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014-2021 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "prim_checker.h"
22 // NeL Misc includes
23 #include "nel/misc/vectord.h"
24 #include "nel/misc/path.h"
25 #include "nel/misc/i_xml.h"
26 #include "nel/misc/aabbox.h"
27 #include "nel/misc/file.h"
29 // NeL Ligo includes
30 #include "nel/ligo/ligo_config.h"
31 #include "nel/ligo/primitive.h"
33 // NeL 3d
34 #include "nel/3d/scene_group.h"
35 #include "nel/3d/transform_shape.h"
36 #include "nel/3d/water_model.h"
37 #include "nel/3d/water_shape.h"
38 #include "nel/3d/quad_grid.h"
40 // STL includes
41 #include <vector>
42 #include <set>
44 using namespace NLMISC;
45 using namespace NLLIGO;
46 using namespace NL3D;
47 using namespace std;
49 NLLIGO::CLigoConfig LigoConfig;
50 extern bool Verbose;
51 extern float WaterThreshold;
54 * Constructor
56 CPrimChecker::CPrimChecker()
63 * init()
65 bool CPrimChecker::build(const string &primitivesPath, const string &igLandPath, const string &igVillagePath, const string &outputDirectory, bool forceRebuild)
67 if (Verbose)
68 nlinfo("Checking pacs.packed_prims consistency");
70 NLLIGO::Register();
72 // Init ligo
73 if (!LigoConfig.readPrimitiveClass ("world_editor_classes.xml", false))
75 // Should be in R:\leveldesign\world_edit_files
76 nlwarning ("Can't load ligo primitive config file world_editor_classes.xml");
77 return false;
80 uint i, j;
81 string outputfname = CPath::standardizePath(outputDirectory)+"pacs.packed_prims";
83 _Grid.clear();
85 vector<string> files;
86 CPath::getPathContent(primitivesPath, true, false, true, files);
88 for (i=0; i<files.size(); ++i)
90 if (CFile::getExtension(files[i]) == "primitive")
92 readFile(files[i]);
96 files.clear();
97 CPath::getPathContent(igLandPath, true, false, true, files);
98 CPath::getPathContent(igVillagePath, true, false, true, files);
100 set<string> noWaterShapes;
102 for (i=0; i<files.size(); ++i)
106 // load ig associated to the zone
107 string igname = files[i];
109 if (CFile::getExtension(igname) != "ig")
110 continue;
112 string ignamelookup = CPath::lookup(igname);
113 //nlinfo("Reading ig '%s'", ignamelookup.c_str());
114 CIFile igStream(ignamelookup);
115 CInstanceGroup ig;
116 igStream.serial(ig);
118 // search in group for water instance
119 for (j=0; j<ig._InstancesInfos.size(); ++j)
121 string shapeName = ig._InstancesInfos[j].Name;
122 if (CFile::getExtension (shapeName) == "")
123 shapeName += ".shape";
125 if (noWaterShapes.find(shapeName) != noWaterShapes.end())
126 continue;
128 string shapeNameLookup = CPath::lookup (shapeName, false, false);
129 if (!shapeNameLookup.empty())
131 CIFile f;
132 if (f.open (shapeNameLookup))
134 CShapeStream shape;
135 shape.serial(f);
137 CWaterShape *wshape = dynamic_cast<CWaterShape*>(shape.getShapePointer());
138 if (wshape == NULL)
140 noWaterShapes.insert(shapeName);
141 continue;
144 //nlinfo("Render water shape '%s'", shapeNameLookup.c_str());
146 CMatrix matrix;
147 ig.getInstanceMatrix(j, matrix);
149 CPolygon wpoly;
150 //wshape->getShapeInWorldSpace(wpoly);
151 CPolygon2D wpoly2d = wshape->getShape();
153 uint k;
154 for (k=0; k<wpoly2d.Vertices.size(); ++k)
156 wpoly.Vertices.push_back(matrix * wpoly2d.Vertices[k]);
159 float zwater = wpoly.Vertices[0].z - WaterThreshold;
160 uint16 idx = (uint16)_WaterHeight.size();
161 _WaterHeight.push_back(zwater);
162 render(wpoly, idx);
164 if (Verbose)
165 nlinfo("Rendered water shape '%s' in instance '%s'", CFile::getFilenameWithoutExtension(shapeName).c_str(), CFile::getFilenameWithoutExtension(igname).c_str());
167 else if (Verbose)
169 noWaterShapes.insert(shapeName);
170 nlwarning ("Can't load shape %s", shapeNameLookup.c_str());
173 else if (Verbose)
175 noWaterShapes.insert(shapeName);
176 nlwarning ("Can't find shape %s", shapeName.c_str());
180 catch (const Exception &e)
182 nlwarning("%s", e.what());
186 COFile f;
187 if (f.open(outputfname))
189 f.serial(_Grid);
190 f.serialCont(_WaterHeight);
192 else
194 nlwarning("Couldn't save pacs.packed_prims file '%s'", outputfname.c_str());
197 return true;
203 * load()
205 bool CPrimChecker::load(const string &outputDirectory)
207 string outputfname = CPath::standardizePath(outputDirectory)+"pacs.packed_prims";
209 CIFile f;
210 if (f.open(outputfname))
212 f.serial(_Grid);
213 f.serialCont(_WaterHeight);
215 else
217 nlwarning("Couldn't load pacs.packed_prims file '%s'", outputfname.c_str());
218 return false;
221 return true;
229 * readFile()
231 void CPrimChecker::readFile(const string &filename)
233 string fullpath = CPath::lookup(filename, false);
235 if (fullpath.empty())
236 return;
238 // lookup for primitive file
239 CIFile f(fullpath);
240 CIXml xml;
242 CPrimitives prims;
244 // load xml file
245 xml.init(f);
246 if (Verbose)
247 nlinfo("Loaded prim file '%s'", filename.c_str());
249 // read nodes
250 if (!prims.read(xml.getRootNode(), filename.c_str(), LigoConfig))
252 nlwarning("Can't use primitive file '%s', xml parse error", filename.c_str());
253 return;
256 // get CPrimNode
257 CPrimNode *primRootNode = prims.RootNode;
259 // read recursive node
260 readPrimitive(primRootNode);
264 * readPrimitive()
266 void CPrimChecker::readPrimitive(IPrimitive *primitive)
268 string className;
270 // check good class and check primitive has a class name
271 if (dynamic_cast<CPrimZone*>(primitive) != NULL && primitive->getPropertyByName("class", className))
273 if (className == "pacs_include")
274 render(static_cast<CPrimZone*>(primitive), Include);
275 else if (className == "pacs_exclude")
276 render(static_cast<CPrimZone*>(primitive), Exclude);
277 else if (className == "pacs_cluster_hint")
278 render(static_cast<CPrimZone*>(primitive), ClusterHint);
281 // parse children
282 uint i;
283 for (i=0; i<primitive->getNumChildren(); ++i)
285 IPrimitive *child;
287 if (!primitive->getChild(child, i))
288 continue;
290 readPrimitive(child);
296 * render()
298 void CPrimChecker::render(CPrimZone *zone, uint8 bits)
300 if (zone->VPoints.size() < 3)
301 return;
303 string name;
304 if (zone->getPropertyByName("name", name) && Verbose)
305 nlinfo("Rendering CPrimZone '%s'", name.c_str());
307 // get the bouding box of the CPrimZone
308 CAABBox box;
310 box.setCenter(zone->VPoints[0]);
311 box.setHalfSize(CVector::Null);
313 uint i;
314 for (i=1; i<zone->VPoints.size(); ++i)
315 box.extend(zone->VPoints[i]);
317 sint32 xmin, ymin, xmax, ymax;
319 xmin = (sint32)(floor(box.getMin().x));
320 ymin = (sint32)(floor(box.getMin().y));
322 xmax = (sint32)(ceil(box.getMax().x));
323 ymax = (sint32)(ceil(box.getMax().y));
325 // Fill grid with points that belong to the CPrimZone
326 sint32 x, y;
327 for (y=ymin; y<=ymax; ++y)
328 for (x=xmin; x<=xmax; ++x)
329 if (zone->contains(CVector((float)x, (float)y, 0.0f)))
330 _Grid.set(x, y, bits);
335 * Render a water shape, as a CPolygon
337 void CPrimChecker::render(const CPolygon &poly, uint16 value)
339 static const sint centerOffset = 20480; // zones are max 40960
340 CPolygon polyOffset = poly;
341 for (ptrdiff_t i = 0; i < (ptrdiff_t)polyOffset.Vertices.size(); ++i)
342 // center to computeBorders range (-32k to 32k)
343 polyOffset.Vertices[i] += CVector(-centerOffset, centerOffset, 0);
345 list<CPolygon> convex;
347 // divide poly in convex polys
348 if (!polyOffset.toConvexPolygons(convex, CMatrix::Identity))
350 convex.clear();
351 CPolygon reverse = polyOffset;
352 std::reverse(reverse.Vertices.begin(), reverse.Vertices.end());
353 if (!reverse.toConvexPolygons(convex, CMatrix::Identity))
354 return;
357 list<CPolygon>::iterator it;
358 for (it=convex.begin(); it!=convex.end(); ++it)
360 CPolygon2D convex2d(*it);
362 CPolygon2D::TRasterVect rasterized;
363 sint ymin;
365 convex2d.computeBorders(rasterized, ymin);
366 ymin -= centerOffset; // uncenter
368 sint dy;
369 for (dy=0; dy<(sint)rasterized.size(); ++dy)
371 sint x;
373 for (x=rasterized[dy].first; x<=rasterized[dy].second; ++x)
375 uint8 prevBits = _Grid.get((uint)x + centerOffset, (uint)(ymin + dy));
377 // only set if there was not a water shape there or if previous was lower
378 if ((prevBits & Water) != 0)
380 uint16 prevWS = _Grid.index((uint)x + centerOffset, (uint)(ymin + dy));
382 if (_WaterHeight[value] < _WaterHeight[prevWS])
383 continue;
386 _Grid.index((uint)x + centerOffset, (uint)(ymin + dy), value);
387 _Grid.set((uint)x + centerOffset, (uint)(ymin + dy), Water);
395 * Render a CPolygon of bit value
397 void CPrimChecker::renderBits(const CPolygon &poly, uint8 bits)
399 static const sint centerOffset = 20480; // zones are max 40960
400 CPolygon polyOffset = poly;
401 for (ptrdiff_t i = 0; i < (ptrdiff_t)polyOffset.Vertices.size(); ++i)
402 // center to computeBorders range (-32k to 32k)
403 polyOffset.Vertices[i] += CVector(-centerOffset, centerOffset, 0);
405 list<CPolygon> convex;
407 // divide poly in convex polys
408 if (!polyOffset.toConvexPolygons(convex, CMatrix::Identity))
410 convex.clear();
411 CPolygon reverse = polyOffset;
412 std::reverse(reverse.Vertices.begin(), reverse.Vertices.end());
413 if (!reverse.toConvexPolygons(convex, CMatrix::Identity))
414 return;
417 list<CPolygon>::iterator it;
418 for (it=convex.begin(); it!=convex.end(); ++it)
420 CPolygon2D convex2d(*it);
422 CPolygon2D::TRasterVect rasterized;
423 sint ymin;
425 convex2d.computeBorders(rasterized, ymin);
426 ymin -= centerOffset; // uncenter
428 sint dy;
429 for (dy=0; dy<(sint)rasterized.size(); ++dy)
431 sint x;
433 for (x=rasterized[dy].first; x<=rasterized[dy].second; ++x)
435 _Grid.set((uint)x + centerOffset, (uint)(ymin + dy), bits);