khanat-opennel-code/code/nel/tools/pacs/build_rbank/prim_checker.cpp

425 lines
9.4 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "prim_checker.h"
// NeL Misc includes
#include "nel/misc/vectord.h"
#include "nel/misc/path.h"
#include "nel/misc/i_xml.h"
#include "nel/misc/aabbox.h"
#include "nel/misc/file.h"
// NeL Ligo includes
#include "nel/ligo/ligo_config.h"
#include "nel/ligo/primitive.h"
// NeL 3d
#include "nel/3d/scene_group.h"
#include "nel/3d/transform_shape.h"
#include "nel/3d/water_model.h"
#include "nel/3d/water_shape.h"
#include "nel/3d/quad_grid.h"
// STL includes
#include <vector>
#include <set>
using namespace NLMISC;
using namespace NLLIGO;
using namespace NL3D;
using namespace std;
NLLIGO::CLigoConfig LigoConfig;
extern bool Verbose;
extern float WaterThreshold;
/*
* Constructor
*/
CPrimChecker::CPrimChecker()
{
}
/*
* init()
*/
bool CPrimChecker::build(const string &primitivesPath, const string &igLandPath, const string &igVillagePath, const string &outputDirectory, bool forceRebuild)
{
if (Verbose)
nlinfo("Checking pacs.packed_prims consistency");
NLLIGO::Register();
// Init ligo
if (!LigoConfig.readPrimitiveClass ("world_editor_classes.xml", false))
{
// Should be in l:\leveldesign\world_edit_files
nlwarning ("Can't load ligo primitive config file world_editor_classes.xml");
return false;
}
uint i, j;
string outputfname = CPath::standardizePath(outputDirectory)+"pacs.packed_prims";
_Grid.clear();
vector<string> files;
CPath::getPathContent(primitivesPath, true, false, true, files);
for (i=0; i<files.size(); ++i)
{
if (CFile::getExtension(files[i]) == "primitive")
{
readFile(files[i]);
}
}
files.clear();
CPath::getPathContent(igLandPath, true, false, true, files);
CPath::getPathContent(igVillagePath, true, false, true, files);
set<string> noWaterShapes;
for (i=0; i<files.size(); ++i)
{
try
{
// load ig associated to the zone
string igname = files[i];
if (CFile::getExtension(igname) != "ig")
continue;
string ignamelookup = CPath::lookup(igname);
//nlinfo("Reading ig '%s'", ignamelookup.c_str());
CIFile igStream(ignamelookup);
CInstanceGroup ig;
igStream.serial(ig);
// search in group for water instance
for (j=0; j<ig._InstancesInfos.size(); ++j)
{
string shapeName = ig._InstancesInfos[j].Name;
if (CFile::getExtension (shapeName) == "")
shapeName += ".shape";
if (noWaterShapes.find(shapeName) != noWaterShapes.end())
continue;
string shapeNameLookup = CPath::lookup (shapeName, false, false);
if (!shapeNameLookup.empty())
{
CIFile f;
if (f.open (shapeNameLookup))
{
CShapeStream shape;
shape.serial(f);
CWaterShape *wshape = dynamic_cast<CWaterShape*>(shape.getShapePointer());
if (wshape == NULL)
{
noWaterShapes.insert(shapeName);
continue;
}
//nlinfo("Render water shape '%s'", shapeNameLookup.c_str());
CMatrix matrix;
ig.getInstanceMatrix(j, matrix);
CPolygon wpoly;
//wshape->getShapeInWorldSpace(wpoly);
CPolygon2D wpoly2d = wshape->getShape();
uint k;
for (k=0; k<wpoly2d.Vertices.size(); ++k)
{
wpoly.Vertices.push_back(matrix * wpoly2d.Vertices[k]);
}
float zwater = wpoly.Vertices[0].z - WaterThreshold;
uint16 idx = (uint16)_WaterHeight.size();
_WaterHeight.push_back(zwater);
render(wpoly, idx);
if (Verbose)
nlinfo("Rendered water shape '%s' in instance '%s'", CFile::getFilenameWithoutExtension(shapeName).c_str(), CFile::getFilenameWithoutExtension(igname).c_str());
}
else if (Verbose)
{
noWaterShapes.insert(shapeName);
nlwarning ("Can't load shape %s", shapeNameLookup.c_str());
}
}
else if (Verbose)
{
noWaterShapes.insert(shapeName);
nlwarning ("Can't find shape %s", shapeName.c_str());
}
}
}
catch (const Exception &e)
{
nlwarning("%s", e.what());
}
}
COFile f;
if (f.open(outputfname))
{
f.serial(_Grid);
f.serialCont(_WaterHeight);
}
else
{
nlwarning("Couldn't save pacs.packed_prims file '%s'", outputfname.c_str());
}
return true;
}
/*
* load()
*/
bool CPrimChecker::load(const string &outputDirectory)
{
string outputfname = CPath::standardizePath(outputDirectory)+"pacs.packed_prims";
CIFile f;
if (f.open(outputfname))
{
f.serial(_Grid);
f.serialCont(_WaterHeight);
}
else
{
nlwarning("Couldn't load pacs.packed_prims file '%s'", outputfname.c_str());
return false;
}
return true;
}
/*
* readFile()
*/
void CPrimChecker::readFile(const string &filename)
{
string fullpath = CPath::lookup(filename, false);
if (fullpath.empty())
return;
// lookup for primitive file
CIFile f(fullpath);
CIXml xml;
CPrimitives prims;
// load xml file
xml.init(f);
if (Verbose)
nlinfo("Loaded prim file '%s'", filename.c_str());
// read nodes
if (!prims.read(xml.getRootNode(), filename.c_str(), LigoConfig))
{
nlwarning("Can't use primitive file '%s', xml parse error", filename.c_str());
return;
}
// get CPrimNode
CPrimNode *primRootNode = prims.RootNode;
// read recursive node
readPrimitive(primRootNode);
}
/*
* readPrimitive()
*/
void CPrimChecker::readPrimitive(IPrimitive *primitive)
{
string className;
// check good class and check primitive has a class name
if (dynamic_cast<CPrimZone*>(primitive) != NULL && primitive->getPropertyByName("class", className))
{
if (className == "pacs_include")
render(static_cast<CPrimZone*>(primitive), Include);
else if (className == "pacs_exclude")
render(static_cast<CPrimZone*>(primitive), Exclude);
else if (className == "pacs_cluster_hint")
render(static_cast<CPrimZone*>(primitive), ClusterHint);
}
// parse children
uint i;
for (i=0; i<primitive->getNumChildren(); ++i)
{
IPrimitive *child;
if (!primitive->getChild(child, i))
continue;
readPrimitive(child);
}
}
/*
* render()
*/
void CPrimChecker::render(CPrimZone *zone, uint8 bits)
{
if (zone->VPoints.size() < 3)
return;
string name;
if (zone->getPropertyByName("name", name) && Verbose)
nlinfo("Rendering CPrimZone '%s'", name.c_str());
// get the bouding box of the CPrimZone
CAABBox box;
box.setCenter(zone->VPoints[0]);
box.setHalfSize(CVector::Null);
uint i;
for (i=1; i<zone->VPoints.size(); ++i)
box.extend(zone->VPoints[i]);
sint32 xmin, ymin, xmax, ymax;
xmin = (sint32)(floor(box.getMin().x));
ymin = (sint32)(floor(box.getMin().y));
xmax = (sint32)(ceil(box.getMax().x));
ymax = (sint32)(ceil(box.getMax().y));
// Fill grid with points that belong to the CPrimZone
sint32 x, y;
for (y=ymin; y<=ymax; ++y)
for (x=xmin; x<=xmax; ++x)
if (zone->contains(CVector((float)x, (float)y, 0.0f)))
_Grid.set(x, y, bits);
}
/*
* Render a water shape, as a CPolygon
*/
void CPrimChecker::render(const CPolygon &poly, uint16 value)
{
list<CPolygon> convex;
// divide poly in convex polys
if (!poly.toConvexPolygons(convex, CMatrix::Identity))
{
convex.clear();
CPolygon reverse = poly;
std::reverse(reverse.Vertices.begin(), reverse.Vertices.end());
if (!reverse.toConvexPolygons(convex, CMatrix::Identity))
return;
}
list<CPolygon>::iterator it;
for (it=convex.begin(); it!=convex.end(); ++it)
{
CPolygon2D convex2d(*it);
CPolygon2D::TRasterVect rasterized;
sint ymin;
convex2d.computeBorders(rasterized, ymin);
sint dy;
for (dy=0; dy<(sint)rasterized.size(); ++dy)
{
sint x;
for (x=rasterized[dy].first; x<=rasterized[dy].second; ++x)
{
uint8 prevBits = _Grid.get((uint)x, (uint)(ymin+dy));
// only set if there was not a water shape there or if previous was lower
if ((prevBits & Water) != 0)
{
uint16 prevWS = _Grid.index((uint)x, (uint)(ymin+dy));
if (_WaterHeight[value] < _WaterHeight[prevWS])
continue;
}
_Grid.index((uint)x, (uint)(ymin+dy), value);
_Grid.set((uint)x, (uint)(ymin+dy), Water);
}
}
}
}
/*
* Render a CPolygon of bit value
*/
void CPrimChecker::renderBits(const CPolygon &poly, uint8 bits)
{
list<CPolygon> convex;
// divide poly in convex polys
if (!poly.toConvexPolygons(convex, CMatrix::Identity))
{
convex.clear();
CPolygon reverse = poly;
std::reverse(reverse.Vertices.begin(), reverse.Vertices.end());
if (!reverse.toConvexPolygons(convex, CMatrix::Identity))
return;
}
list<CPolygon>::iterator it;
for (it=convex.begin(); it!=convex.end(); ++it)
{
CPolygon2D convex2d(*it);
CPolygon2D::TRasterVect rasterized;
sint ymin;
convex2d.computeBorders(rasterized, ymin);
sint dy;
for (dy=0; dy<(sint)rasterized.size(); ++dy)
{
sint x;
for (x=rasterized[dy].first; x<=rasterized[dy].second; ++x)
{
_Grid.set((uint)x, (uint)(ymin+dy), bits);
}
}
}
}