Skip to content

Commit

Permalink
UPBGE: Fix navmesh rebuild after mesh replacement. (#778)
Browse files Browse the repository at this point in the history
Previously the navmesh rebuilding was failing because of two issues,
first the mesh used as source was always the one from the blender object
secondly the navmesh itself wasn't updated in the obstacle simulation.

To solve this first issue the function BuildVertIndArrays is calling
two sub functions, BuildFromDerivedMesh for creating from a derived
mesh and benefit from CD_RECAST layer, if this function fails to get
the recast layer BuildFromMesh is called and generate information from
a RAS_Mesh without detailed meshes.
In this last function the indices computation is solved by the requirement
of Detour library to use 6 indices per polygon, the unused indices are
discarded by value 0xFFFF.

The second issue is solved by calling AddObstaclesForNavMesh only from
KX_NavMeshObject::BuildNavMesh, this call is preceded by a call to
DestroyObstacleForObj intenting to remove the previous obstacle
associated to this navmesh.

Fix issue #633.
  • Loading branch information
panzergame authored Sep 29, 2018
1 parent b9001b9 commit 5b8cb1e
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 138 deletions.
3 changes: 0 additions & 3 deletions source/gameengine/Converter/BL_BlenderDataConversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1810,9 +1810,6 @@ void BL_ConvertBlenderObjects(struct Main *maggie,
KX_NavMeshObject *navmesh = static_cast<KX_NavMeshObject *>(gameobj);
navmesh->SetVisible(false, true);
navmesh->BuildNavMesh();
if (obssimulation) {
obssimulation->AddObstaclesForNavMesh(navmesh);
}
}
}
for (KX_GameObject *gameobj : inactivelist) {
Expand Down
308 changes: 173 additions & 135 deletions source/gameengine/Ketsji/KX_NavMeshObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,186 +124,220 @@ void KX_NavMeshObject::ProcessReplica()
CM_FunctionError("unable to build navigation mesh");
return;
}

KX_ObstacleSimulation *obssimulation = GetScene()->GetObstacleSimulation();
if (obssimulation) {
obssimulation->AddObstaclesForNavMesh(this);
}
}

int KX_NavMeshObject::GetGameObjectType() const
{
return OBJ_NAVMESH;
}


