mirror of
https://github.com/recastnavigation/recastnavigation.git
synced 2026-08-15 15:50:03 +00:00
495 lines
11 KiB
C++
495 lines
11 KiB
C++
//
|
|
// 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 "Sample.h"
|
|
|
|
#include "DetourCrowd.h"
|
|
#include "DetourDebugDraw.h"
|
|
#include "InputGeom.h"
|
|
#include "RecastDebugDraw.h"
|
|
#include "imguiHelpers.h"
|
|
|
|
#include <imgui.h>
|
|
|
|
const char* toolNames[] = {
|
|
"None",
|
|
"Create Tiles",
|
|
"Highlight Tile Cache",
|
|
"Create Temp Obstacles",
|
|
"Test Navmesh",
|
|
"Prune Navmesh",
|
|
"Create Off-Mesh Connections",
|
|
"Create Convex Volumes",
|
|
"Create Crowds",
|
|
};
|
|
|
|
namespace
|
|
{
|
|
|
|
constexpr int NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET';
|
|
constexpr int NAVMESHSET_VERSION = 1;
|
|
|
|
struct NavMeshSetHeader
|
|
{
|
|
int magic;
|
|
int version;
|
|
int numTiles;
|
|
dtNavMeshParams params;
|
|
};
|
|
|
|
struct NavMeshTileHeader
|
|
{
|
|
dtTileRef tileRef;
|
|
int dataSize;
|
|
};
|
|
|
|
}
|
|
|
|
unsigned int SampleDebugDraw::areaToCol(unsigned int area)
|
|
{
|
|
switch (area)
|
|
{
|
|
// Ground (0) : light blue
|
|
case SAMPLE_POLYAREA_GROUND:
|
|
return duRGBA(0, 192, 255, 255);
|
|
// Water : blue
|
|
case SAMPLE_POLYAREA_WATER:
|
|
return duRGBA(0, 0, 255, 255);
|
|
// Road : brown
|
|
case SAMPLE_POLYAREA_ROAD:
|
|
return duRGBA(50, 20, 12, 255);
|
|
// Door : cyan
|
|
case SAMPLE_POLYAREA_DOOR:
|
|
return duRGBA(0, 255, 255, 255);
|
|
// Grass : green
|
|
case SAMPLE_POLYAREA_GRASS:
|
|
return duRGBA(0, 255, 0, 255);
|
|
// Jump : yellow
|
|
case SAMPLE_POLYAREA_JUMP:
|
|
return duRGBA(255, 255, 0, 255);
|
|
// Unexpected : red
|
|
default:
|
|
return duRGBA(255, 0, 0, 255);
|
|
}
|
|
}
|
|
|
|
Sample::Sample() : navMeshDrawFlags(DU_DRAWNAVMESH_OFFMESHCONS | DU_DRAWNAVMESH_CLOSEDLIST)
|
|
{
|
|
resetCommonSettings();
|
|
navQuery = dtAllocNavMeshQuery();
|
|
crowd = dtAllocCrowd();
|
|
}
|
|
|
|
Sample::~Sample()
|
|
{
|
|
dtFreeNavMeshQuery(navQuery);
|
|
dtFreeNavMesh(navMesh);
|
|
dtFreeCrowd(crowd);
|
|
delete tool;
|
|
for (int i = 0; i < static_cast<int>(SampleToolType::MAX_TOOLS); i++)
|
|
{
|
|
delete toolStates[i];
|
|
}
|
|
}
|
|
|
|
void Sample::setTool(SampleTool* newTool)
|
|
{
|
|
delete tool;
|
|
tool = newTool;
|
|
if (tool)
|
|
{
|
|
tool->init(this);
|
|
}
|
|
}
|
|
|
|
void Sample::drawSettingsUI() {}
|
|
|
|
void Sample::drawToolsUI() {}
|
|
|
|
void Sample::drawDebugUI() {}
|
|
|
|
void Sample::render()
|
|
{
|
|
if (!inputGeometry)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Draw mesh
|
|
duDebugDrawTriMesh(
|
|
&debugDraw,
|
|
inputGeometry->mesh.verts.data(),
|
|
inputGeometry->mesh.getVertCount(),
|
|
inputGeometry->mesh.tris.data(),
|
|
inputGeometry->mesh.normals.data(),
|
|
inputGeometry->mesh.getTriCount(),
|
|
0,
|
|
1.0f);
|
|
// Draw bounds
|
|
duDebugDrawBoxWire(
|
|
&debugDraw,
|
|
inputGeometry->meshBoundsMin[0],
|
|
inputGeometry->meshBoundsMin[1],
|
|
inputGeometry->meshBoundsMin[2],
|
|
inputGeometry->meshBoundsMax[0],
|
|
inputGeometry->meshBoundsMax[1],
|
|
inputGeometry->meshBoundsMax[2],
|
|
duRGBA(255, 255, 255, 128),
|
|
1.0f);
|
|
}
|
|
|
|
void Sample::renderOverlay() {}
|
|
|
|
void Sample::onMeshChanged(InputGeom* geom)
|
|
{
|
|
inputGeometry = geom;
|
|
|
|
if (const BuildSettings* buildSettings = geom->getBuildSettings())
|
|
{
|
|
cellSize = buildSettings->cellSize;
|
|
cellHeight = buildSettings->cellHeight;
|
|
agentHeight = buildSettings->agentHeight;
|
|
agentRadius = buildSettings->agentRadius;
|
|
agentMaxClimb = buildSettings->agentMaxClimb;
|
|
agentMaxSlope = buildSettings->agentMaxSlope;
|
|
regionMinSize = buildSettings->regionMinSize;
|
|
regionMergeSize = buildSettings->regionMergeSize;
|
|
edgeMaxLen = buildSettings->edgeMaxLen;
|
|
edgeMaxError = buildSettings->edgeMaxError;
|
|
detailSampleDist = buildSettings->detailSampleDist;
|
|
detailSampleMaxError = buildSettings->detailSampleMaxError;
|
|
partitionType = static_cast<SamplePartitionType>(buildSettings->partitionType);
|
|
}
|
|
}
|
|
|
|
void Sample::collectSettings(BuildSettings& settings)
|
|
{
|
|
settings.cellSize = cellSize;
|
|
settings.cellHeight = cellHeight;
|
|
settings.agentHeight = agentHeight;
|
|
settings.agentRadius = agentRadius;
|
|
settings.agentMaxClimb = agentMaxClimb;
|
|
settings.agentMaxSlope = agentMaxSlope;
|
|
settings.regionMinSize = regionMinSize;
|
|
settings.regionMergeSize = regionMergeSize;
|
|
settings.edgeMaxLen = edgeMaxLen;
|
|
settings.edgeMaxError = edgeMaxError;
|
|
settings.detailSampleDist = detailSampleDist;
|
|
settings.detailSampleMaxError = detailSampleMaxError;
|
|
settings.partitionType = static_cast<int>(partitionType);
|
|
}
|
|
|
|
void Sample::resetCommonSettings()
|
|
{
|
|
cellSize = 0.3f;
|
|
cellHeight = 0.2f;
|
|
agentHeight = 2.0f;
|
|
agentRadius = 0.6f;
|
|
agentMaxClimb = 0.9f;
|
|
agentMaxSlope = 45.0f;
|
|
regionMinSize = 8;
|
|
regionMergeSize = 20;
|
|
edgeMaxLen = 12.0f;
|
|
edgeMaxError = 1.3f;
|
|
vertsPerPoly = 6;
|
|
detailSampleDist = 6.0f;
|
|
detailSampleMaxError = 1.0f;
|
|
partitionType = SamplePartitionType::WATERSHED;
|
|
}
|
|
|
|
void Sample::drawCommonSettingsUI()
|
|
{
|
|
ImGui::SeparatorText("Rasterization");
|
|
DrawFloatSlider(&cellSize, 0.1f, 1.0f, "##Cell Size", "Cell Size", "%.2f");
|
|
DrawFloatSlider(&cellHeight, 0.1f, 1.0f, "##Cell Height", "Cell Height", "%.2f");
|
|
|
|
if (inputGeometry)
|
|
{
|
|
int gridWidth = 0;
|
|
int gridHeight = 0;
|
|
rcCalcGridSize(
|
|
inputGeometry->getNavMeshBoundsMin(),
|
|
inputGeometry->getNavMeshBoundsMax(),
|
|
cellSize,
|
|
&gridWidth,
|
|
&gridHeight);
|
|
DrawRightAlignedText("Voxels %d x %d", gridWidth, gridHeight);
|
|
}
|
|
|
|
ImGui::SeparatorText("Agent");
|
|
DrawFloatSlider(&agentHeight, 0.1f, 5.0f, "##Height", "Height");
|
|
DrawFloatSlider(&agentRadius, 0.001f, 5.0f, "##Radius", "Radius");
|
|
DrawFloatSlider(&agentMaxClimb, 0.1f, 5.0f, "##MaxClimb", "Max Climb");
|
|
DrawFloatSlider(&agentMaxSlope, 0.0f, 90.0f, "##MaxSlope", "Max Slope");
|
|
|
|
ImGui::SeparatorText("Region");
|
|
DrawFloatSlider(®ionMinSize, 0.0f, 150.0f, "##Min Region Size", "Min Region Size");
|
|
DrawFloatSlider(®ionMergeSize, 0.0f, 150.0f, "##Merged Region Size", "Merged Region Size");
|
|
|
|
ImGui::SeparatorText("Partitioning");
|
|
if (ImGui::RadioButton("Watershed", partitionType == SamplePartitionType::WATERSHED))
|
|
{
|
|
partitionType = SamplePartitionType::WATERSHED;
|
|
}
|
|
if (ImGui::RadioButton("Monotone", partitionType == SamplePartitionType::MONOTONE))
|
|
{
|
|
partitionType = SamplePartitionType::MONOTONE;
|
|
}
|
|
if (ImGui::RadioButton("Layers", partitionType == SamplePartitionType::LAYERS))
|
|
{
|
|
partitionType = SamplePartitionType::LAYERS;
|
|
}
|
|
|
|
ImGui::SeparatorText("Filtering");
|
|
ImGui::Checkbox("Low Hanging Obstacles", &filterLowHangingObstacles);
|
|
ImGui::Checkbox("Ledge Spans", &filterLedgeSpans);
|
|
ImGui::Checkbox("Walkable Low Height Spans", &filterWalkableLowHeightSpans);
|
|
|
|
ImGui::SeparatorText("Polygonization");
|
|
DrawFloatSlider(&edgeMaxLen, 0.0f, 50.0f, "##Max Edge Length", "Max Edge Length");
|
|
DrawFloatSlider(&edgeMaxError, 0.1f, 3.0f, "##Max Edge Error", "Max Edge Error");
|
|
DrawIntSlider(&vertsPerPoly, 3, 12, "##Verts Per Poly", "Verts Per Poly");
|
|
|
|
ImGui::SeparatorText("Detail Mesh");
|
|
DrawFloatSlider(&detailSampleDist, 0.0f, 16.0f, "##Sample Distance", "Sample Distance");
|
|
DrawFloatSlider(&detailSampleMaxError, 0.0f, 16.0f, "##Max Sample Error", "Max Sample Error");
|
|
|
|
ImGui::Separator();
|
|
}
|
|
|
|
void Sample::onClick(const float* rayStartPos, const float* rayHitPos, bool shift)
|
|
{
|
|
if (tool)
|
|
{
|
|
tool->onClick(rayStartPos, rayHitPos, shift);
|
|
}
|
|
}
|
|
|
|
void Sample::onToggle()
|
|
{
|
|
if (tool)
|
|
{
|
|
tool->onToggle();
|
|
}
|
|
}
|
|
|
|
void Sample::singleStep()
|
|
{
|
|
if (tool)
|
|
{
|
|
tool->singleStep();
|
|
}
|
|
}
|
|
|
|
bool Sample::build()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void Sample::update(const float dt)
|
|
{
|
|
if (tool)
|
|
{
|
|
tool->update(dt);
|
|
}
|
|
updateToolStates(dt);
|
|
}
|
|
|
|
void Sample::updateToolStates(const float dt) const
|
|
{
|
|
for (int i = 0; i < static_cast<int>(SampleToolType::MAX_TOOLS); i++)
|
|
{
|
|
if (toolStates[i])
|
|
{
|
|
toolStates[i]->update(dt);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sample::initToolStates(Sample* sample) const
|
|
{
|
|
for (int i = 0; i < static_cast<int>(SampleToolType::MAX_TOOLS); i++)
|
|
{
|
|
if (toolStates[i])
|
|
{
|
|
toolStates[i]->init(sample);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sample::resetToolStates() const
|
|
{
|
|
for (int i = 0; i < static_cast<int>(SampleToolType::MAX_TOOLS); i++)
|
|
{
|
|
if (toolStates[i])
|
|
{
|
|
toolStates[i]->reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sample::renderToolStates() const
|
|
{
|
|
for (int i = 0; i < static_cast<int>(SampleToolType::MAX_TOOLS); i++)
|
|
{
|
|
if (toolStates[i])
|
|
{
|
|
toolStates[i]->render();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sample::renderOverlayToolStates() const
|
|
{
|
|
for (int i = 0; i < static_cast<int>(SampleToolType::MAX_TOOLS); i++)
|
|
{
|
|
if (toolStates[i])
|
|
{
|
|
toolStates[i]->renderOverlay();
|
|
}
|
|
}
|
|
}
|
|
|
|
dtNavMesh* Sample::loadAll(const char* path)
|
|
{
|
|
FILE* file = fopen(path, "rb");
|
|
if (!file)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Read header.
|
|
NavMeshSetHeader header;
|
|
size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, file);
|
|
if (readLen != 1)
|
|
{
|
|
fclose(file);
|
|
return nullptr;
|
|
}
|
|
if (header.magic != NAVMESHSET_MAGIC)
|
|
{
|
|
fclose(file);
|
|
return nullptr;
|
|
}
|
|
if (header.version != NAVMESHSET_VERSION)
|
|
{
|
|
fclose(file);
|
|
return nullptr;
|
|
}
|
|
|
|
dtNavMesh* mesh = dtAllocNavMesh();
|
|
if (!mesh)
|
|
{
|
|
fclose(file);
|
|
return nullptr;
|
|
}
|
|
dtStatus status = mesh->init(&header.params);
|
|
if (dtStatusFailed(status))
|
|
{
|
|
fclose(file);
|
|
return nullptr;
|
|
}
|
|
|
|
// Read tiles.
|
|
for (int i = 0; i < header.numTiles; ++i)
|
|
{
|
|
NavMeshTileHeader tileHeader;
|
|
readLen = fread(&tileHeader, sizeof(tileHeader), 1, file);
|
|
if (readLen != 1)
|
|
{
|
|
fclose(file);
|
|
return 0;
|
|
}
|
|
|
|
if (!tileHeader.tileRef || !tileHeader.dataSize)
|
|
{
|
|
break;
|
|
}
|
|
|
|
unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM);
|
|
if (!data)
|
|
{
|
|
break;
|
|
}
|
|
memset(data, 0, tileHeader.dataSize);
|
|
readLen = fread(data, tileHeader.dataSize, 1, file);
|
|
if (readLen != 1)
|
|
{
|
|
dtFree(data);
|
|
fclose(file);
|
|
return 0;
|
|
}
|
|
|
|
mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0);
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
return mesh;
|
|
}
|
|
|
|
void Sample::saveAll(const char* path, const dtNavMesh* mesh)
|
|
{
|
|
if (!mesh)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FILE* file = fopen(path, "wb");
|
|
if (!file)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Store header.
|
|
NavMeshSetHeader header;
|
|
header.magic = NAVMESHSET_MAGIC;
|
|
header.version = NAVMESHSET_VERSION;
|
|
header.numTiles = 0;
|
|
for (int i = 0; i < mesh->getMaxTiles(); ++i)
|
|
{
|
|
const dtMeshTile* tile = mesh->getTile(i);
|
|
if (!tile || !tile->header || !tile->dataSize)
|
|
{
|
|
continue;
|
|
}
|
|
header.numTiles++;
|
|
}
|
|
memcpy(&header.params, mesh->getParams(), sizeof(dtNavMeshParams));
|
|
fwrite(&header, sizeof(NavMeshSetHeader), 1, file);
|
|
|
|
// Store tiles.
|
|
for (int i = 0; i < mesh->getMaxTiles(); ++i)
|
|
{
|
|
const dtMeshTile* tile = mesh->getTile(i);
|
|
if (!tile || !tile->header || !tile->dataSize)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
NavMeshTileHeader tileHeader;
|
|
tileHeader.tileRef = mesh->getTileRef(tile);
|
|
tileHeader.dataSize = tile->dataSize;
|
|
fwrite(&tileHeader, sizeof(tileHeader), 1, file);
|
|
|
|
fwrite(tile->data, tile->dataSize, 1, file);
|
|
}
|
|
|
|
fclose(file);
|
|
}
|