Recast: tilemesh editor refactor

Move common code to Editor_Common and fix solomesh. Solomesh is useful for debugging recast as it allows for generating everything in 1 tile. Also did numerous fixes for the tilecache code, though this is still broken and won't be really used for the r5sdk project, but its nice to have for a potential project outside r5sdk that uses the xyz coordination system, which avoids having to run the navmesh through a conversion pass in runtime.
This commit is contained in:
Kawe Mazidjatari 2024-07-12 13:01:47 +02:00
parent 59a8f12e0e
commit f95eb13ab3
12 changed files with 1083 additions and 1151 deletions

View File

@ -4,11 +4,15 @@ add_module( "exe" "recast" "" ${FOLDER_CONTEXT} TRUE TRUE )
start_sources()
add_sources( SOURCE_GROUP "Builder"
"Editor_Common.cpp"
"Editor_SoloMesh.cpp"
"Editor_TileMesh.cpp"
"InputGeom.cpp"
)
add_sources( SOURCE_GROUP "Builder/Include"
"Include/Editor_Common.h"
"Include/Editor_SoloMesh.h"
"include/Editor_TileMesh.h"
"include/InputGeom.h"
)

View File

@ -29,6 +29,9 @@
#include "NavEditor/Include/InputGeom.h"
#include "NavEditor/Include/Editor.h"
#include "game/server/ai_navmesh.h"
#include "game/server/ai_hull.h"
unsigned int EditorDebugDraw::areaToCol(unsigned int area)
{
switch(area)
@ -170,12 +173,11 @@ void Editor::collectSettings(BuildSettings& settings)
void Editor::resetCommonSettings()
{
selectNavMeshType(NAVMESH_SMALL);
m_cellSize = 16.0f;
m_cellHeight = 5.85f;
m_agentHeight = 2.0f;
m_agentRadius = 0.6f;
m_agentMaxClimb = 0.9f;
m_agentMaxSlope = 45.0f;
m_agentMaxSlope = 45.0f; // todo(amos) put into hull def!
m_regionMinSize = 8;
m_regionMergeSize = 20;
m_edgeMaxLen = 12;
@ -187,6 +189,19 @@ void Editor::resetCommonSettings()
}
void Editor::handleCommonSettings()
{
ImGui::Text("NavMesh Type");
for (int i = 0; i < NAVMESH_COUNT; i++)
{
const NavMeshType_e navMeshType = NavMeshType_e(i);
if (ImGui::Button(NavMesh_GetNameForType(navMeshType), ImVec2(120, 0)))
{
selectNavMeshType(navMeshType);
}
}
ImGui::Separator();
ImGui::PushItemWidth(180.f);
ImGui::Text("Rasterization");
@ -339,6 +354,97 @@ void Editor::renderOverlayToolStates(double* proj, double* model, int* view)
}
}
void Editor::renderNavMeshDebugMenu()
{
ImGui::Text("NavMesh Render Options");
bool isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_OFFMESHCONS);
if (ImGui::Checkbox("Off-Mesh Connections", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_OFFMESHCONS);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_NODES);
if (ImGui::Checkbox("Query Nodes", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_NODES);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_BVTREE);
if (ImGui::Checkbox("BVTree", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_BVTREE);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_PORTALS);
if (ImGui::Checkbox("Portals", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_PORTALS);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_CLOSEDLIST);
if (ImGui::Checkbox("Closed List", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_CLOSEDLIST);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_COLOR_TILES);
if (ImGui::Checkbox("Tile ID Colors", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_COLOR_TILES);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_VERTS);
if (ImGui::Checkbox("Vertex Points", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_VERTS);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_INNERBOUND);
if (ImGui::Checkbox("Inner Poly Boundaries", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_INNERBOUND);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_OUTERBOUND);
if (ImGui::Checkbox("Outer Poly Boundaries", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_OUTERBOUND);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_POLYCENTERS);
if (ImGui::Checkbox("Poly Centers", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_POLYCENTERS);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_POLYGROUPS);
if (ImGui::Checkbox("Poly Group Colors", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_POLYGROUPS);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_DEPTH_MASK);
if (ImGui::Checkbox("Depth Mask", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_DEPTH_MASK);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_ALPHA);
if (ImGui::Checkbox("Transparency", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_ALPHA);
}
const hulldef hulls[NAVMESH_COUNT] = {
{ g_navMeshNames[NAVMESH_SMALL] , NAI_Hull::Width(HULL_HUMAN) , NAI_Hull::Height(HULL_HUMAN) * NAI_Hull::Scale(HULL_HUMAN) , 45, 32 },
{ g_navMeshNames[NAVMESH_MED_SHORT] , NAI_Hull::Width(HULL_PROWLER), NAI_Hull::Height(HULL_PROWLER) * NAI_Hull::Scale(HULL_PROWLER), 50, 32 },
{ g_navMeshNames[NAVMESH_MEDIUM] , NAI_Hull::Width(HULL_MEDIUM) , NAI_Hull::Height(HULL_MEDIUM) * NAI_Hull::Scale(HULL_MEDIUM) , 55, 32 },
{ g_navMeshNames[NAVMESH_LARGE] , NAI_Hull::Width(HULL_TITAN) , NAI_Hull::Height(HULL_TITAN) * NAI_Hull::Scale(HULL_TITAN) , 60, 64 },
{ g_navMeshNames[NAVMESH_EXTRA_LARGE], NAI_Hull::Width(HULL_GOLIATH), NAI_Hull::Height(HULL_GOLIATH) * NAI_Hull::Scale(HULL_GOLIATH), 65, 64 },
};
void Editor::selectNavMeshType(const NavMeshType_e navMeshType)
{
const hulldef& h = hulls[navMeshType];
m_agentRadius = h.radius;
m_agentMaxClimb = h.climbHeight;
m_agentHeight = h.height;
m_navmeshName = h.name;
m_tileSize = h.tileSize;
m_selectedNavMeshType = navMeshType;
}
dtNavMesh* Editor::loadAll(std::string path)
{
fs::path p = "..\\maps\\navmesh\\";
@ -362,12 +468,12 @@ dtNavMesh* Editor::loadAll(std::string path)
fclose(fp);
return 0;
}
if (header.magic != NAVMESHSET_MAGIC)
if (header.magic != NAVMESHSET_MAGIC) // todo(amos) check for tool mode since tilecache uses different constants!
{
fclose(fp);
return 0;
}
if (header.version != NAVMESHSET_VERSION)
if (header.version != NAVMESHSET_VERSION) // todo(amos) check for tool mode since tilecache uses different constants!
{
fclose(fp);
return 0;

View File

@ -0,0 +1,593 @@
//
// 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.
//
#include "Shared/Include/SharedAssert.h"
#include "Include/Editor.h"
#include "Include/Editor_Common.h"
#include "DebugUtils/Include/RecastDebugDraw.h"
#include "DebugUtils/Include/DetourDebugDraw.h"
#include "Include/InputGeom.h"
#include <DetourTileCache/Include/DetourTileCache.h>
// todo(amos): move these to common math.
inline unsigned int nextPow2(unsigned int v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
// todo(amos): move these to common math.
inline unsigned int ilog2(unsigned int v)
{
unsigned int r;
unsigned int shift;
r = (v > 0xffff) << 4; v >>= r;
shift = (v > 0xff) << 3; v >>= shift; r |= shift;
shift = (v > 0xf) << 2; v >>= shift; r |= shift;
shift = (v > 0x3) << 1; v >>= shift; r |= shift;
r |= (v >> 1);
return r;
}
static void EditorCommon_DrawInputGeometry(duDebugDraw* const dd, const InputGeom* const geom,
const float maxSlope, const float textureScale)
{
duDebugDrawTriMeshSlope(dd, geom->getMesh()->getVerts(), geom->getMesh()->getVertCount(),
geom->getMesh()->getTris(), geom->getMesh()->getNormals(), geom->getMesh()->getTriCount(),
maxSlope, textureScale);
}
static void EditorCommon_DrawBoundingBox(duDebugDraw* const dd, const InputGeom* const geom)
{
// Draw bounds
const float* const bmin = geom->getNavMeshBoundsMin();
const float* const bmax = geom->getNavMeshBoundsMax();
duDebugDrawBoxWire(dd, bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2], duRGBA(255, 255, 255, 128), 1.0f);
}
static void EditorCommon_DrawTilingGrid(duDebugDraw* const dd, const InputGeom* const geom, const int tileSize, const float cellSize)
{
const float* const bmin = geom->getNavMeshBoundsMin();
const float* const bmax = geom->getNavMeshBoundsMax();
int gw = 0, gh = 0;
rcCalcGridSize(bmin, bmax, cellSize, &gw, &gh);
const int tw = (gw + tileSize - 1) / tileSize;
const int th = (gh + tileSize - 1) / tileSize;
const float s = tileSize * cellSize;
duDebugDrawGridXY(dd, bmax[0], bmin[1], bmin[2], tw, th, s, duRGBA(0, 0, 0, 64), 1.0f);
}
int EditorCommon_SetAndRenderTileProperties(const InputGeom* const geom, const int tileSize,
const float cellSize, int& maxTiles, int& maxPolysPerTile)
{
int gridSize = 1;
if (geom)
{
int gw = 0, gh = 0;
const float* bmin = geom->getNavMeshBoundsMin();
const float* bmax = geom->getNavMeshBoundsMax();
rcCalcGridSize(bmin, bmax, cellSize, &gw, &gh);
const int ts = tileSize;
const int tw = (gw + ts-1) / ts;
const int th = (gh + ts-1) / ts;
ImGui::Text("Tiles: %d x %d", tw, th);
ImGui::Text("Tile Sizes: %g x %g (%g)", tw* cellSize, th*cellSize, tileSize*cellSize);
// Max tiles and max polys affect how the tile IDs are calculated.
// There are 28 bits available for identifying a tile and a polygon.
int tileBits = rcMin((int)ilog2(nextPow2(tw*th)), 16);
int polyBits = 28 - tileBits;
maxTiles = 1 << tileBits;
maxPolysPerTile = 1 << polyBits;
gridSize = tw*th;
ImGui::Text("Max Tiles: %d", maxTiles);
ImGui::Text("Max Polys: %d", maxPolysPerTile);
}
else
{
maxTiles = 0;
maxPolysPerTile = 0;
gridSize = 1;
}
return gridSize;
}
Editor_StaticTileMeshCommon::Editor_StaticTileMeshCommon()
: m_triareas(nullptr)
, m_solid(nullptr)
, m_chf(nullptr)
, m_cset(nullptr)
, m_pmesh(nullptr)
, m_dmesh(nullptr)
, m_tileMeshDrawFlags(TM_DRAWFLAGS_INPUT_MESH|TM_DRAWFLAGS_NAVMESH)
, m_tileCol(duRGBA(0, 0, 0, 32))
, m_totalBuildTimeMs(0.0f)
, m_drawActiveTile(false)
, m_keepInterResults(false)
{
m_lastBuiltTileBmin[0] = 0.0f;
m_lastBuiltTileBmin[1] = 0.0f;
m_lastBuiltTileBmin[2] = 0.0f;
m_lastBuiltTileBmax[0] = 0.0f;
m_lastBuiltTileBmax[1] = 0.0f;
m_lastBuiltTileBmax[2] = 0.0f;
memset(&m_cfg, 0, sizeof(rcConfig));
}
void Editor_StaticTileMeshCommon::cleanup()
{
delete[] m_triareas;
m_triareas = 0;
rcFreeHeightField(m_solid);
m_solid = 0;
rcFreeCompactHeightfield(m_chf);
m_chf = 0;
rcFreeContourSet(m_cset);
m_cset = 0;
rcFreePolyMesh(m_pmesh);
m_pmesh = 0;
rcFreePolyMeshDetail(m_dmesh);
m_dmesh = 0;
}
void Editor_StaticTileMeshCommon::renderTileMeshRenderOptions()
{
ImGui::Text("TileMesh Render Options");
bool isEnabled = m_tileMeshDrawFlags & TM_DRAWFLAGS_INPUT_MESH;
// This should always be available, since if we load a large mesh we want to
// be able to toggle this off to save on performance. The renderer has to be
// moved to its own thread to solve this issue.
if (ImGui::Checkbox("Input Mesh", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_INPUT_MESH);
// Check which modes are valid.
//const bool hasNavMesh =m_navMesh != 0;
const bool hasChf = m_chf != 0;
const bool hasCset = m_cset != 0;
const bool hasSolid = m_solid != 0;
const bool hasDMesh = m_dmesh != 0;
const bool intermediateDataUnavailable = !hasChf || !hasCset || !hasSolid || !hasDMesh;
isEnabled = getTileMeshDrawFlags() & TM_DRAWFLAGS_NAVMESH;
//ImGui::BeginDisabled(!hasNavMesh);
if (ImGui::Checkbox("NavMesh", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_NAVMESH);
//ImGui::EndDisabled();
isEnabled = getTileMeshDrawFlags() & TM_DRAWFLAGS_VOXELS;
ImGui::BeginDisabled(!hasSolid);
if (ImGui::Checkbox("Voxels", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_VOXELS);
ImGui::EndDisabled();
isEnabled = getTileMeshDrawFlags() & TM_DRAWFLAGS_VOXELS_WALKABLE;
ImGui::BeginDisabled(!hasSolid);
if (ImGui::Checkbox("Walkable Voxels", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_VOXELS_WALKABLE);
ImGui::EndDisabled();
isEnabled = getTileMeshDrawFlags() & TM_DRAWFLAGS_COMPACT;
ImGui::BeginDisabled(!hasChf);
if (ImGui::Checkbox("Compact", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_COMPACT);
ImGui::EndDisabled();
isEnabled = getTileMeshDrawFlags() & TM_DRAWFLAGS_COMPACT_DISTANCE;
ImGui::BeginDisabled(!hasChf);
if (ImGui::Checkbox("Compact Distance", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_COMPACT_DISTANCE);
ImGui::EndDisabled();
isEnabled = getTileMeshDrawFlags() & TM_DRAWFLAGS_COMPACT_REGIONS;
ImGui::BeginDisabled(!hasChf);
if (ImGui::Checkbox("Compact Regions", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_COMPACT_REGIONS);
ImGui::EndDisabled();
isEnabled = getTileMeshDrawFlags() & TM_DRAWFLAGS_REGION_CONNECTIONS;
ImGui::BeginDisabled(!hasCset);
if (ImGui::Checkbox("Region Connections", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_REGION_CONNECTIONS);
ImGui::EndDisabled();
isEnabled = getTileMeshDrawFlags() & TM_DRAWFLAGS_RAW_CONTOURS;
ImGui::BeginDisabled(!hasCset);
if (ImGui::Checkbox("Raw Contours", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_RAW_CONTOURS);
ImGui::EndDisabled();
isEnabled = getTileMeshDrawFlags() & TM_DRAWFLAGS_CONTOURS;
ImGui::BeginDisabled(!hasCset);
if (ImGui::Checkbox("Contours", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_CONTOURS);
ImGui::EndDisabled();
isEnabled = getTileMeshDrawFlags() & TM_DRAWFLAGS_POLYMESH;
ImGui::BeginDisabled(!hasDMesh);
if (ImGui::Checkbox("Poly Mesh", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_POLYMESH);
ImGui::EndDisabled();
isEnabled = getTileMeshDrawFlags() & TM_DRAWFLAGS_POLYMESH_DETAIL;
ImGui::BeginDisabled(!hasDMesh);
if (ImGui::Checkbox("Poly Mesh Detail", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_POLYMESH_DETAIL);
ImGui::EndDisabled();
if (intermediateDataUnavailable)
{
ImGui::Separator();
ImGui::Text("Tick 'Keep Intermediate Results'");
ImGui::Text("rebuild some tiles to see");
ImGui::Text("more debug mode options.");
}
}
void Editor_StaticTileMeshCommon::renderTileMeshData()
{
if (!m_geom || !m_geom->getMesh())
return;
const float texScale = 1.0f / (m_cellSize * 10.0f);
// Draw input mesh
if (getTileMeshDrawFlags() & TM_DRAWFLAGS_INPUT_MESH)
EditorCommon_DrawInputGeometry(&m_dd, m_geom, m_agentMaxSlope, texScale);
glDepthMask(GL_FALSE);
// Draw bounds
EditorCommon_DrawBoundingBox(&m_dd, m_geom);
// Tiling grid.
EditorCommon_DrawTilingGrid(&m_dd, m_geom, m_tileSize, m_cellSize);
if (m_drawActiveTile)
{
// Draw active tile
duDebugDrawBoxWire(&m_dd, m_lastBuiltTileBmin[0], m_lastBuiltTileBmin[1], m_lastBuiltTileBmin[2],
m_lastBuiltTileBmax[0], m_lastBuiltTileBmax[1], m_lastBuiltTileBmax[2], m_tileCol, 1.0f);
}
if (m_navMesh && m_navQuery)
{
if (m_tileMeshDrawFlags & TM_DRAWFLAGS_NAVMESH)
{
duDebugDrawNavMeshWithClosedList(&m_dd, *m_navMesh, *m_navQuery, m_navMeshDrawFlags);
duDebugDrawNavMeshPolysWithFlags(&m_dd, *m_navMesh, EDITOR_POLYFLAGS_DISABLED, duRGBA(0, 0, 0, 128));
}
}
glDepthMask(GL_TRUE);
if (m_chf)
{
if (getTileMeshDrawFlags() & TM_DRAWFLAGS_COMPACT)
duDebugDrawCompactHeightfieldSolid(&m_dd, *m_chf);
if (getTileMeshDrawFlags() & TM_DRAWFLAGS_COMPACT_DISTANCE)
duDebugDrawCompactHeightfieldDistance(&m_dd, *m_chf);
if (getTileMeshDrawFlags() & TM_DRAWFLAGS_COMPACT_REGIONS)
duDebugDrawCompactHeightfieldRegions(&m_dd, *m_chf);
}
if (m_solid)
{
if (getTileMeshDrawFlags() & TM_DRAWFLAGS_VOXELS)
{
glEnable(GL_FOG);
duDebugDrawHeightfieldSolid(&m_dd, *m_solid);
glDisable(GL_FOG);
}
if (getTileMeshDrawFlags() & TM_DRAWFLAGS_VOXELS_WALKABLE)
{
glEnable(GL_FOG);
duDebugDrawHeightfieldWalkable(&m_dd, *m_solid);
glDisable(GL_FOG);
}
}
if (m_cset)
{
if (getTileMeshDrawFlags() & TM_DRAWFLAGS_RAW_CONTOURS)
{
glDepthMask(GL_FALSE);
duDebugDrawRawContours(&m_dd, *m_cset);
glDepthMask(GL_TRUE);
}
if (getTileMeshDrawFlags() & TM_DRAWFLAGS_CONTOURS)
{
glDepthMask(GL_FALSE);
duDebugDrawContours(&m_dd, *m_cset);
glDepthMask(GL_TRUE);
}
}
if ((m_chf &&m_cset) &&
(getTileMeshDrawFlags() & TM_DRAWFLAGS_REGION_CONNECTIONS))
{
duDebugDrawCompactHeightfieldRegions(&m_dd, *m_chf);
glDepthMask(GL_FALSE);
duDebugDrawRegionConnections(&m_dd, *m_cset);
glDepthMask(GL_TRUE);
}
if (m_pmesh && (getTileMeshDrawFlags() & TM_DRAWFLAGS_POLYMESH))
{
glDepthMask(GL_FALSE);
duDebugDrawPolyMesh(&m_dd, *m_pmesh);
glDepthMask(GL_TRUE);
}
if (m_dmesh && (getTileMeshDrawFlags() & TM_DRAWFLAGS_POLYMESH_DETAIL))
{
glDepthMask(GL_FALSE);
duDebugDrawPolyMeshDetail(&m_dd, *m_dmesh);
glDepthMask(GL_TRUE);
}
// TODO: also add flags for this
m_geom->drawConvexVolumes(&m_dd);
m_geom->drawOffMeshConnections(&m_dd);
if (m_tool)
m_tool->handleRender();
renderToolStates();
glDepthMask(GL_TRUE);
}
void Editor_StaticTileMeshCommon::renderIntermediateTileMeshOptions()
{
ImGui::Indent();
ImGui::Indent();
if (ImGui::Button("Load", ImVec2(123, 0)))
{
dtFreeNavMesh(m_navMesh);
m_navMesh = Editor::loadAll(m_modelName.c_str());
m_navQuery->init(m_navMesh, 2048);
m_loadedNavMeshType = m_selectedNavMeshType;
initToolStates(this);
}
if (ImGui::Button("Save", ImVec2(123, 0)))
{
Editor::saveAll(m_modelName.c_str(), m_navMesh);
}
ImGui::Unindent();
ImGui::Unindent();
ImGui::Text("Build Time: %.1fms", m_totalBuildTimeMs);
if (m_navMesh)
{
const dtNavMeshParams& params = m_navMesh->m_params;
const float* origin = m_navMesh->m_orig;
ImGui::Text("Mesh Origin: \n\tX: %g \n\tY: %g \n\tZ: %g", origin[0], origin[1], origin[2]);
ImGui::Text("Tile Dimensions: %g x %g", params.tileWidth, params.tileHeight);
ImGui::Text("Poly Group Count: %d", params.polyGroupCount);
ImGui::Text("Traversal Table Size: %d", params.traversalTableSize);
ImGui::Text("Traversal Table Count: %d", params.traversalTableCount);
ImGui::Text("Max Tiles: %d", params.maxTiles);
ImGui::Text("Max Polys: %d", params.maxPolys);
ImGui::Separator();
}
else
ImGui::Separator();
}
void drawTiles(duDebugDraw* dd, dtTileCache* tc)
{
unsigned int fcol[6];
float bmin[3], bmax[3];
for (int i = 0; i < tc->getTileCount(); ++i)
{
const dtCompressedTile* tile = tc->getTile(i);
if (!tile->header) continue;
tc->calcTightTileBounds(tile->header, bmin, bmax);
const unsigned int col = duIntToCol(i, 64);
duCalcBoxColors(fcol, col, col);
duDebugDrawBox(dd, bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2], fcol);
}
for (int i = 0; i < tc->getTileCount(); ++i)
{
const dtCompressedTile* tile = tc->getTile(i);
if (!tile->header) continue;
tc->calcTightTileBounds(tile->header, bmin, bmax);
const unsigned int col = duIntToCol(i, 255);
const float pad = tc->getParams()->cs * 0.1f;
duDebugDrawBoxWire(dd, bmin[0] - pad, bmin[1] - pad, bmin[2] - pad,
bmax[0] + pad, bmax[1] + pad, bmax[2] + pad, col, 2.0f);
}
}
void drawObstacles(duDebugDraw* dd, const dtTileCache* tc)
{
// Draw obstacles
for (int i = 0; i < tc->getObstacleCount(); ++i)
{
const dtTileCacheObstacle* ob = tc->getObstacle(i);
if (ob->state == DT_OBSTACLE_EMPTY) continue;
float bmin[3], bmax[3];
tc->getObstacleBounds(ob, bmin, bmax);
unsigned int col = 0;
if (ob->state == DT_OBSTACLE_PROCESSING)
col = duRGBA(255, 255, 0, 128);
else if (ob->state == DT_OBSTACLE_PROCESSED)
col = duRGBA(255, 192, 0, 192);
else if (ob->state == DT_OBSTACLE_REMOVING)
col = duRGBA(220, 0, 0, 128);
duDebugDrawCylinder(dd, bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2], col);
duDebugDrawCylinderWire(dd, bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2], duDarkenCol(col), 2);
}
}
Editor_DynamicTileMeshCommon::Editor_DynamicTileMeshCommon()
: m_tileCache(nullptr)
, m_talloc(nullptr)
, m_tcomp(nullptr)
, m_tmproc(nullptr)
, m_cacheBuildTimeMs(0.0f)
, m_cacheCompressedSize(0)
, m_cacheRawSize(0)
, m_cacheLayerCount(0)
, m_cacheBuildMemUsage(0)
, m_tileMeshDrawFlags(TM_DRAWFLAGS_INPUT_MESH|TM_DRAWFLAGS_NAVMESH)
, m_keepInterResults(false)
{
}
void Editor_DynamicTileMeshCommon::renderTileMeshRenderOptions()
{
ImGui::Text("TileMesh Render Options");
bool isEnabled = m_tileMeshDrawFlags & TM_DRAWFLAGS_INPUT_MESH;
// This should always be available, since if we load a large mesh we want to
// be able to toggle this off to save on performance. The renderer has to be
// moved to its own thread to solve this issue.
if (ImGui::Checkbox("Input Mesh", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_INPUT_MESH);
isEnabled = getTileMeshDrawFlags() & TM_DRAWFLAGS_NAVMESH;
//ImGui::BeginDisabled(!hasNavMesh);
if (ImGui::Checkbox("NavMesh", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_NAVMESH);
//ImGui::EndDisabled();
isEnabled = getTileMeshDrawFlags() & TM_DRAWFLAGS_TILE_CACHE_BOUNDS;
ImGui::BeginDisabled(!m_tileCache);
if (ImGui::Checkbox("Cache Bounds", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_TILE_CACHE_BOUNDS);
isEnabled = getTileMeshDrawFlags() & TM_DRAWFLAGS_TILE_CACHE_OBSTACLES;
if (ImGui::Checkbox("Temp Obstacles", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_TILE_CACHE_OBSTACLES);
ImGui::EndDisabled();
}
void Editor_DynamicTileMeshCommon::renderTileMeshData()
{
if (!m_geom || !m_geom->getMesh())
return;
const float texScale = 1.0f / (m_cellSize * 10.0f);
const unsigned int drawFlags = getTileMeshDrawFlags();
// Draw input mesh
if (getTileMeshDrawFlags() & TM_DRAWFLAGS_INPUT_MESH)
EditorCommon_DrawInputGeometry(&m_dd, m_geom, m_agentMaxSlope, texScale);
// Draw bounds
EditorCommon_DrawBoundingBox(&m_dd, m_geom);
// Tiling grid.
EditorCommon_DrawTilingGrid(&m_dd, m_geom, m_tileSize, m_cellSize);
if (m_tileCache && drawFlags & TM_DRAWFLAGS_TILE_CACHE_BOUNDS)
drawTiles(&m_dd, m_tileCache);
if (m_tileCache && drawFlags & TM_DRAWFLAGS_TILE_CACHE_OBSTACLES)
drawObstacles(&m_dd, m_tileCache);
const bool navMeshRenderingEnabled = (drawFlags & TM_DRAWFLAGS_NAVMESH) != 0;
if (m_navMesh && m_navQuery)
{
if (drawFlags & TM_DRAWFLAGS_NAVMESH)
{
duDebugDrawNavMeshWithClosedList(&m_dd, *m_navMesh, *m_navQuery, m_navMeshDrawFlags);
duDebugDrawNavMeshPolysWithFlags(&m_dd, *m_navMesh, EDITOR_POLYFLAGS_DISABLED, duRGBA(0, 0, 0, 128));
}
}
// TODO: also add flags for this
m_geom->drawConvexVolumes(&m_dd);
m_geom->drawOffMeshConnections(&m_dd);
if (m_tool)
m_tool->handleRender();
renderToolStates();
glDepthMask(GL_TRUE);
}

View File

@ -33,16 +33,7 @@
#include "NavEditor/Include/Editor_SoloMesh.h"
Editor_SoloMesh::Editor_SoloMesh() :
m_keepInterResults(true),
m_totalBuildTimeMs(0),
m_triareas(0),
m_solid(0),
m_chf(0),
m_cset(0),
m_pmesh(0),
m_dmesh(0),
m_drawMode(DRAWMODE_NAVMESH)
Editor_SoloMesh::Editor_SoloMesh()
{
setTool(new NavMeshTesterTool);
}
@ -74,264 +65,67 @@ void Editor_SoloMesh::handleSettings()
{
Editor::handleCommonSettings();
if (imguiCheck("Keep Itermediate Results", m_keepInterResults))
m_keepInterResults = !m_keepInterResults;
ImGui::Checkbox("Keep Intermediate Results", &m_keepInterResults);
ImGui::Separator();
imguiSeparator();
imguiIndent();
imguiIndent();
if (imguiButton("Save"))
{
Editor::saveAll("solo_navmesh.bin", m_navMesh);
}
if (imguiButton("Load"))
{
dtFreeNavMesh(m_navMesh);
m_navMesh = Editor::loadAll("solo_navmesh.bin");
m_navQuery->init(m_navMesh, 2048);
}
imguiUnindent();
imguiUnindent();
char msg[64];
snprintf(msg, 64, "Build Time: %.1fms", m_totalBuildTimeMs);
imguiLabel(msg);
imguiSeparator();
Editor_StaticTileMeshCommon::renderIntermediateTileMeshOptions();
}
void Editor_SoloMesh::handleTools()
{
int type = !m_tool ? TOOL_NONE : m_tool->type();
bool enabled = type == TOOL_NAVMESH_TESTER;
if (imguiCheck("Test Navmesh", type == TOOL_NAVMESH_TESTER))
if (ImGui::Checkbox("Test NavMesh", &enabled))
{
setTool(new NavMeshTesterTool);
}
if (imguiCheck("Prune Navmesh", type == TOOL_NAVMESH_PRUNE))
enabled = type == TOOL_NAVMESH_PRUNE;
if (ImGui::Checkbox("Prune NavMesh", &enabled))
{
setTool(new NavMeshPruneTool);
}
if (imguiCheck("Create Off-Mesh Connections", type == TOOL_OFFMESH_CONNECTION))
enabled = type == TOOL_OFFMESH_CONNECTION;
if (ImGui::Checkbox("Create Off-Mesh Connections", &enabled))
{
setTool(new OffMeshConnectionTool);
}
if (imguiCheck("Create Convex Volumes", type == TOOL_CONVEX_VOLUME))
enabled = type == TOOL_CONVEX_VOLUME;
if (ImGui::Checkbox("Create Convex Volumes", &enabled))
{
setTool(new ConvexVolumeTool);
}
if (imguiCheck("Create Crowds", type == TOOL_CROWD))
enabled = type == TOOL_CROWD;
if (ImGui::Checkbox("Create Crowds", &enabled))
{
setTool(new CrowdTool);
}
imguiSeparatorLine();
ImGui::Separator();
imguiIndent();
ImGui::Indent();
if (m_tool)
m_tool->handleMenu();
imguiUnindent();
ImGui::Unindent();
}
void Editor_SoloMesh::handleDebugMode()
{
// Check which modes are valid.
bool valid[MAX_DRAWMODE];
for (int i = 0; i < MAX_DRAWMODE; ++i)
valid[i] = false;
if (m_geom)
{
valid[DRAWMODE_NAVMESH] = m_navMesh != 0;
valid[DRAWMODE_NAVMESH_TRANS] = m_navMesh != 0;
valid[DRAWMODE_NAVMESH_BVTREE] = m_navMesh != 0;
valid[DRAWMODE_NAVMESH_NODES] = m_navQuery != 0;
valid[DRAWMODE_NAVMESH_INVIS] = m_navMesh != 0;
valid[DRAWMODE_MESH] = true;
valid[DRAWMODE_VOXELS] = m_solid != 0;
valid[DRAWMODE_VOXELS_WALKABLE] = m_solid != 0;
valid[DRAWMODE_COMPACT] = m_chf != 0;
valid[DRAWMODE_COMPACT_DISTANCE] = m_chf != 0;
valid[DRAWMODE_COMPACT_REGIONS] = m_chf != 0;
valid[DRAWMODE_REGION_CONNECTIONS] = m_cset != 0;
valid[DRAWMODE_RAW_CONTOURS] = m_cset != 0;
valid[DRAWMODE_BOTH_CONTOURS] = m_cset != 0;
valid[DRAWMODE_CONTOURS] = m_cset != 0;
valid[DRAWMODE_POLYMESH] = m_pmesh != 0;
valid[DRAWMODE_POLYMESH_DETAIL] = m_dmesh != 0;
}
int unavail = 0;
for (int i = 0; i < MAX_DRAWMODE; ++i)
if (!valid[i]) unavail++;
if (unavail == MAX_DRAWMODE)
return;
imguiLabel("Draw");
if (imguiCheck("Input Mesh", m_drawMode == DRAWMODE_MESH, valid[DRAWMODE_MESH]))
m_drawMode = DRAWMODE_MESH;
if (imguiCheck("Navmesh", m_drawMode == DRAWMODE_NAVMESH, valid[DRAWMODE_NAVMESH]))
m_drawMode = DRAWMODE_NAVMESH;
if (imguiCheck("Navmesh Invis", m_drawMode == DRAWMODE_NAVMESH_INVIS, valid[DRAWMODE_NAVMESH_INVIS]))
m_drawMode = DRAWMODE_NAVMESH_INVIS;
if (imguiCheck("Navmesh Trans", m_drawMode == DRAWMODE_NAVMESH_TRANS, valid[DRAWMODE_NAVMESH_TRANS]))
m_drawMode = DRAWMODE_NAVMESH_TRANS;
if (imguiCheck("Navmesh BVTree", m_drawMode == DRAWMODE_NAVMESH_BVTREE, valid[DRAWMODE_NAVMESH_BVTREE]))
m_drawMode = DRAWMODE_NAVMESH_BVTREE;
if (imguiCheck("Navmesh Nodes", m_drawMode == DRAWMODE_NAVMESH_NODES, valid[DRAWMODE_NAVMESH_NODES]))
m_drawMode = DRAWMODE_NAVMESH_NODES;
if (imguiCheck("Voxels", m_drawMode == DRAWMODE_VOXELS, valid[DRAWMODE_VOXELS]))
m_drawMode = DRAWMODE_VOXELS;
if (imguiCheck("Walkable Voxels", m_drawMode == DRAWMODE_VOXELS_WALKABLE, valid[DRAWMODE_VOXELS_WALKABLE]))
m_drawMode = DRAWMODE_VOXELS_WALKABLE;
if (imguiCheck("Compact", m_drawMode == DRAWMODE_COMPACT, valid[DRAWMODE_COMPACT]))
m_drawMode = DRAWMODE_COMPACT;
if (imguiCheck("Compact Distance", m_drawMode == DRAWMODE_COMPACT_DISTANCE, valid[DRAWMODE_COMPACT_DISTANCE]))
m_drawMode = DRAWMODE_COMPACT_DISTANCE;
if (imguiCheck("Compact Regions", m_drawMode == DRAWMODE_COMPACT_REGIONS, valid[DRAWMODE_COMPACT_REGIONS]))
m_drawMode = DRAWMODE_COMPACT_REGIONS;
if (imguiCheck("Region Connections", m_drawMode == DRAWMODE_REGION_CONNECTIONS, valid[DRAWMODE_REGION_CONNECTIONS]))
m_drawMode = DRAWMODE_REGION_CONNECTIONS;
if (imguiCheck("Raw Contours", m_drawMode == DRAWMODE_RAW_CONTOURS, valid[DRAWMODE_RAW_CONTOURS]))
m_drawMode = DRAWMODE_RAW_CONTOURS;
if (imguiCheck("Both Contours", m_drawMode == DRAWMODE_BOTH_CONTOURS, valid[DRAWMODE_BOTH_CONTOURS]))
m_drawMode = DRAWMODE_BOTH_CONTOURS;
if (imguiCheck("Contours", m_drawMode == DRAWMODE_CONTOURS, valid[DRAWMODE_CONTOURS]))
m_drawMode = DRAWMODE_CONTOURS;
if (imguiCheck("Poly Mesh", m_drawMode == DRAWMODE_POLYMESH, valid[DRAWMODE_POLYMESH]))
m_drawMode = DRAWMODE_POLYMESH;
if (imguiCheck("Poly Mesh Detail", m_drawMode == DRAWMODE_POLYMESH_DETAIL, valid[DRAWMODE_POLYMESH_DETAIL]))
m_drawMode = DRAWMODE_POLYMESH_DETAIL;
if (unavail)
{
imguiValue("Tick 'Keep Itermediate Results'");
imguiValue("to see more debug mode options.");
}
Editor::renderNavMeshDebugMenu();
ImGui::Separator();
Editor_StaticTileMeshCommon::renderTileMeshRenderOptions();
}
void Editor_SoloMesh::handleRender()
{
if (!m_geom || !m_geom->getMesh())
return;
glEnable(GL_FOG);
glDepthMask(GL_TRUE);
const float texScale = 1.0f / (m_cellSize * 10.0f);
if (m_drawMode != DRAWMODE_NAVMESH_TRANS)
{
// Draw mesh
duDebugDrawTriMeshSlope(&m_dd, m_geom->getMesh()->getVerts(), m_geom->getMesh()->getVertCount(),
m_geom->getMesh()->getTris(), m_geom->getMesh()->getNormals(), m_geom->getMesh()->getTriCount(),
m_agentMaxSlope, texScale);
m_geom->drawOffMeshConnections(&m_dd);
}
glDisable(GL_FOG);
glDepthMask(GL_FALSE);
// Draw bounds
const float* bmin = m_geom->getNavMeshBoundsMin();
const float* bmax = m_geom->getNavMeshBoundsMax();
duDebugDrawBoxWire(&m_dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f);
m_dd.begin(DU_DRAW_POINTS, 5.0f);
m_dd.vertex(bmin[0],bmin[1],bmin[2],duRGBA(255,255,255,128));
m_dd.end();
if (m_navMesh && m_navQuery &&
(m_drawMode == DRAWMODE_NAVMESH ||
m_drawMode == DRAWMODE_NAVMESH_TRANS ||
m_drawMode == DRAWMODE_NAVMESH_BVTREE ||
m_drawMode == DRAWMODE_NAVMESH_NODES ||
m_drawMode == DRAWMODE_NAVMESH_INVIS))
{
if (m_drawMode != DRAWMODE_NAVMESH_INVIS)
duDebugDrawNavMeshWithClosedList(&m_dd, *m_navMesh, *m_navQuery, m_navMeshDrawFlags);
if (m_drawMode == DRAWMODE_NAVMESH_BVTREE)
duDebugDrawNavMeshBVTree(&m_dd, *m_navMesh);
if (m_drawMode == DRAWMODE_NAVMESH_NODES)
duDebugDrawNavMeshNodes(&m_dd, *m_navQuery);
duDebugDrawNavMeshPolysWithFlags(&m_dd, *m_navMesh, EDITOR_POLYFLAGS_DISABLED, duRGBA(0,0,0,128));
}
glDepthMask(GL_TRUE);
if (m_chf && m_drawMode == DRAWMODE_COMPACT)
duDebugDrawCompactHeightfieldSolid(&m_dd, *m_chf);
if (m_chf && m_drawMode == DRAWMODE_COMPACT_DISTANCE)
duDebugDrawCompactHeightfieldDistance(&m_dd, *m_chf);
if (m_chf && m_drawMode == DRAWMODE_COMPACT_REGIONS)
duDebugDrawCompactHeightfieldRegions(&m_dd, *m_chf);
if (m_solid && m_drawMode == DRAWMODE_VOXELS)
{
glEnable(GL_FOG);
duDebugDrawHeightfieldSolid(&m_dd, *m_solid);
glDisable(GL_FOG);
}
if (m_solid && m_drawMode == DRAWMODE_VOXELS_WALKABLE)
{
glEnable(GL_FOG);
duDebugDrawHeightfieldWalkable(&m_dd, *m_solid);
glDisable(GL_FOG);
}
if (m_cset && m_drawMode == DRAWMODE_RAW_CONTOURS)
{
glDepthMask(GL_FALSE);
duDebugDrawRawContours(&m_dd, *m_cset);
glDepthMask(GL_TRUE);
}
if (m_cset && m_drawMode == DRAWMODE_BOTH_CONTOURS)
{
glDepthMask(GL_FALSE);
duDebugDrawRawContours(&m_dd, *m_cset, 0.5f);
duDebugDrawContours(&m_dd, *m_cset);
glDepthMask(GL_TRUE);
}
if (m_cset && m_drawMode == DRAWMODE_CONTOURS)
{
glDepthMask(GL_FALSE);
duDebugDrawContours(&m_dd, *m_cset);
glDepthMask(GL_TRUE);
}
if (m_chf && m_cset && m_drawMode == DRAWMODE_REGION_CONNECTIONS)
{
duDebugDrawCompactHeightfieldRegions(&m_dd, *m_chf);
glDepthMask(GL_FALSE);
duDebugDrawRegionConnections(&m_dd, *m_cset);
glDepthMask(GL_TRUE);
}
if (m_pmesh && m_drawMode == DRAWMODE_POLYMESH)
{
glDepthMask(GL_FALSE);
duDebugDrawPolyMesh(&m_dd, *m_pmesh);
glDepthMask(GL_TRUE);
}
if (m_dmesh && m_drawMode == DRAWMODE_POLYMESH_DETAIL)
{
glDepthMask(GL_FALSE);
duDebugDrawPolyMeshDetail(&m_dd, *m_dmesh);
glDepthMask(GL_TRUE);
}
m_geom->drawConvexVolumes(&m_dd);
if (m_tool)
m_tool->handleRender();
renderToolStates();
glDepthMask(GL_TRUE);
Editor_StaticTileMeshCommon::renderTileMeshData();
}
void Editor_SoloMesh::handleRenderOverlay(double* proj, double* model, int* view)
@ -360,7 +154,7 @@ void Editor_SoloMesh::handleMeshChanged(class InputGeom* geom)
bool Editor_SoloMesh::handleBuild()
{
if (!m_geom || !m_geom->getMesh())
if (!m_geom || !m_geom->getMesh() || !m_geom->getChunkyMesh())
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Input mesh is not specified.");
return false;
@ -389,8 +183,8 @@ bool Editor_SoloMesh::handleBuild()
m_cfg.walkableRadius = (int)ceilf(m_agentRadius / m_cfg.cs);
m_cfg.maxEdgeLen = (int)(m_edgeMaxLen / m_cellSize);
m_cfg.maxSimplificationError = m_edgeMaxError;
m_cfg.minRegionArea = (int)rcSqr(m_regionMinSize); // Note: area = size*size
m_cfg.mergeRegionArea = (int)rcSqr(m_regionMergeSize); // Note: area = size*size
m_cfg.minRegionArea = rcSqr(m_regionMinSize); // Note: area = size*size
m_cfg.mergeRegionArea = rcSqr(m_regionMergeSize); // Note: area = size*size
m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly;
m_cfg.detailSampleDist = m_detailSampleDist < 0.9f ? 0 : m_cellSize * m_detailSampleDist;
m_cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError;
@ -460,7 +254,7 @@ bool Editor_SoloMesh::handleBuild()
// Step 3. Filter walkables surfaces.
//
// Once all geoemtry is rasterized, we do initial pass of filtering to
// Once all geometry is rasterized, we do initial pass of filtering to
// remove unwanted overhangs caused by the conservative rasterization
// as well as filter spans where the character cannot possibly stand.
if (m_filterLowHangingObstacles)
@ -639,6 +433,9 @@ bool Editor_SoloMesh::handleBuild()
// The GUI may allow more max points per polygon than Detour can handle.
// Only build the detour navmesh if we do not exceed the limit.
const int traversalTableCount = NavMesh_GetTraversalTableCountForNavMeshType(m_selectedNavMeshType);
if (m_cfg.maxVertsPerPoly <= DT_VERTS_PER_POLYGON)
{
unsigned char* navData = 0;
@ -706,17 +503,17 @@ bool Editor_SoloMesh::handleBuild()
m_navMesh = dtAllocNavMesh();
if (!m_navMesh)
{
dtFree(navData);
rdFree(navData);
m_ctx->log(RC_LOG_ERROR, "Could not create Detour navmesh");
return false;
}
dtStatus status;
status = m_navMesh->init(navData, navDataSize, DT_TILE_FREE_DATA);
status = m_navMesh->init(navData, navDataSize, traversalTableCount, DT_TILE_FREE_DATA);
if (dtStatusFailed(status))
{
dtFree(navData);
rdFree(navData);
m_ctx->log(RC_LOG_ERROR, "Could not init Detour navmesh");
return false;
}
@ -728,6 +525,18 @@ bool Editor_SoloMesh::handleBuild()
return false;
}
}
dtDisjointSet data;
if (!dtCreateDisjointPolyGroups(m_navMesh, data))
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Failed to build disjoint poly groups.");
}
if (!dtCreateTraversalTableData(m_navMesh, data, traversalTableCount))
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Failed to build traversal table data.");
}
m_ctx->stopTimer(RC_TIMER_TOTAL);

View File

@ -17,10 +17,9 @@
//
#include "Pch.h"
#include "Shared/Include/SharedAlloc.h"
#include "Shared/Include/SharedAssert.h"
#include "Recast/Include/Recast.h"
#include "Recast/Include/RecastAlloc.h"
#include "Recast/Include/RecastAssert.h"
#include "Detour/Include/DetourAssert.h"
#include "Detour/Include/DetourNavMesh.h"
#include "Detour/Include/DetourNavMeshBuilder.h"
#include "Detour/Include/DetourCommon.h"
@ -125,13 +124,13 @@ struct LinearAllocator : public dtTileCacheAlloc
~LinearAllocator()
{
dtFree(buffer);
rdFree(buffer);
}
void resize(const size_t cap)
{
if (buffer) dtFree(buffer);
buffer = (unsigned char*)dtAlloc(cap, DT_ALLOC_PERM);
if (buffer) rdFree(buffer);
buffer = (unsigned char*)rdAlloc(cap, RD_ALLOC_PERM);
capacity = cap;
}
@ -241,7 +240,7 @@ struct RasterizationContext
rcFreeCompactHeightfield(chf);
for (int i = 0; i < MAX_LAYERS; ++i)
{
dtFree(tiles[i].data);
rdFree(tiles[i].data);
tiles[i].data = 0;
}
}
@ -279,16 +278,18 @@ int Editor_TempObstacles::rasterizeTileLayers(
rcConfig tcfg;
memcpy(&tcfg, &cfg, sizeof(tcfg));
tcfg.bmin[0] = cfg.bmin[0] + tx*tcs;
tcfg.bmin[1] = cfg.bmin[1];
tcfg.bmin[2] = cfg.bmin[2] + ty*tcs;
tcfg.bmax[0] = cfg.bmin[0] + (tx+1)*tcs;
tcfg.bmax[1] = cfg.bmax[1];
tcfg.bmax[2] = cfg.bmin[2] + (ty+1)*tcs;
tcfg.bmin[0] = cfg.bmax[0] - (tx+1)*tcs;
tcfg.bmin[1] = cfg.bmin[1] + (ty)*tcs;
tcfg.bmin[2] = cfg.bmin[2];
tcfg.bmax[0] = cfg.bmax[0] - (tx)*tcs;
tcfg.bmax[1] = cfg.bmin[1] + (ty+1)*tcs;
tcfg.bmax[2] = cfg.bmax[2];
tcfg.bmin[0] -= tcfg.borderSize*tcfg.cs;
tcfg.bmin[2] -= tcfg.borderSize*tcfg.cs;
tcfg.bmin[1] -= tcfg.borderSize*tcfg.cs;
tcfg.bmax[0] += tcfg.borderSize*tcfg.cs;
tcfg.bmax[2] += tcfg.borderSize*tcfg.cs;
tcfg.bmax[1] += tcfg.borderSize*tcfg.cs;
// Allocate voxel heightfield where we rasterize our input data to.
rc.solid = rcAllocHeightfield();
@ -315,11 +316,12 @@ int Editor_TempObstacles::rasterizeTileLayers(
float tbmin[2], tbmax[2];
tbmin[0] = tcfg.bmin[0];
tbmin[1] = tcfg.bmin[2];
tbmin[1] = tcfg.bmin[1];
tbmax[0] = tcfg.bmax[0];
tbmax[1] = tcfg.bmax[2];
int cid[512];// TODO: Make grow when returning too many items.
const int ncid = rcGetChunksOverlappingRect(chunkyMesh, tbmin, tbmax, cid, 512);
tbmax[1] = tcfg.bmax[1];
#if 0
int cid[1024];// TODO: Make grow when returning too many items.
const int ncid = rcGetChunksOverlappingRect(chunkyMesh, tbmin, tbmax, cid, 1024);
if (!ncid)
{
return 0; // empty
@ -338,6 +340,29 @@ int Editor_TempObstacles::rasterizeTileLayers(
if (!rcRasterizeTriangles(m_ctx, verts, nverts, tris, rc.triareas, ntris, *rc.solid, tcfg.walkableClimb))
return 0;
}
#else
int cid[1024];//NOTE: we don't grow it but we reuse it (e.g. like a yieldable function or iterator or sth)
int currentNode = 0;
bool done = false;
do{
int currentCount = 0;
done=rcGetChunksOverlappingRect(chunkyMesh, tbmin, tbmax, cid, 1024,currentCount,currentNode);
for (int i = 0; i < currentCount; ++i)
{
const rcChunkyTriMeshNode& node = chunkyMesh->nodes[cid[i]];
const int* tris = &chunkyMesh->tris[node.i*3];
const int ntris = node.n;
memset(rc.triareas, 0, ntris * sizeof(unsigned char));
rcMarkWalkableTriangles(m_ctx, tcfg.walkableSlopeAngle,
verts, nverts, tris, ntris, rc.triareas);
if (!rcRasterizeTriangles(m_ctx, verts, nverts, tris, rc.triareas, ntris, *rc.solid, tcfg.walkableClimb))
return 0;
}
} while (!done);
#endif
// Once all geometry is rasterized, we do initial pass of filtering to
// remove unwanted overhangs caused by the conservative rasterization
@ -349,7 +374,9 @@ int Editor_TempObstacles::rasterizeTileLayers(
if (m_filterWalkableLowHeightSpans)
rcFilterWalkableLowHeightSpans(m_ctx, tcfg.walkableHeight, *rc.solid);
// Compact the heightfield so that it is faster to handle from now on.
// This will result more cache coherent data as well as the neighbours
// between walkable cells will be calculated.
rc.chf = rcAllocCompactHeightfield();
if (!rc.chf)
{
@ -386,7 +413,7 @@ int Editor_TempObstacles::rasterizeTileLayers(
}
if (!rcBuildHeightfieldLayers(m_ctx, *rc.chf, tcfg.borderSize, tcfg.walkableHeight, *rc.lset))
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build heighfield layers.");
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build heightfield layers.");
return 0;
}
@ -426,7 +453,7 @@ int Editor_TempObstacles::rasterizeTileLayers(
}
}
// Transfer ownsership of tile data from build context to the caller.
// Transfer ownership of tile data from build context to the caller.
int n = 0;
for (int i = 0; i < rcMin(rc.ntiles, maxTiles); ++i)
{
@ -438,39 +465,6 @@ int Editor_TempObstacles::rasterizeTileLayers(
return n;
}
void drawTiles(duDebugDraw* dd, dtTileCache* tc)
{
unsigned int fcol[6];
float bmin[3], bmax[3];
for (int i = 0; i < tc->getTileCount(); ++i)
{
const dtCompressedTile* tile = tc->getTile(i);
if (!tile->header) continue;
tc->calcTightTileBounds(tile->header, bmin, bmax);
const unsigned int col = duIntToCol(i,64);
duCalcBoxColors(fcol, col, col);
duDebugDrawBox(dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], fcol);
}
for (int i = 0; i < tc->getTileCount(); ++i)
{
const dtCompressedTile* tile = tc->getTile(i);
if (!tile->header) continue;
tc->calcTightTileBounds(tile->header, bmin, bmax);
const unsigned int col = duIntToCol(i,255);
const float pad = tc->getParams()->cs * 0.1f;
duDebugDrawBoxWire(dd, bmin[0]-pad,bmin[1]-pad,bmin[2]-pad,
bmax[0]+pad,bmax[1]+pad,bmax[2]+pad, col, 2.0f);
}
}
enum DrawDetailType
{
DRAWDETAIL_AREAS,
@ -575,8 +569,7 @@ void drawDetailOverlay(const dtTileCache* tc, const int tx, const int ty, double
return;
const int rawSize = calcLayerBufferSize(tc->getParams()->width, tc->getParams()->height);
char text[128];
const int h = view[3];
for (int i = 0; i < ntiles; ++i)
{
@ -584,19 +577,21 @@ void drawDetailOverlay(const dtTileCache* tc, const int tx, const int ty, double
float pos[3];
pos[0] = (tile->header->bmin[0]+tile->header->bmax[0])/2.0f;
pos[1] = tile->header->bmin[1];
pos[2] = (tile->header->bmin[2]+tile->header->bmax[2])/2.0f;
pos[1] = (tile->header->bmin[1]+tile->header->bmax[1])/2.0f;
pos[2] = (tile->header->bmin[2]);
GLdouble x, y, z;
if (gluProject((GLdouble)pos[0], (GLdouble)pos[1], (GLdouble)pos[2],
model, proj, view, &x, &y, &z))
{
snprintf(text,128,"(%d,%d)/%d", tile->header->tx,tile->header->ty,tile->header->tlayer);
imguiDrawText((int)x, (int)y-25, IMGUI_ALIGN_CENTER, text, imguiRGBA(0,0,0,220));
snprintf(text,128,"Compressed: %.1f kB", tile->dataSize/1024.0f);
imguiDrawText((int)x, (int)y-45, IMGUI_ALIGN_CENTER, text, imguiRGBA(0,0,0,128));
snprintf(text,128,"Raw:%.1fkB", rawSize/1024.0f);
imguiDrawText((int)x, (int)y-65, IMGUI_ALIGN_CENTER, text, imguiRGBA(0,0,0,128));
ImGui_RenderText(ImGuiTextAlign_e::kAlignCenter, ImVec2((float)x, h-((float)y-25.f)), ImVec4(0.0f, 0.0f, 0.0f, 0.8f),
"(%d,%d)/%d", tile->header->tx, tile->header->ty, tile->header->tlayer);
ImGui_RenderText(ImGuiTextAlign_e::kAlignCenter, ImVec2((float)x, h-((float)y-45.f)), ImVec4(0.0f, 0.0f, 0.0f, 0.8f),
"Compressed: %.1f kB", tile->dataSize/1024.0f);
ImGui_RenderText(ImGuiTextAlign_e::kAlignCenter, ImVec2((float)x, h-((float)y-65.f)), ImVec4(0.0f, 0.0f, 0.0f, 0.8f),
"Raw: %.1fkB", rawSize/1024.0f);
}
}
}
@ -625,36 +620,13 @@ dtObstacleRef hitTestObstacle(const dtTileCache* tc, const float* sp, const floa
}
return tc->getObstacleRef(obmin);
}
void drawObstacles(duDebugDraw* dd, const dtTileCache* tc)
{
// Draw obstacles
for (int i = 0; i < tc->getObstacleCount(); ++i)
{
const dtTileCacheObstacle* ob = tc->getObstacle(i);
if (ob->state == DT_OBSTACLE_EMPTY) continue;
float bmin[3], bmax[3];
tc->getObstacleBounds(ob, bmin,bmax);
unsigned int col = 0;
if (ob->state == DT_OBSTACLE_PROCESSING)
col = duRGBA(255,255,0,128);
else if (ob->state == DT_OBSTACLE_PROCESSED)
col = duRGBA(255,192,0,192);
else if (ob->state == DT_OBSTACLE_REMOVING)
col = duRGBA(220,0,0,128);
duDebugDrawCylinder(dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], col);
duDebugDrawCylinderWire(dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duDarkenCol(col), 2);
}
}
class TempObstacleHilightTool : public EditorTool
{
Editor_TempObstacles* ;
Editor_TempObstacles* m_editor;
float m_hitPos[3];
bool m_hitPosSet;
int m_drawType;
@ -662,7 +634,7 @@ class TempObstacleHilightTool : public EditorTool
public:
TempObstacleHilightTool() :
(0),
m_editor(0),
m_hitPosSet(false),
m_drawType(DRAWDETAIL_AREAS)
{
@ -677,23 +649,31 @@ public:
virtual void init(Editor* editor)
{
= (Editor_TempObstacles*)editor;
m_editor = (Editor_TempObstacles*)editor;
}
virtual void reset() {}
virtual void handleMenu()
{
imguiLabel("Highlight Tile Cache");
imguiValue("Click LMB to highlight a tile.");
imguiSeparator();
if (imguiCheck("Draw Areas", m_drawType == DRAWDETAIL_AREAS))
ImGui::Text("Highlight Tile Cache");
ImGui::Text("Click LMB to highlight a tile.");
ImGui::Separator();
bool enabled = m_drawType == DRAWDETAIL_AREAS; // todo(amos): use flags instead?
if (ImGui::Checkbox("Draw Areas", &enabled))
m_drawType = DRAWDETAIL_AREAS;
if (imguiCheck("Draw Regions", m_drawType == DRAWDETAIL_REGIONS))
enabled = m_drawType == DRAWDETAIL_REGIONS;
if (ImGui::Checkbox("Draw Regions", &enabled))
m_drawType = DRAWDETAIL_REGIONS;
if (imguiCheck("Draw Contours", m_drawType == DRAWDETAIL_CONTOURS))
enabled = m_drawType == DRAWDETAIL_CONTOURS;
if (ImGui::Checkbox("Draw Contours", &enabled))
m_drawType = DRAWDETAIL_CONTOURS;
if (imguiCheck("Draw Mesh", m_drawType == DRAWDETAIL_MESH))
enabled = m_drawType == DRAWDETAIL_MESH;
if (ImGui::Checkbox("Draw Mesh", &enabled))
m_drawType = DRAWDETAIL_MESH;
}
@ -711,9 +691,9 @@ public:
virtual void handleRender()
{
if (m_hitPosSet && )
if (m_hitPosSet && m_editor)
{
const float s = ->getAgentRadius();
const float s = m_editor->getAgentRadius();
glColor4ub(0,0,0,128);
glLineWidth(2.0f);
glBegin(GL_LINES);
@ -727,8 +707,8 @@ public:
glLineWidth(1.0f);
int tx=0, ty=0;
->getTilePos(m_hitPos, tx, ty);
->renderCachedTile(tx,ty,m_drawType);
m_editor->getTilePos(m_hitPos, tx, ty);
m_editor->renderCachedTile(tx,ty,m_drawType);
}
}
@ -736,11 +716,11 @@ public:
{
if (m_hitPosSet)
{
if ()
if (m_editor)
{
int tx=0, ty=0;
->getTilePos(m_hitPos, tx, ty);
->renderCachedTileOverlay(tx,ty,proj,model,view);
m_editor->getTilePos(m_hitPos, tx, ty);
m_editor->renderCachedTileOverlay(tx,ty,proj,model,view);
}
}
}
@ -749,11 +729,11 @@ public:
class TempObstacleCreateTool : public EditorTool
{
Editor_TempObstacles* ;
Editor_TempObstacles* m_editor;
public:
TempObstacleCreateTool() : (0)
TempObstacleCreateTool() : m_editor(0)
{
}
@ -765,32 +745,32 @@ public:
virtual void init(Editor* editor)
{
= (Editor_TempObstacles*)editor;
m_editor = (Editor_TempObstacles*)editor;
}
virtual void reset() {}
virtual void handleMenu()
{
imguiLabel("Create Temp Obstacles");
ImGui::Text("Create Temp Obstacles");
if (imguiButton("Remove All"))
->clearAllTempObstacles();
if (ImGui::Button("Remove All"))
m_editor->clearAllTempObstacles();
imguiSeparator();
ImGui::Separator();
imguiValue("Click LMB to create an obstacle.");
imguiValue("Shift+LMB to remove an obstacle.");
ImGui::Text("Click LMB to create an obstacle.");
ImGui::Text("Shift+LMB to remove an obstacle.");
}
virtual void handleClick(const float* s, const float* p, bool shift)
{
if ()
if (m_editor)
{
if (shift)
->removeTempObstacle(s,p);
m_editor->removeTempObstacle(s,p);
else
->addTempObstacle(p);
m_editor->addTempObstacle(p);
}
}
@ -805,18 +785,9 @@ public:
Editor_TempObstacles::Editor_TempObstacles() :
m_keepInterResults(false),
m_tileCache(0),
m_cacheBuildTimeMs(0),
m_cacheCompressedSize(0),
m_cacheRawSize(0),
m_cacheLayerCount(0),
m_cacheBuildMemUsage(0),
m_drawMode(DRAWMODE_NAVMESH),
m_maxTiles(0),
m_maxPolysPerTile(0),
m_tileSize(48)
Editor_TempObstacles::Editor_TempObstacles()
: m_maxTiles(0)
, m_maxPolysPerTile(0)
{
resetCommonSettings();
@ -838,246 +809,125 @@ void Editor_TempObstacles::handleSettings()
{
Editor::handleCommonSettings();
if (imguiCheck("Keep Itermediate Results", m_keepInterResults))
m_keepInterResults = !m_keepInterResults;
ImGui::Text("Tiling");
ImGui::SliderInt("Tile Size", &m_tileSize, 8, 1024);
imguiLabel("Tiling");
imguiSlider("TileSize", &m_tileSize, 16.0f, 128.0f, 8.0f);
int gridSize = 1;
if (m_geom)
{
const float* bmin = m_geom->getNavMeshBoundsMin();
const float* bmax = m_geom->getNavMeshBoundsMax();
char text[64];
int gw = 0, gh = 0;
rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh);
const int ts = (int)m_tileSize;
const int tw = (gw + ts-1) / ts;
const int th = (gh + ts-1) / ts;
snprintf(text, 64, "Tiles %d x %d", tw, th);
imguiValue(text);
// Max tiles and max polys affect how the tile IDs are caculated.
// There are 22 bits available for identifying a tile and a polygon.
int tileBits = rcMin((int)dtIlog2(dtNextPow2(tw*th*EXPECTED_LAYERS_PER_TILE)), 14);
if (tileBits > 14) tileBits = 14;
int polyBits = 22 - tileBits;
m_maxTiles = 1 << tileBits;
m_maxPolysPerTile = 1 << polyBits;
snprintf(text, 64, "Max Tiles %d", m_maxTiles);
imguiValue(text);
snprintf(text, 64, "Max Polys %d", m_maxPolysPerTile);
imguiValue(text);
gridSize = tw*th;
}
else
{
m_maxTiles = 0;
m_maxPolysPerTile = 0;
}
imguiSeparator();
imguiLabel("Tile Cache");
char msg[64];
ImGui::Checkbox("Keep Intermediate Results", &m_keepInterResults);
const int gridSize = EditorCommon_SetAndRenderTileProperties(m_geom, m_tileSize, m_cellSize, m_maxTiles, m_maxPolysPerTile);
ImGui::Separator();
ImGui::Text("Tile Cache");
const float compressionRatio = (float)m_cacheCompressedSize / (float)(m_cacheRawSize+1);
snprintf(msg, 64, "Layers %d", m_cacheLayerCount);
imguiValue(msg);
snprintf(msg, 64, "Layers (per tile) %.1f", (float)m_cacheLayerCount/(float)gridSize);
imguiValue(msg);
snprintf(msg, 64, "Memory %.1f kB / %.1f kB (%.1f%%)", m_cacheCompressedSize/1024.0f, m_cacheRawSize/1024.0f, compressionRatio*100.0f);
imguiValue(msg);
snprintf(msg, 64, "Navmesh Build Time %.1f ms", m_cacheBuildTimeMs);
imguiValue(msg);
snprintf(msg, 64, "Build Peak Mem Usage %.1f kB", m_cacheBuildMemUsage/1024.0f);
imguiValue(msg);
ImGui::Text("Layers: %d", m_cacheLayerCount);
ImGui::Text("Layers (per tile): %.1f", (float)m_cacheLayerCount/(float)gridSize);
ImGui::Text("Memory: %.1f kB / %.1f kB (%.1f%%)", m_cacheCompressedSize/1024.0f, m_cacheRawSize/1024.0f, compressionRatio*100.0f);
ImGui::Text("Build Peak Mem Usage: %.1f kB", m_cacheBuildMemUsage/1024.0f);
ImGui::Text("Build Time: %.1fms", m_cacheBuildTimeMs);
imguiSeparator();
ImGui::Separator();
imguiIndent();
imguiIndent();
ImGui::Indent();
ImGui::Indent();
if (imguiButton("Save"))
{
saveAll("all_tiles_tilecache.bin");
}
if (imguiButton("Load"))
if (ImGui::Button("Load", ImVec2(123, 0)))
{
dtFreeNavMesh(m_navMesh);
dtFreeTileCache(m_tileCache);
loadAll("all_tiles_tilecache.bin");
m_navMesh = Editor::loadAll(m_modelName.c_str());
m_navQuery->init(m_navMesh, 2048);
m_loadedNavMeshType = m_selectedNavMeshType;
initToolStates(this);
}
imguiUnindent();
imguiUnindent();
imguiSeparator();
if (ImGui::Button("Save", ImVec2(123, 0)))
{
Editor::saveAll(m_modelName.c_str(), m_navMesh);
}
ImGui::Unindent();
ImGui::Unindent();
if (m_navMesh)
{
const dtNavMeshParams& params = m_navMesh->m_params;
const float* origin = m_navMesh->m_orig;
ImGui::Text("Mesh Origin: \n\tX: %g \n\tY: %g \n\tZ: %g", origin[0], origin[1], origin[2]);
ImGui::Text("Tile Dimensions: %g x %g", params.tileWidth, params.tileHeight);
ImGui::Text("Poly Group Count: %d", params.polyGroupCount);
ImGui::Text("Traversal Table Size: %d", params.traversalTableSize);
ImGui::Text("Traversal Table Count: %d", params.traversalTableCount);
ImGui::Text("Max Tiles: %d", params.maxTiles);
ImGui::Text("Max Polys: %d", params.maxPolys);
ImGui::Separator();
}
else
ImGui::Separator();
}
void Editor_TempObstacles::handleTools()
{
int type = !m_tool ? TOOL_NONE : m_tool->type();
bool enabled = type == TOOL_NAVMESH_TESTER;
if (imguiCheck("Test Navmesh", type == TOOL_NAVMESH_TESTER))
if (ImGui::Checkbox("Test NavMesh", &enabled))
{
setTool(new NavMeshTesterTool);
}
if (imguiCheck("Highlight Tile Cache", type == TOOL_TILE_HIGHLIGHT))
enabled = type == TOOL_TILE_HIGHLIGHT;
if (ImGui::Checkbox("Highlight Tile Cache", &enabled))
{
setTool(new TempObstacleHilightTool);
}
if (imguiCheck("Create Temp Obstacles", type == TOOL_TEMP_OBSTACLE))
enabled = type == TOOL_TEMP_OBSTACLE;
if (ImGui::Checkbox("Create Temp Obstacles", &enabled))
{
setTool(new TempObstacleCreateTool);
}
if (imguiCheck("Create Off-Mesh Links", type == TOOL_OFFMESH_CONNECTION))
enabled = type == TOOL_OFFMESH_CONNECTION;
if (ImGui::Checkbox("Create Off-Mesh Links", &enabled))
{
setTool(new OffMeshConnectionTool);
}
if (imguiCheck("Create Convex Volumes", type == TOOL_CONVEX_VOLUME))
enabled = type == TOOL_CONVEX_VOLUME;
if (ImGui::Checkbox("Create Convex Volumes", &enabled))
{
setTool(new ConvexVolumeTool);
}
if (imguiCheck("Create Crowds", type == TOOL_CROWD))
enabled = type == TOOL_CROWD;
if (ImGui::Checkbox("Create Crowds", &enabled))
{
setTool(new CrowdTool);
}
imguiSeparatorLine();
ImGui::Separator();
imguiIndent();
ImGui::Indent();
if (m_tool)
m_tool->handleMenu();
imguiUnindent();
ImGui::Unindent();
}
void Editor_TempObstacles::handleDebugMode()
{
// Check which modes are valid.
bool valid[MAX_DRAWMODE];
for (int i = 0; i < MAX_DRAWMODE; ++i)
valid[i] = false;
if (m_geom)
{
valid[DRAWMODE_NAVMESH] = m_navMesh != 0;
valid[DRAWMODE_NAVMESH_TRANS] = m_navMesh != 0;
valid[DRAWMODE_NAVMESH_BVTREE] = m_navMesh != 0;
valid[DRAWMODE_NAVMESH_NODES] = m_navQuery != 0;
valid[DRAWMODE_NAVMESH_PORTALS] = m_navMesh != 0;
valid[DRAWMODE_NAVMESH_INVIS] = m_navMesh != 0;
valid[DRAWMODE_MESH] = true;
valid[DRAWMODE_CACHE_BOUNDS] = true;
}
int unavail = 0;
for (int i = 0; i < MAX_DRAWMODE; ++i)
if (!valid[i]) unavail++;
if (unavail == MAX_DRAWMODE)
return;
imguiLabel("Draw");
if (imguiCheck("Input Mesh", m_drawMode == DRAWMODE_MESH, valid[DRAWMODE_MESH]))
m_drawMode = DRAWMODE_MESH;
if (imguiCheck("Navmesh", m_drawMode == DRAWMODE_NAVMESH, valid[DRAWMODE_NAVMESH]))
m_drawMode = DRAWMODE_NAVMESH;
if (imguiCheck("Navmesh Invis", m_drawMode == DRAWMODE_NAVMESH_INVIS, valid[DRAWMODE_NAVMESH_INVIS]))
m_drawMode = DRAWMODE_NAVMESH_INVIS;
if (imguiCheck("Navmesh Trans", m_drawMode == DRAWMODE_NAVMESH_TRANS, valid[DRAWMODE_NAVMESH_TRANS]))
m_drawMode = DRAWMODE_NAVMESH_TRANS;
if (imguiCheck("Navmesh BVTree", m_drawMode == DRAWMODE_NAVMESH_BVTREE, valid[DRAWMODE_NAVMESH_BVTREE]))
m_drawMode = DRAWMODE_NAVMESH_BVTREE;
if (imguiCheck("Navmesh Nodes", m_drawMode == DRAWMODE_NAVMESH_NODES, valid[DRAWMODE_NAVMESH_NODES]))
m_drawMode = DRAWMODE_NAVMESH_NODES;
if (imguiCheck("Navmesh Portals", m_drawMode == DRAWMODE_NAVMESH_PORTALS, valid[DRAWMODE_NAVMESH_PORTALS]))
m_drawMode = DRAWMODE_NAVMESH_PORTALS;
if (imguiCheck("Cache Bounds", m_drawMode == DRAWMODE_CACHE_BOUNDS, valid[DRAWMODE_CACHE_BOUNDS]))
m_drawMode = DRAWMODE_CACHE_BOUNDS;
if (unavail)
{
imguiValue("Tick 'Keep Itermediate Results'");
imguiValue("rebuild some tiles to see");
imguiValue("more debug mode options.");
}
Editor::renderNavMeshDebugMenu();
ImGui::Separator();
Editor_DynamicTileMeshCommon::renderTileMeshRenderOptions();
}
void Editor_TempObstacles::handleRender()
{
if (!m_geom || !m_geom->getMesh())
return;
const float texScale = 1.0f / (m_cellSize * 10.0f);
// Draw mesh
if (m_drawMode != DRAWMODE_NAVMESH_TRANS)
{
// Draw mesh
duDebugDrawTriMeshSlope(&m_dd, m_geom->getMesh()->getVerts(), m_geom->getMesh()->getVertCount(),
m_geom->getMesh()->getTris(), m_geom->getMesh()->getNormals(), m_geom->getMesh()->getTriCount(),
m_agentMaxSlope, texScale);
m_geom->drawOffMeshConnections(&m_dd);
}
if (m_tileCache && m_drawMode == DRAWMODE_CACHE_BOUNDS)
drawTiles(&m_dd, m_tileCache);
if (m_tileCache)
drawObstacles(&m_dd, m_tileCache);
glDepthMask(GL_FALSE);
// Draw bounds
const float* bmin = m_geom->getNavMeshBoundsMin();
const float* bmax = m_geom->getNavMeshBoundsMax();
duDebugDrawBoxWire(&m_dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f);
// Tiling grid.
int gw = 0, gh = 0;
rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh);
const int tw = (gw + (int)m_tileSize-1) / (int)m_tileSize;
const int th = (gh + (int)m_tileSize-1) / (int)m_tileSize;
const float s = m_tileSize*m_cellSize;
duDebugDrawGridXZ(&m_dd, bmin[0],bmin[1],bmin[2], tw,th, s, duRGBA(0,0,0,64), 1.0f);
if (m_navMesh && m_navQuery &&
(m_drawMode == DRAWMODE_NAVMESH ||
m_drawMode == DRAWMODE_NAVMESH_TRANS ||
m_drawMode == DRAWMODE_NAVMESH_BVTREE ||
m_drawMode == DRAWMODE_NAVMESH_NODES ||
m_drawMode == DRAWMODE_NAVMESH_PORTALS ||
m_drawMode == DRAWMODE_NAVMESH_INVIS))
{
if (m_drawMode != DRAWMODE_NAVMESH_INVIS)
duDebugDrawNavMeshWithClosedList(&m_dd, *m_navMesh, *m_navQuery, m_navMeshDrawFlags/*|DU_DRAWNAVMESH_COLOR_TILES*/);
if (m_drawMode == DRAWMODE_NAVMESH_BVTREE)
duDebugDrawNavMeshBVTree(&m_dd, *m_navMesh);
if (m_drawMode == DRAWMODE_NAVMESH_PORTALS)
duDebugDrawNavMeshPortals(&m_dd, *m_navMesh);
if (m_drawMode == DRAWMODE_NAVMESH_NODES)
duDebugDrawNavMeshNodes(&m_dd, *m_navQuery);
duDebugDrawNavMeshPolysWithFlags(&m_dd, *m_navMesh, EDITOR_POLYFLAGS_DISABLED, duRGBA(0,0,0,128));
}
glDepthMask(GL_TRUE);
m_geom->drawConvexVolumes(&m_dd);
if (m_tool)
m_tool->handleRender();
renderToolStates();
glDepthMask(GL_TRUE);
Editor_DynamicTileMeshCommon::renderTileMeshData();
}
void Editor_TempObstacles::renderCachedTile(const int tx, const int ty, const int type)
@ -1148,7 +998,7 @@ void Editor_TempObstacles::addTempObstacle(const float* pos)
return;
float p[3];
dtVcopy(p, pos);
p[1] -= 0.5f;
p[2] -= 0.5f;
m_tileCache->addObstacle(p, 1.0f, 2.0f, 0);
}
@ -1204,10 +1054,10 @@ bool Editor_TempObstacles::handleBuild()
cfg.walkableRadius = (int)ceilf(m_agentRadius / cfg.cs);
cfg.maxEdgeLen = (int)(m_edgeMaxLen / m_cellSize);
cfg.maxSimplificationError = m_edgeMaxError;
cfg.minRegionArea = (int)rcSqr(m_regionMinSize); // Note: area = size*size
cfg.mergeRegionArea = (int)rcSqr(m_regionMergeSize); // Note: area = size*size
cfg.maxVertsPerPoly = (int)m_vertsPerPoly;
cfg.tileSize = (int)m_tileSize;
cfg.minRegionArea = rcSqr(m_regionMinSize); // Note: area = size*size
cfg.mergeRegionArea = rcSqr(m_regionMergeSize); // Note: area = size*size
cfg.maxVertsPerPoly = m_vertsPerPoly;
cfg.tileSize = m_tileSize;
cfg.borderSize = cfg.walkableRadius + 3; // Reserve enough padding.
cfg.width = cfg.tileSize + cfg.borderSize*2;
cfg.height = cfg.tileSize + cfg.borderSize*2;
@ -1222,8 +1072,8 @@ bool Editor_TempObstacles::handleBuild()
rcVcopy(tcparams.orig, bmin);
tcparams.cs = m_cellSize;
tcparams.ch = m_cellHeight;
tcparams.width = (int)m_tileSize;
tcparams.height = (int)m_tileSize;
tcparams.width = m_tileSize;
tcparams.height = m_tileSize;
tcparams.walkableHeight = m_agentHeight;
tcparams.walkableRadius = m_agentRadius;
tcparams.walkableClimb = m_agentMaxClimb;
@ -1266,7 +1116,7 @@ bool Editor_TempObstacles::handleBuild()
status = m_navMesh->init(&params);
if (dtStatusFailed(status))
{
m_ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not init navmesh.");
m_ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not init Detour navmesh.");
return false;
}
@ -1300,7 +1150,7 @@ bool Editor_TempObstacles::handleBuild()
status = m_tileCache->addTile(tile->data, tile->dataSize, DT_COMPRESSEDTILE_FREE_DATA, 0);
if (dtStatusFailed(status))
{
dtFree(tile->data);
rdFree(tile->data);
tile->data = 0;
continue;
}
@ -1361,7 +1211,7 @@ void Editor_TempObstacles::getTilePos(const float* pos, int& tx, int& ty)
const float ts = m_tileSize*m_cellSize;
tx = (int)((pos[0] - bmin[0]) / ts);
ty = (int)((pos[2] - bmin[2]) / ts);
ty = (int)((pos[1] - bmin[1]) / ts);
}
static const int TILECACHESET_MAGIC = 'T'<<24 | 'S'<<16 | 'E'<<8 | 'T'; //'TSET';
@ -1487,14 +1337,14 @@ void Editor_TempObstacles::loadAll(const char* path)
if (!tileHeader.tileRef || !tileHeader.dataSize)
break;
unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM);
unsigned char* data = (unsigned char*)rdAlloc(tileHeader.dataSize, RD_ALLOC_PERM);
if (!data) break;
memset(data, 0, tileHeader.dataSize);
size_t tileDataReadReturnCode = fread(data, tileHeader.dataSize, 1, fp);
if( tileDataReadReturnCode != 1)
{
// Error or early EOF
dtFree(data);
rdFree(data);
fclose(fp);
return;
}
@ -1503,7 +1353,7 @@ void Editor_TempObstacles::loadAll(const char* path)
dtStatus addTileStatus = m_tileCache->addTile(data, tileHeader.dataSize, DT_COMPRESSEDTILE_FREE_DATA, &tile);
if (dtStatusFailed(addTileStatus))
{
dtFree(data);
rdFree(data);
}
if (tile)

View File

@ -37,30 +37,6 @@
#include "game/server/ai_hull.h"
inline unsigned int nextPow2(unsigned int v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
inline unsigned int ilog2(unsigned int v)
{
unsigned int r;
unsigned int shift;
r = (v > 0xffff) << 4; v >>= r;
shift = (v > 0xff) << 3; v >>= shift; r |= shift;
shift = (v > 0xf) << 2; v >>= shift; r |= shift;
shift = (v > 0x3) << 1; v >>= shift; r |= shift;
r |= (v >> 1);
return r;
}
class NavMeshTileTool : public EditorTool
{
Editor_TileMesh* m_editor;
@ -165,20 +141,10 @@ public:
Editor_TileMesh::Editor_TileMesh() :
m_keepInterResults(false),
m_buildAll(true),
m_totalBuildTimeMs(0),
m_triareas(0),
m_solid(0),
m_chf(0),
m_cset(0),
m_pmesh(0),
m_dmesh(0),
m_tileMeshDrawFlags(TM_DRAWFLAGS_INPUT_MESH|TM_DRAWFLAGS_NAVMESH),
m_maxTiles(0),
m_maxPolysPerTile(0),
m_tileSize(32),
m_tileCol(duRGBA(0,0,0,32)),
m_tileBuildTime(0),
m_tileMemUsage(0),
m_tileTriCount(0)
@ -190,6 +156,8 @@ Editor_TileMesh::Editor_TileMesh() :
memset(m_lastBuiltTileBmax, 0, sizeof(m_lastBuiltTileBmax));
setTool(new NavMeshTileTool);
m_drawActiveTile = true;
}
Editor_TileMesh::~Editor_TileMesh()
@ -199,56 +167,8 @@ Editor_TileMesh::~Editor_TileMesh()
m_navMesh = 0;
}
void Editor_TileMesh::cleanup()
{
delete [] m_triareas;
m_triareas = 0;
rcFreeHeightField(m_solid);
m_solid = 0;
rcFreeCompactHeightfield(m_chf);
m_chf = 0;
rcFreeContourSet(m_cset);
m_cset = 0;
rcFreePolyMesh(m_pmesh);
m_pmesh = 0;
rcFreePolyMeshDetail(m_dmesh);
m_dmesh = 0;
}
const hulldef hulls[NAVMESH_COUNT] = {
{ g_navMeshNames[NAVMESH_SMALL] , NAI_Hull::Width(HULL_HUMAN) , NAI_Hull::Height(HULL_HUMAN) * NAI_Hull::Scale(HULL_HUMAN) , 45, 32 },
{ g_navMeshNames[NAVMESH_MED_SHORT] , NAI_Hull::Width(HULL_PROWLER), NAI_Hull::Height(HULL_PROWLER) * NAI_Hull::Scale(HULL_PROWLER), 50, 32 },
{ g_navMeshNames[NAVMESH_MEDIUM] , NAI_Hull::Width(HULL_MEDIUM) , NAI_Hull::Height(HULL_MEDIUM) * NAI_Hull::Scale(HULL_MEDIUM) , 55, 32 },
{ g_navMeshNames[NAVMESH_LARGE] , NAI_Hull::Width(HULL_TITAN) , NAI_Hull::Height(HULL_TITAN) * NAI_Hull::Scale(HULL_TITAN) , 60, 64 },
{ g_navMeshNames[NAVMESH_EXTRA_LARGE], NAI_Hull::Width(HULL_GOLIATH), NAI_Hull::Height(HULL_GOLIATH) * NAI_Hull::Scale(HULL_GOLIATH), 65, 64 },
};
void Editor_TileMesh::selectNavMeshType(const NavMeshType_e navMeshType)
{
const hulldef& h = hulls[navMeshType];
m_agentRadius = h.radius;
m_agentMaxClimb = h.climbHeight;
m_agentHeight = h.height;
m_navmeshName = h.name;
m_tileSize = h.tileSize;
m_selectedNavMeshType = navMeshType;
}
void Editor_TileMesh::handleSettings()
{
ImGui::Text("NavMesh Type");
for (int i = 0; i < NAVMESH_COUNT; i++)
{
const NavMeshType_e navMeshType = NavMeshType_e(i);
if (ImGui::Button(NavMesh_GetNameForType(navMeshType), ImVec2(120, 0)))
{
selectNavMeshType(navMeshType);
}
}
ImGui::Separator();
Editor::handleCommonSettings();
ImGui::Text("Tiling");
@ -257,80 +177,10 @@ void Editor_TileMesh::handleSettings()
ImGui::Checkbox("Build All Tiles", &m_buildAll);
ImGui::Checkbox("Keep Intermediate Results", &m_keepInterResults);
if (m_geom)
{
char text[128];
int gw = 0, gh = 0;
const float* bmin = m_geom->getNavMeshBoundsMin();
const float* bmax = m_geom->getNavMeshBoundsMax();
rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh);
const int ts = m_tileSize;
const int tw = (gw + ts-1) / ts;
const int th = (gh + ts-1) / ts;
snprintf(text, sizeof(text), "Tiles: %d x %d", tw, th);
ImGui::Text(text);
snprintf(text, sizeof(text), "Tile Sizes: %g x %g (%g)", tw*m_cellSize, th*m_cellSize, m_tileSize*m_cellSize);
ImGui::Text(text);
// Max tiles and max polys affect how the tile IDs are calculated.
// There are 28 bits available for identifying a tile and a polygon.
int tileBits = rcMin((int)ilog2(nextPow2(tw*th)), 16);
int polyBits = 28 - tileBits;
m_maxTiles = 1 << tileBits;
m_maxPolysPerTile = 1 << polyBits;
snprintf(text, sizeof(text), "Max Tiles: %d", m_maxTiles);
ImGui::Text(text);
snprintf(text, sizeof(text), "Max Polys: %d", m_maxPolysPerTile);
ImGui::Text(text);
}
else
{
m_maxTiles = 0;
m_maxPolysPerTile = 0;
}
EditorCommon_SetAndRenderTileProperties(m_geom, m_tileSize, m_cellSize, m_maxTiles, m_maxPolysPerTile);
ImGui::Separator();
ImGui::Indent();
ImGui::Indent();
if (ImGui::Button("Load", ImVec2(123, 0)))
{
dtFreeNavMesh(m_navMesh);
m_navMesh = Editor::loadAll(m_modelName.c_str());
m_navQuery->init(m_navMesh, 2048);
m_loadedNavMeshType = m_selectedNavMeshType;
initToolStates(this);
}
if (ImGui::Button("Save", ImVec2(123, 0)))
{
Editor::saveAll(m_modelName.c_str(), m_navMesh);
}
ImGui::Unindent();
ImGui::Unindent();
ImGui::Text("Build Time: %.1fms", m_totalBuildTimeMs);
if (m_navMesh)
{
const dtNavMeshParams& params = m_navMesh->m_params;
const float* origin = m_navMesh->m_orig;
ImGui::Text("Mesh Origin: \n\tX: %g \n\tY: %g \n\tZ: %g", origin[0], origin[1], origin[2]);
ImGui::Text("Tile Dimensions: %g x %g", params.tileWidth, params.tileHeight);
ImGui::Text("Poly Group Count: %d", params.polyGroupCount);
ImGui::Text("Traversal Table Size: %d", params.traversalTableSize);
ImGui::Text("Traversal Table Count: %d", params.traversalTableCount);
ImGui::Text("Max Tiles: %d", params.maxTiles);
ImGui::Text("Max Polys: %d", params.maxPolys);
ImGui::Separator();
}
else
ImGui::Separator();
Editor_StaticTileMeshCommon::renderIntermediateTileMeshOptions();
}
void Editor_TileMesh::handleTools()
@ -385,313 +235,14 @@ void Editor_TileMesh::handleTools()
void Editor_TileMesh::handleDebugMode()
{
ImGui::Text("NavMesh Render Options");
bool isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_OFFMESHCONS);
if (ImGui::Checkbox("Off-Mesh Connections", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_OFFMESHCONS);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_NODES);
if (ImGui::Checkbox("Query Nodes", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_NODES);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_BVTREE);
if (ImGui::Checkbox("BVTree", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_BVTREE);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_PORTALS);
if (ImGui::Checkbox("Portals", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_PORTALS);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_CLOSEDLIST);
if (ImGui::Checkbox("Closed List", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_CLOSEDLIST);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_COLOR_TILES);
if (ImGui::Checkbox("Tile ID Colors", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_COLOR_TILES);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_VERTS);
if (ImGui::Checkbox("Vertex Points", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_VERTS);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_INNERBOUND);
if (ImGui::Checkbox("Inner Poly Boundaries", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_INNERBOUND);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_OUTERBOUND);
if (ImGui::Checkbox("Outer Poly Boundaries", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_OUTERBOUND);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_POLYCENTERS);
if (ImGui::Checkbox("Poly Centers", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_POLYCENTERS);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_POLYGROUPS);
if (ImGui::Checkbox("Poly Group Colors", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_POLYGROUPS);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_DEPTH_MASK);
if (ImGui::Checkbox("Depth Mask", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_DEPTH_MASK);
isEnabled = (getNavMeshDrawFlags() & DU_DRAWNAVMESH_ALPHA);
if (ImGui::Checkbox("Transparency", &isEnabled))
toggleNavMeshDrawFlag(DU_DRAWNAVMESH_ALPHA);
Editor::renderNavMeshDebugMenu();
ImGui::Separator();
ImGui::Text("TileMesh Render Options");
isEnabled = m_tileMeshDrawFlags & TM_DRAWFLAGS_INPUT_MESH;
// This should always be available, since if we load a large mesh we want to
// be able to toggle this off to save on performance. The renderer has to be
// moved to its own thread to solve this issue.
if (ImGui::Checkbox("Input Mesh", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_INPUT_MESH);
// Check which modes are valid.
const bool hasNavMesh = m_navMesh != 0;
const bool hasChf = m_chf != 0;
const bool hasCset = m_cset != 0;
const bool hasSolid = m_solid != 0;
const bool hasDMesh = m_dmesh != 0;
const bool intermediateDataUnavailable = !hasChf||!hasCset||!hasSolid||!hasDMesh;
isEnabled = m_tileMeshDrawFlags & TM_DRAWFLAGS_NAVMESH;
ImGui::BeginDisabled(!hasNavMesh);
if (ImGui::Checkbox("NavMesh", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_NAVMESH);
ImGui::EndDisabled();
isEnabled = m_tileMeshDrawFlags & TM_DRAWFLAGS_VOXELS;
ImGui::BeginDisabled(!hasSolid);
if (ImGui::Checkbox("Voxels", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_VOXELS);
ImGui::EndDisabled();
isEnabled = m_tileMeshDrawFlags & TM_DRAWFLAGS_VOXELS_WALKABLE;
ImGui::BeginDisabled(!hasSolid);
if (ImGui::Checkbox("Walkable Voxels", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_VOXELS_WALKABLE);
ImGui::EndDisabled();
isEnabled = m_tileMeshDrawFlags & TM_DRAWFLAGS_COMPACT;
ImGui::BeginDisabled(!hasChf);
if (ImGui::Checkbox("Compact", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_COMPACT);
ImGui::EndDisabled();
isEnabled = m_tileMeshDrawFlags & TM_DRAWFLAGS_COMPACT_DISTANCE;
ImGui::BeginDisabled(!hasChf);
if (ImGui::Checkbox("Compact Distance", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_COMPACT_DISTANCE);
ImGui::EndDisabled();
isEnabled = m_tileMeshDrawFlags & TM_DRAWFLAGS_COMPACT_REGIONS;
ImGui::BeginDisabled(!hasChf);
if (ImGui::Checkbox("Compact Regions", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_COMPACT_REGIONS);
ImGui::EndDisabled();
isEnabled = m_tileMeshDrawFlags & TM_DRAWFLAGS_REGION_CONNECTIONS;
ImGui::BeginDisabled(!hasCset);
if (ImGui::Checkbox("Region Connections", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_REGION_CONNECTIONS);
ImGui::EndDisabled();
isEnabled = m_tileMeshDrawFlags & TM_DRAWFLAGS_RAW_CONTOURS;
ImGui::BeginDisabled(!hasCset);
if (ImGui::Checkbox("Raw Contours", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_RAW_CONTOURS);
ImGui::EndDisabled();
isEnabled = m_tileMeshDrawFlags & TM_DRAWFLAGS_CONTOURS;
ImGui::BeginDisabled(!hasCset);
if (ImGui::Checkbox("Contours", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_CONTOURS);
ImGui::EndDisabled();
isEnabled = m_tileMeshDrawFlags & TM_DRAWFLAGS_POLYMESH;
ImGui::BeginDisabled(!hasDMesh);
if (ImGui::Checkbox("Poly Mesh", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_POLYMESH);
ImGui::EndDisabled();
isEnabled = m_tileMeshDrawFlags & TM_DRAWFLAGS_POLYMESH_DETAIL;
ImGui::BeginDisabled(!hasDMesh);
if (ImGui::Checkbox("Poly Mesh Detail", &isEnabled))
toggleTileMeshDrawFlag(TM_DRAWFLAGS_POLYMESH_DETAIL);
ImGui::EndDisabled();
if (intermediateDataUnavailable)
{
ImGui::Separator();
ImGui::Text("Tick 'Keep Intermediate Results'");
ImGui::Text("rebuild some tiles to see");
ImGui::Text("more debug mode options.");
}
Editor_StaticTileMeshCommon::renderTileMeshRenderOptions();
}
void Editor_TileMesh::handleRender()
{
if (!m_geom || !m_geom->getMesh())
return;
const float texScale = 1.0f / (m_cellSize * 10.0f);
// Draw input mesh
if (m_tileMeshDrawFlags & TM_DRAWFLAGS_INPUT_MESH)
{
// Draw mesh
duDebugDrawTriMeshSlope(&m_dd, m_geom->getMesh()->getVerts(), m_geom->getMesh()->getVertCount(),
m_geom->getMesh()->getTris(), m_geom->getMesh()->getNormals(), m_geom->getMesh()->getTriCount(),
m_agentMaxSlope, texScale);
}
glDepthMask(GL_FALSE);
// Draw bounds
const float* bmin = m_geom->getNavMeshBoundsMin();
const float* bmax = m_geom->getNavMeshBoundsMax();
duDebugDrawBoxWire(&m_dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f);
// Tiling grid.
int gw = 0, gh = 0;
rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh);
const int tw = (gw + m_tileSize-1) / m_tileSize;
const int th = (gh + m_tileSize-1) / m_tileSize;
const float s = m_tileSize*m_cellSize;
duDebugDrawGridXY(&m_dd, bmax[0],bmin[1],bmin[2], tw,th, s, duRGBA(0,0,0,64), 1.0f);
// Draw active tile
duDebugDrawBoxWire(&m_dd, m_lastBuiltTileBmin[0],m_lastBuiltTileBmin[1],m_lastBuiltTileBmin[2],
m_lastBuiltTileBmax[0],m_lastBuiltTileBmax[1],m_lastBuiltTileBmax[2], m_tileCol, 1.0f);
if (m_navMesh && m_navQuery)
{
if (m_tileMeshDrawFlags & TM_DRAWFLAGS_NAVMESH)
{
duDebugDrawNavMeshWithClosedList(&m_dd, *m_navMesh, *m_navQuery, m_navMeshDrawFlags);
duDebugDrawNavMeshPolysWithFlags(&m_dd, *m_navMesh, EDITOR_POLYFLAGS_DISABLED, duRGBA(0, 0, 0, 128));
}
}
glDepthMask(GL_TRUE);
if (m_chf)
{
if (m_tileMeshDrawFlags & TM_DRAWFLAGS_COMPACT)
duDebugDrawCompactHeightfieldSolid(&m_dd, *m_chf);
if (m_tileMeshDrawFlags & TM_DRAWFLAGS_COMPACT_DISTANCE)
duDebugDrawCompactHeightfieldDistance(&m_dd, *m_chf);
if (m_tileMeshDrawFlags & TM_DRAWFLAGS_COMPACT_REGIONS)
duDebugDrawCompactHeightfieldRegions(&m_dd, *m_chf);
}
if (m_solid)
{
if (m_tileMeshDrawFlags & TM_DRAWFLAGS_VOXELS)
{
glEnable(GL_FOG);
duDebugDrawHeightfieldSolid(&m_dd, *m_solid);
glDisable(GL_FOG);
}
if (m_tileMeshDrawFlags & TM_DRAWFLAGS_VOXELS_WALKABLE)
{
glEnable(GL_FOG);
duDebugDrawHeightfieldWalkable(&m_dd, *m_solid);
glDisable(GL_FOG);
}
}
if (m_cset)
{
if (m_tileMeshDrawFlags & TM_DRAWFLAGS_RAW_CONTOURS)
{
glDepthMask(GL_FALSE);
duDebugDrawRawContours(&m_dd, *m_cset);
glDepthMask(GL_TRUE);
}
if (m_tileMeshDrawFlags & TM_DRAWFLAGS_CONTOURS)
{
glDepthMask(GL_FALSE);
duDebugDrawContours(&m_dd, *m_cset);
glDepthMask(GL_TRUE);
}
}
if ((m_chf && m_cset) && (m_tileMeshDrawFlags & TM_DRAWFLAGS_REGION_CONNECTIONS))
{
duDebugDrawCompactHeightfieldRegions(&m_dd, *m_chf);
glDepthMask(GL_FALSE);
duDebugDrawRegionConnections(&m_dd, *m_cset);
glDepthMask(GL_TRUE);
}
if (m_pmesh && (m_tileMeshDrawFlags & TM_DRAWFLAGS_POLYMESH))
{
glDepthMask(GL_FALSE);
duDebugDrawPolyMesh(&m_dd, *m_pmesh);
glDepthMask(GL_TRUE);
}
if (m_dmesh && (m_tileMeshDrawFlags & TM_DRAWFLAGS_POLYMESH_DETAIL))
{
glDepthMask(GL_FALSE);
duDebugDrawPolyMeshDetail(&m_dd, *m_dmesh);
glDepthMask(GL_TRUE);
}
// TODO: also add flags for this
m_geom->drawConvexVolumes(&m_dd);
m_geom->drawOffMeshConnections(&m_dd);
if (m_tool)
m_tool->handleRender();
renderToolStates();
glDepthMask(GL_TRUE);
Editor_StaticTileMeshCommon::renderTileMeshData();
}
void Editor_TileMesh::handleRenderOverlay(double* proj, double* model, int* view)

View File

@ -120,6 +120,7 @@ protected:
bool m_filterLedgeSpans;
bool m_filterWalkableLowHeightSpans;
int m_tileSize;
float m_cellSize;
float m_cellHeight;
float m_agentHeight;
@ -198,6 +199,11 @@ public:
void renderToolStates();
void renderOverlayToolStates(double* proj, double* model, int* view);
void renderNavMeshDebugMenu();
void renderIntermediateTileMeshOptions();
void selectNavMeshType(const NavMeshType_e navMeshType);
void resetCommonSettings();
void handleCommonSettings();

View File

@ -0,0 +1,113 @@
//
// 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 RECASTEDITORCOMMON_H
#define RECASTEDITORCOMMON_H
#include "Recast/Include/Recast.h"
#include "NavEditor/Include/ChunkyTriMesh.h"
enum DrawTileMeshFlags
{
TM_DRAWFLAGS_INPUT_MESH = (1<<0),
TM_DRAWFLAGS_NAVMESH = (1<<1),
TM_DRAWFLAGS_VOXELS = (1<<2),
TM_DRAWFLAGS_VOXELS_WALKABLE = (1<<3),
TM_DRAWFLAGS_COMPACT = (1<<4),
TM_DRAWFLAGS_COMPACT_DISTANCE = (1<<5),
TM_DRAWFLAGS_COMPACT_REGIONS = (1<<6),
TM_DRAWFLAGS_REGION_CONNECTIONS = (1<<7),
TM_DRAWFLAGS_RAW_CONTOURS = (1<<8),
TM_DRAWFLAGS_CONTOURS = (1<<9),
TM_DRAWFLAGS_POLYMESH = (1<<10),
TM_DRAWFLAGS_POLYMESH_DETAIL = (1<<11),
TM_DRAWFLAGS_TILE_CACHE_BOUNDS = (1<<12),
TM_DRAWFLAGS_TILE_CACHE_OBSTACLES = (1<<13),
};
class Editor_StaticTileMeshCommon : public Editor
{
public:
Editor_StaticTileMeshCommon();
void cleanup();
void renderTileMeshRenderOptions();
void renderTileMeshData();
void renderIntermediateTileMeshOptions();
inline unsigned int getTileMeshDrawFlags() const { return m_tileMeshDrawFlags; }
inline void setTileMeshDrawFlags(unsigned int flags) { m_tileMeshDrawFlags = flags; }
inline void toggleTileMeshDrawFlag(unsigned int flag) { m_tileMeshDrawFlags ^= flag; }
protected:
unsigned char* m_triareas;
rcHeightfield* m_solid;
rcCompactHeightfield* m_chf;
rcContourSet* m_cset;
rcPolyMesh* m_pmesh;
rcPolyMeshDetail* m_dmesh;
rcConfig m_cfg;
unsigned int m_tileMeshDrawFlags;
unsigned int m_tileCol;
float m_lastBuiltTileBmin[3];
float m_lastBuiltTileBmax[3];
float m_totalBuildTimeMs;
bool m_drawActiveTile;
bool m_keepInterResults;
};
class Editor_DynamicTileMeshCommon : public Editor
{
public:
Editor_DynamicTileMeshCommon();
void renderTileMeshRenderOptions();
void renderTileMeshData();
inline unsigned int getTileMeshDrawFlags() const { return m_tileMeshDrawFlags; }
inline void setTileMeshDrawFlags(unsigned int flags) { m_tileMeshDrawFlags = flags; }
inline void toggleTileMeshDrawFlag(unsigned int flag) { m_tileMeshDrawFlags ^= flag; }
protected:
class dtTileCache* m_tileCache;
struct LinearAllocator* m_talloc;
struct FastLZCompressor* m_tcomp;
struct MeshProcess* m_tmproc;
float m_cacheBuildTimeMs;
int m_cacheCompressedSize;
int m_cacheRawSize;
int m_cacheLayerCount;
unsigned int m_cacheBuildMemUsage;
unsigned int m_tileMeshDrawFlags;
bool m_keepInterResults;
};
int EditorCommon_SetAndRenderTileProperties(const InputGeom* const geom, const int tileSize,
const float cellSize, int& maxTiles, int& maxPolysPerTile);
#endif // RECASTEDITORCOMMON_H

View File

@ -16,51 +16,17 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef RECASTSAMPLESOLOMESH_H
#define RECASTSAMPLESOLOMESH_H
#ifndef RECASTEDITORSOLOMESH_H
#define RECASTEDITORSOLOMESH_H
#include "Recast/Include/Recast.h"
#include "Detour/Include/DetourNavMesh.h"
#include "NavEditor/Include/Sample.h"
#include "NavEditor/Include/Editor.h"
#include "NavEditor/Include/Editor_Common.h"
class Editor_SoloMesh : public Sample
class Editor_SoloMesh : public Editor_StaticTileMeshCommon
{
protected:
bool m_keepInterResults;
float m_totalBuildTimeMs;
unsigned char* m_triareas;
rcHeightfield* m_solid;
rcCompactHeightfield* m_chf;
rcContourSet* m_cset;
rcPolyMesh* m_pmesh;
rcConfig m_cfg;
rcPolyMeshDetail* m_dmesh;
enum DrawMode
{
DRAWMODE_NAVMESH,
DRAWMODE_NAVMESH_TRANS,
DRAWMODE_NAVMESH_BVTREE,
DRAWMODE_NAVMESH_NODES,
DRAWMODE_NAVMESH_INVIS,
DRAWMODE_MESH,
DRAWMODE_VOXELS,
DRAWMODE_VOXELS_WALKABLE,
DRAWMODE_COMPACT,
DRAWMODE_COMPACT_DISTANCE,
DRAWMODE_COMPACT_REGIONS,
DRAWMODE_REGION_CONNECTIONS,
DRAWMODE_RAW_CONTOURS,
DRAWMODE_BOTH_CONTOURS,
DRAWMODE_CONTOURS,
DRAWMODE_POLYMESH,
DRAWMODE_POLYMESH_DETAIL,
MAX_DRAWMODE
};
DrawMode m_drawMode;
void cleanup();
public:
@ -83,4 +49,4 @@ private:
};
#endif // RECASTSAMPLESOLOMESHSIMPLE_H
#endif // RECASTEDITORSOLOMESH_H

View File

@ -16,50 +16,21 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef RECASTSAMPLETEMPOBSTACLE_H
#define RECASTSAMPLETEMPOBSTACLE_H
#ifndef RECASTEDITORTEMPOBSTACLE_H
#define RECASTEDITORTEMPOBSTACLE_H
#include "Recast/Include/Recast.h"
#include "Detour/Include/DetourNavMesh.h"
#include "NavEditor/Include/ChunkyTriMesh.h"
#include "NavEditor/Include/Sample.h"
#include "NavEditor/Include/Editor.h"
#include "NavEditor/Include/Editor_Common.h"
class Editor_TempObstacles : public Sample
class Editor_TempObstacles : public Editor_DynamicTileMeshCommon
{
protected:
bool m_keepInterResults;
struct LinearAllocator* m_talloc;
struct FastLZCompressor* m_tcomp;
struct MeshProcess* m_tmproc;
class dtTileCache* m_tileCache;
float m_cacheBuildTimeMs;
int m_cacheCompressedSize;
int m_cacheRawSize;
int m_cacheLayerCount;
unsigned int m_cacheBuildMemUsage;
enum DrawMode
{
DRAWMODE_NAVMESH,
DRAWMODE_NAVMESH_TRANS,
DRAWMODE_NAVMESH_BVTREE,
DRAWMODE_NAVMESH_NODES,
DRAWMODE_NAVMESH_PORTALS,
DRAWMODE_NAVMESH_INVIS,
DRAWMODE_MESH,
DRAWMODE_CACHE_BOUNDS,
MAX_DRAWMODE
};
DrawMode m_drawMode;
int m_maxTiles;
int m_maxPolysPerTile;
float m_tileSize;
public:
Editor_TempObstacles();
@ -95,4 +66,4 @@ private:
};
#endif // RECASTSAMPLETEMPOBSTACLE_H
#endif // RECASTEDITORTEMPOBSTACLE_H

View File

@ -16,62 +16,30 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef RECASTSAMPLETILEMESH_H
#define RECASTSAMPLETILEMESH_H
#ifndef RECASTEDITORTILEMESH_H
#define RECASTEDITORTILEMESH_H
#include "Recast/Include/Recast.h"
#include "Detour/Include/DetourNavMesh.h"
#include "NavEditor/Include/ChunkyTriMesh.h"
#include "NavEditor/Include/Editor.h"
#include "NavEditor/Include/Editor_Common.h"
class Editor_TileMesh : public Editor
class Editor_TileMesh : public Editor_StaticTileMeshCommon
{
protected:
bool m_keepInterResults;
bool m_buildAll;
float m_totalBuildTimeMs;
unsigned char* m_triareas;
rcHeightfield* m_solid;
rcCompactHeightfield* m_chf;
rcContourSet* m_cset;
rcPolyMesh* m_pmesh;
rcPolyMeshDetail* m_dmesh;
rcConfig m_cfg;
enum DrawTileMeshFlags
{
TM_DRAWFLAGS_INPUT_MESH = (1<<0),
TM_DRAWFLAGS_NAVMESH = (1<<1),
TM_DRAWFLAGS_VOXELS = (1<<2),
TM_DRAWFLAGS_VOXELS_WALKABLE = (1<<3),
TM_DRAWFLAGS_COMPACT = (1<<4),
TM_DRAWFLAGS_COMPACT_DISTANCE = (1<<5),
TM_DRAWFLAGS_COMPACT_REGIONS = (1<<6),
TM_DRAWFLAGS_REGION_CONNECTIONS = (1<<7),
TM_DRAWFLAGS_RAW_CONTOURS = (1<<8),
TM_DRAWFLAGS_CONTOURS = (1<<9),
TM_DRAWFLAGS_POLYMESH = (1<<10),
TM_DRAWFLAGS_POLYMESH_DETAIL = (1<<11),
};
unsigned int m_tileMeshDrawFlags;
int m_maxTiles;
int m_maxPolysPerTile;
int m_tileSize;
unsigned int m_tileCol;
float m_lastBuiltTileBmin[3];
float m_lastBuiltTileBmax[3];
float m_tileBuildTime;
float m_tileMemUsage;
int m_tileTriCount;
unsigned char* buildTileMesh(const int tx, const int ty, const float* bmin, const float* bmax, int& dataSize);
void cleanup();
void saveAll(const char* path, const dtNavMesh* mesh);
dtNavMesh* loadAll(const char* path);
@ -87,13 +55,6 @@ public:
virtual void handleMeshChanged(class InputGeom* geom);
virtual bool handleBuild();
virtual void collectSettings(struct BuildSettings& settings);
void selectNavMeshType(const NavMeshType_e navMeshType);
inline unsigned int getTileMeshDrawFlags() const { return m_tileMeshDrawFlags; }
inline void setTileMeshDrawFlags(unsigned int flags) { m_tileMeshDrawFlags = flags; }
inline void toggleTileMeshDrawFlag(unsigned int flag) { m_tileMeshDrawFlags ^= flag; }
void getTilePos(const float* pos, int& tx, int& ty);
void getTileExtents(int tx, int ty, float* bmin, float* bmax);
@ -111,4 +72,4 @@ private:
};
#endif // RECASTSAMPLETILEMESH_H
#endif // RECASTEDITORTILEMESH_H

View File

@ -23,6 +23,7 @@
#include "NavEditor/Include/InputGeom.h"
#include "NavEditor/Include/TestCase.h"
#include "NavEditor/Include/Filelist.h"
#include "NavEditor/Include/Editor_SoloMesh.h"
#include "NavEditor/Include/Editor_TileMesh.h"
#include "NavEditor/Include/Editor_Debug.h"
#include "NavEditor/include/DroidSans.h"
@ -36,6 +37,7 @@ struct SampleItem
Editor* (*create)();
const string name;
};
Editor* createSolo() { return new Editor_SoloMesh(); }
Editor* createTile() { return new Editor_TileMesh(); }
Editor* createDebug() { return new Editor_Debug(); }