bool KX_NavMeshObject::BuildVertIndArrays(float *&vertices, int& nverts,
bool KX_NavMeshObject::BuildFromDerivedMesh(float *&vertices, int& nverts,
unsigned short * &polys, int& npolys, unsigned short *&dmeshes,
float *&dvertices, int &ndvertsuniq, unsigned short *&dtris,
int& ndtris, int &vertsPerPoly)
{
DerivedMesh *dm = mesh_create_derived_no_virtual(GetScene()->GetBlenderScene(), GetBlenderObject(),
nullptr, CD_MASK_MESH);
KX_Mesh *meshobj = m_meshes.front();
if (!meshobj->GetMesh()) {
return false;
}

DerivedMesh *dm = CDDM_from_mesh(meshobj->GetMesh());
CustomData *pdata = dm->getPolyDataLayout(dm);
int *recastData = (int *)CustomData_get_layer(pdata, CD_RECAST);
if (recastData) {
int *dtrisToPolysMap = nullptr, *dtrisToTrisMap = nullptr, *trisToFacesMap = nullptr;
int nAllVerts = 0;
float *allVerts = nullptr;
buildNavMeshDataByDerivedMesh(dm, &vertsPerPoly, &nAllVerts, &allVerts, &ndtris, &dtris,
&npolys, &dmeshes, &polys, &dtrisToPolysMap, &dtrisToTrisMap, &trisToFacesMap);

MEM_SAFE_FREE(dtrisToPolysMap);
MEM_SAFE_FREE(dtrisToTrisMap);
MEM_SAFE_FREE(trisToFacesMap);

unsigned short *verticesMap = (unsigned short *)MEM_mallocN(sizeof(*verticesMap) * nAllVerts, __func__);
memset(verticesMap, 0xff, sizeof(*verticesMap) * nAllVerts);
int curIdx = 0;
//vertices - mesh verts
//iterate over all polys and create map for their vertices first...
for (int polyidx = 0; polyidx < npolys; polyidx++) {
unsigned short *poly = &polys[polyidx * vertsPerPoly * 2];
for (int i = 0; i < vertsPerPoly; i++) {
unsigned short idx = poly[i];
if (idx == 0xffff) {
break;
}
if (verticesMap[idx] == 0xffff) {
verticesMap[idx] = curIdx++;
}
poly[i] = verticesMap[idx];
if (!recastData) {
dm->release(dm);
return false;
}

int *dtrisToPolysMap = nullptr, *dtrisToTrisMap = nullptr, *trisToFacesMap = nullptr;
int nAllVerts = 0;
float *allVerts = nullptr;
buildNavMeshDataByDerivedMesh(dm, &vertsPerPoly, &nAllVerts, &allVerts, &ndtris, &dtris,
&npolys, &dmeshes, &polys, &dtrisToPolysMap, &dtrisToTrisMap, &trisToFacesMap);

MEM_SAFE_FREE(dtrisToPolysMap);
MEM_SAFE_FREE(dtrisToTrisMap);
MEM_SAFE_FREE(trisToFacesMap);

unsigned short *verticesMap = (unsigned short *)MEM_mallocN(sizeof(*verticesMap) * nAllVerts, __func__);
memset(verticesMap, 0xff, sizeof(*verticesMap) * nAllVerts);
int curIdx = 0;
//vertices - mesh verts
//iterate over all polys and create map for their vertices first...
for (int polyidx = 0; polyidx < npolys; polyidx++) {
unsigned short *poly = &polys[polyidx * vertsPerPoly * 2];
for (int i = 0; i < vertsPerPoly; i++) {
unsigned short idx = poly[i];
if (idx == 0xffff) {
break;
}
if (verticesMap[idx] == 0xffff) {
verticesMap[idx] = curIdx++;
}
poly[i] = verticesMap[idx];
}
nverts = curIdx;
//...then iterate over detailed meshes
//transform indices to local ones (for each navigation polygon)
for (int polyidx = 0; polyidx < npolys; polyidx++) {
unsigned short *poly = &polys[polyidx * vertsPerPoly * 2];
int nv = polyNumVerts(poly, vertsPerPoly);
unsigned short *dmesh = &dmeshes[4 * polyidx];
unsigned short tribase = dmesh[2];
unsigned short trinum = dmesh[3];
unsigned short vbase = curIdx;
for (int j = 0; j < trinum; j++) {
unsigned short *dtri = &dtris[(tribase + j) * 3 * 2];
for (int k = 0; k < 3; k++) {
int newVertexIdx = verticesMap[dtri[k]];
if (newVertexIdx == 0xffff) {
newVertexIdx = curIdx++;
verticesMap[dtri[k]] = newVertexIdx;
}
}
nverts = curIdx;
//...then iterate over detailed meshes
//transform indices to local ones (for each navigation polygon)
for (int polyidx = 0; polyidx < npolys; polyidx++) {
unsigned short *poly = &polys[polyidx * vertsPerPoly * 2];
int nv = polyNumVerts(poly, vertsPerPoly);
unsigned short *dmesh = &dmeshes[4 * polyidx];
unsigned short tribase = dmesh[2];
unsigned short trinum = dmesh[3];
unsigned short vbase = curIdx;
for (int j = 0; j < trinum; j++) {
unsigned short *dtri = &dtris[(tribase + j) * 3 * 2];
for (int k = 0; k < 3; k++) {
int newVertexIdx = verticesMap[dtri[k]];
if (newVertexIdx == 0xffff) {
newVertexIdx = curIdx++;
verticesMap[dtri[k]] = newVertexIdx;
}

if (newVertexIdx < nverts) {
//it's polygon vertex ("shared")
int idxInPoly = polyFindVertex(poly, vertsPerPoly, newVertexIdx);
if (idxInPoly == -1) {
CM_Error("building NavMeshObject, can't find vertex in polygon\n");
return false;
}
dtri[k] = idxInPoly;
}
else {
dtri[k] = newVertexIdx - vbase + nv;
if (newVertexIdx < nverts) {
//it's polygon vertex ("shared")
int idxInPoly = polyFindVertex(poly, vertsPerPoly, newVertexIdx);
if (idxInPoly == -1) {
CM_Error("building NavMeshObject, can't find vertex in polygon\n");
return false;
}
dtri[k] = idxInPoly;
}
else {
dtri[k] = newVertexIdx - vbase + nv;
}
}
dmesh[0] = vbase - nverts; //verts base
dmesh[1] = curIdx - vbase; //verts num
}
dmesh[0] = vbase - nverts; //verts base
dmesh[1] = curIdx - vbase; //verts num
}

vertices = new float[nverts * 3];
ndvertsuniq = curIdx - nverts;
if (ndvertsuniq > 0) {
dvertices = new float[ndvertsuniq * 3];
}
for (int vi = 0; vi < nAllVerts; vi++) {
int newIdx = verticesMap[vi];
if (newIdx != 0xffff) {
if (newIdx < nverts) {
//navigation mesh vertex
memcpy(vertices + 3 * newIdx, allVerts + 3 * vi, 3 * sizeof(float));
}
else {
//detailed mesh vertex
memcpy(dvertices + 3 * (newIdx - nverts), allVerts + 3 * vi, 3 * sizeof(float));
}
vertices = new float[nverts * 3];
ndvertsuniq = curIdx - nverts;
if (ndvertsuniq > 0) {
dvertices = new float[ndvertsuniq * 3];
}
for (int vi = 0; vi < nAllVerts; vi++) {
int newIdx = verticesMap[vi];
if (newIdx != 0xffff) {
if (newIdx < nverts) {
//navigation mesh vertex
memcpy(vertices + 3 * newIdx, allVerts + 3 * vi, 3 * sizeof(float));
}
else {
//detailed mesh vertex
memcpy(dvertices + 3 * (newIdx - nverts), allVerts + 3 * vi, 3 * sizeof(float));
}
}
}

MEM_SAFE_FREE(allVerts);
MEM_SAFE_FREE(allVerts);
MEM_freeN(verticesMap);
dm->release(dm);

MEM_freeN(verticesMap);
}
else {
//create from RAS_Mesh (detailed mesh is fake)
KX_Mesh *meshobj = m_meshes.front();
vertsPerPoly = 3;
return true;
}

// Indices count.
unsigned int numindices = 0;
// Original (without split of normal or UV) vertex count.
unsigned int numvertices = 0;
bool KX_NavMeshObject::BuildFromMesh(float *&vertices, int& nverts,
unsigned short * &polys, int& npolys, unsigned short *&dmeshes,
float *&dvertices, int &ndvertsuniq, unsigned short *&dtris,
int& ndtris, int &vertsPerPoly)
{
KX_Mesh *meshobj = m_meshes.front();
vertsPerPoly = 3;

for (RAS_MeshMaterial *meshmat : meshobj->GetMeshMaterialList()) {
RAS_DisplayArray *array = meshmat->GetDisplayArray();
// Indices count.
unsigned int numindices = 0;
// Original (without split of normal or UV) vertex count.
unsigned int numvertices = 0;

numindices += array->GetTriangleIndexCount();
numvertices = std::max(numvertices, array->GetMaxOrigIndex() + 1);
}
for (RAS_MeshMaterial *meshmat : meshobj->GetMeshMaterialList()) {
RAS_DisplayArray *array = meshmat->GetDisplayArray();

vertices = new float[numvertices * 3];
polys = (unsigned short *)MEM_callocN(sizeof(unsigned short) * numindices, "BuildVertIndArrays polys");

/// Map from original vertex index to m_vertexArray vertex index.
std::vector<int> vertRemap(numvertices, -1);

// Current vertex written.
unsigned int curvert = 0;
// Current index written.
unsigned int curind = 0;
for (RAS_MeshMaterial *meshmat : meshobj->GetMeshMaterialList()) {
RAS_DisplayArray *array = meshmat->GetDisplayArray();
// Convert location of all vertices and remap if vertices weren't already converted.
for (unsigned int j = 0, numvert = array->GetVertexCount(); j < numvert; ++j) {
const RAS_VertexInfo& info = array->GetVertexInfo(j);
const unsigned int origIndex = info.GetOrigIndex();
/* Avoid double conversion of two unique vertices using the same base:
* using the same original vertex and so the same position.
*/
if (vertRemap[origIndex] != -1) {
continue;
}
numindices += array->GetTriangleIndexCount();
numvertices = std::max(numvertices, array->GetMaxOrigIndex() + 1);
}

copy_v3_v3(&vertices[curvert * 3], array->GetPosition(j).data);
// Detour can't manage more than 65536 vertices.
if (numvertices > 0xffff) {
return false;
}

// Register the vertex index where the position was converted in m_vertexArray.
vertRemap[origIndex] = curvert++;
vertices = new float[numvertices * 3];
// Detour supports 6 indices per polygons natively, 0xffff is the discard value.
polys = (unsigned short *)MEM_callocN(sizeof(unsigned short) * numindices * 2, "BuildVertIndArrays polys");
memset(polys, 0xff, sizeof(unsigned short) * numindices * 2);

/// Map from original vertex index to m_vertexArray vertex index.
std::vector<int> vertRemap(numvertices, -1);

// Current vertex written.
unsigned int curvert = 0;
// Current index written.
unsigned int curind = 0;
for (RAS_MeshMaterial *meshmat : meshobj->GetMeshMaterialList()) {
RAS_DisplayArray *array = meshmat->GetDisplayArray();
// Convert location of all vertices and remap if vertices weren't already converted.
for (unsigned int j = 0, numvert = array->GetVertexCount(); j < numvert; ++j) {
const RAS_VertexInfo& info = array->GetVertexInfo(j);
const unsigned int origIndex = info.GetOrigIndex();
/* Avoid double conversion of two unique vertices using the same base:
* using the same original vertex and so the same position.
*/
if (vertRemap[origIndex] != -1) {
continue;
}

for (unsigned int j = 0, numtris = array->GetTriangleIndexCount(); j < numtris; ++j) {
const unsigned int index = array->GetTriangleIndex(j);
copy_v3_v3(&vertices[curvert * 3], array->GetPosition(j).data);

// Register the vertex index where the position was converted in m_vertexArray.
vertRemap[origIndex] = curvert++;
}

for (unsigned int j = 0, numtris = array->GetTriangleIndexCount() / 3; j < numtris; ++j) {
for (unsigned short k = 0; k < 3; ++k) {
const unsigned int index = array->GetTriangleIndex(j * 3 + k);
const RAS_VertexInfo& info = array->GetVertexInfo(index);
const unsigned int origIndex = info.GetOrigIndex();
polys[curind++] = vertRemap[origIndex];
polys[curind + k] = vertRemap[origIndex];
}
curind += 6;
}

npolys = numindices;
dmeshes = nullptr;
dvertices = nullptr;
ndvertsuniq = 0;
dtris = nullptr;
ndtris = npolys;
}
dm->release(dm);

nverts = numvertices;
npolys = numindices / vertsPerPoly;
dmeshes = nullptr;
dvertices = nullptr;
ndvertsuniq = 0;
dtris = nullptr;
ndtris = npolys;

return true;
}

bool KX_NavMeshObject::BuildVertIndArrays(float *&vertices, int& nverts,
unsigned short * &polys, int& npolys, unsigned short *&dmeshes,
float *&dvertices, int &ndvertsuniq, unsigned short *&dtris,
int& ndtris, int &vertsPerPoly)
{
if (BuildFromDerivedMesh(vertices, nverts, polys, npolys, dmeshes, dvertices, ndvertsuniq, dtris, ndtris, vertsPerPoly)) {
return true;
}

return BuildFromMesh(vertices, nverts, polys, npolys, dmeshes, dvertices, ndvertsuniq, dtris, ndtris, vertsPerPoly);
}

bool KX_NavMeshObject::BuildNavMesh()
{
KX_ObstacleSimulation *obssimulation = GetScene()->GetObstacleSimulation();

if (obssimulation) {
obssimulation->DestroyObstacleForObj(this);
}

if (m_navMesh) {
delete m_navMesh;
m_navMesh = nullptr;
Expand Down Expand Up @@ -492,6 +526,10 @@ bool KX_NavMeshObject::BuildNavMesh()
delete[] vertsi;
}

if (obssimulation) {
obssimulation->AddObstaclesForNavMesh(this);
}

return true;
}

Expand Down
10 changes: 10 additions & 0 deletions source/gameengine/Ketsji/KX_NavMeshObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ class KX_NavMeshObject : public KX_GameObject
protected:
dtStatNavMesh *m_navMesh;

bool BuildFromDerivedMesh(float *&vertices, int& nverts,
unsigned short * &polys, int& npolys, unsigned short *&dmeshes,
float *&dvertices, int &ndvertsuniq, unsigned short * &dtris,
int& ndtris, int &vertsPerPoly);

bool BuildFromMesh(float *&vertices, int& nverts,
unsigned short * &polys, int& npolys, unsigned short *&dmeshes,
float *&dvertices, int &ndvertsuniq, unsigned short * &dtris,
int& ndtris, int &vertsPerPoly);

bool BuildVertIndArrays(float *&vertices, int& nverts,
unsigned short * &polys, int& npolys, unsigned short *&dmeshes,
float *&dvertices, int &ndvertsuniq, unsigned short * &dtris,
Expand Down

0 comments on commit 5b8cb1e

Please sign in to comment.