diff --git a/DebugUtils/Include/RecastDebugDraw.h b/DebugUtils/Include/RecastDebugDraw.h index 21ce6d0..d14517c 100644 --- a/DebugUtils/Include/RecastDebugDraw.h +++ b/DebugUtils/Include/RecastDebugDraw.h @@ -32,6 +32,7 @@ void duDebugDrawCompactHeightfieldDistance(struct duDebugDraw* dd, const struct void duDebugDrawLeanHeightfieldSolid(duDebugDraw* dd, const struct rcLeanHeightfield& lhf); void duDebugDrawHeightfieldLayers(duDebugDraw* dd, const struct rcHeightfieldLayerSet& lset); +void duDebugDrawHeightfieldLayersRegions(duDebugDraw* dd, const struct rcHeightfieldLayerSet& lset); 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); diff --git a/DebugUtils/Source/RecastDebugDraw.cpp b/DebugUtils/Source/RecastDebugDraw.cpp index af1be3b..50edfee 100644 --- a/DebugUtils/Source/RecastDebugDraw.cpp +++ b/DebugUtils/Source/RecastDebugDraw.cpp @@ -329,13 +329,14 @@ void duDebugDrawHeightfieldLayers(duDebugDraw* dd, const struct rcHeightfieldLay const float cs = lset.cs; const float ch = lset.ch; - const int w = lset.width; - const int h = lset.height; for (int i = 0; i < lset.nlayers; ++i) { const rcHeightfieldLayer* layer = &lset.layers[i]; - + + const int w = layer->width; + const int h = layer->height; + unsigned int color = duIntToCol(i+1, 255); // Layer bounds @@ -415,6 +416,93 @@ void duDebugDrawHeightfieldLayers(duDebugDraw* dd, const struct rcHeightfieldLay } +void duDebugDrawHeightfieldLayersRegions(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 int w = layer->width; + const int h = layer->height; + + unsigned int color = duIntToCol(i+1, 255); + + // 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; + duDebugDrawBoxWire(dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duTransCol(color,128), 2.0f); + + // Layer height + dd->begin(DU_DRAW_QUADS); + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const int idx = x+y*w; + const int h = (int)layer->heights[idx]; + if (h == 0xffff) continue; + const unsigned char area = layer->regs ? layer->regs[idx]+1 : 1; + + 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; + + dd->vertex(fx, fy, fz, col); + dd->vertex(fx, fy, fz+cs, col); + dd->vertex(fx+cs, fy, fz+cs, col); + dd->vertex(fx+cs, fy, fz, col); + } + } + 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();*/ + } + +} + 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 6b41cb2..a267eca 100644 --- a/Recast/Include/Recast.h +++ b/Recast/Include/Recast.h @@ -221,6 +221,38 @@ struct rcLeanHeightfield }; + +struct rcHeightfieldLayerPortal +{ + unsigned short pos; // Position of the portal. + unsigned short smin, smax; // Span min/max of the portal. + unsigned char dir; // Direction of the portal (same as used by rcGetCon()). +}; + +struct rcHeightfieldLayer +{ + int width, height; // Width and height of the layer. + int nportals; // Number of portals. + unsigned short ymin, ymax; // Height min/max range. + unsigned short* heights; // Heighfield. + unsigned char* areas; // Area types. + unsigned char* regs; // Regions. + rcHeightfieldLayerPortal* portals; // Portals. +}; + +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(); +void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset); + + + struct rcContour { int* verts; // Vertex coordinates, each vertex contains 4 components. @@ -680,6 +712,7 @@ bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf); // Here area means the count of spans in an area. // Params: // chf - (in/out) compact heightfield representing the open space. +// borderSize - (in) Non-navigable Border around the heightfield. // minRegionArea - (in) the smallest allowed region area. // maxMergeRegionArea - (in) the largest allowed region area which can be merged. // Returns false if operation ran out of memory. @@ -696,12 +729,28 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, // Here area means the count of spans in an area. // Params: // chf - (in/out) compact heightfield representing the open space. +// borderSize - (in) Non-navigable Border around the heightfield. // minRegionArea - (in) the smallest allowed regions size. // maxMergeRegionArea - (in) the largest allowed regions size which can be merged. // Returns false if operation ran out of memory. bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, const int borderSize, const int minRegionArea, const int mergeRegionArea); +// Builds 2D layer representation of a heighfield. +// Params: +// chf - (in) compact heightfield representing the open space. +// borderSize - (in) Non-navigable Border around the heightfield. +// walkableHeight - (in) minimum height where the agent can still walk. +// lset - (out) set of 2D heighfield layers. +// Returns false if operation ran out of memory. +bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int walkableHeight, + rcHeightfieldLayerSet& lset); + +// TODO: move this somewhere else, once the layer meshing is done. +bool rcBuildLayerRegions(rcContext* ctx, rcHeightfieldLayer& layer, const int walkableClimb); + + // Builds simplified contours from the regions outlines. // Params: // chf - (in) compact heightfield which has regions set. @@ -740,40 +789,4 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int -// TODO: Put in right place! -struct rcHeightfieldLayerPortal -{ - unsigned short pos, smin, smax; - unsigned char dir; -}; - -struct rcHeightfieldLayer -{ - unsigned short ymin, ymax; - unsigned short* heights; - unsigned char* areas; - rcHeightfieldLayerPortal* portals; - int nportals; -}; - -struct rcHeightfieldLayerSet -{ - rcHeightfieldLayer* layers; - int nlayers; - int width, height; - int borderSize; - float bmin[3], bmax[3]; // Bounding box of the heightfield. - float cs, ch; // Cell size and height. -}; - -rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet(); -void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset); - - -bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, - const int borderSize, const int walkableHeight, - rcHeightfieldLayerSet& lset); - - - #endif // RECAST_H diff --git a/Recast/Source/Recast.cpp b/Recast/Source/Recast.cpp index bcb8ce4..1e70a0d 100644 --- a/Recast/Source/Recast.cpp +++ b/Recast/Source/Recast.cpp @@ -105,6 +105,8 @@ void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset) { rcFree(lset->layers[i].heights); rcFree(lset->layers[i].areas); + rcFree(lset->layers[i].regs); + rcFree(lset->layers[i].portals); } rcFree(lset->layers); rcFree(lset); diff --git a/Recast/Source/RecastLayers.cpp b/Recast/Source/RecastLayers.cpp index 7ed815f..44ef38b 100644 --- a/Recast/Source/RecastLayers.cpp +++ b/Recast/Source/RecastLayers.cpp @@ -57,6 +57,14 @@ static void addUnique(unsigned char* a, unsigned char& an, unsigned char v) an++; } +static void addUniqueLast(unsigned char* a, unsigned char& an, unsigned char v) +{ + const int n = (int)an; + if (n > 0 && a[n-1] == v) return; + a[an] = v; + an++; +} + static bool contains(const unsigned char* a, const unsigned char an, const unsigned char v) { const int n = (int)an; @@ -469,9 +477,6 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, const int lh = h - borderSize*2; lset.nlayers = (int)layerId; - lset.width = lw; - lset.height = lh; - lset.borderSize = borderSize; rcVcopy(lset.bmin, chf.bmin); rcVcopy(lset.bmax, chf.bmax); lset.bmin[0] += borderSize*chf.cs; @@ -504,6 +509,10 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, // Allocate memory for the current layer. rcHeightfieldLayer* layer = &lset.layers[i]; + + layer->width = lw; + layer->height = lh; + layer->heights = (unsigned short*)rcAlloc(sizeof(unsigned short)*lw*lh, RC_ALLOC_PERM); if (!layer->heights) { @@ -543,13 +552,18 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { const rcCompactSpan& s = chf.spans[i]; - if (srcReg[i] == 0xff) continue; + // Skip unassigned regions. + if (srcReg[i] == 0xff) + continue; + // Skip of does nto belong to current layer. unsigned char lid = regs[srcReg[i]].layerId; if (lid != curId) continue; + // Store height and area type. const int idx = x+y*lw; layer->heights[idx] = s.y; layer->areas[idx] = chf.areas[i]; + // Check connection. unsigned char con = 0; for (int dir = 0; dir < 4; ++dir) @@ -673,3 +687,233 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, return true; } + +inline bool isConnected(rcHeightfieldLayer& layer, const int ia, const int ib, const int walkableClimb) +{ + if (layer.areas[ib] == RC_NULL_AREA) return false; + if (rcAbs((int)layer.heights[ia] - (int)layer.heights[ib]) > walkableClimb) return false; + return true; +} + +struct rcMonotoneRegion +{ + unsigned char neis[RC_MAX_NEIS]; + unsigned char nneis; + unsigned char regId; +}; + +static bool canMerge(rcMonotoneRegion* reg, unsigned char newRegId, const rcMonotoneRegion* regs, const int nregs) +{ + int count = 0; + const int nnei = (int)reg->nneis; + for (int i = 0; i < nnei; ++i) + { + if (regs[reg->neis[i]].regId == newRegId) + count++; + } + return count == 1; +} + +// TODO: move this somewhere else, once the layer meshing is done. +bool rcBuildLayerRegions(rcContext* ctx, rcHeightfieldLayer& layer, const int walkableClimb) +{ + rcAssert(ctx); + +// ctx->startTimer(RC_TIMER_BUILD_LAYERS); + + const int w = layer.width; + const int h = layer.height; + + rcAssert(layer.regs == 0); + + layer.regs = (unsigned char*)rcAlloc(sizeof(unsigned char)*w*h, RC_ALLOC_TEMP); + if (!layer.regs) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' (%d).", w*h); + return false; + } + memset(layer.regs,0xff,sizeof(unsigned char)*w*h); + + const int nsweeps = w; + rcScopedDelete sweeps = (rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP); + if (!sweeps) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' (%d).", nsweeps); + return false; + } + + + // Partition walkable area into monotone regions. + int prevCount[256]; + unsigned char regId = 0; + + for (int y = 0; y < h; ++y) + { + memset(prevCount,0,sizeof(int)*regId); + unsigned char sweepId = 0; + + for (int x = 0; x < w; ++x) + { + const int idx = x + y*w; + if (layer.areas[idx] == RC_NULL_AREA) continue; + + unsigned char sid = 0xff; + + // -x + const int xidx = (x-1)+y*w; + if (x > 0 && isConnected(layer, idx, xidx, walkableClimb)) + { + if (layer.regs[xidx] != 0xff) + sid = layer.regs[xidx]; + } + + if (sid == 0xff) + { + sid = sweepId++; + sweeps[sid].nei = 0xff; + sweeps[sid].ns = 0; + } + + // -y + const int yidx = x+(y-1)*w; + if (y > 0 && isConnected(layer, idx, yidx, walkableClimb)) + { + const unsigned char nr = layer.regs[yidx]; + if (nr != 0xff) + { + // Set neighbour when first valid neighbour is encoutered. + if (sweeps[sid].ns == 0) + sweeps[sid].nei = nr; + + if (sweeps[sid].nei == nr) + { + // Update existing neighbour + sweeps[sid].ns++; + prevCount[nr]++; + } + else + { + // This is hit if there is nore than one neighbour. + // Invalidate the neighbour. + sweeps[sid].nei = 0xff; + } + } + } + + layer.regs[idx] = sid; + } + + // Create unique ID. + for (int i = 0; i < sweepId; ++i) + { + // If the neighbour is set and there is only one continuous connection to it, + // the sweep will be merged with the previous one, else new region is created. + if (sweeps[i].nei != 0xff && prevCount[sweeps[i].nei] == (int)sweeps[i].ns) + { + sweeps[i].id = sweeps[i].nei; + } + else + { + if (regId == 255) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Region ID overflow."); + return false; + } + sweeps[i].id = regId++; + } + } + + // Remap local sweep ids to region ids. + for (int x = 0; x < w; ++x) + { + const int idx = x+y*w; + layer.regs[idx] = sweeps[layer.regs[idx]].id; + } + } + + // Allocate and init layer regions. + const int nregs = (int)regId; + rcScopedDelete regs = (rcMonotoneRegion*)rcAlloc(sizeof(rcMonotoneRegion)*nregs, RC_ALLOC_TEMP); + if (!regs) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' (%d).", nregs); + return false; + } + memset(regs, 0, sizeof(rcMonotoneRegion)*nregs); + for (int i = 0; i < nregs; ++i) + regs[i].regId = 0xff; + + // Find region neighbours. + 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; + + // Update neighbours + const int ymi = x+(y-1)*w; + if (y > 0 && isConnected(layer, idx, ymi, walkableClimb)) + { + const unsigned char rai = layer.regs[ymi]; + if (rai != 0xff && rai != ri) + { + addUniqueLast(regs[ri].neis, regs[ri].nneis, rai); + addUniqueLast(regs[rai].neis, regs[rai].nneis, ri); + } + } + } + } + + // Merge regions. + static const int MAX_STACK = 32; + unsigned char stack[MAX_STACK]; + int nstack = 0; + + unsigned char newRegId = 0; + + for (int i = 0; i < nregs; ++i) + { + if (regs[i].regId != 0xff) + continue; + + nstack = 0; + stack[nstack++] = (unsigned char)i; + + regs[i].regId = newRegId; + + while (nstack) + { + rcMonotoneRegion& reg = regs[stack[0]]; + nstack--; + for (int j = 0; j < nstack; ++j) + stack[j] = stack[j+1]; + + for (int j = 0; j < (int)reg.nneis; ++j) + { + const unsigned char nei = reg.neis[j]; + rcMonotoneRegion& regn = regs[nei]; + if (regn.regId != 0xff) + continue; + if (canMerge(®n, newRegId, regs, nregs)) + { + regn.regId = newRegId; + if (nstack < MAX_STACK) + stack[nstack++] = nei; + } + } + } + + newRegId++; + } + + for (int i = 0; i < w*h; ++i) + { + if (layer.regs[i] != 0xff) + layer.regs[i] = regs[layer.regs[i]].regId; + } + + return true; +} diff --git a/RecastDemo/Bin/Recast.app/Contents/MacOS/Recast b/RecastDemo/Bin/Recast.app/Contents/MacOS/Recast index 6b61005..a983f79 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/Source/Sample_TileMesh.cpp b/RecastDemo/Source/Sample_TileMesh.cpp index 57804a5..8b20bc1 100644 --- a/RecastDemo/Source/Sample_TileMesh.cpp +++ b/RecastDemo/Source/Sample_TileMesh.cpp @@ -665,7 +665,7 @@ void Sample_TileMesh::handleRender() if (m_lset && m_drawMode == DRAWMODE_HEIGHFIELD_LAYERS) { glDepthMask(GL_FALSE); - duDebugDrawHeightfieldLayers(&dd, *m_lset); + duDebugDrawHeightfieldLayersRegions(&dd, *m_lset); glDepthMask(GL_TRUE); } @@ -1113,6 +1113,11 @@ unsigned char* Sample_TileMesh::buildTileMesh(const int tx, const int ty, const m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build heighfield layers."); return 0; } + + for (int i = 0; i < m_lset->nlayers; ++i) + { + rcBuildLayerRegions(m_ctx, m_lset->layers[i], m_cfg.walkableClimb); + } }