Monster update which adds detail height meshes.

- Added detail height mesh generation (RecastDetailMesh.cpp) for single,tiled statmeshes as well as tilemesh.
- Added feature to contour tracing which detects extra vertices along tile edges which should be removed later.
- Changed the tiled stat mesh preprocess, so that it first generated polymeshes per tile and finally combines them.
- Fixed bug in the GUI code where invisible buttons could be pressed.
This commit is contained in:
Mikko Mononen
2009-08-24 12:44:44 +00:00
parent b1addd3574
commit 6de4d30705
33 changed files with 5321 additions and 1765 deletions

View File

@@ -153,6 +153,8 @@ inline bool checkOverlapBox(const unsigned short amin[3], const unsigned short a
void closestPtPointTriangle(float* closest, const float* p,
const float* a, const float* b, const float* c);
bool closestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h);
bool intersectSegmentPoly2D(const float* p0, const float* p1,
const float* verts, int nverts,
float& tmin, float& tmax,

View File

@@ -34,8 +34,16 @@ struct dtStatPoly
unsigned char flags; // Flags (not used).
};
struct dtStatPolyDetail
{
unsigned short vbase; // Offset to detail vertex array.
unsigned short nverts; // Number of vertices in the detail mesh.
unsigned short tbase; // Offset to detail triangle array.
unsigned short ntris; // Number of triangles.
};
const int DT_STAT_NAVMESH_MAGIC = 'NAVM';
const int DT_STAT_NAVMESH_VERSION = 2;
const int DT_STAT_NAVMESH_VERSION = 3;
struct dtStatBVNode
{
@@ -50,11 +58,17 @@ struct dtStatNavMeshHeader
int npolys;
int nverts;
int nnodes;
int ndmeshes;
int ndverts;
int ndtris;
float cs;
float bmin[3], bmax[3];
dtStatPoly* polys;
float* verts;
dtStatBVNode* bvtree;
dtStatPolyDetail* dmeshes;
float* dverts;
unsigned char* dtris;
};
class dtStatNavMesh
@@ -160,8 +174,18 @@ public:
// Returns: true if closest point found.
bool closestPointToPoly(dtStatPolyRef ref, const float* pos, float* closest) const;
// Returns height of the polygon at specified location.
// Params:
// ref - (in) ref to the polygon.
// pos - (in) the point where to locate the height.
// height - (out) height at the location.
// Returns: true if oer polygon.
bool getPolyHeight(dtStatPolyRef ref, const float* pos, float* height) const;
// Returns pointer to a polygon based on ref.
const dtStatPoly* getPolyByRef(dtStatPolyRef ref) const;
// Returns polygon index based on ref, or -1 if failed.
int getPolyIndexByRef(dtStatPolyRef ref) const;
// Returns number of navigation polygons.
inline int getPolyCount() const { return m_header ? m_header->npolys : 0; }
// Rerturns pointer to specified navigation polygon.
@@ -170,6 +194,14 @@ public:
inline int getVertexCount() const { return m_header ? m_header->nverts : 0; }
// Returns pointer to specified vertex.
inline const float* getVertex(int i) const { return &m_header->verts[i*3]; }
// Returns number of navigation polygons details.
inline int getPolyDetailCount() const { return m_header ? m_header->ndmeshes : 0; }
// Rerturns pointer to specified navigation polygon detail.
const dtStatPolyDetail* getPolyDetail(int i) const { return &m_header->dmeshes[i]; }
// Returns pointer to specified vertex.
inline const float* getDetailVertex(int i) const { return &m_header->dverts[i*3]; }
// Returns pointer to specified vertex.
inline const unsigned char* getDetailTri(int i) const { return &m_header->dtris[i*4]; }
bool isInClosedList(dtStatPolyRef ref) const;

View File

@@ -22,6 +22,8 @@
bool dtCreateNavMeshData(const unsigned short* verts, const int nverts,
const unsigned short* polys, const int npolys, const int nvp,
const float* bmin, const float* bmax, float cs, float ch,
const unsigned short* dmeshes, const float* dverts, const int ndverts,
const unsigned char* dtris, const int ndtris,
unsigned char** outData, int* outDataSize);
#endif // DETOURSTATNAVMESHBUILDER_H

View File

@@ -37,7 +37,7 @@ static const int DT_MAX_TILES = 1 << DT_TILE_REF_TILE_BITS;
static const int DT_MAX_POLYGONS = 1 << DT_TILE_REF_POLY_BITS;
static const int DT_TILE_NAVMESH_MAGIC = 'NAVT';
static const int DT_TILE_NAVMESH_VERSION = 1;
static const int DT_TILE_NAVMESH_VERSION = 2;
// Structure holding the navigation polygon data.
struct dtTilePoly
@@ -50,6 +50,14 @@ struct dtTilePoly
unsigned char flags; // Flags (not used).
};
struct dtTilePolyDetail
{
unsigned short vbase; // Offset to detail vertex array.
unsigned short nverts; // Number of vertices in the detail mesh.
unsigned short tbase; // Offset to detail triangle array.
unsigned short ntris; // Number of triangles.
};
// Stucture holding a link to another polygon.
struct dtTileLink
{
@@ -68,10 +76,16 @@ struct dtTileHeader
int nverts; // Number of vertices in the tile.
int nlinks; // Number of links in the tile (will be updated when tile is added).
int maxlinks; // Number of allocated links.
int ndmeshes;
int ndverts;
int ndtris;
float bmin[3], bmax[3]; // Bounding box of the tile.
dtTilePoly* polys; // Pointer to the polygons (will be updated when tile is added).
float* verts; // Pointer to the vertices (will be updated when tile added).
dtTileLink* links; // Pointer to the links (will be updated when tile added).
dtTilePolyDetail* dmeshes;
float* dverts;
unsigned char* dtris;
};
struct dtTile
@@ -236,6 +250,14 @@ public:
// Returns: true if closest point found.
bool closestPointToPoly(dtTilePolyRef ref, const float* pos, float* closest) const;
// Returns height of the polygon at specified location.
// Params:
// ref - (in) ref to the polygon.
// pos - (in) the point where to locate the height.
// height - (out) height at the location.
// Returns: true if over polygon.
bool getPolyHeight(dtTilePolyRef ref, const float* pos, float* height) const;
// Returns pointer to a polygon based on ref.
const dtTilePoly* getPolyByRef(dtTilePolyRef ref) const;

View File

@@ -21,6 +21,8 @@
bool dtCreateNavMeshTileData(const unsigned short* verts, const int nverts,
const unsigned short* polys, const int npolys, const int nvp,
const unsigned short* dmeshes, const float* dverts, const int ndverts,
const unsigned char* dtris, const int ndtris,
const float* bmin, const float* bmax, float cs, float ch, int tileSize, int walkableClimb,
unsigned char** outData, int* outDataSize);

View File

@@ -203,3 +203,42 @@ void calcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float*
tc[1] *= s;
tc[2] *= s;
}
inline float vdot2(const float* a, const float* b)
{
return a[0]*b[0] + a[2]*b[2];
}
#include <stdio.h>
bool closestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h)
{
float v0[3], v1[3], v2[3];
vsub(v0, c,a);
vsub(v1, b,a);
vsub(v2, p,a);
const float dot00 = vdot2(v0, v0);
const float dot01 = vdot2(v0, v1);
const float dot02 = vdot2(v0, v2);
const float dot11 = vdot2(v1, v1);
const float dot12 = vdot2(v1, v2);
// Compute barycentric coordinates
float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
// The (sloppy) epsilon is needed to allow to get height of points which
// are interpolated along the edges of the triangles.
static const float EPS = 1e-4f;
// If point lies inside the triangle, return interpolated ycoord.
if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS)
{
h = a[1] + v0[1]*u + v1[1]*v;
return true;
}
return false;
}

View File

