From 3d88447968ffdbdfc4b9e94a198d186b852c4495 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sat, 26 Jan 2019 16:01:35 +1100 Subject: [PATCH] fix race condition where navmesh export would start before object is exported --- src/tools/pcb_reader/CMakeLists.txt | 5 +- src/tools/pcb_reader/exportmgr.h | 22 +- src/tools/pcb_reader/main.cpp | 2 +- .../pcb_reader/nav/TiledNavmeshGenerator.h | 159 +++++++++ .../pcb_reader/nav/ext/ChunkyTriMesh.cpp | 315 ++++++++++++++++++ src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h | 59 ++++ .../pcb_reader/nav/ext/MeshLoaderObj.cpp | 245 ++++++++++++++ src/tools/pcb_reader/nav/ext/MeshLoaderObj.h | 56 ++++ src/tools/pcb_reader/navmesh_exporter.h | 32 +- 9 files changed, 862 insertions(+), 33 deletions(-) create mode 100644 src/tools/pcb_reader/nav/TiledNavmeshGenerator.h create mode 100644 src/tools/pcb_reader/nav/ext/ChunkyTriMesh.cpp create mode 100644 src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h create mode 100644 src/tools/pcb_reader/nav/ext/MeshLoaderObj.cpp create mode 100644 src/tools/pcb_reader/nav/ext/MeshLoaderObj.h diff --git a/src/tools/pcb_reader/CMakeLists.txt b/src/tools/pcb_reader/CMakeLists.txt index 96b4af4e..4381ace7 100644 --- a/src/tools/pcb_reader/CMakeLists.txt +++ b/src/tools/pcb_reader/CMakeLists.txt @@ -3,7 +3,10 @@ cmake_policy(SET CMP0015 NEW) project(Tool_pcb_reader2) file(GLOB SERVER_PUBLIC_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*") -file(GLOB SERVER_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.c*") +file(GLOB SERVER_SOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + *.c* + nav/*.c* + nav/ext/*.c*) add_executable(pcb_reader2 ${SERVER_PUBLIC_INCLUDE_FILES} ${SERVER_SOURCE_FILES}) diff --git a/src/tools/pcb_reader/exportmgr.h b/src/tools/pcb_reader/exportmgr.h index 656e15f7..c448b5d2 100644 --- a/src/tools/pcb_reader/exportmgr.h +++ b/src/tools/pcb_reader/exportmgr.h @@ -20,14 +20,14 @@ public: void exportZone(const ExportedZone& zone, ExportFileType exportFileTypes) { - if( exportFileTypes & ExportFileType::WavefrontObj ) + m_threadpool.queue( [zone, exportFileTypes]() { - m_threadpool.queue( [zone](){ ObjExporter::exportZone( zone ); } ); - } - if( exportFileTypes & ExportFileType::Navmesh ) - { - m_threadpool.queue( [zone](){ NavmeshExporter::exportZone( zone ); } ); - } + if( exportFileTypes & ExportFileType::WavefrontObj ) + ObjExporter::exportZone( zone ); + + if( exportFileTypes & ExportFileType::Navmesh ) + NavmeshExporter::exportZone( zone ); + } ); } void exportGroup( const std::string& zoneName, const ExportedGroup& group, ExportFileType exportFileTypes ) @@ -36,10 +36,10 @@ public: { m_threadpool.queue( [zoneName, group](){ ObjExporter::exportGroup( zoneName, group ); } ); } - if( exportFileTypes & ExportFileType::Navmesh ) - { - m_threadpool.queue( [zoneName, group](){ NavmeshExporter::exportGroup( zoneName, group ); } ); - } +// if( exportFileTypes & ExportFileType::Navmesh ) +// { +// m_threadpool.queue( [zoneName, group](){ NavmeshExporter::exportGroup( zoneName, group ); } ); +// } } void waitForTasks() diff --git a/src/tools/pcb_reader/main.cpp b/src/tools/pcb_reader/main.cpp index 60befa41..ee3c9a5c 100644 --- a/src/tools/pcb_reader/main.cpp +++ b/src/tools/pcb_reader/main.cpp @@ -140,7 +140,7 @@ int main( int argc, char* argv[] ) } catch( std::exception& e ) { - printf( "Unable to initialise EXD! Usage: pcb_reader \"path/to/FINAL FANTASY XIV - A REALM REBORN/game/sqpack\" [--no-obj, --dump-all, --navmesh]" ); + printf( "Unable to initialise EXD!\n Usage: pcb_reader \"path/to/FINAL FANTASY XIV - A REALM REBORN/game/sqpack\" [--no-obj, --dump-all, --navmesh]\n" ); return -1; } ExportMgr exportMgr; diff --git a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h new file mode 100644 index 00000000..93d3051e --- /dev/null +++ b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h @@ -0,0 +1,159 @@ +#ifndef SAPPHIRE_TILEDNAVMESHGENERATOR_H +#define SAPPHIRE_TILEDNAVMESHGENERATOR_H + +#include +#include +#include + +#include "ext/MeshLoaderObj.h" +#include "ext/ChunkyTriMesh.h" + +#include "recastnavigation/Detour/Include/DetourNavMesh.h" +#include "recastnavigation/Detour/Include/DetourNavMeshQuery.h" +#include "recastnavigation/Recast/Include/Recast.h" + +namespace fs = std::experimental::filesystem; + +class TiledNavmeshGenerator +{ +private: + rcMeshLoaderObj* m_mesh; + rcChunkyTriMesh* m_chunkyMesh; + + dtNavMesh* m_navMesh; + dtNavMeshQuery* m_navQuery; + + + float m_meshBMin[ 3 ]; + float m_meshBMax[ 3 ]; + + float m_tileSize = 160.f; + float m_cellSize = 0.2f; + + int m_maxTiles = 0; + int m_maxPolysPerTile = 0; + + inline unsigned int nextPow2( uint32_t v ) + { + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; + } + + inline unsigned int ilog2( uint32_t v ) + { + uint32_t r; + uint32_t shift; + r = (v > 0xffff) << 4; v >>= r; + shift = (v > 0xff) << 3; v >>= shift; r |= shift; + shift = (v > 0xf) << 2; v >>= shift; r |= shift; + shift = (v > 0x3) << 1; v >>= shift; r |= shift; + r |= (v >> 1); + return r; + } + + +public: + explicit TiledNavmeshGenerator( const std::string& path ) + { + if( !fs::exists( path ) ) + throw std::runtime_error( "what" ); + + printf( "[Navmesh] loading obj: %s\n", path.c_str() ); + + m_mesh = new rcMeshLoaderObj; + assert( m_mesh ); + + if( !m_mesh->load( path ) ) + { + printf( "[Navmesh] Failed to allocate rcMeshLoaderObj\n" ); + return; + } + + rcCalcBounds( m_mesh->getVerts(), m_mesh->getVertCount(), m_meshBMin, m_meshBMax ); + + m_chunkyMesh = new rcChunkyTriMesh; + assert( m_chunkyMesh ); + + if( !rcCreateChunkyTriMesh( m_mesh->getVerts(), m_mesh->getTris(), m_mesh->getTriCount(), 256, m_chunkyMesh ) ) + { + printf( "[Navmesh] buildTiledNavigation: Failed to build chunky mesh.\n" ); + return; + } + + printf( "[Navmesh] loaded obj, verts: %i tris: %i\n", m_mesh->getVertCount(), m_mesh->getTriCount() ); + + // todo: load some bullshit settings from exd + + int gw = 0, gh = 0; + rcCalcGridSize( m_meshBMin, m_meshBMax, m_cellSize, &gw, &gh ); + + auto ts = static_cast< uint32_t >( m_tileSize ); + const uint32_t tw = (gw + ts-1) / ts; + const uint32_t th = (gh + ts-1) / ts; + + printf( "[Navmesh] Tiles %d x %d\n", tw, th ); + + int tileBits = rcMin((int)ilog2(nextPow2(tw*th)), 14); + if (tileBits > 14) tileBits = 14; + int polyBits = 22 - tileBits; + m_maxTiles = 1 << tileBits; + m_maxPolysPerTile = 1 << polyBits; + + printf( "[Navmesh] Max Tiles: %d\tMax Polys: %d\n", m_maxTiles, m_maxPolysPerTile ); + } + + bool buildNavmesh() + { + assert( m_mesh ); + + m_navMesh = dtAllocNavMesh(); + if( !m_navMesh ) + { + printf( "[Navmesh] buildTiledNavigation: Could not allocate navmesh.\n" ); + return false; + } + + dtNavMeshParams params{}; + rcVcopy( params.orig, m_meshBMin ); + params.tileWidth = m_tileSize * m_cellSize; + params.tileHeight = m_tileSize * m_cellSize; + params.maxTiles = m_maxTiles; + params.maxPolys = m_maxPolysPerTile; + + dtStatus status; + + status = m_navMesh->init( ¶ms ); + if( dtStatusFailed( status ) ) + { + printf( "[Navigation] buildTiledNavigation: Could not init navmesh.\n" ); + return false; + } + + m_navQuery = dtAllocNavMeshQuery(); + assert( m_navQuery ); + + status = m_navQuery->init( m_navMesh, 2048 ); + if( dtStatusFailed( status ) ) + { + printf( "[Navigation] buildTiledNavigation: Could not init Detour navmesh query\n" ); + return false; + } + + } + + ~TiledNavmeshGenerator() + { + delete m_mesh; + delete m_chunkyMesh; + } + +}; + + +#endif //SAPPHIRE_TILEDNAVMESHGENERATOR_H diff --git a/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.cpp b/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.cpp new file mode 100644 index 00000000..2991d4ff --- /dev/null +++ b/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.cpp @@ -0,0 +1,315 @@ +// +// 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 "ChunkyTriMesh.h" +#include +#include +#include + +struct BoundsItem +{ + float bmin[2]; + float bmax[2]; + int i; +}; + +static int compareItemX(const void* va, const void* vb) +{ + const BoundsItem* a = (const BoundsItem*)va; + const BoundsItem* b = (const BoundsItem*)vb; + if (a->bmin[0] < b->bmin[0]) + return -1; + if (a->bmin[0] > b->bmin[0]) + return 1; + return 0; +} + +static int compareItemY(const void* va, const void* vb) +{ + const BoundsItem* a = (const BoundsItem*)va; + const BoundsItem* b = (const BoundsItem*)vb; + if (a->bmin[1] < b->bmin[1]) + return -1; + if (a->bmin[1] > b->bmin[1]) + return 1; + return 0; +} + +static void calcExtends(const BoundsItem* items, const int /*nitems*/, + const int imin, const int imax, + float* bmin, float* bmax) +{ + bmin[0] = items[imin].bmin[0]; + bmin[1] = items[imin].bmin[1]; + + bmax[0] = items[imin].bmax[0]; + bmax[1] = items[imin].bmax[1]; + + for (int i = imin+1; i < imax; ++i) + { + const BoundsItem& it = items[i]; + if (it.bmin[0] < bmin[0]) bmin[0] = it.bmin[0]; + if (it.bmin[1] < bmin[1]) bmin[1] = it.bmin[1]; + + if (it.bmax[0] > bmax[0]) bmax[0] = it.bmax[0]; + if (it.bmax[1] > bmax[1]) bmax[1] = it.bmax[1]; + } +} + +inline int longestAxis(float x, float y) +{ + return y > x ? 1 : 0; +} + +static void subdivide(BoundsItem* items, int nitems, int imin, int imax, int trisPerChunk, + int& curNode, rcChunkyTriMeshNode* nodes, const int maxNodes, + int& curTri, int* outTris, const int* inTris) +{ + int inum = imax - imin; + int icur = curNode; + + if (curNode > maxNodes) + return; + + rcChunkyTriMeshNode& node = nodes[curNode++]; + + if (inum <= trisPerChunk) + { + // Leaf + calcExtends(items, nitems, imin, imax, node.bmin, node.bmax); + + // Copy triangles. + node.i = curTri; + node.n = inum; + + for (int i = imin; i < imax; ++i) + { + const int* src = &inTris[items[i].i*3]; + int* dst = &outTris[curTri*3]; + curTri++; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + } + } + else + { + // Split + calcExtends(items, nitems, imin, imax, node.bmin, node.bmax); + + int axis = longestAxis(node.bmax[0] - node.bmin[0], + node.bmax[1] - node.bmin[1]); + + if (axis == 0) + { + // Sort along x-axis + qsort(items+imin, static_cast(inum), sizeof(BoundsItem), compareItemX); + } + else if (axis == 1) + { + // Sort along y-axis + qsort(items+imin, static_cast(inum), sizeof(BoundsItem), compareItemY); + } + + int isplit = imin+inum/2; + + // Left + subdivide(items, nitems, imin, isplit, trisPerChunk, curNode, nodes, maxNodes, curTri, outTris, inTris); + // Right + subdivide(items, nitems, isplit, imax, trisPerChunk, curNode, nodes, maxNodes, curTri, outTris, inTris); + + int iescape = curNode - icur; + // Negative index means escape. + node.i = -iescape; + } +} + +bool rcCreateChunkyTriMesh(const float* verts, const int* tris, int ntris, + int trisPerChunk, rcChunkyTriMesh* cm) +{ + int nchunks = (ntris + trisPerChunk-1) / trisPerChunk; + + cm->nodes = new rcChunkyTriMeshNode[nchunks*4]; + if (!cm->nodes) + return false; + + cm->tris = new int[ntris*3]; + if (!cm->tris) + return false; + + cm->ntris = ntris; + + // Build tree + BoundsItem* items = new BoundsItem[ntris]; + if (!items) + return false; + + for (int i = 0; i < ntris; i++) + { + const int* t = &tris[i*3]; + BoundsItem& it = items[i]; + it.i = i; + // Calc triangle XZ bounds. + it.bmin[0] = it.bmax[0] = verts[t[0]*3+0]; + it.bmin[1] = it.bmax[1] = verts[t[0]*3+2]; + for (int j = 1; j < 3; ++j) + { + const float* v = &verts[t[j]*3]; + if (v[0] < it.bmin[0]) it.bmin[0] = v[0]; + if (v[2] < it.bmin[1]) it.bmin[1] = v[2]; + + if (v[0] > it.bmax[0]) it.bmax[0] = v[0]; + if (v[2] > it.bmax[1]) it.bmax[1] = v[2]; + } + } + + int curTri = 0; + int curNode = 0; + subdivide(items, ntris, 0, ntris, trisPerChunk, curNode, cm->nodes, nchunks*4, curTri, cm->tris, tris); + + delete [] items; + + cm->nnodes = curNode; + + // Calc max tris per node. + cm->maxTrisPerChunk = 0; + for (int i = 0; i < cm->nnodes; ++i) + { + rcChunkyTriMeshNode& node = cm->nodes[i]; + const bool isLeaf = node.i >= 0; + if (!isLeaf) continue; + if (node.n > cm->maxTrisPerChunk) + cm->maxTrisPerChunk = node.n; + } + + return true; +} + + +inline bool checkOverlapRect(const float amin[2], const float amax[2], + const float bmin[2], const float bmax[2]) +{ + bool overlap = true; + overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap; + overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap; + return overlap; +} + +int rcGetChunksOverlappingRect(const rcChunkyTriMesh* cm, + float bmin[2], float bmax[2], + int* ids, const int maxIds) +{ + // Traverse tree + int i = 0; + int n = 0; + while (i < cm->nnodes) + { + const rcChunkyTriMeshNode* node = &cm->nodes[i]; + const bool overlap = checkOverlapRect(bmin, bmax, node->bmin, node->bmax); + const bool isLeafNode = node->i >= 0; + + if (isLeafNode && overlap) + { + if (n < maxIds) + { + ids[n] = i; + n++; + } + } + + if (overlap || isLeafNode) + i++; + else + { + const int escapeIndex = -node->i; + i += escapeIndex; + } + } + + return n; +} + + + +static bool checkOverlapSegment(const float p[2], const float q[2], + const float bmin[2], const float bmax[2]) +{ + static const float EPSILON = 1e-6f; + + float tmin = 0; + float tmax = 1; + float d[2]; + d[0] = q[0] - p[0]; + d[1] = q[1] - p[1]; + + for (int i = 0; i < 2; i++) + { + if (fabsf(d[i]) < EPSILON) + { + // Ray is parallel to slab. No hit if origin not within slab + if (p[i] < bmin[i] || p[i] > bmax[i]) + return false; + } + else + { + // Compute intersection t value of ray with near and far plane of slab + float ood = 1.0f / d[i]; + float t1 = (bmin[i] - p[i]) * ood; + float t2 = (bmax[i] - p[i]) * ood; + if (t1 > t2) { float tmp = t1; t1 = t2; t2 = tmp; } + if (t1 > tmin) tmin = t1; + if (t2 < tmax) tmax = t2; + if (tmin > tmax) return false; + } + } + return true; +} + +int rcGetChunksOverlappingSegment(const rcChunkyTriMesh* cm, + float p[2], float q[2], + int* ids, const int maxIds) +{ + // Traverse tree + int i = 0; + int n = 0; + while (i < cm->nnodes) + { + const rcChunkyTriMeshNode* node = &cm->nodes[i]; + const bool overlap = checkOverlapSegment(p, q, node->bmin, node->bmax); + const bool isLeafNode = node->i >= 0; + + if (isLeafNode && overlap) + { + if (n < maxIds) + { + ids[n] = i; + n++; + } + } + + if (overlap || isLeafNode) + i++; + else + { + const int escapeIndex = -node->i; + i += escapeIndex; + } + } + + return n; +} diff --git a/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h b/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h new file mode 100644 index 00000000..65849799 --- /dev/null +++ b/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h @@ -0,0 +1,59 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#ifndef CHUNKYTRIMESH_H +#define CHUNKYTRIMESH_H + +struct rcChunkyTriMeshNode +{ + float bmin[2]; + float bmax[2]; + int i; + int n; +}; + +struct rcChunkyTriMesh +{ + inline rcChunkyTriMesh() : nodes(0), nnodes(0), tris(0), ntris(0), maxTrisPerChunk(0) {}; + inline ~rcChunkyTriMesh() { delete [] nodes; delete [] tris; } + + rcChunkyTriMeshNode* nodes; + int nnodes; + int* tris; + int ntris; + int maxTrisPerChunk; + +private: + // Explicitly disabled copy constructor and copy assignment operator. + rcChunkyTriMesh(const rcChunkyTriMesh&); + rcChunkyTriMesh& operator=(const rcChunkyTriMesh&); +}; + +/// Creates partitioned triangle mesh (AABB tree), +/// where each node contains at max trisPerChunk triangles. +bool rcCreateChunkyTriMesh(const float* verts, const int* tris, int ntris, + int trisPerChunk, rcChunkyTriMesh* cm); + +/// Returns the chunk indices which overlap the input rectable. +int rcGetChunksOverlappingRect(const rcChunkyTriMesh* cm, float bmin[2], float bmax[2], int* ids, const int maxIds); + +/// Returns the chunk indices which overlap the input segment. +int rcGetChunksOverlappingSegment(const rcChunkyTriMesh* cm, float p[2], float q[2], int* ids, const int maxIds); + + +#endif // CHUNKYTRIMESH_H diff --git a/src/tools/pcb_reader/nav/ext/MeshLoaderObj.cpp b/src/tools/pcb_reader/nav/ext/MeshLoaderObj.cpp new file mode 100644 index 00000000..19047485 --- /dev/null +++ b/src/tools/pcb_reader/nav/ext/MeshLoaderObj.cpp @@ -0,0 +1,245 @@ +// +// 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 "MeshLoaderObj.h" +#include +#include +#include +#define _USE_MATH_DEFINES +#include + +rcMeshLoaderObj::rcMeshLoaderObj() : + m_scale(1.0f), + m_verts(0), + m_tris(0), + m_normals(0), + m_vertCount(0), + m_triCount(0) +{ +} + +rcMeshLoaderObj::~rcMeshLoaderObj() +{ + delete [] m_verts; + delete [] m_normals; + delete [] m_tris; +} + +void rcMeshLoaderObj::addVertex(float x, float y, float z, int& cap) +{ + if (m_vertCount+1 > cap) + { + cap = !cap ? 8 : cap*2; + float* nv = new float[cap*3]; + if (m_vertCount) + memcpy(nv, m_verts, m_vertCount*3*sizeof(float)); + delete [] m_verts; + m_verts = nv; + } + float* dst = &m_verts[m_vertCount*3]; + *dst++ = x*m_scale; + *dst++ = y*m_scale; + *dst++ = z*m_scale; + m_vertCount++; +} + +void rcMeshLoaderObj::addTriangle(int a, int b, int c, int& cap) +{ + if (m_triCount+1 > cap) + { + cap = !cap ? 8 : cap*2; + int* nv = new int[cap*3]; + if (m_triCount) + memcpy(nv, m_tris, m_triCount*3*sizeof(int)); + delete [] m_tris; + m_tris = nv; + } + int* dst = &m_tris[m_triCount*3]; + *dst++ = a; + *dst++ = b; + *dst++ = c; + m_triCount++; +} + +static char* parseRow(char* buf, char* bufEnd, char* row, int len) +{ + bool start = true; + bool done = false; + int n = 0; + while (!done && buf < bufEnd) + { + char c = *buf; + buf++; + // multirow + switch (c) + { + case '\\': + break; + case '\n': + if (start) break; + done = true; + break; + case '\r': + break; + case '\t': + case ' ': + if (start) break; + // else falls through + default: + start = false; + row[n++] = c; + if (n >= len-1) + done = true; + break; + } + } + row[n] = '\0'; + return buf; +} + +static int parseFace(char* row, int* data, int n, int vcnt) +{ + int j = 0; + while (*row != '\0') + { + // Skip initial white space + while (*row != '\0' && (*row == ' ' || *row == '\t')) + row++; + char* s = row; + // Find vertex delimiter and terminated the string there for conversion. + while (*row != '\0' && *row != ' ' && *row != '\t') + { + if (*row == '/') *row = '\0'; + row++; + } + if (*s == '\0') + continue; + int vi = atoi(s); + data[j++] = vi < 0 ? vi+vcnt : vi-1; + if (j >= n) return j; + } + return j; +} + +bool rcMeshLoaderObj::load(const std::string& filename) +{ + char* buf = 0; + FILE* fp = fopen(filename.c_str(), "rb"); + if (!fp) + return false; + if (fseek(fp, 0, SEEK_END) != 0) + { + fclose(fp); + return false; + } + long bufSize = ftell(fp); + if (bufSize < 0) + { + fclose(fp); + return false; + } + if (fseek(fp, 0, SEEK_SET) != 0) + { + fclose(fp); + return false; + } + buf = new char[bufSize]; + if (!buf) + { + fclose(fp); + return false; + } + size_t readLen = fread(buf, bufSize, 1, fp); + fclose(fp); + + if (readLen != 1) + { + delete[] buf; + return false; + } + + char* src = buf; + char* srcEnd = buf + bufSize; + char row[512]; + int face[32]; + float x,y,z; + int nv; + int vcap = 0; + int tcap = 0; + + while (src < srcEnd) + { + // Parse one row + row[0] = '\0'; + src = parseRow(src, srcEnd, row, sizeof(row)/sizeof(char)); + // Skip comments + if (row[0] == '#') continue; + if (row[0] == 'v' && row[1] != 'n' && row[1] != 't') + { + // Vertex pos + sscanf(row+1, "%f %f %f", &x, &y, &z); + addVertex(x, y, z, vcap); + } + if (row[0] == 'f') + { + // Faces + nv = parseFace(row+1, face, 32, m_vertCount); + for (int i = 2; i < nv; ++i) + { + const int a = face[0]; + const int b = face[i-1]; + const int c = face[i]; + if (a < 0 || a >= m_vertCount || b < 0 || b >= m_vertCount || c < 0 || c >= m_vertCount) + continue; + addTriangle(a, b, c, tcap); + } + } + } + + delete [] buf; + + // Calculate normals. + m_normals = new float[m_triCount*3]; + for (int i = 0; i < m_triCount*3; i += 3) + { + const float* v0 = &m_verts[m_tris[i]*3]; + const float* v1 = &m_verts[m_tris[i+1]*3]; + const float* v2 = &m_verts[m_tris[i+2]*3]; + float e0[3], e1[3]; + for (int j = 0; j < 3; ++j) + { + e0[j] = v1[j] - v0[j]; + e1[j] = v2[j] - v0[j]; + } + float* n = &m_normals[i]; + n[0] = e0[1]*e1[2] - e0[2]*e1[1]; + n[1] = e0[2]*e1[0] - e0[0]*e1[2]; + n[2] = e0[0]*e1[1] - e0[1]*e1[0]; + float d = sqrtf(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]); + if (d > 0) + { + d = 1.0f/d; + n[0] *= d; + n[1] *= d; + n[2] *= d; + } + } + + m_filename = filename; + return true; +} diff --git a/src/tools/pcb_reader/nav/ext/MeshLoaderObj.h b/src/tools/pcb_reader/nav/ext/MeshLoaderObj.h new file mode 100644 index 00000000..075c04b3 --- /dev/null +++ b/src/tools/pcb_reader/nav/ext/MeshLoaderObj.h @@ -0,0 +1,56 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#ifndef MESHLOADER_OBJ +#define MESHLOADER_OBJ + +#include + +class rcMeshLoaderObj +{ +public: + rcMeshLoaderObj(); + ~rcMeshLoaderObj(); + + bool load(const std::string& 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: + // Explicitly disabled copy constructor and copy assignment operator. + rcMeshLoaderObj(const rcMeshLoaderObj&); + rcMeshLoaderObj& operator=(const rcMeshLoaderObj&); + + void addVertex(float x, float y, float z, int& cap); + void addTriangle(int a, int b, int c, int& cap); + + std::string m_filename; + float m_scale; + float* m_verts; + int* m_tris; + float* m_normals; + int m_vertCount; + int m_triCount; +}; + +#endif // MESHLOADER_OBJ diff --git a/src/tools/pcb_reader/navmesh_exporter.h b/src/tools/pcb_reader/navmesh_exporter.h index c3d4d0c6..ec06a0cd 100644 --- a/src/tools/pcb_reader/navmesh_exporter.h +++ b/src/tools/pcb_reader/navmesh_exporter.h @@ -9,17 +9,8 @@ #include #include "exporter.h" -/* -#include -#include -#include -#include -#include -#include -#include -#include -#include -*/ +#include "nav/TiledNavmeshGenerator.h" + class NavmeshExporter { public: @@ -27,7 +18,16 @@ public: { auto start = std::chrono::high_resolution_clock::now(); - auto fileName = zone.name + ".obj"; + auto dir = std::experimental::filesystem::current_path().string() + "/pcb_export/" + zone.name + "/"; + auto fileName = dir + zone.name + ".obj"; + + TiledNavmeshGenerator gen( fileName ); + + if( !gen.buildNavmesh() ) + { + printf( "[Navmesh] Failed to build navmesh for '%s'\n", zone.name.c_str() ); + return; + } auto end = std::chrono::high_resolution_clock::now(); printf( "[Navmesh] Finished exporting %s in %lu ms\n", @@ -37,15 +37,7 @@ public: static void exportGroup( const std::string& zoneName, const ExportedGroup& group ) { - auto start = std::chrono::high_resolution_clock::now(); - auto fileName = zoneName + "_" + group.name + ".obj"; - - auto end = std::chrono::high_resolution_clock::now(); - - printf( "[Navmesh] Finished exporting %s in %lu ms\n", - fileName.c_str(), - std::chrono::duration_cast< std::chrono::milliseconds >( end - start ).count() ); } private: /*/