blender/source/gameengine/Ketsji/KX_NavMeshObject.cpp
Sybren A. Stüvel d3acfa1d87 BGE: Navmesh fixes and improvements
The navigation mesh functionality was broken for quite a while. This patch
contains fixes: recalculating tesselations before getting the number of
tesselation faces (it otherwise returned 0) before calculating the navmesh,
and calling `DM_ensure_tessface()` on the navmesh's `DerivedMesh` object
(which fixes visualisation in Blender). This allows one to create a new
navmesh, which also works in the BGE.

Furthermore, the patch adds several return values, and shows more error
messages when things go wrong. In several places in the navmesh creation
code, return codes weren't checked and errors silently ignored.

Reviewers: nicks, brita_, campbellbarton, lordloki, moguri, panzergame

Reviewed By: panzergame

Differential Revision: https://developer.blender.org/D1435
2015-07-28 13:54:41 +02:00

739 lines
19 KiB
C++

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
#include "BLI_math_vector.h"
#include "KX_NavMeshObject.h"
#include "RAS_MeshObject.h"
#include "RAS_Polygon.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
extern "C" {
#include "BKE_scene.h"
#include "BKE_customdata.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_DerivedMesh.h"
#include "BKE_navmesh_conversion.h"
}
#include "KX_PythonInit.h"
#include "KX_PyMath.h"
#include "EXP_Value.h"
#include "Recast.h"
#include "DetourStatNavMeshBuilder.h"
#include "KX_ObstacleSimulation.h"
#define MAX_PATH_LEN 256
static const float polyPickExt[3] = {2, 4, 2};
static void calcMeshBounds(const float* vert, int nverts, float* bmin, float* bmax)
{
bmin[0] = bmax[0] = vert[0];
bmin[1] = bmax[1] = vert[1];
bmin[2] = bmax[2] = vert[2];
for (int i=1; i<nverts; i++)
{
if (bmin[0]>vert[3*i+0]) bmin[0] = vert[3*i+0];
if (bmin[1]>vert[3*i+1]) bmin[1] = vert[3*i+1];
if (bmin[2]>vert[3*i+2]) bmin[2] = vert[3*i+2];
if (bmax[0]<vert[3*i+0]) bmax[0] = vert[3*i+0];
if (bmax[1]<vert[3*i+1]) bmax[1] = vert[3*i+1];
if (bmax[2]<vert[3*i+2]) bmax[2] = vert[3*i+2];
}
}
inline void flipAxes(float* vec)
{
std::swap(vec[1],vec[2]);
}
KX_NavMeshObject::KX_NavMeshObject(void* sgReplicationInfo, SG_Callbacks callbacks)
: KX_GameObject(sgReplicationInfo, callbacks)
, m_navMesh(NULL)
{
}
KX_NavMeshObject::~KX_NavMeshObject()
{
if (m_navMesh)
delete m_navMesh;
}
CValue* KX_NavMeshObject::GetReplica()
{
KX_NavMeshObject* replica = new KX_NavMeshObject(*this);
replica->ProcessReplica();
return replica;
}
void KX_NavMeshObject::ProcessReplica()
{
KX_GameObject::ProcessReplica();
m_navMesh = NULL; /* without this, building frees the navmesh we copied from */
if (!BuildNavMesh()) {
std::cout << "Error in " << __func__ << ": unable to build navigation mesh" << std::endl;
return;
}
KX_Scene* scene = KX_GetActiveScene();
KX_ObstacleSimulation* obssimulation = scene->GetObstacleSimulation();
if (obssimulation)
obssimulation->AddObstaclesForNavMesh(this);
}
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)
{
DerivedMesh* dm = mesh_create_derived_no_virtual(GetScene()->GetBlenderScene(), GetBlenderObject(),
NULL, CD_MASK_MESH);
CustomData *pdata = dm->getPolyDataLayout(dm);
int* recastData = (int*) CustomData_get_layer(pdata, CD_RECAST);
if (recastData)
{
int *dtrisToPolysMap=NULL, *dtrisToTrisMap=NULL, *trisToFacesMap=NULL;
int nAllVerts = 0;
float *allVerts = NULL;
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;
}
if (newVertexIdx<nverts)
{
//it's polygon vertex ("shared")
int idxInPoly = polyFindVertex(poly, vertsPerPoly, newVertexIdx);
if (idxInPoly==-1)
{
printf("Building NavMeshObject: Error! 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
}
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_freeN(verticesMap);
}
else
{
//create from RAS_MeshObject (detailed mesh is fake)
RAS_MeshObject* meshobj = GetMesh(0);
vertsPerPoly = 3;
nverts = meshobj->m_sharedvertex_map.size();
if (nverts >= 0xffff)
return false;
//calculate count of tris
int nmeshpolys = meshobj->NumPolygons();
npolys = nmeshpolys;
for (int p=0; p<nmeshpolys; p++)
{
int vertcount = meshobj->GetPolygon(p)->VertexCount();
npolys+=vertcount-3;
}
//create verts
vertices = new float[nverts*3];
float* vert = vertices;
for (int vi=0; vi<nverts; vi++)
{
const float* pos = !meshobj->m_sharedvertex_map[vi].empty() ? meshobj->GetVertexLocation(vi) : NULL;
if (pos)
copy_v3_v3(vert, pos);
else
{
memset(vert, 0, 3*sizeof(float)); //vertex isn't in any poly, set dummy zero coordinates
}
vert+=3;
}
//create tris
polys = (unsigned short *)MEM_callocN(sizeof(unsigned short)*3*2*npolys, "BuildVertIndArrays polys");
memset(polys, 0xff, sizeof(unsigned short)*3*2*npolys);
unsigned short *poly = polys;
RAS_Polygon* raspoly;
for (int p=0; p<nmeshpolys; p++)
{
raspoly = meshobj->GetPolygon(p);
for (int v=0; v<raspoly->VertexCount()-2; v++)
{
poly[0] = raspoly->GetVertex(0)->getOrigIndex();
for (size_t i=1; i<3; i++)
{
poly[i] = raspoly->GetVertex(v+i)->getOrigIndex();
}
poly += 6;
}
}
dmeshes = NULL;
dvertices = NULL;
ndvertsuniq = 0;
dtris = NULL;
ndtris = npolys;
}
dm->release(dm);
return true;
}
bool KX_NavMeshObject::BuildNavMesh()
{
if (m_navMesh)
{
delete m_navMesh;
m_navMesh = NULL;
}
if (GetMeshCount()==0)
{
printf("Can't find mesh for navmesh object: %s\n", m_name.ReadPtr());
return false;
}
float *vertices = NULL, *dvertices = NULL;
unsigned short *polys = NULL, *dtris = NULL, *dmeshes = NULL;
int nverts = 0, npolys = 0, ndvertsuniq = 0, ndtris = 0;
int vertsPerPoly = 0;
if (!BuildVertIndArrays(vertices, nverts, polys, npolys,
dmeshes, dvertices, ndvertsuniq, dtris, ndtris, vertsPerPoly )
|| vertsPerPoly<3)
{
printf("Can't build navigation mesh data for object:%s\n", m_name.ReadPtr());
if (vertices) delete[] vertices;
return false;
}
MT_Point3 pos;
if (dmeshes==NULL)
{
for (int i=0; i<nverts; i++)
{
flipAxes(&vertices[i*3]);
}
for (int i=0; i<ndvertsuniq; i++)
{
flipAxes(&dvertices[i*3]);
}
}
if (!buildMeshAdjacency(polys, npolys, nverts, vertsPerPoly)) {
std::cout << __func__ << ": unable to build mesh adjacency information." << std::endl;
delete[] vertices;
return false;
}
float cs = 0.2f;
if (!nverts || !npolys)
{
if (vertices) delete[] vertices;
return false;
}
float bmin[3], bmax[3];
calcMeshBounds(vertices, nverts, bmin, bmax);
//quantize vertex pos
unsigned short* vertsi = new unsigned short[3*nverts];
float ics = 1.f/cs;
for (int i=0; i<nverts; i++)
{
vertsi[3*i+0] = static_cast<unsigned short>((vertices[3*i+0]-bmin[0])*ics);
vertsi[3*i+1] = static_cast<unsigned short>((vertices[3*i+1]-bmin[1])*ics);
vertsi[3*i+2] = static_cast<unsigned short>((vertices[3*i+2]-bmin[2])*ics);
}
// Calculate data size
const int headerSize = sizeof(dtStatNavMeshHeader);
const int vertsSize = sizeof(float)*3*nverts;
const int polysSize = sizeof(dtStatPoly)*npolys;
const int nodesSize = sizeof(dtStatBVNode)*npolys*2;
const int detailMeshesSize = sizeof(dtStatPolyDetail)*npolys;
const int detailVertsSize = sizeof(float)*3*ndvertsuniq;
const int detailTrisSize = sizeof(unsigned char)*4*ndtris;
const int dataSize = headerSize + vertsSize + polysSize + nodesSize +
detailMeshesSize + detailVertsSize + detailTrisSize;
unsigned char* data = new unsigned char[dataSize];
if (!data)
return false;
memset(data, 0, dataSize);
unsigned char* d = data;
dtStatNavMeshHeader* header = (dtStatNavMeshHeader*)d; d += headerSize;
float* navVerts = (float*)d; d += vertsSize;
dtStatPoly* navPolys = (dtStatPoly*)d; d += polysSize;
dtStatBVNode* navNodes = (dtStatBVNode*)d; d += nodesSize;
dtStatPolyDetail* navDMeshes = (dtStatPolyDetail*)d; d += detailMeshesSize;
float* navDVerts = (float*)d; d += detailVertsSize;
unsigned char* navDTris = (unsigned char*)d; d += detailTrisSize;
// Store header
header->magic = DT_STAT_NAVMESH_MAGIC;
header->version = DT_STAT_NAVMESH_VERSION;
header->npolys = npolys;
header->nverts = nverts;
header->cs = cs;
header->bmin[0] = bmin[0];
header->bmin[1] = bmin[1];
header->bmin[2] = bmin[2];
header->bmax[0] = bmax[0];
header->bmax[1] = bmax[1];
header->bmax[2] = bmax[2];
header->ndmeshes = npolys;
header->ndverts = ndvertsuniq;
header->ndtris = ndtris;
// Store vertices
for (int i = 0; i < nverts; ++i)
{
const unsigned short* iv = &vertsi[i*3];
float* v = &navVerts[i*3];
v[0] = bmin[0] + iv[0] * cs;
v[1] = bmin[1] + iv[1] * cs;
v[2] = bmin[2] + iv[2] * cs;
}
//memcpy(navVerts, vertices, nverts*3*sizeof(float));
// Store polygons
const unsigned short* src = polys;
for (int i = 0; i < npolys; ++i)
{
dtStatPoly* p = &navPolys[i];
p->nv = 0;
for (int j = 0; j < vertsPerPoly; ++j)
{
if (src[j] == 0xffff) break;
p->v[j] = src[j];
p->n[j] = src[vertsPerPoly+j]+1;
p->nv++;
}
src += vertsPerPoly*2;
}
header->nnodes = createBVTree(vertsi, nverts, polys, npolys, vertsPerPoly,
cs, cs, npolys*2, navNodes);
if (dmeshes==NULL)
{
//create fake detail meshes
for (int i = 0; i < npolys; ++i)
{
dtStatPolyDetail& dtl = navDMeshes[i];
dtl.vbase = 0;
dtl.nverts = 0;
dtl.tbase = i;
dtl.ntris = 1;
}
// setup triangles.
unsigned char* tri = navDTris;
for (size_t i=0; i<ndtris; i++)
{
for (size_t j=0; j<3; j++)
tri[4*i+j] = j;
}
}
else
{
//verts
memcpy(navDVerts, dvertices, ndvertsuniq*3*sizeof(float));
//tris
unsigned char* tri = navDTris;
for (size_t i=0; i<ndtris; i++)
{
for (size_t j=0; j<3; j++)
tri[4*i+j] = dtris[6*i+j];
}
//detailed meshes
for (int i = 0; i < npolys; ++i)
{
dtStatPolyDetail& dtl = navDMeshes[i];
dtl.vbase = dmeshes[i*4+0];
dtl.nverts = dmeshes[i*4+1];
dtl.tbase = dmeshes[i*4+2];
dtl.ntris = dmeshes[i*4+3];
}
}
m_navMesh = new dtStatNavMesh;
m_navMesh->init(data, dataSize, true);
delete [] vertices;
/* navmesh conversion is using C guarded alloc for memory allocaitons */
MEM_freeN(polys);
if (dmeshes) MEM_freeN(dmeshes);
if (dtris) MEM_freeN(dtris);
if (dvertices)
delete [] dvertices;
if (vertsi)
delete [] vertsi;
return true;
}
dtStatNavMesh* KX_NavMeshObject::GetNavMesh()
{
return m_navMesh;
}
void KX_NavMeshObject::DrawNavMesh(NavMeshRenderMode renderMode)
{
if (!m_navMesh)
return;
MT_Vector3 color(0.f, 0.f, 0.f);
switch (renderMode)
{
case RM_POLYS :
case RM_WALLS :
for (int pi=0; pi<m_navMesh->getPolyCount(); pi++)
{
const dtStatPoly* poly = m_navMesh->getPoly(pi);
for (int i = 0, j = (int)poly->nv-1; i < (int)poly->nv; j = i++)
{
if (poly->n[j] && renderMode==RM_WALLS)
continue;
const float* vif = m_navMesh->getVertex(poly->v[i]);
const float* vjf = m_navMesh->getVertex(poly->v[j]);
MT_Point3 vi(vif[0], vif[2], vif[1]);
MT_Point3 vj(vjf[0], vjf[2], vjf[1]);
vi = TransformToWorldCoords(vi);
vj = TransformToWorldCoords(vj);
KX_RasterizerDrawDebugLine(vi, vj, color);
}
}
break;
case RM_TRIS :
for (int i = 0; i < m_navMesh->getPolyDetailCount(); ++i)
{
const dtStatPoly* p = m_navMesh->getPoly(i);
const dtStatPolyDetail* pd = m_navMesh->getPolyDetail(i);
for (int j = 0; j < pd->ntris; ++j)
{
const unsigned char* t = m_navMesh->getDetailTri(pd->tbase+j);
MT_Point3 tri[3];
for (int k = 0; k < 3; ++k)
{
const float* v;
if (t[k] < p->nv)
v = m_navMesh->getVertex(p->v[t[k]]);
else
v = m_navMesh->getDetailVertex(pd->vbase+(t[k]-p->nv));
float pos[3];
rcVcopy(pos, v);
flipAxes(pos);
tri[k].setValue(pos);
}
for (int k=0; k<3; k++)
tri[k] = TransformToWorldCoords(tri[k]);
for (int k=0; k<3; k++)
KX_RasterizerDrawDebugLine(tri[k], tri[(k+1)%3], color);
}
}
break;
default:
/* pass */
break;
}
}
MT_Point3 KX_NavMeshObject::TransformToLocalCoords(const MT_Point3& wpos)
{
MT_Matrix3x3 orientation = NodeGetWorldOrientation();
const MT_Vector3& scaling = NodeGetWorldScaling();
orientation.scale(scaling[0], scaling[1], scaling[2]);
MT_Transform worldtr(NodeGetWorldPosition(), orientation);
MT_Transform invworldtr;
invworldtr.invert(worldtr);
MT_Point3 lpos = invworldtr(wpos);
return lpos;
}
MT_Point3 KX_NavMeshObject::TransformToWorldCoords(const MT_Point3& lpos)
{
MT_Matrix3x3 orientation = NodeGetWorldOrientation();
const MT_Vector3& scaling = NodeGetWorldScaling();
orientation.scale(scaling[0], scaling[1], scaling[2]);
MT_Transform worldtr(NodeGetWorldPosition(), orientation);
MT_Point3 wpos = worldtr(lpos);
return wpos;
}
int KX_NavMeshObject::FindPath(const MT_Point3& from, const MT_Point3& to, float* path, int maxPathLen)
{
if (!m_navMesh)
return 0;
MT_Point3 localfrom = TransformToLocalCoords(from);
MT_Point3 localto = TransformToLocalCoords(to);
float spos[3], epos[3];
localfrom.getValue(spos); flipAxes(spos);
localto.getValue(epos); flipAxes(epos);
dtStatPolyRef sPolyRef = m_navMesh->findNearestPoly(spos, polyPickExt);
dtStatPolyRef ePolyRef = m_navMesh->findNearestPoly(epos, polyPickExt);
int pathLen = 0;
if (sPolyRef && ePolyRef)
{
dtStatPolyRef* polys = new dtStatPolyRef[maxPathLen];
int npolys;
npolys = m_navMesh->findPath(sPolyRef, ePolyRef, spos, epos, polys, maxPathLen);
if (npolys)
{
pathLen = m_navMesh->findStraightPath(spos, epos, polys, npolys, path, maxPathLen);
for (int i=0; i<pathLen; i++)
{
flipAxes(&path[i*3]);
MT_Point3 waypoint(&path[i*3]);
waypoint = TransformToWorldCoords(waypoint);
waypoint.getValue(&path[i*3]);
}
}
delete[] polys;
}
return pathLen;
}
float KX_NavMeshObject::Raycast(const MT_Point3& from, const MT_Point3& to)
{
if (!m_navMesh)
return 0.f;
MT_Point3 localfrom = TransformToLocalCoords(from);
MT_Point3 localto = TransformToLocalCoords(to);
float spos[3], epos[3];
localfrom.getValue(spos); flipAxes(spos);
localto.getValue(epos); flipAxes(epos);
dtStatPolyRef sPolyRef = m_navMesh->findNearestPoly(spos, polyPickExt);
float t=0;
static dtStatPolyRef polys[MAX_PATH_LEN];
m_navMesh->raycast(sPolyRef, spos, epos, t, polys, MAX_PATH_LEN);
return t;
}
void KX_NavMeshObject::DrawPath(const float *path, int pathLen, const MT_Vector3& color)
{
MT_Vector3 a,b;
for (int i=0; i<pathLen-1; i++)
{
a.setValue(&path[3*i]);
b.setValue(&path[3*(i+1)]);
KX_RasterizerDrawDebugLine(a, b, color);
}
}
#ifdef WITH_PYTHON
//----------------------------------------------------------------------------
//Python
PyTypeObject KX_NavMeshObject::Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"KX_NavMeshObject",
sizeof(PyObjectPlus_Proxy),
0,
py_base_dealloc,
0,
0,
0,
0,
py_base_repr,
0,
0,
0,
0,0,0,0,0,0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
0,0,0,0,0,0,0,
Methods,
0,
0,
&KX_GameObject::Type,
0,0,0,0,0,0,
py_base_new
};
PyAttributeDef KX_NavMeshObject::Attributes[] = {
{ NULL } //Sentinel
};
//KX_PYMETHODTABLE_NOARGS(KX_GameObject, getD),
PyMethodDef KX_NavMeshObject::Methods[] = {
KX_PYMETHODTABLE(KX_NavMeshObject, findPath),
KX_PYMETHODTABLE(KX_NavMeshObject, raycast),
KX_PYMETHODTABLE(KX_NavMeshObject, draw),
KX_PYMETHODTABLE(KX_NavMeshObject, rebuild),
{NULL,NULL} //Sentinel
};
KX_PYMETHODDEF_DOC(KX_NavMeshObject, findPath,
"findPath(start, goal): find path from start to goal points\n"
"Returns a path as list of points)\n")
{
PyObject *ob_from, *ob_to;
if (!PyArg_ParseTuple(args,"OO:getPath",&ob_from,&ob_to))
return NULL;
MT_Point3 from, to;
if (!PyVecTo(ob_from, from) || !PyVecTo(ob_to, to))
return NULL;
float path[MAX_PATH_LEN*3];
int pathLen = FindPath(from, to, path, MAX_PATH_LEN);
PyObject *pathList = PyList_New( pathLen );
for (int i=0; i<pathLen; i++)
{
MT_Point3 point(&path[3*i]);
PyList_SET_ITEM(pathList, i, PyObjectFrom(point));
}
return pathList;
}
KX_PYMETHODDEF_DOC(KX_NavMeshObject, raycast,
"raycast(start, goal): raycast from start to goal points\n"
"Returns hit factor)\n")
{
PyObject *ob_from, *ob_to;
if (!PyArg_ParseTuple(args,"OO:getPath",&ob_from,&ob_to))
return NULL;
MT_Point3 from, to;
if (!PyVecTo(ob_from, from) || !PyVecTo(ob_to, to))
return NULL;
float hit = Raycast(from, to);
return PyFloat_FromDouble(hit);
}
KX_PYMETHODDEF_DOC(KX_NavMeshObject, draw,
"draw(mode): navigation mesh debug drawing\n"
"mode: WALLS, POLYS, TRIS\n")
{
int arg;
NavMeshRenderMode renderMode = RM_TRIS;
if (PyArg_ParseTuple(args,"i:rebuild",&arg) && arg>=0 && arg<RM_MAX)
renderMode = (NavMeshRenderMode)arg;
DrawNavMesh(renderMode);
Py_RETURN_NONE;
}
KX_PYMETHODDEF_DOC_NOARGS(KX_NavMeshObject, rebuild,
"rebuild(): rebuild navigation mesh\n")
{
BuildNavMesh();
Py_RETURN_NONE;
}
#endif // WITH_PYTHON