diff --git a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp index 6fa08d7b..1a5a22b4 100644 --- a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp +++ b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp @@ -1951,35 +1951,65 @@ namespace DEVGMCommands { return; } - // Get the pre-generated terrain mesh - const auto& terrainMesh = zone->GetTerrainMesh(); - if (terrainMesh.vertices.empty()) { - ChatPackets::SendSystemMessage(sysAddr, u"Zone does not have valid terrain mesh data."); + // Get the Raw terrain data + const auto& raw = zone->GetZoneRaw(); + if (raw.chunks.empty()) { + ChatPackets::SendSystemMessage(sysAddr, u"Zone does not have valid terrain data."); return; } - // Spawn at ALL vertices in the current scene without any filtering or spacing + // Spawn at all sceneMap points in the current scene uint32_t spawnedCount = 0; - for (const auto& vertex : terrainMesh.vertices) { - // Check if this vertex belongs to the current scene - if (LWOSCENEID(vertex.sceneID) == currentSceneID) { - // Use the vertex position - NiPoint3 spawnPos = vertex.position; - - EntityInfo info; - info.lot = lot + currentSceneID.GetSceneID(); // to differentiate scenes - info.pos = spawnPos; - info.rot = QuatUtils::IDENTITY; - info.spawner = nullptr; - info.spawnerID = entity->GetObjectID(); - info.spawnerNodeID = 0; - info.settings = { new LDFData(u"SpawnedFromSlashCommand", true) }; + for (const auto& chunk : raw.chunks) { + if (chunk.sceneMap.empty() || chunk.colorMapResolution == 0 || chunk.heightMap.empty()) continue; - Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr); - if (newEntity != nullptr) { - Game::entityManager->ConstructEntity(newEntity); - spawnedCount++; + // Iterate through the heightmap (same as GenerateTerrainMesh) + 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]; + + // 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 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]; + } + + // 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; + info.lot = lot + currentSceneID.GetSceneID(); // to differentiate scenes + info.pos = spawnPos; + info.rot = QuatUtils::IDENTITY; + info.spawner = nullptr; + info.spawnerID = entity->GetObjectID(); + info.spawnerNodeID = 0; + info.settings = { new LDFData(u"SpawnedFromSlashCommand", true) }; + + Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr); + if (newEntity != nullptr) { + Game::entityManager->ConstructEntity(newEntity); + spawnedCount++; + } + } } } } @@ -2011,34 +2041,68 @@ namespace DEVGMCommands { return; } - // Get the pre-generated terrain mesh - const auto& terrainMesh = zone->GetTerrainMesh(); - if (terrainMesh.vertices.empty()) { - ChatPackets::SendSystemMessage(sysAddr, u"Zone does not have valid terrain mesh data."); + // Get the Raw terrain data + const auto& raw = zone->GetZoneRaw(); + if (raw.chunks.empty()) { + ChatPackets::SendSystemMessage(sysAddr, u"Zone does not have valid terrain data."); return; } - // Spawn at ALL vertices without any filtering or spacing restrictions for maximum accuracy + // Spawn at all sceneMap points across all scenes uint32_t spawnedCount = 0; std::map sceneSpawnCounts; // Track spawns per scene - for (const auto& vertex : terrainMesh.vertices) { - // Skip invalid scenes (scene ID 0 typically means no scene) - if (vertex.sceneID == 0) continue; - EntityInfo info; - info.lot = lot + vertex.sceneID; // to show different scenes - info.pos = vertex.position; - info.rot = QuatUtils::IDENTITY; - info.spawner = nullptr; - info.spawnerID = entity->GetObjectID(); - info.spawnerNodeID = 0; - info.settings = { new LDFData(u"SpawnedFromSlashCommand", true) }; + for (const auto& chunk : raw.chunks) { + if (chunk.sceneMap.empty() || chunk.colorMapResolution == 0 || chunk.heightMap.empty()) continue; - Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr); - if (newEntity != nullptr) { - Game::entityManager->ConstructEntity(newEntity); - spawnedCount++; - sceneSpawnCounts[vertex.sceneID]++; + // Iterate through the heightmap (same as GenerateTerrainMesh) + 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]; + + // 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 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]; + } + + // 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; + info.lot = lot + sceneID; // to show different scenes + info.pos = spawnPos; + info.rot = QuatUtils::IDENTITY; + info.spawner = nullptr; + info.spawnerID = entity->GetObjectID(); + info.spawnerNodeID = 0; + info.settings = { new LDFData(u"SpawnedFromSlashCommand", true) }; + + Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr); + if (newEntity != nullptr) { + Game::entityManager->ConstructEntity(newEntity); + spawnedCount++; + sceneSpawnCounts[sceneID]++; + } + } } } diff --git a/dZoneManager/dZoneManager.cpp b/dZoneManager/dZoneManager.cpp index 5cb376d4..58f904d9 100644 --- a/dZoneManager/dZoneManager.cpp +++ b/dZoneManager/dZoneManager.cpp @@ -308,46 +308,49 @@ LWOSCENEID dZoneManager::GetSceneIDFromPosition(const NiPoint3& position) const const auto& terrainMesh = m_pZone->GetTerrainMesh(); - // If mesh is empty, no scene data available - if (terrainMesh.vertices.empty() || terrainMesh.triangles.empty()) { + // If no chunks, no scene data available + if (raw.chunks.empty()) { return LWOSCENEID_INVALID; } - // Find the triangle containing this position (ignoring Y coordinate for scene lookup) - // We iterate through all triangles and find the one that contains the point in 2D (XZ plane) - for (size_t i = 0; i < terrainMesh.triangles.size(); i += 3) { - const auto& v0 = terrainMesh.vertices[terrainMesh.triangles[i]]; - const auto& v1 = terrainMesh.vertices[terrainMesh.triangles[i + 1]]; - const auto& v2 = terrainMesh.vertices[terrainMesh.triangles[i + 2]]; + // Find the chunk containing this position + // Reverse the world position calculation from GenerateTerrainMesh + for (const auto& chunk : raw.chunks) { + if (chunk.sceneMap.empty()) continue; - // Check if position is inside this triangle using 2D (XZ) coordinates - // Using barycentric coordinates / cross product method - const float x = position.x; - const float z = position.z; + // 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); - const float x0 = v0.position.x; - const float z0 = v0.position.z; - const float x1 = v1.position.x; - const float z1 = v1.position.z; - const float x2 = v2.position.x; - const float z2 = v2.position.z; + // Check if position is within this chunk's heightmap bounds + if (heightI >= 0.0f && heightI < chunk.width && + heightJ >= 0.0f && heightJ < 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 uint32_t sceneI = std::min(static_cast(sceneMapI), chunk.colorMapResolution - 1); + const uint32_t sceneJ = std::min(static_cast(sceneMapJ), chunk.colorMapResolution - 1); - // Calculate barycentric coordinates - const float denom = (z1 - z2) * (x0 - x2) + (x2 - x1) * (z0 - z2); - if (std::abs(denom) < 0.0001f) continue; // Degenerate triangle + // Scene map uses the same indexing pattern as heightmap: row * width + col + const uint32_t sceneIndex = sceneI * chunk.colorMapResolution + sceneJ; - const float a = ((z1 - z2) * (x - x2) + (x2 - x1) * (z - z2)) / denom; - const float b = ((z2 - z0) * (x - x2) + (x0 - x2) * (z - z2)) / denom; - const float c = 1.0f - a - b; + // Bounds check + if (sceneIndex >= chunk.sceneMap.size()) { + return LWOSCENEID_INVALID; + } - // Point is inside triangle if all barycentric coordinates are non-negative - if (a >= 0.0f && b >= 0.0f && c >= 0.0f) { - // Return the scene ID from the first vertex (all vertices in a triangle should have the same scene ID) - return LWOSCENEID(v0.sceneID); + // Get scene ID from sceneMap + const uint8_t sceneID = chunk.sceneMap[sceneIndex]; + + // Return the scene ID + return LWOSCENEID(sceneID, 0); } } - // Position not found in any triangle + // Position not found in any chunk return LWOSCENEID_INVALID; }