599 lines
14 KiB
C
599 lines
14 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/>.
|
||
|
|
||
|
#ifndef NL_SURFACE_SPLITTER_H
|
||
|
#define NL_SURFACE_SPLITTER_H
|
||
|
|
||
|
#include "nel/misc/types_nl.h"
|
||
|
|
||
|
#include "nel/pacs/local_retriever.h"
|
||
|
#include "nel/pacs/quad_grid.h"
|
||
|
|
||
|
#include <map>
|
||
|
|
||
|
/**
|
||
|
* TODO Class description
|
||
|
* \author Benjamin Legros
|
||
|
* \author Nevrax France
|
||
|
* \date 2002
|
||
|
*/
|
||
|
class CSurfaceSplitter
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
///
|
||
|
class CVector2s64;
|
||
|
class CSurfaceId;
|
||
|
class CChain;
|
||
|
class CLoop;
|
||
|
class CSurface;
|
||
|
|
||
|
///
|
||
|
class CFixed64
|
||
|
{
|
||
|
protected:
|
||
|
sint64 _Value;
|
||
|
public:
|
||
|
CFixed64() : _Value(0) {}
|
||
|
CFixed64(const sint64 &v) : _Value(v) {}
|
||
|
CFixed64(const double &v) : _Value((sint64)(v*4294967296.0)) {}
|
||
|
CFixed64(const CFixed64 &v) : _Value(v._Value) {}
|
||
|
|
||
|
operator double(void) const { return ((double)_Value) / 4294967296.0; }
|
||
|
operator float(void) const { return ((float)_Value) / 4294967296.0f; }
|
||
|
operator sint64(void) const { return _Value; }
|
||
|
|
||
|
CFixed64 operator + (const CFixed64 &f) const { return CFixed64(_Value + f._Value); }
|
||
|
CFixed64 operator - (const CFixed64 &f) const { return CFixed64(_Value - f._Value); }
|
||
|
CFixed64 operator - () const { return CFixed64(-_Value); }
|
||
|
CFixed64 operator * (const CFixed64 &f) const
|
||
|
{
|
||
|
sint64 vh = _Value>>32;
|
||
|
sint64 vl = _Value&0xffffffff;
|
||
|
sint64 uh = f._Value>>32;
|
||
|
sint64 ul = f._Value&0xffffffff;
|
||
|
|
||
|
sint64 res = ((vh*uh)<<32)+(vh*ul+uh*vl)+((uint64)(vl*ul)>>32);
|
||
|
double fres = ((double)res) / 4294967296.0;
|
||
|
double cfres = (double)(*this) * (double)f;
|
||
|
return CFixed64(res);
|
||
|
}
|
||
|
CFixed64 operator / (const CFixed64 &f) const
|
||
|
{
|
||
|
if (f._Value == 0)
|
||
|
return CFixed64();
|
||
|
|
||
|
sint sign = 0;
|
||
|
uint64 a = _Value;
|
||
|
uint64 b = f._Value;
|
||
|
|
||
|
if ((sint64)a < 0)
|
||
|
a = -(sint64)a, sign ^= 1;
|
||
|
if ((sint64)b < 0)
|
||
|
b = -(sint64)b, sign ^= 1;
|
||
|
|
||
|
uint64 lb = INT64_CONSTANT (0x8000000000000000);
|
||
|
uint64 q = 0;
|
||
|
uint64 nh = a>>32;
|
||
|
uint64 nl = a<<32;
|
||
|
|
||
|
while (lb != 0)
|
||
|
{
|
||
|
if (nh >= b)
|
||
|
{
|
||
|
q |= (lb+lb);
|
||
|
nh -= b;
|
||
|
}
|
||
|
nh = (nh+nh) + (nl&lb ? 1 : 0);
|
||
|
lb >>= 1;
|
||
|
}
|
||
|
|
||
|
// round final bit
|
||
|
if (nh >= b)
|
||
|
++q;
|
||
|
|
||
|
CFixed64 result(sign ? -(sint64)q : +(sint64)q );
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
CFixed64 &operator += (const CFixed64 &f) { _Value+=f._Value; return *this; }
|
||
|
CFixed64 &operator -= (const CFixed64 &f) { _Value-=f._Value; return *this; }
|
||
|
CFixed64 &operator *= (const CFixed64 &f) { *this = *this*f; return *this; }
|
||
|
CFixed64 &operator /= (const CFixed64 &f) { *this = *this/f; return *this; }
|
||
|
|
||
|
bool operator == (const CFixed64 &f) const { return _Value == f._Value; }
|
||
|
bool operator != (const CFixed64 &f) const { return _Value != f._Value; }
|
||
|
bool operator < (const CFixed64 &f) const { return _Value < f._Value; }
|
||
|
bool operator <= (const CFixed64 &f) const { return _Value <= f._Value; }
|
||
|
bool operator >= (const CFixed64 &f) const { return _Value >= f._Value; }
|
||
|
bool operator > (const CFixed64 &f) const { return _Value > f._Value; }
|
||
|
|
||
|
double sqnorm() const { return (double)(*this * *this); }
|
||
|
double norm() const { return sqrt(sqnorm()); }
|
||
|
};
|
||
|
|
||
|
///
|
||
|
class CVector2s64
|
||
|
{
|
||
|
public:
|
||
|
CFixed64 x;
|
||
|
CFixed64 y;
|
||
|
|
||
|
CVector2s64() : x(), y() {}
|
||
|
CVector2s64(const sint64 &X, const sint64 &Y) : x(X), y(Y) {}
|
||
|
CVector2s64(const CFixed64 &X, const CFixed64 &Y) : x(X), y(Y) {}
|
||
|
CVector2s64(const NLMISC::CVector &v) : x(v.x), y(v.y) {}
|
||
|
CVector2s64(const NLMISC::CVectorD &v) : x(v.x), y(v.y) {}
|
||
|
CVector2s64(const NLPACS::CVector2s &v) : x(((sint64)v.x) << 25), y(((sint64)v.y) << 25) {}
|
||
|
|
||
|
CVector2s64 operator + (const CVector2s64 &v) const { return CVector2s64(x+v.x, y+v.y); }
|
||
|
CVector2s64 operator - (const CVector2s64 &v) const { return CVector2s64(x-v.x, y-v.y); }
|
||
|
CVector2s64 operator * (sint64 s) const { return CVector2s64(x*CFixed64(s), y*CFixed64(s)); }
|
||
|
CVector2s64 operator * (CFixed64 s) const { return CVector2s64(x*s, y*s); }
|
||
|
CVector2s64 operator * (double s) const { return CVector2s64(x*CFixed64(s), y*CFixed64(s)); }
|
||
|
CVector2s64 operator / (sint64 s) const { return CVector2s64(x*CFixed64(s), y*CFixed64(s)); }
|
||
|
CVector2s64 operator / (CFixed64 s) const { return CVector2s64(x*s, y*s); }
|
||
|
CVector2s64 operator / (double s) const { return CVector2s64(x*CFixed64(s), y*CFixed64(s)); }
|
||
|
CFixed64 operator * (const CVector2s64 &v) const { return x*v.x + y*v.y; }
|
||
|
CFixed64 operator ^ (const CVector2s64 &v) const { return x*v.y - y*v.x; }
|
||
|
|
||
|
bool operator == (const CVector2s64 &v) const { return x == v.x && y == v.y; }
|
||
|
bool operator != (const CVector2s64 &v) const { return x != v.x || y != v.y; }
|
||
|
|
||
|
CVector2s64 &operator += (const CVector2s64 &v)
|
||
|
{
|
||
|
x += v.x;
|
||
|
y += v.y;
|
||
|
return *this;
|
||
|
}
|
||
|
CVector2s64 &operator -= (const CVector2s64 &v)
|
||
|
{
|
||
|
x -= v.x;
|
||
|
y -= v.y;
|
||
|
return *this;
|
||
|
}
|
||
|
CVector2s64 &operator *= (sint64 s)
|
||
|
{
|
||
|
CFixed64 ss(s);
|
||
|
x *= ss;
|
||
|
y *= ss;
|
||
|
return *this;
|
||
|
}
|
||
|
CVector2s64 &operator *= (double s)
|
||
|
{
|
||
|
CFixed64 ss(s);
|
||
|
x *= ss;
|
||
|
y *= ss;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
NLMISC::CVector asVector() const
|
||
|
{
|
||
|
return NLMISC::CVector((float)x, (float)y, 0.0f);
|
||
|
}
|
||
|
NLMISC::CVectorD asVectorD() const
|
||
|
{
|
||
|
return NLMISC::CVectorD((double)x, (double)y, 0.0);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
///
|
||
|
class CSurfaceId
|
||
|
{
|
||
|
public:
|
||
|
uint16 Instance;
|
||
|
uint16 Surface;
|
||
|
uint16 SubSurface;
|
||
|
|
||
|
CSurfaceId(uint16 instance=65535, uint16 surface=65535, uint16 subsurface=0) :
|
||
|
Instance(instance),
|
||
|
Surface(surface),
|
||
|
SubSurface(subsurface) {}
|
||
|
|
||
|
bool operator == (const CSurfaceId &id) const { return Instance == id.Instance && Surface == id.Surface && SubSurface == id.SubSurface; }
|
||
|
bool operator != (const CSurfaceId &id) const { return !(*this == id); }
|
||
|
|
||
|
bool operator < (const CSurfaceId &id) const
|
||
|
{
|
||
|
if (Instance < id.Instance)
|
||
|
return true;
|
||
|
else if (Instance > id.Instance)
|
||
|
return false;
|
||
|
else if (Surface < id.Surface)
|
||
|
return true;
|
||
|
else if (Surface > id.Surface)
|
||
|
return false;
|
||
|
else
|
||
|
return SubSurface < id.SubSurface;
|
||
|
}
|
||
|
|
||
|
bool operator > (const CSurfaceId &id) const { return id < *this; }
|
||
|
bool operator <= (const CSurfaceId &id) const { return *this == id || *this < id; }
|
||
|
bool operator >= (const CSurfaceId &id) const { return *this == id || id < *this; }
|
||
|
};
|
||
|
|
||
|
///
|
||
|
class CChainId
|
||
|
{
|
||
|
public:
|
||
|
CChainId(uint32 id=0) : Id(id) {}
|
||
|
uint32 Id;
|
||
|
|
||
|
bool operator == (const CChainId &id) const { return Id == id.Id; }
|
||
|
bool operator != (const CChainId &id) const { return !(*this == id); }
|
||
|
bool operator < (const CChainId &id) const { return Id < id.Id; }
|
||
|
bool operator > (const CChainId &id) const { return id < *this; }
|
||
|
bool operator <= (const CChainId &id) const { return *this == id || *this < id; }
|
||
|
bool operator >= (const CChainId &id) const { return *this == id || id < *this; }
|
||
|
};
|
||
|
|
||
|
|
||
|
///
|
||
|
class CEdgeId
|
||
|
{
|
||
|
public:
|
||
|
CEdgeId(const CChainId &chain=CChainId(), uint edge=0) : Chain(chain), Edge(edge) {}
|
||
|
CChainId Chain;
|
||
|
uint Edge;
|
||
|
};
|
||
|
|
||
|
typedef NLPACS::CQuadGrid<CEdgeId> TEdgeGrid;
|
||
|
|
||
|
|
||
|
///
|
||
|
class CTipId
|
||
|
{
|
||
|
public:
|
||
|
CTipId(uint32 id=0) : Id(id) {}
|
||
|
uint32 Id;
|
||
|
|
||
|
bool operator == (const CTipId &id) const { return Id == id.Id; }
|
||
|
bool operator != (const CTipId &id) const { return !(*this == id); }
|
||
|
bool operator < (const CTipId &id) const { return Id < id.Id; }
|
||
|
bool operator > (const CTipId &id) const { return id < *this; }
|
||
|
bool operator <= (const CTipId &id) const { return *this == id || *this < id; }
|
||
|
bool operator >= (const CTipId &id) const { return *this == id || id < *this; }
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
///
|
||
|
class CChain
|
||
|
{
|
||
|
public:
|
||
|
CChainId Id;
|
||
|
CSurfaceId Left;
|
||
|
CSurfaceId Right;
|
||
|
CTipId Start;
|
||
|
CTipId Stop;
|
||
|
std::vector<CVector2s64> Vertices;
|
||
|
std::vector<TEdgeGrid::CIterator> Iterators;
|
||
|
bool DontSplit;
|
||
|
|
||
|
///
|
||
|
CChain() : DontSplit(false) {}
|
||
|
|
||
|
///
|
||
|
void dump(bool forward) const
|
||
|
{
|
||
|
NLMISC::InfoLog->displayRawNL("Chain:%d[Left=%d:%d:%d,Right=%d:%d:%d]", Id.Id, Left.Instance, Left.Surface, Left.SubSurface, Right.Instance, Right.Surface, Right.SubSurface);
|
||
|
sint i, j=0;
|
||
|
if (forward)
|
||
|
{
|
||
|
for (i=0; i<(sint)Vertices.size(); ++i, ++j)
|
||
|
NLMISC::InfoLog->displayRawNL("%d;%.3f;%.3f", j, (double)Vertices[i].x, (double)Vertices[i].y);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (i=(sint)Vertices.size()-1; i>=0; --i, ++j)
|
||
|
NLMISC::InfoLog->displayRawNL("%d;%.3f;%.3f", j, (double)Vertices[i].x, (double)Vertices[i].y);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
///
|
||
|
class CTip
|
||
|
{
|
||
|
public:
|
||
|
CTipId Id;
|
||
|
CVector2s64 Tip;
|
||
|
std::vector<CChainId> Chains;
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
///
|
||
|
class CLoop
|
||
|
{
|
||
|
public:
|
||
|
CSurfaceId Surface;
|
||
|
std::vector<CChainId> Chains;
|
||
|
|
||
|
///
|
||
|
void dump(const CSurfaceSplitter &splitter) const
|
||
|
{
|
||
|
uint i;
|
||
|
for (i=0; i<Chains.size(); ++i)
|
||
|
{
|
||
|
const CChain *chain = splitter.getChain(Chains[i]);
|
||
|
if (chain != NULL)
|
||
|
chain->dump(Surface == chain->Left);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///
|
||
|
void removeChain(CChainId chain)
|
||
|
{
|
||
|
std::vector<CChainId>::iterator it;
|
||
|
for (it=Chains.begin(); it!=Chains.end(); )
|
||
|
{
|
||
|
if (*it == chain)
|
||
|
it = Chains.erase(it);
|
||
|
else
|
||
|
++it;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///
|
||
|
class iterator
|
||
|
{
|
||
|
public:
|
||
|
CSurfaceSplitter *pSplitter;
|
||
|
CLoop *pLoop;
|
||
|
sint Chain;
|
||
|
CChain *pChain;
|
||
|
sint ChainVertex;
|
||
|
sint8 Direction;
|
||
|
sint8 ChainDirection;
|
||
|
|
||
|
iterator(CSurfaceSplitter *splitter=NULL, CLoop *loop=NULL, bool forward=true) : pSplitter(splitter), pLoop(loop), Chain(0), pChain(NULL), ChainVertex(0), Direction(0), ChainDirection(0)
|
||
|
{
|
||
|
Direction = forward ? +1 : -1;
|
||
|
if (splitter == NULL || pLoop == NULL)
|
||
|
return;
|
||
|
Chain = (Direction>0 ? 0 : (sint)pLoop->Chains.size()-1);
|
||
|
resetChain();
|
||
|
}
|
||
|
iterator(const iterator &it)
|
||
|
{
|
||
|
*this = it;
|
||
|
}
|
||
|
|
||
|
iterator &operator = (const iterator &it)
|
||
|
{
|
||
|
pSplitter = it.pSplitter;
|
||
|
pLoop = it.pLoop;
|
||
|
Chain = it.Chain;
|
||
|
pChain = it.pChain;
|
||
|
ChainVertex = it.ChainVertex;
|
||
|
Direction = it.Direction;
|
||
|
return *this;
|
||
|
}
|
||
|
bool operator == (const iterator &it)
|
||
|
{
|
||
|
return pSplitter == it.pSplitter &&
|
||
|
pLoop == it.pLoop &&
|
||
|
Chain == it.Chain &&
|
||
|
ChainVertex == it.ChainVertex;
|
||
|
}
|
||
|
bool operator != (const iterator &it) { return !(*this == it); }
|
||
|
|
||
|
// ++it
|
||
|
iterator &operator ++ ()
|
||
|
{
|
||
|
ChainVertex += ChainDirection;
|
||
|
if (ChainVertex == 0 || ChainVertex == (sint)pChain->Vertices.size()-1)
|
||
|
{
|
||
|
Chain += Direction;
|
||
|
resetChain();
|
||
|
}
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
// it++
|
||
|
iterator operator ++ (int)
|
||
|
{
|
||
|
iterator tmp(*this);
|
||
|
|
||
|
ChainVertex += ChainDirection;
|
||
|
if (ChainVertex == 0 || ChainVertex == (sint)pChain->Vertices.size()-1)
|
||
|
{
|
||
|
Chain += Direction;
|
||
|
resetChain();
|
||
|
}
|
||
|
|
||
|
return tmp;
|
||
|
}
|
||
|
|
||
|
void resetChain()
|
||
|
{
|
||
|
if (Chain < 0 || Chain == (sint)pLoop->Chains.size())
|
||
|
{
|
||
|
pSplitter = NULL;
|
||
|
pLoop = NULL;
|
||
|
Chain = 0;
|
||
|
pChain = NULL;
|
||
|
ChainVertex = 0;
|
||
|
Direction = 0;
|
||
|
ChainDirection = 0;
|
||
|
return;
|
||
|
}
|
||
|
pChain = pSplitter->getChain(pLoop->Chains[Chain]);
|
||
|
ChainDirection = (pChain->Left == pLoop->Surface ? Direction : -Direction);
|
||
|
ChainVertex = (ChainDirection>0 ? 0 : (sint)pChain->Vertices.size()-1);
|
||
|
}
|
||
|
|
||
|
CVector2s64 operator * () const
|
||
|
{
|
||
|
return pChain->Vertices[ChainVertex];
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
///
|
||
|
class CSurface
|
||
|
{
|
||
|
public:
|
||
|
CSurfaceId Id;
|
||
|
std::vector<CLoop> Loops;
|
||
|
|
||
|
///
|
||
|
void dump(const CSurfaceSplitter &splitter) const
|
||
|
{
|
||
|
NLMISC::InfoLog->displayRawNL("---Surface:%d:%d:%d", Id.Instance, Id.Surface, Id.SubSurface);
|
||
|
uint i;
|
||
|
for (i=0; i<Loops.size(); ++i)
|
||
|
{
|
||
|
NLMISC::InfoLog->displayRawNL("Loop:%d", i);
|
||
|
Loops[i].dump(splitter);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///
|
||
|
void removeChain(CChainId chain)
|
||
|
{
|
||
|
uint i;
|
||
|
for (i=0; i<Loops.size(); ++i)
|
||
|
Loops[i].removeChain(chain);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
protected:
|
||
|
typedef std::map<CSurfaceId, CSurface> TSurfaceMap;
|
||
|
typedef std::map<CChainId, CChain> TChainMap;
|
||
|
typedef std::map<CTipId, CTip> TTipMap;
|
||
|
|
||
|
TSurfaceMap _Surfaces;
|
||
|
uint _NumSurfaces;
|
||
|
TChainMap _Chains;
|
||
|
uint _NumChains;
|
||
|
TTipMap _Tips;
|
||
|
uint _NumTips;
|
||
|
|
||
|
TEdgeGrid _Edges;
|
||
|
|
||
|
|
||
|
public:
|
||
|
|
||
|
/// Constructor
|
||
|
CSurfaceSplitter();
|
||
|
|
||
|
///
|
||
|
void build(NLPACS::CLocalRetriever &lr);
|
||
|
|
||
|
///
|
||
|
CSurface *getSurface(const CSurfaceId &id)
|
||
|
{
|
||
|
TSurfaceMap::iterator it = _Surfaces.find(id);
|
||
|
return (it == _Surfaces.end() ? NULL : &((*it).second));
|
||
|
}
|
||
|
|
||
|
///
|
||
|
CChain *getChain(const CChainId &id)
|
||
|
{
|
||
|
TChainMap::iterator it = _Chains.find(id);
|
||
|
return (it == _Chains.end() ? NULL : &((*it).second));
|
||
|
}
|
||
|
|
||
|
///
|
||
|
const CSurface *getSurface(const CSurfaceId &id) const
|
||
|
{
|
||
|
TSurfaceMap::const_iterator it = _Surfaces.find(id);
|
||
|
return (it == _Surfaces.end() ? NULL : &((*it).second));
|
||
|
}
|
||
|
|
||
|
///
|
||
|
const CChain *getChain(const CChainId &id) const
|
||
|
{
|
||
|
TChainMap::const_iterator it = _Chains.find(id);
|
||
|
return (it == _Chains.end() ? NULL : &((*it).second));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
protected:
|
||
|
|
||
|
///
|
||
|
void buildChain(NLPACS::CLocalRetriever &lr, uint chain);
|
||
|
|
||
|
///
|
||
|
void buildSurface(NLPACS::CLocalRetriever &lr, uint surface);
|
||
|
|
||
|
|
||
|
|
||
|
///
|
||
|
void initEdgeGrid();
|
||
|
|
||
|
|
||
|
|
||
|
///
|
||
|
void splitChains();
|
||
|
|
||
|
///
|
||
|
void splitChain(TChainMap::iterator it, uint &numInters);
|
||
|
|
||
|
|
||
|
|
||
|
///
|
||
|
void dump() const
|
||
|
{
|
||
|
TSurfaceMap::const_iterator it;
|
||
|
|
||
|
for (it=_Surfaces.begin(); it!=_Surfaces.end(); ++it)
|
||
|
(*it).second.dump(*this);
|
||
|
}
|
||
|
|
||
|
///
|
||
|
void dumpChain(CChainId chain) const
|
||
|
{
|
||
|
TChainMap::const_iterator it = _Chains.find(chain);
|
||
|
if (it == _Chains.end())
|
||
|
return;
|
||
|
|
||
|
(*it).second.dump(true);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
///
|
||
|
bool intersect(const CVector2s64 &v0, const CVector2s64 &v1,
|
||
|
const CVector2s64 &c0, const CVector2s64 &c1,
|
||
|
CVector2s64 &intersect, CFixed64 &ndist);
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
///
|
||
|
CChainId addChain(const CSurfaceId &left, const CSurfaceId &right, const std::vector<CVector2s64> &points, bool dontSplit=false);
|
||
|
|
||
|
///
|
||
|
void removeChain(CChainId chain);
|
||
|
|
||
|
///
|
||
|
void replaceChain(CChainId chain, const std::vector<CChainId> &chains);
|
||
|
};
|
||
|
|
||
|
|
||
|
#endif // NL_SURFACE_SPLITTER_H
|
||
|
|
||
|
/* End of surface_splitter.h */
|