@@ -23,24 +23,47 @@
void dtDebugDrawStatNavMeshPoly(const dtStatNavMesh* mesh, dtStatPolyRef ref, const float* col)
{
const dtStatPoly* p = mesh->getPolyByRef(ref);
if (!p)
return;
int idx = mesh->getPolyIndexByRef(ref);
if (idx == -1) return;
glColor4f(col[0],col[1],col[2],0.25f);
glBegin(GL_TRIANGLES);
unsigned short vi[3];
for (int j = 2; j < (int)p->nv; ++j)
if (mesh->getPolyDetailCount())
{
vi[0] = p->v[0];
vi[1] = p->v[j-1];
vi[2] = p->v[j];
for (int k = 0; k < 3; ++k)
const dtStatPoly* p = mesh->getPoly(idx);
const dtStatPolyDetail* pd = mesh->getPolyDetail(idx);
glBegin(GL_TRIANGLES);
for (int j = 0; j < pd->ntris; ++j)
{
const float* v = mesh->getVertex(vi[k]);
glVertex3f(v[0], v[1]+0.2f, v[2]);
const unsigned char* t = mesh->getDetailTri(pd->tbase+j);
for (int k = 0; k < 3; ++k)
{
if (t[k] < p->nv)
glVertex3fv(mesh->getVertex(p->v[t[k]]));
else
glVertex3fv(mesh->getDetailVertex(pd->vbase+(t[k]-p->nv)));
}
}
glEnd();
}
else
{
const dtStatPoly* p = mesh->getPoly(idx);
glBegin(GL_TRIANGLES);
unsigned short vi[3];
for (int j = 2; j < (int)p->nv; ++j)
{
vi[0] = p->v[0];
vi[1] = p->v[j-1];
vi[2] = p->v[j];
for (int k = 0; k < 3; ++k)
{
const float* v = mesh->getVertex(vi[k]);
glVertex3f(v[0], v[1]+0.2f, v[2]);
}
}
glEnd();
}
glEnd();
}
static void drawBoxWire(float minx, float miny, float minz, float maxx, float maxy, float maxz, const float* col)
@@ -103,76 +126,113 @@ void dtDebugDrawStatNavMeshBVTree(const dtStatNavMesh* mesh)
glEnd();
}
void dtDebugDrawStatNavMesh(const dtStatNavMesh* mesh, bool drawClosedList)
static float distancePtLine2d(const float* pt, const float* p, const float* q)
{
glBegin(GL_TRIANGLES);
float pqx = q[0] - p[0];
float pqz = q[2] - p[2];
float dx = pt[0] - p[0];
float dz = pt[2] - p[2];
float d = pqx*pqx + pqz*pqz;
float t = pqx*dx + pqz*dz;
if (d != 0) t /= d;
dx = p[0] + t*pqx - pt[0];
dz = p[2] + t*pqz - pt[2];
return dx*dx + dz*dz;
}
static void drawStatMeshPolyBoundaries(const dtStatNavMesh* mesh, bool inner)
{
static const float thr = 0.01f*0.01f;
glBegin(GL_LINES);
for (int i = 0; i < mesh->getPolyCount(); ++i)
{
const dtStatPoly* p = mesh->getPoly(i);
const dtStatPolyDetail* pd = mesh->getPolyDetail(i);
for (int j = 0, nj = (int)p->nv; j < nj; ++j)
{
if (inner)
{
// Skip non-connected edges.
if (p->n[j] == 0) continue;
}
else
{
// Skip connected edges.
if (p->n[j] != 0) continue;
}
const float* v0 = mesh->getVertex(p->v[j]);
const float* v1 = mesh->getVertex(p->v[(j+1) % nj]);
// Draw detail mesh edges which align with the actual poly edge.
// This is really slow.
for (int k = 0; k < pd->ntris; ++k)
{
const unsigned char* t = mesh->getDetailTri(pd->tbase+k);
const float* tv[3];
for (int m = 0; m < 3; ++m)
{
if (t[m] < p->nv)
tv[m] = mesh->getVertex(p->v[t[m]]);
else
tv[m] = mesh->getDetailVertex(pd->vbase+(t[m]-p->nv));
}
for (int m = 0, n = 2; m < 3; n=m++)
{
if (((t[3] >> (n*2)) & 0x3) == 0) continue; // Skip inner edges.
if (distancePtLine2d(tv[n],v0,v1) < thr &&
distancePtLine2d(tv[m],v0,v1) < thr)
{
glVertex3fv(tv[n]);
glVertex3fv(tv[m]);
}
}
}
}
}
glEnd();
}
void dtDebugDrawStatNavMesh(const dtStatNavMesh* mesh, bool drawClosedList)
{
glBegin(GL_TRIANGLES);
for (int i = 0; i < mesh->getPolyDetailCount(); ++i)
{
const dtStatPoly* p = mesh->getPoly(i);
const dtStatPolyDetail* pd = mesh->getPolyDetail(i);
if (drawClosedList && mesh->isInClosedList(i+1))
glColor4ub(255,196,0,64);
else
glColor4ub(0,196,255,64);
unsigned short vi[3];
for (int j = 2; j < (int)p->nv; ++j)
for (int j = 0; j < pd->ntris; ++j)
{
vi[0] = p->v[0];
vi[1] = p->v[j-1];
vi[2] = p->v[j];
const unsigned char* t = mesh->getDetailTri(pd->tbase+j);
for (int k = 0; k < 3; ++k)
{
const float* v = mesh->getVertex(vi[k]);
glVertex3f(v[0], v[1]+0.2f, v[2]);
if (t[k] < p->nv)
glVertex3fv(mesh->getVertex(p->v[t[k]]));
else
glVertex3fv(mesh->getDetailVertex(pd->vbase+(t[k]-p->nv)));
}
}
}
glEnd();
// Draw tri boundaries
// Draw inter poly boundaries
glColor4ub(0,48,64,32);
glLineWidth(1.5f);
glBegin(GL_LINES);
for (int i = 0; i < mesh->getPolyCount(); ++i)
{
const dtStatPoly* p = mesh->getPoly(i);
for (int j = 0, nj = (int)p->nv; j < nj; ++j)
{
if (p->n[j] == 0) continue;
int vi[2];
vi[0] = p->v[j];
vi[1] = p->v[(j+1) % nj];
for (int k = 0; k < 2; ++k)
{
const float* v = mesh->getVertex(vi[k]);
glVertex3f(v[0], v[1]+0.21f, v[2]);
}
}
}
glEnd();
drawStatMeshPolyBoundaries(mesh, true);
// Draw boundaries
// Draw outer poly boundaries
glLineWidth(2.5f);
glColor4ub(0,48,64,220);
glBegin(GL_LINES);
for (int i = 0; i < mesh->getPolyCount(); ++i)
{
const dtStatPoly* p = mesh->getPoly(i);
for (int j = 0, nj = (int)p->nv; j < nj; ++j)
{
if (p->n[j] != 0) continue;
int vi[2];
vi[0] = p->v[j];
vi[1] = p->v[(j+1) % nj];
for (int k = 0; k < 2; ++k)
{
const float* v = mesh->getVertex(vi[k]);
glVertex3f(v[0], v[1]+0.21f, v[2]);
}
}
}
glEnd();
drawStatMeshPolyBoundaries(mesh, false);
glLineWidth(1.0f);
glPointSize(3.0f);
@@ -181,116 +241,120 @@ void dtDebugDrawStatNavMesh(const dtStatNavMesh* mesh, bool drawClosedList)
for (int i = 0; i < mesh->getVertexCount(); ++i)
{
const float* v = mesh->getVertex(i);
glVertex3f(v[0], v[1]+0.21f, v[2]);
glVertex3f(v[0], v[1], v[2]);
}
glEnd();
glPointSize(1.0f);
}
static void drawTilePolyBoundaries(const dtTileHeader* header, bool inner)
{
static const float thr = 0.01f*0.01f;
glBegin(GL_LINES);
for (int i = 0; i < header->npolys; ++i)
{
const dtTilePoly* p = &header->polys[i];
const dtTilePolyDetail* pd = &header->dmeshes[i];
for (int j = 0, nj = (int)p->nv; j < nj; ++j)
{
if (inner)
{
if (p->n[j] == 0) continue;
if (p->n[j] & 0x8000)
{
bool con = false;
for (int k = 0; k < p->nlinks; ++k)
{
if (header->links[p->links+k].e == j)
{
con = true;
break;
}
}
if (con)
glColor4ub(255,255,255,128);
else
glColor4ub(0,0,0,128);
}
else
glColor4ub(0,48,64,32);
}
else
{
if (p->n[j] != 0) continue;
}
const float* v0 = &header->verts[p->v[j]*3];
const float* v1 = &header->verts[p->v[(j+1)%nj]*3];
// Draw detail mesh edges which align with the actual poly edge.
// This is really slow.
for (int k = 0; k < pd->ntris; ++k)
{
const unsigned char* t = &header->dtris[(pd->tbase+k)*4];
const float* tv[3];
for (int m = 0; m < 3; ++m)
{
if (t[m] < p->nv)
tv[m] = &header->verts[p->v[t[m]]*3];
else
tv[m] = &header->dverts[(pd->vbase+(t[m]-p->nv))*3];
}
for (int m = 0, n = 2; m < 3; n=m++)
{
if (((t[3] >> (n*2)) & 0x3) == 0) continue; // Skip inner detail edges.
if (distancePtLine2d(tv[n],v0,v1) < thr &&
distancePtLine2d(tv[m],v0,v1) < thr)
{
glVertex3fv(tv[n]);
glVertex3fv(tv[m]);
}
}
}
}
}
glEnd();
}
static void drawTile(const dtTileHeader* header)
{
glBegin(GL_TRIANGLES);
for (int i = 0; i < header->npolys; ++i)
{
const dtTilePoly* p = &header->polys[i];
const dtTilePolyDetail* pd = &header->dmeshes[i];
glColor4ub(0,196,255,64);
unsigned short vi[3];
for (int j = 2; j < (int)p->nv; ++j)
for (int j = 0; j < pd->ntris; ++j)
{
vi[0] = p->v[0];
vi[1] = p->v[j-1];
vi[2] = p->v[j];
const unsigned char* t = &header->dtris[(pd->tbase+j)*4];
for (int k = 0; k < 3; ++k)
{
const float* v = &header->verts[vi[k]*3];
glVertex3f(v[0], v[1]+0.2f, v[2]);
}
}
}
glEnd();
// Draw tri boundaries
glLineWidth(1.5f);
glBegin(GL_LINES);
for (int i = 0; i < header->npolys; ++i)
{
const dtTilePoly* p = &header->polys[i];
for (int j = 0, nj = (int)p->nv; j < nj; ++j)
{
if (p->n[j] == 0) continue;
if (p->n[j] & 0x8000)
{
bool con = false;
for (int k = 0; k < p->nlinks; ++k)
{
if (header->links[p->links+k].e == j)
{
con = true;
break;
}
}
if (con)
glColor4ub(255,255,255,128);
if (t[k] < p->nv)
glVertex3fv(&header->verts[p->v[t[k]]*3]);
else
glColor4ub(0,0,0,128);
}
else
glColor4ub(0,48,64,32);
/*
{
// Portal
int side = (p->n[j] >> 13) & 3;
int i = p->n[j] & 0x1fff;
if (!header->portals[side][i].ncon) continue;
}*/
int vi[2];
vi[0] = p->v[j];
vi[1] = p->v[(j+1) % nj];
for (int k = 0; k < 2; ++k)
{
const float* v = &header->verts[vi[k]*3];
glVertex3f(v[0], v[1]+0.21f, v[2]);
glVertex3fv(&header->dverts[(pd->vbase+t[k]-p->nv)*3]);
}
}
}
glEnd();
// Draw boundaries
// Draw inter poly boundaries
glColor4ub(0,48,64,32);
glLineWidth(1.5f);
drawTilePolyBoundaries(header, true);
// Draw outer poly boundaries
glLineWidth(2.5f);
glColor4ub(0,48,64,220);
glBegin(GL_LINES);
for (int i = 0; i < header->npolys; ++i)
{
const dtTilePoly* p = &header->polys[i];
for (int j = 0, nj = (int)p->nv; j < nj; ++j)
{
if (p->n[j] != 0)
{
/* if (p->n[j] & 0x8000)
{
// Portal
int side = (p->n[j] >> 13) & 3;
int i = p->n[j] & 0x1fff;
if (header->portals[side][i].ncon) continue;
}
else*/
continue;
}
int vi[2];
vi[0] = p->v[j];
vi[1] = p->v[(j+1) % nj];
for (int k = 0; k < 2; ++k)
{
const float* v = &header->verts[vi[k]*3];
glVertex3f(v[0], v[1]+0.21f, v[2]);
}
}
}
glEnd();
drawTilePolyBoundaries(header, false);
glLineWidth(1.0f);
glPointSize(3.0f);
@@ -299,10 +363,10 @@ static void drawTile(const dtTileHeader* header)
for (int i = 0; i < header->nverts; ++i)
{
const float* v = &header->verts[i*3];
glVertex3f(v[0], v[1]+0.21f, v[2]);
glVertex3f(v[0], v[1], v[2]);
}
glEnd();
glPointSize(1.0f);
glPointSize(1.0f);
// Draw portals
/* glBegin(GL_LINES);
@@ -401,21 +465,30 @@ void dtDebugDrawTiledNavMesh(const dtTiledNavMesh* mesh)
void dtDebugDrawTiledNavMeshPoly(const dtTiledNavMesh* mesh, dtTilePolyRef ref, const float* col)
{
const dtTilePoly* p = mesh->getPolyByRef(ref);
if (!p) return;
const float* verts = mesh->getPolyVertsByRef(ref);
unsigned int salt, it, ip;
dtDecodeTileId(ref, salt, it, ip);
if (it >= DT_MAX_TILES) return;
const dtTile* tile = mesh->getTile(it);
if (tile->salt != salt || tile->header == 0) return;
const dtTileHeader* header = tile->header;
if (ip >= (unsigned int)header->npolys) return;
glColor4f(col[0],col[1],col[2],0.25f);
const dtTilePoly* p = &header->polys[ip];
const dtTilePolyDetail* pd = &header->dmeshes[ip];
glBegin(GL_TRIANGLES);
unsigned short vi[3];
for (int j = 2; j < (int)p->nv; ++j)
for (int i = 0; i < pd->ntris; ++i)
{
vi[0] = p->v[0];
vi[1] = p->v[j-1];
vi[2] = p->v[j];
for (int k = 0; k < 3; ++k)
const unsigned char* t = &header->dtris[(pd->tbase+i)*4];
for (int j = 0; j < 3; ++j)
{
const float* v = &verts[vi[k]*3];
glVertex3f(v[0], v[1]+0.2f, v[2]);
if (t[j] < p->nv)
glVertex3fv(&header->verts[p->v[t[j]]*3]);
else
glVertex3fv(&header->dverts[(pd->vbase+t[j]-p->nv)*3]);
}
}
glEnd();

View File

@@ -55,10 +55,19 @@ bool dtStatNavMesh::init(unsigned char* data, int dataSize, bool ownsData)
const int headerSize = sizeof(dtStatNavMeshHeader);
const int vertsSize = sizeof(float)*3*header->nverts;
const int polysSize = sizeof(dtStatPoly)*header->npolys;
const int nodesSize = sizeof(dtStatBVNode)*header->npolys*2;
const int detailMeshesSize = sizeof(dtStatPolyDetail)*header->ndmeshes;
const int detailVertsSize = sizeof(float)*3*header->ndverts;
const int detailTrisSize = sizeof(unsigned char)*4*header->ndtris;
header->verts = (float*)(data + headerSize);
header->polys = (dtStatPoly*)(data + headerSize + vertsSize);
header->bvtree = (dtStatBVNode*)(data + headerSize + vertsSize + polysSize);
unsigned char* d = data + headerSize;
header->verts = (float*)d; d += vertsSize;
header->polys = (dtStatPoly*)d; d += polysSize;
header->bvtree = (dtStatBVNode*)d; d += nodesSize;
header->dmeshes = (dtStatPolyDetail*)d; d += detailMeshesSize;
header->dverts = (float*)d; d += detailVertsSize;
header->dtris = (unsigned char*)d; d += detailTrisSize;
m_nodePool = new dtNodePool(2048, 256);
if (!m_nodePool)
@@ -85,6 +94,12 @@ const dtStatPoly* dtStatNavMesh::getPolyByRef(dtStatPolyRef ref) const
return &m_header->polys[ref-1];
}
int dtStatNavMesh::getPolyIndexByRef(dtStatPolyRef ref) const
{
if (!m_header || ref == 0 || (int)ref > m_header->npolys) return -1;
return (int)ref-1;
}
int dtStatNavMesh::findPath(dtStatPolyRef startRef, dtStatPolyRef endRef,
const float* startPos, const float* endPos,
dtStatPolyRef* path, const int maxPathSize)
@@ -219,20 +234,27 @@ int dtStatNavMesh::findPath(dtStatPolyRef startRef, dtStatPolyRef endRef,
bool dtStatNavMesh::closestPointToPoly(dtStatPolyRef ref, const float* pos, float* closest) const
{
const dtStatPoly* poly = getPolyByRef(ref);
if (!poly)
int idx = getPolyIndexByRef(ref);
if (idx == -1)
return false;
float closestDistSqr = FLT_MAX;
const dtStatPoly* p = getPoly(idx);
const dtStatPolyDetail* pd = getPolyDetail(idx);
for (int i = 2; i < (int)poly->nv; ++i)
for (int j = 0; j < pd->ntris; ++j)
{
const float* v0 = getVertex(poly->v[0]);
const float* v1 = getVertex(poly->v[i-1]);
const float* v2 = getVertex(poly->v[i]);
const unsigned char* t = getDetailTri(pd->tbase+j);
const float* v[3];
for (int k = 0; k < 3; ++k)
{
if (t[k] < p->nv)
v[k] = getVertex(p->v[t[k]]);
else
v[k] = getDetailVertex(pd->vbase+(t[k]-p->nv));
}
float pt[3];
closestPtPointTriangle(pt, pos, v0, v1, v2);
closestPtPointTriangle(pt, pos, v[0], v[1], v[2]);
float d = vdistSqr(pos, pt);
if (d < closestDistSqr)
{
@@ -244,6 +266,38 @@ bool dtStatNavMesh::closestPointToPoly(dtStatPolyRef ref, const float* pos, floa
return true;
}
bool dtStatNavMesh::getPolyHeight(dtStatPolyRef ref, const float* pos, float* height) const
{
int idx = getPolyIndexByRef(ref);
if (idx == -1)
return false;
const dtStatPoly* p = getPoly(idx);
const dtStatPolyDetail* pd = getPolyDetail(idx);
for (int i = 0; i < pd->ntris; ++i)
{
const unsigned char* t = getDetailTri(pd->tbase+i);
const float* v[3];
for (int j = 0; j < 3; ++j)
{
if (t[j] < p->nv)
v[j] = getVertex(p->v[t[j]]);
else
v[j] = getDetailVertex(pd->vbase+(t[j]-p->nv));
}
float h;
if (closestHeightPointTriangle(pos, v[0], v[1], v[2], h))
{
if (height)
*height = h;
return true;
}
}
return false;
}
int dtStatNavMesh::findStraightPath(const float* startPos, const float* endPos,
const dtStatPolyRef* path, const int pathSize,
float* straightPath, const int maxStraightPathSize)

View File

@@ -209,6 +209,8 @@ static int createBVTree(const unsigned short* verts, const int nverts,
bool dtCreateNavMeshData(const unsigned short* verts, const int nverts,
const unsigned short* polys, const int npolys, const int nvp,
const float* bmin, const float* bmax, float cs, float ch,
const unsigned short* dmeshes, const float* dverts, const int ndverts,
const unsigned char* dtris, const int ndtris,
unsigned char** outData, int* outDataSize)
{
if (nvp > DT_STAT_VERTS_PER_POLYGON)
@@ -220,23 +222,52 @@ bool dtCreateNavMeshData(const unsigned short* verts, const int nverts,
return false;
if (!npolys)
return false;
if (!dmeshes || !dverts || ! dtris)
return false;
// Find unique detail vertices.
int uniqueDetailVerts = 0;
if (dmeshes)
{
for (int i = 0; i < npolys; ++i)
{
const unsigned short* p = &polys[i*nvp*2];
int ndv = dmeshes[i*4+1];
int nv = 0;
for (int j = 0; j < nvp; ++j)
{
if (p[j] == 0xffff) break;
nv++;
}
ndv -= nv;
uniqueDetailVerts += ndv;
}
}
// Calculate data size
const int headerSize = sizeof(dtStatNavMeshHeader);
const int vertsSize = sizeof(float)*3*nverts;
const int polysSize = sizeof(dtStatPoly)*npolys;
const int nodesSize = sizeof(dtStatBVNode)*npolys*2;
const int detailMeshesSize = sizeof(dtStatPolyDetail)*npolys;
const int detailVertsSize = sizeof(float)*3*uniqueDetailVerts;
const int detailTrisSize = sizeof(unsigned char)*4*ndtris;
const int dataSize = headerSize + vertsSize + polysSize + nodesSize;
const int dataSize = headerSize + vertsSize + polysSize + nodesSize +
detailMeshesSize + detailVertsSize + detailTrisSize;
unsigned char* data = new unsigned char[dataSize];
if (!data)
return false;
memset(data, 0, dataSize);
dtStatNavMeshHeader* header = (dtStatNavMeshHeader*)(data);
float* navVerts = (float*)(data + headerSize);
dtStatPoly* navPolys = (dtStatPoly*)(data + headerSize + vertsSize);
dtStatBVNode* nodes = (dtStatBVNode*)(data + headerSize + vertsSize + polysSize);
unsigned char* d = data;
dtStatNavMeshHeader* header = (dtStatNavMeshHeader*)d; d += headerSize;
float* navVerts = (float*)d; d += vertsSize;
dtStatPoly* navPolys = (dtStatPoly*)d; d += polysSize;
dtStatBVNode* navNodes = (dtStatBVNode*)d; d += nodesSize;
dtStatPolyDetail* navDMeshes = (dtStatPolyDetail*)d; d += detailMeshesSize;
float* navDVerts = (float*)d; d += detailVertsSize;
unsigned char* navDTris = (unsigned char*)d; d += detailTrisSize;
// Store header
header->magic = DT_STAT_NAVMESH_MAGIC;
@@ -250,6 +281,9 @@ bool dtCreateNavMeshData(const unsigned short* verts, const int nverts,
header->bmax[0] = bmax[0];
header->bmax[1] = bmax[1];
header->bmax[2] = bmax[2];
header->ndmeshes = dmeshes ? npolys : 0;
header->ndverts = dmeshes ? uniqueDetailVerts : 0;
header->ndtris = dmeshes ? ndtris : 0;
// Store vertices
for (int i = 0; i < nverts; ++i)
@@ -278,7 +312,32 @@ bool dtCreateNavMeshData(const unsigned short* verts, const int nverts,
}
header->nnodes = createBVTree(verts, nverts, polys, npolys, nvp,
cs, ch, npolys*2, nodes);
cs, ch, npolys*2, navNodes);
// Store detail meshes and vertices.
// The nav polygon vertices are stored as the first vertices on each mesh.
// We compress the mesh data by skipping them and using the navmesh coordinates.
unsigned short vbase = 0;
for (int i = 0; i < npolys; ++i)
{
dtStatPolyDetail& dtl = navDMeshes[i];
const int vb = dmeshes[i*4+0];
const int ndv = dmeshes[i*4+1];
const int nv = navPolys[i].nv;
dtl.vbase = vbase;
dtl.nverts = ndv-nv;
dtl.tbase = dmeshes[i*4+2];
dtl.ntris = dmeshes[i*4+3];
// Copy vertices except the first 'nv' verts which are equal to nav poly verts.
if (ndv-nv)
{
memcpy(&navDVerts[vbase*3], &dverts[(vb+nv)*3], sizeof(float)*3*(ndv-nv));
vbase += ndv-nv;
}
}
// Store triangles.
memcpy(navDTris, dtris, sizeof(unsigned char)*4*ndtris);
*outData = data;
*outDataSize = dataSize;

View File

@@ -363,9 +363,18 @@ bool dtTiledNavMesh::addTileAt(int x, int y, unsigned char* data, int dataSize,
const int headerSize = sizeof(dtTileHeader);
const int vertsSize = sizeof(float)*3*header->nverts;
const int polysSize = sizeof(dtTilePoly)*header->npolys;
header->verts = (float*)(data + headerSize);
header->polys = (dtTilePoly*)(data + headerSize + vertsSize);
header->links = (dtTileLink*)(data + headerSize + vertsSize + polysSize);
const int linksSize = sizeof(dtTileLink)*(header->maxlinks);
const int detailMeshesSize = sizeof(dtTilePolyDetail)*header->ndmeshes;
const int detailVertsSize = sizeof(float)*3*header->ndverts;
const int detailTrisSize = sizeof(unsigned char)*4*header->ndtris;
unsigned char* d = data + headerSize;
header->verts = (float*)d; d += vertsSize;
header->polys = (dtTilePoly*)d; d += polysSize;
header->links = (dtTileLink*)d; d += linksSize;
header->dmeshes = (dtTilePolyDetail*)d; d += detailMeshesSize;
header->dverts = (float*)d; d += detailVertsSize;
header->dtris = (unsigned char*)d; d += detailTrisSize;
// Init tile.
tile->header = header;
@@ -492,21 +501,27 @@ bool dtTiledNavMesh::closestPointToPoly(dtTilePolyRef ref, const float* pos, flo
dtDecodeTileId(ref, salt, it, ip);
if (it >= DT_MAX_TILES) return false;
if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return false;
const dtTileHeader* h = m_tiles[it].header;
const dtTileHeader* header = m_tiles[it].header;
if (ip >= (unsigned int)h->npolys) return false;
const dtTilePoly* poly = &h->polys[ip];
if (ip >= (unsigned int)header->npolys) return false;
const dtTilePoly* poly = &header->polys[ip];
float closestDistSqr = FLT_MAX;
const dtTilePolyDetail* pd = &header->dmeshes[ip];
for (int i = 2; i < (int)poly->nv; ++i)
for (int j = 0; j < pd->ntris; ++j)
{
const float* v0 = &h->verts[poly->v[0]*3];
const float* v1 = &h->verts[poly->v[i-1]*3];
const float* v2 = &h->verts[poly->v[i]*3];
const unsigned char* t = &header->dtris[(pd->tbase+j)*4];
const float* v[3];
for (int k = 0; k < 3; ++k)
{
if (t[k] < poly->nv)
v[k] = &header->verts[poly->v[t[k]]*3];
else
v[k] = &header->dverts[(pd->vbase+(t[k]-poly->nv))*3];
}
float pt[3];
closestPtPointTriangle(pt, pos, v0, v1, v2);
closestPtPointTriangle(pt, pos, v[0], v[1], v[2]);
float d = vdistSqr(pos, pt);
if (d < closestDistSqr)
{
@@ -518,6 +533,42 @@ bool dtTiledNavMesh::closestPointToPoly(dtTilePolyRef ref, const float* pos, flo
return true;
}
bool dtTiledNavMesh::getPolyHeight(dtTilePolyRef ref, const float* pos, float* height) const
{
unsigned int salt, it, ip;
dtDecodeTileId(ref, salt, it, ip);
if (it >= DT_MAX_TILES) return false;
if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return false;
const dtTileHeader* header = m_tiles[it].header;
if (ip >= (unsigned int)header->npolys) return false;
const dtTilePoly* poly = &header->polys[ip];
const dtTilePolyDetail* pd = &header->dmeshes[ip];
for (int j = 0; j < pd->ntris; ++j)
{
const unsigned char* t = &header->dtris[(pd->tbase+j)*4];
const float* v[3];
for (int k = 0; k < 3; ++k)
{
if (t[k] < poly->nv)
v[k] = &header->verts[poly->v[t[k]]*3];
else
v[k] = &header->dverts[(pd->vbase+(t[k]-poly->nv))*3];
}
float h;
if (closestHeightPointTriangle(pos, v[0], v[1], v[2], h))
{
if (height)
*height = h;
return true;
}
}
return false;
}
dtTilePolyRef dtTiledNavMesh::findNearestPoly(const float* center, const float* extents)
{
// Get nearby polygons from proximity grid.
@@ -666,12 +717,12 @@ int dtTiledNavMesh::findPath(dtTilePolyRef startRef, dtTilePolyRef endRef,
unsigned int salt, it, ip;
dtDecodeTileId(bestNode->id, salt, it, ip);
// The API input has been cheked already, skip checking internal data.
const dtTileHeader* h = m_tiles[it].header;
const dtTilePoly* poly = &h->polys[ip];
const dtTileHeader* header = m_tiles[it].header;
const dtTilePoly* poly = &header->polys[ip];
for (int i = 0; i < poly->nlinks; ++i)
{
dtTilePolyRef neighbour = h->links[poly->links+i].ref;
dtTilePolyRef neighbour = header->links[poly->links+i].ref;
if (neighbour)
{
// Skip parent node.
@@ -992,14 +1043,14 @@ int dtTiledNavMesh::raycast(dtTilePolyRef centerRef, const float* startPos, cons
// The API input has been cheked already, skip checking internal data.
unsigned int salt, it, ip;
dtDecodeTileId(curRef, salt, it, ip);
const dtTileHeader* h = m_tiles[it].header;
const dtTilePoly* poly = &h->polys[ip];
const dtTileHeader* header = m_tiles[it].header;
const dtTilePoly* poly = &header->polys[ip];
// Collect vertices.
int nv = 0;
for (int i = 0; i < (int)poly->nv; ++i)
{
vcopy(&verts[nv*3], &h->verts[poly->v[i]*3]);
vcopy(&verts[nv*3], &header->verts[poly->v[i]*3]);
nv++;
}
if (nv < 3)
@@ -1026,7 +1077,7 @@ int dtTiledNavMesh::raycast(dtTilePolyRef centerRef, const float* startPos, cons
dtTilePolyRef nextRef = 0;
for (int i = 0; i < poly->nlinks; ++i)
{
const dtTileLink* link = &h->links[poly->links+i];
const dtTileLink* link = &header->links[poly->links+i];
if ((int)link->e == segMax)
{
// If the link is internal, just return the ref.
@@ -1039,8 +1090,8 @@ int dtTiledNavMesh::raycast(dtTilePolyRef centerRef, const float* startPos, cons
// If the link is at tile boundary,
const int v0 = poly->v[link->e];
const int v1 = poly->v[(link->e+1) % poly->nv];
const float* left = &h->verts[v0*3];
const float* right = &h->verts[v1*3];
const float* left = &header->verts[v0*3];
const float* right = &header->verts[v1*3];
// Check that the intersection lies inside the link portal.
if (link->side == 0 || link->side == 2)
@@ -1132,12 +1183,12 @@ int dtTiledNavMesh::findPolysAround(dtTilePolyRef centerRef, const float* center
unsigned int salt, it, ip;
dtDecodeTileId(bestNode->id, salt, it, ip);
// The API input has been cheked already, skip checking internal data.
const dtTileHeader* h = m_tiles[it].header;
const dtTilePoly* poly = &h->polys[ip];
const dtTileHeader* header = m_tiles[it].header;
const dtTilePoly* poly = &header->polys[ip];
for (int i = 0; i < poly->nlinks; ++i)
{
const dtTileLink* link = &h->links[poly->links+i];
const dtTileLink* link = &header->links[poly->links+i];
dtTilePolyRef neighbour = link->ref;
if (neighbour)
{
@@ -1146,8 +1197,8 @@ int dtTiledNavMesh::findPolysAround(dtTilePolyRef centerRef, const float* center
continue;
// Calc distance to the edge.
const float* va = &h->verts[poly->v[link->e]*3];
const float* vb = &h->verts[poly->v[(link->e+1)%poly->nv]*3];
const float* va = &header->verts[poly->v[link->e]*3];
const float* vb = &header->verts[poly->v[(link->e+1)%poly->nv]*3];
float tseg;
float distSqr = distancePtSegSqr2D(centerPos, va, vb, tseg);
@@ -1235,8 +1286,8 @@ float dtTiledNavMesh::findDistanceToWall(dtTilePolyRef centerRef, const float* c
unsigned int salt, it, ip;
dtDecodeTileId(bestNode->id, salt, it, ip);
// The API input has been cheked already, skip checking internal data.
const dtTileHeader* h = m_tiles[it].header;
const dtTilePoly* poly = &h->polys[ip];
const dtTileHeader* header = m_tiles[it].header;
const dtTilePoly* poly = &header->polys[ip];
// Hit test walls.
for (int i = 0, j = (int)poly->nv-1; i < (int)poly->nv; j = i++)
@@ -1248,7 +1299,7 @@ float dtTiledNavMesh::findDistanceToWall(dtTilePolyRef centerRef, const float* c
bool solid = true;
for (int i = 0; i < poly->nlinks; ++i)
{
const dtTileLink* link = &h->links[poly->links+i];
const dtTileLink* link = &header->links[poly->links+i];
if (link->e == j && link->ref != 0)
{
solid = false;
@@ -1264,8 +1315,8 @@ float dtTiledNavMesh::findDistanceToWall(dtTilePolyRef centerRef, const float* c
}
// Calc distance to the edge.
const float* vj = &h->verts[poly->v[j]*3];
const float* vi = &h->verts[poly->v[i]*3];
const float* vj = &header->verts[poly->v[j]*3];
const float* vi = &header->verts[poly->v[i]*3];
float tseg;
float distSqr = distancePtSegSqr2D(centerPos, vj, vi, tseg);
@@ -1283,7 +1334,7 @@ float dtTiledNavMesh::findDistanceToWall(dtTilePolyRef centerRef, const float* c
for (int i = 0; i < poly->nlinks; ++i)
{
const dtTileLink* link = &h->links[poly->links+i];
const dtTileLink* link = &header->links[poly->links+i];
dtTilePolyRef neighbour = link->ref;
if (neighbour)
{
@@ -1292,8 +1343,8 @@ float dtTiledNavMesh::findDistanceToWall(dtTilePolyRef centerRef, const float* c
continue;
// Calc distance to the edge.
const float* va = &h->verts[poly->v[link->e]*3];
const float* vb = &h->verts[poly->v[(link->e+1)%poly->nv]*3];
const float* va = &header->verts[poly->v[link->e]*3];
const float* vb = &header->verts[poly->v[(link->e+1)%poly->nv]*3];
float tseg;
float distSqr = distancePtSegSqr2D(centerPos, va, vb, tseg);

View File

@@ -25,6 +25,8 @@
bool dtCreateNavMeshTileData(const unsigned short* verts, const int nverts,
const unsigned short* polys, const int npolys, const int nvp,
const unsigned short* dmeshes, const float* dverts, const int ndverts,
const unsigned char* dtris, const int ndtris,
const float* bmin, const float* bmax, float cs, float ch, int tileSize, int walkableClimb,
unsigned char** outData, int* outDataSize)
{
@@ -37,6 +39,8 @@ bool dtCreateNavMeshTileData(const unsigned short* verts, const int nverts,
return false;
if (!npolys)
return false;
if (!dmeshes || !dverts || ! dtris)
return false;
// Find portal edges which are at tile borders.
int nedges = 0;
@@ -64,34 +68,70 @@ bool dtCreateNavMeshTileData(const unsigned short* verts, const int nverts,
nportals++; // z-
}
}
const int maxLinks = nedges + nportals*2;
// Find unique detail vertices.
int uniqueDetailVerts = 0;
if (dmeshes)
{
for (int i = 0; i < npolys; ++i)
{
const unsigned short* p = &polys[i*nvp*2];
int ndv = dmeshes[i*4+1];
int nv = 0;
for (int j = 0; j < nvp; ++j)
{
if (p[j] == 0xffff) break;
nv++;
}
ndv -= nv;
uniqueDetailVerts += ndv;
}
}
// Calculate data size
const int headerSize = sizeof(dtTileHeader);
const int vertsSize = sizeof(float)*3*nverts;
const int polysSize = sizeof(dtTilePoly)*npolys;
const int linksSize = sizeof(dtTileLink)*(nedges + nportals*2);
const int dataSize = headerSize + vertsSize + polysSize + linksSize;
const int linksSize = sizeof(dtTileLink)*maxLinks;
const int detailMeshesSize = sizeof(dtTilePolyDetail)*npolys;
const int detailVertsSize = sizeof(float)*3*uniqueDetailVerts;
const int detailTrisSize = sizeof(unsigned char)*4*ndtris;
const int dataSize = headerSize + vertsSize + polysSize + linksSize +
detailMeshesSize + detailVertsSize + detailTrisSize;
unsigned char* data = new unsigned char[dataSize];
if (!data)
return false;
memset(data, 0, dataSize);
dtTileHeader* header = (dtTileHeader*)(data);
float* navVerts = (float*)(data + headerSize);
dtTilePoly* navPolys = (dtTilePoly*)(data + headerSize + vertsSize);
unsigned char* d = data;
dtTileHeader* header = (dtTileHeader*)d; d += headerSize;
float* navVerts = (float*)d; d += vertsSize;
dtTilePoly* navPolys = (dtTilePoly*)d; d += polysSize;
d += linksSize;
dtTilePolyDetail* navDMeshes = (dtTilePolyDetail*)d; d += detailMeshesSize;
float* navDVerts = (float*)d; d += detailVertsSize;
unsigned char* navDTris = (unsigned char*)d; d += detailTrisSize;
// Store header
header->magic = DT_TILE_NAVMESH_MAGIC;
header->version = DT_TILE_NAVMESH_VERSION;
header->npolys = npolys;
header->nverts = nverts;
header->maxlinks = nedges + nportals*2;
header->maxlinks = maxLinks;
header->bmin[0] = bmin[0];
header->bmin[1] = bmin[1];
header->bmin[2] = bmin[2];
header->bmax[0] = bmax[0];
header->bmax[1] = bmax[1];
header->bmax[2] = bmax[2];
header->ndmeshes = npolys;
header->ndverts = uniqueDetailVerts;
header->ndtris = ndtris;
// Store vertices
for (int i = 0; i < nverts; ++i)
@@ -141,6 +181,30 @@ bool dtCreateNavMeshTileData(const unsigned short* verts, const int nverts,
poly->n[j] = 0x8000 | 3;
}
}
// Store detail meshes and vertices.
// The nav polygon vertices are stored as the first vertices on each mesh.
// We compress the mesh data by skipping them and using the navmesh coordinates.
unsigned short vbase = 0;
for (int i = 0; i < npolys; ++i)
{
dtTilePolyDetail& dtl = navDMeshes[i];
const int vb = dmeshes[i*4+0];
const int ndv = dmeshes[i*4+1];
const int nv = navPolys[i].nv;
dtl.vbase = vbase;
dtl.nverts = ndv-nv;
dtl.tbase = dmeshes[i*4+2];
dtl.ntris = dmeshes[i*4+3];
// Copy vertices except the first 'nv' verts which are equal to nav poly verts.
if (ndv-nv)
{
memcpy(&navDVerts[vbase*3], &dverts[(vb+nv)*3], sizeof(float)*3*(ndv-nv));
vbase += ndv-nv;
}
}
// Store triangles.
memcpy(navDTris, dtris, sizeof(unsigned char)*4*ndtris);
*outData = data;
*outDataSize = dataSize;

View File

@@ -19,22 +19,26 @@
#ifndef RECAST_H
#define RECAST_H
// The units of the parameters are specified in parenthesis as follows:
// (vx) voxels, (wu) world units
struct rcConfig
{
int width, height; // Dimensions of the rasterized heighfield
int tileSize; // Size if a tile.
int borderSize; // Non-navigable Border around the heightfield.
float cs, ch; // Grid cell size and height.
float bmin[3], bmax[3]; // Grid bounds.
int width, height; // Dimensions of the rasterized heighfield (vx)
int tileSize; // Width and Height of a tile (vx)
int borderSize; // Non-navigable Border around the heightfield (vx)
float cs, ch; // Grid cell size and height (wu)
float bmin[3], bmax[3]; // Grid bounds (wu)
float walkableSlopeAngle; // Maximum walkble slope angle in degrees.
int walkableHeight; // Minimum height where the agent can still walk.
int walkableClimb; // Maximum height between grid cells the agent can climb.
int walkableRadius; // Radius of the agent in cells.
int maxEdgeLen; // Maximum contour edge length in cells.
float maxSimplificationError; // Maximum distance error from contour to cells.
int minRegionSize; // Minimum regions size. Smaller regions will be deleted.
int mergeRegionSize; // Minimum regions size. Smaller regions will be merged.
int maxVertsPerPoly; // Max number of vertices per polygon.
int walkableHeight; // Minimum height where the agent can still walk (vx)
int walkableClimb; // Maximum height between grid cells the agent can climb (vx)
int walkableRadius; // Radius of the agent in cells (vx)
int maxEdgeLen; // Maximum contour edge length (vx)
float maxSimplificationError; // Maximum distance error from contour to cells (vx)
int minRegionSize; // Minimum regions size. Smaller regions will be deleted (vx)
int mergeRegionSize; // Minimum regions size. Smaller regions will be merged (vx)
int maxVertsPerPoly; // Max number of vertices per polygon
float detailSampleDist; // Detail mesh sample spacing.
float detailSampleMaxError; // Detail mesh simplification max sample error.
};
// Heightfield span.
@@ -126,8 +130,10 @@ struct rcContourSet
{
inline rcContourSet() : conts(0), nconts(0) {}
inline ~rcContourSet() { delete [] conts; }
rcContour* conts; // Pointer to all contours.
int nconts; // Number of contours.
rcContour* conts; // Pointer to all contours.
int nconts; // Number of contours.
float bmin[3], bmax[3]; // Bounding box of the heightfield.
float cs, ch; // Cell size and height.
};
// Polymesh store a connected mesh of polygons.
@@ -138,15 +144,16 @@ struct rcContourSet
// are set os 0xffff. If an polygon edge does not have a neighbour
// the neighbour index is set to 0xffff.
// Vertices can be transformed into world space as follows:
// x = bmin[0] + verts[i*3+0]*cs;
// y = bmin[1] + verts[i*3+1]*ch;
// z = bmin[2] + verts[i*3+2]*cs;
// x = bmin[0] + verts[i*3+0]*cs;
// y = bmin[1] + verts[i*3+1]*ch;
// z = bmin[2] + verts[i*3+2]*cs;
struct rcPolyMesh
{
inline rcPolyMesh() : verts(0), polys(0), nverts(0), npolys(0), nvp(3) {}
inline ~rcPolyMesh() { delete [] verts; delete [] polys; }
inline rcPolyMesh() : verts(0), polys(0), regs(0), nverts(0), npolys(0), nvp(3) {}
inline ~rcPolyMesh() { delete [] verts; delete [] polys; delete [] regs; }
unsigned short* verts; // Vertices of the mesh, 3 elements per vertex.
unsigned short* polys; // Polygons of the mesh, nvp*2 elements per polygon.
unsigned short* regs; // Regions of the polygons.
int nverts; // Number of vertices.
int npolys; // Number of polygons.
int nvp; // Max number of vertices per polygon.
@@ -154,6 +161,35 @@ struct rcPolyMesh
float cs, ch; // Cell size and height.
};
// Detail mesh generated from a rcPolyMesh.
// Each submesh represents a polygon in the polymesh and they are stored in
// excatly same order. Each submesh is described as 4 values:
// base vertex, vertex count, base triangle, triangle count. That is,
// const unsigned char* t = &dtl.tris[(tbase+i)*3]; and
// const float* v = &dtl.verts[(vbase+t[j])*3];
// If the input polygon has 'n' vertices, those vertices are first in the
// submesh vertex list. This allows to compres the mesh by not storing the
// first vertices and using the polymesh vertices instead.
struct rcPolyMeshDetail
{
inline rcPolyMeshDetail() :
meshes(0), verts(0), tris(0),
nmeshes(0), nverts(0), ntris(0) {}
inline ~rcPolyMeshDetail()
{
delete [] meshes; delete [] verts; delete [] tris;
}
unsigned short* meshes; // Pointer to all mesh data.
float* verts; // Pointer to all vertex data.
unsigned char* tris; // Pointer to all triangle data.
int nmeshes; // Number of meshes.
int nverts; // Number of total vertices.
int ntris; // Number of triangles.
};
// Simple dynamic array ints.
class rcIntArray
{
@@ -425,39 +461,32 @@ bool rcBuildRegions(rcCompactHeightfield& chf,
// cset - (out) Resulting contour set.
// Returns false if operation ran out of memory.
bool rcBuildContours(rcCompactHeightfield& chf,
float maxError, int maxEdgeLen,
const float maxError, const int maxEdgeLen,
rcContourSet& cset);
// Ensures that connected contour sets A and B share the same vertices at the shared edges.
// Params:
// cseta - (in) contour set A.
// csetb - (in) contour set B.
// walkableHeight - (in) minimum height where the agent can still walk
// edgex, edgez - (in) defines the planes where the edges can be merged
// orig - (in) origin of the contour set A.
// cs - (in) grid cell size
// ch - (in) grid cell height
bool rcFixupAdjacentContours(rcContourSet* cseta, rcContourSet* csetb,
const int walkableClimb, const int edgex, const int edgez);
// Translates the cordinates of the contour set.
// Params:
// cset - (in) contour set to translate.
// dx - (in) delta X.
// dy - (in) delta Y.
// dz - (in) delta Z.
void rcTranslateContours(rcContourSet* cset, int dx, int dy, int dz);
// Builds connected convex polygon mesh from contour polygons.
// Params:
// cset - (in) contour set.
// nvp - (in) maximum number of vertices per polygon.
// mesh - (out) poly mesh.
// nvp - (int) maximum number of vertices per polygon.
// Returns false if operation ran out of memory.
bool rcBuildPolyMesh(rcContourSet& cset,
const float* bmin, const float* bmax,
const float cs, const float ch, int nvp,
rcPolyMesh& mesh);
bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh);
bool rcMergePolyMeshes(rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh);
// Builds detail triangle mesh for each polygon in the poly mesh.
// Params:
// mesh - (in) poly mesh to detail.
// chf - (in) compacy height field, used to query height for new vertices.
// sampleDist - (in) spacing between height samples used to generate more detail into mesh.
// sampleMaxError - (in) maximum allowed distance between simplified detail mesh and height sample.
// pmdtl - (out) detail mesh.
// Returns false if operation ran out of memory.
bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& chf,
const float sampleDist, const float sampleMaxError,
rcPolyMeshDetail& dmesh);
bool rcMergePolyMeshDetails(rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh);
#endif // RECAST_H

View File

@@ -44,10 +44,11 @@ void rcDebugDrawCompactHeightfieldSolid(const struct rcCompactHeightfield& chf);
void rcDebugDrawCompactHeightfieldRegions(const struct rcCompactHeightfield& chf);
void rcDebugDrawCompactHeightfieldDistance(const struct rcCompactHeightfield& chf);
void rcDebugDrawRegionConnections(const struct rcContourSet& cset, const float* orig, float cs, float ch, const float alpha = 1.0f);
void rcDebugDrawRawContours(const struct rcContourSet& cset, const float* orig, float cs, float ch, const float alpha = 1.0f);
void rcDebugDrawContours(const struct rcContourSet& cset, const float* orig, float cs, float ch);
void rcDebugDrawRegionConnections(const struct rcContourSet& cset, const float alpha = 1.0f);
void rcDebugDrawRawContours(const struct rcContourSet& cset, const float alpha = 1.0f);
void rcDebugDrawContours(const struct rcContourSet& cset, const float alpha = 1.0f);
void rcDebugDrawPolyMesh(const struct rcPolyMesh& mesh);
void rcDebugDrawPolyMeshDetail(const struct rcPolyMeshDetail& dmesh);
void rcDebugDrawCylinderWire(float minx, float miny, float minz, float maxx, float maxy, float maxz, const float* col);
void rcDebugDrawBoxWire(float minx, float miny, float minz, float maxx, float maxy, float maxz, const float* col);

View File

@@ -66,7 +66,9 @@ struct rcBuildTimes
int buildRegionsExp;
int buildRegionsFlood;
int buildRegionsFilter;
int fixupContours;
int buildDetailMesh;
int mergePolyMesh;
int mergePolyMeshDetail;
};
void rcSetLog(rcLog* log);

View File

@@ -40,7 +40,7 @@ void rcIntArray::resize(int n)
}
m_size = n;
}
void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax)
{
// Calculate bounding box.
@@ -77,20 +77,6 @@ bool rcCreateHeightfield(rcHeightfield& hf, int width, int height,
return true;
}
/*void rcMarkWalkableTriangles(const float walkableSlopeAngle,
const int* tris, const float* norms, int nt,
unsigned char* flags)
{
const float walkableThr = cosf(walkableSlopeAngle/180.0f*(float)M_PI);
for (int i = 0; i < nt; ++i)
{
// Check if the face is walkable.
if (norms[i*3+1] > walkableThr)
flags[i] |= RC_WALKABLE;
}
}*/
static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* norm)
{
float e0[3], e1[3];
@@ -284,4 +270,3 @@ static int getCompactHeightFieldMemoryusage(const rcCompactHeightfield& chf)
size += sizeof(rcCompactCell) * chf.width * chf.height;
return size;
}

