From a31da928ba582dcbaf712949578d1f42d2b6b414 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Sun, 17 Jan 2016 18:55:54 +0100 Subject: [PATCH] Add settings storage to geometry sets This adds the ability for geometry sets to store build settings that can be automatically applied when they are loaded. This should allow sharing of .gset files to demonstrate problems with certain settings on certain files. It also allows people to diagnose problems more easily by being able to dump their own triangle meshes and settings and load them in the demo, with all of its visualization options. .gset files can be created from the current mesh and settings by pressing the 9 key, which will generate it in the same folder as the input mesh. Also converts more of the demo to use STL. --- .gitignore | 4 +- RecastDemo/Include/Filelist.h | 3 +- RecastDemo/Include/InputGeom.h | 58 +++++++- RecastDemo/Include/MeshLoaderObj.h | 18 +-- RecastDemo/Include/Sample.h | 7 +- RecastDemo/Include/Sample_TileMesh.h | 5 +- RecastDemo/Include/TestCase.h | 11 +- RecastDemo/Source/Filelist.cpp | 10 +- RecastDemo/Source/InputGeom.cpp | 96 +++++++++++++- RecastDemo/Source/MeshLoaderObj.cpp | 10 +- RecastDemo/Source/Sample.cpp | 46 +++++-- RecastDemo/Source/Sample_SoloMesh.cpp | 8 +- RecastDemo/Source/Sample_TempObstacles.cpp | 16 +-- RecastDemo/Source/Sample_TileMesh.cpp | 102 ++++++++------ RecastDemo/Source/TestCase.cpp | 8 +- RecastDemo/Source/main.cpp | 147 +++++++-------------- 16 files changed, 333 insertions(+), 216 deletions(-) diff --git a/.gitignore b/.gitignore index cdc1b76..c4a9ba0 100644 --- a/.gitignore +++ b/.gitignore @@ -16,8 +16,8 @@ RecastDemo/Bin/Tests # Build directory RecastDemo/Build -# Ignore some meshes based on name -RecastDemo/Bin/Meshes/_* +# Ignore meshes +RecastDemo/Bin/Meshes/* ## Logs and databases # *.log diff --git a/RecastDemo/Include/Filelist.h b/RecastDemo/Include/Filelist.h index 31c8d95..30ade6e 100644 --- a/RecastDemo/Include/Filelist.h +++ b/RecastDemo/Include/Filelist.h @@ -22,6 +22,7 @@ #include #include -void scanDirectory(std::string path, std::string ext, std::vector& fileList); +void scanDirectoryAppend(const std::string& path, const std::string& ext, std::vector& fileList); +void scanDirectory(const std::string& path, const std::string& ext, std::vector& fileList); #endif // FILELIST_H diff --git a/RecastDemo/Include/InputGeom.h b/RecastDemo/Include/InputGeom.h index b508f68..ba00098 100644 --- a/RecastDemo/Include/InputGeom.h +++ b/RecastDemo/Include/InputGeom.h @@ -31,11 +31,51 @@ struct ConvexVolume int area; }; +struct BuildSettings +{ + // Cell size in world units + float cellSize; + // Cell height in world units + float cellHeight; + // Agent height in world units + float agentHeight; + // Agent radius in world units + float agentRadius; + // Agent max climb in world units + float agentMaxClimb; + // Agent max slope in degrees + float agentMaxSlope; + // Region minimum size in voxels. + // regionMinSize = sqrt(regionMinArea) + float regionMinSize; + // Region merge size in voxels. + // regionMergeSize = sqrt(regionMergeArea) + float regionMergeSize; + // Edge max length in world units + float edgeMaxLen; + // Edge max error in voxels + float edgeMaxError; + float vertsPerPoly; + // Detail sample distance in voxels + float detailSampleDist; + // Detail sample max error in voxel heights. + float detailSampleMaxError; + // Partition type, see SamplePartitionType + int partitionType; + // Bounds of the area to mesh + float navMeshBMin[3]; + float navMeshBMax[3]; + // Size of the tiles in voxels + float tileSize; +}; + class InputGeom { rcChunkyTriMesh* m_chunkyMesh; rcMeshLoaderObj* m_mesh; float m_meshBMin[3], m_meshBMax[3]; + BuildSettings m_buildSettings; + bool m_hasBuildSettings; /// @name Off-Mesh connections. ///@{ @@ -56,20 +96,24 @@ class InputGeom int m_volumeCount; ///@} + bool loadMesh(class rcContext* ctx, const std::string& filepath); + bool loadGeomSet(class rcContext* ctx, const std::string& filepath); public: InputGeom(); ~InputGeom(); - bool loadMesh(class rcContext* ctx, const char* filepath); - bool load(class rcContext* ctx, const char* filepath); - bool save(const char* filepath); + bool load(class rcContext* ctx, const std::string& filepath); + bool saveGeomSet(const BuildSettings* settings); /// Method to return static mesh data. - inline const rcMeshLoaderObj* getMesh() const { return m_mesh; } - inline const float* getMeshBoundsMin() const { return m_meshBMin; } - inline const float* getMeshBoundsMax() const { return m_meshBMax; } - inline const rcChunkyTriMesh* getChunkyMesh() const { return m_chunkyMesh; } + const rcMeshLoaderObj* getMesh() const { return m_mesh; } + const float* getMeshBoundsMin() const { return m_meshBMin; } + const float* getMeshBoundsMax() const { return m_meshBMax; } + const float* getNavMeshBoundsMin() const { return m_hasBuildSettings ? m_buildSettings.navMeshBMin : m_meshBMin; } + const float* getNavMeshBoundsMax() const { return m_hasBuildSettings ? m_buildSettings.navMeshBMax : m_meshBMax; } + const rcChunkyTriMesh* getChunkyMesh() const { return m_chunkyMesh; } + const BuildSettings* getBuildSettings() const { return m_hasBuildSettings ? &m_buildSettings : 0; } bool raycastMesh(float* src, float* dst, float& tmin); /// @name Off-Mesh connections. diff --git a/RecastDemo/Include/MeshLoaderObj.h b/RecastDemo/Include/MeshLoaderObj.h index 18cb10c..01cb154 100644 --- a/RecastDemo/Include/MeshLoaderObj.h +++ b/RecastDemo/Include/MeshLoaderObj.h @@ -19,27 +19,29 @@ #ifndef MESHLOADER_OBJ #define MESHLOADER_OBJ +#include + class rcMeshLoaderObj { public: rcMeshLoaderObj(); ~rcMeshLoaderObj(); - bool load(const char* fileName); + bool load(const std::string& fileName); - inline const float* getVerts() const { return m_verts; } - inline const float* getNormals() const { return m_normals; } - inline const int* getTris() const { return m_tris; } - inline int getVertCount() const { return m_vertCount; } - inline int getTriCount() const { return m_triCount; } - inline const char* getFileName() const { return m_filename; } + const float* getVerts() const { return m_verts; } + const float* getNormals() const { return m_normals; } + const int* getTris() const { return m_tris; } + int getVertCount() const { return m_vertCount; } + int getTriCount() const { return m_triCount; } + const std::string& getFileName() const { return m_filename; } private: void addVertex(float x, float y, float z, int& cap); void addTriangle(int a, int b, int c, int& cap); - char m_filename[260]; + std::string m_filename; float m_scale; float* m_verts; int* m_tris; diff --git a/RecastDemo/Include/Sample.h b/RecastDemo/Include/Sample.h index 1f058fc..9305b1f 100644 --- a/RecastDemo/Include/Sample.h +++ b/RecastDemo/Include/Sample.h @@ -141,6 +141,7 @@ public: virtual void handleMeshChanged(class InputGeom* geom); virtual bool handleBuild(); virtual void handleUpdate(const float dt); + virtual void collectSettings(struct BuildSettings& settings); virtual class InputGeom* getInputGeom() { return m_geom; } virtual class dtNavMesh* getNavMesh() { return m_navMesh; } @@ -149,11 +150,9 @@ public: virtual float getAgentRadius() { return m_agentRadius; } virtual float getAgentHeight() { return m_agentHeight; } virtual float getAgentClimb() { return m_agentMaxClimb; } - virtual const float* getBoundsMin(); - virtual const float* getBoundsMax(); - inline unsigned char getNavMeshDrawFlags() const { return m_navMeshDrawFlags; } - inline void setNavMeshDrawFlags(unsigned char flags) { m_navMeshDrawFlags = flags; } + unsigned char getNavMeshDrawFlags() const { return m_navMeshDrawFlags; } + void setNavMeshDrawFlags(unsigned char flags) { m_navMeshDrawFlags = flags; } void updateToolStates(const float dt); void initToolStates(Sample* sample); diff --git a/RecastDemo/Include/Sample_TileMesh.h b/RecastDemo/Include/Sample_TileMesh.h index 58f52c1..b322f81 100644 --- a/RecastDemo/Include/Sample_TileMesh.h +++ b/RecastDemo/Include/Sample_TileMesh.h @@ -69,8 +69,8 @@ protected: float m_tileSize; unsigned int m_tileCol; - float m_tileBmin[3]; - float m_tileBmax[3]; + float m_lastBuiltTileBmin[3]; + float m_lastBuiltTileBmax[3]; float m_tileBuildTime; float m_tileMemUsage; int m_tileTriCount; @@ -93,6 +93,7 @@ public: virtual void handleRenderOverlay(double* proj, double* model, int* view); virtual void handleMeshChanged(class InputGeom* geom); virtual bool handleBuild(); + virtual void collectSettings(struct BuildSettings& settings); void getTilePos(const float* pos, int& tx, int& ty); diff --git a/RecastDemo/Include/TestCase.h b/RecastDemo/Include/TestCase.h index 3269042..e89e67d 100644 --- a/RecastDemo/Include/TestCase.h +++ b/RecastDemo/Include/TestCase.h @@ -19,6 +19,7 @@ #ifndef TESTCASE_H #define TESTCASE_H +#include #include "DetourNavMesh.h" class TestCase @@ -60,8 +61,8 @@ class TestCase Test* next; }; - char m_sampleName[256]; - char m_geomFileName[256]; + std::string m_sampleName; + std::string m_geomFileName; Test* m_tests; void resetTimes(); @@ -70,10 +71,10 @@ public: TestCase(); ~TestCase(); - bool load(const char* filePath); + bool load(const std::string& filePath); - inline const char* getSampleName() const { return m_sampleName; } - inline const char* getGeomFileName() const { return m_geomFileName; } + const std::string& getSampleName() const { return m_sampleName; } + const std::string& getGeomFileName() const { return m_geomFileName; } void doTests(class dtNavMesh* navmesh, class dtNavMeshQuery* navquery); diff --git a/RecastDemo/Source/Filelist.cpp b/RecastDemo/Source/Filelist.cpp index 0af3cd6..1bd79d7 100644 --- a/RecastDemo/Source/Filelist.cpp +++ b/RecastDemo/Source/Filelist.cpp @@ -29,10 +29,8 @@ using std::vector; using std::string; -void scanDirectory(string path, string ext, vector& filelist) +void scanDirectoryAppend(const string& path, const string& ext, vector& filelist) { - filelist.clear(); - #ifdef WIN32 string pathWithExt = path + "/*" + ext; @@ -71,3 +69,9 @@ void scanDirectory(string path, string ext, vector& filelist) // Sort the list of files alphabetically. std::sort(filelist.begin(), filelist.end()); } + +void scanDirectory(const string& path, const string& ext, vector& filelist) +{ + filelist.clear(); + scanDirectoryAppend(path, ext, filelist); +} \ No newline at end of file diff --git a/RecastDemo/Source/InputGeom.cpp b/RecastDemo/Source/InputGeom.cpp index b532511..a0ec2c2 100644 --- a/RecastDemo/Source/InputGeom.cpp +++ b/RecastDemo/Source/InputGeom.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "Recast.h" #include "InputGeom.h" #include "ChunkyTriMesh.h" @@ -107,6 +108,7 @@ static char* parseRow(char* buf, char* bufEnd, char* row, int len) InputGeom::InputGeom() : m_chunkyMesh(0), m_mesh(0), + m_hasBuildSettings(false), m_offMeshConCount(0), m_volumeCount(0) { @@ -118,7 +120,7 @@ InputGeom::~InputGeom() delete m_mesh; } -bool InputGeom::loadMesh(rcContext* ctx, const char* filepath) +bool InputGeom::loadMesh(rcContext* ctx, const std::string& filepath) { if (m_mesh) { @@ -138,7 +140,7 @@ bool InputGeom::loadMesh(rcContext* ctx, const char* filepath) } if (!m_mesh->load(filepath)) { - ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not load '%s'", filepath); + ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not load '%s'", filepath.c_str()); return false; } @@ -159,10 +161,10 @@ bool InputGeom::loadMesh(rcContext* ctx, const char* filepath) return true; } -bool InputGeom::load(rcContext* ctx, const char* filePath) +bool InputGeom::loadGeomSet(rcContext* ctx, const std::string& filepath) { char* buf = 0; - FILE* fp = fopen(filePath, "rb"); + FILE* fp = fopen(filepath.c_str(), "rb"); if (!fp) return false; fseek(fp, 0, SEEK_END); @@ -243,6 +245,33 @@ bool InputGeom::load(rcContext* ctx, const char* filePath) } } } + else if (row[0] == 's') + { + // Settings + m_hasBuildSettings = true; + sscanf(row + 1, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d %f %f %f %f %f %f %f", + &m_buildSettings.cellSize, + &m_buildSettings.cellHeight, + &m_buildSettings.agentHeight, + &m_buildSettings.agentRadius, + &m_buildSettings.agentMaxClimb, + &m_buildSettings.agentMaxSlope, + &m_buildSettings.regionMinSize, + &m_buildSettings.regionMergeSize, + &m_buildSettings.edgeMaxLen, + &m_buildSettings.edgeMaxError, + &m_buildSettings.vertsPerPoly, + &m_buildSettings.detailSampleDist, + &m_buildSettings.detailSampleMaxError, + &m_buildSettings.partitionType, + &m_buildSettings.navMeshBMin[0], + &m_buildSettings.navMeshBMin[1], + &m_buildSettings.navMeshBMin[2], + &m_buildSettings.navMeshBMax[0], + &m_buildSettings.navMeshBMax[1], + &m_buildSettings.navMeshBMax[2], + &m_buildSettings.tileSize); + } } delete [] buf; @@ -250,15 +279,68 @@ bool InputGeom::load(rcContext* ctx, const char* filePath) return true; } -bool InputGeom::save(const char* filepath) +bool InputGeom::load(rcContext* ctx, const std::string& filepath) +{ + size_t extensionPos = filepath.find_last_of('.'); + if (extensionPos == std::string::npos) + return false; + + std::string extension = filepath.substr(extensionPos); + std::transform(extension.begin(), extension.end(), extension.begin(), tolower); + + if (extension == ".gset") + return loadGeomSet(ctx, filepath); + if (extension == ".obj") + return loadMesh(ctx, filepath); + + return false; +} + +bool InputGeom::saveGeomSet(const BuildSettings* settings) { if (!m_mesh) return false; - FILE* fp = fopen(filepath, "w"); + // Change extension + std::string filepath = m_mesh->getFileName(); + size_t extPos = filepath.find_last_of('.'); + if (extPos != std::string::npos) + filepath = filepath.substr(0, extPos); + + filepath += ".gset"; + + FILE* fp = fopen(filepath.c_str(), "w"); if (!fp) return false; // Store mesh filename. - fprintf(fp, "f %s\n", m_mesh->getFileName()); + fprintf(fp, "f %s\n", m_mesh->getFileName().c_str()); + + // Store settings if any + if (settings) + { + fprintf(fp, + "s %f %f %f %f %f %f %f %f %f %f %f %f %f %d %f %f %f %f %f %f %f\n", + settings->cellSize, + settings->cellHeight, + settings->agentHeight, + settings->agentRadius, + settings->agentMaxClimb, + settings->agentMaxSlope, + settings->regionMinSize, + settings->regionMergeSize, + settings->edgeMaxLen, + settings->edgeMaxError, + settings->vertsPerPoly, + settings->detailSampleDist, + settings->detailSampleMaxError, + settings->partitionType, + settings->navMeshBMin[0], + settings->navMeshBMin[1], + settings->navMeshBMin[2], + settings->navMeshBMax[0], + settings->navMeshBMax[1], + settings->navMeshBMax[2], + settings->tileSize); + } // Store off-mesh links. for (int i = 0; i < m_offMeshConCount; ++i) diff --git a/RecastDemo/Source/MeshLoaderObj.cpp b/RecastDemo/Source/MeshLoaderObj.cpp index 9c50047..66f4d7f 100644 --- a/RecastDemo/Source/MeshLoaderObj.cpp +++ b/RecastDemo/Source/MeshLoaderObj.cpp @@ -19,7 +19,7 @@ #include "MeshLoaderObj.h" #include #include -#include +#include #define _USE_MATH_DEFINES #include @@ -135,10 +135,10 @@ static int parseFace(char* row, int* data, int n, int vcnt) return j; } -bool rcMeshLoaderObj::load(const char* filename) +bool rcMeshLoaderObj::load(const std::string& filename) { char* buf = 0; - FILE* fp = fopen(filename, "rb"); + FILE* fp = fopen(filename.c_str(), "rb"); if (!fp) return false; fseek(fp, 0, SEEK_END); @@ -226,8 +226,6 @@ bool rcMeshLoaderObj::load(const char* filename) } } - strncpy(m_filename, filename, sizeof(m_filename)); - m_filename[sizeof(m_filename)-1] = '\0'; - + m_filename = filename; return true; } diff --git a/RecastDemo/Source/Sample.cpp b/RecastDemo/Source/Sample.cpp index 4e01528..3046e8e 100644 --- a/RecastDemo/Source/Sample.cpp +++ b/RecastDemo/Source/Sample.cpp @@ -105,19 +105,45 @@ void Sample::handleRenderOverlay(double* /*proj*/, double* /*model*/, int* /*vie void Sample::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; + } } -const float* Sample::getBoundsMin() +void Sample::collectSettings(BuildSettings& settings) { - if (!m_geom) return 0; - return m_geom->getMeshBoundsMin(); + 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; } -const float* Sample::getBoundsMax() -{ - if (!m_geom) return 0; - return m_geom->getMeshBoundsMax(); -} void Sample::resetCommonSettings() { @@ -145,8 +171,8 @@ void Sample::handleCommonSettings() if (m_geom) { - const float* bmin = m_geom->getMeshBoundsMin(); - const float* bmax = m_geom->getMeshBoundsMax(); + 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]; diff --git a/RecastDemo/Source/Sample_SoloMesh.cpp b/RecastDemo/Source/Sample_SoloMesh.cpp index 1fd2cc9..1e5b75d 100644 --- a/RecastDemo/Source/Sample_SoloMesh.cpp +++ b/RecastDemo/Source/Sample_SoloMesh.cpp @@ -235,8 +235,8 @@ void Sample_SoloMesh::handleRender() glDepthMask(GL_FALSE); // Draw bounds - const float* bmin = m_geom->getMeshBoundsMin(); - const float* bmax = m_geom->getMeshBoundsMax(); + const float* bmin = m_geom->getNavMeshBoundsMin(); + const float* bmax = m_geom->getNavMeshBoundsMax(); duDebugDrawBoxWire(&dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f); dd.begin(DU_DRAW_POINTS, 5.0f); dd.vertex(bmin[0],bmin[1],bmin[2],duRGBA(255,255,255,128)); @@ -362,8 +362,8 @@ bool Sample_SoloMesh::handleBuild() cleanup(); - const float* bmin = m_geom->getMeshBoundsMin(); - const float* bmax = m_geom->getMeshBoundsMax(); + const float* bmin = m_geom->getNavMeshBoundsMin(); + const float* bmax = m_geom->getNavMeshBoundsMax(); const float* verts = m_geom->getMesh()->getVerts(); const int nverts = m_geom->getMesh()->getVertCount(); const int* tris = m_geom->getMesh()->getTris(); diff --git a/RecastDemo/Source/Sample_TempObstacles.cpp b/RecastDemo/Source/Sample_TempObstacles.cpp index 0594cc3..a109d1b 100644 --- a/RecastDemo/Source/Sample_TempObstacles.cpp +++ b/RecastDemo/Source/Sample_TempObstacles.cpp @@ -866,8 +866,8 @@ void Sample_TempObstacles::handleSettings() int gridSize = 1; if (m_geom) { - const float* bmin = m_geom->getMeshBoundsMin(); - const float* bmax = m_geom->getMeshBoundsMax(); + 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); @@ -1059,8 +1059,8 @@ void Sample_TempObstacles::handleRender() glDepthMask(GL_FALSE); // Draw bounds - const float* bmin = m_geom->getMeshBoundsMin(); - const float* bmax = m_geom->getMeshBoundsMax(); + const float* bmin = m_geom->getNavMeshBoundsMin(); + const float* bmax = m_geom->getNavMeshBoundsMax(); duDebugDrawBoxWire(&dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f); // Tiling grid. @@ -1208,8 +1208,8 @@ bool Sample_TempObstacles::handleBuild() m_tmproc->init(m_geom); // Init cache - const float* bmin = m_geom->getMeshBoundsMin(); - const float* bmax = m_geom->getMeshBoundsMax(); + const float* bmin = m_geom->getNavMeshBoundsMin(); + const float* bmax = m_geom->getNavMeshBoundsMax(); int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); const int ts = (int)m_tileSize; @@ -1280,7 +1280,7 @@ bool Sample_TempObstacles::handleBuild() dtNavMeshParams params; memset(¶ms, 0, sizeof(params)); - rcVcopy(params.orig, m_geom->getMeshBoundsMin()); + rcVcopy(params.orig, bmin); params.tileWidth = m_tileSize*m_cellSize; params.tileHeight = m_tileSize*m_cellSize; params.maxTiles = m_maxTiles; @@ -1380,7 +1380,7 @@ void Sample_TempObstacles::getTilePos(const float* pos, int& tx, int& ty) { if (!m_geom) return; - const float* bmin = m_geom->getMeshBoundsMin(); + const float* bmin = m_geom->getNavMeshBoundsMin(); const float ts = m_tileSize*m_cellSize; tx = (int)((pos[0] - bmin[0]) / ts); diff --git a/RecastDemo/Source/Sample_TileMesh.cpp b/RecastDemo/Source/Sample_TileMesh.cpp index 8794586..675726a 100644 --- a/RecastDemo/Source/Sample_TileMesh.cpp +++ b/RecastDemo/Source/Sample_TileMesh.cpp @@ -195,8 +195,8 @@ Sample_TileMesh::Sample_TileMesh() : m_tileTriCount(0) { resetCommonSettings(); - memset(m_tileBmin, 0, sizeof(m_tileBmin)); - memset(m_tileBmax, 0, sizeof(m_tileBmax)); + memset(m_lastBuiltTileBmin, 0, sizeof(m_lastBuiltTileBmin)); + memset(m_lastBuiltTileBmax, 0, sizeof(m_lastBuiltTileBmax)); setTool(new NavMeshTileTool); } @@ -359,10 +359,10 @@ void Sample_TileMesh::handleSettings() if (m_geom) { - const float* bmin = m_geom->getMeshBoundsMin(); - const float* bmax = m_geom->getMeshBoundsMax(); char text[64]; 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 = (int)m_tileSize; const int tw = (gw + ts-1) / ts; @@ -561,8 +561,8 @@ void Sample_TileMesh::handleRender() glDepthMask(GL_FALSE); // Draw bounds - const float* bmin = m_geom->getMeshBoundsMin(); - const float* bmax = m_geom->getMeshBoundsMax(); + const float* bmin = m_geom->getNavMeshBoundsMin(); + const float* bmax = m_geom->getNavMeshBoundsMax(); duDebugDrawBoxWire(&dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f); // Tiling grid. @@ -574,8 +574,8 @@ void Sample_TileMesh::handleRender() duDebugDrawGridXZ(&dd, bmin[0],bmin[1],bmin[2], tw,th, s, duRGBA(0,0,0,64), 1.0f); // Draw active tile - duDebugDrawBoxWire(&dd, m_tileBmin[0],m_tileBmin[1],m_tileBmin[2], - m_tileBmax[0],m_tileBmax[1],m_tileBmax[2], m_tileCol, 1.0f); + duDebugDrawBoxWire(&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 && (m_drawMode == DRAWMODE_NAVMESH || @@ -674,7 +674,7 @@ void Sample_TileMesh::handleRenderOverlay(double* proj, double* model, int* view GLdouble x, y, z; // Draw start and end point labels - if (m_tileBuildTime > 0.0f && gluProject((GLdouble)(m_tileBmin[0]+m_tileBmax[0])/2, (GLdouble)(m_tileBmin[1]+m_tileBmax[1])/2, (GLdouble)(m_tileBmin[2]+m_tileBmax[2])/2, + if (m_tileBuildTime > 0.0f && gluProject((GLdouble)(m_lastBuiltTileBmin[0]+m_lastBuiltTileBmax[0])/2, (GLdouble)(m_lastBuiltTileBmin[1]+m_lastBuiltTileBmax[1])/2, (GLdouble)(m_lastBuiltTileBmin[2]+m_lastBuiltTileBmax[2])/2, model, proj, view, &x, &y, &z)) { char text[32]; @@ -687,10 +687,14 @@ void Sample_TileMesh::handleRenderOverlay(double* proj, double* model, int* view renderOverlayToolStates(proj, model, view); } -void Sample_TileMesh::handleMeshChanged(class InputGeom* geom) +void Sample_TileMesh::handleMeshChanged(InputGeom* geom) { Sample::handleMeshChanged(geom); + const BuildSettings* buildSettings = geom->getBuildSettings(); + if (buildSettings && buildSettings->tileSize > 0) + m_tileSize = buildSettings->tileSize; + cleanup(); dtFreeNavMesh(m_navMesh); @@ -723,7 +727,7 @@ bool Sample_TileMesh::handleBuild() } dtNavMeshParams params; - rcVcopy(params.orig, m_geom->getMeshBoundsMin()); + rcVcopy(params.orig, m_geom->getNavMeshBoundsMin()); params.tileWidth = m_tileSize*m_cellSize; params.tileHeight = m_tileSize*m_cellSize; params.maxTiles = m_maxTiles; @@ -755,32 +759,39 @@ bool Sample_TileMesh::handleBuild() return true; } +void Sample_TileMesh::collectSettings(BuildSettings& settings) +{ + Sample::collectSettings(settings); + + settings.tileSize = m_tileSize; +} + void Sample_TileMesh::buildTile(const float* pos) { if (!m_geom) return; if (!m_navMesh) return; - const float* bmin = m_geom->getMeshBoundsMin(); - const float* bmax = m_geom->getMeshBoundsMax(); + const float* bmin = m_geom->getNavMeshBoundsMin(); + const float* bmax = m_geom->getNavMeshBoundsMax(); const float ts = m_tileSize*m_cellSize; const int tx = (int)((pos[0] - bmin[0]) / ts); const int ty = (int)((pos[2] - bmin[2]) / ts); - m_tileBmin[0] = bmin[0] + tx*ts; - m_tileBmin[1] = bmin[1]; - m_tileBmin[2] = bmin[2] + ty*ts; + m_lastBuiltTileBmin[0] = bmin[0] + tx*ts; + m_lastBuiltTileBmin[1] = bmin[1]; + m_lastBuiltTileBmin[2] = bmin[2] + ty*ts; - m_tileBmax[0] = bmin[0] + (tx+1)*ts; - m_tileBmax[1] = bmax[1]; - m_tileBmax[2] = bmin[2] + (ty+1)*ts; + m_lastBuiltTileBmax[0] = bmin[0] + (tx+1)*ts; + m_lastBuiltTileBmax[1] = bmax[1]; + m_lastBuiltTileBmax[2] = bmin[2] + (ty+1)*ts; m_tileCol = duRGBA(255,255,255,64); m_ctx->resetLog(); int dataSize = 0; - unsigned char* data = buildTileMesh(tx, ty, m_tileBmin, m_tileBmax, dataSize); + unsigned char* data = buildTileMesh(tx, ty, m_lastBuiltTileBmin, m_lastBuiltTileBmax, dataSize); // Remove any previous data (navmesh owns and deletes the data). m_navMesh->removeTile(m_navMesh->getTileRefAt(tx,ty,0),0,0); @@ -801,7 +812,7 @@ void Sample_TileMesh::getTilePos(const float* pos, int& tx, int& ty) { if (!m_geom) return; - const float* bmin = m_geom->getMeshBoundsMin(); + const float* bmin = m_geom->getNavMeshBoundsMin(); const float ts = m_tileSize*m_cellSize; tx = (int)((pos[0] - bmin[0]) / ts); @@ -813,20 +824,20 @@ void Sample_TileMesh::removeTile(const float* pos) if (!m_geom) return; if (!m_navMesh) return; - const float* bmin = m_geom->getMeshBoundsMin(); - const float* bmax = m_geom->getMeshBoundsMax(); + const float* bmin = m_geom->getNavMeshBoundsMin(); + const float* bmax = m_geom->getNavMeshBoundsMax(); const float ts = m_tileSize*m_cellSize; const int tx = (int)((pos[0] - bmin[0]) / ts); const int ty = (int)((pos[2] - bmin[2]) / ts); - m_tileBmin[0] = bmin[0] + tx*ts; - m_tileBmin[1] = bmin[1]; - m_tileBmin[2] = bmin[2] + ty*ts; + m_lastBuiltTileBmin[0] = bmin[0] + tx*ts; + m_lastBuiltTileBmin[1] = bmin[1]; + m_lastBuiltTileBmin[2] = bmin[2] + ty*ts; - m_tileBmax[0] = bmin[0] + (tx+1)*ts; - m_tileBmax[1] = bmax[1]; - m_tileBmax[2] = bmin[2] + (ty+1)*ts; + m_lastBuiltTileBmax[0] = bmin[0] + (tx+1)*ts; + m_lastBuiltTileBmax[1] = bmax[1]; + m_lastBuiltTileBmax[2] = bmin[2] + (ty+1)*ts; m_tileCol = duRGBA(128,32,16,64); @@ -838,8 +849,8 @@ void Sample_TileMesh::buildAllTiles() if (!m_geom) return; if (!m_navMesh) return; - const float* bmin = m_geom->getMeshBoundsMin(); - const float* bmax = m_geom->getMeshBoundsMax(); + const float* bmin = m_geom->getNavMeshBoundsMin(); + const float* bmax = m_geom->getNavMeshBoundsMax(); int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); const int ts = (int)m_tileSize; @@ -855,16 +866,16 @@ void Sample_TileMesh::buildAllTiles() { for (int x = 0; x < tw; ++x) { - m_tileBmin[0] = bmin[0] + x*tcs; - m_tileBmin[1] = bmin[1]; - m_tileBmin[2] = bmin[2] + y*tcs; + m_lastBuiltTileBmin[0] = bmin[0] + x*tcs; + m_lastBuiltTileBmin[1] = bmin[1]; + m_lastBuiltTileBmin[2] = bmin[2] + y*tcs; - m_tileBmax[0] = bmin[0] + (x+1)*tcs; - m_tileBmax[1] = bmax[1]; - m_tileBmax[2] = bmin[2] + (y+1)*tcs; + m_lastBuiltTileBmax[0] = bmin[0] + (x+1)*tcs; + m_lastBuiltTileBmax[1] = bmax[1]; + m_lastBuiltTileBmax[2] = bmin[2] + (y+1)*tcs; int dataSize = 0; - unsigned char* data = buildTileMesh(x, y, m_tileBmin, m_tileBmax, dataSize); + unsigned char* data = buildTileMesh(x, y, m_lastBuiltTileBmin, m_lastBuiltTileBmax, dataSize); if (data) { // Remove any previous data (navmesh owns and deletes the data). @@ -886,8 +897,11 @@ void Sample_TileMesh::buildAllTiles() void Sample_TileMesh::removeAllTiles() { - const float* bmin = m_geom->getMeshBoundsMin(); - const float* bmax = m_geom->getMeshBoundsMax(); + if (!m_geom || !m_navMesh) + return; + + const float* bmin = m_geom->getNavMeshBoundsMin(); + const float* bmax = m_geom->getNavMeshBoundsMax(); int gw = 0, gh = 0; rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh); const int ts = (int)m_tileSize; @@ -1106,14 +1120,14 @@ unsigned char* Sample_TileMesh::buildTileMesh(const int tx, const int ty, const if (!rcBuildDistanceField(m_ctx, *m_chf)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build distance field."); - return false; + return 0; } // Partition the walkable surface into simple regions without holes. if (!rcBuildRegions(m_ctx, *m_chf, m_cfg.borderSize, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build watershed regions."); - return false; + return 0; } } else if (m_partitionType == SAMPLE_PARTITION_MONOTONE) @@ -1123,7 +1137,7 @@ unsigned char* Sample_TileMesh::buildTileMesh(const int tx, const int ty, const if (!rcBuildRegionsMonotone(m_ctx, *m_chf, m_cfg.borderSize, m_cfg.minRegionArea, m_cfg.mergeRegionArea)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build monotone regions."); - return false; + return 0; } } else // SAMPLE_PARTITION_LAYERS @@ -1132,7 +1146,7 @@ unsigned char* Sample_TileMesh::buildTileMesh(const int tx, const int ty, const if (!rcBuildLayerRegions(m_ctx, *m_chf, m_cfg.borderSize, m_cfg.minRegionArea)) { m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build layer regions."); - return false; + return 0; } } diff --git a/RecastDemo/Source/TestCase.cpp b/RecastDemo/Source/TestCase.cpp index f625171..7c447a3 100644 --- a/RecastDemo/Source/TestCase.cpp +++ b/RecastDemo/Source/TestCase.cpp @@ -88,18 +88,18 @@ static char* parseRow(char* buf, char* bufEnd, char* row, int len) return buf; } -static void copyName(char* dst, const char* src) +static void copyName(std::string& dst, const char* src) { // Skip white spaces while (*src && isspace(*src)) src++; - strcpy(dst, src); + dst = src; } -bool TestCase::load(const char* filePath) +bool TestCase::load(const std::string& filePath) { char* buf = 0; - FILE* fp = fopen(filePath, "rb"); + FILE* fp = fopen(filePath.c_str(), "rb"); if (!fp) return false; fseek(fp, 0, SEEK_END); diff --git a/RecastDemo/Source/main.cpp b/RecastDemo/Source/main.cpp index e2a3052..bf2e663 100644 --- a/RecastDemo/Source/main.cpp +++ b/RecastDemo/Source/main.cpp @@ -56,7 +56,7 @@ using std::vector; struct SampleItem { Sample* (*create)(); - const char* name; + const string name; }; Sample* createSolo() { return new Sample_SoloMesh(); } Sample* createTile() { return new Sample_TileMesh(); } @@ -167,10 +167,11 @@ int main(int /*argc*/, char** /*argv*/) int logScroll = 0; int toolsScroll = 0; - char sampleName[64] = "Choose Sample..."; + string sampleName = "Choose Sample..."; vector files; - char meshName[128] = "Choose Mesh..."; + const string meshesFolder = "Meshes"; + string meshName = "Choose Mesh..."; float markerPosition[3] = {0, 0, 0}; bool markerPositionSet = false; @@ -180,6 +181,8 @@ int main(int /*argc*/, char** /*argv*/) InputGeom* geom = 0; Sample* sample = 0; + + const string testCasesFolder = "TestCases"; TestCase* test = 0; BuildContext ctx; @@ -219,7 +222,7 @@ int main(int /*argc*/, char** /*argv*/) showLevels = false; showSample = false; showTestCases = true; - scanDirectory("TestCases", ".txt", files); + scanDirectory(testCasesFolder, ".txt", files); } else if (event.key.keysym.sym == SDLK_TAB) { @@ -237,56 +240,18 @@ int main(int /*argc*/, char** /*argv*/) } else if (event.key.keysym.sym == SDLK_9) { - if (geom) - geom->save("geomset.txt"); - } - else if (event.key.keysym.sym == SDLK_0) - { - delete geom; - geom = new InputGeom; - if (!geom || !geom->load(&ctx, "geomset.txt")) - { - delete geom; - geom = 0; - - showLog = true; - logScroll = 0; - ctx.dumpLog("Geom load log %s:", meshName); - } if (sample && geom) { - sample->handleMeshChanged(geom); - } - - if (geom || sample) - { - const float* bmin = 0; - const float* bmax = 0; - if (sample) - { - bmin = sample->getBoundsMin(); - bmax = sample->getBoundsMax(); - } - else if (geom) - { - bmin = geom->getMeshBoundsMin(); - bmax = geom->getMeshBoundsMax(); - } - // Reset camera and fog to match the mesh bounds. - if (bmin && bmax) - { - camr = sqrtf(rcSqr(bmax[0] - bmin[0]) + - rcSqr(bmax[1] - bmin[1]) + - rcSqr(bmax[2] - bmin[2])) / 2; - cameraPos[0] = (bmax[0] + bmin[0]) / 2 + camr; - cameraPos[1] = (bmax[1] + bmin[1]) / 2 + camr; - cameraPos[2] = (bmax[2] + bmin[2]) / 2 + camr; - camr *= 3; - } - cameraEulers[0] = 45; - cameraEulers[1] = -45; - glFogf(GL_FOG_START, camr * 0.2f); - glFogf(GL_FOG_END, camr * 1.25f); + string savePath = meshesFolder + "/"; + BuildSettings settings; + memset(&settings, 0, sizeof(settings)); + + rcVcopy(settings.navMeshBMin, geom->getNavMeshBoundsMin()); + rcVcopy(settings.navMeshBMax, geom->getNavMeshBoundsMax()); + + sample->collectSettings(settings); + + geom->saveGeomSet(&settings); } } else if (event.key.keysym.sym == SDLK_RIGHT) @@ -577,7 +542,7 @@ int main(int /*argc*/, char** /*argv*/) imguiSeparator(); imguiLabel("Sample"); - if (imguiButton(sampleName)) + if (imguiButton(sampleName.c_str())) { if (showSample) { @@ -593,7 +558,7 @@ int main(int /*argc*/, char** /*argv*/) imguiSeparator(); imguiLabel("Input Mesh"); - if (imguiButton(meshName)) + if (imguiButton(meshName.c_str())) { if (showLevels) { @@ -604,7 +569,8 @@ int main(int /*argc*/, char** /*argv*/) showSample = false; showTestCases = false; showLevels = true; - scanDirectory("Meshes", ".obj", files); + scanDirectory(meshesFolder, ".obj", files); + scanDirectoryAppend(meshesFolder, ".gset", files); } } if (geom) @@ -631,7 +597,7 @@ int main(int /*argc*/, char** /*argv*/) showLog = true; logScroll = 0; } - ctx.dumpLog("Build log %s:", meshName); + ctx.dumpLog("Build log %s:", meshName.c_str()); // Clear test. delete test; @@ -660,11 +626,11 @@ int main(int /*argc*/, char** /*argv*/) Sample* newSample = 0; for (int i = 0; i < g_nsamples; ++i) { - if (imguiItem(g_samples[i].name)) + if (imguiItem(g_samples[i].name.c_str())) { newSample = g_samples[i].create(); if (newSample) - strcpy(sampleName, g_samples[i].name); + sampleName = g_samples[i].name; } } if (newSample) @@ -683,15 +649,10 @@ int main(int /*argc*/, char** /*argv*/) { const float* bmin = 0; const float* bmax = 0; - if (sample) + if (geom) { - bmin = sample->getBoundsMin(); - bmax = sample->getBoundsMax(); - } - else if (geom) - { - bmin = geom->getMeshBoundsMin(); - bmax = geom->getMeshBoundsMax(); + bmin = geom->getNavMeshBoundsMin(); + bmax = geom->getNavMeshBoundsMax(); } // Reset camera and fog to match the mesh bounds. if (bmin && bmax) @@ -733,26 +694,23 @@ int main(int /*argc*/, char** /*argv*/) if (levelToLoad != filesEnd) { - strncpy(meshName, levelToLoad->c_str(), sizeof(meshName)); - meshName[sizeof(meshName)-1] = '\0'; + meshName = *levelToLoad; showLevels = false; delete geom; geom = 0; - char path[256]; - strcpy(path, "Meshes/"); - strcat(path, meshName); + string path = meshesFolder + "/" + meshName; geom = new InputGeom; - if (!geom || !geom->loadMesh(&ctx, path)) + if (!geom->load(&ctx, path)) { delete geom; geom = 0; showLog = true; logScroll = 0; - ctx.dumpLog("Geom load log %s:", meshName); + ctx.dumpLog("Geom load log %s:", meshName.c_str()); } if (sample && geom) { @@ -763,15 +721,10 @@ int main(int /*argc*/, char** /*argv*/) { const float* bmin = 0; const float* bmax = 0; - if (sample) + if (geom) { - bmin = sample->getBoundsMin(); - bmax = sample->getBoundsMax(); - } - else if (geom) - { - bmin = geom->getMeshBoundsMin(); - bmax = geom->getMeshBoundsMax(); + bmin = geom->getNavMeshBoundsMin(); + bmax = geom->getNavMeshBoundsMax(); } // Reset camera and fog to match the mesh bounds. if (bmin && bmax) @@ -815,9 +768,7 @@ int main(int /*argc*/, char** /*argv*/) if (testToLoad != filesEnd) { - char path[256]; - strcpy(path, "TestCases/"); - strcat(path, testToLoad->c_str()); + string path = testCasesFolder + "/" + *testToLoad; test = new TestCase; if (test) { @@ -832,10 +783,11 @@ int main(int /*argc*/, char** /*argv*/) Sample* newSample = 0; for (int i = 0; i < g_nsamples; ++i) { - if (strcmp(g_samples[i].name, test->getSampleName()) == 0) + if (g_samples[i].name == test->getSampleName()) { newSample = g_samples[i].create(); - if (newSample) strcpy(sampleName, g_samples[i].name); + if (newSample) + sampleName = g_samples[i].name; } } if (newSample) @@ -847,23 +799,21 @@ int main(int /*argc*/, char** /*argv*/) } // Load geom. - strcpy(meshName, test->getGeomFileName()); - meshName[sizeof(meshName)-1] = '\0'; + meshName = test->getGeomFileName(); delete geom; geom = 0; - strcpy(path, "Meshes/"); - strcat(path, meshName); + path = meshesFolder + "/" + meshName; geom = new InputGeom; - if (!geom || !geom->loadMesh(&ctx, path)) + if (!geom || !geom->load(&ctx, path)) { delete geom; geom = 0; showLog = true; logScroll = 0; - ctx.dumpLog("Geom load log %s:", meshName); + ctx.dumpLog("Geom load log %s:", meshName.c_str()); } if (sample && geom) { @@ -877,22 +827,17 @@ int main(int /*argc*/, char** /*argv*/) ctx.resetLog(); if (sample && !sample->handleBuild()) { - ctx.dumpLog("Build log %s:", meshName); + ctx.dumpLog("Build log %s:", meshName.c_str()); } if (geom || sample) { const float* bmin = 0; const float* bmax = 0; - if (sample) + if (geom) { - bmin = sample->getBoundsMin(); - bmax = sample->getBoundsMax(); - } - else if (geom) - { - bmin = geom->getMeshBoundsMin(); - bmax = geom->getMeshBoundsMax(); + bmin = geom->getNavMeshBoundsMin(); + bmax = geom->getNavMeshBoundsMax(); } // Reset camera and fog to match the mesh bounds. if (bmin && bmax)