Use flood fill for potentially overlapping polys

This changes the detail sampling to find height data by using the flood
fill method for polygons that were merged because of a border vertex
removal. This could potentially cause overlapping polygons to receive
the same region IDs which would later make the fast-path sampling in
getHeightData sample the wrong heights.

Also includes optimizations for this path and getHeightData itself.
Previously the seeding was responsible for 25% of the total time spent
in getHeightData in these cases. We now use the direction and prefer
moving directly towards the center of the polygon. This makes the
overhead of seedArrayWithPolyCenter virtually nonexistent (~2%
according to profiling).

Fix #146
This commit is contained in:
Jakob Botsch Nielsen 2016-01-14 11:11:06 +01:00
parent 5a342b83e8
commit 5fce286267
5 changed files with 214 additions and 169 deletions

View File

@ -513,6 +513,14 @@ void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh);
/// @see rcCompactSpan::reg /// @see rcCompactSpan::reg
static const unsigned short RC_BORDER_REG = 0x8000; static const unsigned short RC_BORDER_REG = 0x8000;
/// Polygon touches multiple regions.
/// If a polygon has this region ID it was merged with or created
/// from polygons of different regions during the polymesh
/// build step that removes redundant border vertices.
/// (Used during the polymesh and detail polymesh build processes)
/// @see rcPolyMesh::regs
static const unsigned short RC_MULTIPLE_REGS = 0;
/// Border vertex flag. /// Border vertex flag.
/// If a region ID has this bit set, then the associated element lies on /// If a region ID has this bit set, then the associated element lies on
/// a tile border. If a contour vertex's region ID has this bit set, the /// a tile border. If a contour vertex's region ID has this bit set, the
@ -1059,7 +1067,7 @@ inline int rcGetCon(const rcCompactSpan& s, int dir)
/// in the direction. /// in the direction.
inline int rcGetDirOffsetX(int dir) inline int rcGetDirOffsetX(int dir)
{ {
const int offset[4] = { -1, 0, 1, 0, }; static const int offset[4] = { -1, 0, 1, 0, };
return offset[dir&0x03]; return offset[dir&0x03];
} }
@ -1069,10 +1077,20 @@ inline int rcGetDirOffsetX(int dir)
/// in the direction. /// in the direction.
inline int rcGetDirOffsetY(int dir) inline int rcGetDirOffsetY(int dir)
{ {
const int offset[4] = { 0, 1, 0, -1 }; static const int offset[4] = { 0, 1, 0, -1 };
return offset[dir&0x03]; return offset[dir&0x03];
} }
/// Gets the direction for the specified offset. One of x and y should be 0.
/// @param[in] x The x offset. [Limits: -1 <= value <= 1]
/// @param[in] y The y offset. [Limits: -1 <= value <= 1]
/// @return The direction that represents the offset.
inline int rcGetDirForOffset(int x, int y)
{
static const int dirs[5] = { 3, 0, -1, 2, 1 };
return dirs[((y+1)<<1)+x];
}
/// @} /// @}
/// @name Layer, Contour, Polymesh, and Detail Mesh Functions /// @name Layer, Contour, Polymesh, and Detail Mesh Functions
/// @see rcHeightfieldLayer, rcContourSet, rcPolyMesh, rcPolyMeshDetail /// @see rcHeightfieldLayer, rcContourSet, rcPolyMesh, rcPolyMeshDetail

View File

