diff --git a/RecastDemo/Include/NavmeshPruneTool.h b/RecastDemo/Include/NavmeshPruneTool.h new file mode 100644 index 0000000..00697d9 --- /dev/null +++ b/RecastDemo/Include/NavmeshPruneTool.h @@ -0,0 +1,51 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#ifndef NAVMESHPRUNETOOL_H +#define NAVMESHPRUNETOOL_H + +#include "Sample.h" + +// Prune navmesh to accessible locations from a point. + +class NavmeshPruneTool : public SampleTool +{ + Sample* m_sample; + + class NavmeshFlags* m_flags; + + float m_hitPos[3]; + bool m_hitPosSet; + +public: + NavmeshPruneTool(); + ~NavmeshPruneTool(); + + virtual int type() { return TOOL_NAVMESH_PRUNE; } + virtual void init(Sample* sample); + virtual void reset(); + virtual void handleMenu(); + virtual void handleClick(const float* s, const float* p, bool shift); + virtual void handleToggle(); + virtual void handleStep(); + virtual void handleUpdate(const float dt); + virtual void handleRender(); + virtual void handleRenderOverlay(double* proj, double* model, int* view); +}; + +#endif // NAVMESHPRUNETOOL_H diff --git a/RecastDemo/Include/Sample.h b/RecastDemo/Include/Sample.h index 9b617dd..68b5e7a 100644 --- a/RecastDemo/Include/Sample.h +++ b/RecastDemo/Include/Sample.h @@ -31,6 +31,7 @@ enum SampleToolType TOOL_TILE_HIGHLIGHT, TOOL_TEMP_OBSTACLE, TOOL_NAVMESH_TESTER, + TOOL_NAVMESH_PRUNE, TOOL_OFFMESH_CONNECTION, TOOL_CONVEX_VOLUME, TOOL_CROWD, diff --git a/RecastDemo/Source/CrowdTool.cpp b/RecastDemo/Source/CrowdTool.cpp index 3d1c582..a1bff71 100644 --- a/RecastDemo/Source/CrowdTool.cpp +++ b/RecastDemo/Source/CrowdTool.cpp @@ -561,8 +561,6 @@ void CrowdTool::handleRender() if (!nav || !crowd) return; - duDebugDrawNavMeshPolysWithFlags(&dd, *nav, SAMPLE_POLYFLAGS_DISABLED, duRGBA(220,0,0,128)); - if (m_showNodes && crowd->getPathQueue()) { const dtNavMeshQuery* navquery = crowd->getPathQueue()->getNavQuery(); diff --git a/RecastDemo/Source/NavMeshTesterTool.cpp b/RecastDemo/Source/NavMeshTesterTool.cpp index 693a216..373a0be 100644 --- a/RecastDemo/Source/NavMeshTesterTool.cpp +++ b/RecastDemo/Source/NavMeshTesterTool.cpp @@ -159,7 +159,7 @@ NavMeshTesterTool::NavMeshTesterTool() : m_pathIterNum(0), m_steerPointCount(0) { - m_filter.setIncludeFlags(SAMPLE_POLYFLAGS_ALL); + m_filter.setIncludeFlags(SAMPLE_POLYFLAGS_ALL ^ SAMPLE_POLYFLAGS_DISABLED); m_filter.setExcludeFlags(0); m_polyPickExt[0] = 2; diff --git a/RecastDemo/Source/NavmeshPruneTool.cpp b/RecastDemo/Source/NavmeshPruneTool.cpp new file mode 100644 index 0000000..45a9112 --- /dev/null +++ b/RecastDemo/Source/NavmeshPruneTool.cpp @@ -0,0 +1,349 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include "SDL.h" +#include "SDL_opengl.h" +#include "imgui.h" +#include "NavmeshPruneTool.h" +#include "InputGeom.h" +#include "Sample.h" +#include "DetourNavMesh.h" +#include "DetourCommon.h" +#include "DetourAssert.h" +#include "DetourDebugDraw.h" + +#ifdef WIN32 +# define snprintf _snprintf +#endif + + + +// Copy/paste from Recast int array +class PolyRefArray +{ + dtPolyRef* m_data; + int m_size, m_cap; + inline PolyRefArray(const PolyRefArray&); + inline PolyRefArray& operator=(const PolyRefArray&); +public: + + inline PolyRefArray() : m_data(0), m_size(0), m_cap(0) {} + inline PolyRefArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); } + inline ~PolyRefArray() { dtFree(m_data); } + void resize(int n) + { + if (n > m_cap) + { + if (!m_cap) m_cap = n; + while (m_cap < n) m_cap *= 2; + dtPolyRef* newData = (dtPolyRef*)dtAlloc(m_cap*sizeof(dtPolyRef), DT_ALLOC_TEMP); + if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(dtPolyRef)); + dtFree(m_data); + m_data = newData; + } + m_size = n; + } + inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; } + inline dtPolyRef pop() { if (m_size > 0) m_size--; return m_data[m_size]; } + inline const dtPolyRef& operator[](int i) const { return m_data[i]; } + inline dtPolyRef& operator[](int i) { return m_data[i]; } + inline int size() const { return m_size; } +}; + + + + +class NavmeshFlags +{ + struct TileFlags + { + inline void purge() { dtFree(flags); } + unsigned char* flags; + int nflags; + dtPolyRef base; + }; + + const dtNavMesh* m_nav; + TileFlags* m_tiles; + int m_ntiles; + +public: + NavmeshFlags() : + m_nav(0), m_tiles(0), m_ntiles(0) + { + } + + ~NavmeshFlags() + { + for (int i = 0; i < m_ntiles; ++i) + m_tiles[i].purge(); + dtFree(m_tiles); + } + + bool init(const dtNavMesh* nav) + { + m_ntiles = nav->getMaxTiles(); + if (!m_ntiles) + return true; + m_tiles = (TileFlags*)dtAlloc(sizeof(TileFlags)*m_ntiles, DT_ALLOC_TEMP); + if (!m_tiles) + { + return false; + } + memset(m_tiles, 0, sizeof(TileFlags)*m_ntiles); + + // Alloc flags for each tile. + for (int i = 0; i < nav->getMaxTiles(); ++i) + { + const dtMeshTile* tile = nav->getTile(i); + if (!tile->header) continue; + TileFlags* tf = &m_tiles[i]; + tf->nflags = tile->header->polyCount; + tf->base = nav->getPolyRefBase(tile); + if (tf->nflags) + { + tf->flags = (unsigned char*)dtAlloc(tf->nflags, DT_ALLOC_TEMP); + if (!tf->flags) + return false; + memset(tf->flags, 0, tf->nflags); + } + } + + m_nav = nav; + + return false; + } + + inline void clearAllFlags() + { + for (int i = 0; i < m_ntiles; ++i) + { + TileFlags* tf = &m_tiles[i]; + if (tf->nflags) + memset(tf->flags, 0, tf->nflags); + } + } + + inline unsigned char getFlags(dtPolyRef ref) + { + dtAssert(m_nav); + dtAssert(m_ntiles); + // Assume the ref is valid, no bounds checks. + unsigned int salt, it, ip; + m_nav->decodePolyId(ref, salt, it, ip); + return m_tiles[it].flags[ip]; + } + + inline void setFlags(dtPolyRef ref, unsigned char flags) + { + dtAssert(m_nav); + dtAssert(m_ntiles); + // Assume the ref is valid, no bounds checks. + unsigned int salt, it, ip; + m_nav->decodePolyId(ref, salt, it, ip); + m_tiles[it].flags[ip] = flags; + } + +}; + +static void floodNavmesh(dtNavMesh* nav, NavmeshFlags* flags, dtPolyRef start, unsigned char flag) +{ + // If already visited, skip. + if (flags->getFlags(start)) + return; + + PolyRefArray openList; + openList.push(start); + + while (openList.size()) + { + const dtPolyRef ref = openList.pop(); + // Get current poly and tile. + // The API input has been cheked already, skip checking internal data. + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + nav->getTileAndPolyByRefUnsafe(ref, &tile, &poly); + + // Visit linked polygons. + for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next) + { + const dtPolyRef neiRef = tile->links[i].ref; + // Skip invalid and already visited. + if (!neiRef || flags->getFlags(neiRef)) + continue; + // Mark as visited + flags->setFlags(neiRef, flag); + // Visit neighbours + openList.push(neiRef); + } + } +} + +static void disableUnvisitedPolys(dtNavMesh* nav, NavmeshFlags* flags) +{ + for (int i = 0; i < nav->getMaxTiles(); ++i) + { + const dtMeshTile* tile = ((const dtNavMesh*)nav)->getTile(i); + if (!tile->header) continue; + const dtPolyRef base = nav->getPolyRefBase(tile); + for (int j = 0; j < tile->header->polyCount; ++j) + { + const dtPolyRef ref = base | (unsigned int)j; + if (!flags->getFlags(ref)) + { + unsigned short f = 0; + nav->getPolyFlags(ref, &f); + nav->setPolyFlags(ref, f | SAMPLE_POLYFLAGS_DISABLED); + } + } + } +} + +NavmeshPruneTool::NavmeshPruneTool() : + m_flags(0), + m_hitPosSet(false) +{ +} + +NavmeshPruneTool::~NavmeshPruneTool() +{ + delete m_flags; +} + +void NavmeshPruneTool::init(Sample* sample) +{ + m_sample = sample; +} + +void NavmeshPruneTool::reset() +{ + m_hitPosSet = false; + delete m_flags; + m_flags = 0; +} + +void NavmeshPruneTool::handleMenu() +{ + dtNavMesh* nav = m_sample->getNavMesh(); + if (!nav) return; + if (!m_flags) return; + + if (imguiButton("Clear Selection")) + { + m_flags->clearAllFlags(); + } + + if (imguiButton("Prune Unselected")) + { + disableUnvisitedPolys(nav, m_flags); + delete m_flags; + m_flags = 0; + } +} + +void NavmeshPruneTool::handleClick(const float* /*s*/, const float* p, bool shift) +{ + if (!m_sample) return; + InputGeom* geom = m_sample->getInputGeom(); + if (!geom) return; + dtNavMesh* nav = m_sample->getNavMesh(); + if (!nav) return; + dtNavMeshQuery* query = m_sample->getNavMeshQuery(); + if (!query) return; + + dtVcopy(m_hitPos, p); + m_hitPosSet = true; + + if (!m_flags) + { + m_flags = new NavmeshFlags; + m_flags->init(nav); + } + + const float ext[3] = {2,4,2}; + dtQueryFilter filter; + dtPolyRef ref = 0; + query->findNearestPoly(p, ext, &filter, &ref, 0); + + floodNavmesh(nav, m_flags, ref, 1); +} + +void NavmeshPruneTool::handleToggle() +{ +} + +void NavmeshPruneTool::handleStep() +{ +} + +void NavmeshPruneTool::handleUpdate(const float /*dt*/) +{ +} + +void NavmeshPruneTool::handleRender() +{ + DebugDrawGL dd; + + if (m_hitPosSet) + { + const float s = m_sample->getAgentRadius(); + const unsigned int col = duRGBA(255,255,255,255); + dd.begin(DU_DRAW_LINES); + dd.vertex(m_hitPos[0]-s,m_hitPos[1],m_hitPos[2], col); + dd.vertex(m_hitPos[0]+s,m_hitPos[1],m_hitPos[2], col); + dd.vertex(m_hitPos[0],m_hitPos[1]-s,m_hitPos[2], col); + dd.vertex(m_hitPos[0],m_hitPos[1]+s,m_hitPos[2], col); + dd.vertex(m_hitPos[0],m_hitPos[1],m_hitPos[2]-s, col); + dd.vertex(m_hitPos[0],m_hitPos[1],m_hitPos[2]+s, col); + dd.end(); + } + + const dtNavMesh* nav = m_sample->getNavMesh(); + if (m_flags && nav) + { + for (int i = 0; i < nav->getMaxTiles(); ++i) + { + const dtMeshTile* tile = nav->getTile(i); + if (!tile->header) continue; + const dtPolyRef base = nav->getPolyRefBase(tile); + for (int j = 0; j < tile->header->polyCount; ++j) + { + const dtPolyRef ref = base | (unsigned int)j; + if (m_flags->getFlags(ref)) + { + duDebugDrawNavMeshPoly(&dd, *nav, ref, duRGBA(255,255,255,128)); + } + } + } + } + +} + +void NavmeshPruneTool::handleRenderOverlay(double* proj, double* model, int* view) +{ + // Tool help + const int h = view[3]; + + imguiDrawText(280, h-40, IMGUI_ALIGN_LEFT, "LMB: Click fill area.", imguiRGBA(255,255,255,192)); + +} diff --git a/RecastDemo/Source/Sample_SoloMesh.cpp b/RecastDemo/Source/Sample_SoloMesh.cpp index ad42698..8f3a8bb 100644 --- a/RecastDemo/Source/Sample_SoloMesh.cpp +++ b/RecastDemo/Source/Sample_SoloMesh.cpp @@ -33,6 +33,7 @@ #include "DetourNavMeshBuilder.h" #include "DetourDebugDraw.h" #include "NavMeshTesterTool.h" +#include "NavmeshPruneTool.h" #include "OffMeshConnectionTool.h" #include "ConvexVolumeTool.h" #include "CrowdTool.h" @@ -103,6 +104,10 @@ void Sample_SoloMesh::handleTools() { setTool(new NavMeshTesterTool); } + if (imguiCheck("Prune Navmesh", type == TOOL_NAVMESH_PRUNE)) + { + setTool(new NavmeshPruneTool); + } if (imguiCheck("Create Off-Mesh Connections", type == TOOL_OFFMESH_CONNECTION)) { setTool(new OffMeshConnectionTool); @@ -247,6 +252,7 @@ void Sample_SoloMesh::handleRender() duDebugDrawNavMeshBVTree(&dd, *m_navMesh); if (m_drawMode == DRAWMODE_NAVMESH_NODES) duDebugDrawNavMeshNodes(&dd, *m_navQuery); + duDebugDrawNavMeshPolysWithFlags(&dd, *m_navMesh, SAMPLE_POLYFLAGS_DISABLED, duRGBA(0,0,0,128)); } glDepthMask(GL_TRUE); diff --git a/RecastDemo/Source/Sample_TempObstacles.cpp b/RecastDemo/Source/Sample_TempObstacles.cpp index b0f22b2..05a8cdd 100644 --- a/RecastDemo/Source/Sample_TempObstacles.cpp +++ b/RecastDemo/Source/Sample_TempObstacles.cpp @@ -914,6 +914,7 @@ void Sample_TempObstacles::handleRender() duDebugDrawNavMeshPortals(&dd, *m_navMesh); if (m_drawMode == DRAWMODE_NAVMESH_NODES) duDebugDrawNavMeshNodes(&dd, *m_navQuery); + duDebugDrawNavMeshPolysWithFlags(&dd, *m_navMesh, SAMPLE_POLYFLAGS_DISABLED, duRGBA(0,0,0,128)); } diff --git a/RecastDemo/Source/Sample_TileMesh.cpp b/RecastDemo/Source/Sample_TileMesh.cpp index 1705a0e..f6d300e 100644 --- a/RecastDemo/Source/Sample_TileMesh.cpp +++ b/RecastDemo/Source/Sample_TileMesh.cpp @@ -32,6 +32,7 @@ #include "DetourNavMeshBuilder.h" #include "DetourDebugDraw.h" #include "NavMeshTesterTool.h" +#include "NavMeshPruneTool.h" #include "OffMeshConnectionTool.h" #include "ConvexVolumeTool.h" #include "CrowdTool.h" @@ -412,6 +413,10 @@ void Sample_TileMesh::handleTools() { setTool(new NavMeshTesterTool); } + if (imguiCheck("Prune Navmesh", type == TOOL_NAVMESH_PRUNE)) + { + setTool(new NavmeshPruneTool); + } if (imguiCheck("Create Tiles", type == TOOL_TILE_EDIT)) { setTool(new NavMeshTileTool); @@ -575,6 +580,7 @@ void Sample_TileMesh::handleRender() duDebugDrawNavMeshPortals(&dd, *m_navMesh); if (m_drawMode == DRAWMODE_NAVMESH_NODES) duDebugDrawNavMeshNodes(&dd, *m_navQuery); + duDebugDrawNavMeshPolysWithFlags(&dd, *m_navMesh, SAMPLE_POLYFLAGS_DISABLED, duRGBA(0,0,0,128)); }