From 20c05cb2f21f45a1b54acb35f3be06a01db00145 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Fri, 17 Oct 2025 09:17:10 -0500 Subject: [PATCH] clamp search to bounds --- .../SlashCommands/DEVGMCommands.cpp | 38 +++---- dZoneManager/Raw.cpp | 104 ++++++++++-------- dZoneManager/Raw.h | 10 +- dZoneManager/dZoneManager.cpp | 20 ++-- 4 files changed, 95 insertions(+), 77 deletions(-) diff --git a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp index 1a5a22b4..48c6d153 100644 --- a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp +++ b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp @@ -1974,8 +1974,8 @@ namespace DEVGMCommands { const float y = chunk.heightMap[heightIndex]; // Map heightmap position to scene map position (same as GenerateTerrainMesh) - const float sceneMapI = (static_cast(i) / static_cast(chunk.width - 1)) * static_cast(chunk.colorMapResolution - 1); - const float sceneMapJ = (static_cast(j) / static_cast(chunk.height - 1)) * static_cast(chunk.colorMapResolution - 1); + const float sceneMapI = ((i) / (chunk.width - 1)) * (chunk.colorMapResolution - 1); + const float sceneMapJ = ((j) / (chunk.height - 1)) * (chunk.colorMapResolution - 1); const uint32_t sceneI = std::min(static_cast(sceneMapI), chunk.colorMapResolution - 1); const uint32_t sceneJ = std::min(static_cast(sceneMapJ), chunk.colorMapResolution - 1); @@ -1987,15 +1987,13 @@ namespace DEVGMCommands { } // Check if this point belongs to the current scene - if (sceneID == currentSceneID.GetSceneID()) { - // Calculate world position (same as GenerateTerrainMesh) - const float worldX = (static_cast(i) + (chunk.offsetWorldX / chunk.scaleFactor)) * chunk.scaleFactor; - const float worldY = (y / chunk.scaleFactor) * chunk.scaleFactor; - const float worldZ = (static_cast(j) + (chunk.offsetWorldZ / chunk.scaleFactor)) * chunk.scaleFactor; - - NiPoint3 spawnPos(worldX, worldY, worldZ); - - EntityInfo info; + if (sceneID == currentSceneID.GetSceneID()) { + // Calculate world position (same as GenerateTerrainMesh) + const float worldX = ((i) + (chunk.offsetX / chunk.scaleFactor)) * chunk.scaleFactor; + const float worldY = (y / chunk.scaleFactor) * chunk.scaleFactor; + const float worldZ = ((j) + (chunk.offsetZ / chunk.scaleFactor)) * chunk.scaleFactor; + + NiPoint3 spawnPos(worldX, worldY, worldZ); EntityInfo info; info.lot = lot + currentSceneID.GetSceneID(); // to differentiate scenes info.pos = spawnPos; info.rot = QuatUtils::IDENTITY; @@ -2065,8 +2063,8 @@ namespace DEVGMCommands { const float y = chunk.heightMap[heightIndex]; // Map heightmap position to scene map position (same as GenerateTerrainMesh) - const float sceneMapI = (static_cast(i) / static_cast(chunk.width - 1)) * static_cast(chunk.colorMapResolution - 1); - const float sceneMapJ = (static_cast(j) / static_cast(chunk.height - 1)) * static_cast(chunk.colorMapResolution - 1); + const float sceneMapI = ((i) / (chunk.width - 1)) * (chunk.colorMapResolution - 1); + const float sceneMapJ = ((j) / (chunk.height - 1)) * (chunk.colorMapResolution - 1); const uint32_t sceneI = std::min(static_cast(sceneMapI), chunk.colorMapResolution - 1); const uint32_t sceneJ = std::min(static_cast(sceneMapJ), chunk.colorMapResolution - 1); @@ -2080,14 +2078,12 @@ namespace DEVGMCommands { // Skip invalid scenes (scene ID 0 typically means no scene) if (sceneID == 0) continue; - // Calculate world position (same as GenerateTerrainMesh) - const float worldX = (static_cast(i) + (chunk.offsetWorldX / chunk.scaleFactor)) * chunk.scaleFactor; - const float worldY = (y / chunk.scaleFactor) * chunk.scaleFactor; - const float worldZ = (static_cast(j) + (chunk.offsetWorldZ / chunk.scaleFactor)) * chunk.scaleFactor; - - NiPoint3 spawnPos(worldX, worldY, worldZ); - - EntityInfo info; + // Calculate world position (same as GenerateTerrainMesh) + const float worldX = ((i) + (chunk.offsetX / chunk.scaleFactor)) * chunk.scaleFactor; + const float worldY = (y / chunk.scaleFactor) * chunk.scaleFactor; + const float worldZ = ((j) + (chunk.offsetZ / chunk.scaleFactor)) * chunk.scaleFactor; + + NiPoint3 spawnPos(worldX, worldY, worldZ); EntityInfo info; info.lot = lot + sceneID; // to show different scenes info.pos = spawnPos; info.rot = QuatUtils::IDENTITY; diff --git a/dZoneManager/Raw.cpp b/dZoneManager/Raw.cpp index 3431eedf..e8af952c 100644 --- a/dZoneManager/Raw.cpp +++ b/dZoneManager/Raw.cpp @@ -4,6 +4,7 @@ #include "SceneColor.h" #include #include +#include namespace Raw { @@ -51,22 +52,20 @@ namespace Raw { */ static bool ReadChunk(std::istream& stream, Chunk& chunk, uint16_t version) { try { - // Read basic chunk info - BinaryIO::BinaryRead(stream, chunk.id); - if (stream.fail()) { - return false; - } + // Read basic chunk info + BinaryIO::BinaryRead(stream, chunk.id); + if (stream.fail()) { + return false; + } - BinaryIO::BinaryRead(stream, chunk.width); - BinaryIO::BinaryRead(stream, chunk.height); - BinaryIO::BinaryRead(stream, chunk.offsetWorldX); - BinaryIO::BinaryRead(stream, chunk.offsetWorldZ); + BinaryIO::BinaryRead(stream, chunk.width); + BinaryIO::BinaryRead(stream, chunk.height); + BinaryIO::BinaryRead(stream, chunk.offsetX); + BinaryIO::BinaryRead(stream, chunk.offsetZ); - if (stream.fail()) { - return false; - } - - // For version < 32, shader ID comes before texture IDs + if (stream.fail()) { + return false; + } // For version < 32, shader ID comes before texture IDs if (version < 32) { BinaryIO::BinaryRead(stream, chunk.shaderId); } @@ -264,9 +263,30 @@ namespace Raw { return false; } } + + // Calculate terrain bounds from all chunks + if (!outRaw.chunks.empty()) { + outRaw.minBoundsX = std::numeric_limits::max(); + outRaw.minBoundsZ = std::numeric_limits::max(); + outRaw.maxBoundsX = std::numeric_limits::lowest(); + outRaw.maxBoundsZ = std::numeric_limits::lowest(); + + for (const auto& chunk : outRaw.chunks) { + // Calculate chunk bounds + const float chunkMinX = chunk.offsetX; + const float chunkMinZ = chunk.offsetZ; + const float chunkMaxX = chunkMinX + (chunk.width * chunk.scaleFactor); + const float chunkMaxZ = chunkMinZ + (chunk.height * chunk.scaleFactor); + + // Update overall bounds + outRaw.minBoundsX = std::min(outRaw.minBoundsX, chunkMinX); + outRaw.minBoundsZ = std::min(outRaw.minBoundsZ, chunkMinZ); + outRaw.maxBoundsX = std::max(outRaw.maxBoundsX, chunkMaxX); + outRaw.maxBoundsZ = std::max(outRaw.maxBoundsZ, chunkMaxZ); + } LOG("Raw terrain bounds: X[%.2f, %.2f], Z[%.2f, %.2f]", + outRaw.minBoundsX, outRaw.maxBoundsX, outRaw.minBoundsZ, outRaw.maxBoundsZ); } - - return true; + } return true; } catch (const std::exception&) { return false; } @@ -302,40 +322,32 @@ namespace Raw { const uint32_t heightIndex = chunk.width * i + j; if (heightIndex >= chunk.heightMap.size()) continue; - const float y = chunk.heightMap[heightIndex]; + const float y = chunk.heightMap[heightIndex]; - // Calculate world position - // Based on RawFile::GenerateFinalMeshFromChunks in dTerrain: - // tempVert.SetX(tempVert.GetX() + (chunk->m_X / chunk->m_HeightMap->m_ScaleFactor)); - // tempVert.SetY(tempVert.GetY() / chunk->m_HeightMap->m_ScaleFactor); - // tempVert.SetZ(tempVert.GetZ() + (chunk->m_Z / chunk->m_HeightMap->m_ScaleFactor)); - // tempVert *= chunk->m_HeightMap->m_ScaleFactor; - - float worldX = (static_cast(i) + (chunk.offsetWorldX / chunk.scaleFactor)) * chunk.scaleFactor; - float worldY = (y / chunk.scaleFactor) * chunk.scaleFactor; - float worldZ = (static_cast(j) + (chunk.offsetWorldZ / chunk.scaleFactor)) * chunk.scaleFactor; + // Calculate world position + const float worldX = ((i) + (chunk.offsetX / chunk.scaleFactor)) * chunk.scaleFactor; + const float worldY = (y / chunk.scaleFactor) * chunk.scaleFactor; + const float worldZ = ((j) + (chunk.offsetZ / chunk.scaleFactor)) * chunk.scaleFactor; - NiPoint3 worldPos(worldX, worldY, worldZ); + const NiPoint3 worldPos(worldX, worldY, worldZ); - // Get scene ID at this position - // Map heightmap position to scene map position - // The scene map is colorMapResolution x colorMapResolution - // We need to map from heightmap coordinates (i, j) to scene map coordinates - const float sceneMapI = (static_cast(i) / static_cast(chunk.width - 1)) * static_cast(chunk.colorMapResolution - 1); - const float sceneMapJ = (static_cast(j) / static_cast(chunk.height - 1)) * static_cast(chunk.colorMapResolution - 1); - - const uint32_t sceneI = std::min(static_cast(sceneMapI), chunk.colorMapResolution - 1); - const uint32_t sceneJ = std::min(static_cast(sceneMapJ), chunk.colorMapResolution - 1); - // Scene map uses the same indexing pattern as heightmap: row * width + col - const uint32_t sceneIndex = sceneI * chunk.colorMapResolution + sceneJ; + // Get scene ID at this position + // Map heightmap position to scene map position + // The scene map is colorMapResolution x colorMapResolution + // We need to map from heightmap coordinates (i, j) to scene map coordinates + const float sceneMapI = ((i) / (chunk.width - 1)) * (chunk.colorMapResolution - 1); + const float sceneMapJ = ((j) / (chunk.height - 1)) * (chunk.colorMapResolution - 1); + + const uint32_t sceneI = std::min(static_cast(sceneMapI), chunk.colorMapResolution - 1); + const uint32_t sceneJ = std::min(static_cast(sceneMapJ), chunk.colorMapResolution - 1); + // Scene map uses the same indexing pattern as heightmap: row * width + col + const uint32_t sceneIndex = sceneI * chunk.colorMapResolution + sceneJ; - uint8_t sceneID = 0; - if (sceneIndex < chunk.sceneMap.size()) { - sceneID = chunk.sceneMap[sceneIndex]; - } - outMesh.vertices.emplace_back(worldPos, sceneID); - - // Generate triangles (same pattern as dTerrain) + uint8_t sceneID = 0; + if (sceneIndex < chunk.sceneMap.size()) { + sceneID = chunk.sceneMap[sceneIndex]; + } + outMesh.vertices.emplace_back(worldPos, sceneID); if (i > 0 && j > 0) { const uint32_t currentVert = vertexOffset + chunk.width * i + j; const uint32_t leftVert = currentVert - 1; diff --git a/dZoneManager/Raw.h b/dZoneManager/Raw.h index fa65cded..afe611fe 100644 --- a/dZoneManager/Raw.h +++ b/dZoneManager/Raw.h @@ -68,8 +68,8 @@ struct Chunk { uint32_t id; uint32_t width; uint32_t height; - float offsetWorldX; - float offsetWorldZ; + float offsetX; + float offsetZ; uint32_t shaderId; // Texture IDs (4 textures per chunk) @@ -119,6 +119,12 @@ struct Raw { uint32_t numChunksWidth = 0; uint32_t numChunksHeight = 0; std::vector chunks; + + // Calculated bounds of the entire terrain + float minBoundsX = 0.0f; + float minBoundsZ = 0.0f; + float maxBoundsX = 0.0f; + float maxBoundsZ = 0.0f; }; /** diff --git a/dZoneManager/dZoneManager.cpp b/dZoneManager/dZoneManager.cpp index 5e064ce3..530bb55f 100644 --- a/dZoneManager/dZoneManager.cpp +++ b/dZoneManager/dZoneManager.cpp @@ -313,23 +313,27 @@ LWOSCENEID dZoneManager::GetSceneIDFromPosition(const NiPoint3& position) const return LWOSCENEID_INVALID; } + // Convert 3D position to 2D (XZ plane) and clamp to terrain bounds + float posX = std::clamp(position.x, raw.minBoundsX, raw.maxBoundsX); + float posZ = std::clamp(position.z, raw.minBoundsZ, raw.maxBoundsZ); + // Find the chunk containing this position // Reverse the world position calculation from GenerateTerrainMesh for (const auto& chunk : raw.chunks) { if (chunk.sceneMap.empty()) continue; - // Reverse: worldX = (i + offsetWorldX/scaleFactor) * scaleFactor - // Therefore: i = worldX/scaleFactor - offsetWorldX/scaleFactor - const float heightI = position.x / chunk.scaleFactor - (chunk.offsetWorldX / chunk.scaleFactor); - const float heightJ = position.z / chunk.scaleFactor - (chunk.offsetWorldZ / chunk.scaleFactor); + // Reverse: worldX = (i + offsetX/scaleFactor) * scaleFactor + // Therefore: i = worldX/scaleFactor - offsetX/scaleFactor + const float heightI = posX / chunk.scaleFactor - (chunk.offsetX / chunk.scaleFactor); + const float heightJ = posZ / chunk.scaleFactor - (chunk.offsetZ / chunk.scaleFactor); // Check if position is within this chunk's heightmap bounds - if (heightI >= 0.0f && heightI < chunk.width && - heightJ >= 0.0f && heightJ < chunk.height) { + if (heightI >= 0.0f && heightI < static_cast(chunk.width) && + heightJ >= 0.0f && heightJ < static_cast(chunk.height)) { // Map heightmap position to scene map position (same as GenerateTerrainMesh) - const float sceneMapI = (heightI / (chunk.width - 1)) * (chunk.colorMapResolution - 1); - const float sceneMapJ = (heightJ / (chunk.height - 1)) * (chunk.colorMapResolution - 1); + const float sceneMapI = (heightI / static_cast(chunk.width - 1)) * static_cast(chunk.colorMapResolution - 1); + const float sceneMapJ = (heightJ / static_cast(chunk.height - 1)) * static_cast(chunk.colorMapResolution - 1); const uint32_t sceneI = std::min(static_cast(sceneMapI), chunk.colorMapResolution - 1); const uint32_t sceneJ = std::min(static_cast(sceneMapJ), chunk.colorMapResolution - 1);