View File

@@ -26,11 +26,17 @@
static int getCornerHeight(int x, int y, int i, int dir,
const rcCompactHeightfield& chf)
const rcCompactHeightfield& chf,
bool& isBorderVertex)
{
const rcCompactSpan& s = chf.spans[i];
int ch = (int)s.y;
int dirp = (dir+1) & 0x3;
unsigned short regs[4] = {0,0,0,0};
regs[0] = s.reg;
if (rcGetCon(s, dir) != 0xf)
{
const int ax = x + rcGetDirOffsetX(dir);
@@ -38,6 +44,7 @@ static int getCornerHeight(int x, int y, int i, int dir,
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
const rcCompactSpan& as = chf.spans[ai];
ch = rcMax(ch, (int)as.y);
regs[1] = as.reg;
if (rcGetCon(as, dirp) != 0xf)
{
const int ax2 = ax + rcGetDirOffsetX(dirp);
@@ -45,6 +52,7 @@ static int getCornerHeight(int x, int y, int i, int dir,
const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dirp);
const rcCompactSpan& as2 = chf.spans[ai2];
ch = rcMax(ch, (int)as2.y);
regs[2] = as2.reg;
}
}
if (rcGetCon(s, dirp) != 0xf)
@@ -54,6 +62,7 @@ static int getCornerHeight(int x, int y, int i, int dir,
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dirp);
const rcCompactSpan& as = chf.spans[ai];
ch = rcMax(ch, (int)as.y);
regs[3] = as.reg;
if (rcGetCon(as, dir) != 0xf)
{
const int ax2 = ax + rcGetDirOffsetX(dir);
@@ -61,6 +70,27 @@ static int getCornerHeight(int x, int y, int i, int dir,
const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dir);
const rcCompactSpan& as2 = chf.spans[ai2];
ch = rcMax(ch, (int)as2.y);
regs[2] = as2.reg;
}
}
// Check if the vertex is special edge vertex, these vertices will be removed later.
for (int j = 0; j < 4; ++j)
{
const int a = j;
const int b = (j+1) & 0x3;
const int c = (j+2) & 0x3;
const int d = (j+3) & 0x3;
// The vertex is a border vertex there are two same exterior cells in a row,
// followed by two interior cells and none of the regions are out of bounds.
const bool twoSameExts = (regs[a] & regs[b] & 0x8000) != 0 && regs[a] == regs[b];
const bool twoInts = ((regs[c] | regs[d]) & 0x8000) == 0;
const bool noZeros = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0;
if (twoSameExts && twoInts && noZeros)
{
isBorderVertex = true;
break;
}
}
@@ -85,8 +115,9 @@ static void walkContour(int x, int y, int i,
if (flags[i] & (1 << dir))
{
// Choose the edge corner
bool isBorderVertex = false;
int px = x;
int py = getCornerHeight(x, y, i, dir, chf);
int py = getCornerHeight(x, y, i, dir, chf, isBorderVertex);
int pz = y;
switch(dir)
{
@@ -105,6 +136,12 @@ static void walkContour(int x, int y, int i,
r = (int)as.reg;
}
/* if (r & 0x8000)
printf("0x8000\n");*/
if (isBorderVertex)
r |= 0x10000;
points.push(px);
points.push(py);
points.push(pz);
@@ -192,7 +229,7 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma
bool noConnections = true;
for (int i = 0; i < points.size(); i += 4)
{
if (points[i+3] != 0)
if ((points[i+3] & 0xffff) != 0)
{
noConnections = false;
break;
@@ -249,7 +286,7 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma
for (int i = 0, ni = points.size()/4; i < ni; ++i)
{
int ii = (i+1) % ni;
if (points[i*4+3] != points[ii*4+3])
if ((points[i*4+3] & 0xffff) != (points[ii*4+3] & 0xffff))
{
simplified.push(points[i*4+0]);
simplified.push(points[i*4+1]);
@@ -282,7 +319,7 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma
int ci = (ai+1) % pn;
// Tesselate only outer edges.
if (points[ci*4+3] == 0)
if ((points[ci*4+3] & 0xffff) == 0)
{
while (ci != bi)
{
@@ -344,7 +381,7 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma
int ci = (ai+1) % pn;
// Tesselate only outer edges.
if (points[ci*4+3] == 0)
if ((points[ci*4+3] & 0xffff) == 0)
{
int dx = bx - ax;
int dz = bz - az;
@@ -384,8 +421,11 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma
for (int i = 0; i < simplified.size()/4; ++i)
{
int ai = (simplified[i*4+3]+1) % pn;
simplified[i*4+3] = points[ai*4+3];
// The edge vertex flag is take from the current raw point,
// and the neighbour region is take from the next raw point.
const int ai = (simplified[i*4+3]+1) % pn;
const int bi = simplified[i*4+3];
simplified[i*4+3] = (points[ai*4+3] & 0xffff) | (points[bi*4+3] & 0x10000);
}
}
@@ -497,7 +537,7 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
}
bool rcBuildContours(rcCompactHeightfield& chf,
float maxError, int maxEdgeLen,
const float maxError, const int maxEdgeLen,
rcContourSet& cset)
{
const int w = chf.width;
@@ -505,6 +545,11 @@ bool rcBuildContours(rcCompactHeightfield& chf,
rcTimeVal startTime = rcGetPerformanceTimer();
vcopy(cset.bmin, chf.bmin);
vcopy(cset.bmax, chf.bmax);
cset.cs = chf.cs;
cset.ch = chf.ch;
const int maxContours = chf.maxRegions*2;
cset.conts = new rcContour[maxContours];
if (!cset.conts)
@@ -520,6 +565,7 @@ bool rcBuildContours(rcCompactHeightfield& chf,
}
rcTimeVal traceStartTime = rcGetPerformanceTimer();
// Mark boundaries.
for (int y = 0; y < h; ++y)
@@ -689,167 +735,3 @@ bool rcBuildContours(rcCompactHeightfield& chf,
return true;
}
static bool insertPoint(rcContour* c, int idx, const int* v)
{
int* newVerts = new int[(c->nverts+1)*4];
if (!newVerts)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "insertPoint: Out of memory 'newVerts'.");
return false;
}
if (idx > 0)
memcpy(newVerts, c->verts, sizeof(int)*4*idx);
newVerts[idx*4+0] = v[0];
newVerts[idx*4+1] = v[1];
newVerts[idx*4+2] = v[2];
newVerts[idx*4+3] = 0;
if (c->nverts - idx > 0)
memcpy(&newVerts[(idx+1)*4], &c->verts[idx*4], sizeof(int)*4*(c->nverts - idx));
delete [] c->verts;
c->verts = newVerts;
c->nverts++;
return true;
}
static bool conformVertex(rcContourSet* cset, const int* v,
const int pminy, const int pmaxy,
const int nminy, const int nmaxy,
const int walkableClimb)
{
for (int i = 0; i < cset->nconts; ++i)
{
rcContour* c = &cset->conts[i];
for (int j = 0; j < c->nverts; ++j)
{
const int k = (j+1) % c->nverts;
const int* vj = &c->verts[j*4];
const int* vk = &c->verts[k*4];
const int miny = rcMin(vj[1], vk[1]);
const int maxy = rcMax(vj[1], vk[1]);
// Is edge within y-range.
if ((miny > pmaxy || maxy < pminy) &&
(miny > nmaxy || maxy < nminy))
continue;
if (vj[0] == vk[0] && vj[0] == v[0])
{
// The segment is x edge.
const int minz = rcMin(vj[2], vk[2]);
const int maxz = rcMax(vj[2], vk[2]);
if (v[2] > minz && v[2] < maxz)
{
return insertPoint(c, j+1, v);
}
}
else if (vj[2] == vk[2] && vj[2] == v[2])
{
// The segment is z edge.
const int minx = rcMin(vj[0], vk[0]);
const int maxx = rcMax(vj[0], vk[0]);
if (v[0] > minx && v[0] < maxx)
{
return insertPoint(c, j+1, v);
}
}
}
}
return true;
}
bool rcFixupAdjacentContours(rcContourSet* cseta, rcContourSet* csetb,
const int walkableClimb, const int edgex, const int edgez)
{
if (!cseta || !csetb)
return true;
rcTimeVal startTime = rcGetPerformanceTimer();
for (int i = 0; i < cseta->nconts; ++i)
{
const rcContour& c = cseta->conts[i];
for (int j = 0; j < c.nverts; ++j)
{
const int* v = &c.verts[j*4];
const int* pv = &c.verts[((j+c.nverts-1)%c.nverts)*4];
const int* nv = &c.verts[((j+1)%c.nverts)*4];
// If the vertex is at the tile edge, make sure it also exists in
// the neighbour contour set.
if (v[0] == edgex || v[2] == edgez)
{
const int pminy = rcMin(v[1], pv[1]);
const int pmaxy = rcMax(v[1], pv[1]);
const int nminy = rcMin(v[1], nv[1]);
const int nmaxy = rcMax(v[1], nv[1]);
if (!conformVertex(csetb, v, pminy, pmaxy, nminy, nmaxy, walkableClimb))
return false;
}
}
}
for (int i = 0; i < csetb->nconts; ++i)
{
const rcContour& c = csetb->conts[i];
for (int j = 0; j < c.nverts; ++j)
{
const int* v = &c.verts[j*4];
const int* pv = &c.verts[((j+c.nverts-1)%c.nverts)*4];
const int* nv = &c.verts[((j+1)%c.nverts)*4];
// If the vertex is at the tile edge, make sure it also exists in
// the neighbour contour set.
if (v[0] == edgex || v[2] == edgez)
{
const int pminy = rcMin(v[1], pv[1]);
const int pmaxy = rcMax(v[1], pv[1]);
const int nminy = rcMin(v[1], nv[1]);
const int nmaxy = rcMax(v[1], nv[1]);
if (!conformVertex(cseta, v, pminy, pmaxy, nminy, nmaxy, walkableClimb))
return false;
}
}
}
rcTimeVal endTime = rcGetPerformanceTimer();
if (rcGetBuildTimes())
rcGetBuildTimes()->fixupContours += rcGetDeltaTimeUsec(startTime, endTime);
return true;
}
void rcTranslateContours(rcContourSet* cset, int dx, int dy, int dz)
{
if (!cset) return;
for (int i = 0; i < cset->nconts; ++i)
{
rcContour& cont = cset->conts[i];
for (int i = 0; i < cont.nverts; ++i)
{
int* v = &cont.verts[i*4];
v[0] += dx;
v[1] += dy;
v[2] += dz;
}
for (int i = 0; i < cont.nrverts; ++i)
{
int* v = &cont.rverts[i*4];
v[0] += dx;
v[1] += dy;
v[2] += dz;
}
}
}

View File

@@ -31,7 +31,7 @@ void rcDebugDrawMesh(const float* verts, int nverts,
glBegin(GL_TRIANGLES);
for (int i = 0; i < ntris*3; i += 3)
{
float a = (2+normals[i+0]+normals[i+1])/4;
float a = (2+normals[i+0]+normals[i+1])/4 * 0.5f;
if (flags && !flags[i/3])
glColor3f(a,a*0.3f,a*0.1f);
else
@@ -420,8 +420,12 @@ void rcDrawArc(const float* p0, const float* p1)
glEnd();
}
void rcDebugDrawRegionConnections(const rcContourSet& cset, const float* orig, float cs, float ch, const float alpha)
void rcDebugDrawRegionConnections(const rcContourSet& cset, const float alpha)
{
const float* orig = cset.bmin;
const float cs = cset.cs;
const float ch = cset.ch;
// Draw centers
float pos[3], pos2[3];
@@ -469,8 +473,11 @@ void rcDebugDrawRegionConnections(const rcContourSet& cset, const float* orig, f
glPointSize(1.0f);
}
void rcDebugDrawRawContours(const rcContourSet& cset, const float* orig, float cs, float ch, const float alpha)
void rcDebugDrawRawContours(const rcContourSet& cset, const float alpha)
{
const float* orig = cset.bmin;
const float cs = cset.cs;
const float ch = cset.ch;
float col[4] = { 1,1,1,alpha };
glLineWidth(2.0f);
glPointSize(2.0f);
@@ -499,8 +506,20 @@ void rcDebugDrawRawContours(const rcContourSet& cset, const float* orig, float c
for (int j = 0; j < c.nrverts; ++j)
{
const int* v = &c.rverts[j*4];
float off = 0;
if (v[3] & 0x10000)
{
glColor4ub(255,255,255,255);
off = ch*2;
}
else
{
glColor4fv(col);
}
float fx = orig[0] + v[0]*cs;
float fy = orig[1] + (v[1]+1+(i&1))*ch;
float fy = orig[1] + (v[1]+1+(i&1))*ch + off;
float fz = orig[2] + v[2]*cs;
glVertex3f(fx,fy,fz);
}
@@ -510,8 +529,11 @@ void rcDebugDrawRawContours(const rcContourSet& cset, const float* orig, float c
glPointSize(1.0f);
}
void rcDebugDrawContours(const rcContourSet& cset, const float* orig, float cs, float ch)
void rcDebugDrawContours(const rcContourSet& cset, const float alpha)
{
const float* orig = cset.bmin;
const float cs = cset.cs;
const float ch = cset.ch;
float col[4] = { 1,1,1,1 };
glLineWidth(2.5f);
glPointSize(3.0f);
@@ -540,8 +562,19 @@ void rcDebugDrawContours(const rcContourSet& cset, const float* orig, float cs,
for (int j = 0; j < c.nverts; ++j)
{
const int* v = &c.verts[j*4];
float off = 0;
if (v[3] & 0x10000)
{
glColor4ub(255,255,255,255);
off = ch*2;
}
else
{
glColor4fv(col);
}
float fx = orig[0] + v[0]*cs;
float fy = orig[1] + (v[1]+1+(i&1))*ch;
float fy = orig[1] + (v[1]+1+(i&1))*ch + off;
float fz = orig[2] + v[2]*cs;
glVertex3f(fx,fy,fz);
}
@@ -557,11 +590,13 @@ void rcDebugDrawPolyMesh(const struct rcPolyMesh& mesh)
const float cs = mesh.cs;
const float ch = mesh.ch;
const float* orig = mesh.bmin;
glColor4ub(0,196,255,64);
float col[4] = {1,1,1,0.5f};
glBegin(GL_TRIANGLES);
for (int i = 0; i < mesh.npolys; ++i)
{
const unsigned short* p = &mesh.polys[i*nvp*2];
intToCol(i, col);
glColor4fv(col);
unsigned short vi[3];
for (int j = 2; j < nvp; ++j)
{
@@ -652,5 +687,73 @@ void rcDebugDrawPolyMesh(const struct rcPolyMesh& mesh)
glVertex3f(x, y, z);
}
glEnd();
glPointSize(1.0f);
glPointSize(1.0f);
}
void rcDebugDrawPolyMeshDetail(const struct rcPolyMeshDetail& dmesh)
{
float col[4] = {1,1,1,0.75f};
for (int i = 0; i < dmesh.nmeshes; ++i)
{
const unsigned short* m = &dmesh.meshes[i*4];
const unsigned short bverts = m[0];
const unsigned short nverts = m[1];
const unsigned short btris = m[2];
const unsigned short ntris = m[3];
intToCol(i, col);
const float* verts = &dmesh.verts[bverts*3];
const unsigned char* tris = &dmesh.tris[btris*4];
glPointSize(3.0f);
glBegin(GL_POINTS);
for (int j = 0; j < nverts; ++j)
{
glColor4ub(0,0,0,64);
glVertex3fv(&verts[j*3]);
}
glEnd();
glPointSize(1.0f);
glBegin(GL_TRIANGLES);
glColor4fv(col);
for (int j = 0; j < ntris; ++j)
{
glVertex3fv(&verts[tris[j*4+0]*3]);
glVertex3fv(&verts[tris[j*4+1]*3]);
glVertex3fv(&verts[tris[j*4+2]*3]);
}
glEnd();
for (int j = 0; j < ntris; ++j)
{
glBegin(GL_LINES);
const unsigned char* t = &tris[j*4];
for (int k = 0, kp = 2; k < 3; kp=k++)
{
unsigned char ef = (t[3] >> (kp*2)) & 0x3;
if (ef == 0)
{
// Internal edge
if (t[kp] < t[k])
{
glColor4ub(255,255,255,32);
glVertex3fv(&verts[t[kp]*3]);
glVertex3fv(&verts[t[k]*3]);
}
}
else
{
// Ext edge
glColor4ub(0,0,0,128);
glVertex3fv(&verts[t[kp]*3]);
glVertex3fv(&verts[t[k]*3]);
}
}
glEnd();
}
}
}

View File

@@ -136,13 +136,12 @@ inline int computeVertexHash(int x, int y, int z)
static int addVertex(unsigned short x, unsigned short y, unsigned short z,
unsigned short* verts, int* firstVert, int* nextVert, int& nv)
{
int bucket = computeVertexHash(x, 0/*y*/, z);
int bucket = computeVertexHash(x, 0, z);
int i = firstVert[bucket];
while (i != -1)
{
const unsigned short* v = &verts[i*3];
// if (v[0] == x && v[1] == y && v[2] == z)
if (v[0] == x && (rcAbs(v[1] - y) <= 2) && v[2] == z)
return i;
i = nextVert[i]; // next
@@ -475,17 +474,331 @@ static void mergePolys(unsigned short* pa, unsigned short* pb,
memcpy(pa, tmp, sizeof(unsigned short)*nvp);
}
bool rcBuildPolyMesh(rcContourSet& cset,
const float* bmin, const float* bmax,
const float cs, const float ch, int nvp,
rcPolyMesh& mesh)
static void pushFront(int v, int* arr, int& an)
{
an++;
for (int i = an-1; i > 0; --i) arr[i] = arr[i-1];
arr[0] = v;
}
static void pushBack(int v, int* arr, int& an)
{
arr[an] = v;
an++;
}
static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int maxTris)
{
static const int nvp = mesh.nvp;
int* edges = 0;
int nedges = 0;
int* hole = 0;
int nhole = 0;
int* hreg = 0;
int nhreg = 0;
int* tris = 0;
int* tverts = 0;
int* thole = 0;
unsigned short* polys = 0;
unsigned short* pregs = 0;
int npolys = 0;
// Count number of polygons to remove.
int nrem = 0;
for (int i = 0; i < mesh.npolys; ++i)
{
unsigned short* p = &mesh.polys[i*nvp*2];
for (int j = 0; j < nvp; ++j)
if (p[j] == rem) { nrem++; break; }
}
edges = new int[nrem*nvp*3];
if (!edges)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", nrem*nvp*3);
goto failure;
}
hole = new int[nrem*nvp];
if (!hole)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", nrem*nvp);
goto failure;
}
hreg = new int[nrem*nvp];
if (!hreg)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", nrem*nvp);
goto failure;
}
for (int i = 0; i < mesh.npolys; ++i)
{
unsigned short* p = &mesh.polys[i*nvp*2];
const int nv = countPolyVerts(p, nvp);
bool hasRem = false;
for (int j = 0; j < nv; ++j)
if (p[j] == rem) hasRem = true;
if (hasRem)
{
// Collect edges which does not touch the removed vertex.
for (int j = 0, k = nv-1; j < nv; k = j++)
{
if (p[j] != rem && p[k] != rem)
{
int* e = &edges[nedges*3];
e[0] = p[k];
e[1] = p[j];
e[2] = mesh.regs[i];
nedges++;
}
}
// Remove the polygon.
unsigned short* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2];
memcpy(p,p2,sizeof(unsigned short)*nvp);
mesh.regs[i] = mesh.regs[mesh.npolys-1];
mesh.npolys--;
--i;
}
}
// Remove vertex.
for (int i = (int)rem; i < mesh.nverts; ++i)
{
mesh.verts[i*3+0] = mesh.verts[(i+1)*3+0];
mesh.verts[i*3+1] = mesh.verts[(i+1)*3+1];
mesh.verts[i*3+2] = mesh.verts[(i+1)*3+2];
}
mesh.nverts--;
// Adjust indices to match the removed vertex layout.
for (int i = 0; i < mesh.npolys; ++i)
{
unsigned short* p = &mesh.polys[i*nvp*2];
const int nv = countPolyVerts(p, nvp);
for (int j = 0; j < nv; ++j)
if (p[j] > rem) p[j]--;
}
for (int i = 0; i < nedges; ++i)
{
if (edges[i*3+0] > rem) edges[i*3+0]--;
if (edges[i*3+1] > rem) edges[i*3+1]--;
}
if (nedges == 0)
return true;
hole[nhole] = edges[0];
hreg[nhole] = edges[2];
nhole++;
while (nedges)
{
bool match = false;
for (int i = 0; i < nedges; ++i)
{
const int ea = edges[i*3+0];
const int eb = edges[i*3+1];
const int r = edges[i*3+2];
bool add = false;
if (hole[0] == eb)
{
pushFront(ea, hole, nhole);
pushFront(r, hreg, nhreg);
add = true;
}
else if (hole[nhole-1] == ea)
{
pushBack(eb, hole, nhole);
pushBack(r, hreg, nhreg);
add = true;
}
if (add)
{
// Remove edge.
edges[i*3+0] = edges[(nedges-1)*3+0];
edges[i*3+1] = edges[(nedges-1)*3+1];
edges[i*3+2] = edges[(nedges-1)*3+2];
--nedges;
match = true;
--i;
}
}
if (!match)
break;
}
tris = new int[nhole*3];
if (!tris)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3);
goto failure;
}
tverts = new int[nhole*4];
if (!tverts)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4);
goto failure;
}
thole = new int[nhole];
if (!tverts)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole);
goto failure;
}
// Generate temp vertex array for triangulation.
for (int i = 0; i < nhole; ++i)
{
const int pi = hole[i];
tverts[i*4+0] = mesh.verts[pi*3+0];
tverts[i*4+1] = mesh.verts[pi*3+1];
tverts[i*4+2] = mesh.verts[pi*3+2];
tverts[i*4+3] = 0;
thole[i] = i;
}
// Triangulate the hole.
int ntris = triangulate(nhole, &tverts[0], &thole[0], tris);
// Merge the hole triangles back to polygons.
polys = new unsigned short[(ntris+1)*nvp];
if (!polys)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp);
goto failure;
}
pregs = new unsigned short[ntris];
if (!pregs)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'pregs' (%d).", ntris);
goto failure;
}
unsigned short* tmpPoly = &polys[ntris*nvp];
// Build initial polygons.
memset(polys, 0xff, ntris*nvp*sizeof(unsigned short));
for (int j = 0; j < ntris; ++j)
{
int* t = &tris[j*3];
if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2])
{
polys[npolys*nvp+0] = (unsigned short)hole[t[0]];
polys[npolys*nvp+1] = (unsigned short)hole[t[1]];
polys[npolys*nvp+2] = (unsigned short)hole[t[2]];
pregs[npolys] = hreg[t[0]];
npolys++;
}
}
if (!npolys)
return true;
// Merge polygons.
if (nvp > 3)
{
while (true)
{
// Find best polygons to merge.
int bestMergeVal = 0;
int bestPa, bestPb, bestEa, bestEb;
for (int j = 0; j < npolys-1; ++j)
{
unsigned short* pj = &polys[j*nvp];
for (int k = j+1; k < npolys; ++k)
{
unsigned short* pk = &polys[k*nvp];
int ea, eb;
int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp);
if (v > bestMergeVal)
{
bestMergeVal = v;
bestPa = j;
bestPb = k;
bestEa = ea;
bestEb = eb;
}
}
}
if (bestMergeVal > 0)
{
// Found best, merge.
unsigned short* pa = &polys[bestPa*nvp];
unsigned short* pb = &polys[bestPb*nvp];
mergePolys(pa, pb, mesh.verts, bestEa, bestEb, tmpPoly, nvp);
memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp);
pregs[bestPb] = pregs[npolys-1];
npolys--;
}
else
{
// Could not merge any polygons, stop.
break;
}
}
}
// Store polygons.
for (int i = 0; i < npolys; ++i)
{
if (mesh.npolys >= maxTris) break;
unsigned short* p = &mesh.polys[mesh.npolys*nvp*2];
memset(p,0xff,sizeof(unsigned short)*nvp*2);
for (int j = 0; j < nvp; ++j)
p[j] = polys[i*nvp+j];
mesh.regs[mesh.npolys] = pregs[i];
mesh.npolys++;
}
delete [] edges;
delete [] hole;
delete [] hreg;
delete [] tris;
delete [] thole;
delete [] tverts;
delete [] polys;
delete [] pregs;
return true;
failure:
delete [] edges;
delete [] hole;
delete [] hreg;
delete [] tris;
delete [] thole;
delete [] tverts;
delete [] polys;
delete [] pregs;
return false;
}
bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh)
{
rcTimeVal startTime = rcGetPerformanceTimer();
vcopy(mesh.bmin, bmin);
vcopy(mesh.bmax, bmax);
mesh.cs = cs;
mesh.ch = ch;
vcopy(mesh.bmin, cset.bmin);
vcopy(mesh.bmax, cset.bmax);
mesh.cs = cset.cs;
mesh.ch = cset.ch;
int maxVertices = 0;
int maxTris = 0;
@@ -504,19 +817,42 @@ bool rcBuildPolyMesh(rcContourSet& cset,
return false;
}
unsigned char* vflags = 0;
int* nextVert = 0;
int* firstVert = 0;
int* indices = 0;
int* tris = 0;
unsigned short* polys = 0;
vflags = new unsigned char[maxVertices];
if (!vflags)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
goto failure;
}
memset(vflags, 0, maxVertices);
mesh.verts = new unsigned short[maxVertices*3];
if (!mesh.verts)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
return false;
goto failure;
}
mesh.polys = new unsigned short[maxTris*nvp*2];
if (!mesh.polys)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxTris*nvp);
return false;
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' (%d).", maxTris*nvp*2);
goto failure;
}
mesh.regs = new unsigned short[maxTris];
if (!mesh.regs)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.regs' (%d).", maxTris);
goto failure;
}
mesh.nverts = 0;
mesh.npolys = 0;
@@ -524,54 +860,49 @@ bool rcBuildPolyMesh(rcContourSet& cset,
memset(mesh.verts, 0, sizeof(unsigned short)*maxVertices*3);
memset(mesh.polys, 0xff, sizeof(unsigned short)*maxTris*nvp*2);
memset(mesh.regs, 0, sizeof(unsigned short)*maxTris);
int* nextVert = new int[maxVertices];
nextVert = new int[maxVertices];
if (!nextVert)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices);
return false;
goto failure;
}
memset(nextVert, 0, sizeof(int)*maxVertices);
int* firstVert = new int[VERTEX_BUCKET_COUNT];
firstVert = new int[VERTEX_BUCKET_COUNT];
if (!firstVert)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
return false;
goto failure;
}
for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
firstVert[i] = -1;
int* indices = new int[maxVertsPerCont];
indices = new int[maxVertsPerCont];
if (!indices)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont);
return false;
goto failure;
}
int* tris = new int[maxVertsPerCont*3];
tris = new int[maxVertsPerCont*3];
if (!tris)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3);
return false;
goto failure;
}
unsigned short* polys = new unsigned short[maxVertsPerCont*nvp];
polys = new unsigned short[(maxVertsPerCont+1)*nvp];
if (!polys)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp);
return false;
}
unsigned short* tmpPoly = new unsigned short[nvp];
if (!tmpPoly)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tmpPoly' (%d).", nvp);
return false;
goto failure;
}
unsigned short* tmpPoly = &polys[maxVertsPerCont*nvp];
for (int i = 0; i < cset.nconts; ++i)
{
@@ -609,6 +940,11 @@ bool rcBuildPolyMesh(rcContourSet& cset,
const int* v = &cont.verts[j*4];
indices[j] = addVertex((unsigned short)v[0], (unsigned short)v[1], (unsigned short)v[2],
mesh.verts, firstVert, nextVert, mesh.nverts);
if (v[3] & 0x10000)
{
// This vertex should be removed.
vflags[indices[j]] = 1;
}
}
// Build initial polygons.
@@ -681,11 +1017,26 @@ bool rcBuildPolyMesh(rcContourSet& cset,
unsigned short* q = &polys[j*nvp];
for (int k = 0; k < nvp; ++k)
p[k] = q[k];
mesh.regs[mesh.npolys] = cont.reg;
mesh.npolys++;
}
}
delete [] tmpPoly;
// Remove edge vertices.
for (int i = 0; i < mesh.nverts; ++i)
{
if (vflags[i])
{
if (!removeVertex(mesh, i, maxTris))
goto failure;
for (int j = i; j < mesh.nverts-1; ++j)
vflags[j] = vflags[j+1];
--i;
}
}
delete [] vflags;
delete [] firstVert;
delete [] nextVert;
delete [] indices;
@@ -707,4 +1058,155 @@ bool rcBuildPolyMesh(rcContourSet& cset,
rcGetBuildTimes()->buildPolymesh += rcGetDeltaTimeUsec(startTime, endTime);
return true;
failure:
delete [] vflags;
delete [] tmpPoly;
delete [] firstVert;
delete [] nextVert;
delete [] indices;
delete [] tris;
return false;
}
bool rcMergePolyMeshes(rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh)
{
if (!nmeshes || !meshes)
return true;
rcTimeVal startTime = rcGetPerformanceTimer();
int* nextVert = 0;
int* firstVert = 0;
unsigned short* vremap = 0;
mesh.nvp = meshes[0]->nvp;
mesh.cs = meshes[0]->cs;
mesh.ch = meshes[0]->ch;
vcopy(mesh.bmin, meshes[0]->bmin);
vcopy(mesh.bmax, meshes[0]->bmax);
int maxVerts = 0;
int maxPolys = 0;
int maxVertsPerMesh = 0;
for (int i = 0; i < nmeshes; ++i)
{
vmin(mesh.bmin, meshes[i]->bmin);
vmax(mesh.bmax, meshes[i]->bmax);
maxVertsPerMesh = rcMax(maxVertsPerMesh, meshes[i]->nverts);
maxVerts += meshes[i]->nverts;
maxPolys += meshes[i]->npolys;
}
mesh.nverts = 0;
mesh.verts = new unsigned short[maxVerts*3];
if (!mesh.verts)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.verts' (%d).", maxVerts*3);
return false;
}
mesh.npolys = 0;
mesh.polys = new unsigned short[maxPolys*2*mesh.nvp];
if (!mesh.polys)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.polys' (%d).", maxPolys*2*mesh.nvp);
return false;
}
memset(mesh.polys, 0xff, sizeof(unsigned short)*maxPolys*2*mesh.nvp);
mesh.regs = new unsigned short[maxPolys];
if (!mesh.regs)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.regs' (%d).", maxPolys);
return false;
}
memset(mesh.regs, 0, sizeof(unsigned short)*maxPolys);
nextVert = new int[maxVerts];
if (!nextVert)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts);
goto failure;
}
memset(nextVert, 0, sizeof(int)*maxVerts);
firstVert = new int[VERTEX_BUCKET_COUNT];
if (!firstVert)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
goto failure;
}
for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
firstVert[i] = -1;
vremap = new unsigned short[maxVertsPerMesh];
if (!vremap)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh);
goto failure;
}
memset(nextVert, 0, sizeof(int)*maxVerts);
for (int i = 0; i < nmeshes; ++i)
{
const rcPolyMesh* pmesh = meshes[i];
const unsigned short ox = (unsigned short)floorf((pmesh->bmin[0]-mesh.bmin[0])/mesh.cs+0.5f);
const unsigned short oz = (unsigned short)floorf((pmesh->bmin[2]-mesh.bmin[2])/mesh.cs+0.5f);
for (int j = 0; j < pmesh->nverts; ++j)
{
unsigned short* v = &pmesh->verts[j*3];
vremap[j] = addVertex(v[0]+ox, v[1], v[2]+oz,
mesh.verts, firstVert, nextVert, mesh.nverts);
}
for (int j = 0; j < pmesh->npolys; ++j)
{
unsigned short* tgt = &mesh.polys[mesh.npolys*2*mesh.nvp];
unsigned short* src = &pmesh->polys[j*2*mesh.nvp];
mesh.regs[mesh.npolys] = pmesh->regs[j];
mesh.npolys++;
for (int k = 0; k < mesh.nvp; ++k)
{
if (src[k] == 0xffff) break;
tgt[k] = vremap[src[k]];
}
}
}
// Calculate adjacency.
if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, mesh.nvp))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Adjacency failed.");
return false;
}
delete [] firstVert;
delete [] nextVert;
delete [] vremap;
rcTimeVal endTime = rcGetPerformanceTimer();
if (rcGetBuildTimes())
rcGetBuildTimes()->mergePolyMesh += rcGetDeltaTimeUsec(startTime, endTime);
return true;
failure:
delete [] firstVert;
delete [] nextVert;
delete [] vremap;
return false;
}

