Recast: revert to post polygon transformation

In commit a2d5d52dc4e571388b9b86f21e40a5110a69665e, the logic has been adjusted to build the polygon in the right order in the algorithm itself, but some indices are incorrect causing bad tesselation. The issue is somewhere in RecastMesh.cpp, writing polygon indices in the incorrect order '0, 3, 2, 1' instead of '3, 2, 1, 0'.  Reverted to using "REVERSE_DIRECTION 0" for RecastMesh.cpp.
Additional notes, tesselation appears correct when building the mesh with the layered partitioner, monotone and watershed causes bad tesselation. The vertex order is however still incorrect.
This commit is contained in:
Kawe Mazidjatari 2024-10-17 13:09:43 +02:00
parent a8b302f165
commit 1e48d8abd9
4 changed files with 153 additions and 157 deletions

View File

@ -1222,7 +1222,6 @@ unsigned char* Editor_TileMesh::buildTileMesh(const int tx, const int ty, const
return 0;
}
//rcFlipPolyMesh(*m_pmesh);
if (!rcBuildPolyMeshDetail(m_ctx, *m_pmesh, *m_chf,
m_cfg.detailSampleDist, m_cfg.detailSampleMaxError,
*m_dmesh))
@ -1231,7 +1230,6 @@ unsigned char* Editor_TileMesh::buildTileMesh(const int tx, const int ty, const
return 0;
}
//rcFlipPolyMeshDetail(*m_dmesh,m_pmesh->nverts);
if (!m_keepInterResults)
{
rcFreeCompactHeightfield(m_chf);

View File

@ -1164,9 +1164,6 @@ bool rcCopyPolyMesh(rcContext* ctx, const rcPolyMesh& src, rcPolyMesh& dst);
/// @returns True if the operation completed successfully.
bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh);
void rcFlipPolyMesh(rcPolyMesh& mesh);
void rcFlipPolyMeshDetail(rcPolyMeshDetail& mdetail, int poly_tris);
/// @}
#endif // RECAST_H

View File

