r5sdk/r5dev/naveditor/Editor.cpp
2023-04-03 14:48:38 +02:00

502 lines
13 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 "Pch.h"
#include "Recast/Include/Recast.h"
#include "Detour/Include/DetourNavMesh.h"
#include "Detour/Include/DetourNavMeshQuery.h"
#include "DetourCrowd/Include/DetourCrowd.h"
#include "DebugUtils/Include/RecastDebugDraw.h"
#include "DebugUtils/Include/DetourDebugDraw.h"
#include "NavEditor/Include/FileTypes.h"
#include "NavEditor/Include/GameUtils.h"
#include "NavEditor/Include/InputGeom.h"
#include "NavEditor/Include/Editor.h"
unsigned int EditorDebugDraw::areaToCol(unsigned int area)
{
switch(area)
{
// Ground (0) : light blue
case EDITOR_POLYAREA_GROUND: return duRGBA(0, 135, 255, 255);
// Water : blue
case EDITOR_POLYAREA_WATER: return duRGBA(0, 0, 255, 255);
// Road : brown
case EDITOR_POLYAREA_ROAD: return duRGBA(50, 20, 12, 255);
// Door : cyan
case EDITOR_POLYAREA_DOOR: return duRGBA(0, 255, 255, 255);
// Grass : green
case EDITOR_POLYAREA_GRASS: return duRGBA(0, 255, 0, 255);
// Jump : yellow
case EDITOR_POLYAREA_JUMP: return duRGBA(255, 255, 0, 255);
// Unexpected : red
default: return duRGBA(255, 0, 0, 255);
}
}
Editor::Editor() :
m_geom(0),
m_navMesh(0),
m_navQuery(0),
m_crowd(0),
m_navMeshDrawFlags(DU_DRAWNAVMESH_OFFMESHCONS|DU_DRAWNAVMESH_CLOSEDLIST),
m_filterLowHangingObstacles(true),
m_filterLedgeSpans(true),
m_filterWalkableLowHeightSpans(true),
m_tool(0),
m_ctx(0)
{
resetCommonSettings();
m_navQuery = dtAllocNavMeshQuery();
m_crowd = dtAllocCrowd();
for (int i = 0; i < MAX_TOOLS; i++)
m_toolStates[i] = 0;
}
Editor::~Editor()
{
dtFreeNavMeshQuery(m_navQuery);
dtFreeNavMesh(m_navMesh);
dtFreeCrowd(m_crowd);
delete m_tool;
for (int i = 0; i < MAX_TOOLS; i++)
delete m_toolStates[i];
}
void Editor::setTool(EditorTool* tool)
{
delete m_tool;
m_tool = tool;
if (tool)
m_tool->init(this);
}
void Editor::handleSettings()
{
}
void Editor::handleTools()
{
}
void Editor::handleDebugMode()
{
}
void Editor::handleRender()
{
if (!m_geom)
return;
// Draw mesh
duDebugDrawTriMesh(&m_dd, m_geom->getMesh()->getVerts(), m_geom->getMesh()->getVertCount(),
m_geom->getMesh()->getTris(), m_geom->getMesh()->getNormals(), m_geom->getMesh()->getTriCount(), 0, 1.0f);
// Draw bounds
const float* bmin = m_geom->getMeshBoundsMin();
const float* bmax = m_geom->getMeshBoundsMax();
duDebugDrawBoxWire(&m_dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f);
}
void Editor::handleRenderOverlay(double* /*proj*/, double* /*model*/, int* /*view*/)
{
}
void Editor::handleMeshChanged(InputGeom* geom)
{
m_geom = geom;
const BuildSettings* buildSettings = geom->getBuildSettings();
if (buildSettings)
{
m_cellSize = buildSettings->cellSize;
m_cellHeight = buildSettings->cellHeight;
m_agentHeight = buildSettings->agentHeight;
m_agentRadius = buildSettings->agentRadius;
m_agentMaxClimb = buildSettings->agentMaxClimb;
m_agentMaxSlope = buildSettings->agentMaxSlope;
m_regionMinSize = buildSettings->regionMinSize;
m_regionMergeSize = buildSettings->regionMergeSize;
m_edgeMaxLen = buildSettings->edgeMaxLen;
m_edgeMaxError = buildSettings->edgeMaxError;
m_vertsPerPoly = buildSettings->vertsPerPoly;
m_detailSampleDist = buildSettings->detailSampleDist;
m_detailSampleMaxError = buildSettings->detailSampleMaxError;
m_partitionType = buildSettings->partitionType;
}
}
void Editor::collectSettings(BuildSettings& settings)
{
settings.cellSize = m_cellSize;
settings.cellHeight = m_cellHeight;
settings.agentHeight = m_agentHeight;
settings.agentRadius = m_agentRadius;
settings.agentMaxClimb = m_agentMaxClimb;
settings.agentMaxSlope = m_agentMaxSlope;
settings.regionMinSize = m_regionMinSize;
settings.regionMergeSize = m_regionMergeSize;
settings.edgeMaxLen = m_edgeMaxLen;
settings.edgeMaxError = m_edgeMaxError;
settings.vertsPerPoly = m_vertsPerPoly;
settings.detailSampleDist = m_detailSampleDist;
settings.detailSampleMaxError = m_detailSampleMaxError;
settings.partitionType = m_partitionType;
}
void Editor::resetCommonSettings()
{
m_cellSize = 15.0f;
m_cellHeight = 5.85f;
m_agentHeight = 2.0f;
m_agentRadius = 0.6f;
m_agentMaxClimb = 0.9f;
m_agentMaxSlope = 45.0f;
m_regionMinSize = 8;
m_regionMergeSize = 20;
m_edgeMaxLen = 12.0f;
m_edgeMaxError = 1.3f;
m_vertsPerPoly = 6.0f;
m_detailSampleDist = 6.0f;
m_detailSampleMaxError = 1.0f;
m_partitionType = EDITOR_PARTITION_WATERSHED;
m_reachabilityTableCount = 4;
}
void Editor::handleCommonSettings()
{
imguiLabel("Rasterization");
imguiSlider("Cell Size", &m_cellSize, 0.1f, 100.0f, 0.01f);
imguiSlider("Cell Height", &m_cellHeight, 0.1f, 100.0f, 0.01f);
if (m_geom)
{
const float* bmin = m_geom->getNavMeshBoundsMin();
const float* bmax = m_geom->getNavMeshBoundsMax();
int gw = 0, gh = 0;
rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh);
char text[64];
snprintf(text, 64, "Voxels %d x %d", gw, gh);
imguiValue(text);
}
imguiSeparator();
imguiLabel("Agent");
imguiSlider("Height", &m_agentHeight, 0.1f, 500.0f, 0.1f);
imguiSlider("Radius", &m_agentRadius, 0.0f, 500.0f, 0.1f);
imguiSlider("Max Climb", &m_agentMaxClimb, 0.1f, 120.0f, 0.1f);
imguiSlider("Max Slope", &m_agentMaxSlope, 0.0f, 90.0f, 1.0f);
imguiSeparator();
imguiLabel("Region");
imguiSlider("Min Region Size", &m_regionMinSize, 0.0f, 750.0f, 1.0f);
imguiSlider("Merged Region Size", &m_regionMergeSize, 0.0f, 750.0f, 1.0f);
imguiSeparator();
imguiLabel("Partitioning");
if (imguiCheck("Watershed", m_partitionType == EDITOR_PARTITION_WATERSHED))
m_partitionType = EDITOR_PARTITION_WATERSHED;
if (imguiCheck("Monotone", m_partitionType == EDITOR_PARTITION_MONOTONE))
m_partitionType = EDITOR_PARTITION_MONOTONE;
if (imguiCheck("Layers", m_partitionType == EDITOR_PARTITION_LAYERS))
m_partitionType = EDITOR_PARTITION_LAYERS;
imguiSeparator();
imguiLabel("Filtering");
if (imguiCheck("Low Hanging Obstacles", m_filterLowHangingObstacles))
m_filterLowHangingObstacles = !m_filterLowHangingObstacles;
if (imguiCheck("Ledge Spans", m_filterLedgeSpans))
m_filterLedgeSpans= !m_filterLedgeSpans;
if (imguiCheck("Walkable Low Height Spans", m_filterWalkableLowHeightSpans))
m_filterWalkableLowHeightSpans = !m_filterWalkableLowHeightSpans;
imguiSeparator();
imguiLabel("Polygonization");
imguiSlider("Max Edge Length", &m_edgeMaxLen, 0.0f, 50.0f, 1.0f);
imguiSlider("Max Edge Error", &m_edgeMaxError, 0.1f, 3.0f, 0.1f);
imguiSlider("Verts Per Poly", &m_vertsPerPoly, 3.0f, 6.0f, 1.0f);
imguiSeparator();
imguiLabel("Detail Mesh");
imguiSlider("Sample Distance", &m_detailSampleDist, 0.0f, 16.0f, 1.0f);
imguiSlider("Max Sample Error", &m_detailSampleMaxError, 0.0f, 16.0f, 1.0f);
imguiSeparator();
}
void Editor::handleClick(const float* s, const float* p, bool shift)
{
if (m_tool)
m_tool->handleClick(s, p, shift);
}
void Editor::handleToggle()
{
if (m_tool)
m_tool->handleToggle();
}
void Editor::handleStep()
{
if (m_tool)
m_tool->handleStep();
}
bool Editor::handleBuild()
{
return true;
}
void Editor::handleUpdate(const float dt)
{
if (m_tool)
m_tool->handleUpdate(dt);
updateToolStates(dt);
}
void Editor::updateToolStates(const float dt)
{
for (int i = 0; i < MAX_TOOLS; i++)
{
if (m_toolStates[i])
m_toolStates[i]->handleUpdate(dt);
}
}
void Editor::initToolStates(Editor* editor)
{
for (int i = 0; i < MAX_TOOLS; i++)
{
if (m_toolStates[i])
m_toolStates[i]->init(editor);
}
}
void Editor::resetToolStates()
{
for (int i = 0; i < MAX_TOOLS; i++)
{
if (m_toolStates[i])
m_toolStates[i]->reset();
}
}
void Editor::renderToolStates()
{
for (int i = 0; i < MAX_TOOLS; i++)
{
if (m_toolStates[i])
m_toolStates[i]->handleRender();
}
}
void Editor::renderOverlayToolStates(double* proj, double* model, int* view)
{
for (int i = 0; i < MAX_TOOLS; i++)
{
if (m_toolStates[i])
m_toolStates[i]->handleRenderOverlay(proj, model, view);
}
}
dtNavMesh* Editor::loadAll(std::string path)
{
fs::path p = "..\\maps\\navmesh\\";
if (fs::is_directory(p))
{
path.insert(0, p.string());
}
char buffer[256];
sprintf(buffer, "%s_%s.nm", path.c_str(), m_navmeshName);
FILE* fp = fopen(buffer, "rb");
if (!fp)
return 0;
// Read header.
NavMeshSetHeader header;
size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp);
if (readLen != 1)
{
fclose(fp);
return 0;
}
if (header.magic != NAVMESHSET_MAGIC)
{
fclose(fp);
return 0;
}
if (header.version != NAVMESHSET_VERSION)
{
fclose(fp);
return 0;
}
dtNavMesh* mesh = dtAllocNavMesh();
if (!mesh)
{
fclose(fp);
return 0;
}
dtStatus status = mesh->init(&header.params);
if (dtStatusFailed(status))
{
fclose(fp);
return 0;
}
// Read tiles.
for (int i = 0; i < header.numTiles; ++i)
{
NavMeshTileHeader tileHeader;
readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp);
if (readLen != 1)
{
fclose(fp);
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, fp);
if (readLen != 1)
{
dtFree(data);
fclose(fp);
return 0;
}
mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, NULL);
}
fclose(fp);
return mesh;
}
void Editor::saveAll(std::string path, dtNavMesh* mesh)
{
if (!mesh)
return;
fs::path p = "..\\maps\\navmesh\\";
if (fs::is_directory(p))
{
path.insert(0, p.string());
}
char buffer[256];
sprintf(buffer, "%s_%s.nm", path.c_str(), m_navmeshName);
FILE* fp = fopen(buffer, "wb");
if (!fp)
return;
// Store header.
NavMeshSetHeader header;
header.magic = NAVMESHSET_MAGIC;
header.version = NAVMESHSET_VERSION;
header.numTiles = 0;
for (int i = 0; i < mesh->getMaxTiles(); ++i)
{
dtMeshTile* tile = mesh->getTile(i);
if (!tile || !tile->header || !tile->dataSize)
continue;
header.numTiles++;
}
memcpy(&header.params, mesh->getParams(), sizeof(dtNavMeshParams));
//LinkTableData linkData;
//buildLinkTable(mesh, linkData);
//int tableSize = ((linkData.setCount + 31) / 32) * linkData.setCount * 32;
//std::vector<int> reachability(tableSize, 0);
//for (int i = 0; i < linkData.setCount; i++)
// setReachable(reachability, linkData.setCount, i, i, true);
//if (reachability.size() > 0)
//{
// for (size_t i = reachability.size() - 1; i >= 0; i--)
// {
// if (reachability[i] == 0)
// {
// reachability.erase(reachability.begin() + i);
// tableSize--;
// }
// else
// break;
// }
//}
//header.params.disjointPolyGroupCount = linkData.setCount;
//header.params.reachabilityTableCount = m_reachabilityTableCount;
//header.params.reachabilityTableSize = tableSize;
header.params.disjointPolyGroupCount = 4;
header.params.reachabilityTableCount = m_reachabilityTableCount;
header.params.reachabilityTableSize = ((header.params.disjointPolyGroupCount + 31) / 32) * header.params.disjointPolyGroupCount * 32;
fwrite(&header, sizeof(NavMeshSetHeader), 1, fp);
// Store tiles.
for (int i = 0; i < mesh->getMaxTiles(); ++i)
{
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, fp);
fwrite(tile->data, tile->dataSize, 1, fp);
}
////still dont know what this thing is...
//int header_sth = 0;
//for (int i = 0; i < linkData.setCount; i++)
// fwrite(&header_sth, sizeof(int), 1, fp);
//for (int i = 0; i < header.params.reachabilityTableCount; i++)
// fwrite(reachability.data(), sizeof(int), tableSize, fp);
int header_sth[4] = { 0,0,0 };
fwrite(header_sth, sizeof(int), 4, fp);
unsigned int reachability[32 * 4];
for (int i = 0; i < 32 * 4; i++)
reachability[i] = 0xffffffff;
for (int i = 0; i < header.params.reachabilityTableCount; i++)
fwrite(reachability, sizeof(int), (header.params.reachabilityTableSize / 4), fp);
fclose(fp);
}