View File

@@ -0,0 +1,969 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#include <float.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "Recast.h"
#include "RecastLog.h"
#include "RecastTimer.h"
struct rcHeightPatch
{
inline rcHeightPatch() : data(0) {}
inline ~rcHeightPatch() { delete [] data; }
unsigned short* data;
int xmin, ymin, width, height;
};
static int circumCircle(const float xp, const float yp,
const float x1, const float y1,
const float x2, const float y2,
const float x3, const float y3,
float& xc, float& yc, float& rsqr)
{
static const float EPSILON = 1e-6f;
const float fabsy1y2 = rcAbs(y1-y2);
const float fabsy2y3 = rcAbs(y2-y3);
/* Check for coincident points */
if (fabsy1y2 < EPSILON && fabsy2y3 < EPSILON)
return 0;
if (fabsy1y2 < EPSILON)
{
const float m2 = - (x3-x2) / (y3-y2);
const float mx2 = (x2 + x3) / 2.0f;
const float my2 = (y2 + y3) / 2.0f;
xc = (x2 + x1) / 2.0f;
yc = m2 * (xc - mx2) + my2;
}
else if (fabsy2y3 < EPSILON)
{
const float m1 = - (x2-x1) / (y2-y1);
const float mx1 = (x1 + x2) / 2.0f;
const float my1 = (y1 + y2) / 2.0f;
xc = (x3 + x2) / 2.0f;
yc = m1 * (xc - mx1) + my1;
}
else
{
const float m1 = - (x2-x1) / (y2-y1);
const float m2 = - (x3-x2) / (y3-y2);
const float mx1 = (x1 + x2) / 2.0f;
const float mx2 = (x2 + x3) / 2.0f;
const float my1 = (y1 + y2) / 2.0f;
const float my2 = (y2 + y3) / 2.0f;
xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2);
if (fabsy1y2 > fabsy2y3)
yc = m1 * (xc - mx1) + my1;
else
yc = m2 * (xc - mx2) + my2;
}
float dx,dy;
dx = x2 - xc;
dy = y2 - yc;
rsqr = dx*dx + dy*dy;
dx = xp - xc;
dy = yp - yc;
const float drsqr = dx*dx + dy*dy;
return (drsqr <= rsqr) ? 1 : 0;
}
static int ptcmp(void* up, const void *v1, const void *v2)
{
const float* verts = (const float*)up;
const float* p1 = &verts[(*(const int*)v1)*3];
const float* p2 = &verts[(*(const int*)v2)*3];
if (p1[0] < p2[0])
return -1;
else if (p1[0] > p2[0])
return 1;
else
return 0;
}
// Based on Paul Bourke's triangulate.c
// http://astronomy.swin.edu.au/~pbourke/terrain/triangulate/triangulate.c
static void delaunay(const int nv, float *verts, rcIntArray& idx, rcIntArray& tris, rcIntArray& edges)
{
// Sort vertices
idx.resize(nv);
for (int i = 0; i < nv; ++i)
idx[i] = i;
qsort_r(&idx[0], idx.size(), sizeof(int), verts, ptcmp);
// Find the maximum and minimum vertex bounds.
// This is to allow calculation of the bounding triangle
float xmin = verts[0];
float ymin = verts[2];
float xmax = xmin;
float ymax = ymin;
for (int i = 1; i < nv; ++i)
{
xmin = rcMin(xmin, verts[i*3+0]);
xmax = rcMax(xmax, verts[i*3+0]);
ymin = rcMin(ymin, verts[i*3+2]);
ymax = rcMax(ymax, verts[i*3+2]);
}
float dx = xmax - xmin;
float dy = ymax - ymin;
float dmax = (dx > dy) ? dx : dy;
float xmid = (xmax + xmin) / 2.0;
float ymid = (ymax + ymin) / 2.0;
// Set up the supertriangle
// This is a triangle which encompasses all the sample points.
// The supertriangle coordinates are added to the end of the
// vertex list. The supertriangle is the first triangle in
// the triangle list.
float sv[3*3];
sv[0] = xmid - 20 * dmax;
sv[1] = 0;
sv[2] = ymid - dmax;
sv[3] = xmid;
sv[4] = 0;
sv[5] = ymid + 20 * dmax;
sv[6] = xmid + 20 * dmax;
sv[7] = 0;
sv[8] = ymid - dmax;
tris.push(-3);
tris.push(-2);
tris.push(-1);
tris.push(0); // not completed
for (int i = 0; i < nv; ++i)
{
const float xp = verts[idx[i]*3+0];
const float yp = verts[idx[i]*3+2];
edges.resize(0);
// Set up the edge buffer.
// If the point (xp,yp) lies inside the circumcircle then the
// three edges of that triangle are added to the edge buffer
// and that triangle is removed.
for (int j = 0; j < tris.size()/4; ++j)
{
int* t = &tris[j*4];
if (t[3]) // completed?
continue;
const float* v1 = t[0] < 0 ? &sv[(t[0]+3)*3] : &verts[idx[t[0]]*3];
const float* v2 = t[1] < 0 ? &sv[(t[1]+3)*3] : &verts[idx[t[1]]*3];
const float* v3 = t[2] < 0 ? &sv[(t[2]+3)*3] : &verts[idx[t[2]]*3];
float xc,yc,rsqr;
int inside = circumCircle(xp,yp, v1[0],v1[2], v2[0],v2[2], v3[0],v3[2], xc,yc,rsqr);
if (xc < xp && rcSqr(xp-xc) > rsqr)
t[3] = 1;
if (inside)
{
// Collect triangle edges.
edges.push(t[0]);
edges.push(t[1]);
edges.push(t[1]);
edges.push(t[2]);
edges.push(t[2]);
edges.push(t[0]);
// Remove triangle j.
t[0] = tris[tris.size()-4];
t[1] = tris[tris.size()-3];
t[2] = tris[tris.size()-2];
t[3] = tris[tris.size()-1];
tris.resize(tris.size()-4);
j--;
}
}
// Remove duplicate edges.
const int ne = edges.size()/2;
for (int j = 0; j < ne-1; ++j)
{
for (int k = j+1; k < ne; ++k)
{
// Dupe?, make null.
if ((edges[j*2+0] == edges[k*2+1]) && (edges[j*2+1] == edges[k*2+0]))
{
edges[j*2+0] = 0;
edges[j*2+1] = 0;
edges[k*2+0] = 0;
edges[k*2+1] = 0;
}
}
}
// Form new triangles for the current point
// Skipping over any null.
// All edges are arranged in clockwise order.
for (int j = 0; j < ne; ++j)
{
if (edges[j*2+0] == edges[j*2+1]) continue;
tris.push(edges[j*2+0]);
tris.push(edges[j*2+1]);
tris.push(i);
tris.push(0); // not completed
}
}
// Remove triangles with supertriangle vertices
// These are triangles which have a vertex number greater than nv
for (int i = 0; i < tris.size()/4; ++i)
{
int* t = &tris[i*4];
if (t[0] < 0 || t[1] < 0 || t[2] < 0)
{
t[0] = tris[tris.size()-4];
t[1] = tris[tris.size()-3];
t[2] = tris[tris.size()-2];
t[3] = tris[tris.size()-1];
tris.resize(tris.size()-4);
i--;
}
}
// Triangle vertices are pointing to sorted vertices, remap indices.
for (int i = 0; i < tris.size(); ++i)
tris[i] = idx[tris[i]];
}
inline float vdot2(const float* a, const float* b)
{
return a[0]*b[0] + a[2]*b[2];
}
static float distPtTri(const float* p, const float* a, const float* b, const float* c)
{
float v0[3], v1[3], v2[3];
vsub(v0, c,a);
vsub(v1, b,a);
vsub(v2, p,a);
const float dot00 = vdot2(v0, v0);
const float dot01 = vdot2(v0, v1);
const float dot02 = vdot2(v0, v2);
const float dot11 = vdot2(v1, v1);
const float dot12 = vdot2(v1, v2);
// Compute barycentric coordinates
float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
// If point lies inside the triangle, return interpolated y-coord.
static const float EPS = 1e-4f;
if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS)
{
float y = a[1] + v0[1]*u + v1[1]*v;
return fabsf(y-p[1]);
}
return FLT_MAX;
}
static float distancePtSeg(const float* pt, const float* p, const float* q)
{
float pqx = q[0] - p[0];
float pqy = q[1] - p[1];
float pqz = q[2] - p[2];
float dx = pt[0] - p[0];
float dy = pt[1] - p[1];
float dz = pt[2] - p[2];
float d = pqx*pqx + pqy*pqy + pqz*pqz;
float t = pqx*dx + pqy*dy + pqz*dz;
if (d > 0)
t /= d;
if (t < 0)
t = 0;
else if (t > 1)
t = 1;
dx = p[0] + t*pqx - pt[0];
dy = p[1] + t*pqy - pt[1];
dz = p[2] + t*pqz - pt[2];
return dx*dx + dy*dy + dz*dz;
}
static float distancePtSeg2d(const float* pt, const float* p, const float* q)
{
float pqx = q[0] - p[0];
float pqz = q[2] - p[2];
float dx = pt[0] - p[0];
float dz = pt[2] - p[2];
float d = pqx*pqx + pqz*pqz;
float t = pqx*dx + pqz*dz;
if (d > 0)
t /= d;
if (t < 0)
t = 0;
else if (t > 1)
t = 1;
dx = p[0] + t*pqx - pt[0];
dz = p[2] + t*pqz - pt[2];
return dx*dx + dz*dz;
}
static float distToTriMesh(const float* p, const float* verts, int nverts, const int* tris, int ntris)
{
float dmin = FLT_MAX;
for (int i = 0; i < ntris; ++i)
{
const float* va = &verts[tris[i*4+0]*3];
const float* vb = &verts[tris[i*4+1]*3];
const float* vc = &verts[tris[i*4+2]*3];
float d = distPtTri(p, va,vb,vc);
if (d < dmin)
dmin = d;
}
if (dmin == FLT_MAX) return -1;
return dmin;
}
static float distToPoly(int nvert, const float* verts, const float* p)
{
float dmin = FLT_MAX;
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++)
{
const float* vi = &verts[i*3];
const float* vj = &verts[j*3];
if (((vi[2] > p[2]) != (vj[2] > p[2])) &&
(p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
c = !c;
dmin = rcMin(dmin, distancePtSeg2d(p, vj, vi));
}
return c ? -dmin : dmin;
}
static unsigned short getHeight(const float* pos, const float* bmin, const float ics, const rcHeightPatch& hp)
{
int ix = (int)floorf((pos[0]-bmin[0])*ics + 0.01f);
int iz = (int)floorf((pos[2]-bmin[2])*ics + 0.01f);
ix = rcClamp(ix-hp.xmin, 0, hp.width);
iz = rcClamp(iz-hp.ymin, 0, hp.height);
unsigned short h = hp.data[ix+iz*hp.width];
return h;
}
static bool buildPolyDetail(const float* in, const int nin, unsigned short reg,
const float sampleDist, const float sampleMaxError,
const rcCompactHeightfield& chf, const rcHeightPatch& hp,
float* verts, int& nverts, rcIntArray& tris,
rcIntArray& edges, rcIntArray& idx, rcIntArray& samples)
{
static const int MAX_VERTS = 256;
static const int MAX_EDGE = 64;
float edge[(MAX_EDGE+1)*3];
nverts = 0;
for (int i = 0; i < nin; ++i)
vcopy(&verts[i*3], &in[i*3]);
nverts = nin;
const float ics = 1.0f/chf.cs;
// Tesselate outlines.
// This is done in separate pass in order to ensure
// seamless height values across the ply boundaries.
if (sampleDist > 0)
{
for (int i = 0, j = nin-1; i < nin; j=i++)
{
const float* vj = &in[j*3];
const float* vi = &in[i*3];
// Make sure the segments are always handled in same order
// using lexological sort or else there will be seams.
if (fabsf(vj[0]-vi[0]) < 1e-6f)
{
if (vj[2] > vi[2])
rcSwap(vj,vi);
}
else
{
if (vj[0] > vi[0])
rcSwap(vj,vi);
}
// Create samples along the edge.
float dx = vi[0] - vj[0];
float dy = vi[1] - vj[1];
float dz = vi[2] - vj[2];
float d = sqrtf(dx*dx + dz*dz);
int nn = 1 + (int)floorf(d/sampleDist);
if (nn > MAX_EDGE) nn = MAX_EDGE;
if (nverts+nn >= MAX_VERTS)
nn = MAX_VERTS-1-nverts;
for (int k = 0; k <= nn; ++k)
{
float u = (float)k/(float)nn;
float* pos = &edge[k*3];
pos[0] = vj[0] + dx*u;
pos[1] = vj[1] + dy*u;
pos[2] = vj[2] + dz*u;
pos[1] = chf.bmin[1] + getHeight(pos, chf.bmin, ics, hp)*chf.ch;
}
// Simplify samples.
int idx[MAX_EDGE] = {0,nn};
int nidx = 2;
for (int k = 0; k < nidx-1; )
{
const int a = idx[k];
const int b = idx[k+1];
const float* va = &edge[a*3];
const float* vb = &edge[b*3];
// Find maximum deviation along the segment.
float maxd = 0;
int maxi = -1;
for (int m = a+1; m < b; ++m)
{
float d = distancePtSeg(&edge[m*3],va,vb);
if (d > maxd)
{
maxd = d;
maxi = m;
}
}
// If the max deviation is larger than accepted error,
// add new point, else continue to next segment.
if (maxi != -1 && maxd > rcSqr(sampleMaxError))
{
for (int m = nidx; m > k; --m)
idx[m] = idx[m-1];
idx[k+1] = maxi;
nidx++;
}
else
{
++k;
}
}
// Add new vertices.
for (int k = 1; k < nidx-1; ++k)
{
vcopy(&verts[nverts*3], &edge[idx[k]*3]);
nverts++;
}
}
}
// Tesselate the base mesh.
edges.resize(0);
tris.resize(0);
idx.resize(0);
delaunay(nverts, verts, idx, tris, edges);
if (sampleDist > 0)
{
// Create sample locations in a grid.
float bmin[3], bmax[3];
vcopy(bmin, in);
vcopy(bmax, in);
for (int i = 1; i < nin; ++i)
{
vmin(bmin, &in[i*3]);
vmax(bmax, &in[i*3]);
}
int x0 = (int)floorf(bmin[0]/sampleDist);
int x1 = (int)ceilf(bmax[0]/sampleDist);
int z0 = (int)floorf(bmin[2]/sampleDist);
int z1 = (int)ceilf(bmax[2]/sampleDist);
samples.resize(0);
for (int z = z0; z < z1; ++z)
{
for (int x = x0; x < x1; ++x)
{
float pt[3];
pt[0] = x*sampleDist;
pt[2] = z*sampleDist;
// Make sure the samples are not too close to the edges.
if (distToPoly(nin,in,pt) > -sampleDist/2) continue;
samples.push(x);
samples.push(getHeight(pt, chf.bmin, ics, hp));
samples.push(z);
}
}
// Add the samples starting from the one that has the most
// error. The procedure stops when all samples are added
// or when the max error is within treshold.
const int nsamples = samples.size()/3;
for (int iter = 0; iter < nsamples; ++iter)
{
// Find sample with most error.
float bestpt[3];
float bestd = 0;
for (int i = 0; i < nsamples; ++i)
{
float pt[3];
pt[0] = samples[i*3+0]*sampleDist;
pt[1] = chf.bmin[1] + samples[i*3+1]*chf.ch;
pt[2] = samples[i*3+2]*sampleDist;
float d = distToTriMesh(pt, verts, nverts, &tris[0], tris.size()/4);
if (d < 0) continue; // did not hit the mesh.
if (d > bestd)
{
bestd = d;
vcopy(bestpt,pt);
}
}
// If the max error is within accepted threshold, stop tesselating.
if (bestd <= sampleMaxError)
break;
// Add the new sample point.
vcopy(&verts[nverts*3],bestpt);
nverts++;
// Create new triangulation.
// TODO: Incremental add instead of full rebuild.
edges.resize(0);
tris.resize(0);
idx.resize(0);
delaunay(nverts, verts, idx, tris, edges);
if (nverts >= MAX_VERTS)
break;
}
}
return true;
}
static void getHeightData(const rcCompactHeightfield& chf,
const unsigned short* poly, const int npoly,
const unsigned short* verts,
rcHeightPatch& hp, rcIntArray& stack)
{
// Floodfill the heightfield to get 2D height data,
// starting at vertex locations as seeds.
memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
stack.resize(0);
// Use poly vertices as seed points for the flood fill.
for (int j = 0; j < npoly; ++j)
{
const int ax = (int)verts[poly[j]*3+0];
const int ay = (int)verts[poly[j]*3+1];
const int az = (int)verts[poly[j]*3+2];
if (ax < hp.xmin || ax >= hp.xmin+hp.width ||
az < hp.ymin || az >= hp.ymin+hp.height)
continue;
const rcCompactCell& c = chf.cells[ax+az*chf.width];
int dmin = 0xffff;
int ai = -1;
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
{
const rcCompactSpan& s = chf.spans[i];
int d = rcAbs(ay - (int)s.y);
if (d < dmin)
{
ai = i;
dmin = d;
}
}
if (ai != -1)
{
stack.push(ax);
stack.push(az);
stack.push(ai);
}
}
while (stack.size() > 0)
{
int ci = stack.pop();
int cy = stack.pop();
int cx = stack.pop();
// Skip already visited locations.
int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
if (hp.data[idx] != 0xffff)
continue;
const rcCompactSpan& cs = chf.spans[ci];
hp.data[idx] = cs.y;
for (int dir = 0; dir < 4; ++dir)
{
if (rcGetCon(cs, dir) == 0xf) continue;
const int ax = cx + rcGetDirOffsetX(dir);
const int ay = cy + rcGetDirOffsetY(dir);
if (ax < hp.xmin || ax >= (hp.xmin+hp.width) ||
ay < hp.ymin || ay >= (hp.ymin+hp.height))
continue;
if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0xffff)
continue;
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(cs, dir);
stack.push(ax);
stack.push(ay);
stack.push(ai);
}
}
}
static unsigned char getEdgeFlags(const float* va, const float* vb,
const float* vpoly, const int npoly)
{
// Return true if edge (va,vb) is part of the polygon.
static const float thrSqr = rcSqr(0.001f);
for (int i = 0, j = npoly-1; i < npoly; j=i++)
{
if (distancePtSeg2d(va, &vpoly[j*3], &vpoly[i*3]) < thrSqr &&
distancePtSeg2d(vb, &vpoly[j*3], &vpoly[i*3]) < thrSqr)
return 1;
}
return 0;
}
static unsigned char getTriFlags(const float* va, const float* vb, const float* vc,
const float* vpoly, const int npoly)
{
unsigned char flags = 0;
flags |= getEdgeFlags(va,vb,vpoly,npoly) << 0;
flags |= getEdgeFlags(vb,vc,vpoly,npoly) << 2;
flags |= getEdgeFlags(vc,va,vpoly,npoly) << 4;
return flags;
}
bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& chf,
const float sampleDist, const float sampleMaxError,
rcPolyMeshDetail& dmesh)
{
rcTimeVal startTime = rcGetPerformanceTimer();
if (mesh.nverts == 0 || mesh.npolys == 0)
return true;
const int nvp = mesh.nvp;
const float cs = mesh.cs;
const float ch = mesh.ch;
const float* orig = mesh.bmin;
rcIntArray edges(64);
rcIntArray tris(512);
rcIntArray idx(512);
rcIntArray stack(512);
rcIntArray samples(512);
float verts[256*3];
float poly[nvp*3];
int* bounds = 0;
rcHeightPatch hp;
int nPolyVerts = 0;
int maxhw = 0, maxhh = 0;
bounds = new int[mesh.npolys*4];
if (!bounds)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' (%d).", mesh.npolys*4);
goto failure;
}
// Find max size for a polygon area.
for (int i = 0; i < mesh.npolys; ++i)
{
const unsigned short* p = &mesh.polys[i*nvp*2];
int& xmin = bounds[i*4+0];
int& xmax = bounds[i*4+1];
int& ymin = bounds[i*4+2];
int& ymax = bounds[i*4+3];
xmin = chf.width;
xmax = 0;
ymin = chf.height;
ymax = 0;
for (int j = 0; j < nvp; ++j)
{
if(p[j] == 0xffff) break;
const unsigned short* v = &mesh.verts[p[j]*3];
xmin = rcMin(xmin, (int)v[0]);
xmax = rcMax(xmax, (int)v[0]);
ymin = rcMin(ymin, (int)v[2]);
ymax = rcMax(ymax, (int)v[2]);
nPolyVerts++;
}
xmin = rcMax(0,xmin-1);
xmax = rcMin(chf.width,xmax+1);
ymin = rcMax(0,ymin-1);
ymax = rcMin(chf.height,ymax+1);
if (xmin >= xmax || ymin >= ymax) continue;
maxhw = rcMax(maxhw, xmax-xmin);
maxhh = rcMax(maxhh, ymax-ymin);
}
hp.data = new unsigned short[maxhw*maxhh];
if (!hp.data)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'hp.data' (%d).", maxhw*maxhh);
goto failure;
}
dmesh.nmeshes = mesh.npolys;
dmesh.nverts = 0;
dmesh.ntris = 0;
dmesh.meshes = new unsigned short[dmesh.nmeshes*4];
if (!dmesh.meshes)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' (%d).", dmesh.nmeshes*4);
goto failure;
}
int vcap = nPolyVerts+nPolyVerts/2;
int tcap = vcap*2;
dmesh.nverts = 0;
dmesh.verts = new float[vcap*3];
if (!dmesh.verts)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", vcap*3);
goto failure;
}
dmesh.ntris = 0;
dmesh.tris = new unsigned char[tcap*4];
if (!dmesh.tris)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", tcap*4);
goto failure;
}
for (int i = 0; i < mesh.npolys; ++i)
{
const unsigned short* p = &mesh.polys[i*nvp*2];
// Find polygon bounding box.
int npoly = 0;
for (int j = 0; j < nvp; ++j)
{
if(p[j] == 0xffff) break;
const unsigned short* v = &mesh.verts[p[j]*3];
poly[j*3+0] = orig[0] + v[0]*cs;
poly[j*3+1] = orig[1] + v[1]*ch;
poly[j*3+2] = orig[2] + v[2]*cs;
npoly++;
}
// Get the height data from the area of the polygon.
hp.xmin = bounds[i*4+0];
hp.ymin = bounds[i*4+2];
hp.width = bounds[i*4+1]-bounds[i*4+0];
hp.height = bounds[i*4+3]-bounds[i*4+2];
getHeightData(chf, p, npoly, mesh.verts, hp, stack);
// Build detail mesh.
int nverts = 0;
if (!buildPolyDetail(poly, npoly, mesh.regs[i],
sampleDist, sampleMaxError,
chf, hp, verts, nverts, tris,
edges, idx, samples))
{
goto failure;
}
// Offset detail vertices, unnecassary?
for (int j = 0; j < nverts; ++j)
verts[j*3+1] += chf.ch;
// Store detail submesh.
const int ntris = tris.size()/4;
dmesh.meshes[i*4+0] = dmesh.nverts;
dmesh.meshes[i*4+1] = (unsigned short)nverts;
dmesh.meshes[i*4+2] = dmesh.ntris;
dmesh.meshes[i*4+3] = (unsigned short)ntris;
// Store vertices, allocate more memory if necessary.
if (dmesh.nverts+nverts > vcap)
{
while (dmesh.nverts+nverts > vcap)
vcap += 256;
float* newv = new float[vcap*3];
if (!newv)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newv' (%d).", vcap*3);
goto failure;
}
if (dmesh.nverts)
memcpy(newv, dmesh.verts, sizeof(float)*3*dmesh.nverts);
delete [] dmesh.verts;
dmesh.verts = newv;
}
for (int j = 0; j < nverts; ++j)
{
dmesh.verts[dmesh.nverts*3+0] = verts[j*3+0];
dmesh.verts[dmesh.nverts*3+1] = verts[j*3+1];
dmesh.verts[dmesh.nverts*3+2] = verts[j*3+2];
dmesh.nverts++;
}
// Store triangles, allocate more memory if necessary.
if (dmesh.ntris+ntris > tcap)
{
while (dmesh.ntris+ntris > tcap)
tcap += 256;
unsigned char* newt = new unsigned char[tcap*4];
if (!newt)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newt' (%d).", tcap*4);
goto failure;
}
if (dmesh.ntris)
memcpy(newt, dmesh.tris, sizeof(unsigned char)*4*dmesh.ntris);
delete [] dmesh.tris;
dmesh.tris = newt;
}
for (int j = 0; j < ntris; ++j)
{
const int* t = &tris[j*4];
dmesh.tris[dmesh.ntris*4+0] = (unsigned char)t[0];
dmesh.tris[dmesh.ntris*4+1] = (unsigned char)t[1];
dmesh.tris[dmesh.ntris*4+2] = (unsigned char)t[2];
dmesh.tris[dmesh.ntris*4+3] = getTriFlags(&verts[t[0]*3], &verts[t[1]*3], &verts[t[2]*3], poly, npoly);
dmesh.ntris++;
}
}
delete [] bounds;
rcTimeVal endTime = rcGetPerformanceTimer();
if (rcGetBuildTimes())
rcGetBuildTimes()->buildDetailMesh += rcGetDeltaTimeUsec(startTime, endTime);
return true;
failure:
delete [] bounds;
return false;
}
bool rcMergePolyMeshDetails(rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh)
{
rcTimeVal startTime = rcGetPerformanceTimer();
int maxVerts = 0;
int maxTris = 0;
int maxMeshes = 0;
for (int i = 0; i < nmeshes; ++i)
{
if (!meshes[i]) continue;
maxVerts += meshes[i]->nverts;
maxTris += meshes[i]->ntris;
maxMeshes += meshes[i]->nmeshes;
}
mesh.nmeshes = 0;
mesh.meshes = new unsigned short[maxMeshes*4];
if (!mesh.meshes)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' (%d).", maxMeshes*4);
return false;
}
mesh.ntris = 0;
mesh.tris = new unsigned char[maxTris*4];
if (!mesh.tris)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", maxTris*4);
return false;
}
mesh.nverts = 0;
mesh.verts = new float[maxVerts*3];
if (!mesh.verts)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", maxVerts*3);
return false;
}
// Merge datas.
for (int i = 0; i < nmeshes; ++i)
{
rcPolyMeshDetail* dm = meshes[i];
if (!dm) continue;
for (int j = 0; j < dm->nmeshes; ++j)
{
unsigned short* dst = &mesh.meshes[mesh.nmeshes*4];
unsigned short* src = &dm->meshes[j*4];
dst[0] = mesh.nverts+src[0];
dst[1] = src[1];
dst[2] = mesh.ntris+src[2];
dst[3] = src[3];
mesh.nmeshes++;
}
for (int k = 0; k < dm->nverts; ++k)
{
vcopy(&mesh.verts[mesh.nverts*3], &dm->verts[k*3]);
mesh.nverts++;
}
for (int k = 0; k < dm->ntris; ++k)
{
mesh.tris[mesh.ntris*4+0] = dm->tris[k*4+0];
mesh.tris[mesh.ntris*4+1] = dm->tris[k*4+1];
mesh.tris[mesh.ntris*4+2] = dm->tris[k*4+2];
mesh.tris[mesh.ntris*4+3] = dm->tris[k*4+3];
mesh.ntris++;
}
}
rcTimeVal endTime = rcGetPerformanceTimer();
if (rcGetBuildTimes())
rcGetBuildTimes()->mergePolyMeshDetail += rcGetDeltaTimeUsec(startTime, endTime);
return true;
}

