Fixed out of bounds bug in rcBuildLayerRegions. Improved region merging.

This commit is contained in:
Mikko Mononen 2011-02-28 08:18:44 +00:00
parent ce6f2a52fc
commit a1babd6803
2 changed files with 58 additions and 40 deletions

View File

@ -697,23 +697,30 @@ inline bool isConnected(rcHeightfieldLayer& layer, const int ia, const int ib, c
struct rcMonotoneRegion struct rcMonotoneRegion
{ {
int area;
unsigned char neis[RC_MAX_NEIS]; unsigned char neis[RC_MAX_NEIS];
unsigned char nneis; unsigned char nneis;
unsigned char regId; unsigned char regId;
}; };
static bool canMerge(rcMonotoneRegion* reg, unsigned char newRegId, const rcMonotoneRegion* regs, const int nregs) static bool canMerge(unsigned char oldRegId, unsigned char newRegId, const rcMonotoneRegion* regs, const int nregs)
{ {
int count = 0; int count = 0;
const int nnei = (int)reg->nneis; for (int i = 0; i < nregs; ++i)
for (int i = 0; i < nnei; ++i)
{ {
if (regs[reg->neis[i]].regId == newRegId) const rcMonotoneRegion& reg = regs[i];
if (reg.regId != oldRegId) continue;
const int nnei = (int)reg.nneis;
for (int j = 0; j < nnei; ++j)
{
if (regs[reg.neis[j]].regId == newRegId)
count++; count++;
} }
}
return count == 1; return count == 1;
} }
// TODO: move this somewhere else, once the layer meshing is done. // TODO: move this somewhere else, once the layer meshing is done.
bool rcBuildLayerRegions(rcContext* ctx, rcHeightfieldLayer& layer, const int walkableClimb) bool rcBuildLayerRegions(rcContext* ctx, rcHeightfieldLayer& layer, const int walkableClimb)
{ {
@ -741,7 +748,7 @@ bool rcBuildLayerRegions(rcContext* ctx, rcHeightfieldLayer& layer, const int wa
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' (%d).", nsweeps); ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' (%d).", nsweeps);
return false; return false;
} }
memset(sweeps,0,sizeof(rcLayerSweepSpan)*nsweeps);
// Partition walkable area into monotone regions. // Partition walkable area into monotone regions.
int prevCount[256]; int prevCount[256];
@ -749,6 +756,7 @@ bool rcBuildLayerRegions(rcContext* ctx, rcHeightfieldLayer& layer, const int wa
for (int y = 0; y < h; ++y) for (int y = 0; y < h; ++y)
{ {
if (regId > 0)
memset(prevCount,0,sizeof(int)*regId); memset(prevCount,0,sizeof(int)*regId);
unsigned char sweepId = 0; unsigned char sweepId = 0;
@ -827,6 +835,7 @@ bool rcBuildLayerRegions(rcContext* ctx, rcHeightfieldLayer& layer, const int wa
for (int x = 0; x < w; ++x) for (int x = 0; x < w; ++x)
{ {
const int idx = x+y*w; const int idx = x+y*w;
if (layer.regs[idx] != 0xff)
layer.regs[idx] = sweeps[layer.regs[idx]].id; layer.regs[idx] = sweeps[layer.regs[idx]].id;
} }
} }
@ -853,6 +862,9 @@ bool rcBuildLayerRegions(rcContext* ctx, rcHeightfieldLayer& layer, const int wa
if (ri == 0xff) if (ri == 0xff)
continue; continue;
// Update area.
regs[ri].area++;
// Update neighbours // Update neighbours
const int ymi = x+(y-1)*w; const int ymi = x+(y-1)*w;
if (y > 0 && isConnected(layer, idx, ymi, walkableClimb)) if (y > 0 && isConnected(layer, idx, ymi, walkableClimb))
@ -867,47 +879,53 @@ bool rcBuildLayerRegions(rcContext* ctx, rcHeightfieldLayer& layer, const int wa
} }
} }
// Merge regions. for (int i = 0; i < nregs; ++i)
static const int MAX_STACK = 32; regs[i].regId = (unsigned char)i;
unsigned char stack[MAX_STACK];
int nstack = 0;
unsigned char newRegId = 0;
for (int i = 0; i < nregs; ++i) for (int i = 0; i < nregs; ++i)
{ {
if (regs[i].regId != 0xff) rcMonotoneRegion& reg = regs[i];
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];
int merge = -1;
int mergea = 0;
for (int j = 0; j < (int)reg.nneis; ++j) for (int j = 0; j < (int)reg.nneis; ++j)
{ {
const unsigned char nei = reg.neis[j]; const unsigned char nei = reg.neis[j];
rcMonotoneRegion& regn = regs[nei]; rcMonotoneRegion& regn = regs[nei];
if (regn.regId != 0xff) if (reg.regId == regn.regId)
continue; continue;
if (canMerge(&regn, newRegId, regs, nregs)) if (regn.area > mergea)
{ {
regn.regId = newRegId; if (canMerge(reg.regId, regn.regId, regs, nregs))
if (nstack < MAX_STACK) {
stack[nstack++] = nei; mergea = regn.area;
merge = (int)nei;
} }
} }
} }
if (merge != -1)
{
const unsigned char oldId = reg.regId;
const unsigned char newId = regs[merge].regId;
for (int j = 0; j < nregs; ++j)
if (regs[j].regId == oldId)
regs[j].regId = newId;
}
}
newRegId++; // Compact ids.
} unsigned char remap[256];
memset(remap, 0, 256);
// Find number of unique regions.
regId = 0;
for (int i = 0; i < nregs; ++i)
remap[regs[i].regId] = 1;
for (int i = 0; i < 256; ++i)
if (remap[i])
remap[i] = regId++;
// Remap ids.
for (int i = 0; i < nregs; ++i)
regs[i].regId = remap[regs[i].regId];
for (int i = 0; i < w*h; ++i) for (int i = 0; i < w*h; ++i)
{ {