#pragma once #include #include #include #include namespace wowee { namespace pipeline { /** * ADT chunk coordinates */ struct ADTCoord { int32_t x; int32_t y; }; /** * Heightmap for a map chunk (9x9 + 8x8 grid) */ struct HeightMap { std::array heights; // 9x9 outer + 8x8 inner vertices float getHeight(int x, int y) const; bool isLoaded() const { return heights[0] != 0.0f || heights[1] != 0.0f; } }; /** * Texture layer for a map chunk */ struct TextureLayer { uint32_t textureId; // Index into MTEX array uint32_t flags; // Layer flags uint32_t offsetMCAL; // Offset to alpha map in MCAL chunk uint32_t effectId; // Effect ID (optional) bool useAlpha() const { return (flags & 0x100) != 0; } bool compressedAlpha() const { return (flags & 0x200) != 0; } }; /** * Map chunk (256x256 units, 1/16 of ADT) */ struct MapChunk { uint32_t flags; uint32_t indexX; uint32_t indexY; uint16_t holes; // 4x4 bitmask for terrain holes (cave entrances, etc.) float position[3]; // World position (X, Y, Z) HeightMap heightMap; std::vector layers; std::vector alphaMap; // Alpha blend maps for layers // Normals (compressed) std::array normals; // X, Y, Z per vertex bool hasHeightMap() const { return heightMap.isLoaded(); } bool hasLayers() const { return !layers.empty(); } // Check if a quad has a hole (y and x are quad indices 0-7) bool isHole(int y, int x) const { int column = y / 2; int row = x / 2; int bit = 1 << (column * 4 + row); return (bit & holes) != 0; } }; /** * Complete ADT terrain tile (16x16 map chunks) */ struct ADTTerrain { bool loaded = false; uint32_t version = 0; ADTCoord coord; // ADT coordinates (e.g., 32, 49 for Azeroth) // 16x16 map chunks (256 total) std::array chunks; // Texture filenames std::vector textures; // Doodad definitions (M2 models) std::vector doodadNames; std::vector doodadIds; // WMO definitions (buildings) std::vector wmoNames; std::vector wmoIds; // Doodad placement data (from MDDF chunk) struct DoodadPlacement { uint32_t nameId; // Index into doodadNames uint32_t uniqueId; float position[3]; // X, Y, Z float rotation[3]; // Rotation in degrees uint16_t scale; // 1024 = 1.0 uint16_t flags; }; std::vector doodadPlacements; // WMO placement data (from MODF chunk) struct WMOPlacement { uint32_t nameId; // Index into wmoNames uint32_t uniqueId; float position[3]; // X, Y, Z float rotation[3]; // Rotation in degrees float extentLower[3]; // Bounding box float extentUpper[3]; uint16_t flags; uint16_t doodadSet; }; std::vector wmoPlacements; // Water/liquid data (from MH2O chunk) struct WaterLayer { uint16_t liquidType; // Type of liquid (0=water, 1=ocean, 2=magma, 3=slime) uint16_t flags; float minHeight; float maxHeight; uint8_t x; // X offset within chunk (0-7) uint8_t y; // Y offset within chunk (0-7) uint8_t width; // Width in vertices (1-9) uint8_t height; // Height in vertices (1-9) std::vector heights; // Height values (width * height) std::vector mask; // Render mask (which tiles to render) }; struct ChunkWater { std::vector layers; bool hasWater() const { return !layers.empty(); } }; std::array waterData; // Water for each chunk MapChunk& getChunk(int x, int y) { return chunks[y * 16 + x]; } const MapChunk& getChunk(int x, int y) const { return chunks[y * 16 + x]; } bool isLoaded() const { return loaded; } size_t getTextureCount() const { return textures.size(); } }; /** * ADT terrain loader * * Loads WoW 3.3.5a ADT (Azeroth Data Tile) terrain files */ class ADTLoader { public: /** * Load ADT terrain from byte data * @param adtData Raw ADT file data * @return Loaded terrain (check isLoaded()) */ static ADTTerrain load(const std::vector& adtData); private: // Chunk identifiers (as they appear in file when read as little-endian uint32) static constexpr uint32_t MVER = 0x4D564552; // Version (ASCII "MVER") static constexpr uint32_t MHDR = 0x4D484452; // Header (ASCII "MHDR") static constexpr uint32_t MCIN = 0x4D43494E; // Chunk info (ASCII "MCIN") static constexpr uint32_t MTEX = 0x4D544558; // Textures (ASCII "MTEX") static constexpr uint32_t MMDX = 0x4D4D4458; // Doodad names (ASCII "MMDX") static constexpr uint32_t MMID = 0x4D4D4944; // Doodad IDs (ASCII "MMID") static constexpr uint32_t MWMO = 0x4D574D4F; // WMO names (ASCII "MWMO") static constexpr uint32_t MWID = 0x4D574944; // WMO IDs (ASCII "MWID") static constexpr uint32_t MDDF = 0x4D444446; // Doodad placement (ASCII "MDDF") static constexpr uint32_t MODF = 0x4D4F4446; // WMO placement (ASCII "MODF") static constexpr uint32_t MH2O = 0x4D48324F; // Water/liquid (ASCII "MH2O") static constexpr uint32_t MCNK = 0x4D434E4B; // Map chunk (ASCII "MCNK") // Sub-chunks within MCNK static constexpr uint32_t MCVT = 0x4D435654; // Height values (ASCII "MCVT") static constexpr uint32_t MCNR = 0x4D434E52; // Normals (ASCII "MCNR") static constexpr uint32_t MCLY = 0x4D434C59; // Layers (ASCII "MCLY") static constexpr uint32_t MCRF = 0x4D435246; // References (ASCII "MCRF") static constexpr uint32_t MCSH = 0x4D435348; // Shadow map (ASCII "MCSH") static constexpr uint32_t MCAL = 0x4D43414C; // Alpha maps (ASCII "MCAL") static constexpr uint32_t MCLQ = 0x4D434C51; // Liquid (deprecated) (ASCII "MCLQ") struct ChunkHeader { uint32_t magic; uint32_t size; }; static bool readChunkHeader(const uint8_t* data, size_t offset, size_t dataSize, ChunkHeader& header); static uint32_t readUInt32(const uint8_t* data, size_t offset); static uint16_t readUInt16(const uint8_t* data, size_t offset); static float readFloat(const uint8_t* data, size_t offset); static void parseMVER(const uint8_t* data, size_t size, ADTTerrain& terrain); static void parseMTEX(const uint8_t* data, size_t size, ADTTerrain& terrain); static void parseMMDX(const uint8_t* data, size_t size, ADTTerrain& terrain); static void parseMWMO(const uint8_t* data, size_t size, ADTTerrain& terrain); static void parseMDDF(const uint8_t* data, size_t size, ADTTerrain& terrain); static void parseMODF(const uint8_t* data, size_t size, ADTTerrain& terrain); static void parseMCNK(const uint8_t* data, size_t size, int chunkIndex, ADTTerrain& terrain); static void parseMCVT(const uint8_t* data, size_t size, MapChunk& chunk); static void parseMCNR(const uint8_t* data, size_t size, MapChunk& chunk); static void parseMCLY(const uint8_t* data, size_t size, MapChunk& chunk); static void parseMCAL(const uint8_t* data, size_t size, MapChunk& chunk); static void parseMH2O(const uint8_t* data, size_t size, ADTTerrain& terrain); }; } // namespace pipeline } // namespace wowee