@ -64,40 +64,54 @@ class rcIntArray
int m_size, m_cap; int m_size, m_cap;
inline rcIntArray(const rcIntArray&); inline rcIntArray(const rcIntArray&);
inline rcIntArray& operator=(const rcIntArray&); inline rcIntArray& operator=(const rcIntArray&);
void doResize(int n);
public: public:
/// Constructs an instance with an initial array size of zero. /// Constructs an instance with an initial array size of zero.
inline rcIntArray() : m_data(0), m_size(0), m_cap(0) {} rcIntArray() : m_data(0), m_size(0), m_cap(0) {}
/// Constructs an instance initialized to the specified size. /// Constructs an instance initialized to the specified size.
/// @param[in] n The initial size of the integer array. /// @param[in] n The initial size of the integer array.
inline rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); } rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); }
inline ~rcIntArray() { rcFree(m_data); } ~rcIntArray() { rcFree(m_data); }
/// Specifies the new size of the integer array. /// Specifies the new size of the integer array.
/// @param[in] n The new size of the integer array. /// @param[in] n The new size of the integer array.
void resize(int n); void resize(int n)
{
if (n > m_cap)
doResize(n);
m_size = n;
}
/// Push the specified integer onto the end of the array and increases the size by one. /// Push the specified integer onto the end of the array and increases the size by one.
/// @param[in] item The new value. /// @param[in] item The new value.
inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; } void push(int item) { resize(m_size+1); m_data[m_size-1] = item; }
/// Returns the value at the end of the array and reduces the size by one. /// Returns the value at the end of the array and reduces the size by one.
/// @return The value at the end of the array. /// @return The value at the end of the array.
inline int pop() { if (m_size > 0) m_size--; return m_data[m_size]; } int pop()
{
if (m_size > 0)
m_size--;
return m_data[m_size];
}
/// The value at the specified array index. /// The value at the specified array index.
/// @warning Does not provide overflow protection. /// @warning Does not provide overflow protection.
/// @param[in] i The index of the value. /// @param[in] i The index of the value.
inline const int& operator[](int i) const { return m_data[i]; } const int& operator[](int i) const { return m_data[i]; }
/// The value at the specified array index. /// The value at the specified array index.
/// @warning Does not provide overflow protection. /// @warning Does not provide overflow protection.
/// @param[in] i The index of the value. /// @param[in] i The index of the value.
inline int& operator[](int i) { return m_data[i]; } int& operator[](int i) { return m_data[i]; }
/// The current size of the integer array. /// The current size of the integer array.
inline int size() const { return m_size; } int size() const { return m_size; }
}; };
/// A simple helper class used to delete an array when it goes out of scope. /// A simple helper class used to delete an array when it goes out of scope.

View File

@ -72,17 +72,13 @@ void rcFree(void* ptr)
/// Using this method ensures the array is at least large enough to hold /// Using this method ensures the array is at least large enough to hold
/// the specified number of elements. This can improve performance by /// the specified number of elements. This can improve performance by
/// avoiding auto-resizing during use. /// avoiding auto-resizing during use.
void rcIntArray::resize(int n) void rcIntArray::doResize(int n)
{ {
if (n > m_cap) if (!m_cap) m_cap = n;
{ while (m_cap < n) m_cap *= 2;
if (!m_cap) m_cap = n; int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP);
while (m_cap < n) m_cap *= 2; if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP); rcFree(m_data);
if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int)); m_data = newData;
rcFree(m_data);
m_data = newData;
}
m_size = n;
} }

View File

