From 56784fcde9e4a4ff1553bf555e66b59208c90b3c Mon Sep 17 00:00:00 2001 From: Nick Samarin Date: Wed, 19 May 2010 01:42:17 +0000 Subject: [PATCH] added converting mesh of game object to Detour StatNavMesh (game object is defined by property "navmesh") --- .../Detour/Include/DetourStatNavMeshBuilder.h | 4 + .../Source/DetourStatNavMeshBuilder.cpp | 2 +- .../recastnavigation/Recast/Include/Recast.h | 1 + .../Recast/Source/RecastMesh.cpp | 2 +- .../gameengine/converter/KX_converter.vcproj | 6 +- .../gameengine/ketsji/KX_ketsji.vcproj | 14 +- .../Converter/BL_BlenderDataConversion.cpp | 21 ++ source/gameengine/Ketsji/KX_KetsjiEngine.cpp | 5 + source/gameengine/Ketsji/KX_Pathfinder.cpp | 332 ++++++++++++++++++ source/gameengine/Ketsji/KX_Pathfinder.h | 51 +++ source/gameengine/Ketsji/KX_Scene.cpp | 12 +- source/gameengine/Ketsji/KX_Scene.h | 5 +- 12 files changed, 444 insertions(+), 11 deletions(-) create mode 100644 source/gameengine/Ketsji/KX_Pathfinder.cpp create mode 100644 source/gameengine/Ketsji/KX_Pathfinder.h diff --git a/extern/recastnavigation/Detour/Include/DetourStatNavMeshBuilder.h b/extern/recastnavigation/Detour/Include/DetourStatNavMeshBuilder.h index 3b8f7519b2f..03c79c429e7 100644 --- a/extern/recastnavigation/Detour/Include/DetourStatNavMeshBuilder.h +++ b/extern/recastnavigation/Detour/Include/DetourStatNavMeshBuilder.h @@ -26,4 +26,8 @@ bool dtCreateNavMeshData(const unsigned short* verts, const int nverts, const unsigned char* dtris, const int ndtris, unsigned char** outData, int* outDataSize); +int createBVTree(const unsigned short* verts, const int nverts, + const unsigned short* polys, const int npolys, const int nvp, + float cs, float ch, int nnodes, dtStatBVNode* nodes); + #endif // DETOURSTATNAVMESHBUILDER_H \ No newline at end of file diff --git a/extern/recastnavigation/Detour/Source/DetourStatNavMeshBuilder.cpp b/extern/recastnavigation/Detour/Source/DetourStatNavMeshBuilder.cpp index a2bfb94edbc..2ca455fb53d 100644 --- a/extern/recastnavigation/Detour/Source/DetourStatNavMeshBuilder.cpp +++ b/extern/recastnavigation/Detour/Source/DetourStatNavMeshBuilder.cpp @@ -160,7 +160,7 @@ static void subdivide(BVItem* items, int nitems, int imin, int imax, int& curNod } } -static int createBVTree(const unsigned short* verts, const int nverts, +/*static*/ int createBVTree(const unsigned short* verts, const int nverts, const unsigned short* polys, const int npolys, const int nvp, float cs, float ch, int nnodes, dtStatBVNode* nodes) diff --git a/extern/recastnavigation/Recast/Include/Recast.h b/extern/recastnavigation/Recast/Include/Recast.h index 5fe5447ab7c..f25ab47f8fa 100644 --- a/extern/recastnavigation/Recast/Include/Recast.h +++ b/extern/recastnavigation/Recast/Include/Recast.h @@ -496,5 +496,6 @@ bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& c bool rcMergePolyMeshDetails(rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh); +bool buildMeshAdjacency(unsigned short* polys, const int npolys, const int nverts, const int vertsPerPoly); #endif // RECAST_H diff --git a/extern/recastnavigation/Recast/Source/RecastMesh.cpp b/extern/recastnavigation/Recast/Source/RecastMesh.cpp index 0bcca9314c6..45ed4a2fdc6 100644 --- a/extern/recastnavigation/Recast/Source/RecastMesh.cpp +++ b/extern/recastnavigation/Recast/Source/RecastMesh.cpp @@ -32,7 +32,7 @@ struct rcEdge unsigned short poly[2]; }; -static bool buildMeshAdjacency(unsigned short* polys, const int npolys, +/*static */bool buildMeshAdjacency(unsigned short* polys, const int npolys, const int nverts, const int vertsPerPoly) { // Based on code by Eric Lengyel from: diff --git a/projectfiles_vc9/gameengine/converter/KX_converter.vcproj b/projectfiles_vc9/gameengine/converter/KX_converter.vcproj index 474ecb36f46..8dc9b41b9bc 100644 --- a/projectfiles_vc9/gameengine/converter/KX_converter.vcproj +++ b/projectfiles_vc9/gameengine/converter/KX_converter.vcproj @@ -1,7 +1,7 @@ + + @@ -856,6 +860,10 @@ RelativePath="..\..\..\source\gameengine\Ketsji\KX_OdePhysicsController.h" > + + diff --git a/source/gameengine/Converter/BL_BlenderDataConversion.cpp b/source/gameengine/Converter/BL_BlenderDataConversion.cpp index 4a2aa3695fa..09c7c8eeed9 100644 --- a/source/gameengine/Converter/BL_BlenderDataConversion.cpp +++ b/source/gameengine/Converter/BL_BlenderDataConversion.cpp @@ -172,6 +172,8 @@ extern "C" { #include "BL_ArmatureObject.h" #include "BL_DeformableGameObject.h" +#include "KX_Pathfinder.h" + #ifdef __cplusplus extern "C" { #endif @@ -2669,6 +2671,25 @@ void BL_ConvertBlenderObjects(struct Main* maggie, logicbrick_conversionlist->Release(); + //create navigation mesh + KX_Pathfinder* pathfinder = kxscene->GetPathfinder(); + if (pathfinder) + { + for ( i=0;iGetCount();i++) + { + KX_GameObject* gameobj = static_cast(objectlist->GetValue(i)); + struct Object* blenderobject = gameobj->GetBlenderObject(); + if (blenderobject->type==OB_MESH && gameobj->GetProperty("navmesh") && gameobj->GetMeshCount()>0) + { + RAS_MeshObject* meshobj = gameobj->GetMesh(0); + pathfinder->createFromMesh(meshobj); + gameobj->SetVisible(0, 0); + } + } + } + + + // Calculate the scene btree - // too slow - commented out. //kxscene->SetNodeTree(tf.MakeTree()); diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp index 71cd8b36045..52a3471f481 100644 --- a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp +++ b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp @@ -75,6 +75,8 @@ #include "DNA_world_types.h" #include "DNA_scene_types.h" +#include "KX_Pathfinder.h" + // If define: little test for Nzc: guarded drawing. If the canvas is // not valid, skip rendering this frame. //#define NZC_GUARDED_OUTPUT @@ -1323,6 +1325,9 @@ void KX_KetsjiEngine::RenderFrame(KX_Scene* scene, KX_Camera* cam) if (scene->GetPhysicsEnvironment()) scene->GetPhysicsEnvironment()->debugDrawWorld(); + + if (scene->GetPathfinder()) + scene->GetPathfinder()->debugDraw(); } /* To run once per scene diff --git a/source/gameengine/Ketsji/KX_Pathfinder.cpp b/source/gameengine/Ketsji/KX_Pathfinder.cpp new file mode 100644 index 00000000000..df9d9cb4869 --- /dev/null +++ b/source/gameengine/Ketsji/KX_Pathfinder.cpp @@ -0,0 +1,332 @@ +/** +* $Id: +* ***** 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 "KX_Pathfinder.h" +#include "RAS_MeshObject.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 "KX_PythonInit.h" +#include "Recast.h" +#include "DetourStatNavMeshBuilder.h" + +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; ivert[i+0]) bmin[0] = vert[i+0]; + if (bmin[1]>vert[i+1]) bmin[1] = vert[i+1]; + if (bmin[2]>vert[i+2]) bmin[2] = vert[i+2]; + + if (bmax[0]HasColliderPolygon()==false) + { + return false; + } + + DerivedMesh* dm = CDDM_from_mesh(meshobj->GetMesh(), NULL); + + MVert *mvert = dm->getVertArray(dm); + MFace *mface = dm->getFaceArray(dm); + int numpolys = dm->getNumFaces(dm); + int numverts = dm->getNumVerts(dm); + int* index = (int*)dm->getFaceDataArray(dm, CD_ORIGINDEX); + MTFace *tface = (MTFace *)dm->getFaceDataArray(dm, CD_MTFACE); + + vector vert_tag_array(numverts, false); + vector vert_remap_array(numverts, 0); + + // Tag verts we're using + for (int p2=0; p2GetPolygon((index)? index[p2]: p2); + // only add polygons that have the collision flag set + if (poly->IsCollider()) + { + if (vert_tag_array[mf->v1]==false) + {vert_tag_array[mf->v1]= true;vert_remap_array[mf->v1]= (size_t)nverts;nverts++;} + if (vert_tag_array[mf->v2]==false) + {vert_tag_array[mf->v2]= true;vert_remap_array[mf->v2]= (size_t)nverts;nverts++;} + if (vert_tag_array[mf->v3]==false) + {vert_tag_array[mf->v3]= true;vert_remap_array[mf->v3]= (size_t)nverts;nverts++;} + if (mf->v4 && vert_tag_array[mf->v4]==false) + {vert_tag_array[mf->v4]= true;vert_remap_array[mf->v4]= (size_t)nverts;nverts++;} + npolys += (mf->v4 ? 2:1); /* a quad or a tri */ + } + } + + if (nverts >= 0xffff) + return false; + + vertices = new float[nverts*3]; + faces = new unsigned short[npolys*3*2]; + memset(faces,0xff,sizeof(unsigned short)*3*2*npolys); + float *bt= vertices; + unsigned short *tri_pt= faces; + + for (int p2=0; p2GetPolygon((index)? index[p2]: p2); + // only add polygons that have the collisionflag set + if (poly->IsCollider()) + { + MVert *v1= &mvert[mf->v1]; + MVert *v2= &mvert[mf->v2]; + MVert *v3= &mvert[mf->v3]; + + // the face indicies + tri_pt[0]= vert_remap_array[mf->v1]; + tri_pt[1]= vert_remap_array[mf->v2]; + tri_pt[2]= vert_remap_array[mf->v3]; + tri_pt= tri_pt+6; + + // the vertex location + if (vert_tag_array[mf->v1]==true) { /* *** v1 *** */ + vert_tag_array[mf->v1]= false; + *bt++ = v1->co[0]; + *bt++ = v1->co[1]; + *bt++ = v1->co[2]; + } + if (vert_tag_array[mf->v2]==true) { /* *** v2 *** */ + vert_tag_array[mf->v2]= false; + *bt++ = v2->co[0]; + *bt++ = v2->co[1]; + *bt++ = v2->co[2]; + } + if (vert_tag_array[mf->v3]==true) { /* *** v3 *** */ + vert_tag_array[mf->v3]= false; + *bt++ = v3->co[0]; + *bt++ = v3->co[1]; + *bt++ = v3->co[2]; + } + + if (mf->v4) + { + MVert *v4= &mvert[mf->v4]; + + tri_pt[0]= vert_remap_array[mf->v1]; + tri_pt[1]= vert_remap_array[mf->v3]; + tri_pt[2]= vert_remap_array[mf->v4]; + tri_pt= tri_pt+3; + + // the vertex location + if (vert_tag_array[mf->v4]==true) { /* *** v4 *** */ + vert_tag_array[mf->v4]= false; + *bt++ = v4->co[0]; + *bt++ = v4->co[1]; + *bt++ = v4->co[2]; + } + } + } + } + + dm->release(dm); + dm = NULL; + const int vertsPerPoly = 3; + buildMeshAdjacency(faces, npolys, nverts, vertsPerPoly); + return true; +} + +bool KX_Pathfinder::createFromMesh(RAS_MeshObject* meshobj) +{ + float* vertices = NULL; + unsigned short* faces = NULL; + int nverts = 0, npolys = 0; + if (!buildVertIndArrays(meshobj, vertices, nverts, faces, npolys)) + return false; + + + int ndtris = npolys; + int uniqueDetailVerts = 0; + float cs = 0.2f; + + if (!nverts || !npolys) + return false; + + float bmin[3], bmax[3]; + calcMeshBounds(vertices, nverts, bmin, bmax); + + // 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*uniqueDetailVerts; + 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 = uniqueDetailVerts; + header->ndtris = ndtris; + + memcpy(navVerts, vertices, nverts*3*sizeof(float)); + + // Store polygons + const int nvp = 3; + const unsigned short* src = faces; + for (int i = 0; i < npolys; ++i) + { + dtStatPoly* p = &navPolys[i]; + p->nv = 0; + for (int j = 0; j < nvp; ++j) + { + p->v[j] = src[j]; + p->n[j] = src[nvp+j]+1; + p->nv++; + } + src += nvp*2; + } + + //quantize vertex pos to creating BVTree + unsigned short* vertsi = new unsigned short[3*nverts]; + float* vf = vertices; + unsigned short* vi = vertsi; + float ics = 1.f/cs; + for (int i=0; i(vf[i]*ics); + } + header->nnodes = createBVTree(vertsi, nverts, faces, npolys, nvp, + cs, cs, npolys*2, navNodes); + + //create fake detail meshes + unsigned short vbase = 0; + 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; + const unsigned short* face = faces; + for(size_t i=0; iinit(data, dataSize, true); + + delete [] vertices; + delete [] faces; + + return true; +} + +void KX_Pathfinder::debugDraw() +{ + if (!m_navMesh) + return; + MT_Vector3 color(0.f, 0.f, 0.f); + + 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_Vector3 tri[3]; + for (int k = 0; k < 3; ++k) + { + if (t[k] < p->nv) + tri[k].setValue(m_navMesh->getVertex(p->v[t[k]])); + else + tri[k].setValue(m_navMesh->getDetailVertex(pd->vbase+(t[k]-p->nv))); + } + + for (int k=0; k<3; k++) + KX_RasterizerDrawDebugLine(tri[k], tri[(k+1)%3], color); + } + } +} diff --git a/source/gameengine/Ketsji/KX_Pathfinder.h b/source/gameengine/Ketsji/KX_Pathfinder.h new file mode 100644 index 00000000000..587f5af2b4f --- /dev/null +++ b/source/gameengine/Ketsji/KX_Pathfinder.h @@ -0,0 +1,51 @@ +/** +* $Id: +* +* ***** 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 ***** +*/ +#ifndef __KX_PATHFINDER +#define __KX_PATHFINDER +#include "DetourStatNavMesh.h" +#include + +class RAS_MeshObject; + +class KX_Pathfinder +{ +public: + KX_Pathfinder(); + ~KX_Pathfinder(); + bool createFromMesh(RAS_MeshObject* meshobj); + void debugDraw(); +protected: + bool buildVertIndArrays(RAS_MeshObject* meshobj, float *&vertices, int& nverts, + unsigned short *&faces, int& npolys); + + dtStatNavMesh* m_navMesh; +}; + +#endif //__KX_PATHFINDER + diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp index ebd0fa5c525..4f2884ee751 100644 --- a/source/gameengine/Ketsji/KX_Scene.cpp +++ b/source/gameengine/Ketsji/KX_Scene.cpp @@ -94,7 +94,7 @@ #endif #include "KX_Light.h" - +#include "KX_Pathfinder.h" #include void* KX_SceneReplicationFunc(SG_IObject* node,void* gameobj,void* scene) @@ -150,7 +150,8 @@ KX_Scene::KX_Scene(class SCA_IInputDevice* keyboarddevice, m_networkDeviceInterface(ndi), m_active_camera(NULL), m_ueberExecutionPriority(0), - m_blenderScene(scene) + m_blenderScene(scene), + m_pathfinder(NULL) { m_suspendedtime = 0.0; m_suspendeddelta = 0.0; @@ -210,6 +211,8 @@ KX_Scene::KX_Scene(class SCA_IInputDevice* keyboarddevice, m_rootnode = NULL; m_bucketmanager=new RAS_BucketManager(); + + m_pathfinder = new KX_Pathfinder(); #ifndef DISABLE_PYTHON m_attr_dict = PyDict_New(); /* new ref */ @@ -265,6 +268,11 @@ KX_Scene::~KX_Scene() delete m_bucketmanager; } + if (m_pathfinder) + { + delete m_pathfinder; + } + #ifndef DISABLE_PYTHON PyDict_Clear(m_attr_dict); Py_DECREF(m_attr_dict); diff --git a/source/gameengine/Ketsji/KX_Scene.h b/source/gameengine/Ketsji/KX_Scene.h index 4755eee6a6b..e19d846f0ad 100644 --- a/source/gameengine/Ketsji/KX_Scene.h +++ b/source/gameengine/Ketsji/KX_Scene.h @@ -83,6 +83,7 @@ class SCA_JoystickManager; class btCollisionShape; class KX_BlenderSceneConverter; struct KX_ClientObjectInfo; +class KX_Pathfinder; /* for ID freeing */ #define IS_TAGGED(_id) ((_id) && (((ID *)_id)->flag & LIB_DOIT)) @@ -276,6 +277,8 @@ protected: struct Scene* m_blenderScene; RAS_2DFilterManager m_filtermanager; + + KX_Pathfinder* m_pathfinder; public: KX_Scene(class SCA_IInputDevice* keyboarddevice, class SCA_IInputDevice* mousedevice, @@ -611,7 +614,7 @@ public: // m_bucketmanager->PrintStats(verbose_level) //} - + KX_Pathfinder *GetPathfinder() {return m_pathfinder; }; }; typedef std::vector KX_SceneList;