@ -184,7 +184,7 @@ inline bool leftOn(const int* a, const int* b, const int* c)
{
return area2(a, b, c) <= 0;
}
#define REVERSE_DIRECTION 1
#define REVERSE_DIRECTION 0
inline bool right(const int* a, const int* b, const int* c)
{
return area2(a, b, c) > 0;
@ -254,7 +254,7 @@ static bool vequal(const int* a, const int* b)
#endif
// Returns T if (v_i, v_j) is a proper internal *or* external
// diagonal of P, *ignoring edges incident to v_i and v_j*.
static bool diagonalie(int i, int j, int n, const int* verts, int* indices)
static bool diagonalie(int i, int j, int n, const int* verts, const int* indices)
{
const int* d0 = &verts[(indices[i] & 0x0fffffff) * 4];
const int* d1 = &verts[(indices[j] & 0x0fffffff) * 4];
@ -281,7 +281,7 @@ static bool diagonalie(int i, int j, int n, const int* verts, int* indices)
// Returns true if the diagonal (i,j) is strictly internal to the
// polygon P in the neighborhood of the i endpoint.
static bool inCone(int i, int j, int n, const int* verts, int* indices)
static bool inCone(int i, int j, int n, const int* verts, const int* indices)
{
const int* pi = &verts[(indices[i] & 0x0fffffff) * 4];
const int* pj = &verts[(indices[j] & 0x0fffffff) * 4];
@ -306,13 +306,13 @@ static bool inCone(int i, int j, int n, const int* verts, int* indices)
// Returns T if (v_i, v_j) is a proper internal
// diagonal of P.
static bool diagonal(int i, int j, int n, const int* verts, int* indices)
static bool diagonal(int i, int j, int n, const int* verts, const int* indices)
{
return inCone(i, j, n, verts, indices) && diagonalie(i, j, n, verts, indices);
}
static bool diagonalieLoose(int i, int j, int n, const int* verts, int* indices)
static bool diagonalieLoose(int i, int j, int n, const int* verts, const int* indices)
{
const int* d0 = &verts[(indices[i] & 0x0fffffff) * 4];
const int* d1 = &verts[(indices[j] & 0x0fffffff) * 4];
@ -337,7 +337,7 @@ static bool diagonalieLoose(int i, int j, int n, const int* verts, int* indices)
return true;
}
static bool inConeLoose(int i, int j, int n, const int* verts, int* indices)
static bool inConeLoose(int i, int j, int n, const int* verts, const int* indices)
{
const int* pi = &verts[(indices[i] & 0x0fffffff) * 4];
const int* pj = &verts[(indices[j] & 0x0fffffff) * 4];
@ -360,7 +360,7 @@ static bool inConeLoose(int i, int j, int n, const int* verts, int* indices)
#endif
}
static bool diagonalLoose(int i, int j, int n, const int* verts, int* indices)
static bool diagonalLoose(int i, int j, int n, const int* verts, const int* indices)
{
return inConeLoose(i, j, n, verts, indices) && diagonalieLoose(i, j, n, verts, indices);
}
@ -670,10 +670,10 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho
{
int* e = &edges[m * 3];
#if REVERSE_DIRECTION
if (e[2] == b)
if (e[0] == b)
{
// Exists, increment vertex share count.
e[1]++;
e[2]++;
exists = true;
}
#else
@ -689,7 +689,7 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho
if (!exists)
{
int* e = &edges[nedges*3];
e[0] = a;
e[0] = a; // todo for REVERSE_DIRECTION, figure out if we need to flip here as well.
e[1] = b;
e[2] = 1;
nedges++;
@ -1033,6 +1033,77 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
return true;
}
void flipPolyMesh(rcPolyMesh& mesh)
{
for (int i = 0; i < mesh.npolys; i++)
{
const int nvp = mesh.nvp;
auto polyBegin = mesh.polys+2*nvp*i;
auto neisBegins = mesh.polys+2*nvp*i+nvp;
int curCount = 0;
for (int j = 0; j < nvp; j++)
{
if (polyBegin[j] != 0xffff)
curCount++;
else
break;
}
// Flip order of vertexes
for (int j = 0; j < curCount/2; j++)
{
rdSwap(polyBegin[j], polyBegin[curCount-j-1]);
rdSwap(neisBegins[j], neisBegins[curCount-j-1]);
}
if (curCount)
{
// Shift neis directions left, this is needed because the neis index edges
// not vertexes
const unsigned short zval = neisBegins[0];
for (int j = 0; j < curCount-1; j++)
{
neisBegins[j] = neisBegins[j + 1];
}
neisBegins[curCount-1] = zval;
}
// Flip neis directions.
/*
original code for reference
cur.x ==0 && next.x ==0 -> 0
cur.y ==h && next.x ==h -> 1
cur.x ==w && next.x ==w -> 2
cur.y ==0 && next.y ==0 -> 3
3
###
0 ### 2
###
1
*/
//const int lkup[4] = { 0,1,2,3 }; // NOOP
const int lkup[4] = { 2,1,0,3 }; // flip x only
//const int lkup[4] = { 0,3,2,1 }; // flip y only
//const int lkup[4] = { 2,3,0,1 }; // flip x and y
//const int lkup[4] = { 3,0,1,2 }; // exchange x/y (90 deg)
//const int lkup[4] = { 1,2,3,0 }; // -90 deg
//const int lkup[4] = { 2,3,0,1 }; // 180 deg
for (int j = 0; j < curCount; j++)
{
if ((neisBegins[j] & 0x8000) && (neisBegins[j] != 0xffff))
{
neisBegins[j] = (unsigned short)lkup[neisBegins[j] & 0x3] | 0x8000;
}
}
}
}
/// @par
///
/// @note If the mesh data is to be used to construct a Detour navigation mesh, then the upper
@ -1374,9 +1445,13 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe
break;
vi[0] = p[0];
#if REVERSE_DIRECTION
vi[1] = p[j];
vi[2] = p[j-1];
#else
vi[1] = p[j-1];
vi[2] = p[j];
#endif
for (int k = 0; k < 3; k++)
{
const unsigned short* v = &mesh.verts[vi[k]*3];
@ -1400,6 +1475,10 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff);
}
#if !REVERSE_DIRECTION
flipPolyMesh(mesh);
#endif // !REVERSE_DIRECTION
return true;
}
@ -1646,84 +1725,3 @@ bool rcCopyPolyMesh(rcContext* ctx, const rcPolyMesh& src, rcPolyMesh& dst)
return true;
}
void shift_left(unsigned short* arr, int count)
{
if (count == 0)return;
auto zval = arr[0];
for (int i = 0; i < count-1; i++)
{
arr[i] = arr[i + 1];
}
arr[count - 1] = zval;
}
void flip_neis_direction(unsigned short* arr, int count)
{
//const int lkup[4] = { 0,1,2,3 }; //NOOP
const int lkup[4] = { 2,1,0,3 };// flip x only
//const int lkup[4] = { 0,3,2,1 }; //flip y only
//const int lkup[4] = { 2,3,0,1 }; //flip x and y
//const int lkup[4] = { 3,0,1,2 }; //exchange x/y (90 deg)
//const int lkup[4] = { 1,2,3,0 }; //-90 deg
//const int lkup[4] = { 2,3,0,1 }; //180 deg
for (int i = 0; i < count; i++)
{
if ((arr[i] & 0x8000) && (arr[i] != 0xffff))
{
arr[i] = (unsigned short)lkup[arr[i] & 0x3] | 0x8000;
}
}
/* original code for reference
cur.x ==0 && next.x ==0 -> 0
cur.y ==h && next.x ==h -> 1
cur.x ==w && next.x ==w -> 2
cur.y ==0 && next.y ==0 -> 3
3
###
0 ### 2
###
1
if ((int)va[0] == 0 && (int)vb[0] == 0)
p[nvp+j] = 0x8000 | 0;
else if ((int)va[1] == h && (int)vb[1] == h)
p[nvp+j] = 0x8000 | 1;
else if ((int)va[0] == w && (int)vb[0] == w)
p[nvp+j] = 0x8000 | 2;
else if ((int)va[1] == 0 && (int)vb[1] == 0)
p[nvp+j] = 0x8000 | 3;
*/
}
void rcFlipPolyMesh(rcPolyMesh& /*mesh*/)
{
#if !REVERSE_DIRECTION
for (int i = 0; i < mesh.npolys; i++)
{
int max_verts = mesh.nvp;
auto poly_begin = mesh.polys + 2 * max_verts*i;
auto poly_begin_neis = mesh.polys + 2 * max_verts*i+max_verts;//i.e. every second thing in this array is poly neis
int cur_count = 0;
for (int j = 0; j < max_verts; j++)
{
if (poly_begin[j] != 0xffff)
cur_count++;
else
break;
}
//flip order of vertexes
///*
for (int j = 0; j < cur_count / 2; j++)
{
std::swap(poly_begin[j], poly_begin[cur_count - j - 1]);
std::swap(poly_begin_neis[j], poly_begin_neis[cur_count - j - 1]);
}
//*/
shift_left(poly_begin_neis, cur_count); //this is needed because the neis index edges not vertexes
flip_neis_direction(poly_begin_neis, cur_count);
}
#endif // !REVERSE_DIRECTION
}

View File

@ -21,7 +21,7 @@
#include "Shared/Include/SharedAssert.h"
static const unsigned RC_UNSET_HEIGHT = 0xffff;
#define REVERSE_DIRECTION 1
#define REVERSE_DIRECTION 1 // REVERSE_DIRECTION 0 is broken, 1 is producing correct results.
struct rcHeightPatch
{
@ -296,32 +296,13 @@ static int addEdge(rcContext* ctx, int* edges, int& nedges, const int maxEdges,
if (e == EV_UNDEF)
{
int* edge = &edges[nedges*4];
edge[0] = s;
edge[1] = t;
edge[2] = l;
edge[3] = r;
return nedges++;
}
else
{
return EV_UNDEF;
}
}
static int addEdgeFlipped(rcContext* ctx, int* edges, int& nedges, const int maxEdges, int s, int t, int l, int r)
{
if (nedges >= maxEdges)
{
ctx->log(RC_LOG_ERROR, "addEdge: Too many edges (%d/%d).", nedges, maxEdges);
return EV_UNDEF;
}
// Add edge if not already in the triangulation.
int e = findEdge(edges, nedges, s, t);
if (e == EV_UNDEF)
{
int* edge = &edges[nedges * 4];
#if REVERSE_DIRECTION
edge[0] = t;
edge[1] = s;
#else
edge[0] = s;
edge[1] = t;
#endif
edge[2] = l;
edge[3] = r;
return nedges++;
@ -464,10 +445,8 @@ static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges
#if REVERSE_DIRECTION
#define updateFace updateRightFace
#define addEdgeN addEdgeFlipped
#else
#define updateFace updateLeftFace
#define addEdgeN addEdge
#endif
// Add new triangle or update edge info if s-t is on hull.
if (pt < npts)
@ -478,14 +457,14 @@ static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges
// Add new edge or update face info of old edge.
e = findEdge(edges, nedges, pt, s);
if (e == EV_UNDEF)
addEdgeN(ctx, edges, nedges, maxEdges, pt, s, nfaces, EV_UNDEF);
addEdge(ctx, edges, nedges, maxEdges, pt, s, nfaces, EV_UNDEF);
else
updateFace(&edges[e*4], pt, s, nfaces);
// Add new edge or update face info of old edge.
e = findEdge(edges, nedges, t, pt);
if (e == EV_UNDEF)
addEdgeN(ctx, edges, nedges, maxEdges, t, pt, nfaces, EV_UNDEF);
addEdge(ctx, edges, nedges, maxEdges, t, pt, nfaces, EV_UNDEF);
else
updateFace(&edges[e*4], t, pt, nfaces);
@ -510,7 +489,7 @@ void delaunayHull(rcContext* ctx, const int npts, const float* pts,
edges.resize(maxEdges*4);
for (int i = 0, j = nhull-1; i < nhull; j=i++)
addEdge(ctx, &edges[0], nedges, maxEdges, hull[j],hull[i], EV_HULL, EV_UNDEF);
addEdge(ctx, &edges[0], nedges, maxEdges, hull[i],hull[j], EV_HULL, EV_UNDEF);
int currentEdge = 0;
while (currentEdge < nedges)
@ -741,9 +720,15 @@ static void setTriFlags(rdIntArray& tris, int nhull, int* hull)
int b = tris[i+1];
int c = tris[i+2];
unsigned short flags = 0;
#if REVERSE_DIRECTION
flags |= (onHull(a, c, nhull, hull) ? RD_DETAIL_EDGE_BOUNDARY : 0) << 0;
flags |= (onHull(c, b, nhull, hull) ? RD_DETAIL_EDGE_BOUNDARY : 0) << 2;
flags |= (onHull(b, a, nhull, hull) ? RD_DETAIL_EDGE_BOUNDARY : 0) << 4;
#else
flags |= (onHull(a, b, nhull, hull) ? RD_DETAIL_EDGE_BOUNDARY : 0) << 0;
flags |= (onHull(b, c, nhull, hull) ? RD_DETAIL_EDGE_BOUNDARY : 0) << 2;
flags |= (onHull(c, a, nhull, hull) ? RD_DETAIL_EDGE_BOUNDARY : 0) << 4;
#endif
tris[i + 3] = (int)flags;
}
}
@ -1254,6 +1239,40 @@ static void getHeightData(rcContext* ctx, const rcCompactHeightfield& chf,
}
}
void flipPolyMeshDetail(rcPolyMeshDetail& mdetail, const int nverts)
{
for (int i = 0; i < mdetail.ntris; i++)
{
unsigned char* t = mdetail.tris + i * 4;
bool skip = false;
for (int j = 0; j < 3; j++)
{
// Vert is a poly vert not a detail vert, skip it.
if (t[j] < nverts)
{
skip = true;
break;
}
}
if (skip)
continue;
rdSwap(t[0], t[2]);
// Flip tri flags.
const unsigned char tf = t[3];
unsigned char flags = 0;
flags |= ((tf >> 2) & 0b11) << 0;
flags |= ((tf >> 0) & 0b11) << 2;
flags |= ((tf >> 4) & 0b11) << 4;
t[3] = flags;
}
}
/// @par
///
/// See the #rcConfig documentation for more information on the configuration parameters.
@ -1476,14 +1495,25 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
{
const int* t = &tris[j*4];
#if REVERSE_DIRECTION
dmesh.tris[dmesh.ntris*4+0] = (unsigned char)t[0];
dmesh.tris[dmesh.ntris*4+1] = (unsigned char)t[2];
dmesh.tris[dmesh.ntris*4+2] = (unsigned char)t[1];
dmesh.tris[dmesh.ntris*4+3] = (unsigned char)t[3];
#else
dmesh.tris[dmesh.ntris*4+0] = (unsigned char)t[0];
dmesh.tris[dmesh.ntris*4+1] = (unsigned char)t[1];
dmesh.tris[dmesh.ntris*4+2] = (unsigned char)t[2];
dmesh.tris[dmesh.ntris*4+3] = (unsigned char)t[3];
#endif // !REVERSE_DIRECTION
dmesh.ntris++;
}
}
#ifndef REVERSE_DIRECTION
flipPolyMeshDetail(dmesh, mesh.nverts)
#endif // !REVERSE_DIRECTION
return true;
}
@ -1563,30 +1593,3 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
return true;
}
#if !REVERSE_DIRECTION
static unsigned char flip_flags(unsigned char flags_in)
{
unsigned char flags = 0;
flags |= ((flags_in >>2) & 0b11) << 0;
flags |= ((flags_in >>0) & 0b11) << 2;
flags |= ((flags_in >>4) & 0b11) << 4;
return flags;
}
#endif // !REVERSE_DIRECTION
void rcFlipPolyMeshDetail(rcPolyMeshDetail& /*mdetail*/,int /*poly_tris*/)
{
#if !REVERSE_DIRECTION
for (int i = 0; i < mdetail.ntris; i++)
{
auto tri_begin = mdetail.tris + i * 4;
bool skip = false;
for (int j = 0; j < 3; j++)
if (tri_begin[j] < poly_tris)
skip = true;
if (skip)
continue;
std::swap(tri_begin[0], tri_begin[2]);
tri_begin[3]=flip_flags(tri_begin[3]);
}
#endif // !REVERSE_DIRECTION
}