View File

@@ -447,7 +447,7 @@ static void removeAdjacentNeighbours(rcRegion& reg)
}
}
void replaceNeighbour(rcRegion& reg, unsigned short oldId, unsigned short newId)
static void replaceNeighbour(rcRegion& reg, unsigned short oldId, unsigned short newId)
{
bool neiChanged = false;
for (int i = 0; i < reg.connections.size(); ++i)
@@ -467,7 +467,7 @@ void replaceNeighbour(rcRegion& reg, unsigned short oldId, unsigned short newId)
removeAdjacentNeighbours(reg);
}
bool canMergeWithRegion(rcRegion& reg, unsigned short id)
static bool canMergeWithRegion(rcRegion& reg, unsigned short id)
{
int n = 0;
for (int i = 0; i < reg.connections.size(); ++i)

File diff suppressed because it is too large Load Diff

View File

@@ -279,14 +279,14 @@
<key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
<array>
<array>
<integer>10</integer>
<integer>3</integer>
<integer>32</integer>
<integer>18</integer>
<integer>1</integer>
<integer>0</integer>
</array>
</array>
<key>PBXSmartGroupTreeModuleOutlineStateVisibleRectKey</key>
<string>{{0, 33}, {282, 628}}</string>
<string>{{0, 229}, {282, 628}}</string>
</dict>
<key>PBXTopSmartGroupGIDs</key>
<array/>
@@ -321,7 +321,7 @@
<key>PBXProjectModuleGUID</key>
<string>6B8632A30F78115100E2684A</string>
<key>PBXProjectModuleLabel</key>
<string>DetourTileNavMesh.h</string>
<string>RecastMeshDetail.cpp</string>
<key>PBXSplitModuleInNavigatorKey</key>
<dict>
<key>Split0</key>
@@ -329,105 +329,99 @@
<key>PBXProjectModuleGUID</key>
<string>6B8632A40F78115100E2684A</string>
<key>PBXProjectModuleLabel</key>
<string>DetourTileNavMesh.h</string>
<string>RecastMeshDetail.cpp</string>
<key>_historyCapacity</key>
<integer>0</integer>
<key>bookmark</key>
<string>6B58CAE510198B2400956BA2</string>
<string>6B33154A1042C0A500E98B50</string>
<key>history</key>
<array>
<string>6B7707F00FBD90F100D21BAE</string>
<string>6B995BDF0FE0D9B300D5C493</string>
<string>6BC745A70FF527E50083A694</string>
<string>6BC745A80FF527E50083A694</string>
<string>6B9B7D9D0FF91AC600A9090F</string>
<string>6B25B43E0FFA1786004F1BC4</string>
<string>6B25B44B0FFA1968004F1BC4</string>
<string>6B2AEC620FFB8AB0005BE9CC</string>
<string>6B2AEC670FFB8AB0005BE9CC</string>
<string>6B2AECED0FFB8B41005BE9CC</string>
<string>6B092B4F0FFCA0A20088D3A5</string>
<string>6B092B500FFCA0A20088D3A5</string>
<string>6B092CC10FFE40160088D3A5</string>
<string>6B0249BE1003793900CF7107</string>
<string>6B024BD31006059C00CF7107</string>
<string>6B024C011006098300CF7107</string>
<string>6B024C1110060C7600CF7107</string>
<string>6B1186211006945C0018F96F</string>
<string>6B1186D1100699A00018F96F</string>
<string>6B7EBB69100721310066EF8C</string>
<string>6B555D24100B136A00247EA3</string>
<string>6B555D30100B143200247EA3</string>
<string>6B555E01100B285300247EA3</string>
<string>6B555E5F100B334900247EA3</string>
<string>6B555E60100B334900247EA3</string>
<string>6B555EA1100B37AB00247EA3</string>
<string>6B555F0C100B473F00247EA3</string>
<string>6B555F0D100B473F00247EA3</string>
<string>6B555F0E100B473F00247EA3</string>
<string>6B555F0F100B473F00247EA3</string>
<string>6B92CE68100E0577003DA304</string>
<string>6B92CE69100E0577003DA304</string>
<string>6B92CE6A100E0577003DA304</string>
<string>6B92CE6F100E0577003DA304</string>
<string>6B92CE70100E0577003DA304</string>
<string>6B92CE72100E0577003DA304</string>
<string>6B92CE8A100E0739003DA304</string>
<string>6B995F9F100F336B00D7BF5A</string>
<string>6B995FA2100F336B00D7BF5A</string>
<string>6B995FDF100F387200D7BF5A</string>
<string>6B996059100F42AF00D7BF5A</string>
<string>6B8AE8DA10121C6000FF1D07</string>
<string>6B8AE8FA10123B5700FF1D07</string>
<string>6B8AE8FB10123B5700FF1D07</string>
<string>6B8AE8FC10123B5700FF1D07</string>
<string>6B8AE8FE10123B5700FF1D07</string>
<string>6B8AE90010123B5700FF1D07</string>
<string>6B8AE90210123B5700FF1D07</string>
<string>6B8AE90310123B5700FF1D07</string>
<string>6B8AE90410123B5700FF1D07</string>
<string>6B8AE90510123B5700FF1D07</string>
<string>6B8AE90610123B5700FF1D07</string>
<string>6B8AE90710123B5700FF1D07</string>
<string>6BD4DBB910145A50003FF199</string>
<string>6BD4DBBA10145A50003FF199</string>
<string>6BD4DBC710145C42003FF199</string>
<string>6BD4DBC810145C42003FF199</string>
<string>6BD4DBDC101485D3003FF199</string>
<string>6B9D0AF4102991F0009B1A6C</string>
<string>6B93FDBB102FFCFE00F0C0DA</string>
<string>6B6241E91034B2FA0002E346</string>
<string>6B6241EB1034B2FA0002E346</string>
<string>6B955A0610359EBB00FE9FE3</string>
<string>6B955A0810359EBB00FE9FE3</string>
<string>6B955A0A10359EBB00FE9FE3</string>
<string>6B955A0C10359EBB00FE9FE3</string>
<string>6B95069A103869D900213080</string>
<string>6B9506F810388A4200213080</string>
<string>6B41876A1039827000FBF4A5</string>
<string>6B41878E1039854600FBF4A5</string>
<string>6B74D091103DDCC300623975</string>
<string>6B05B00C10405F0A004A71D1</string>
<string>6B05B01010405F0A004A71D1</string>
<string>6BF26BC5104158E90099C14A</string>
<string>6BF26BC9104158E90099C14A</string>
<string>6B3314EA1042BC8200E98B50</string>
<string>6B3314EC1042BC8200E98B50</string>
<string>6B3314ED1042BC8200E98B50</string>
<string>6B3314EE1042BC8200E98B50</string>
<string>6B3314EF1042BC8200E98B50</string>
<string>6B3314F01042BC8200E98B50</string>
<string>6B3314F11042BC8200E98B50</string>
<string>6B3314F21042BC8200E98B50</string>
<string>6B3314F31042BC8200E98B50</string>
<string>6B3314F41042BC8200E98B50</string>
<string>6B3314F51042BC8200E98B50</string>
<string>6B3314F61042BC8200E98B50</string>
<string>6B3314F71042BC8200E98B50</string>
<string>6B3314F81042BC8200E98B50</string>
<string>6B3314F91042BC8200E98B50</string>
<string>6B3314FB1042BC8200E98B50</string>
<string>6B33152E1042BD3500E98B50</string>
<string>6B3315321042BE1600E98B50</string>
<string>6B3315331042BE1600E98B50</string>
<string>6B3315341042BE1600E98B50</string>
<string>6B3315351042BE1600E98B50</string>
<string>6B3315361042BE1600E98B50</string>
<string>6B3315371042BE1600E98B50</string>
<string>6B3315381042BE1600E98B50</string>
<string>6B3315391042BE1600E98B50</string>
<string>6B3315481042C02C00E98B50</string>
</array>
<key>prevStack</key>
<array>
<string>6B1E02680F924A8500CC0038</string>
<string>6B1E02FC0F92563500CC0038</string>
<string>6B1E032E0F925D9100CC0038</string>
<string>6B8DB3900F9798DE007FA9E1</string>
<string>6B458EA80FB4540500044EA9</string>
<string>6B7707B90FBD66CF00D21BAE</string>
<string>6B7707F90FBD90F100D21BAE</string>
<string>6B7708F70FBDA96300D21BAE</string>
<string>6BB787C30FC03EAD003C24DB</string>
<string>6BB788290FC0593E003C24DB</string>
<string>6BB7882A0FC0593E003C24DB</string>
<string>6BB7882B0FC0593E003C24DB</string>
<string>6BB85D3E0FCEAA6300758966</string>
<string>6BC620920FD7C2380022CACF</string>
<string>6BC745AD0FF527E50083A694</string>
<string>6BC745AE0FF527E50083A694</string>
<string>6BC745AF0FF527E50083A694</string>
<string>6B9B7DA30FF91AC600A9090F</string>
<string>6B9B7DA40FF91AC600A9090F</string>
<string>6B25B4120FFA1545004F1BC4</string>
<string>6B86333B0F7813A600E2684A</string>
<string>6B25B4080FFA13E9004F1BC4</string>
<string>6B25B6250FFA63C8004F1BC4</string>
<string>6B2AEC740FFB8AB0005BE9CC</string>
<string>6B2AEC750FFB8AB0005BE9CC</string>
<string>6B2AED930FFBA45B005BE9CC</string>
<string>6B092B1A0FFC98FF0088D3A5</string>
<string>6B092B530FFCA0A20088D3A5</string>
<string>6B092BBC0FFCEC1A0088D3A5</string>
<string>6B2AEC970FFB8AB0005BE9CC</string>
<string>6B0249051001EABD00CF7107</string>
<string>6B02498D1003751300CF7107</string>
<string>6B024A721004A2FE00CF7107</string>
<string>6B024BCF1005DFAB00CF7107</string>
@@ -442,15 +436,65 @@
<string>6B555E13100B285300247EA3</string>
<string>6B555EE0100B39A600247EA3</string>
<string>6B555EF9100B42E600247EA3</string>
<string>6B555FB4100B595C00247EA3</string>
<string>6B8AE8DF10121C6000FF1D07</string>
<string>6B8AE90D10123B5700FF1D07</string>
<string>6B8AE90E10123B5700FF1D07</string>
<string>6B8AE90F10123B5700FF1D07</string>
<string>6B8AE91010123B5700FF1D07</string>
<string>6B8AE91810123B5700FF1D07</string>
<string>6B8AE91910123B5700FF1D07</string>
<string>6BD4DBCD10145C42003FF199</string>
<string>6B93FDF010300CBE00F0C0DA</string>
<string>6B95065010383C6900213080</string>
<string>6B9506891038680900213080</string>
<string>6B9507401038910D00213080</string>
<string>6B41875F10397C9F00FBF4A5</string>
<string>6B4187C41039909100FBF4A5</string>
<string>6B418869103ACAC700FBF4A5</string>
<string>6B74D052103DD64C00623975</string>
<string>6B05AF59104042D5004A71D1</string>
<string>6B05AFA810404FAB004A71D1</string>
<string>6B05AFA910404FAB004A71D1</string>
<string>6BF26BB51041520F0099C14A</string>
<string>6BF26BD4104158E90099C14A</string>
<string>6BF26BD5104158E90099C14A</string>
<string>6BF26BD8104158E90099C14A</string>
<string>6BF26C1110415C680099C14A</string>
<string>6BF26C1210415C680099C14A</string>
<string>6B3314FD1042BC8200E98B50</string>
<string>6B3314FE1042BC8200E98B50</string>
<string>6B3314FF1042BC8200E98B50</string>
<string>6B3315001042BC8200E98B50</string>
<string>6B3315011042BC8200E98B50</string>
<string>6B3315021042BC8200E98B50</string>
<string>6B3315031042BC8200E98B50</string>
<string>6B3315041042BC8200E98B50</string>
<string>6B3315051042BC8200E98B50</string>
<string>6B3315061042BC8200E98B50</string>
<string>6B3315071042BC8200E98B50</string>
<string>6B3315081042BC8200E98B50</string>
<string>6B3315091042BC8200E98B50</string>
<string>6B33150A1042BC8200E98B50</string>
<string>6B33150B1042BC8200E98B50</string>
<string>6B33150C1042BC8200E98B50</string>
<string>6B33150D1042BC8200E98B50</string>
<string>6B33150E1042BC8200E98B50</string>
<string>6B33150F1042BC8200E98B50</string>
<string>6B3315101042BC8200E98B50</string>
<string>6B3315111042BC8200E98B50</string>
<string>6B3315121042BC8200E98B50</string>
<string>6B3315131042BC8200E98B50</string>
<string>6B3315141042BC8200E98B50</string>
<string>6B3315151042BC8200E98B50</string>
<string>6B3315161042BC8200E98B50</string>
<string>6B3315171042BC8200E98B50</string>
<string>6B3315181042BC8200E98B50</string>
<string>6B3315191042BC8200E98B50</string>
<string>6B33151A1042BC8200E98B50</string>
<string>6B33151B1042BC8200E98B50</string>
<string>6B33151C1042BC8200E98B50</string>
<string>6B3315301042BD3500E98B50</string>
<string>6B33153B1042BE1600E98B50</string>
<string>6B33153C1042BE1600E98B50</string>
<string>6B33153D1042BE1600E98B50</string>
<string>6B33153E1042BE1600E98B50</string>
<string>6B33153F1042BE1600E98B50</string>
<string>6B3315401042BE1600E98B50</string>
<string>6B3315411042BE1600E98B50</string>
<string>6B3315421042BE1600E98B50</string>
</array>
</dict>
<key>SplitCount</key>
@@ -464,18 +508,18 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {976, 522}}</string>
<string>{{0, 0}, {976, 490}}</string>
<key>RubberWindowFrame</key>
<string>0 91 1280 687 0 0 1280 778 </string>
</dict>
<key>Module</key>
<string>PBXNavigatorGroup</string>
<key>Proportion</key>
<string>522pt</string>
<string>490pt</string>
</dict>
<dict>
<key>Proportion</key>
<string>119pt</string>
<string>151pt</string>
<key>Tabs</key>
<array>
<dict>
@@ -489,9 +533,7 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{10, 27}, {976, 92}}</string>
<key>RubberWindowFrame</key>
<string>0 91 1280 687 0 0 1280 778 </string>
<string>{{10, 27}, {976, 55}}</string>
</dict>
<key>Module</key>
<string>XCDetailModule</string>
@@ -507,7 +549,7 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{10, 27}, {976, -27}}</string>
<string>{{10, 27}, {976, 172}}</string>
</dict>
<key>Module</key>
<string>PBXProjectFindModule</string>
@@ -545,7 +587,9 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{10, 27}, {976, 165}}</string>
<string>{{10, 27}, {976, 124}}</string>
<key>RubberWindowFrame</key>
<string>0 91 1280 687 0 0 1280 778 </string>
</dict>
<key>Module</key>
<string>PBXBuildResultsModule</string>
@@ -573,11 +617,11 @@
</array>
<key>TableOfContents</key>
<array>
<string>6B58CAE610198B2400956BA2</string>
<string>6B33151E1042BC8200E98B50</string>
<string>1CA23ED40692098700951B8B</string>
<string>6B58CAE710198B2400956BA2</string>
<string>6B33151F1042BC8200E98B50</string>
<string>6B8632A30F78115100E2684A</string>
<string>6B58CAE810198B2400956BA2</string>
<string>6B3315201042BC8200E98B50</string>
<string>1CA23EDF0692099D00951B8B</string>
<string>1CA23EE00692099D00951B8B</string>
<string>1CA23EE10692099D00951B8B</string>
@@ -626,12 +670,12 @@
<key>GeometryConfiguration</key>
<dict>
<key>Frame</key>
<string>{{0, 0}, {1280, 242}}</string>
<string>{{0, 0}, {1280, 356}}</string>
</dict>
<key>Module</key>
<string>PBXDebugCLIModule</string>
<key>Proportion</key>
<string>242pt</string>
<string>356pt</string>
</dict>
<dict>
<key>ContentConfiguration</key>
@@ -650,8 +694,8 @@
<string>yes</string>
<key>sizes</key>
<array>
<string>{{0, 0}, {637, 110}}</string>
<string>{{637, 0}, {643, 110}}</string>
<string>{{0, 0}, {623, 117}}</string>
<string>{{623, 0}, {657, 117}}</string>
</array>
</dict>
<key>VerticalSplitView</key>
@@ -666,8 +710,8 @@
<string>yes</string>
<key>sizes</key>
<array>
<string>{{0, 0}, {1280, 110}}</string>
<string>{{0, 110}, {1280, 289}}</string>
<string>{{0, 0}, {1280, 117}}</string>
<string>{{0, 117}, {1280, 168}}</string>
</array>
</dict>
</dict>
@@ -687,7 +731,7 @@
<key>DebugSTDIOWindowFrame</key>
<string>{{200, 200}, {500, 300}}</string>
<key>Frame</key>
<string>{{0, 247}, {1280, 399}}</string>
<string>{{0, 361}, {1280, 285}}</string>
<key>PBXDebugSessionStackFrameViewKey</key>
<dict>
<key>DebugVariablesTableConfiguration</key>
@@ -697,16 +741,16 @@
<string>Value</string>
<real>85</real>
<string>Summary</string>
<real>413</real>
<real>427</real>
</array>
<key>Frame</key>
<string>{{637, 0}, {643, 110}}</string>
<string>{{623, 0}, {657, 117}}</string>
</dict>
</dict>
<key>Module</key>
<string>PBXDebugSessionModule</string>
<key>Proportion</key>
<string>399pt</string>
<string>285pt</string>
</dict>
</array>
<key>Name</key>
@@ -724,14 +768,14 @@
</array>
<key>TableOfContents</key>
<array>
<string>6B8AE8E410121C6000FF1D07</string>
<string>6B3315211042BC8200E98B50</string>
<string>1CCC7628064C1048000F2A68</string>
<string>1CCC7629064C1048000F2A68</string>
<string>6B8AE8E510121C6000FF1D07</string>
<string>6B8AE8E610121C6000FF1D07</string>
<string>6B8AE8E710121C6000FF1D07</string>
<string>6B8AE8E810121C6000FF1D07</string>
<string>6B8AE8E910121C6000FF1D07</string>
<string>6B3315221042BC8200E98B50</string>
<string>6B3315231042BC8200E98B50</string>
<string>6B3315241042BC8200E98B50</string>
<string>6B3315251042BC8200E98B50</string>
<string>6B3315261042BC8200E98B50</string>
</array>
<key>ToolbarConfiguration</key>
<string>xcode.toolbar.config.debugV3</string>
@@ -1293,7 +1337,6 @@
<key>PBXSmartGroupTreeModuleOutlineStateSelectionKey</key>
<array>
<array>
<integer>2</integer>
<integer>0</integer>
</array>
</array>
@@ -1364,8 +1407,8 @@
<true/>
<key>TableOfContents</key>
<array>
<string>6BB788730FC05EB9003C24DB</string>
<string>6BB788740FC05EB9003C24DB</string>
<string>6B74D0B7103DE14B00623975</string>
<string>6B74D0B8103DE14B00623975</string>
<string>1CE0B1FE06471DED0097A5F4</string>
<string>1CA1AED706398EBD00589147</string>
</array>
@@ -1374,7 +1417,7 @@
<key>WindowString</key>
<string>21 346 744 409 0 0 1280 778 </string>
<key>WindowToolGUID</key>
<string>6BB788730FC05EB9003C24DB</string>
<string>6B74D0B7103DE14B00623975</string>
<key>WindowToolIsVisible</key>
<true/>
</dict>