@ -528,8 +528,8 @@ static int getPolyMergeValue(unsigned short* pa, unsigned short* pb,
return dx*dx + dy*dy; return dx*dx + dy*dy;
} }
static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb, static void mergePolyVerts(unsigned short* pa, unsigned short* pb, int ea, int eb,
unsigned short* tmp, const int nvp) unsigned short* tmp, const int nvp)
{ {
const int na = countPolyVerts(pa, nvp); const int na = countPolyVerts(pa, nvp);
const int nb = countPolyVerts(pb, nvp); const int nb = countPolyVerts(pb, nvp);
@ -895,7 +895,14 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
polys[npolys*nvp+0] = (unsigned short)hole[t[0]]; polys[npolys*nvp+0] = (unsigned short)hole[t[0]];
polys[npolys*nvp+1] = (unsigned short)hole[t[1]]; polys[npolys*nvp+1] = (unsigned short)hole[t[1]];
polys[npolys*nvp+2] = (unsigned short)hole[t[2]]; polys[npolys*nvp+2] = (unsigned short)hole[t[2]];
pregs[npolys] = (unsigned short)hreg[t[0]];
// If this polygon covers multiple region types then
// mark it as such
if (hreg[t[0]] != hreg[t[1]] || hreg[t[1]] != hreg[t[2]])
pregs[npolys] = RC_MULTIPLE_REGS;
else
pregs[npolys] = (unsigned short)hreg[t[0]];
pareas[npolys] = (unsigned char)harea[t[0]]; pareas[npolys] = (unsigned char)harea[t[0]];
npolys++; npolys++;
} }
@ -936,7 +943,10 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
// Found best, merge. // Found best, merge.
unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pa = &polys[bestPa*nvp];
unsigned short* pb = &polys[bestPb*nvp]; unsigned short* pb = &polys[bestPb*nvp];
mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp); mergePolyVerts(pa, pb, bestEa, bestEb, tmpPoly, nvp);
if (pregs[bestPa] != pregs[bestPb])
pregs[bestPa] = RC_MULTIPLE_REGS;
unsigned short* last = &polys[(npolys-1)*nvp]; unsigned short* last = &polys[(npolys-1)*nvp];
if (pb != last) if (pb != last)
memcpy(pb, last, sizeof(unsigned short)*nvp); memcpy(pb, last, sizeof(unsigned short)*nvp);
@ -1182,7 +1192,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe
// Found best, merge. // Found best, merge.
unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pa = &polys[bestPa*nvp];
unsigned short* pb = &polys[bestPb*nvp]; unsigned short* pb = &polys[bestPb*nvp];
mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp); mergePolyVerts(pa, pb, bestEa, bestEb, tmpPoly, nvp);
unsigned short* lastPoly = &polys[(npolys-1)*nvp]; unsigned short* lastPoly = &polys[(npolys-1)*nvp];
if (pb != lastPoly) if (pb != lastPoly)
memcpy(pb, lastPoly, sizeof(unsigned short)*nvp); memcpy(pb, lastPoly, sizeof(unsigned short)*nvp);

View File

