diff --git a/include/NavKit/Resource.h b/include/NavKit/Resource.h index 61c6a93..01b578c 100644 --- a/include/NavKit/Resource.h +++ b/include/NavKit/Resource.h @@ -152,6 +152,8 @@ #define IDC_CHECK_SKIP_RPKG_EXTRACT 30414 #define IDC_CHECK_EXTRACT_TEXTURE_FILES 30415 #define IDC_CHECK_APPLY_TEXTURES 30416 +#define IDC_BUTTON_SELECT_ALL_LODS 30417 +#define IDC_BUTTON_DESELECT_ALL_LODS 30418 #define IDD_EXTRACT_NAVP_DIALOG 30500 #define IDC_COMBOBOX_NAVP 30501 diff --git a/include/NavKit/adapter/RecastAdapter.h b/include/NavKit/adapter/RecastAdapter.h index df3e3aa..ca442e3 100644 --- a/include/NavKit/adapter/RecastAdapter.h +++ b/include/NavKit/adapter/RecastAdapter.h @@ -23,6 +23,7 @@ class Sample; class BuildContext; class InputGeom; class DebugDrawGL; +class SceneMeshHitTestResult; class RecastAdapter { RecastAdapter(); @@ -101,7 +102,9 @@ class RecastAdapter { dtPolyRef getAdjacentPoly(dtPolyRef poly, int edgeIndex) const; - void doHitTest(int mx, int my); + void setMarker(const SceneMeshHitTestResult& result); + + SceneMeshHitTestResult doHitTest(int mx, int my); void loadSettings() const; @@ -111,11 +114,11 @@ class RecastAdapter { static Vec3 convertFromRecastToNavPower(Vec3 pos); - std::vector getEdges(dtPolyRef polyRef) const; + static std::vector getEdges(const dtNavMeshQuery* navQuery, dtPolyRef polyRef) ; - Vec3 calculateNormal(dtPolyRef polyRef) const; + Vec3 calculateNormal(dtNavMeshQuery* navQuery, dtPolyRef polyRef) const; - Vec3 calculateCentroid(dtPolyRef polyRef) const; + Vec3 calculateCentroid(dtNavMeshQuery* navQuery, dtPolyRef polyRef) const; const dtNavMesh* getNavMesh() const; diff --git a/include/NavKit/module/Renderer.h b/include/NavKit/module/Renderer.h index ea3e3fc..b89d891 100644 --- a/include/NavKit/module/Renderer.h +++ b/include/NavKit/module/Renderer.h @@ -27,6 +27,19 @@ class HitTestResult { int selectedIndex; }; +class SceneMeshHitTestResult { +public: + SceneMeshHitTestResult() : hitTime(0.0f), hitIndex(-1) { + rayStart[0] = rayStart[1] = rayStart[2] = 0.0f; + rayEnd[0] = rayEnd[1] = rayEnd[2] = 0.0f; + } + + float rayStart[3]; + float rayEnd[3]; + float hitTime; + int hitIndex; +}; + class Renderer { public: Renderer(); diff --git a/include/NavKit/util/Pathfinding.h b/include/NavKit/util/Pathfinding.h index 873ab74..cbb8337 100644 --- a/include/NavKit/util/Pathfinding.h +++ b/include/NavKit/util/Pathfinding.h @@ -32,11 +32,13 @@ namespace Pathfinding { NavPower::BBox calculateBBox(const NavPower::Area* area); - Vec3* GetClosestPosInArea2d_G2_EdgeIndex(Vec3* navPowerResult, + Vec3* GetClosestPosInArea2d_G2_EdgeIndex(dtNavMeshQuery* navQuery, + Vec3* navPowerResult, dtPolyRef poly, const Vec3* navPowerPos, int* edgeIndex); Vec3* GetClosestPosInArea2d_G2_ClosestPos( + dtNavMeshQuery* navQuery, Vec3* resultNavPower, dtPolyRef polyRef, const Vec3* posWCoordNavPower, diff --git a/lib/Debug/NavWeakness.dll b/lib/Debug/NavWeakness.dll index 655dca2..7ef8553 100644 Binary files a/lib/Debug/NavWeakness.dll and b/lib/Debug/NavWeakness.dll differ diff --git a/lib/Release/NavWeakness.dll b/lib/Release/NavWeakness.dll index 91e8e2e..34c3daf 100644 Binary files a/lib/Release/NavWeakness.dll and b/lib/Release/NavWeakness.dll differ diff --git a/src/NavKit.rc b/src/NavKit.rc index 2c52d6f..f10ca9a 100644 --- a/src/NavKit.rc +++ b/src/NavKit.rc @@ -291,14 +291,18 @@ BEGIN CONTROL "Prim",IDC_RADIO_MESH_TYPE_PRIM,"Button",BS_AUTORADIOBUTTON,64,20,40,10 GROUPBOX "Prim Level of Detail",IDC_STATIC,7,50,246,75 - AUTOCHECKBOX "LOD 1", IDC_CHECK_PRIM_LOD_1, 14, 65, 50, 8 - AUTOCHECKBOX "LOD 2", IDC_CHECK_PRIM_LOD_2, 14, 80, 50, 8 - AUTOCHECKBOX "LOD 3", IDC_CHECK_PRIM_LOD_3, 14, 95, 50, 8 - AUTOCHECKBOX "LOD 4", IDC_CHECK_PRIM_LOD_4, 14, 110, 50, 8 - AUTOCHECKBOX "LOD 5", IDC_CHECK_PRIM_LOD_5, 84, 65, 50, 8 - AUTOCHECKBOX "LOD 6", IDC_CHECK_PRIM_LOD_6, 84, 80, 50, 8 - AUTOCHECKBOX "LOD 7", IDC_CHECK_PRIM_LOD_7, 84, 95, 50, 8 - AUTOCHECKBOX "LOD 8", IDC_CHECK_PRIM_LOD_8, 84, 110, 50, 8 + PUSHBUTTON "Select All",IDC_BUTTON_SELECT_ALL_LODS,14,65,60,14 + PUSHBUTTON "Deselect All",IDC_BUTTON_DESELECT_ALL_LODS,84,65,60,14 + + AUTOCHECKBOX "LOD 1 (Highest)", IDC_CHECK_PRIM_LOD_1, 14, 85, 70, 8 + AUTOCHECKBOX "LOD 2", IDC_CHECK_PRIM_LOD_2, 84, 85, 40, 8 + AUTOCHECKBOX "LOD 3", IDC_CHECK_PRIM_LOD_3, 134, 85, 40, 8 + AUTOCHECKBOX "LOD 4", IDC_CHECK_PRIM_LOD_4, 184, 85, 40, 8 + + AUTOCHECKBOX "LOD 5", IDC_CHECK_PRIM_LOD_5, 14, 105, 40, 8 + AUTOCHECKBOX "LOD 6", IDC_CHECK_PRIM_LOD_6, 84, 105, 40, 8 + AUTOCHECKBOX "LOD 7", IDC_CHECK_PRIM_LOD_7, 134, 105, 40, 8 + AUTOCHECKBOX "LOD 8", IDC_CHECK_PRIM_LOD_8, 184, 105, 40, 8 GROUPBOX "Build Type for Blender File",IDC_STATIC,7,135,246,35 CONTROL "Copy",IDC_RADIO_BUILD_TYPE_COPY,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,150,40,10 diff --git a/src/adapter/RecastAdapter.cpp b/src/adapter/RecastAdapter.cpp index 41b39b7..7b516b6 100644 --- a/src/adapter/RecastAdapter.cpp +++ b/src/adapter/RecastAdapter.cpp @@ -442,6 +442,7 @@ void RecastAdapter::renderRecastNavmesh(const bool isAirgInstance) const { if (!mesh) { return; } + dtNavMeshQuery* navQuery = sample->getNavMeshQuery(); for (int tileIndex = 0; tileIndex < mesh->getMaxTiles(); tileIndex++) { const dtMeshTile* tile = mesh->getTile(tileIndex); if (!tile || !tile->header) { @@ -474,14 +475,14 @@ void RecastAdapter::renderRecastNavmesh(const bool isAirgInstance) const { for (int polyIndex = 0; polyIndex < tile->header->polyCount; polyIndex++) { const dtPolyRef polyRef = getPoly(tileIndex, polyIndex); - auto edges = getEdges(polyRef); + auto edges = getEdges(navQuery, polyRef); glBegin(GL_LINE_LOOP); glVertex3f(edges[0].X, edges[0].Y, edges[0].Z); glVertex3f(edges[1].X, edges[1].Y, edges[1].Z); glVertex3f(edges[2].X, edges[2].Y, edges[2].Z); glEnd(); - auto centroid = calculateCentroid(polyRef); - renderer.drawText(("ref: " + std::to_string(polyRef) + " idx: " + std::to_string(polyIndex)).c_str(), + auto centroid = calculateCentroid(navQuery, polyRef); + renderer.drawText("ref: " + std::to_string(polyRef) + " idx: " + std::to_string(polyIndex), {centroid.X, centroid.Y, centroid.Z}, color); } } @@ -952,7 +953,35 @@ dtPolyRef RecastAdapter::getAdjacentPoly(const dtPolyRef polyRef, const int edge return 0; } -void RecastAdapter::doHitTest(const int mx, const int my) { +void RecastAdapter::setMarker(const SceneMeshHitTestResult& result) { + markerPositionSet = true; + markerPosition[0] = result.rayStart[0] + (result.rayEnd[0] - result.rayStart[0]) * result.hitTime; + markerPosition[1] = result.rayStart[1] + (result.rayEnd[1] - result.rayStart[1]) * result.hitTime; + markerPosition[2] = result.rayStart[2] + (result.rayEnd[2] - result.rayStart[2]) * result.hitTime; + for (auto [object, vertexRange] : SceneMesh::getInstance().objectTriangleRanges) { + if (result.hitIndex >= vertexRange.first && result.hitIndex < vertexRange.second) { + selectedObject = object; + break; + } + } + std::string meshNameString; + std::string roomString; + if (Scene::getInstance().sceneLoaded) { + if (const auto mesh = Scene::getInstance().findMeshByHashAndIdAndPos( + selectedObject.substr(0, 16), selectedObject.substr(17, 16), markerPosition); mesh != nullptr) { + meshNameString = mesh->entity.name; + roomString = " Room Folder: " + mesh->roomFolderName + " Room: " + mesh->roomName; + } + } + Logger::log( + NK_INFO, + ("Selected Object: '" + meshNameString + "' Mesh: '" + selectedObject + "' Obj vertex: " + + std::to_string(result.hitIndex) + roomString + + ". Setting marker position to: " + std::to_string(markerPosition[0]) + ", " + + std::to_string(markerPosition[1]) + ", " + std::to_string(markerPosition[2])).c_str()); +} + +SceneMeshHitTestResult RecastAdapter::doHitTest(const int mx, const int my) { float rayStart[3]; float rayEnd[3]; float hitTime; @@ -966,40 +995,23 @@ void RecastAdapter::doHitTest(const int mx, const int my) { rayEnd[0] = (float)x; rayEnd[1] = (float)y; rayEnd[2] = (float)z; - const int hit = inputGeom->raycastMesh(rayStart, rayEnd, hitTime); - if (hit != -1) { - // Marker - markerPositionSet = true; - markerPosition[0] = rayStart[0] + (rayEnd[0] - rayStart[0]) * hitTime; - markerPosition[1] = rayStart[1] + (rayEnd[1] - rayStart[1]) * hitTime; - markerPosition[2] = rayStart[2] + (rayEnd[2] - rayStart[2]) * hitTime; - for (auto [object, vertexRange] : SceneMesh::getInstance().objectTriangleRanges) { - if (hit >= vertexRange.first && hit < vertexRange.second) { - selectedObject = object; - break; - } - } - std::string meshNameString; - std::string roomString; - if (Scene::getInstance().sceneLoaded) { - if (const auto mesh = Scene::getInstance().findMeshByHashAndIdAndPos( - selectedObject.substr(0, 16), selectedObject.substr(17, 16), markerPosition); mesh != nullptr) { - meshNameString = mesh->entity.name; - roomString = " Room Folder: " + mesh->roomFolderName + " Room: " + mesh->roomName; - } - } - Logger::log( - NK_INFO, - ("Selected Object: '" + meshNameString + "' Mesh: '" + selectedObject + "' Obj vertex: " + - std::to_string(hit) + roomString + - ". Setting marker position to: " + std::to_string(markerPosition[0]) + ", " + - std::to_string(markerPosition[1]) + ", " + std::to_string(markerPosition[2])).c_str()); - } else { - if (SDL_GetModState()) { - // Marker - markerPositionSet = false; - } + SceneMeshHitTestResult result; + if (const int hitIndex = inputGeom->raycastMesh(rayStart, rayEnd, hitTime); hitIndex != -1) { + result.hitIndex = hitIndex; + result.rayStart[0] = rayStart[0]; + result.rayStart[1] = rayStart[1]; + result.rayStart[2] = rayStart[2]; + result.rayEnd[0] = rayEnd[0]; + result.rayEnd[1] = rayEnd[1]; + result.rayEnd[2] = rayEnd[2]; + result.hitTime = hitTime; + return result; + } + result.hitIndex = -1; + if (SDL_GetModState()) { + markerPositionSet = false; } + return result; } void RecastAdapter::loadSettings() const { @@ -1085,8 +1097,16 @@ Vec3 RecastAdapter::convertFromRecastToNavPower(Vec3 pos) { return {pos.X, -pos.Z, pos.Y}; } -std::vector RecastAdapter::getEdges(const dtPolyRef polyRef) const { - const dtNavMesh* mesh = sample->getNavMesh(); +std::vector RecastAdapter::getEdges(const dtNavMeshQuery* navQuery, const dtPolyRef polyRef) { + if (!navQuery) { + return {}; + } + + const dtNavMesh* mesh = navQuery->getAttachedNavMesh(); + if (!mesh) { + return {}; + } + unsigned int salt = 0; unsigned int tileIndex = 0; unsigned int polyIndex = 0; @@ -1106,8 +1126,11 @@ std::vector RecastAdapter::getEdges(const dtPolyRef polyRef) const { return edges; } -Vec3 RecastAdapter::calculateNormal(const dtPolyRef polyRef) const { - const std::vector edges = getEdges(polyRef); +Vec3 RecastAdapter::calculateNormal(dtNavMeshQuery* navQuery, const dtPolyRef polyRef) const { + const std::vector edges = getEdges(navQuery, polyRef); + if (edges.size() < 3) { + return {0.0f, 1.0f, 0.0f}; + } const Vec3 v0 = edges.at(0); const Vec3 v1 = edges.at(1); const Vec3 v2 = edges.at(2); @@ -1118,9 +1141,13 @@ Vec3 RecastAdapter::calculateNormal(const dtPolyRef polyRef) const { return cross.GetUnitVec(); } -Vec3 RecastAdapter::calculateCentroid(const dtPolyRef polyRef) const { - const std::vector edges = getEdges(polyRef); - const Vec3 normal = calculateNormal(polyRef); +Vec3 RecastAdapter::calculateCentroid(dtNavMeshQuery* navQuery, const dtPolyRef polyRef) const { + const std::vector edges = getEdges(navQuery, polyRef); + if (edges.empty()) { + return {0.0f, 0.0f, 0.0f}; + } + + const Vec3 normal = calculateNormal(navQuery, polyRef); const Vec3 v0 = edges.at(0); const Vec3 v1 = edges.at(1); diff --git a/src/model/Json.cpp b/src/model/Json.cpp index 9fbc935..225c3b4 100644 --- a/src/model/Json.cpp +++ b/src/model/Json.cpp @@ -1,5 +1,7 @@ #include "../../include/NavKit/model/Json.h" +#include +#include #include "../../include/NavKit/module/Logger.h" #include "../../include/NavKit/module/Scene.h" @@ -22,9 +24,31 @@ std::string Json::toString(std::string_view val) { template Json::JsonValueProxy::operator t() { using simdT = typename SimdJsonTypeMap::type; simdT val; - if (json[field].get(val) == simdjson::SUCCESS) { + + if (simdjson::ondemand::value jsonValue = json[field]; jsonValue.get(val) == simdjson::SUCCESS) { Logger::log(NK_DEBUG, "Field: %s value: %s", field.c_str(), toString(val).c_str()); return static_cast(val); + } else if constexpr (std::is_arithmetic_v) { + std::string_view strVal; + if (simdjson::error_code err; (err = jsonValue.get_string().get(strVal)) == simdjson::SUCCESS) { + try { + if constexpr (std::is_integral_v) { + val = static_cast(std::stoll(std::string(strVal))); + } else if constexpr (std::is_floating_point_v) { + val = static_cast(std::stod(std::string(strVal))); + } + Logger::log(NK_DEBUG, "Field: %s value (from string conversion): %s", field.c_str(), + toString(val).c_str()); + return static_cast(val); + } catch (const std::exception& e) { + Logger::log(NK_ERROR, "Error converting string '%s' to numeric type for field: %s. Exception: %s", + std::string(strVal).c_str(), field.c_str(), e.what()); + } + } else { + Logger::log( + NK_ERROR, "Error getting value for field: %s. Not a direct numeric or string value. Simdjson error: %s", + field.c_str(), simdjson::error_message(err)); + } } Logger::log(NK_ERROR, "Error getting value for field: %s", field.c_str()); return {}; @@ -125,12 +149,12 @@ void Json::Mesh::writeJson(std::ostream& f) const { R"(","roomFolderName":")" << roomFolderName << R"(","entity":{"id":")" << entity.id << R"(","name":")" << entity.name << R"(",)"; - entity.position.writeJson(f); - f << ","; - entity.rotation.writeJson(f); - f << ","; - entity.scale.writeJson(f); - f << "}}"; + entity.position.writeJson(f); + f << ","; + entity.rotation.writeJson(f); + f << ","; + entity.scale.writeJson(f); + f << "}}"; } Json::Meshes::Meshes(simdjson::ondemand::array meshesJson) { @@ -180,7 +204,21 @@ void Json::PfBoxes::readPathfindingBBoxes() { Vec3 s = entity.scale.data; Rotation r = entity.rotation; PfBoxType type = entity.type; - scene.includeBox = {id, name, p, s, r, type}; + if (scene.includeBox.id.empty()) { + scene.includeBox = {id, name, p, s, r, type}; + } else { + // Set scene.includeBox pos, scale, and rotation to be the span of the two boxes + float minX = std::min(scene.includeBox.pos.x - scene.includeBox.scale.x / 2.0f, p.x - s.x / 2.0f); + float minY = std::min(scene.includeBox.pos.y - scene.includeBox.scale.y / 2.0f, p.y - s.y / 2.0f); + float minZ = std::min(scene.includeBox.pos.z - scene.includeBox.scale.z / 2.0f, p.z - s.z / 2.0f); + float maxX = std::max(scene.includeBox.pos.x + scene.includeBox.scale.x / 2.0f, p.x + s.x / 2.0f); + float maxY = std::max(scene.includeBox.pos.y + scene.includeBox.scale.y / 2.0f, p.y + s.y / 2.0f); + float maxZ = std::max(scene.includeBox.pos.z + scene.includeBox.scale.z / 2.0f, p.z + s.z / 2.0f); + + scene.includeBox.pos = {(minX + maxX) / 2.0f, (minY + maxY) / 2.0f, (minZ + maxZ) / 2.0f}; + scene.includeBox.scale = {maxX - minX, maxY - minY, maxZ - minZ}; + scene.includeBox.rotation = {0, 0, 0, 1}; + } includeBoxFound = true; } if (entity.type.data == EXCLUDE_TYPE) { diff --git a/src/module/InputHandler.cpp b/src/module/InputHandler.cpp index 57c25be..bd204fb 100644 --- a/src/module/InputHandler.cpp +++ b/src/module/InputHandler.cpp @@ -202,7 +202,7 @@ void InputHandler::hitTest() const { const Gui& gui = Gui::getInstance(); Navp& navp = Navp::getInstance(); Airg& airg = Airg::getInstance(); - SceneMesh& obj = SceneMesh::getInstance(); + SceneMesh& sceneMesh = SceneMesh::getInstance(); RecastAdapter& recastAdapter = RecastAdapter::getInstance(); const Renderer& renderer = Renderer::getInstance(); if (!gui.mouseOverMenu) { @@ -210,15 +210,13 @@ void InputHandler::hitTest() const { (navp.doNavpExclusionBoxHitTest && navp.showPfExclusionBoxes) || (navp.doNavpPfSeedPointHitTest && navp.showPfSeedPoints) || (airg.doAirgHitTest && airg.airgLoaded && airg.showAirg) || - (obj.doObjHitTest && obj.objLoaded && obj.showObj)) { - const HitTestResult hitTestResult = renderer.hitTestRender(mousePos[0], mousePos[1]); - if (hitTestResult.type == NAVMESH_AREA) { - if (hitTestResult.selectedIndex == navp.selectedNavpAreaIndex) { - navp.setSelectedNavpAreaIndex(-1); - } else { - navp.setSelectedNavpAreaIndex(hitTestResult.selectedIndex); - } - } else if (hitTestResult.type == AIRG_WAYPOINT) { + (sceneMesh.doObjHitTest && sceneMesh.objLoaded && sceneMesh.showObj)) { + SceneMeshHitTestResult sceneMeshHitResult; + if (sceneMesh.showObj && sceneMesh.objLoaded && sceneMesh.doObjHitTest) { + sceneMeshHitResult = recastAdapter.doHitTest(mousePos[0], mousePos[1]); + } + + if (const HitTestResult hitTestResult = renderer.hitTestRender(mousePos[0], mousePos[1]); hitTestResult.type == AIRG_WAYPOINT) { if (hitTestResult.selectedIndex == airg.selectedWaypointIndex) { airg.setSelectedAirgWaypointIndex(-1); } else { @@ -240,18 +238,29 @@ void InputHandler::hitTest() const { } else { navp.setSelectedExclusionBoxIndex(hitTestResult.selectedIndex); } + } else if (hitTestResult.type == NAVMESH_AREA) { + if (hitTestResult.selectedIndex == navp.selectedNavpAreaIndex) { + navp.setSelectedNavpAreaIndex(-1); + } else { + navp.setSelectedNavpAreaIndex(hitTestResult.selectedIndex); + } } else { - navp.setSelectedNavpAreaIndex(-1); - airg.setSelectedAirgWaypointIndex(-1); - if (obj.showObj && obj.objLoaded) { - recastAdapter.doHitTest(mousePos[0], mousePos[1]); + if (hitTestResult.type == NONE && sceneMeshHitResult.hitIndex == -1) { + navp.setSelectedNavpAreaIndex(-1); + airg.setSelectedAirgWaypointIndex(-1); + navp.setSelectedExclusionBoxIndex(-1); + navp.setSelectedPfSeedPointIndex(-1); + } + if (sceneMeshHitResult.hitIndex != -1) { + recastAdapter.setMarker(sceneMeshHitResult); } } + navp.doNavpHitTest = false; navp.doNavpExclusionBoxHitTest = false; navp.doNavpPfSeedPointHitTest = false; airg.doAirgHitTest = false; - obj.doObjHitTest = false; + sceneMesh.doObjHitTest = false; } } Menu::updateMenuState(); diff --git a/src/module/Renderer.cpp b/src/module/Renderer.cpp index 5b5b7a3..cf4fa71 100644 --- a/src/module/Renderer.cpp +++ b/src/module/Renderer.cpp @@ -493,140 +493,68 @@ void Renderer::drawText(const std::string& text, const Vec3 pos, const Vec3 colo void Renderer::drawBox(const Vec3 pos, const Vec3 size, const Math::Quaternion rotation, const bool filled, const Vec3 fillColor, const bool outlined, const Vec3 outlineColor, const float alpha) const { - static GLuint vao = 0, vbo = 0; - if (vao == 0) { - glGenVertexArrays(1, &vao); - glGenBuffers(1, &vbo); - glBindVertexArray(vao); - glBindBuffer(GL_ARRAY_BUFFER, vbo); + static GLuint filledVao = 0, filledVbo = 0; + static GLuint outlineVao = 0, outlineVbo = 0; + + if (filledVao == 0) { + constexpr float unitCube[] = { + -0.5f,-0.5f,-0.5f, 0.5f,-0.5f,-0.5f, 0.5f, 0.5f,-0.5f, -0.5f,-0.5f,-0.5f, 0.5f, 0.5f,-0.5f, -0.5f, 0.5f,-0.5f, + -0.5f,-0.5f, 0.5f, 0.5f,-0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f,-0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, + -0.5f,-0.5f,-0.5f, -0.5f, 0.5f,-0.5f, -0.5f, 0.5f, 0.5f, -0.5f,-0.5f,-0.5f, -0.5f, 0.5f, 0.5f, -0.5f,-0.5f, 0.5f, + 0.5f,-0.5f,-0.5f, 0.5f, 0.5f,-0.5f, 0.5f, 0.5f, 0.5f, 0.5f,-0.5f,-0.5f, 0.5f, 0.5f, 0.5f, 0.5f,-0.5f, 0.5f, + -0.5f,-0.5f,-0.5f, 0.5f,-0.5f,-0.5f, 0.5f,-0.5f, 0.5f, -0.5f,-0.5f,-0.5f, 0.5f,-0.5f, 0.5f, -0.5f,-0.5f, 0.5f, + -0.5f, 0.5f,-0.5f, 0.5f, 0.5f,-0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f,-0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f + }; + glGenVertexArrays(1, &filledVao); + glGenBuffers(1, &filledVbo); + glBindVertexArray(filledVao); + glBindBuffer(GL_ARRAY_BUFFER, filledVbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(unitCube), unitCube, GL_STATIC_DRAW); glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), static_cast(nullptr)); + + constexpr float unitOutline[] = { + -0.5f,-0.5f,-0.5f, 0.5f,-0.5f,-0.5f, 0.5f,-0.5f,-0.5f, 0.5f, 0.5f,-0.5f, + 0.5f, 0.5f,-0.5f, -0.5f, 0.5f,-0.5f, -0.5f, 0.5f,-0.5f, -0.5f,-0.5f,-0.5f, + -0.5f,-0.5f, 0.5f, 0.5f,-0.5f, 0.5f, 0.5f,-0.5f, 0.5f, 0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f,-0.5f, 0.5f, + -0.5f,-0.5f,-0.5f, -0.5f,-0.5f, 0.5f, 0.5f,-0.5f,-0.5f, 0.5f,-0.5f, 0.5f, + 0.5f, 0.5f,-0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f,-0.5f, -0.5f, 0.5f, 0.5f + }; + glGenVertexArrays(1, &outlineVao); + glGenBuffers(1, &outlineVbo); + glBindVertexArray(outlineVao); + glBindBuffer(GL_ARRAY_BUFFER, outlineVbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(unitOutline), unitOutline, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), static_cast(nullptr)); glBindVertexArray(0); } - std::vector filledVertices; - std::vector outlinedVertices; - - auto addQuad = [&](Vec3 v1, Vec3 v2, Vec3 v3, Vec3 v4) { - if (filled) { - filledVertices.push_back(pos.X + v1.X); - filledVertices.push_back(pos.Y + v1.Y); - filledVertices.push_back(pos.Z + v1.Z); - filledVertices.push_back(pos.X + v2.X); - filledVertices.push_back(pos.Y + v2.Y); - filledVertices.push_back(pos.Z + v2.Z); - filledVertices.push_back(pos.X + v3.X); - filledVertices.push_back(pos.Y + v3.Y); - filledVertices.push_back(pos.Z + v3.Z); - - filledVertices.push_back(pos.X + v1.X); - filledVertices.push_back(pos.Y + v1.Y); - filledVertices.push_back(pos.Z + v1.Z); - filledVertices.push_back(pos.X + v3.X); - filledVertices.push_back(pos.Y + v3.Y); - filledVertices.push_back(pos.Z + v3.Z); - filledVertices.push_back(pos.X + v4.X); - filledVertices.push_back(pos.Y + v4.Y); - filledVertices.push_back(pos.Z + v4.Z); - } - if (outlined) { - outlinedVertices.push_back(pos.X + v1.X); - outlinedVertices.push_back(pos.Y + v1.Y); - outlinedVertices.push_back(pos.Z + v1.Z); - outlinedVertices.push_back(pos.X + v2.X); - outlinedVertices.push_back(pos.Y + v2.Y); - outlinedVertices.push_back(pos.Z + v2.Z); - - outlinedVertices.push_back(pos.X + v2.X); - outlinedVertices.push_back(pos.Y + v2.Y); - outlinedVertices.push_back(pos.Z + v2.Z); - outlinedVertices.push_back(pos.X + v3.X); - outlinedVertices.push_back(pos.Y + v3.Y); - outlinedVertices.push_back(pos.Z + v3.Z); - - outlinedVertices.push_back(pos.X + v3.X); - outlinedVertices.push_back(pos.Y + v3.Y); - outlinedVertices.push_back(pos.Z + v3.Z); - outlinedVertices.push_back(pos.X + v4.X); - outlinedVertices.push_back(pos.Y + v4.Y); - outlinedVertices.push_back(pos.Z + v4.Z); - - outlinedVertices.push_back(pos.X + v4.X); - outlinedVertices.push_back(pos.Y + v4.Y); - outlinedVertices.push_back(pos.Z + v4.Z); - outlinedVertices.push_back(pos.X + v1.X); - outlinedVertices.push_back(pos.Y + v1.Y); - outlinedVertices.push_back(pos.Z + v1.Z); - } - }; - - // Bottom - addQuad( - rotatePoint({-size.X / 2, -size.Y / 2, -size.Z / 2}, rotation), - rotatePoint({-size.X / 2, +size.Y / 2, -size.Z / 2}, rotation), - rotatePoint({+size.X / 2, +size.Y / 2, -size.Z / 2}, rotation), - rotatePoint({+size.X / 2, -size.Y / 2, -size.Z / 2}, rotation) - ); - // Top - addQuad( - rotatePoint({-size.X / 2, -size.Y / 2, +size.Z / 2}, rotation), - rotatePoint({-size.X / 2, +size.Y / 2, +size.Z / 2}, rotation), - rotatePoint({+size.X / 2, +size.Y / 2, +size.Z / 2}, rotation), - rotatePoint({+size.X / 2, -size.Y / 2, +size.Z / 2}, rotation) - ); - // Left - addQuad( - rotatePoint({-size.X / 2, -size.Y / 2, -size.Z / 2}, rotation), - rotatePoint({-size.X / 2, -size.Y / 2, +size.Z / 2}, rotation), - rotatePoint({-size.X / 2, +size.Y / 2, +size.Z / 2}, rotation), - rotatePoint({-size.X / 2, +size.Y / 2, -size.Z / 2}, rotation) - ); - // Right - addQuad( - rotatePoint({+size.X / 2, -size.Y / 2, -size.Z / 2}, rotation), - rotatePoint({+size.X / 2, -size.Y / 2, +size.Z / 2}, rotation), - rotatePoint({+size.X / 2, +size.Y / 2, +size.Z / 2}, rotation), - rotatePoint({+size.X / 2, +size.Y / 2, -size.Z / 2}, rotation) - ); - // Front - addQuad( - rotatePoint({-size.X / 2, -size.Y / 2, -size.Z / 2}, rotation), - rotatePoint({-size.X / 2, -size.Y / 2, +size.Z / 2}, rotation), - rotatePoint({+size.X / 2, -size.Y / 2, +size.Z / 2}, rotation), - rotatePoint({+size.X / 2, -size.Y / 2, -size.Z / 2}, rotation) - ); - // Back - addQuad( - rotatePoint({-size.X / 2, +size.Y / 2, -size.Z / 2}, rotation), - rotatePoint({-size.X / 2, +size.Y / 2, +size.Z / 2}, rotation), - rotatePoint({+size.X / 2, +size.Y / 2, +size.Z / 2}, rotation), - rotatePoint({+size.X / 2, +size.Y / 2, -size.Z / 2}, rotation) - ); + glm::mat4 model = glm::translate(glm::mat4(1.0f), glm::vec3(pos.X, pos.Y, pos.Z)); + const glm::quat q(rotation.w, rotation.x, rotation.y, rotation.z); + model = model * glm::mat4_cast(q); + model = glm::scale(model, glm::vec3(size.X, size.Y, size.Z)); shader.use(); shader.setBool("useFlatColor", true); shader.setBool("useVertexColor", false); shader.setMat4("projection", projection); shader.setMat4("view", view); - shader.setMat4("model", glm::mat4(1.0f)); - - glBindVertexArray(vao); - glBindBuffer(GL_ARRAY_BUFFER, vbo); + shader.setMat4("model", model); glPolygonOffset(-2.0f, -2.0f); - if (filled && !filledVertices.empty()) { - glBufferData(GL_ARRAY_BUFFER, filledVertices.size() * sizeof(float), filledVertices.data(), GL_DYNAMIC_DRAW); - // Use the alpha parameter provided to the function + if (filled) { + glBindVertexArray(filledVao); shader.setVec4("flatColor", glm::vec4(fillColor.X, fillColor.Y, fillColor.Z, alpha)); - glDrawArrays(GL_TRIANGLES, 0, filledVertices.size() / 3); + glDrawArrays(GL_TRIANGLES, 0, 36); } - if (outlined && !outlinedVertices.empty()) { - glBufferData(GL_ARRAY_BUFFER, outlinedVertices.size() * sizeof(float), outlinedVertices.data(), - GL_DYNAMIC_DRAW); + if (outlined) { + glBindVertexArray(outlineVao); shader.setVec4("flatColor", glm::vec4(outlineColor.X, outlineColor.Y, outlineColor.Z, alpha)); - glDrawArrays(GL_LINES, 0, outlinedVertices.size() / 3); + glDrawArrays(GL_LINES, 0, 24); } glPolygonOffset(0.0f, 0.0f); @@ -638,6 +566,11 @@ HitTestResult Renderer::hitTestRender(const int mx, const int my) const { glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glClearColor(1.0, 1.0, 1.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST); + + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + getInstance().renderFrame(); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); if (const GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); status != GL_FRAMEBUFFER_COMPLETE) { Logger::log(NK_ERROR, ("FB error, status: 0x" + std::to_string(static_cast(status))).c_str()); diff --git a/src/module/Rpkg.cpp b/src/module/Rpkg.cpp index f4ee380..9ce8fed 100644 --- a/src/module/Rpkg.cpp +++ b/src/module/Rpkg.cpp @@ -142,6 +142,7 @@ void Rpkg::initExtractionData() { // workers.clear(); // Logger::log(NK_INFO, "Done getting full hash list."); Menu::updateMenuState(); + Logger::log(NK_INFO, "Done initializing."); } void Rpkg::checkHitmanVersion() { diff --git a/src/module/Scene.cpp b/src/module/Scene.cpp index f2cdc63..8b85665 100644 --- a/src/module/Scene.cpp +++ b/src/module/Scene.cpp @@ -60,6 +60,7 @@ void Scene::loadMeshes(const std::function& errorCallback, simdjson::simdjson_result& jsonDocument) { Json::Meshes newMeshes; try { + Logger::log(NK_INFO, "Loading meshes."); newMeshes = Json::Meshes(jsonDocument["meshes"]); } catch (const std::exception& e) { Logger::log(NK_ERROR, e.what()); @@ -73,6 +74,7 @@ void Scene::loadPfBoxes(const std::function& errorCallback, simdjson::simdjson_result& jsonDocument) { Json::PfBoxes pfBoxes; try { + Logger::log(NK_INFO, "Loading Pathfinding boxes."); pfBoxes = Json::PfBoxes(jsonDocument["pfBoxes"]); } catch (const std::exception& e) { Logger::log(NK_ERROR, e.what()); @@ -95,7 +97,9 @@ void Scene::loadPfBoxes(const std::function& errorCallback, void Scene::loadVersion(simdjson::simdjson_result& jsonDocument) { try { + Logger::log(NK_INFO, "Checking NavKit scene version."); version = static_cast(static_cast(jsonDocument["version"])); + Logger::log(NK_INFO, "NavKit scene version: %d", version); } catch (...) { Logger::log(NK_INFO, "Version field not found in scene, defaulting to version zero."); version = 0; @@ -106,6 +110,7 @@ void Scene::loadPfSeedPoints(const std::function& errorCallback, simdjson::simdjson_result& jsonDocument) { Json::PfSeedPoints newPfSeedPoints; try { + Logger::log(NK_INFO, "Loading Pathfinding seed points."); newPfSeedPoints = Json::PfSeedPoints(jsonDocument["pfSeedPoints"]); } catch (const std::exception& e) { Logger::log(NK_ERROR, e.what()); @@ -122,9 +127,9 @@ void Scene::loadRoomsAndVolumes(const std::function& errorCallback, gates = Json::Gates(jsonDocument["gates"]).gates; Logger::log(NK_INFO, "Loading Rooms."); rooms = Json::Rooms(jsonDocument["rooms"]).rooms; - Logger::log(NK_INFO, "Loading Ai Area Worlds."); + Logger::log(NK_INFO, "Loading AI Area Worlds."); aiAreaWorlds = Json::AiAreaWorlds(jsonDocument["aiAreaWorld"]).aiAreaWorlds; - Logger::log(NK_INFO, "Loading Ai Areas."); + Logger::log(NK_INFO, "Loading AI Areas."); aiAreas = Json::AiAreas(jsonDocument["aiArea"]).aiAreas; Logger::log(NK_INFO, "Loading Volume Boxes."); volumeBoxes = Json::VolumeBoxes(jsonDocument["volumeBoxes"]).volumeBoxes; @@ -179,8 +184,8 @@ void Scene::loadScene(const std::string& fileName, const std::function& loadMeshes(errorCallback, jsonDocument); loadPfBoxes(errorCallback, jsonDocument); loadPfSeedPoints(errorCallback, jsonDocument); - loadRoomsAndVolumes(errorCallback, jsonDocument); loadMatis(errorCallback, jsonDocument); + loadRoomsAndVolumes(errorCallback, jsonDocument); loadPrimMatis(errorCallback, jsonDocument); callback(); diff --git a/src/module/SceneMesh.cpp b/src/module/SceneMesh.cpp index 66d9a3b..3a9d5b2 100644 --- a/src/module/SceneMesh.cpp +++ b/src/module/SceneMesh.cpp @@ -99,6 +99,9 @@ void SceneMesh::updateObjDialogControls(const HWND hDlg) { for (int i = 0; i < 8; ++i) { EnableWindow(GetDlgItem(hDlg, IDC_CHECK_PRIM_LOD_1 + i), isPrim); } + EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_SELECT_ALL_LODS), isPrim); + EnableWindow(GetDlgItem(hDlg, IDC_BUTTON_DESELECT_ALL_LODS), isPrim); + CheckRadioButton(hDlg, IDC_RADIO_BUILD_TYPE_COPY, IDC_RADIO_BUILD_TYPE_INSTANCE, obj.sceneMeshBuildType == COPY ? IDC_RADIO_BUILD_TYPE_COPY : IDC_RADIO_BUILD_TYPE_INSTANCE); @@ -170,6 +173,18 @@ INT_PTR CALLBACK SceneMesh::ObjSettingsDialogProc(const HWND hDlg, const UINT me return TRUE; } + case IDC_BUTTON_SELECT_ALL_LODS: + case IDC_BUTTON_DESELECT_ALL_LODS: { + const bool check = LOWORD(wParam) == IDC_BUTTON_SELECT_ALL_LODS; + for (bool& primLod : sceneMesh.primLods) { + primLod = check; + } + sceneMesh.saveSceneMeshSettings(); + Logger::log(NK_INFO, "Prim LODs %s.", check ? "all selected" : "all deselected"); + updateObjDialogControls(hDlg); + return TRUE; + } + case IDC_BUTTON_RESET_DEFAULTS: { sceneMesh.resetDefaults(); updateObjDialogControls(hDlg); diff --git a/src/render/Model.cpp b/src/render/Model.cpp index d886ea6..78f8a89 100644 --- a/src/render/Model.cpp +++ b/src/render/Model.cpp @@ -1,5 +1,3 @@ -#include -#include #include #include #include @@ -9,9 +7,9 @@ #include #include #include -#include #include #include +#include #include #include "../../include/NavKit/render/Model.h" @@ -19,6 +17,8 @@ #include +static std::mutex g_TextureMutex; + Texture loadTextureDataFromFile(const char* path, const std::string& directory) { Texture texture; texture.id = 0; @@ -60,7 +60,8 @@ Texture loadTextureDataFromFile(const char* path, const std::string& directory) texture.loaded = true; stbi_image_free(data); } else { - Logger::log(NK_ERROR, "Texture failed to load at path: %s. Reason: %s", filename.c_str(), stbi_failure_reason()); + Logger::log(NK_ERROR, "Texture failed to load at path: %s. Reason: %s", filename.c_str(), + stbi_failure_reason()); } return texture; @@ -74,10 +75,18 @@ Mesh Model::processBatchedMeshes(const std::vector& batch, const aiScen std::vector indices; std::vector textures; - unsigned int vertexOffset = 0; + unsigned int totalVertices = 0; + unsigned int totalIndices = 0; + for (aiMesh* mesh : batch) { + totalVertices += mesh->mNumVertices; + totalIndices += mesh->mNumFaces * 3; + } + vertices.reserve(totalVertices); + indices.reserve(totalIndices); + unsigned int vertexOffset = 0; for (aiMesh* mesh : batch) { - for (unsigned int i = 0; i < mesh->mNumVertices; i++) { + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { Vertex vertex; glm::vec3 vector; vector.x = mesh->mVertices[i].x; @@ -115,9 +124,9 @@ Mesh Model::processBatchedMeshes(const std::vector& batch, const aiScen vertices.push_back(vertex); } - for (unsigned int i = 0; i < mesh->mNumFaces; i++) { + for (unsigned int i = 0; i < mesh->mNumFaces; ++i) { aiFace face = mesh->mFaces[i]; - for (unsigned int j = 0; j < face.mNumIndices; j++) { + for (unsigned int j = 0; j < face.mNumIndices; ++j) { indices.push_back(face.mIndices[j] + vertexOffset); } } @@ -152,23 +161,30 @@ std::vector Model::loadMaterialTexturesStatic(aiMaterial* mat, aiTextur const std::string& directory, std::vector& texturesLoaded) { std::vector textures; - for (unsigned int i = 0; i < mat->GetTextureCount(type); i++) { + for (unsigned int i = 0; i < mat->GetTextureCount(type); ++i) { aiString str; mat->GetTexture(type, i, &str); bool skip = false; - for (unsigned int j = 0; j < texturesLoaded.size(); j++) { - if (std::strcmp(texturesLoaded[j].path.data, str.C_Str()) == 0) { - textures.push_back(texturesLoaded[j]); - skip = true; - break; + { + std::lock_guard lock(g_TextureMutex); + for (unsigned int j = 0; j < texturesLoaded.size(); ++j) { + if (std::strcmp(texturesLoaded[j].path.data, str.C_Str()) == 0) { + textures.push_back(texturesLoaded[j]); + skip = true; + break; + } } } + if (!skip) { Texture texture = loadTextureDataFromFile(str.C_Str(), directory); texture.type = typeName; texture.path = str; textures.push_back(texture); - texturesLoaded.push_back(texture); + { + std::lock_guard lock(g_TextureMutex); + texturesLoaded.push_back(texture); + } } } return textures; @@ -194,7 +210,7 @@ void Model::loadModelData(std::string const& path) { meshes.clear(); texturesLoaded.clear(); - std::map> batches; + std::map, std::vector> batches; for (unsigned int i = 0; i < scene->mNumMeshes; i++) { aiMesh* mesh = scene->mMeshes[i]; @@ -205,13 +221,18 @@ void Model::loadModelData(std::string const& path) { batchId = name.substr(0, underscorePos); } - std::string key = batchId + "_" + std::to_string(mesh->mMaterialIndex); + batches[{batchId, mesh->mMaterialIndex}].push_back(mesh); + } - batches[key].push_back(mesh); + std::vector> meshFutures; + for (auto& batch : batches | std::views::values) { + meshFutures.push_back(std::async(std::launch::async, [this, &batch, scene]() { + return processBatchedMeshes(batch, scene, this->directory, this->texturesLoaded); + })); } - for (auto& [key, batch] : batches) { - meshes.push_back(processBatchedMeshes(batch, scene, directory, texturesLoaded)); + for (auto& fut : meshFutures) { + meshes.push_back(fut.get()); } } diff --git a/src/resource/fragment.glsl b/src/resource/fragment.glsl index d1e54c5..2ec7171 100644 --- a/src/resource/fragment.glsl +++ b/src/resource/fragment.glsl @@ -28,7 +28,7 @@ void main() vec3 norm = normalize(Normal); vec3 lightDir = normalize(vec3(0.5, 0.866, 0.5)); float diff = max(dot(norm, lightDir), 0.0); - float ambientStrength = 0.3; + float ambientStrength = 0.1; vec3 lightIntensity = vec3(ambientStrength + diff); vec4 texColor; diff --git a/src/resource/glacier2obj.py b/src/resource/glacier2obj.py index 62f20b4..9626271 100644 --- a/src/resource/glacier2obj.py +++ b/src/resource/glacier2obj.py @@ -2563,7 +2563,7 @@ def load_volume_boxes(json_data, volume_types): def load_scenario(path_to_nav_json, path_to_output_obj_file, mesh_type, lod_mask, build_type, filter_to_include_box, - apply_textures): + apply_textures, output_to_blend): start = timer() log("INFO", "Loading scenario.", "load_scenario") log("INFO", "Nav.Json file: " + path_to_nav_json, "load_scenario") @@ -2575,7 +2575,8 @@ def load_scenario(path_to_nav_json, path_to_output_obj_file, mesh_type, lod_mask room_color_index = 0 room_folder_color_index = 0 - load_volume_boxes(data, ["gates", "rooms", "aiArea"]) + if output_to_blend: + load_volume_boxes(data, ["gates", "rooms", "aiArea"]) # Get the "pathfinding include" box pf_include_box_info = None @@ -2959,9 +2960,9 @@ def main(): # Ensure the render engine is set to EEVEE so transparency properties are active # In 4.2+, EEVEE Next is 'BLENDER_EEVEE_NEXT' bpy.context.scene.render.engine = 'BLENDER_EEVEE_NEXT' - + output_to_blend = output_path[-4:] == 'both' or output_path[-5:] == 'blend' scenario = load_scenario(scene_path, output_path, mesh_type, lod_mask, build_type, filter_to_include_box, - apply_textures) + apply_textures, output_to_blend) if scenario == 1: log("INFO", 'Failed to import scenario "%s"' % scene_path, "main") return 1 diff --git a/src/util/GridGenerator.cpp b/src/util/GridGenerator.cpp index 9410cd7..02d47a8 100644 --- a/src/util/GridGenerator.cpp +++ b/src/util/GridGenerator.cpp @@ -138,14 +138,14 @@ void GridGenerator::GetGridProperties() { const Navp& navp = Navp::getInstance(); Vec3 min{10000.0f, 10000.0f, 10000.0f}; Vec3 max{-10000.0f, -10000.0f, -10000.0f}; - for (const auto section : navp.navMesh->m_aSections) { - for (const auto navGraph : section.m_aNavGraphs) { + for (const auto& section : navp.navMesh->m_aSections) { + for (const auto& navGraph : section.m_aNavGraphs) { min.X = std::min(min.X, navGraph.m_hdr->m_bbox.m_min.X); min.Y = std::min(min.Y, navGraph.m_hdr->m_bbox.m_min.Y); min.Z = std::min(min.Z, navGraph.m_hdr->m_bbox.m_min.Z); - max.X = std::min(max.X, navGraph.m_hdr->m_bbox.m_max.X); - max.Y = std::min(max.Y, navGraph.m_hdr->m_bbox.m_max.Y); - max.Z = std::min(max.Z, navGraph.m_hdr->m_bbox.m_max.Z); + max.X = std::max(max.X, navGraph.m_hdr->m_bbox.m_max.X); + max.Y = std::max(max.Y, navGraph.m_hdr->m_bbox.m_max.Y); + max.Z = std::max(max.Z, navGraph.m_hdr->m_bbox.m_max.Z); } } min.X += grid.xOffset; @@ -241,13 +241,13 @@ void GridGenerator::GenerateWaypointNodes() { cells.push_back(newCell); } else { bool shouldAddNewCell = true; - for (auto cell : cells) { + for (auto& cell : cells) { // 2.25 * tan(18) degrees = 0.73 if (abs(cellZIndex - cell.fZ) < 0.73) { if (cellInArea) { // Add the point to an existing cell - if (cells.back().m_Points.empty()) { - cells.back().m_Points.push_back({ + if (cell.m_Points.empty()) { + cell.m_Points.push_back({ vMappedPos.x, vMappedPos.y, vMappedPos.z, 0.0 }); } @@ -282,18 +282,10 @@ void GridGenerator::GenerateWaypointNodes() { } } - const int minX = floor((grid->m_Properties.vMin.x) / spacing); - const int minY = floor((grid->m_Properties.vMin.y) / spacing); - const int maxX = floor((grid->m_Properties.vMax.x) / spacing); - const int maxY = floor((grid->m_Properties.vMax.y) / spacing); - for (int y = minY; y <= maxY; ++y) { - for (int x = minX; x <= maxX; ++x) { - int offset = x + y * grid->m_Properties.nGridWidth; - auto& cells = waypointCells[offset]; - std::ranges::sort(cells, [](const Pathfinding::SGCell& a, const Pathfinding::SGCell& b) { - return a.fZ < b.fZ; - }); - } + for (auto& cells : waypointCells | std::views::values) { + std::ranges::sort(cells, [](const Pathfinding::SGCell& a, const Pathfinding::SGCell& b) { + return a.fZ < b.fZ; + }); } Logger::log(NK_INFO, "Finished generating waypoint nodes."); } @@ -307,8 +299,11 @@ void GridGenerator::GenerateWaypointConnectivityMap() { m_WaypointMap.clear(); int waypointIndex = 0; + const int nGridWidth = static_cast(airg.reasoningGrid->m_Properties.nGridWidth); + + Logger::log(NK_INFO, "Checking for possible waypoint locations within grid bounds: %d, %d", nGridWidth, nGridWidth); for (const auto& cells : waypointCells | std::views::values) { - for (auto cell : cells) { + for (const auto& cell : cells) { for (const auto point : cell.m_Points) { Waypoint waypoint; waypoint.vPos = {point.x, point.y, point.z + 0.001f, 1.0}; @@ -321,7 +316,6 @@ void GridGenerator::GenerateWaypointConnectivityMap() { const float4 vPos = {point.x, point.y, point.z, 0.0}; const Vec4 vMinVec4 = airg.reasoningGrid->m_Properties.vMin; const float4 vMin = {vMinVec4.x, vMinVec4.y, vMinVec4.z, 0.0}; - const int nGridWidth = airg.reasoningGrid->m_Properties.nGridWidth; const int x = floor((vPos.x - vMin.x) / fGridSpacing); const int y = floor((vPos.y - vMin.y) / fGridSpacing); @@ -336,11 +330,15 @@ void GridGenerator::GenerateWaypointConnectivityMap() { // Add waypoint to the waypoint list and corresponding entry in the map if (nOffset != -1) { - if (waypointIndex % 100 == 0) { - Logger::log(NK_INFO, - ("Adding new waypoint. position: X: " + std::to_string(waypoint.vPos.x) + " Y: " + - std::to_string(waypoint.vPos.y) + " Z: " + std::to_string(waypoint.vPos.z)). - c_str()); + if (waypointIndex >= 65535) { + Logger::log( + NK_ERROR, "Critical: Waypoint limit (65535) reached. Aborting to prevent overflow."); + airg.reasoningGrid->m_nNodeCount = airg.reasoningGrid->m_WaypointList.size(); + return; + } + if (waypointIndex == 0 || waypointIndex % 500 == 0) { + Logger::log(NK_INFO, "Adding new waypoint #%d. position: X: %0.2f Y: %0.2f Z: %0.2f", + waypointIndex, waypoint.vPos.x, waypoint.vPos.y, waypoint.vPos.z); } m_WaypointMap[nOffset].push_back(waypointIndex); airg.reasoningGrid->m_WaypointList.push_back(waypoint); @@ -447,7 +445,7 @@ void GridGenerator::AlignNodes() { distance.y < distanceThreshold ) { Navp& navp = Navp::getInstance(); - auto centroidNavPower = recastAirgAdapter.calculateCentroid(remappedLocation.polyRef); + auto centroidNavPower = recastAirgAdapter.calculateCentroid(&navQuery, remappedLocation.polyRef); auto area = navp.posToAreaMap.find(centroidNavPower); float radius = 0.1; if ((area != navp.posToAreaMap.end() && area->second->m_area->m_usageFlags == @@ -705,7 +703,7 @@ Pathfinding::ZPFLocation* GridGenerator::MapLocation_Internal( for (int i = 0; i < areasToCheckCount; ++i) { dtPolyRef polyRef = 0; polyRef = closestReachablePolys[i]; - GetClosestPosInArea2d_G2_ClosestPos(&candidatePosNavPower, polyRef, &vPosNavPowerVec3, &data); + GetClosestPosInArea2d_G2_ClosestPos(navQuery, &candidatePosNavPower, polyRef, &vPosNavPowerVec3, &data); float4 candidatePosNavPower4 = { candidatePosNavPower.X, candidatePosNavPower.Y, candidatePosNavPower.Z, static_cast(i) @@ -812,7 +810,7 @@ float4 GridGenerator::MapToCell(dtNavMeshQuery* navQuery, const float4* vCellNav distance.z <= distanceThreshold.z) { RecastAdapter& recastAirgAdapter = RecastAdapter::getAirgInstance(); const Vec3 centroidNavPower = RecastAdapter::convertFromRecastToNavPower( - recastAirgAdapter.calculateCentroid(pfLocation.polyRef)); + recastAirgAdapter.calculateCentroid(navQuery, pfLocation.polyRef)); if (const auto areaNavPowerPos = area.m_area->m_pos; abs(centroidNavPower.X - areaNavPowerPos.X) < 0.1 && abs(centroidNavPower.Y - areaNavPowerPos.Y) < 0.1 && @@ -860,7 +858,9 @@ bool GridGenerator::IsInside(dtNavMeshQuery* navQuery, Pathfinding::ZPFLocation* // Check if the remapped location has a valid area if (remappedLocationValid) { // Calculate the distance between the original and remapped locations - const float4 remappedNavPowerPos = {remappedLocation.pos.x, remappedLocation.pos.y, remappedLocation.pos.z, 1.0f}; + const float4 remappedNavPowerPos = { + remappedLocation.pos.x, remappedLocation.pos.y, remappedLocation.pos.z, 1.0f + }; const float4 distance = remappedNavPowerPos - navPowerPos; const float distanceSquared = distance.x * distance.x + distance.y * distance.y + distance.z * distance.z; @@ -928,7 +928,7 @@ void GridGenerator::GetCellBitmap(const float4* vNavPowerPosition, bool* pBitmap Navp& navp = Navp::getInstance(); RecastAdapter& recastAirgAdapter = RecastAdapter::getAirgInstance(); auto centroidNavPowerVec3 = RecastAdapter::convertFromRecastToNavPower( - recastAirgAdapter.calculateCentroid(cellLocation.polyRef)); + recastAirgAdapter.calculateCentroid(&navQuery, cellLocation.polyRef)); auto area = navp.posToAreaMap.find(centroidNavPowerVec3); if (area != navp.posToAreaMap.end() && area->second->m_area->m_usageFlags == NavPower::AreaUsageFlags::AREA_STEPS) { @@ -1041,7 +1041,7 @@ bool GridGenerator::NearestOuterEdge(dtNavMeshQuery* navQuery, Pathfinding::ZPFL for (int areaIndex = 0; areaIndex < numAreas; ++areaIndex) { int numEdges = 0; dtPolyRef polyRef = polys[areaIndex]; - numEdges = recastAirgAdapter.getEdges(polyRef).size(); + numEdges = recastAirgAdapter.getEdges(navQuery, polyRef).size(); // Iterate through each edge of the current area for (int edgeIndex = 0; edgeIndex < numEdges; ++edgeIndex) { // Get adjacent area and check if it's valid @@ -1052,9 +1052,10 @@ bool GridGenerator::NearestOuterEdge(dtNavMeshQuery* navQuery, Pathfinding::ZPFL continue; } polyRef = polys[areaIndex]; - auto edgesRecast = recastAirgAdapter.getEdges(polyRef); + auto edgesRecast = recastAirgAdapter.getEdges(navQuery, polyRef); const Vec3 edgeNavPowerStart = RecastAdapter::convertFromRecastToNavPower(edgesRecast[edgeIndex]); - const Vec3 edgeNavPowerEnd = RecastAdapter::convertFromRecastToNavPower(edgesRecast[(edgeIndex + 1) % numEdges]); + const Vec3 edgeNavPowerEnd = RecastAdapter::convertFromRecastToNavPower( + edgesRecast[(edgeIndex + 1) % numEdges]); // Calculate the closest point on the edge float4 edgeNavPowerStartPos = {edgeNavPowerStart.X, edgeNavPowerStart.Y, edgeNavPowerStart.Z, 1.0f}; @@ -1134,4 +1135,4 @@ void GridGenerator::buildVisionAndDeadEndData() { msg += " seconds"; Logger::log(NK_INFO, msg.data()); airg.buildingVisionAndDeadEndData = false; -} \ No newline at end of file +} diff --git a/src/util/Pathfinding.cpp b/src/util/Pathfinding.cpp index 98ce791..4d4e030 100644 --- a/src/util/Pathfinding.cpp +++ b/src/util/Pathfinding.cpp @@ -9,15 +9,15 @@ namespace Pathfinding { NavPower::BBox calculateBBox(const NavPower::Area* area) { - const float s_minFloat = -300000000000; - const float s_maxFloat = 300000000000; + constexpr float sMinFloat = -300000000000.0f; + constexpr float sMaxFloat = 300000000000.0f; NavPower::BBox bbox; - bbox.m_min.X = s_maxFloat; - bbox.m_min.Y = s_maxFloat; - bbox.m_min.Z = s_maxFloat; - bbox.m_max.X = s_minFloat; - bbox.m_max.Y = s_minFloat; - bbox.m_max.Z = s_minFloat; + bbox.m_min.X = sMaxFloat; + bbox.m_min.Y = sMaxFloat; + bbox.m_min.Z = sMaxFloat; + bbox.m_max.X = sMinFloat; + bbox.m_max.Y = sMinFloat; + bbox.m_max.Z = sMinFloat; for (auto& edge : area->m_edges) { bbox.m_max.X = std::max(bbox.m_max.X, edge->m_pos.X); bbox.m_max.Y = std::max(bbox.m_max.Y, edge->m_pos.Y); @@ -29,7 +29,8 @@ namespace Pathfinding { return bbox; } - Vec3* GetClosestPosInArea2d_G2_EdgeIndex(Vec3* navPowerResult, + Vec3* GetClosestPosInArea2d_G2_EdgeIndex(dtNavMeshQuery* navQuery, + Vec3* navPowerResult, const dtPolyRef polyRef, const Vec3* navPowerPos, int* edgeIndex) { *edgeIndex = -1; @@ -38,7 +39,7 @@ namespace Pathfinding { // Check if point is in polygon int numEdges = 0; const RecastAdapter& recastAirgAdapter = RecastAdapter::getAirgInstance(); - const std::vector polyEdges = recastAirgAdapter.getEdges(polyRef); + const std::vector polyEdges = RecastAdapter::getEdges(navQuery, polyRef); numEdges = polyEdges.size(); for (int i = 0; i < numEdges; ++i) { const int j = (i + 1) % numEdges; @@ -56,7 +57,7 @@ namespace Pathfinding { } if (edgeIntersections % 2 == 1) { const Vec3 normalNavPower = RecastAdapter::convertFromRecastToNavPower( - recastAirgAdapter.calculateNormal(polyRef)); + recastAirgAdapter.calculateNormal(navQuery, polyRef)); const Vec3 v1NavPower = RecastAdapter::convertFromRecastToNavPower(polyEdges[0]); navPowerResult->X = navPowerPos->X; navPowerResult->Y = navPowerPos->Y; @@ -92,7 +93,7 @@ namespace Pathfinding { navPowerResult->X = projectionNavPower.X; navPowerResult->Y = projectionNavPower.Y; const Vec3 normalNavPower = RecastAdapter::convertFromRecastToNavPower( - recastAirgAdapter.calculateNormal(polyRef)); + recastAirgAdapter.calculateNormal(navQuery, polyRef)); const float dNavPower = -(v1NavPower.X * normalNavPower.X + v1NavPower.Y * normalNavPower.Y + v1NavPower .Z * normalNavPower.Z); navPowerResult->Z = -(projectionNavPower.X * normalNavPower.X + projectionNavPower.Y * normalNavPower.Y @@ -103,20 +104,20 @@ namespace Pathfinding { } Vec3* GetClosestPosInArea2d_G2_ClosestPos( + dtNavMeshQuery* navQuery, Vec3* resultNavPower, const dtPolyRef polyRef, const Vec3* posWCoordNavPower, ClosestPositionData* pDataOut) { bool valid = false; - GridGenerator& gridGenerator = GridGenerator::getInstance(); valid = polyRef != 0; if (valid) { Vec3 closestNavPowerPos; - const Vec3 closestRecastPos = RecastAdapter::getAirgInstance().calculateCentroid(polyRef); + const Vec3 closestRecastPos = RecastAdapter::getAirgInstance().calculateCentroid(navQuery, polyRef); closestNavPowerPos = RecastAdapter::convertFromRecastToNavPower(closestRecastPos); int edgeIndex = -1; - GetClosestPosInArea2d_G2_EdgeIndex(&closestNavPowerPos, polyRef, posWCoordNavPower, &edgeIndex); + GetClosestPosInArea2d_G2_EdgeIndex(navQuery, &closestNavPowerPos, polyRef, posWCoordNavPower, &edgeIndex); if (pDataOut) { pDataOut->edgeIndex = edgeIndex;