View File

@@ -32,6 +32,7 @@
6B2AEC560FFB89E7005BE9CC /* Sample_StatMeshTiled.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B2AEC550FFB89E7005BE9CC /* Sample_StatMeshTiled.cpp */; };
6B2AEC5A0FFB8A7A005BE9CC /* DetourTileNavMesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B2AEC590FFB8A7A005BE9CC /* DetourTileNavMesh.cpp */; };
6B555DB1100B212E00247EA3 /* imguiRenderGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B555DB0100B212E00247EA3 /* imguiRenderGL.cpp */; };
6B62416A103434880002E346 /* RecastMeshDetail.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B624169103434880002E346 /* RecastMeshDetail.cpp */; };
6B8632DA0F78122C00E2684A /* SDL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B8632D90F78122C00E2684A /* SDL.framework */; };
6B8632DC0F78123E00E2684A /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B8632DB0F78123E00E2684A /* OpenGL.framework */; };
6BB788170FC0472B003C24DB /* ChunkyTriMesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6BB788160FC0472B003C24DB /* ChunkyTriMesh.cpp */; };
@@ -92,6 +93,7 @@
6B555DAE100B211D00247EA3 /* imguiRenderGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imguiRenderGL.h; path = ../../Include/imguiRenderGL.h; sourceTree = SOURCE_ROOT; };
6B555DB0100B212E00247EA3 /* imguiRenderGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imguiRenderGL.cpp; path = ../../Source/imguiRenderGL.cpp; sourceTree = SOURCE_ROOT; };
6B555DF6100B273500247EA3 /* stb_truetype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = stb_truetype.h; path = ../../Contrib/stb_truetype.h; sourceTree = SOURCE_ROOT; };
6B624169103434880002E346 /* RecastMeshDetail.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RecastMeshDetail.cpp; path = ../../../Recast/Source/RecastMeshDetail.cpp; sourceTree = SOURCE_ROOT; };
6B8632D90F78122C00E2684A /* SDL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL.framework; path = Library/Frameworks/SDL.framework; sourceTree = SDKROOT; };
6B8632DB0F78123E00E2684A /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
6BB788160FC0472B003C24DB /* ChunkyTriMesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ChunkyTriMesh.cpp; path = ../../Source/ChunkyTriMesh.cpp; sourceTree = SOURCE_ROOT; };
@@ -226,6 +228,7 @@
6B137C7F0F7FCBFE00459200 /* RecastDebugDraw.h */,
6B137C800F7FCBFE00459200 /* RecastLog.h */,
6B137C810F7FCBFE00459200 /* RecastTimer.h */,
6B624169103434880002E346 /* RecastMeshDetail.cpp */,
);
name = Recast;
sourceTree = "<group>";
@@ -351,6 +354,7 @@
6B1185F51006895B0018F96F /* DetourNode.cpp in Sources */,
6B1185FE10068B150018F96F /* DetourCommon.cpp in Sources */,
6B555DB1100B212E00247EA3 /* imguiRenderGL.cpp in Sources */,
6B62416A103434880002E346 /* RecastMeshDetail.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -23,6 +23,8 @@ protected:
float m_edgeMaxLen;
float m_edgeMaxError;
float m_vertsPerPoly;
float m_detailSampleDist;
float m_detailSampleMaxError;
public:
Sample();

View File

@@ -17,8 +17,9 @@ protected:
rcHeightfield* m_solid;
rcCompactHeightfield* m_chf;
rcContourSet* m_cset;
rcPolyMesh* m_polyMesh;
rcPolyMesh* m_pmesh;
rcConfig m_cfg;
rcPolyMeshDetail* m_dmesh;
enum DrawMode
{
@@ -37,6 +38,7 @@ protected:
DRAWMODE_BOTH_CONTOURS,
DRAWMODE_CONTOURS,
DRAWMODE_POLYMESH,
DRAWMODE_POLYMESH_DETAIL,
MAX_DRAWMODE
};

View File

@@ -13,11 +13,13 @@ protected:
struct Tile
{
inline Tile() : chf(0), solid(0), cset(0), buildTime(0) {}
inline ~Tile() { delete chf; delete cset; delete solid; }
inline Tile() : chf(0), solid(0), cset(0), pmesh(0), dmesh(0), buildTime(0) {}
inline ~Tile() { delete chf; delete cset; delete solid; delete pmesh; delete dmesh; }
rcCompactHeightfield* chf;
rcHeightfield* solid;
rcContourSet* cset;
rcPolyMesh* pmesh;
rcPolyMeshDetail* dmesh;
int buildTime;
};
@@ -37,7 +39,8 @@ protected:
rcBuildTimes m_buildTimes;
rcChunkyTriMesh* m_chunkyMesh;
rcPolyMesh* m_polyMesh;
rcPolyMesh* m_pmesh;
rcPolyMeshDetail* m_dmesh;
rcConfig m_cfg;
TileSet* m_tileSet;
@@ -65,6 +68,7 @@ protected:
DRAWMODE_BOTH_CONTOURS,
DRAWMODE_CONTOURS,
DRAWMODE_POLYMESH,
DRAWMODE_POLYMESH_DETAIL,
MAX_DRAWMODE
};

View File

@@ -38,7 +38,8 @@ protected:
rcHeightfield* m_solid;
rcCompactHeightfield* m_chf;
rcContourSet* m_cset;
rcPolyMesh* m_polyMesh;
rcPolyMesh* m_pmesh;
rcPolyMeshDetail* m_dmesh;
rcConfig m_cfg;
float m_tileSize;

View File

@@ -74,6 +74,8 @@ void Sample::resetCommonSettings()
m_edgeMaxLen = 12.0f;
m_edgeMaxError = 1.3f;
m_vertsPerPoly = 6.0f;
m_detailSampleDist = 6.0f;
m_detailSampleMaxError = 1.0f;
}
void Sample::handleCommonSettings()
@@ -106,6 +108,11 @@ void Sample::handleCommonSettings()
imguiSlider("Max Edge Error", &m_edgeMaxError, 0.1f, 3.0f, 0.1f);
imguiSlider("Verts Per Poly", &m_vertsPerPoly, 3.0f, 12.0f, 1.0f);
imguiSeparator();
imguiLabel("Detail Mesh");
imguiSlider("Sample Distance", &m_detailSampleDist, 0.0f, 16.0f, 1.0f);
imguiSlider("Max Sample Error", &m_detailSampleMaxError, 0.0f, 16.0f, 1.0f);
imguiSeparator();
}

View File

@@ -219,14 +219,14 @@ void Sample_StatMesh::toolRender(int flags)
}
if (m_nstraightPath)
{
glColor4ub(128,16,0,220);
glColor4ub(64,16,0,220);
glLineWidth(3.0f);
glBegin(GL_LINE_STRIP);
for (int i = 0; i < m_nstraightPath; ++i)
glVertex3f(m_straightPath[i*3], m_straightPath[i*3+1]+0.4f, m_straightPath[i*3+2]);
glEnd();
glLineWidth(1.0f);
glPointSize(4.0f);
glPointSize(6.0f);
glBegin(GL_POINTS);
for (int i = 0; i < m_nstraightPath; ++i)
glVertex3f(m_straightPath[i*3], m_straightPath[i*3+1]+0.4f, m_straightPath[i*3+2]);
@@ -243,7 +243,7 @@ void Sample_StatMesh::toolRender(int flags)
for (int i = 1; i < m_npolys; ++i)
dtDebugDrawStatNavMeshPoly(m_navMesh, m_polys[i], pathCol);
glColor4ub(128,16,0,220);
glColor4ub(64,16,0,220);
glLineWidth(3.0f);
glBegin(GL_LINE_STRIP);
for (int i = 0; i < m_nstraightPath; ++i)

View File

@@ -25,7 +25,8 @@ Sample_StatMeshSimple::Sample_StatMeshSimple() :
m_solid(0),
m_chf(0),
m_cset(0),
m_polyMesh(0),
m_pmesh(0),
m_dmesh(0),
m_drawMode(DRAWMODE_NAVMESH)
{
}
@@ -45,8 +46,10 @@ void Sample_StatMeshSimple::cleanup()
m_chf = 0;
delete m_cset;
m_cset = 0;
delete m_polyMesh;
m_polyMesh = 0;
delete m_pmesh;
m_pmesh = 0;
delete m_dmesh;
m_dmesh = 0;
toolCleanup();
}
@@ -83,7 +86,8 @@ void Sample_StatMeshSimple::handleDebugMode()
valid[DRAWMODE_RAW_CONTOURS] = m_cset != 0;
valid[DRAWMODE_BOTH_CONTOURS] = m_cset != 0;
valid[DRAWMODE_CONTOURS] = m_cset != 0;
valid[DRAWMODE_POLYMESH] = m_polyMesh != 0;
valid[DRAWMODE_POLYMESH] = m_pmesh != 0;
valid[DRAWMODE_POLYMESH_DETAIL] = m_dmesh != 0;
}
int unavail = 0;
@@ -124,6 +128,8 @@ void Sample_StatMeshSimple::handleDebugMode()
m_drawMode = DRAWMODE_CONTOURS;
if (imguiCheck("Poly Mesh", m_drawMode == DRAWMODE_POLYMESH, valid[DRAWMODE_POLYMESH]))
m_drawMode = DRAWMODE_POLYMESH;
if (imguiCheck("Poly Mesh Detail", m_drawMode == DRAWMODE_POLYMESH_DETAIL, valid[DRAWMODE_POLYMESH_DETAIL]))
m_drawMode = DRAWMODE_POLYMESH_DETAIL;
if (unavail)
{
@@ -198,20 +204,20 @@ void Sample_StatMeshSimple::handleRender()
if (m_cset && m_drawMode == DRAWMODE_RAW_CONTOURS)
{
glDepthMask(GL_FALSE);
rcDebugDrawRawContours(*m_cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch);
rcDebugDrawRawContours(*m_cset);
glDepthMask(GL_TRUE);
}
if (m_cset && m_drawMode == DRAWMODE_BOTH_CONTOURS)
{
glDepthMask(GL_FALSE);
rcDebugDrawRawContours(*m_cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch, 0.5f);
rcDebugDrawContours(*m_cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch);
rcDebugDrawRawContours(*m_cset, 0.5f);
rcDebugDrawContours(*m_cset);
glDepthMask(GL_TRUE);
}
if (m_cset && m_drawMode == DRAWMODE_CONTOURS)
{
glDepthMask(GL_FALSE);
rcDebugDrawContours(*m_cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch);
rcDebugDrawContours(*m_cset);
glDepthMask(GL_TRUE);
}
if (m_chf && m_cset && m_drawMode == DRAWMODE_REGION_CONNECTIONS)
@@ -219,16 +225,22 @@ void Sample_StatMeshSimple::handleRender()
rcDebugDrawCompactHeightfieldRegions(*m_chf);
glDepthMask(GL_FALSE);
rcDebugDrawRegionConnections(*m_cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch);
rcDebugDrawRegionConnections(*m_cset);
glDepthMask(GL_TRUE);
}
if (m_polyMesh && m_drawMode == DRAWMODE_POLYMESH)
if (m_pmesh && m_drawMode == DRAWMODE_POLYMESH)
{
glDepthMask(GL_FALSE);
rcDebugDrawPolyMesh(*m_polyMesh);
rcDebugDrawPolyMesh(*m_pmesh);
glDepthMask(GL_TRUE);
}
if (m_dmesh && m_drawMode == DRAWMODE_POLYMESH_DETAIL)
{
glDepthMask(GL_FALSE);
rcDebugDrawPolyMeshDetail(*m_dmesh);
glDepthMask(GL_TRUE);
}
static const float startCol[4] = { 0.5f, 0.1f, 0.0f, 0.75f };
static const float endCol[4] = { 0.2f, 0.4f, 0.0f, 0.75f };
if (m_sposSet)
@@ -236,6 +248,7 @@ void Sample_StatMeshSimple::handleRender()
if (m_eposSet)
drawAgent(m_epos, m_agentRadius, m_agentHeight, m_agentMaxClimb, endCol);
glDepthMask(GL_TRUE);
}
void Sample_StatMeshSimple::handleRenderOverlay(double* proj, double* model, int* view)
@@ -281,6 +294,8 @@ bool Sample_StatMeshSimple::handleBuild()
m_cfg.minRegionSize = (int)rcSqr(m_regionMinSize);
m_cfg.mergeRegionSize = (int)rcSqr(m_regionMergeSize);
m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly;
m_cfg.detailSampleDist = m_detailSampleDist < 0.9f ? 0 : m_cellSize * m_detailSampleDist;
m_cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError;
// Set the area where the navigation will be build.
// Here the bounds of the input mesh are used, but the
@@ -417,42 +432,56 @@ bool Sample_StatMeshSimple::handleBuild()
return false;
}
if (!m_keepInterResults)
{
delete m_chf;
m_chf = 0;
}
//
// Step 6. Build polygons mesh from contours.
//
// Build polygon navmesh from the contours.
m_polyMesh = new rcPolyMesh;
if (!m_polyMesh)
m_pmesh = new rcPolyMesh;
if (!m_pmesh)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'polyMesh'.");
rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmesh'.");
return false;
}
if (!rcBuildPolyMesh(*m_cset, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch, m_cfg.maxVertsPerPoly, *m_polyMesh))
if (!rcBuildPolyMesh(*m_cset, m_cfg.maxVertsPerPoly, *m_pmesh))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not triangulate contours.");
return false;
}
//
// Step 7. Create detail mesh which allows to access approximate height on each polygon.
//
m_dmesh = new rcPolyMeshDetail;
if (!m_dmesh)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmdtl'.");
return false;
}
if (!rcBuildPolyMeshDetail(*m_pmesh, *m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, *m_dmesh))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not build detail mesh.");
}
if (!m_keepInterResults)
{
delete m_chf;
m_chf = 0;
delete m_cset;
m_cset = 0;
}
// At this point the navigation mesh data is ready, you can access it from m_polyMesh.
// At this point the navigation mesh data is ready, you can access it from m_pmesh.
// See rcDebugDrawPolyMesh or dtCreateNavMeshData as examples how to access the data.
//
// (Optional) Step 7. Create Detour data from detour poly mesh.
// (Optional) Step 8. Create Detour data from Recast poly mesh.
//
// The GUI may allow more max points per polygon than Detour can handle.
@@ -461,9 +490,11 @@ bool Sample_StatMeshSimple::handleBuild()
{
unsigned char* navData = 0;
int navDataSize = 0;
if (!dtCreateNavMeshData(m_polyMesh->verts, m_polyMesh->nverts,
m_polyMesh->polys, m_polyMesh->npolys, m_polyMesh->nvp,
m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch, &navData, &navDataSize))
if (!dtCreateNavMeshData(m_pmesh->verts, m_pmesh->nverts,
m_pmesh->polys, m_pmesh->npolys, m_pmesh->nvp,
m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch,
m_dmesh->meshes, m_dmesh->verts, m_dmesh->nverts, m_dmesh->tris, m_dmesh->ntris,
&navData, &navDataSize))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "Could not build Detour navmesh.");
@@ -517,11 +548,10 @@ bool Sample_StatMeshSimple::handleBuild()
rcGetLog()->log(RC_LOG_PROGRESS, " - trace: %.1fms (%.1f%%)", m_buildTimes.buildContoursTrace/1000.0f, m_buildTimes.buildContoursTrace*pc);
rcGetLog()->log(RC_LOG_PROGRESS, " - simplify: %.1fms (%.1f%%)", m_buildTimes.buildContoursSimplify/1000.0f, m_buildTimes.buildContoursSimplify*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Fixup contours: %.1fms (%.1f%%)", m_buildTimes.fixupContours/1000.0f, m_buildTimes.fixupContours*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Build Polymesh: %.1fms (%.1f%%)", m_buildTimes.buildPolymesh/1000.0f, m_buildTimes.buildPolymesh*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Build Polymesh Detail: %.1fms (%.1f%%)", m_buildTimes.buildDetailMesh/1000.0f, m_buildTimes.buildDetailMesh*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Polymesh: Verts:%d Polys:%d", m_polyMesh->nverts, m_polyMesh->npolys);
rcGetLog()->log(RC_LOG_PROGRESS, "Polymesh: Verts:%d Polys:%d", m_pmesh->nverts, m_pmesh->npolys);
rcGetLog()->log(RC_LOG_PROGRESS, "TOTAL: %.1fms", rcGetDeltaTimeUsec(totStartTime, totEndTime)/1000.0f);
}

View File

@@ -23,7 +23,8 @@ Sample_StatMeshTiled::Sample_StatMeshTiled() :
m_keepInterResults(false),
m_tileSize(64),
m_chunkyMesh(0),
m_polyMesh(0),
m_pmesh(0),
m_dmesh(0),
m_tileSet(0),
m_statPolysPerTileSamples(0),
m_statTimePerTileSamples(0),
@@ -42,8 +43,10 @@ void Sample_StatMeshTiled::cleanup()
m_chunkyMesh = 0;
delete m_tileSet;
m_tileSet = 0;
delete m_polyMesh;
m_polyMesh = 0;
delete m_pmesh;
m_pmesh = 0;
delete m_dmesh;
m_dmesh = 0;
toolCleanup();
m_statTimePerTileSamples = 0;
m_statPolysPerTileSamples = 0;
@@ -84,6 +87,8 @@ void Sample_StatMeshTiled::handleDebugMode()
bool hasChf = false;
bool hasSolid = false;
bool hasCset = false;
bool hasPmesh = false;
bool hasDmesh = false;
if (m_tileSet)
{
for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i)
@@ -91,8 +96,12 @@ void Sample_StatMeshTiled::handleDebugMode()
if (m_tileSet->tiles[i].solid) hasSolid = true;
if (m_tileSet->tiles[i].chf) hasChf = true;
if (m_tileSet->tiles[i].cset) hasCset = true;
if (m_tileSet->tiles[i].pmesh) hasPmesh = true;
if (m_tileSet->tiles[i].dmesh) hasDmesh = true;
}
}
if (m_pmesh) hasPmesh = true;
if (m_dmesh) hasDmesh = true;
if (m_verts && m_tris)
{
@@ -110,7 +119,8 @@ void Sample_StatMeshTiled::handleDebugMode()
valid[DRAWMODE_RAW_CONTOURS] = hasCset;
valid[DRAWMODE_BOTH_CONTOURS] = hasCset;
valid[DRAWMODE_CONTOURS] = hasCset;
valid[DRAWMODE_POLYMESH] = m_polyMesh != 0;
valid[DRAWMODE_POLYMESH] = hasPmesh;
valid[DRAWMODE_POLYMESH_DETAIL] = hasDmesh;
}
int unavail = 0;
@@ -151,6 +161,8 @@ void Sample_StatMeshTiled::handleDebugMode()
m_drawMode = DRAWMODE_CONTOURS;
if (imguiCheck("Poly Mesh", m_drawMode == DRAWMODE_POLYMESH, valid[DRAWMODE_POLYMESH]))
m_drawMode = DRAWMODE_POLYMESH;
if (imguiCheck("Poly Mesh Detail", m_drawMode == DRAWMODE_POLYMESH_DETAIL, valid[DRAWMODE_POLYMESH_DETAIL]))
m_drawMode = DRAWMODE_POLYMESH_DETAIL;
if (unavail)
{
@@ -295,7 +307,7 @@ void Sample_StatMeshTiled::handleRender()
for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i)
{
if (m_tileSet->tiles[i].cset)
rcDebugDrawRawContours(*m_tileSet->tiles[i].cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch);
rcDebugDrawRawContours(*m_tileSet->tiles[i].cset);
}
glDepthMask(GL_TRUE);
}
@@ -306,8 +318,8 @@ void Sample_StatMeshTiled::handleRender()
{
if (m_tileSet->tiles[i].cset)
{
rcDebugDrawRawContours(*m_tileSet->tiles[i].cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch, 0.5f);
rcDebugDrawContours(*m_tileSet->tiles[i].cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch);
rcDebugDrawRawContours(*m_tileSet->tiles[i].cset, 0.5f);
rcDebugDrawContours(*m_tileSet->tiles[i].cset);
}
}
glDepthMask(GL_TRUE);
@@ -318,7 +330,7 @@ void Sample_StatMeshTiled::handleRender()
for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i)
{
if (m_tileSet->tiles[i].cset)
rcDebugDrawContours(*m_tileSet->tiles[i].cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch);
rcDebugDrawContours(*m_tileSet->tiles[i].cset);
}
glDepthMask(GL_TRUE);
}
@@ -334,14 +346,43 @@ void Sample_StatMeshTiled::handleRender()
for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i)
{
if (m_tileSet->tiles[i].cset)
rcDebugDrawRegionConnections(*m_tileSet->tiles[i].cset, m_cfg.bmin, m_cfg.cs, m_cfg.ch);
rcDebugDrawRegionConnections(*m_tileSet->tiles[i].cset);
}
glDepthMask(GL_TRUE);
}
if (m_polyMesh && m_drawMode == DRAWMODE_POLYMESH)
if (/*m_pmesh &&*/ m_drawMode == DRAWMODE_POLYMESH)
{
glDepthMask(GL_FALSE);
rcDebugDrawPolyMesh(*m_polyMesh);
if (m_pmesh)
{
rcDebugDrawPolyMesh(*m_pmesh);
}
else
{
for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i)
{
if (m_tileSet->tiles[i].pmesh)
rcDebugDrawPolyMesh(*m_tileSet->tiles[i].pmesh);
}
}
glDepthMask(GL_TRUE);
}
if (/*m_dmesh &&*/ m_drawMode == DRAWMODE_POLYMESH_DETAIL)
{
glDepthMask(GL_FALSE);
if (m_dmesh)
{
rcDebugDrawPolyMeshDetail(*m_dmesh);
}
else
{
for (int i = 0; i < m_tileSet->width*m_tileSet->height; ++i)
{
if (m_tileSet->tiles[i].dmesh)
rcDebugDrawPolyMeshDetail(*m_tileSet->tiles[i].dmesh);
}
}
glDepthMask(GL_TRUE);
}
}
@@ -535,6 +576,8 @@ bool Sample_StatMeshTiled::handleBuild()
m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly;
m_cfg.tileSize = (int)m_tileSize;
m_cfg.borderSize = m_cfg.walkableRadius*2 + 2; // Reserve enough padding.
m_cfg.detailSampleDist = m_detailSampleDist < 0.9f ? 0 : m_cellSize * m_detailSampleDist;
m_cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError;
// Set the area where the navigation will be build.
// Here the bounds of the input mesh are used, but the
@@ -719,14 +762,6 @@ bool Sample_StatMeshTiled::handleBuild()
continue;
}
if (m_keepInterResults)
{
tile.solid = solid;
solid = 0;
tile.chf = chf;
chf = 0;
}
if (!cset->nconts)
{
delete cset;
@@ -734,12 +769,63 @@ bool Sample_StatMeshTiled::handleBuild()
continue;
}
tile.cset = cset;
// Offset the vertices in the cset.
rcTranslateContours(tile.cset, x*tileCfg.tileSize - tileCfg.borderSize, 0, y*tileCfg.tileSize - tileCfg.borderSize);
tile.pmesh = new rcPolyMesh;
if (!tile.pmesh)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Out of memory 'pmesh'.", x, y);
continue;
}
if (!rcBuildPolyMesh(*cset, tileCfg.maxVertsPerPoly, *tile.pmesh))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Could not create poly mesh.", x, y);
continue;
}
tile.dmesh = new rcPolyMeshDetail;
if (!tile.dmesh)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Out of memory 'dmesh'.", x, y);
continue;
}
if (!rcBuildPolyMeshDetail(*tile.pmesh, *chf, tileCfg.detailSampleDist, tileCfg .detailSampleMaxError, *tile.dmesh))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Could not build detail mesh.", x, y);
continue;
}
if (m_keepInterResults)
{
tile.solid = solid;
solid = 0;
tile.chf = chf;
chf = 0;
tile.cset = cset;
cset = 0;
}
rcTimeVal endTime = rcGetPerformanceTimer();
tile.buildTime += rcGetDeltaTimeUsec(startTime, endTime);
// Some extra code to measure some per tile statistics,
// such as build time and how many polygons there are per tile.
if (tile.pmesh)
{
int bucket = tile.pmesh->npolys;
if (bucket < 0) bucket = 0;
if (bucket >= MAX_STAT_BUCKETS) bucket = MAX_STAT_BUCKETS-1;
m_statPolysPerTile[bucket]++;
m_statPolysPerTileSamples++;
}
int bucket = (tile.buildTime+500)/1000;
if (bucket < 0) bucket = 0;
if (bucket >= MAX_STAT_BUCKETS) bucket = MAX_STAT_BUCKETS-1;
m_statTimePerTile[bucket]++;
m_statTimePerTileSamples++;
}
}
@@ -747,143 +833,58 @@ bool Sample_StatMeshTiled::handleBuild()
delete solid;
delete chf;
// Some extra code to measure some per tile statistics,
// such as build time and how many polygons there are per tile.
if (m_measurePerTileTimings)
// Merge per tile poly and detail meshes.
rcPolyMesh** pmmerge = new rcPolyMesh*[m_tileSet->width*m_tileSet->height];
if (!pmmerge)
{
for (int y = 0; y < m_tileSet->height; ++y)
{
for (int x = 0; x < m_tileSet->width; ++x)
{
Tile& tile = m_tileSet->tiles[x + y*m_tileSet->width];
if (!tile.cset)
continue;
rcTimeVal startTime = rcGetPerformanceTimer();
rcPolyMesh* polyMesh = new rcPolyMesh;
if (!polyMesh)
continue;
if (rcBuildPolyMesh(*tile.cset, m_cfg.bmin, m_cfg.bmax,
m_cfg.cs, m_cfg.ch, m_cfg.maxVertsPerPoly, *polyMesh))
{
int bucket = polyMesh->npolys;
if (bucket < 0) bucket = 0;
if (bucket >= MAX_STAT_BUCKETS) bucket = MAX_STAT_BUCKETS-1;
m_statPolysPerTile[bucket]++;
m_statPolysPerTileSamples++;
}
delete polyMesh;
rcTimeVal endTime = rcGetPerformanceTimer();
int time = tile.buildTime += rcGetDeltaTimeUsec(startTime, endTime);
int bucket = (time+500)/1000;
if (bucket < 0) bucket = 0;
if (bucket >= MAX_STAT_BUCKETS) bucket = MAX_STAT_BUCKETS-1;
m_statTimePerTile[bucket]++;
m_statTimePerTileSamples++;
}
}
}
// Make sure that the vertices along the tile edges match,
// so that they can be later properly stitched together.
for (int y = 0; y < m_tileSet->height; ++y)
{
for (int x = 0; x < m_tileSet->width; ++x)
{
rcTimeVal startTime = rcGetPerformanceTimer();
if ((x+1) < m_tileSet->width)
{
if (!rcFixupAdjacentContours(m_tileSet->tiles[x + y*m_tileSet->width].cset,
m_tileSet->tiles[x+1 + y*m_tileSet->width].cset,
m_cfg.walkableClimb, (x+1)*m_cfg.tileSize, -1))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Could not fixup x+1.", x, y);
return false;
}
}
if ((y+1) < m_tileSet->height)
{
if (!rcFixupAdjacentContours(m_tileSet->tiles[x + y*m_tileSet->width].cset,
m_tileSet->tiles[x + (y+1)*m_tileSet->width].cset,
m_cfg.walkableClimb, -1, (y+1)*m_cfg.tileSize))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: [%d,%d] Could not fixup y+1.", x, y);
return false;
}
}
rcTimeVal endTime = rcGetPerformanceTimer();
m_tileSet->tiles[x+y*m_tileSet->width].buildTime += rcGetDeltaTimeUsec(startTime, endTime);
}
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: Out of memory 'pmmerge' (%d).", m_tileSet->width*m_tileSet->height);
return false;
}
// Combine contours.
rcContourSet combSet;
combSet.nconts = 0;
rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[m_tileSet->width*m_tileSet->height];
if (!dmmerge)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: Out of memory 'dmmerge' (%d).", m_tileSet->width*m_tileSet->height);
return false;
}
int nmerge = 0;
for (int y = 0; y < m_tileSet->height; ++y)
{
for (int x = 0; x < m_tileSet->width; ++x)
{
Tile& tile = m_tileSet->tiles[x + y*m_tileSet->width];
if (!tile.cset) continue;
combSet.nconts += tile.cset->nconts;
}
}
combSet.conts = new rcContour[combSet.nconts];
if (!combSet.conts)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: Out of memory 'combSet.conts' (%d).", combSet.nconts);
return false;
}
int n = 0;
for (int y = 0; y < m_tileSet->height; ++y)
{
for (int x = 0; x < m_tileSet->width; ++x)
{
Tile& tile = m_tileSet->tiles[x + y*m_tileSet->width];
if (!tile.cset) continue;
for (int i = 0; i < tile.cset->nconts; ++i)
if (tile.pmesh)
{
combSet.conts[n].verts = tile.cset->conts[i].verts;
combSet.conts[n].nverts = tile.cset->conts[i].nverts;
combSet.conts[n].reg = tile.cset->conts[i].reg;
n++;
pmmerge[nmerge] = tile.pmesh;
dmmerge[nmerge] = tile.dmesh;
nmerge++;
}
}
}
m_polyMesh = new rcPolyMesh;
if (!m_polyMesh)
m_pmesh = new rcPolyMesh;
if (!m_pmesh)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'polyMesh'.");
rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmesh'.");
return false;
}
rcMergePolyMeshes(pmmerge, nmerge, *m_pmesh);
bool polyRes = rcBuildPolyMesh(combSet, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch, m_cfg.maxVertsPerPoly, *m_polyMesh);
// Remove vertex binding to avoid double deletion.
for (int i = 0; i < combSet.nconts; ++i)
m_dmesh = new rcPolyMeshDetail;
if (!m_dmesh)
{
combSet.conts[i].verts = 0;
combSet.conts[i].nverts = 0;
}
if (!polyRes)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildTiledNavigation: Could not triangulate contours.");
rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'dmesh'.");
return false;
}
rcMergePolyMeshDetails(dmmerge, nmerge, *m_dmesh);
delete [] pmmerge;
delete [] dmmerge;
if (!m_keepInterResults)
{
@@ -894,17 +895,23 @@ bool Sample_StatMeshTiled::handleBuild()
Tile& tile = m_tileSet->tiles[x + y*m_tileSet->width];
delete tile.cset;
tile.cset = 0;
delete tile.pmesh;
tile.pmesh = 0;
delete tile.dmesh;
tile.dmesh = 0;
}
}
}
if (m_cfg.maxVertsPerPoly <= DT_STAT_VERTS_PER_POLYGON)
if (m_pmesh && m_cfg.maxVertsPerPoly <= DT_STAT_VERTS_PER_POLYGON)
{
unsigned char* navData = 0;
int navDataSize = 0;
if (!dtCreateNavMeshData(m_polyMesh->verts, m_polyMesh->nverts,
m_polyMesh->polys, m_polyMesh->npolys, m_polyMesh->nvp,
m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch, &navData, &navDataSize))
if (!dtCreateNavMeshData(m_pmesh->verts, m_pmesh->nverts,
m_pmesh->polys, m_pmesh->npolys, m_pmesh->nvp,
m_pmesh->bmin, m_pmesh->bmax, m_pmesh->cs, m_pmesh->ch,
m_dmesh->meshes, m_dmesh->verts, m_dmesh->nverts, m_dmesh->tris, m_dmesh->ntris,
&navData, &navDataSize))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "Could not build Detour navmesh.");
@@ -958,11 +965,13 @@ bool Sample_StatMeshTiled::handleBuild()
rcGetLog()->log(RC_LOG_PROGRESS, " - trace: %.1fms (%.1f%%)", m_buildTimes.buildContoursTrace/1000.0f, m_buildTimes.buildContoursTrace*pc);
rcGetLog()->log(RC_LOG_PROGRESS, " - simplify: %.1fms (%.1f%%)", m_buildTimes.buildContoursSimplify/1000.0f, m_buildTimes.buildContoursSimplify*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Fixup contours: %.1fms (%.1f%%)", m_buildTimes.fixupContours/1000.0f, m_buildTimes.fixupContours*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Build Polymesh: %.1fms (%.1f%%)", m_buildTimes.buildPolymesh/1000.0f, m_buildTimes.buildPolymesh*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Build Polymesh Detail: %.1fms (%.1f%%)", m_buildTimes.buildDetailMesh/1000.0f, m_buildTimes.buildDetailMesh*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Merge Polymeshes: %.1fms (%.1f%%)", m_buildTimes.mergePolyMesh/1000.0f, m_buildTimes.mergePolyMesh*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Merge Polymesh Details: %.1fms (%.1f%%)", m_buildTimes.mergePolyMeshDetail/1000.0f, m_buildTimes.mergePolyMeshDetail*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Polymesh: Verts:%d Polys:%d", m_polyMesh->nverts, m_polyMesh->npolys);
if (m_pmesh)
rcGetLog()->log(RC_LOG_PROGRESS, "Polymesh: Verts:%d Polys:%d", m_pmesh->nverts, m_pmesh->npolys);
rcGetLog()->log(RC_LOG_PROGRESS, "TOTAL: %.1fms", rcGetDeltaTimeUsec(totStartTime, totEndTime)/1000.0f);
}

