#pragma once #include #include #include #include namespace wowee::core::coords { inline constexpr float TILE_SIZE = 533.33333f; inline constexpr float ZEROPOINT = 32.0f * TILE_SIZE; inline constexpr float PI = 3.14159265358979323846f; inline constexpr float TWO_PI = 6.28318530717958647692f; // ---- Canonical WoW world coordinate system (per-map) ---- // +X = North, +Y = West, +Z = Up (height) // Origin (0,0,0) is the center of the 64x64 tile grid. // Full extent: ±17066.66656 in X and Y. // // ---- Engine rendering coordinate system ---- // renderX = wowY (west), renderY = wowX (north), renderZ = wowZ (up) // Terrain vertices (MCNK) are stored directly in this space. // // ---- ADT file placement coordinate system ---- // Used by MDDF (doodads) and MODF (WMOs) records in ADT files. // Range [0, 34133.333] with center at ZEROPOINT (17066.666). // adtY = height; adtX/adtZ are horizontal. // ---- Server / emulator coordinate system ---- // WoW emulators (TrinityCore, MaNGOS, AzerothCore, CMaNGOS) send positions // over the wire as (X, Y, Z) where: // server.X = canonical.Y (west axis) // server.Y = canonical.X (north axis) // server.Z = canonical.Z (height) // This is also the byte order inside movement packets on the wire. // Convert server/wire coordinates → canonical WoW coordinates. inline glm::vec3 serverToCanonical(const glm::vec3& server) { return glm::vec3(server.y, server.x, server.z); } // Convert canonical WoW coordinates → server/wire coordinates. inline glm::vec3 canonicalToServer(const glm::vec3& canonical) { return glm::vec3(canonical.y, canonical.x, canonical.z); } // Normalize angle to [-PI, PI]. inline float normalizeAngleRad(float a) { while (a > PI) a -= TWO_PI; while (a < -PI) a += TWO_PI; return a; } // Convert server/wire yaw (radians) → canonical yaw (radians). // // Codebase canonical convention: atan2(-dy, dx) in (canonical_X=north, canonical_Y=west). // North=0, East=+π/2, South=±π, West=-π/2. // // Server direction at angle s: (cos s, sin s) in (server_X=canonical_Y, server_Y=canonical_X). // After swap: dir_c = (sin s, cos s) in (canonical_X, canonical_Y). // atan2(-dy, dx) = atan2(-cos s, sin s) = s - π/2. inline float serverToCanonicalYaw(float serverYaw) { return normalizeAngleRad(serverYaw - (PI * 0.5f)); } // Convert canonical yaw (radians) → server/wire yaw (radians). // Inverse of serverToCanonicalYaw: s = c + π/2. inline float canonicalToServerYaw(float canonicalYaw) { return normalizeAngleRad(canonicalYaw + (PI * 0.5f)); } // Convert between canonical WoW and engine rendering coordinates (just swap X/Y). inline glm::vec3 canonicalToRender(const glm::vec3& wow) { return glm::vec3(wow.y, wow.x, wow.z); } inline glm::vec3 renderToCanonical(const glm::vec3& render) { return glm::vec3(render.y, render.x, render.z); } // ADT file placement data (MDDF/MODF) -> engine rendering coordinates. inline glm::vec3 adtToWorld(float adtX, float adtY, float adtZ) { return glm::vec3( -(adtZ - ZEROPOINT), // renderX = ZP - adtZ (= wowY) -(adtX - ZEROPOINT), // renderY = ZP - adtX (= wowX) adtY // renderZ = adtY (= wowZ) ); } inline glm::vec3 adtToWorld(const glm::vec3& adt) { return adtToWorld(adt.x, adt.y, adt.z); } // Engine rendering coordinates -> ADT file placement data. inline glm::vec3 worldToAdt(float renderX, float renderY, float renderZ) { return glm::vec3( ZEROPOINT - renderY, // adtX = ZP - renderY (= ZP - wowX) renderZ, // adtY = renderZ (= wowZ, height) ZEROPOINT - renderX // adtZ = ZP - renderX (= ZP - wowY) ); } inline glm::vec3 worldToAdt(const glm::vec3& world) { return worldToAdt(world.x, world.y, world.z); } // Engine rendering coordinates -> ADT tile indices. // Returns (tileX, tileY) matching ADT filename: Map_{tileX}_{tileY}.adt // Uses canonical formula: tileN = floor(32 - wowN / TILE_SIZE) inline std::pair worldToTile(float renderX, float renderY) { // renderY = wowX (north), renderX = wowY (west) int tileX = static_cast(std::floor(32.0f - renderY / TILE_SIZE)); int tileY = static_cast(std::floor(32.0f - renderX / TILE_SIZE)); tileX = std::clamp(tileX, 0, 63); tileY = std::clamp(tileY, 0, 63); return {tileX, tileY}; } // Canonical WoW coordinates -> ADT tile indices. inline std::pair canonicalToTile(float wowX, float wowY) { int tileX = static_cast(std::floor(32.0f - wowX / TILE_SIZE)); int tileY = static_cast(std::floor(32.0f - wowY / TILE_SIZE)); tileX = std::clamp(tileX, 0, 63); tileY = std::clamp(tileY, 0, 63); return {tileX, tileY}; } } // namespace wowee::core::coords