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)