Skip to content

Commit

Permalink
Use flood fill for potentially overlapping polys
Browse files Browse the repository at this point in the history
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 recastnavigation#146
  • Loading branch information
jakobbotsch committed Jan 14, 2016
1 parent 5a342b8 commit 5fce286
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 169 deletions.
22 changes: 20 additions & 2 deletions Recast/Include/Recast.h
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,14 @@ void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh);
/// @see rcCompactSpan::reg
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.
/// 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
Expand Down Expand Up @@ -1059,7 +1067,7 @@ inline int rcGetCon(const rcCompactSpan& s, int dir)
/// in the direction.
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];
}

Expand All @@ -1069,10 +1077,20 @@ inline int rcGetDirOffsetX(int dir)
/// in the direction.
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];
}

/// 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
/// @see rcHeightfieldLayer, rcContourSet, rcPolyMesh, rcPolyMeshDetail
Expand Down
32 changes: 23 additions & 9 deletions Recast/Include/RecastAlloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,40 +64,54 @@ class rcIntArray
int m_size, m_cap;
inline rcIntArray(const rcIntArray&);
inline rcIntArray& operator=(const rcIntArray&);

void doResize(int n);
public:

/// 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.
/// @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); }
inline ~rcIntArray() { rcFree(m_data); }
rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); }
~rcIntArray() { rcFree(m_data); }

/// Specifies 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.
/// @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.
/// @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.
/// @warning Does not provide overflow protection.
/// @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.
/// @warning Does not provide overflow protection.
/// @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.
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.
Expand Down
18 changes: 7 additions & 11 deletions Recast/Source/RecastAlloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,13 @@ void rcFree(void* ptr)
/// Using this method ensures the array is at least large enough to hold
/// the specified number of elements. This can improve performance by
/// 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;
int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP);
if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
rcFree(m_data);
m_data = newData;
}
m_size = n;
if (!m_cap) m_cap = n;
while (m_cap < n) m_cap *= 2;
int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP);
if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
rcFree(m_data);
m_data = newData;
}

22 changes: 16 additions & 6 deletions Recast/Source/RecastMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -528,8 +528,8 @@ static int getPolyMergeValue(unsigned short* pa, unsigned short* pb,
return dx*dx + dy*dy;
}

static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb,
unsigned short* tmp, const int nvp)
static void mergePolyVerts(unsigned short* pa, unsigned short* pb, int ea, int eb,
unsigned short* tmp, const int nvp)
{
const int na = countPolyVerts(pa, nvp);
const int nb = countPolyVerts(pb, nvp);
Expand Down Expand Up @@ -695,7 +695,7 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", numRemovedVerts*nvp);
return false;
}

int nhreg = 0;
rcScopedDelete<int> hreg = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
if (!hreg)
Expand Down Expand Up @@ -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+1] = (unsigned short)hole[t[1]];
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]];
npolys++;
}
Expand Down Expand Up @@ -936,7 +943,10 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
// Found best, merge.
unsigned short* pa = &polys[bestPa*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];
if (pb != last)
memcpy(pb, last, sizeof(unsigned short)*nvp);
Expand Down Expand Up @@ -1182,7 +1192,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe
// Found best, merge.
unsigned short* pa = &polys[bestPa*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];
if (pb != lastPoly)
memcpy(pb, lastPoly, sizeof(unsigned short)*nvp);
Expand Down
Loading

0 comments on commit 5fce286

Please sign in to comment.