diff --git a/DebugUtils/Include/RecastDebugDraw.h b/DebugUtils/Include/RecastDebugDraw.h index d14517c..8c81a3e 100644 --- a/DebugUtils/Include/RecastDebugDraw.h +++ b/DebugUtils/Include/RecastDebugDraw.h @@ -34,6 +34,9 @@ void duDebugDrawLeanHeightfieldSolid(duDebugDraw* dd, const struct rcLeanHeightf void duDebugDrawHeightfieldLayers(duDebugDraw* dd, const struct rcHeightfieldLayerSet& lset); void duDebugDrawHeightfieldLayersRegions(duDebugDraw* dd, const struct rcHeightfieldLayerSet& lset); +void duDebugDrawLayerContours(duDebugDraw* dd, const struct rcLayerContourSet& lcset); + + void duDebugDrawRegionConnections(struct duDebugDraw* dd, const struct rcContourSet& cset, const float alpha = 1.0f); void duDebugDrawRawContours(struct duDebugDraw* dd, const struct rcContourSet& cset, const float alpha = 1.0f); void duDebugDrawContours(struct duDebugDraw* dd, const struct rcContourSet& cset, const float alpha = 1.0f); diff --git a/DebugUtils/Source/RecastDebugDraw.cpp b/DebugUtils/Source/RecastDebugDraw.cpp index 50edfee..a44069f 100644 --- a/DebugUtils/Source/RecastDebugDraw.cpp +++ b/DebugUtils/Source/RecastDebugDraw.cpp @@ -323,17 +323,61 @@ void duDebugDrawLeanHeightfieldSolid(duDebugDraw* dd, const rcLeanHeightfield& l dd->end(); } +static void drawLayerPortals(duDebugDraw* dd, const rcHeightfieldLayer* layer, const unsigned int color) +{ + const float cs = layer->cs; + const float ch = layer->ch; + const float h = (layer->ymax-layer->ymin+1)*ch; + + unsigned int pcol = duLerpCol(color,duRGBA(255,255,255,255),64); + dd->begin(DU_DRAW_LINES, 2.0f); + for (int j = 0; j < layer->nportals; ++j) + { + const rcHeightfieldLayerPortal* portal = &layer->portals[j]; + if (portal->dir == 0 || portal->dir == 2) + { + const int xx = portal->dir == 0 ? (int)portal->pos : (int)portal->pos+1; + const float fx = layer->bmin[0] + xx*cs; + const float fya = layer->bmin[1] + (layer->ymin)*ch; + const float fyb = layer->bmin[1] + (layer->ymin)*ch; + const float fza = layer->bmin[2] + portal->smin*cs; + const float fzb = layer->bmin[2] + portal->smax*cs; + dd->vertex(fx, fya+h, fza, pcol); + dd->vertex(fx, fyb+h, fzb, pcol); + dd->vertex(fx, fya, fza, pcol); + dd->vertex(fx, fya+h, fza, pcol); + dd->vertex(fx, fyb, fzb, pcol); + dd->vertex(fx, fyb+h, fzb, pcol); + } + else if (portal->dir == 3 || portal->dir == 1) + { + const int yy = portal->dir == 3 ? (int)portal->pos : (int)portal->pos+1; + const float fxa = layer->bmin[0] + portal->smin*cs; + const float fxb = layer->bmin[0] + portal->smax*cs; + const float fya = layer->bmin[1] + (layer->ymin)*ch; + const float fyb = layer->bmin[1] + (layer->ymin)*ch; + const float fz = layer->bmin[2] + yy*cs; + dd->vertex(fxa, fya+h, fz, pcol); + dd->vertex(fxb, fyb+h, fz, pcol); + dd->vertex(fxa, fya, fz, pcol); + dd->vertex(fxa, fya+h, fz, pcol); + dd->vertex(fxb, fyb, fz, pcol); + dd->vertex(fxb, fyb+h, fz, pcol); + } + } + dd->end(); +} + void duDebugDrawHeightfieldLayers(duDebugDraw* dd, const struct rcHeightfieldLayerSet& lset) { if (!dd) return; - const float cs = lset.cs; - const float ch = lset.ch; - for (int i = 0; i < lset.nlayers; ++i) { const rcHeightfieldLayer* layer = &lset.layers[i]; + const float cs = layer->cs; + const float ch = layer->ch; const int w = layer->width; const int h = layer->height; @@ -341,10 +385,10 @@ void duDebugDrawHeightfieldLayers(duDebugDraw* dd, const struct rcHeightfieldLay // Layer bounds float bmin[3], bmax[3]; - rcVcopy(bmin, lset.bmin); - rcVcopy(bmax, lset.bmax); - bmin[1] = lset.bmin[1] + (layer->ymin-1)*ch; - bmax[1] = lset.bmin[1] + (layer->ymax+1)*ch; + rcVcopy(bmin, layer->bmin); + rcVcopy(bmax, layer->bmax); + bmin[1] = layer->bmin[1] + (layer->ymin-1)*ch; + bmax[1] = layer->bmin[1] + (layer->ymax+1)*ch; duDebugDrawBoxWire(dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duTransCol(color,128), 2.0f); // Layer height @@ -366,9 +410,9 @@ void duDebugDrawHeightfieldLayers(duDebugDraw* dd, const struct rcHeightfieldLay else col = duLerpCol(color, duIntToCol(area, 255), 32); - const float fx = lset.bmin[0] + x*cs; - const float fy = lset.bmin[1] + (h+1)*ch; - const float fz = lset.bmin[2] + y*cs; + const float fx = layer->bmin[0] + x*cs; + const float fy = layer->bmin[1] + (h+1)*ch; + const float fz = layer->bmin[2] + y*cs; dd->vertex(fx, fy, fz, col); dd->vertex(fx, fy, fz+cs, col); @@ -379,39 +423,7 @@ void duDebugDrawHeightfieldLayers(duDebugDraw* dd, const struct rcHeightfieldLay dd->end(); // Portals - unsigned int pcol = duLerpCol(color,duRGBA(255,255,255,255),128); - dd->begin(DU_DRAW_LINES, 2.0f); - for (int j = 0; j < layer->nportals; ++j) - { - const rcHeightfieldLayerPortal* portal = &layer->portals[j]; - if (portal->dir == 0 || portal->dir == 2) - { - const int ha = (int)layer->heights[portal->pos + portal->smin*w]; - const int hb = (int)layer->heights[portal->pos + (portal->smax-1)*w]; - const int xx = (portal->dir == 0) ? portal->pos : portal->pos+1; - const float fx = lset.bmin[0] + xx*cs; - const float fya = lset.bmin[1] + (ha+4)*ch; - const float fyb = lset.bmin[1] + (hb+4)*ch; - const float fza = lset.bmin[2] + portal->smin*cs; - const float fzb = lset.bmin[2] + portal->smax*cs; - dd->vertex(fx, fya, fza, pcol); - dd->vertex(fx, fyb, fzb, pcol); - } - else if (portal->dir == 3 || portal->dir == 1) - { - const int ha = (int)layer->heights[portal->smin + portal->pos*w]; - const int hb = (int)layer->heights[(portal->smax-1) + portal->pos*w]; - const int yy = (portal->dir == 3) ? portal->pos : portal->pos+1; - const float fxa = lset.bmin[0] + portal->smin*cs; - const float fxb = lset.bmin[0] + portal->smax*cs; - const float fya = lset.bmin[1] + (ha+3)*ch; - const float fyb = lset.bmin[1] + (hb+3)*ch; - const float fz = lset.bmin[2] + yy*cs; - dd->vertex(fxa, fya, fz, pcol); - dd->vertex(fxb, fyb, fz, pcol); - } - } - dd->end(); + drawLayerPortals(dd, layer, color); } } @@ -420,13 +432,12 @@ void duDebugDrawHeightfieldLayersRegions(duDebugDraw* dd, const struct rcHeightf { if (!dd) return; - const float cs = lset.cs; - const float ch = lset.ch; - for (int i = 0; i < lset.nlayers; ++i) { const rcHeightfieldLayer* layer = &lset.layers[i]; + const float cs = layer->cs; + const float ch = layer->ch; const int w = layer->width; const int h = layer->height; @@ -434,10 +445,10 @@ void duDebugDrawHeightfieldLayersRegions(duDebugDraw* dd, const struct rcHeightf // Layer bounds float bmin[3], bmax[3]; - rcVcopy(bmin, lset.bmin); - rcVcopy(bmax, lset.bmax); - bmin[1] = lset.bmin[1] + (layer->ymin-1)*ch; - bmax[1] = lset.bmin[1] + (layer->ymax+1)*ch; + rcVcopy(bmin, layer->bmin); + rcVcopy(bmax, layer->bmax); + bmin[1] = layer->bmin[1] + (layer->ymin-1)*ch; + bmax[1] = layer->bmin[1] + (layer->ymax+1)*ch; duDebugDrawBoxWire(dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duTransCol(color,128), 2.0f); // Layer height @@ -453,9 +464,9 @@ void duDebugDrawHeightfieldLayersRegions(duDebugDraw* dd, const struct rcHeightf unsigned int col = duLerpCol(color, duIntToCol(area, 255), 128); - const float fx = lset.bmin[0] + x*cs; - const float fy = lset.bmin[1] + (h+1)*ch; - const float fz = lset.bmin[2] + y*cs; + const float fx = layer->bmin[0] + x*cs; + const float fy = layer->bmin[1] + (h+1)*ch; + const float fz = layer->bmin[2] + y*cs; dd->vertex(fx, fy, fz, col); dd->vertex(fx, fy, fz+cs, col); @@ -464,45 +475,74 @@ void duDebugDrawHeightfieldLayersRegions(duDebugDraw* dd, const struct rcHeightf } } dd->end(); - -/* // Portals - unsigned int pcol = duLerpCol(color,duRGBA(255,255,255,255),128); - dd->begin(DU_DRAW_LINES, 2.0f); - for (int j = 0; j < layer->nportals; ++j) - { - const rcHeightfieldLayerPortal* portal = &layer->portals[j]; - if (portal->dir == 0 || portal->dir == 2) - { - const int ha = (int)layer->heights[portal->pos + portal->smin*w]; - const int hb = (int)layer->heights[portal->pos + (portal->smax-1)*w]; - const int xx = (portal->dir == 0) ? portal->pos : portal->pos+1; - const float fx = lset.bmin[0] + xx*cs; - const float fya = lset.bmin[1] + (ha+4)*ch; - const float fyb = lset.bmin[1] + (hb+4)*ch; - const float fza = lset.bmin[2] + portal->smin*cs; - const float fzb = lset.bmin[2] + portal->smax*cs; - dd->vertex(fx, fya, fza, pcol); - dd->vertex(fx, fyb, fzb, pcol); - } - else if (portal->dir == 3 || portal->dir == 1) - { - const int ha = (int)layer->heights[portal->smin + portal->pos*w]; - const int hb = (int)layer->heights[(portal->smax-1) + portal->pos*w]; - const int yy = (portal->dir == 3) ? portal->pos : portal->pos+1; - const float fxa = lset.bmin[0] + portal->smin*cs; - const float fxb = lset.bmin[0] + portal->smax*cs; - const float fya = lset.bmin[1] + (ha+3)*ch; - const float fyb = lset.bmin[1] + (hb+3)*ch; - const float fz = lset.bmin[2] + yy*cs; - dd->vertex(fxa, fya, fz, pcol); - dd->vertex(fxb, fyb, fz, pcol); - } - } - dd->end();*/ + + // Portals + drawLayerPortals(dd, layer, color); } } +void duDebugDrawLayerContours(duDebugDraw* dd, const struct rcLayerContourSet& lcset) +{ + if (!dd) return; + + const float* orig = lcset.bmin; + const float cs = lcset.cs; + const float ch = lcset.ch; + + const unsigned char a = 255;// (unsigned char)(alpha*255.0f); + + dd->begin(DU_DRAW_LINES, 2.0f); + + for (int i = 0; i < lcset.nconts; ++i) + { + const rcLayerContour& c = lcset.conts[i]; + unsigned int color = 0; + + for (int j = 0; j < c.nverts; ++j) + { + const int k = (j+1) % c.nverts; + const unsigned char* va = &c.verts[j*4]; + const unsigned char* vb = &c.verts[k*4]; + const float ax = orig[0] + va[0]*cs; + const float ay = orig[1] + (va[1]+1+(i&1))*ch; + const float az = orig[2] + va[2]*cs; + const float bx = orig[0] + vb[0]*cs; + const float by = orig[1] + (vb[1]+1+(i&1))*ch; + const float bz = orig[2] + vb[2]*cs; + color = duIntToCol(vb[3], a); + dd->vertex(ax,ay,az,color); + dd->vertex(bx,by,bz,duDarkenCol(color)); + } + } + dd->end(); + + dd->begin(DU_DRAW_POINTS, 4.0f); + + for (int i = 0; i < lcset.nconts; ++i) + { + const rcLayerContour& c = lcset.conts[i]; + unsigned int color = 0; + + for (int j = 0; j < c.nverts; ++j) + { + const int k = (j+1) % c.nverts; + const unsigned char* va = &c.verts[j*4]; + const unsigned char* vb = &c.verts[k*4]; + + color = duDarkenCol(duIntToCol(va[3], a)); + if (va[3] != vb[3]) + color = duRGBA(255,255,255,a); + + float fx = orig[0] + va[0]*cs; + float fy = orig[1] + (va[1]+1+(i&1))*ch; + float fz = orig[2] + va[2]*cs; + dd->vertex(fx,fy,fz, color); + } + } + dd->end(); +} + static void getContourCenter(const rcContour* cont, const float* orig, float cs, float ch, float* center) { center[0] = 0; diff --git a/Recast/Include/Recast.h b/Recast/Include/Recast.h index a267eca..945a5f3 100644 --- a/Recast/Include/Recast.h +++ b/Recast/Include/Recast.h @@ -224,15 +224,19 @@ struct rcLeanHeightfield struct rcHeightfieldLayerPortal { - unsigned short pos; // Position of the portal. - unsigned short smin, smax; // Span min/max of the portal. + unsigned char pos; // Position of the portal. unsigned char dir; // Direction of the portal (same as used by rcGetCon()). + unsigned char smin, smax; // Span min/max of the portal. + unsigned short hmin, hmax; // Span min/max of the portal. }; struct rcHeightfieldLayer { + float bmin[3], bmax[3]; // Bounding box of the heightfield. + float cs, ch; // Cell size and height. int width, height; // Width and height of the layer. int nportals; // Number of portals. + unsigned char regCount; unsigned short ymin, ymax; // Height min/max range. unsigned short* heights; // Heighfield. unsigned char* areas; // Area types. @@ -244,8 +248,6 @@ struct rcHeightfieldLayerSet { rcHeightfieldLayer* layers; // Pointer to layers. int nlayers; // Number of layers. - float bmin[3], bmax[3]; // Bounding box of the heightfield. - float cs, ch; // Cell size and height. }; rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet(); @@ -751,6 +753,34 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, bool rcBuildLayerRegions(rcContext* ctx, rcHeightfieldLayer& layer, const int walkableClimb); + +struct rcLayerContour +{ + int nverts; + unsigned char* verts; + unsigned char reg, area; +}; + +struct rcLayerContourSet +{ + float bmin[3], bmax[3]; // Bounding box of the heightfield. + float cs, ch; // Cell size and height. + int nconts; + rcLayerContour* conts; +}; + +rcLayerContourSet* rcAllocLayerContourSet(); +void rcFreeLayerContourSet(rcLayerContourSet* lset); + +// TODO: move this somewhere else, once the layer meshing is done. +bool rcBuildLayerContours(rcContext* ctx, + rcHeightfieldLayer& layer, + const int walkableClimb, const float maxError, + rcLayerContourSet& lcset); + + + + // Builds simplified contours from the regions outlines. // Params: // chf - (in) compact heightfield which has regions set. diff --git a/Recast/Source/Recast.cpp b/Recast/Source/Recast.cpp index 1e70a0d..733eaaf 100644 --- a/Recast/Source/Recast.cpp +++ b/Recast/Source/Recast.cpp @@ -112,6 +112,28 @@ void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset) rcFree(lset); } + +rcLayerContourSet* rcAllocLayerContourSet() +{ + rcLayerContourSet* cset = (rcLayerContourSet*)rcAlloc(sizeof(rcLayerContourSet), RC_ALLOC_PERM); + memset(cset, 0, sizeof(rcLayerContourSet)); + return cset; +} + +void rcFreeLayerContourSet(rcLayerContourSet* cset) +{ + if (!cset) return; + for (int i = 0; i < cset->nconts; ++i) + { + rcFree(cset->conts[i].verts); +// rcFree(cset->conts[i].rverts); + } + rcFree(cset->conts); + rcFree(cset); +} + + + rcContourSet* rcAllocContourSet() { rcContourSet* cset = (rcContourSet*)rcAlloc(sizeof(rcContourSet), RC_ALLOC_PERM); diff --git a/Recast/Source/RecastContour.cpp b/Recast/Source/RecastContour.cpp index 1906b6e..630784a 100644 --- a/Recast/Source/RecastContour.cpp +++ b/Recast/Source/RecastContour.cpp @@ -340,7 +340,7 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, endi = ai; } - // Tessellate only outer edges oredges between areas. + // Tessellate only outer edges or edges between areas. if ((points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0 || (points[ci*4+3] & RC_AREA_BORDER)) { diff --git a/Recast/Source/RecastLayers.cpp b/Recast/Source/RecastLayers.cpp index e321f3f..f30a151 100644 --- a/Recast/Source/RecastLayers.cpp +++ b/Recast/Source/RecastLayers.cpp @@ -476,15 +476,16 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, const int lw = w - borderSize*2; const int lh = h - borderSize*2; + // Build contracted bbox for layers. + float bmin[3], bmax[3]; + rcVcopy(bmin, chf.bmin); + rcVcopy(bmax, chf.bmax); + bmin[0] += borderSize*chf.cs; + bmin[2] += borderSize*chf.cs; + bmax[0] -= borderSize*chf.cs; + bmax[2] -= borderSize*chf.cs; + lset.nlayers = (int)layerId; - rcVcopy(lset.bmin, chf.bmin); - rcVcopy(lset.bmax, chf.bmax); - lset.bmin[0] += borderSize*chf.cs; - lset.bmin[2] += borderSize*chf.cs; - lset.bmax[0] -= borderSize*chf.cs; - lset.bmax[2] -= borderSize*chf.cs; - lset.cs = chf.cs; - lset.ch = chf.ch; lset.layers = (rcHeightfieldLayer*)rcAlloc(sizeof(rcHeightfieldLayer)*lset.nlayers, RC_ALLOC_PERM); if (!lset.layers) @@ -512,6 +513,11 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, layer->width = lw; layer->height = lh; + layer->cs = chf.cs; + layer->ch = chf.ch; + // TODO: Should this be local bbox instead? + rcVcopy(layer->bmin, bmin); + rcVcopy(layer->bmax, bmax); layer->heights = (unsigned short*)rcAlloc(sizeof(unsigned short)*lw*lh, RC_ALLOC_PERM); if (!layer->heights) @@ -622,15 +628,15 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, if (start[j] != -1) { // Add portal. - rcHeightfieldLayerPortal* portal = allocPortal(&layer->portals,layer->nportals,cportals); + rcHeightfieldLayerPortal* portal = allocPortal(&layer->portals, layer->nportals, cportals); if (!portal) { ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'portals' (%d).", cportals); return false; } - portal->pos = (unsigned short)y; - portal->smin = (unsigned short)start[j]; - portal->smax = (unsigned short)x; + portal->pos = (unsigned char)y/*+off[j]*/; + portal->smin = (unsigned char)start[j]; + portal->smax = (unsigned char)x; portal->dir = dir[j]; start[j] = -1; @@ -669,9 +675,9 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'portals' (%d).", cportals); return false; } - portal->pos = (unsigned short)x; - portal->smin = (unsigned short)start[j]; - portal->smax = (unsigned short)y; + portal->pos = (unsigned char)x/*+off[j]*/; + portal->smin = (unsigned char)start[j]; + portal->smax = (unsigned char)y; portal->dir = dir[j]; start[j] = -1; @@ -690,7 +696,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, inline bool isConnected(rcHeightfieldLayer& layer, const int ia, const int ib, const int walkableClimb) { - if (layer.areas[ib] == RC_NULL_AREA) return false; + if (layer.areas[ia] != layer.areas[ib]) return false; if (rcAbs((int)layer.heights[ia] - (int)layer.heights[ib]) > walkableClimb) return false; return true; } @@ -927,6 +933,8 @@ bool rcBuildLayerRegions(rcContext* ctx, rcHeightfieldLayer& layer, const int wa for (int i = 0; i < nregs; ++i) regs[i].regId = remap[regs[i].regId]; + layer.regCount = regId; + for (int i = 0; i < w*h; ++i) { if (layer.regs[i] != 0xff) @@ -935,3 +943,489 @@ bool rcBuildLayerRegions(rcContext* ctx, rcHeightfieldLayer& layer, const int wa return true; } + +static bool allocVert(rcLayerContour& cont, int& cverts) +{ + if (cont.nverts+1 > cverts) + { + cverts = !cverts ? 16 : cverts*2; + unsigned char* nv = (unsigned char*)rcAlloc(cverts*4, RC_ALLOC_TEMP); + if (!nv) return false; + if (cont.nverts) + memcpy(nv, cont.verts, cont.nverts*4); + rcFree(cont.verts); + cont.verts = nv; + } + return true; +} + +static bool addVertex(rcLayerContour& cont, int x, int y, int z, int r, int& cverts) +{ + // Try to merge with existing segments. + if (cont.nverts > 1) + { + unsigned char* pa = &cont.verts[(cont.nverts-2)*4]; + unsigned char* pb = &cont.verts[(cont.nverts-1)*4]; + if ((int)pb[3] == r) + { + if (pa[0] == pb[0] && (int)pb[0] == x) + { + // The verts are aligned aling x-axis, update z. + pb[1] = (unsigned char)y; + pb[2] = (unsigned char)z; + pb[3] = (unsigned char)r; + return true; + } + else if (pa[2] == pb[2] && (int)pb[2] == z) + { + // The verts are aligned aling z-axis, update x. + pb[0] = (unsigned char)x; + pb[1] = (unsigned char)y; + pb[3] = (unsigned char)r; + return true; + } + } + } + + // Add new point. + if (!allocVert(cont, cverts)) + return false; + + unsigned char* v = &cont.verts[cont.nverts*4]; + v[0] = (unsigned char)x; + v[1] = (unsigned char)y; + v[2] = (unsigned char)z; + v[3] = (unsigned char)r; + cont.nverts++; + + return true; +} + + +static unsigned char getNeighbourReg(rcHeightfieldLayer& layer, + const unsigned char* cons, + const int ax, const int ay, const int dir, + const int walkableClimb) +{ + const int ia = ax+ay*layer.width; + + const int bx = ax + rcGetDirOffsetX(dir); + const int by = ay + rcGetDirOffsetY(dir); + if (bx < 0 || by < 0 || bx >= layer.width || by >= layer.height) + { + if (cons[ia] & (1< walkableClimb) + { + if (cons[ia] & (1< 0 && x == startX && y == startY && dir == startDir) + break; + + x = nx; + y = ny; + dir = ndir; + + iter++; + } + + // Remove last vertex if it is duplicate of the first one. + unsigned char* pa = &cont.verts[(cont.nverts-1)*4]; + unsigned char* pb = &cont.verts[0]; + if (pa[0] == pb[0] && pa[2] == pb[2]) + cont.nverts--; + + return true; +} + + +static float distancePtSeg(const int x, const int z, + const int px, const int pz, + const int qx, const int qz) +{ + /* float pqx = (float)(qx - px); + float pqy = (float)(qy - py); + float pqz = (float)(qz - pz); + float dx = (float)(x - px); + float dy = (float)(y - py); + float dz = (float)(z - pz); + 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 = px + t*pqx - x; + dy = py + t*pqy - y; + dz = pz + t*pqz - z; + + return dx*dx + dy*dy + dz*dz;*/ + + float pqx = (float)(qx - px); + float pqz = (float)(qz - pz); + float dx = (float)(x - px); + float dz = (float)(z - pz); + 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 = px + t*pqx - x; + dz = pz + t*pqz - z; + + return dx*dx + dz*dz; +} + +static bool simplifyContour(rcLayerContour& cont, const float maxError) +{ + int* poly = (int*)rcAlloc(sizeof(int)*cont.nverts, RC_ALLOC_TEMP); + if (!poly) + return false; + int npoly = 0; + + for (int i = 0; i < cont.nverts; ++i) + { + int j = (i+1) % cont.nverts; + // Check for start of a wall segment. + unsigned char ra = cont.verts[j*4+3]; + unsigned char rb = cont.verts[i*4+3]; + if (ra != rb) + poly[npoly++] = i; + } + if (npoly < 2) + { + // If there is no transitions at all, + // create some initial points for the simplification process. + // Find lower-left and upper-right vertices of the contour. + int llx = cont.verts[0]; + int llz = cont.verts[2]; + int lli = 0; + int urx = cont.verts[0]; + int urz = cont.verts[2]; + int uri = 0; + for (int i = 1; i < cont.nverts; ++i) + { + int x = cont.verts[i*4+0]; + int z = cont.verts[i*4+2]; + if (x < llx || (x == llx && z < llz)) + { + llx = x; + llz = z; + lli = i; + } + if (x > urx || (x == urx && z > urz)) + { + urx = x; + urz = z; + uri = i; + } + } + npoly = 0; + poly[npoly++] = lli; + poly[npoly++] = uri; + } + + // Add points until all raw points are within + // error tolerance to the simplified shape. + for (int i = 0; i < npoly; ) + { + int ii = (i+1) % npoly; + + const int ai = poly[i]; + const int ax = cont.verts[ai*4+0]; + const int az = cont.verts[ai*4+2]; + + const int bi = poly[ii]; + const int bx = cont.verts[bi*4+0]; + const int bz = cont.verts[bi*4+2]; + + // Find maximum deviation from the segment. + float maxd = 0; + int maxi = -1; + int ci, cinc, endi; + + // Traverse the segment in lexilogical order so that the + // max deviation is calculated similarly when traversing + // opposite segments. + if (bx > ax || (bx == ax && bz > az)) + { + cinc = 1; + ci = (ai+cinc) % cont.nverts; + endi = bi; + } + else + { + cinc = cont.nverts-1; + ci = (bi+cinc) % cont.nverts; + endi = ai; + } + + // Tessellate only outer edges or edges between areas. + while (ci != endi) + { + float d = distancePtSeg(cont.verts[ci*4+0], cont.verts[ci*4+2], ax, az, bx, bz); + if (d > maxd) + { + maxd = d; + maxi = ci; + } + ci = (ci+cinc) % cont.nverts; + } + + + // If the max deviation is larger than accepted error, + // add new point, else continue to next segment. + if (maxi != -1 && maxd > (maxError*maxError)) + { + npoly++; + for (int j = npoly-1; j > i; --j) + poly[j] = poly[j-1]; + poly[i+1] = maxi; + } + else + { + ++i; + } + } + + // Remap vertices + int start = 0; + for (int i = 1; i < npoly; ++i) + if (poly[i] < poly[start]) + start = i; + + cont.nverts = 0; + for (int i = 0; i < npoly; ++i) + { + const int j = (start+i) % npoly; + unsigned char* src = &cont.verts[poly[j]*4]; + unsigned char* dst = &cont.verts[cont.nverts*4]; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + cont.nverts++; + } + + rcFree(poly); + + return true; +} + +// TODO: move this somewhere else, once the layer meshing is done. +bool rcBuildLayerContours(rcContext* ctx, + rcHeightfieldLayer& layer, + const int walkableClimb, const float maxError, + rcLayerContourSet& lcset) +{ + rcAssert(ctx); + + const int w = layer.width; + const int h = layer.height; + + rcAssert(lcset.conts == 0); + + rcVcopy(lcset.bmin, layer.bmin); + rcVcopy(lcset.bmax, layer.bmax); + lcset.cs = layer.cs; + lcset.ch = layer.ch; + lcset.nconts = layer.regCount; + lcset.conts = (rcLayerContour*)rcAlloc(sizeof(rcLayerContour)*lcset.nconts, RC_ALLOC_TEMP); + if (!lcset.conts) + { + ctx->log(RC_LOG_ERROR, "rcBuildLayerContours: Out of memory 'conts' (%d).", lcset.nconts); + return false; + } + memset(lcset.conts, 0, sizeof(rcLayerContour)*lcset.nconts); + + rcScopedDelete cons = (unsigned char*)rcAlloc(sizeof(unsigned char)*w*h, RC_ALLOC_TEMP); + if (!cons) + { + ctx->log(RC_LOG_ERROR, "rcBuildLayerContours: Out of memory 'cons' (%d).", w*h); + return false; + } + memset(cons,0,sizeof(unsigned char)*w*h); + + +/* if (portal->dir == 0 || portal->dir == 2) + { + const int xx = portal->dir == 0 ? (int)portal->pos : (int)portal->pos+1; + const float fx = layer->bmin[0] + xx*cs; + const float fya = layer->bmin[1] + (layer->ymin)*ch; + const float fyb = layer->bmin[1] + (layer->ymin)*ch; + const float fza = layer->bmin[2] + portal->smin*cs; + const float fzb = layer->bmin[2] + portal->smax*cs; + dd->vertex(fx, fya+h, fza, pcol); + dd->vertex(fx, fyb+h, fzb, pcol); + } + else if (portal->dir == 3 || portal->dir == 1) + { + const int yy = portal->dir == 3 ? (int)portal->pos : (int)portal->pos+1; + const float fxa = layer->bmin[0] + portal->smin*cs; + const float fxb = layer->bmin[0] + portal->smax*cs; + const float fya = layer->bmin[1] + (layer->ymin)*ch; + const float fyb = layer->bmin[1] + (layer->ymin)*ch; + const float fz = layer->bmin[2] + yy*cs; + dd->vertex(fxa, fya+h, fz, pcol); + dd->vertex(fxb, fyb+h, fz, pcol); + }*/ + + // Paint portals + for (int i = 0; i < layer.nportals; ++i) + { + const rcHeightfieldLayerPortal* portal = &layer.portals[i]; + if (portal->dir == 0 || portal->dir == 2) + { + const unsigned char mask = (const unsigned char)(1 << portal->dir); + for (int j = (int)portal->smin; j < (int)portal->smax; ++j) + cons[(int)portal->pos + j*w] |= mask; + } + else + { + const unsigned char mask = (const unsigned char)(1 << portal->dir); + for (int j = (int)portal->smin; j < (int)portal->smax; ++j) + cons[j + (int)portal->pos*w] |= mask; + } + } + +/* printf("cons:\n"); + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + unsigned char c = cons[x+y*w]; + if (c == 0) + printf("."); + else + printf("%x",c); + } + printf("\n"); + }*/ + + + // Find contours. + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const int idx = x+y*w; + const unsigned char ri = layer.regs[idx]; + if (ri == 0xff) + continue; + + rcLayerContour& cont = lcset.conts[ri]; + + if (cont.nverts > 0) + continue; + + cont.reg = ri; + cont.area = layer.areas[idx]; + + if (!walkContour(x, y, layer, cons, walkableClimb, cont)) + { + ctx->log(RC_LOG_ERROR, "rcBuildLayerContours: Failed to walk contour."); + return false; + } + + if (!simplifyContour(cont, maxError)) + { + ctx->log(RC_LOG_ERROR, "rcBuildLayerContours: Failed to simplify contour."); + return false; + } + + } + } + + return true; +} + + diff --git a/RecastDemo/Bin/Recast.app/Contents/MacOS/Recast b/RecastDemo/Bin/Recast.app/Contents/MacOS/Recast index fc02572..b5a6a0d 100755 Binary files a/RecastDemo/Bin/Recast.app/Contents/MacOS/Recast and b/RecastDemo/Bin/Recast.app/Contents/MacOS/Recast differ diff --git a/RecastDemo/Include/Sample_TileMesh.h b/RecastDemo/Include/Sample_TileMesh.h index 4e2d9a0..027cb84 100644 --- a/RecastDemo/Include/Sample_TileMesh.h +++ b/RecastDemo/Include/Sample_TileMesh.h @@ -35,11 +35,15 @@ protected: unsigned char* m_triareas; rcHeightfield* m_solid; rcCompactHeightfield* m_chf; - rcHeightfieldLayerSet* m_lset; rcContourSet* m_cset; rcPolyMesh* m_pmesh; rcPolyMeshDetail* m_dmesh; rcConfig m_cfg; + + static const int MAX_LAYERS = 128; + rcHeightfieldLayerSet* m_lset; + rcLayerContourSet* m_lcsets[MAX_LAYERS]; + int m_nlcsets; enum DrawMode { @@ -61,7 +65,10 @@ protected: DRAWMODE_CONTOURS, DRAWMODE_POLYMESH, DRAWMODE_POLYMESH_DETAIL, + DRAWMODE_HEIGHFIELD_LAYERS, + DRAWMODE_LAYER_CONTOURS, + MAX_DRAWMODE }; diff --git a/RecastDemo/Source/Sample_TileMesh.cpp b/RecastDemo/Source/Sample_TileMesh.cpp index 8b20bc1..3c6dba0 100644 --- a/RecastDemo/Source/Sample_TileMesh.cpp +++ b/RecastDemo/Source/Sample_TileMesh.cpp @@ -178,10 +178,11 @@ Sample_TileMesh::Sample_TileMesh() : m_triareas(0), m_solid(0), m_chf(0), - m_lset(0), m_cset(0), m_pmesh(0), m_dmesh(0), + m_lset(0), + m_nlcsets(0), m_drawMode(DRAWMODE_NAVMESH), m_maxTiles(0), m_maxPolysPerTile(0), @@ -194,6 +195,8 @@ Sample_TileMesh::Sample_TileMesh() : resetCommonSettings(); memset(m_tileBmin, 0, sizeof(m_tileBmin)); memset(m_tileBmax, 0, sizeof(m_tileBmax)); + + memset(m_lcsets, 0, sizeof(m_lcsets)); setTool(new NavMeshTileTool); } @@ -213,14 +216,21 @@ void Sample_TileMesh::cleanup() m_solid = 0; rcFreeCompactHeightfield(m_chf); m_chf = 0; - rcFreeHeightfieldLayerSet(m_lset); - m_lset = 0; rcFreeContourSet(m_cset); m_cset = 0; rcFreePolyMesh(m_pmesh); m_pmesh = 0; rcFreePolyMeshDetail(m_dmesh); m_dmesh = 0; + + rcFreeHeightfieldLayerSet(m_lset); + m_lset = 0; + for (int i = 0; i < MAX_LAYERS; ++i) + { + rcFreeLayerContourSet(m_lcsets[i]); + m_lcsets[i] = 0; + } + m_nlcsets = 0; } @@ -470,6 +480,7 @@ void Sample_TileMesh::handleDebugMode() valid[DRAWMODE_POLYMESH] = m_pmesh != 0; valid[DRAWMODE_POLYMESH_DETAIL] = m_dmesh != 0; valid[DRAWMODE_HEIGHFIELD_LAYERS] = m_lset != 0; + valid[DRAWMODE_LAYER_CONTOURS] = m_nlcsets != 0; } int unavail = 0; @@ -516,8 +527,11 @@ void Sample_TileMesh::handleDebugMode() m_drawMode = DRAWMODE_POLYMESH; if (imguiCheck("Poly Mesh Detail", m_drawMode == DRAWMODE_POLYMESH_DETAIL, valid[DRAWMODE_POLYMESH_DETAIL])) m_drawMode = DRAWMODE_POLYMESH_DETAIL; + if (imguiCheck("Heighfield Layers", m_drawMode == DRAWMODE_HEIGHFIELD_LAYERS, valid[DRAWMODE_HEIGHFIELD_LAYERS])) m_drawMode = DRAWMODE_HEIGHFIELD_LAYERS; + if (imguiCheck("Layer Contours", m_drawMode == DRAWMODE_LAYER_CONTOURS, valid[DRAWMODE_LAYER_CONTOURS])) + m_drawMode = DRAWMODE_LAYER_CONTOURS; if (unavail) { @@ -668,6 +682,14 @@ void Sample_TileMesh::handleRender() duDebugDrawHeightfieldLayersRegions(&dd, *m_lset); glDepthMask(GL_TRUE); } + + if (m_nlcsets && m_drawMode == DRAWMODE_LAYER_CONTOURS) + { + glDepthMask(GL_FALSE); + for (int i = 0; i < m_nlcsets; ++i) + duDebugDrawLayerContours(&dd, *m_lcsets[i]); + glDepthMask(GL_TRUE); + } m_geom->drawConvexVolumes(&dd); @@ -697,8 +719,8 @@ void Sample_TileMesh::handleRenderOverlay(double* proj, double* model, int* view const rcHeightfieldLayer* layer = &m_lset->layers[i]; unsigned int color = duIntToCol(i+1, 255); float pos[3]; - rcVcopy(pos, m_lset->bmin); - pos[1] = m_lset->bmin[1] + ((layer->ymin+layer->ymax)/2)*m_lset->ch; + rcVcopy(pos, layer->bmin); + pos[1] = layer->bmin[1] + ((layer->ymin+layer->ymax)/2)*layer->ch; if (gluProject((GLdouble)pos[0], (GLdouble)pos[1], (GLdouble)pos[2], model, proj, view, &x, &y, &z)) { char text[32]; @@ -1114,9 +1136,17 @@ unsigned char* Sample_TileMesh::buildTileMesh(const int tx, const int ty, const return 0; } + m_nlcsets = 0; for (int i = 0; i < m_lset->nlayers; ++i) { rcBuildLayerRegions(m_ctx, m_lset->layers[i], m_cfg.walkableClimb); + + m_lcsets[m_nlcsets] = rcAllocLayerContourSet(); + if (!rcBuildLayerContours(m_ctx, m_lset->layers[i], + m_cfg.walkableClimb, m_cfg.maxSimplificationError, + *m_lcsets[m_nlcsets])) + break; + m_nlcsets++; } }