diff --git a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp index e47bb25f..e3f8462f 100644 --- a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp +++ b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp @@ -2007,52 +2007,31 @@ namespace DEVGMCommands { return; } - // Spawn at all sceneMap points in the current scene uint32_t spawnedCount = 0; - + for (const auto& chunk : raw.chunks) { - if (chunk.sceneMap.empty() || chunk.colorMapResolution == 0 || chunk.heightMap.empty() - || chunk.width <= 1 || chunk.height <= 1 || chunk.scaleFactor <= 0.0f) continue; + if (!chunk.IsValidForSceneLookup()) continue; for (uint32_t i = 0; i < chunk.width; ++i) { for (uint32_t j = 0; j < chunk.height; ++j) { - const uint32_t heightIndex = chunk.width * i + j; - if (heightIndex >= chunk.heightMap.size()) continue; + if (i * chunk.width + j >= chunk.heightMap.size()) continue; - const float y = chunk.heightMap[heightIndex]; + const uint8_t sceneID = chunk.GetSceneIDAtGrid(i, j); + if (sceneID != currentSceneID.GetSceneID()) continue; - 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); + EntityInfo info; + info.lot = lot + sceneID; + info.pos = chunk.GridToWorldPos(i, j); + info.rot = QuatUtils::IDENTITY; + info.spawner = nullptr; + info.spawnerID = entity->GetObjectID(); + info.spawnerNodeID = 0; + info.settings.Insert(u"SpawnedFromSlashCommand", true); - const uint32_t sceneI = std::min(static_cast(sceneMapI), chunk.colorMapResolution - 1); - const uint32_t sceneJ = std::min(static_cast(sceneMapJ), chunk.colorMapResolution - 1); - const uint32_t sceneIndex = sceneI * chunk.colorMapResolution + sceneJ; - - uint8_t sceneID = 0; - if (sceneIndex < chunk.sceneMap.size()) { - sceneID = chunk.sceneMap[sceneIndex]; - } - - if (sceneID == currentSceneID.GetSceneID()) { - const float worldX = (static_cast(i) + (chunk.offsetX / chunk.scaleFactor)) * chunk.scaleFactor; - const float worldY = y; - const float worldZ = (static_cast(j) + (chunk.offsetZ / chunk.scaleFactor)) * chunk.scaleFactor; - - NiPoint3 spawnPos(worldX, worldY, worldZ); - EntityInfo info; - info.lot = lot + currentSceneID.GetSceneID(); - info.pos = spawnPos; - info.rot = QuatUtils::IDENTITY; - info.spawner = nullptr; - info.spawnerID = entity->GetObjectID(); - info.spawnerNodeID = 0; - info.settings.Insert(u"SpawnedFromSlashCommand", true); - - Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr); - if (newEntity != nullptr) { - Game::entityManager->ConstructEntity(newEntity); - spawnedCount++; - } + Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr); + if (newEntity != nullptr) { + Game::entityManager->ConstructEntity(newEntity); + spawnedCount++; } } } @@ -2092,43 +2071,22 @@ namespace DEVGMCommands { return; } - // Spawn at all sceneMap points across all scenes uint32_t spawnedCount = 0; - std::map sceneSpawnCounts; // Track spawns per scene - + std::map sceneSpawnCounts; + for (const auto& chunk : raw.chunks) { - if (chunk.sceneMap.empty() || chunk.colorMapResolution == 0 || chunk.heightMap.empty() - || chunk.width <= 1 || chunk.height <= 1 || chunk.scaleFactor <= 0.0f) continue; + if (!chunk.IsValidForSceneLookup()) continue; for (uint32_t i = 0; i < chunk.width; ++i) { for (uint32_t j = 0; j < chunk.height; ++j) { - const uint32_t heightIndex = chunk.width * i + j; - if (heightIndex >= chunk.heightMap.size()) continue; - - const float y = chunk.heightMap[heightIndex]; - - 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); - const uint32_t sceneIndex = sceneI * chunk.colorMapResolution + sceneJ; - - uint8_t sceneID = 0; - if (sceneIndex < chunk.sceneMap.size()) { - sceneID = chunk.sceneMap[sceneIndex]; - } + if (i * chunk.width + j >= chunk.heightMap.size()) continue; + const uint8_t sceneID = chunk.GetSceneIDAtGrid(i, j); if (sceneID == 0) continue; - const float worldX = (static_cast(i) + (chunk.offsetX / chunk.scaleFactor)) * chunk.scaleFactor; - const float worldY = y; - const float worldZ = (static_cast(j) + (chunk.offsetZ / chunk.scaleFactor)) * chunk.scaleFactor; - - NiPoint3 spawnPos(worldX, worldY, worldZ); EntityInfo info; info.lot = lot + sceneID; - info.pos = spawnPos; + info.pos = chunk.GridToWorldPos(i, j); info.rot = QuatUtils::IDENTITY; info.spawner = nullptr; info.spawnerID = entity->GetObjectID(); diff --git a/dZoneManager/Raw.cpp b/dZoneManager/Raw.cpp index 2b7e5090..c949eecf 100644 --- a/dZoneManager/Raw.cpp +++ b/dZoneManager/Raw.cpp @@ -14,6 +14,30 @@ constexpr uint32_t kMaxChunks = 1024; namespace Raw { +bool Chunk::IsValidForSceneLookup() const { + return !sceneMap.empty() && colorMapResolution > 0 && !heightMap.empty() + && scaleFactor > 0.0f && width > 1 && height > 1; +} + +uint8_t Chunk::GetSceneIDAtGrid(uint32_t i, uint32_t j) const { + const float sceneMapI = (static_cast(i) / static_cast(width - 1)) * static_cast(colorMapResolution - 1); + const float sceneMapJ = (static_cast(j) / static_cast(height - 1)) * static_cast(colorMapResolution - 1); + const uint32_t sceneI = std::min(static_cast(sceneMapI), colorMapResolution - 1); + const uint32_t sceneJ = std::min(static_cast(sceneMapJ), colorMapResolution - 1); + const uint32_t sceneIndex = sceneI * colorMapResolution + sceneJ; + if (sceneIndex >= sceneMap.size()) return 0; + return sceneMap[sceneIndex]; +} + +NiPoint3 Chunk::GridToWorldPos(uint32_t i, uint32_t j) const { + const float y = (i * width + j < heightMap.size()) ? heightMap[i * width + j] : 0.0f; + return NiPoint3( + (static_cast(i) + (offsetX / scaleFactor)) * scaleFactor, + y, + (static_cast(j) + (offsetZ / scaleFactor)) * scaleFactor + ); +} + /** * @brief Read flair attributes from stream */ @@ -386,45 +410,14 @@ namespace Raw { uint32_t vertexOffset = 0; for (const auto& chunk : raw.chunks) { - // Skip chunks without scene maps or with invalid dimensions/scale - if (chunk.sceneMap.empty() || chunk.colorMapResolution == 0 || chunk.heightMap.empty() - || chunk.scaleFactor <= 0.0f || chunk.width <= 1 || chunk.height <= 1) { - LOG("Skipping chunk %u (sceneMap: %zu, colorMapRes: %u, heightMap: %zu, scaleFactor: %f, width: %u, height: %u)", - chunk.id, chunk.sceneMap.size(), chunk.colorMapResolution, chunk.heightMap.size(), - chunk.scaleFactor, chunk.width, chunk.height); - continue; - } + if (!chunk.IsValidForSceneLookup()) continue; - LOG("Processing chunk %u: width=%u, height=%u, colorMapRes=%u, sceneMapSize=%zu", - chunk.id, chunk.width, chunk.height, chunk.colorMapResolution, chunk.sceneMap.size()); - - // Generate vertices for this chunk for (uint32_t i = 0; i < chunk.width; ++i) { for (uint32_t j = 0; j < chunk.height; ++j) { - // Get height at this position const uint32_t heightIndex = chunk.width * i + j; if (heightIndex >= chunk.heightMap.size()) continue; - - const float y = chunk.heightMap[heightIndex]; - const float worldX = (static_cast(i) + (chunk.offsetX / chunk.scaleFactor)) * chunk.scaleFactor; - const float worldY = y; - const float worldZ = (static_cast(j) + (chunk.offsetZ / chunk.scaleFactor)) * chunk.scaleFactor; - - const NiPoint3 worldPos(worldX, worldY, worldZ); - - 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); - 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); + outMesh.vertices.emplace_back(chunk.GridToWorldPos(i, j), chunk.GetSceneIDAtGrid(i, j)); 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 593d4e00..61469633 100644 --- a/dZoneManager/Raw.h +++ b/dZoneManager/Raw.h @@ -101,7 +101,10 @@ struct Chunk { std::vector meshVertUsage; std::vector meshVertSize; std::vector meshTri; - + + bool IsValidForSceneLookup() const; + uint8_t GetSceneIDAtGrid(uint32_t i, uint32_t j) const; + NiPoint3 GridToWorldPos(uint32_t i, uint32_t j) const; }; /** diff --git a/dZoneManager/Zone.cpp b/dZoneManager/Zone.cpp index 32b9bc7d..ebb1e3f1 100644 --- a/dZoneManager/Zone.cpp +++ b/dZoneManager/Zone.cpp @@ -397,7 +397,7 @@ SceneTransitionInfo Zone::LoadSceneTransitionInfo(std::istream& file) { uint32_t sceneID, layerID; BinaryIO::BinaryRead(file, sceneID); BinaryIO::BinaryRead(file, layerID); - info.sceneID = LWOSCENEID(static_cast(sceneID), layerID); + info.sceneID = LWOSCENEID(sceneID, layerID); BinaryIO::BinaryRead(file, info.position); return info; } diff --git a/dZoneManager/dZoneManager.cpp b/dZoneManager/dZoneManager.cpp index 9e029b42..aeb6b478 100644 --- a/dZoneManager/dZoneManager.cpp +++ b/dZoneManager/dZoneManager.cpp @@ -321,37 +321,17 @@ LWOSCENEID dZoneManager::GetSceneIDFromPosition(const NiPoint3& position) const // Find the chunk containing this position // Reverse the world position calculation from GenerateTerrainMesh for (const auto& chunk : raw.chunks) { - if (chunk.sceneMap.empty() || chunk.scaleFactor <= 0.0f || chunk.width <= 1 || chunk.height <= 1 || chunk.colorMapResolution == 0) continue; + if (!chunk.IsValidForSceneLookup()) continue; - // 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 < static_cast(chunk.width) && heightJ >= 0.0f && heightJ < static_cast(chunk.height)) { - - 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); - - // Scene map uses the same indexing pattern as heightmap: row * width + col - const uint32_t sceneIndex = sceneI * chunk.colorMapResolution + sceneJ; - - // Bounds check: if this chunk's sceneMap is inconsistent, skip this chunk - if (sceneIndex >= chunk.sceneMap.size()) { - LOG_DEBUG("GetSceneIDFromPosition: sceneIndex %u out of bounds (sceneMap size: %zu), skipping malformed chunk.", sceneIndex, chunk.sceneMap.size()); - continue; - } - - // Get scene ID from sceneMap - const uint8_t sceneID = chunk.sceneMap[sceneIndex]; - - // Return the scene ID - return LWOSCENEID(sceneID, 0); + const uint32_t gridI = std::min(static_cast(heightI), chunk.width - 1); + const uint32_t gridJ = std::min(static_cast(heightJ), chunk.height - 1); + return LWOSCENEID(chunk.GetSceneIDAtGrid(gridI, gridJ), 0); } }