@ -834,33 +834,25 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
return true; return true;
} }
static void seedArrayWithPolyCenter(rcContext* ctx, const rcCompactHeightfield& chf,
static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf, const unsigned short* poly, const int npoly,
const unsigned short* poly, const int npoly, const unsigned short* verts, const int bs,
const unsigned short* verts, const int bs, rcHeightPatch& hp, rcIntArray& array)
rcHeightPatch& hp, rcIntArray& stack)
{ {
// Floodfill the heightfield to get 2D height data,
// starting at vertex locations as seeds.
// Note: Reads to the compact heightfield are offset by border size (bs) // Note: Reads to the compact heightfield are offset by border size (bs)
// since border size offset is already removed from the polymesh vertices. // since border size offset is already removed from the polymesh vertices.
memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height);
stack.resize(0);
static const int offset[9*2] = static const int offset[9*2] =
{ {
0,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1, -1,0, 0,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1, -1,0,
}; };
// Use poly vertices as seed points for the flood fill. // Find cell closest to a poly vertex
for (int j = 0; j < npoly; ++j) int startCellX = 0, startCellY = 0, startSpanIndex = -1;
int dmin = RC_UNSET_HEIGHT;
for (int j = 0; j < npoly && dmin > 0; ++j)
{ {
int cx = 0, cz = 0, ci =-1; for (int k = 0; k < 9 && dmin > 0; ++k)
int dmin = RC_UNSET_HEIGHT;
for (int k = 0; k < 9; ++k)
{ {
const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0]; const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0];
const int ay = (int)verts[poly[j]*3+1]; const int ay = (int)verts[poly[j]*3+1];
@ -870,191 +862,208 @@ static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf,
continue; continue;
const rcCompactCell& c = chf.cells[(ax+bs)+(az+bs)*chf.width]; const rcCompactCell& c = chf.cells[(ax+bs)+(az+bs)*chf.width];
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni && dmin > 0; ++i)
{ {
const rcCompactSpan& s = chf.spans[i]; const rcCompactSpan& s = chf.spans[i];
int d = rcAbs(ay - (int)s.y); int d = rcAbs(ay - (int)s.y);
if (d < dmin) if (d < dmin)
{ {
cx = ax; startCellX = ax;
cz = az; startCellY = az;
ci = i; startSpanIndex = i;
dmin = d; dmin = d;
} }
} }
} }
if (ci != -1)
{
stack.push(cx);
stack.push(cz);
stack.push(ci);
}
} }
// Find center of the polygon using flood fill. rcAssert(startSpanIndex != -1);
int pcx = 0, pcz = 0; // Find center of the polygon
int pcx = 0, pcy = 0;
for (int j = 0; j < npoly; ++j) for (int j = 0; j < npoly; ++j)
{ {
pcx += (int)verts[poly[j]*3+0]; pcx += (int)verts[poly[j]*3+0];
pcz += (int)verts[poly[j]*3+2]; pcy += (int)verts[poly[j]*3+2];
} }
pcx /= npoly; pcx /= npoly;
pcz /= npoly; pcy /= npoly;
for (int i = 0; i < stack.size(); i += 3) // Use seeds array as a stack for DFS
array.resize(0);
array.push(startCellX);
array.push(startCellY);
array.push(startSpanIndex);
int dirs[] = { 0, 1, 2, 3 };
memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height);
// DFS to move to the center. Note that we need a DFS here and can not just move
// directly towards the center without recording intermediate nodes, even though the polygons
// are convex. In very rare we can get stuck due to contour simplification if we do not
// record nodes.
int cx = -1, cy = -1, ci = -1;
while (true)
{ {
int cx = stack[i+0]; if (array.size() < 3)
int cy = stack[i+1];
int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
hp.data[idx] = 1;
}
while (stack.size() > 0)
{
int ci = stack.pop();
int cy = stack.pop();
int cx = stack.pop();
// Check if close to center of the polygon.
if (rcAbs(cx-pcx) <= 1 && rcAbs(cy-pcz) <= 1)
{ {
stack.resize(0); ctx->log(RC_LOG_WARNING, "Walk towards polygon center failed to reach center");
stack.push(cx);
stack.push(cy);
stack.push(ci);
break; break;
} }
ci = array.pop();
cy = array.pop();
cx = array.pop();
if (cx == pcx && cy == pcy)
break;
// If we are already at the correct X-position, prefer direction
// directly towards the center in the Y-axis; otherwise prefer
// direction in the X-axis
int directDir;
if (cx == pcx)
directDir = rcGetDirForOffset(0, pcy > cy ? 1 : -1);
else
directDir = rcGetDirForOffset(pcx > cx ? 1 : -1, 0);
// Push the direct dir last so we start with this on next iteration
rcSwap(dirs[directDir], dirs[3]);
const rcCompactSpan& cs = chf.spans[ci]; const rcCompactSpan& cs = chf.spans[ci];
for (int i = 0; i < 4; i++)
for (int dir = 0; dir < 4; ++dir)
{ {
if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue; int dir = dirs[i];
if (rcGetCon(cs, dir) == RC_NOT_CONNECTED)
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; continue;
if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0) int newX = cx + rcGetDirOffsetX(dir);
int newY = cy + rcGetDirOffsetY(dir);
int hpx = newX - hp.xmin;
int hpy = newY - hp.ymin;
if (hpx < 0 || hpx >= hp.width || hpy < 0 || hpy >= hp.height)
continue; continue;
const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir); if (hp.data[hpx+hpy*hp.width] != 0)
continue;
int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width; hp.data[hpx+hpy*hp.width] = 1;
hp.data[idx] = 1; array.push(newX);
array.push(newY);
stack.push(ax); array.push((int)chf.cells[(newX+bs)+(newY+bs)*chf.width].index + rcGetCon(cs, dir));
stack.push(ay);
stack.push(ai);
} }
rcSwap(dirs[directDir], dirs[3]);
} }
array.resize(0);
// getHeightData seeds are given in coordinates with borders
array.push(cx+bs);
array.push(cy+bs);
array.push(ci);
memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
const rcCompactSpan& cs = chf.spans[ci];
// Mark start locations. hp.data[cx-hp.xmin+(cy-hp.ymin)*hp.width] = cs.y;
for (int i = 0; i < stack.size(); i += 3)
{
int cx = stack[i+0];
int cy = stack[i+1];
int ci = stack[i+2];
int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
const rcCompactSpan& cs = chf.spans[ci];
hp.data[idx] = cs.y;
// getHeightData seeds are given in coordinates with borders
stack[i+0] += bs;
stack[i+1] += bs;
}
} }
static void push3(rcIntArray& queue, int v1, int v2, int v3)
{
queue.resize(queue.size() + 3);
queue[queue.size() - 3] = v1;
queue[queue.size() - 2] = v2;
queue[queue.size() - 1] = v3;
}
static void getHeightData(const rcCompactHeightfield& chf, static void getHeightData(rcContext* ctx, const rcCompactHeightfield& chf,
const unsigned short* poly, const int npoly, const unsigned short* poly, const int npoly,
const unsigned short* verts, const int bs, const unsigned short* verts, const int bs,
rcHeightPatch& hp, rcIntArray& stack, rcHeightPatch& hp, rcIntArray& queue,
int region) int region)
{ {
// Note: Reads to the compact heightfield are offset by border size (bs) // Note: Reads to the compact heightfield are offset by border size (bs)
// since border size offset is already removed from the polymesh vertices. // since border size offset is already removed from the polymesh vertices.
stack.resize(0); queue.resize(0);
// Set all heights to RC_UNSET_HEIGHT.
memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
bool empty = true; bool empty = true;
// Copy the height from the same region, and mark region borders // We cannot sample from this poly if it was created from polys
// as seed points to fill the rest. // of different regions. If it was then it could potentially be overlapping
for (int hy = 0; hy < hp.height; hy++) // with polys of that region and the heights sampled here could be wrong.
if (region != RC_MULTIPLE_REGS)
{ {
int y = hp.ymin + hy + bs; // Copy the height from the same region, and mark region borders
for (int hx = 0; hx < hp.width; hx++) // as seed points to fill the rest.
for (int hy = 0; hy < hp.height; hy++)
{ {
int x = hp.xmin + hx + bs; int y = hp.ymin + hy + bs;
const rcCompactCell& c = chf.cells[x+y*chf.width]; for (int hx = 0; hx < hp.width; hx++)
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
{ {
const rcCompactSpan& s = chf.spans[i]; int x = hp.xmin + hx + bs;
if (s.reg == region) const rcCompactCell& c = chf.cells[x + y*chf.width];
for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i)
{ {
// Store height const rcCompactSpan& s = chf.spans[i];
hp.data[hx + hy*hp.width] = s.y; if (s.reg == region)
empty = false;
// If any of the neighbours is not in same region,
// add the current location as flood fill start
bool border = false;
for (int dir = 0; dir < 4; ++dir)
{ {
if (rcGetCon(s, dir) != RC_NOT_CONNECTED) // Store height
hp.data[hx + hy*hp.width] = s.y;
empty = false;
// If any of the neighbours is not in same region,
// add the current location as flood fill start
bool border = false;
for (int dir = 0; dir < 4; ++dir)
{ {
const int ax = x + rcGetDirOffsetX(dir); if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
const int ay = y + rcGetDirOffsetY(dir);
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
const rcCompactSpan& as = chf.spans[ai];
if (as.reg != region)
{ {
border = true; const int ax = x + rcGetDirOffsetX(dir);
break; const int ay = y + rcGetDirOffsetY(dir);
const int ai = (int)chf.cells[ax + ay*chf.width].index + rcGetCon(s, dir);
const rcCompactSpan& as = chf.spans[ai];
if (as.reg != region)
{
border = true;
break;
}
} }
} }
if (border)
push3(queue, x, y, i);
break;
} }
if (border)
{
stack.push(x);
stack.push(y);
stack.push(i);
}
break;
} }
} }
} }
} }
// if the polygon does not contian any points from the current region (rare, but happens) // if the polygon does not contain any points from the current region (rare, but happens)
// then use the cells closest to the polygon vertices as seeds to fill the height field // or if it could potentially be overlapping polygons of the same region,
// then use the center as the seed point.
if (empty) if (empty)
getHeightDataSeedsFromVertices(chf, poly, npoly, verts, bs, hp, stack); seedArrayWithPolyCenter(ctx, chf, poly, npoly, verts, bs, hp, queue);
static const int RETRACT_SIZE = 256; static const int RETRACT_SIZE = 256;
int head = 0; int head = 0;
while (head*3 < stack.size()) // We assume the seed is centered in the polygon, so a BFS to collect
// height data will ensure we do not move onto overlapping polygons and
// sample wrong heights.
while (head*3 < queue.size())
{ {
int cx = stack[head*3+0]; int cx = queue[head*3+0];
int cy = stack[head*3+1]; int cy = queue[head*3+1];
int ci = stack[head*3+2]; int ci = queue[head*3+2];
head++; head++;
if (head >= RETRACT_SIZE) if (head >= RETRACT_SIZE)
{ {
head = 0; head = 0;
if (stack.size() > RETRACT_SIZE*3) if (queue.size() > RETRACT_SIZE*3)
memmove(&stack[0], &stack[RETRACT_SIZE*3], sizeof(int)*(stack.size()-RETRACT_SIZE*3)); memmove(&queue[0], &queue[RETRACT_SIZE*3], sizeof(int)*(queue.size()-RETRACT_SIZE*3));
stack.resize(stack.size()-RETRACT_SIZE*3); queue.resize(queue.size()-RETRACT_SIZE*3);
} }
const rcCompactSpan& cs = chf.spans[ci]; const rcCompactSpan& cs = chf.spans[ci];
@ -1067,7 +1076,7 @@ static void getHeightData(const rcCompactHeightfield& chf,
const int hx = ax - hp.xmin - bs; const int hx = ax - hp.xmin - bs;
const int hy = ay - hp.ymin - bs; const int hy = ay - hp.ymin - bs;
if (hx < 0 || hx >= hp.width || hy < 0 || hy >= hp.height) if ((unsigned int)hx >= (unsigned int)hp.width || (unsigned int)hy >= (unsigned int)hp.height)
continue; continue;
if (hp.data[hx + hy*hp.width] != RC_UNSET_HEIGHT) if (hp.data[hx + hy*hp.width] != RC_UNSET_HEIGHT)
@ -1078,9 +1087,7 @@ static void getHeightData(const rcCompactHeightfield& chf,
hp.data[hx + hy*hp.width] = as.y; hp.data[hx + hy*hp.width] = as.y;
stack.push(ax); push3(queue, ax, ay, ai);
stack.push(ay);
stack.push(ai);
} }
} }
} }
@ -1133,7 +1140,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
rcIntArray edges(64); rcIntArray edges(64);
rcIntArray tris(512); rcIntArray tris(512);
rcIntArray stack(512); rcIntArray arr(512);
rcIntArray samples(512); rcIntArray samples(512);
float verts[256*3]; float verts[256*3];
rcHeightPatch hp; rcHeightPatch hp;
@ -1240,7 +1247,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
hp.ymin = bounds[i*4+2]; hp.ymin = bounds[i*4+2];
hp.width = bounds[i*4+1]-bounds[i*4+0]; hp.width = bounds[i*4+1]-bounds[i*4+0];
hp.height = bounds[i*4+3]-bounds[i*4+2]; hp.height = bounds[i*4+3]-bounds[i*4+2];
getHeightData(chf, p, npoly, mesh.verts, borderSize, hp, stack, mesh.regs[i]); getHeightData(ctx, chf, p, npoly, mesh.verts, borderSize, hp, arr, mesh.regs[i]);
// Build detail mesh. // Build detail mesh.
int nverts = 0; int nverts = 0;