From 2d859216a4aa04caa2357e6a107907bf2d153515 Mon Sep 17 00:00:00 2001 From: Graham Pentheny Date: Mon, 25 Aug 2025 00:16:13 -0400 Subject: [PATCH] Moved mesh loading to a method in Mesh class --- RecastDemo/Include/InputGeom.h | 9 + RecastDemo/Include/MeshLoaderObj.h | 23 --- RecastDemo/Source/InputGeom.cpp | 258 +++++++++++++++++++++----- RecastDemo/Source/MeshLoaderObj.cpp | 189 ------------------- RecastDemo/Source/Sample_SoloMesh.cpp | 5 - 5 files changed, 219 insertions(+), 265 deletions(-) delete mode 100644 RecastDemo/Include/MeshLoaderObj.h delete mode 100644 RecastDemo/Source/MeshLoaderObj.cpp diff --git a/RecastDemo/Include/InputGeom.h b/RecastDemo/Include/InputGeom.h index a7cae9c9..6d5cafe0 100644 --- a/RecastDemo/Include/InputGeom.h +++ b/RecastDemo/Include/InputGeom.h @@ -77,8 +77,17 @@ struct Mesh std::vector tris; std::vector normals; + void reset() + { + verts.clear(); + tris.clear(); + normals.clear(); + } + [[nodiscard]] int getVertCount() const { return static_cast(verts.size()) / 3; } [[nodiscard]] int getTriCount() const { return static_cast(tris.size()) / 3; } + + void readFromObj(char* buf, size_t bufLen); }; class InputGeom diff --git a/RecastDemo/Include/MeshLoaderObj.h b/RecastDemo/Include/MeshLoaderObj.h deleted file mode 100644 index 90cd572d..00000000 --- a/RecastDemo/Include/MeshLoaderObj.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// 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. -// - -#pragma once - -#include - -void parseObjData(char* buf, size_t bufLen, std::vector& verts, std::vector& tris, std::vector& normals); diff --git a/RecastDemo/Source/InputGeom.cpp b/RecastDemo/Source/InputGeom.cpp index 3e2a80f5..1354ab93 100644 --- a/RecastDemo/Source/InputGeom.cpp +++ b/RecastDemo/Source/InputGeom.cpp @@ -20,7 +20,6 @@ #include "PartitionedMesh.h" #include "DebugDraw.h" -#include "MeshLoaderObj.h" #include "Recast.h" #include "Sample.h" @@ -134,6 +133,214 @@ char* parseRow(char* buf, char* bufEnd, char* row, int len) row[n] = '\0'; return buf; } + +bool isectSegAABB(const float* sp, const float* sq, const float* amin, const float* amax, float& tmin, float& tmax) +{ + static const float EPS = 1e-6f; + + float d[]{sq[0] - sp[0], sq[1] - sp[1], sq[2] - sp[2]}; + tmin = 0.0; + tmax = 1.0f; + + for (int i = 0; i < 3; i++) + { + if (fabsf(d[i]) < EPS) + { + if (sp[i] < amin[i] || sp[i] > amax[i]) + { + return false; + } + } + else + { + const float ood = 1.0f / d[i]; + float t1 = (amin[i] - sp[i]) * ood; + float t2 = (amax[i] - sp[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; +} + +char* readRow(char* buf, char* bufEnd, char* row, int len) +{ + // skip leading whitespace + for (; buf < bufEnd; ++buf) + { + char c = *buf; + if (c != '\\' && c != '\r' && c != '\n' && c != '\t' && c != ' ') + { + break; + } + } + + int n = 0; + for (; buf < bufEnd; ++buf) + { + char c = *buf; + + if (c == '\n') + { + break; + } + + if (c == '\\' || c == '\r') + { + // skip + continue; + } + + // Copy character + row[n++] = c; + if (n >= len - 1) + { + break; + } + } + row[n] = '\0'; + return buf; +} + +int readFace(char* row, int* data, int maxDataLen, int vertCount) +{ + int numVertices = 0; + while (*row != '\0') + { + // Skip initial white space + while (*row != '\0' && (*row == ' ' || *row == '\t')) + { + row++; + } + + char* s = row; + // Find vertex delimiter and terminate the string there for conversion. + while (*row != '\0' && *row != ' ' && *row != '\t') + { + if (*row == '/') + { + *row = '\0'; + } + row++; + } + + if (*s == '\0') + { + continue; + } + + int vertexIndex = atoi(s); + data[numVertices++] = vertexIndex < 0 ? vertexIndex + vertCount : vertexIndex - 1; + if (numVertices >= maxDataLen) + { + break; + } + } + return numVertices; +} +} + +void Mesh::readFromObj(char *buf, size_t bufLen) +{ + char* src = buf; + char* srcEnd = buf + bufLen; + char row[512]; + int face[32]; + float x, y, z; + int numVertices; + + while (src < srcEnd) + { + // Parse one row + row[0] = '\0'; + src = readRow(src, srcEnd, row, sizeof(row) / sizeof(row[0])); + + if (row[0] == '#') + { + // Comment + continue; + } + if (row[0] == 'v' && row[1] != 'n' && row[1] != 't') + { + // Vertex pos + sscanf(row + 1, "%f %f %f", &x, &y, &z); + verts.push_back(x); + verts.push_back(y); + verts.push_back(z); + } + if (row[0] == 'f') + { + // Face + const int vertCount = static_cast(verts.size()) / 3; + numVertices = readFace(row + 1, face, sizeof(face) / sizeof(face[0]), vertCount); + for (int i = 2; i < numVertices; ++i) + { + const int a = face[0]; + const int b = face[i - 1]; + const int c = face[i]; + if (a < 0 || a >= vertCount || b < 0 || b >= vertCount || c < 0 || c >= vertCount) + { + continue; + } + + tris.push_back(a); + tris.push_back(b); + tris.push_back(c); + } + } + } + + // Calculate face normals. + normals.resize(tris.size()); + for (int i = 0; i < static_cast(tris.size()); i += 3) + { + const float* vertex0 = &verts[tris[i + 0] * 3]; + const float* vertex1 = &verts[tris[i + 1] * 3]; + const float* vertex2 = &verts[tris[i + 2] * 3]; + + // Construct two triangle edges + float edge0[3]; + float edge1[3]; + for (int j = 0; j < 3; ++j) + { + edge0[j] = vertex1[j] - vertex0[j]; + edge1[j] = vertex2[j] - vertex0[j]; + } + + float* normal = &normals[i]; + + // Cross product + normal[0] = edge0[1] * edge1[2] - edge0[2] * edge1[1]; + normal[1] = edge0[2] * edge1[0] - edge0[0] * edge1[2]; + normal[2] = edge0[0] * edge1[1] - edge0[1] * edge1[0]; + + // Normalize + float normalLength = sqrtf(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]); + if (normalLength > 0) + { + normalLength = 1.0f / normalLength; + normal[0] *= normalLength; + normal[1] *= normalLength; + normal[2] *= normalLength; + } + } } InputGeom::~InputGeom() @@ -163,7 +370,8 @@ bool InputGeom::loadMesh(rcContext* ctx, const std::string& filepath) offMeshConCount = 0; convexVolumeCount = 0; - parseObjData(buffer, bufferLen, mesh.verts, mesh.tris, mesh.normals); + mesh.reset(); + mesh.readFromObj(buffer, bufferLen); rcCalcBounds(mesh.verts.data(), mesh.getVertCount(), meshBoundsMin, meshBoundsMax); delete partitionedMesh; @@ -408,52 +616,6 @@ bool InputGeom::saveGeomSet(const BuildSettings* settings) return true; } -static bool isectSegAABB(const float* sp, const float* sq, const float* amin, const float* amax, float& tmin, float& tmax) -{ - static const float EPS = 1e-6f; - - float d[]{sq[0] - sp[0], sq[1] - sp[1], sq[2] - sp[2]}; - tmin = 0.0; - tmax = 1.0f; - - for (int i = 0; i < 3; i++) - { - if (fabsf(d[i]) < EPS) - { - if (sp[i] < amin[i] || sp[i] > amax[i]) - { - return false; - } - } - else - { - const float ood = 1.0f / d[i]; - float t1 = (amin[i] - sp[i]) * ood; - float t2 = (amax[i] - sp[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; -} - bool InputGeom::raycastMesh(float* src, float* dst, float& tmin) { // Prune hit ray. diff --git a/RecastDemo/Source/MeshLoaderObj.cpp b/RecastDemo/Source/MeshLoaderObj.cpp deleted file mode 100644 index 701dc329..00000000 --- a/RecastDemo/Source/MeshLoaderObj.cpp +++ /dev/null @@ -1,189 +0,0 @@ -// -// 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 -#include - -namespace -{ -char* readRow(char* buf, char* bufEnd, char* row, int len) -{ - // skip leading whitespace - for (; buf < bufEnd; ++buf) - { - char c = *buf; - if (c != '\\' && c != '\r' && c != '\n' && c != '\t' && c != ' ') - { - break; - } - } - - int n = 0; - for (; buf < bufEnd; ++buf) - { - char c = *buf; - - if (c == '\n') - { - break; - } - - if (c == '\\' || c == '\r') - { - // skip - continue; - } - - // Copy character - row[n++] = c; - if (n >= len - 1) - { - break; - } - } - row[n] = '\0'; - return buf; -} - -int readFace(char* row, int* data, int maxDataLen, int vertCount) -{ - int numVertices = 0; - while (*row != '\0') - { - // Skip initial white space - while (*row != '\0' && (*row == ' ' || *row == '\t')) - { - row++; - } - - char* s = row; - // Find vertex delimiter and terminate the string there for conversion. - while (*row != '\0' && *row != ' ' && *row != '\t') - { - if (*row == '/') - { - *row = '\0'; - } - row++; - } - - if (*s == '\0') - { - continue; - } - - int vertexIndex = atoi(s); - data[numVertices++] = vertexIndex < 0 ? vertexIndex + vertCount : vertexIndex - 1; - if (numVertices >= maxDataLen) - { - break; - } - } - return numVertices; -} -} - -void parseObjData(char* buf, size_t bufLen, std::vector& verts, std::vector& tris, std::vector& normals) -{ - char* src = buf; - char* srcEnd = buf + bufLen; - char row[512]; - int face[32]; - float x, y, z; - int numVertices; - - while (src < srcEnd) - { - // Parse one row - row[0] = '\0'; - src = readRow(src, srcEnd, row, sizeof(row) / sizeof(row[0])); - - if (row[0] == '#') - { - // Comment - continue; - } - if (row[0] == 'v' && row[1] != 'n' && row[1] != 't') - { - // Vertex pos - sscanf(row + 1, "%f %f %f", &x, &y, &z); - verts.push_back(x); - verts.push_back(y); - verts.push_back(z); - } - if (row[0] == 'f') - { - // Face - const int vertCount = static_cast(verts.size()) / 3; - numVertices = readFace(row + 1, face, sizeof(face) / sizeof(face[0]), vertCount); - for (int i = 2; i < numVertices; ++i) - { - const int a = face[0]; - const int b = face[i - 1]; - const int c = face[i]; - if (a < 0 || a >= vertCount || b < 0 || b >= vertCount || c < 0 || c >= vertCount) - { - continue; - } - - tris.push_back(a); - tris.push_back(b); - tris.push_back(c); - } - } - } - - // Calculate face normals. - normals.resize(tris.size()); - for (int i = 0; i < static_cast(tris.size()); i += 3) - { - const float* vertex0 = &verts[tris[i + 0] * 3]; - const float* vertex1 = &verts[tris[i + 1] * 3]; - const float* vertex2 = &verts[tris[i + 2] * 3]; - - // Construct two triangle edges - float edge0[3]; - float edge1[3]; - for (int j = 0; j < 3; ++j) - { - edge0[j] = vertex1[j] - vertex0[j]; - edge1[j] = vertex2[j] - vertex0[j]; - } - - float* normal = &normals[i]; - - // Cross product - normal[0] = edge0[1] * edge1[2] - edge0[2] * edge1[1]; - normal[1] = edge0[2] * edge1[0] - edge0[0] * edge1[2]; - normal[2] = edge0[0] * edge1[1] - edge0[1] * edge1[0]; - - // Normalize - float normalLength = sqrtf(normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]); - if (normalLength > 0) - { - normalLength = 1.0f / normalLength; - normal[0] *= normalLength; - normal[1] *= normalLength; - normal[2] *= normalLength; - } - } -} diff --git a/RecastDemo/Source/Sample_SoloMesh.cpp b/RecastDemo/Source/Sample_SoloMesh.cpp index 19a0e74b..c4c60358 100644 --- a/RecastDemo/Source/Sample_SoloMesh.cpp +++ b/RecastDemo/Source/Sample_SoloMesh.cpp @@ -36,13 +36,8 @@ #include #include -#include #include -#ifdef WIN32 -# define snprintf _snprintf -#endif - const char* Sample_SoloMesh::drawModeNames[]{ "Input Mesh", "Navmesh",