View File

@@ -45,7 +45,8 @@ Sample_TileMesh::Sample_TileMesh() :
m_solid(0),
m_chf(0),
m_cset(0),
m_polyMesh(0),
m_pmesh(0),
m_dmesh(0),
m_tileSize(32),
m_sposSet(false),
m_eposSet(false),
@@ -84,8 +85,10 @@ void Sample_TileMesh::cleanup()
m_chf = 0;
delete m_cset;
m_cset = 0;
delete m_polyMesh;
m_polyMesh = 0;
delete m_pmesh;
m_pmesh = 0;
delete m_dmesh;
m_dmesh = 0;
}
void Sample_TileMesh::handleSettings()
@@ -299,28 +302,32 @@ void Sample_TileMesh::handleRender()
if (m_sposSet)
{
const float s = 0.5f;
glColor4ub(128,0,0,255);
glColor4ub(64,16,0,255);
glLineWidth(3.0f);
glBegin(GL_LINES);
glVertex3f(m_spos[0]-s,m_spos[1],m_spos[2]);
glVertex3f(m_spos[0]+s,m_spos[1],m_spos[2]);
glVertex3f(m_spos[0],m_spos[1]-s,m_spos[2]);
glVertex3f(m_spos[0],m_spos[1]+s,m_spos[2]);
glVertex3f(m_spos[0],m_spos[1],m_spos[2]-s);
glVertex3f(m_spos[0],m_spos[1],m_spos[2]+s);
glVertex3f(m_spos[0]-s,m_spos[1]+m_cellHeight,m_spos[2]);
glVertex3f(m_spos[0]+s,m_spos[1]+m_cellHeight,m_spos[2]);
glVertex3f(m_spos[0],m_spos[1]-s+m_cellHeight,m_spos[2]);
glVertex3f(m_spos[0],m_spos[1]+s+m_cellHeight,m_spos[2]);
glVertex3f(m_spos[0],m_spos[1]+m_cellHeight,m_spos[2]-s);
glVertex3f(m_spos[0],m_spos[1]+m_cellHeight,m_spos[2]+s);
glEnd();
glLineWidth(1.0f);
}
if (m_eposSet)
{
const float s = 0.5f;
glColor4ub(0,128,0,255);
glColor4ub(16,64,0,255);
glLineWidth(3.0f);
glBegin(GL_LINES);
glVertex3f(m_epos[0]-s,m_epos[1],m_epos[2]);
glVertex3f(m_epos[0]+s,m_epos[1],m_epos[2]);
glVertex3f(m_epos[0],m_epos[1]-s,m_epos[2]);
glVertex3f(m_epos[0],m_epos[1]+s,m_epos[2]);
glVertex3f(m_epos[0],m_epos[1],m_epos[2]-s);
glVertex3f(m_epos[0],m_epos[1],m_epos[2]+s);
glVertex3f(m_epos[0]-s,m_epos[1]+m_cellHeight,m_epos[2]);
glVertex3f(m_epos[0]+s,m_epos[1]+m_cellHeight,m_epos[2]);
glVertex3f(m_epos[0],m_epos[1]-s+m_cellHeight,m_epos[2]);
glVertex3f(m_epos[0],m_epos[1]+s+m_cellHeight,m_epos[2]);
glVertex3f(m_epos[0],m_epos[1]+m_cellHeight,m_epos[2]-s);
glVertex3f(m_epos[0],m_epos[1]+m_cellHeight,m_epos[2]+s);
glEnd();
glLineWidth(1.0f);
}
static const float startCol[4] = { 0.5f, 0.1f, 0.0f, 0.75f };
@@ -339,7 +346,7 @@ void Sample_TileMesh::handleRender()
}
if (m_nstraightPath)
{
glColor4ub(128,16,0,220);
glColor4ub(64,16,0,220);
glLineWidth(3.0f);
glBegin(GL_LINE_STRIP);
for (int i = 0; i < m_nstraightPath; ++i)
@@ -363,7 +370,7 @@ void Sample_TileMesh::handleRender()
for (int i = 1; i < m_npolys; ++i)
dtDebugDrawTiledNavMeshPoly(m_navMesh, m_polys[i], pathCol);
glColor4ub(128,16,0,220);
glColor4ub(64,16,0,220);
glLineWidth(3.0f);
glBegin(GL_LINE_STRIP);
for (int i = 0; i < m_nstraightPath; ++i)
@@ -607,11 +614,8 @@ unsigned char* Sample_TileMesh::buildTileMesh(const float* bmin, const float* bm
m_cfg.borderSize = m_cfg.walkableRadius*2 + 2; // Reserve enough padding.
m_cfg.width = m_cfg.tileSize + m_cfg.borderSize*2;
m_cfg.height = m_cfg.tileSize + m_cfg.borderSize*2;
/* if (m_cfg.maxVertsPerPoly == DT_VERTS_PER_POLYGON)
m_drawMode = DRAWMODE_NAVMESH;
else
m_drawMode = DRAWMODE_POLYMESH;*/
m_cfg.detailSampleDist = m_detailSampleDist < 0.9f ? 0 : m_cellSize * m_detailSampleDist;
m_cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError;
vcopy(m_cfg.bmin, bmin);
vcopy(m_cfg.bmax, bmax);
@@ -754,29 +758,43 @@ unsigned char* Sample_TileMesh::buildTileMesh(const float* bmin, const float* bm
return 0;
}
if (!m_keepInterResults)
{
delete m_chf;
m_chf = 0;
}
// Build polygon navmesh from the contours.
m_polyMesh = new rcPolyMesh;
if (!m_polyMesh)
m_pmesh = new rcPolyMesh;
if (!m_pmesh)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'polyMesh'.");
rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmesh'.");
return 0;
}
if (!rcBuildPolyMesh(*m_cset, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch, m_cfg.maxVertsPerPoly, *m_polyMesh))
if (!rcBuildPolyMesh(*m_cset, m_cfg.maxVertsPerPoly, *m_pmesh))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could not triangulate contours.");
return 0;
}
// Build detail mesh.
m_dmesh = new rcPolyMeshDetail;
if (!m_dmesh)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'dmesh'.");
return 0;
}
if (!rcBuildPolyMeshDetail(*m_pmesh, *m_chf,
m_cfg.detailSampleDist, m_cfg.detailSampleMaxError,
*m_dmesh))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "buildNavigation: Could build polymesh detail.");
return 0;
}
if (!m_keepInterResults)
{
delete m_chf;
m_chf = 0;
delete m_cset;
m_cset = 0;
}
@@ -785,16 +803,17 @@ unsigned char* Sample_TileMesh::buildTileMesh(const float* bmin, const float* bm
int navDataSize = 0;
if (m_cfg.maxVertsPerPoly == DT_TILE_VERTS_PER_POLYGON)
{
// Remove padding from the polymesh data.
for (int i = 0; i < m_polyMesh->nverts; ++i)
// Remove padding from the polymesh data. TODO: Remove this odditity.
for (int i = 0; i < m_pmesh->nverts; ++i)
{
unsigned short* v = &m_polyMesh->verts[i*3];
unsigned short* v = &m_pmesh->verts[i*3];
v[0] -= (unsigned short)m_cfg.borderSize;
v[2] -= (unsigned short)m_cfg.borderSize;
}
if (!dtCreateNavMeshTileData(m_polyMesh->verts, m_polyMesh->nverts,
m_polyMesh->polys, m_polyMesh->npolys, m_polyMesh->nvp,
if (!dtCreateNavMeshTileData(m_pmesh->verts, m_pmesh->nverts,
m_pmesh->polys, m_pmesh->npolys, m_pmesh->nvp,
m_dmesh->meshes, m_dmesh->verts, m_dmesh->nverts, m_dmesh->tris, m_dmesh->ntris,
bmin, bmax, m_cfg.cs, m_cfg.ch, m_cfg.tileSize, m_cfg.walkableClimb, &navData, &navDataSize))
{
if (rcGetLog())
@@ -833,11 +852,15 @@ unsigned char* Sample_TileMesh::buildTileMesh(const float* bmin, const float* bm
rcGetLog()->log(RC_LOG_PROGRESS, " - trace: %.1fms (%.1f%%)", m_buildTimes.buildContoursTrace/1000.0f, m_buildTimes.buildContoursTrace*pc);
rcGetLog()->log(RC_LOG_PROGRESS, " - simplify: %.1fms (%.1f%%)", m_buildTimes.buildContoursSimplify/1000.0f, m_buildTimes.buildContoursSimplify*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Fixup contours: %.1fms (%.1f%%)", m_buildTimes.fixupContours/1000.0f, m_buildTimes.fixupContours*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Build Polymesh: %.1fms (%.1f%%)", m_buildTimes.buildPolymesh/1000.0f, m_buildTimes.buildPolymesh*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Build Polymesh Detail: %.1fms (%.1f%%)", m_buildTimes.buildDetailMesh/1000.0f, m_buildTimes.buildDetailMesh*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Merge Polymeshes: %.1fms (%.1f%%)", m_buildTimes.mergePolyMesh/1000.0f, m_buildTimes.mergePolyMesh*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Merge Polymesh Details: %.1fms (%.1f%%)", m_buildTimes.mergePolyMeshDetail/1000.0f, m_buildTimes.mergePolyMeshDetail*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Build Polymesh: %.1fms (%.1f%%)", m_buildTimes.buildPolymesh/1000.0f, m_buildTimes.buildPolymesh*pc);
rcGetLog()->log(RC_LOG_PROGRESS, "Polymesh: Verts:%d Polys:%d", m_polyMesh->nverts, m_polyMesh->npolys);
rcGetLog()->log(RC_LOG_PROGRESS, "Polymesh: Verts:%d Polys:%d", m_pmesh->nverts, m_pmesh->npolys);
rcGetLog()->log(RC_LOG_PROGRESS, "TOTAL: %.1fms", rcGetDeltaTimeUsec(totStartTime, totEndTime)/1000.0f);
}

View File

@@ -132,7 +132,7 @@ struct GuiState
mx(-1), my(-1), scroll(0),
active(0), hot(0), hotToBe(0), isHot(false), isActive(false), wentActive(false),
dragX(0), dragY(0), dragOrig(0), widgetX(0), widgetY(0), widgetW(100),
areaId(0), widgetId(0)
insideCurrentScroll(false), areaId(0), widgetId(0)
{
}
@@ -149,6 +149,7 @@ struct GuiState
int dragX, dragY;
float dragOrig;
int widgetX, widgetY, widgetW;
bool insideCurrentScroll;
unsigned int areaId;
unsigned int widgetId;
@@ -171,9 +172,9 @@ inline bool isHot(unsigned int id)
return g_state.hot == id;
}
inline bool inRect(int x, int y, int w, int h)
inline bool inRect(int x, int y, int w, int h, bool checkScroll = true)
{
return g_state.mx >= x && g_state.mx <= x+w && g_state.my >= y && g_state.my <= y+h;
return (!checkScroll || g_state.insideCurrentScroll) && g_state.mx >= x && g_state.mx <= x+w && g_state.my >= y && g_state.my <= y+h;
}
inline void clearInput()
@@ -324,14 +325,15 @@ bool imguiBeginScrollArea(const char* name, int x, int y, int w, int h, int* scr
g_focusTop = y-AREA_HEADER;
g_focusBottom = y-AREA_HEADER+h;
g_insideScrollArea = inRect(x, y, w, h, false);
g_state.insideCurrentScroll = g_insideScrollArea;
addGfxCmdRoundedRect(x, y, w, h, 6, imguiRGBA(0,0,0,192));
addGfxCmdText(x+AREA_HEADER/2, y+h-AREA_HEADER/2-TEXT_HEIGHT/2, IMGUI_ALIGN_LEFT, name, imguiRGBA(255,255,255,128));
addGfxCmdScissor(x+SCROLL_AREA_PADDING, y+SCROLL_AREA_PADDING, w-SCROLL_AREA_PADDING*4, h-AREA_HEADER-SCROLL_AREA_PADDING);
g_insideScrollArea = inRect(x, y, w, h);
return g_insideScrollArea;
}
@@ -403,8 +405,8 @@ void imguiEndScrollArea()
if (*g_scrollVal > (sh - h)) *g_scrollVal = (sh - h);
}
}
}
g_state.insideCurrentScroll = false;
}
bool imguiButton(const char* text, bool enabled)