Merge pull request #151 from Janiels/fix-detail-sampling
Use flood fill for potentially overlapping polys
This commit is contained in:
commit
f002387afc
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
if (!m_cap) m_cap = n;
|
||||||
while (m_cap < n) m_cap *= 2;
|
while (m_cap < n) m_cap *= 2;
|
||||||
int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP);
|
int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP);
|
||||||
if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
|
if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
|
||||||
rcFree(m_data);
|
rcFree(m_data);
|
||||||
m_data = newData;
|
m_data = newData;
|
||||||
}
|
|
||||||
m_size = n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -528,7 +528,7 @@ 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);
|
||||||
@ -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]];
|
||||||
|
|
||||||
|
// 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]];
|
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);
|
||||||
|
@ -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& stack)
|
rcHeightPatch& hp, rcIntArray& array)
|
||||||
{
|
{
|
||||||
// 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 cx = 0, cz = 0, ci =-1;
|
|
||||||
int dmin = RC_UNSET_HEIGHT;
|
int dmin = RC_UNSET_HEIGHT;
|
||||||
for (int k = 0; k < 9; ++k)
|
for (int j = 0; j < npoly && dmin > 0; ++j)
|
||||||
|
{
|
||||||
|
for (int k = 0; k < 9 && dmin > 0; ++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,123 +862,139 @@ 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);
|
||||||
int cx = stack[i+0];
|
array.push(startCellX);
|
||||||
int cy = stack[i+1];
|
array.push(startCellY);
|
||||||
int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
|
array.push(startSpanIndex);
|
||||||
hp.data[idx] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (stack.size() > 0)
|
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 ci = stack.pop();
|
if (array.size() < 3)
|
||||||
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);
|
||||||
|
array.push((int)chf.cells[(newX+bs)+(newY+bs)*chf.width].index + rcGetCon(cs, dir));
|
||||||
|
}
|
||||||
|
|
||||||
stack.push(ax);
|
rcSwap(dirs[directDir], dirs[3]);
|
||||||
stack.push(ay);
|
|
||||||
stack.push(ai);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
// Mark start locations.
|
|
||||||
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];
|
const rcCompactSpan& cs = chf.spans[ci];
|
||||||
hp.data[idx] = cs.y;
|
hp.data[cx-hp.xmin+(cy-hp.ymin)*hp.width] = 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;
|
||||||
|
|
||||||
|
// We cannot sample from this poly if it was created from polys
|
||||||
|
// of different regions. If it was then it could potentially be overlapping
|
||||||
|
// with polys of that region and the heights sampled here could be wrong.
|
||||||
|
if (region != RC_MULTIPLE_REGS)
|
||||||
|
{
|
||||||
// Copy the height from the same region, and mark region borders
|
// Copy the height from the same region, and mark region borders
|
||||||
// as seed points to fill the rest.
|
// as seed points to fill the rest.
|
||||||
for (int hy = 0; hy < hp.height; hy++)
|
for (int hy = 0; hy < hp.height; hy++)
|
||||||
@ -995,8 +1003,8 @@ static void getHeightData(const rcCompactHeightfield& chf,
|
|||||||
for (int hx = 0; hx < hp.width; hx++)
|
for (int hx = 0; hx < hp.width; hx++)
|
||||||
{
|
{
|
||||||
int x = hp.xmin + hx + bs;
|
int x = hp.xmin + hx + bs;
|
||||||
const rcCompactCell& c = chf.cells[x+y*chf.width];
|
const rcCompactCell& c = chf.cells[x + y*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; ++i)
|
||||||
{
|
{
|
||||||
const rcCompactSpan& s = chf.spans[i];
|
const rcCompactSpan& s = chf.spans[i];
|
||||||
if (s.reg == region)
|
if (s.reg == region)
|
||||||
@ -1014,7 +1022,7 @@ static void getHeightData(const rcCompactHeightfield& chf,
|
|||||||
{
|
{
|
||||||
const int ax = x + rcGetDirOffsetX(dir);
|
const int ax = x + rcGetDirOffsetX(dir);
|
||||||
const int ay = y + rcGetDirOffsetY(dir);
|
const int ay = y + rcGetDirOffsetY(dir);
|
||||||
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
|
const int ai = (int)chf.cells[ax + ay*chf.width].index + rcGetCon(s, dir);
|
||||||
const rcCompactSpan& as = chf.spans[ai];
|
const rcCompactSpan& as = chf.spans[ai];
|
||||||
if (as.reg != region)
|
if (as.reg != region)
|
||||||
{
|
{
|
||||||
@ -1024,37 +1032,38 @@ static void getHeightData(const rcCompactHeightfield& chf,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (border)
|
if (border)
|
||||||
{
|
push3(queue, x, y, i);
|
||||||
stack.push(x);
|
|
||||||
stack.push(y);
|
|
||||||
stack.push(i);
|
|
||||||
}
|
|
||||||
break;
|
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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user