1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014-2021 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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"
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"
30 #include "nel/ligo/ligo_config.h"
31 #include "nel/ligo/primitive.h"
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"
44 using namespace NLMISC
;
45 using namespace NLLIGO
;
49 NLLIGO::CLigoConfig LigoConfig
;
51 extern float WaterThreshold
;
56 CPrimChecker::CPrimChecker()
65 bool CPrimChecker::build(const string
&primitivesPath
, const string
&igLandPath
, const string
&igVillagePath
, const string
&outputDirectory
, bool forceRebuild
)
68 nlinfo("Checking pacs.packed_prims consistency");
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");
81 string outputfname
= CPath::standardizePath(outputDirectory
)+"pacs.packed_prims";
86 CPath::getPathContent(primitivesPath
, true, false, true, files
);
88 for (i
=0; i
<files
.size(); ++i
)
90 if (CFile::getExtension(files
[i
]) == "primitive")
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")
112 string ignamelookup
= CPath::lookup(igname
);
113 //nlinfo("Reading ig '%s'", ignamelookup.c_str());
114 CIFile
igStream(ignamelookup
);
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())
128 string shapeNameLookup
= CPath::lookup (shapeName
, false, false);
129 if (!shapeNameLookup
.empty())
132 if (f
.open (shapeNameLookup
))
137 CWaterShape
*wshape
= dynamic_cast<CWaterShape
*>(shape
.getShapePointer());
140 noWaterShapes
.insert(shapeName
);
144 //nlinfo("Render water shape '%s'", shapeNameLookup.c_str());
147 ig
.getInstanceMatrix(j
, matrix
);
150 //wshape->getShapeInWorldSpace(wpoly);
151 CPolygon2D wpoly2d
= wshape
->getShape();
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
);
165 nlinfo("Rendered water shape '%s' in instance '%s'", CFile::getFilenameWithoutExtension(shapeName
).c_str(), CFile::getFilenameWithoutExtension(igname
).c_str());
169 noWaterShapes
.insert(shapeName
);
170 nlwarning ("Can't load shape %s", shapeNameLookup
.c_str());
175 noWaterShapes
.insert(shapeName
);
176 nlwarning ("Can't find shape %s", shapeName
.c_str());
180 catch (const Exception
&e
)
182 nlwarning("%s", e
.what());
187 if (f
.open(outputfname
))
190 f
.serialCont(_WaterHeight
);
194 nlwarning("Couldn't save pacs.packed_prims file '%s'", outputfname
.c_str());
205 bool CPrimChecker::load(const string
&outputDirectory
)
207 string outputfname
= CPath::standardizePath(outputDirectory
)+"pacs.packed_prims";
210 if (f
.open(outputfname
))
213 f
.serialCont(_WaterHeight
);
217 nlwarning("Couldn't load pacs.packed_prims file '%s'", outputfname
.c_str());
231 void CPrimChecker::readFile(const string
&filename
)
233 string fullpath
= CPath::lookup(filename
, false);
235 if (fullpath
.empty())
238 // lookup for primitive file
247 nlinfo("Loaded prim file '%s'", filename
.c_str());
250 if (!prims
.read(xml
.getRootNode(), filename
.c_str(), LigoConfig
))
252 nlwarning("Can't use primitive file '%s', xml parse error", filename
.c_str());
257 CPrimNode
*primRootNode
= prims
.RootNode
;
259 // read recursive node
260 readPrimitive(primRootNode
);
266 void CPrimChecker::readPrimitive(IPrimitive
*primitive
)
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
);
283 for (i
=0; i
<primitive
->getNumChildren(); ++i
)
287 if (!primitive
->getChild(child
, i
))
290 readPrimitive(child
);
298 void CPrimChecker::render(CPrimZone
*zone
, uint8 bits
)
300 if (zone
->VPoints
.size() < 3)
304 if (zone
->getPropertyByName("name", name
) && Verbose
)
305 nlinfo("Rendering CPrimZone '%s'", name
.c_str());
307 // get the bouding box of the CPrimZone
310 box
.setCenter(zone
->VPoints
[0]);
311 box
.setHalfSize(CVector::Null
);
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
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
))
351 CPolygon reverse
= polyOffset
;
352 std::reverse(reverse
.Vertices
.begin(), reverse
.Vertices
.end());
353 if (!reverse
.toConvexPolygons(convex
, CMatrix::Identity
))
357 list
<CPolygon
>::iterator it
;
358 for (it
=convex
.begin(); it
!=convex
.end(); ++it
)
360 CPolygon2D
convex2d(*it
);
362 CPolygon2D::TRasterVect rasterized
;
365 convex2d
.computeBorders(rasterized
, ymin
);
366 ymin
-= centerOffset
; // uncenter
369 for (dy
=0; dy
<(sint
)rasterized
.size(); ++dy
)
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
])
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
))
411 CPolygon reverse
= polyOffset
;
412 std::reverse(reverse
.Vertices
.begin(), reverse
.Vertices
.end());
413 if (!reverse
.toConvexPolygons(convex
, CMatrix::Identity
))
417 list
<CPolygon
>::iterator it
;
418 for (it
=convex
.begin(); it
!=convex
.end(); ++it
)
420 CPolygon2D
convex2d(*it
);
422 CPolygon2D::TRasterVect rasterized
;
425 convex2d
.computeBorders(rasterized
, ymin
);
426 ymin
-= centerOffset
; // uncenter
429 for (dy
=0; dy
<(sint
)rasterized
.size(); ++dy
)
433 for (x
=rasterized
[dy
].first
; x
<=rasterized
[dy
].second
; ++x
)
435 _Grid
.set((uint
)x
+ centerOffset
, (uint
)(ymin
+ dy
), bits
);