mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 00:03:50 +00:00
Initial commit: wowee native WoW 3.3.5a client
This commit is contained in:
commit
ce6cb8f38e
147 changed files with 32347 additions and 0 deletions
210
include/pipeline/adt_loader.hpp
Normal file
210
include/pipeline/adt_loader.hpp
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
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<float, 145> 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<TextureLayer> layers;
|
||||
std::vector<uint8_t> alphaMap; // Alpha blend maps for layers
|
||||
|
||||
// Normals (compressed)
|
||||
std::array<int8_t, 145 * 3> 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<MapChunk, 256> chunks;
|
||||
|
||||
// Texture filenames
|
||||
std::vector<std::string> textures;
|
||||
|
||||
// Doodad definitions (M2 models)
|
||||
std::vector<std::string> doodadNames;
|
||||
std::vector<uint32_t> doodadIds;
|
||||
|
||||
// WMO definitions (buildings)
|
||||
std::vector<std::string> wmoNames;
|
||||
std::vector<uint32_t> 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<DoodadPlacement> 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<WMOPlacement> 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<float> heights; // Height values (width * height)
|
||||
std::vector<uint8_t> mask; // Render mask (which tiles to render)
|
||||
};
|
||||
|
||||
struct ChunkWater {
|
||||
std::vector<WaterLayer> layers;
|
||||
bool hasWater() const { return !layers.empty(); }
|
||||
};
|
||||
|
||||
std::array<ChunkWater, 256> 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<uint8_t>& 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
|
||||
107
include/pipeline/asset_manager.hpp
Normal file
107
include/pipeline/asset_manager.hpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#pragma once
|
||||
|
||||
#include "pipeline/mpq_manager.hpp"
|
||||
#include "pipeline/blp_loader.hpp"
|
||||
#include "pipeline/dbc_loader.hpp"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
/**
|
||||
* AssetManager - Unified interface for loading WoW assets
|
||||
*
|
||||
* Coordinates MPQ archives, texture loading, and database files
|
||||
*/
|
||||
class AssetManager {
|
||||
public:
|
||||
AssetManager();
|
||||
~AssetManager();
|
||||
|
||||
/**
|
||||
* Initialize asset manager
|
||||
* @param dataPath Path to WoW Data directory
|
||||
* @return true if initialization succeeded
|
||||
*/
|
||||
bool initialize(const std::string& dataPath);
|
||||
|
||||
/**
|
||||
* Shutdown and cleanup
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Check if asset manager is initialized
|
||||
*/
|
||||
bool isInitialized() const { return initialized; }
|
||||
|
||||
/**
|
||||
* Load a BLP texture
|
||||
* @param path Virtual path to BLP file (e.g., "Textures\\Minimap\\Background.blp")
|
||||
* @return BLP image (check isValid())
|
||||
*/
|
||||
BLPImage loadTexture(const std::string& path);
|
||||
|
||||
/**
|
||||
* Load a DBC file
|
||||
* @param name DBC file name (e.g., "Map.dbc")
|
||||
* @return Loaded DBC file (check isLoaded())
|
||||
*/
|
||||
std::shared_ptr<DBCFile> loadDBC(const std::string& name);
|
||||
|
||||
/**
|
||||
* Get a cached DBC file
|
||||
* @param name DBC file name
|
||||
* @return Cached DBC or nullptr if not loaded
|
||||
*/
|
||||
std::shared_ptr<DBCFile> getDBC(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* Check if a file exists in MPQ archives
|
||||
* @param path Virtual file path
|
||||
* @return true if file exists
|
||||
*/
|
||||
bool fileExists(const std::string& path) const;
|
||||
|
||||
/**
|
||||
* Read raw file data from MPQ archives
|
||||
* @param path Virtual file path
|
||||
* @return File contents (empty if not found)
|
||||
*/
|
||||
std::vector<uint8_t> readFile(const std::string& path) const;
|
||||
|
||||
/**
|
||||
* Get MPQ manager for direct access
|
||||
*/
|
||||
MPQManager& getMPQManager() { return mpqManager; }
|
||||
const MPQManager& getMPQManager() const { return mpqManager; }
|
||||
|
||||
/**
|
||||
* Get loaded DBC count
|
||||
*/
|
||||
size_t getLoadedDBCCount() const { return dbcCache.size(); }
|
||||
|
||||
/**
|
||||
* Clear all cached resources
|
||||
*/
|
||||
void clearCache();
|
||||
|
||||
private:
|
||||
bool initialized = false;
|
||||
std::string dataPath;
|
||||
|
||||
MPQManager mpqManager;
|
||||
mutable std::mutex readMutex;
|
||||
std::map<std::string, std::shared_ptr<DBCFile>> dbcCache;
|
||||
|
||||
/**
|
||||
* Normalize path for case-insensitive lookup
|
||||
*/
|
||||
std::string normalizePath(const std::string& path) const;
|
||||
};
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
110
include/pipeline/blp_loader.hpp
Normal file
110
include/pipeline/blp_loader.hpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
/**
|
||||
* BLP image format (Blizzard Picture)
|
||||
*/
|
||||
enum class BLPFormat {
|
||||
UNKNOWN = 0,
|
||||
BLP0 = 1, // Alpha channel only
|
||||
BLP1 = 2, // DXT compression or uncompressed
|
||||
BLP2 = 3 // DXT compression with mipmaps
|
||||
};
|
||||
|
||||
/**
|
||||
* BLP compression type
|
||||
*/
|
||||
enum class BLPCompression {
|
||||
NONE = 0,
|
||||
PALETTE = 1, // 256-color palette
|
||||
DXT1 = 2, // DXT1 compression (no alpha or 1-bit alpha)
|
||||
DXT3 = 3, // DXT3 compression (4-bit alpha)
|
||||
DXT5 = 4, // DXT5 compression (interpolated alpha)
|
||||
ARGB8888 = 5 // Uncompressed 32-bit ARGB
|
||||
};
|
||||
|
||||
/**
|
||||
* Loaded BLP image data
|
||||
*/
|
||||
struct BLPImage {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int channels = 4;
|
||||
int mipLevels = 1;
|
||||
BLPFormat format = BLPFormat::UNKNOWN;
|
||||
BLPCompression compression = BLPCompression::NONE;
|
||||
std::vector<uint8_t> data; // RGBA8 pixel data (decompressed)
|
||||
std::vector<std::vector<uint8_t>> mipmaps; // Mipmap levels
|
||||
|
||||
bool isValid() const { return width > 0 && height > 0 && !data.empty(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* BLP texture loader
|
||||
*
|
||||
* Supports BLP0, BLP1, BLP2 formats
|
||||
* Handles DXT1/3/5 compression and palette formats
|
||||
*/
|
||||
class BLPLoader {
|
||||
public:
|
||||
/**
|
||||
* Load BLP image from byte data
|
||||
* @param blpData Raw BLP file data
|
||||
* @return Loaded image (check isValid())
|
||||
*/
|
||||
static BLPImage load(const std::vector<uint8_t>& blpData);
|
||||
|
||||
/**
|
||||
* Get format name for debugging
|
||||
*/
|
||||
static const char* getFormatName(BLPFormat format);
|
||||
static const char* getCompressionName(BLPCompression compression);
|
||||
|
||||
private:
|
||||
// BLP1 file header — all fields after magic are uint32
|
||||
// Used by classic WoW through WotLK for many textures
|
||||
struct BLP1Header {
|
||||
char magic[4]; // 'BLP1'
|
||||
uint32_t compression; // 0=JPEG, 1=palette (uncompressed/indexed)
|
||||
uint32_t alphaBits; // 0, 1, 4, or 8
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t extra; // Flags/unknown (often 4 or 5)
|
||||
uint32_t hasMips; // 0 or 1
|
||||
uint32_t mipOffsets[16];
|
||||
uint32_t mipSizes[16];
|
||||
uint32_t palette[256]; // 256-color BGRA palette (for compression=1)
|
||||
};
|
||||
|
||||
// BLP2 file header — compression fields are uint8
|
||||
// Used by WoW from TBC onwards (coexists with BLP1 in WotLK)
|
||||
struct BLP2Header {
|
||||
char magic[4]; // 'BLP2'
|
||||
uint32_t version; // Always 1
|
||||
uint8_t compression; // 1=uncompressed/palette, 2=DXTC, 3=A8R8G8B8
|
||||
uint8_t alphaDepth; // 0, 1, 4, or 8
|
||||
uint8_t alphaEncoding; // 0=DXT1, 1=DXT3, 7=DXT5
|
||||
uint8_t hasMips; // Has mipmaps
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t mipOffsets[16];
|
||||
uint32_t mipSizes[16];
|
||||
uint32_t palette[256]; // 256-color BGRA palette (for compression=1)
|
||||
};
|
||||
|
||||
static BLPImage loadBLP1(const uint8_t* data, size_t size);
|
||||
static BLPImage loadBLP2(const uint8_t* data, size_t size);
|
||||
static void decompressDXT1(const uint8_t* src, uint8_t* dst, int width, int height);
|
||||
static void decompressDXT3(const uint8_t* src, uint8_t* dst, int width, int height);
|
||||
static void decompressDXT5(const uint8_t* src, uint8_t* dst, int width, int height);
|
||||
static void decompressPalette(const uint8_t* src, uint8_t* dst, const uint32_t* palette, int width, int height, uint8_t alphaDepth = 8);
|
||||
};
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
135
include/pipeline/dbc_loader.hpp
Normal file
135
include/pipeline/dbc_loader.hpp
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
/**
|
||||
* DBC File - WoW Database Client file
|
||||
*
|
||||
* DBC files store game database tables (spells, items, maps, creatures, etc.)
|
||||
* Format: Fixed header + fixed-size records + string block
|
||||
*/
|
||||
class DBCFile {
|
||||
public:
|
||||
DBCFile();
|
||||
~DBCFile();
|
||||
|
||||
/**
|
||||
* Load DBC file from byte data
|
||||
* @param dbcData Raw DBC file data
|
||||
* @return true if loaded successfully
|
||||
*/
|
||||
bool load(const std::vector<uint8_t>& dbcData);
|
||||
|
||||
/**
|
||||
* Check if DBC is loaded
|
||||
*/
|
||||
bool isLoaded() const { return loaded; }
|
||||
|
||||
/**
|
||||
* Get record count
|
||||
*/
|
||||
uint32_t getRecordCount() const { return recordCount; }
|
||||
|
||||
/**
|
||||
* Get field count (number of 32-bit fields per record)
|
||||
*/
|
||||
uint32_t getFieldCount() const { return fieldCount; }
|
||||
|
||||
/**
|
||||
* Get record size in bytes
|
||||
*/
|
||||
uint32_t getRecordSize() const { return recordSize; }
|
||||
|
||||
/**
|
||||
* Get string block size
|
||||
*/
|
||||
uint32_t getStringBlockSize() const { return stringBlockSize; }
|
||||
|
||||
/**
|
||||
* Get a record by index
|
||||
* @param index Record index (0 to recordCount-1)
|
||||
* @return Pointer to record data (recordSize bytes) or nullptr
|
||||
*/
|
||||
const uint8_t* getRecord(uint32_t index) const;
|
||||
|
||||
/**
|
||||
* Get a 32-bit integer field from a record
|
||||
* @param recordIndex Record index
|
||||
* @param fieldIndex Field index (0 to fieldCount-1)
|
||||
* @return Field value
|
||||
*/
|
||||
uint32_t getUInt32(uint32_t recordIndex, uint32_t fieldIndex) const;
|
||||
|
||||
/**
|
||||
* Get a 32-bit signed integer field from a record
|
||||
* @param recordIndex Record index
|
||||
* @param fieldIndex Field index
|
||||
* @return Field value
|
||||
*/
|
||||
int32_t getInt32(uint32_t recordIndex, uint32_t fieldIndex) const;
|
||||
|
||||
/**
|
||||
* Get a float field from a record
|
||||
* @param recordIndex Record index
|
||||
* @param fieldIndex Field index
|
||||
* @return Field value
|
||||
*/
|
||||
float getFloat(uint32_t recordIndex, uint32_t fieldIndex) const;
|
||||
|
||||
/**
|
||||
* Get a string field from a record
|
||||
* @param recordIndex Record index
|
||||
* @param fieldIndex Field index (contains string offset)
|
||||
* @return String value
|
||||
*/
|
||||
std::string getString(uint32_t recordIndex, uint32_t fieldIndex) const;
|
||||
|
||||
/**
|
||||
* Get string by offset in string block
|
||||
* @param offset Offset into string block
|
||||
* @return String value
|
||||
*/
|
||||
std::string getStringByOffset(uint32_t offset) const;
|
||||
|
||||
/**
|
||||
* Find a record by ID (assumes first field is ID)
|
||||
* @param id Record ID to find
|
||||
* @return Record index or -1 if not found
|
||||
*/
|
||||
int32_t findRecordById(uint32_t id) const;
|
||||
|
||||
private:
|
||||
// DBC file header (20 bytes)
|
||||
struct DBCHeader {
|
||||
char magic[4]; // 'WDBC'
|
||||
uint32_t recordCount; // Number of records
|
||||
uint32_t fieldCount; // Number of fields per record
|
||||
uint32_t recordSize; // Size of each record in bytes
|
||||
uint32_t stringBlockSize; // Size of string block
|
||||
};
|
||||
|
||||
bool loaded = false;
|
||||
uint32_t recordCount = 0;
|
||||
uint32_t fieldCount = 0;
|
||||
uint32_t recordSize = 0;
|
||||
uint32_t stringBlockSize = 0;
|
||||
|
||||
std::vector<uint8_t> recordData; // All record data
|
||||
std::vector<uint8_t> stringBlock; // String block
|
||||
|
||||
// Cache for record ID -> index lookup
|
||||
mutable std::map<uint32_t, uint32_t> idToIndexCache;
|
||||
mutable bool idCacheBuilt = false;
|
||||
|
||||
void buildIdCache() const;
|
||||
};
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
187
include/pipeline/m2_loader.hpp
Normal file
187
include/pipeline/m2_loader.hpp
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
/**
|
||||
* M2 Model Format (WoW Character/Creature Models)
|
||||
*
|
||||
* M2 files contain:
|
||||
* - Skeletal animated meshes
|
||||
* - Multiple texture units and materials
|
||||
* - Animation sequences
|
||||
* - Bone hierarchy
|
||||
* - Particle emitters, ribbon emitters, etc.
|
||||
*
|
||||
* Reference: https://wowdev.wiki/M2
|
||||
*/
|
||||
|
||||
// Animation sequence data
|
||||
struct M2Sequence {
|
||||
uint32_t id; // Animation ID
|
||||
uint32_t variationIndex; // Sub-animation index
|
||||
uint32_t duration; // Length in milliseconds
|
||||
float movingSpeed; // Speed during animation
|
||||
uint32_t flags; // Animation flags
|
||||
int16_t frequency; // Probability weight
|
||||
uint32_t replayMin; // Minimum replay delay
|
||||
uint32_t replayMax; // Maximum replay delay
|
||||
uint32_t blendTime; // Blend time in ms
|
||||
glm::vec3 boundMin; // Bounding box
|
||||
glm::vec3 boundMax;
|
||||
float boundRadius; // Bounding sphere radius
|
||||
int16_t nextAnimation; // Next animation in chain
|
||||
uint16_t aliasNext; // Alias for next animation
|
||||
};
|
||||
|
||||
// Animation track with per-sequence keyframe data
|
||||
struct M2AnimationTrack {
|
||||
uint16_t interpolationType = 0; // 0=none, 1=linear, 2=hermite, 3=bezier
|
||||
int16_t globalSequence = -1; // -1 if not a global sequence
|
||||
|
||||
struct SequenceKeys {
|
||||
std::vector<uint32_t> timestamps; // Milliseconds
|
||||
std::vector<glm::vec3> vec3Values; // For translation/scale tracks
|
||||
std::vector<glm::quat> quatValues; // For rotation tracks
|
||||
};
|
||||
std::vector<SequenceKeys> sequences; // One per animation sequence
|
||||
|
||||
bool hasData() const { return !sequences.empty(); }
|
||||
};
|
||||
|
||||
// Bone data for skeletal animation
|
||||
struct M2Bone {
|
||||
int32_t keyBoneId; // Bone ID (-1 = not key bone)
|
||||
uint32_t flags; // Bone flags
|
||||
int16_t parentBone; // Parent bone index (-1 = root)
|
||||
uint16_t submeshId; // Submesh ID
|
||||
glm::vec3 pivot; // Pivot point
|
||||
|
||||
M2AnimationTrack translation; // Position keyframes per sequence
|
||||
M2AnimationTrack rotation; // Rotation keyframes per sequence
|
||||
M2AnimationTrack scale; // Scale keyframes per sequence
|
||||
};
|
||||
|
||||
// Vertex with skinning data
|
||||
struct M2Vertex {
|
||||
glm::vec3 position;
|
||||
uint8_t boneWeights[4]; // Bone weights (0-255)
|
||||
uint8_t boneIndices[4]; // Bone indices
|
||||
glm::vec3 normal;
|
||||
glm::vec2 texCoords[2]; // Two UV sets
|
||||
};
|
||||
|
||||
// Texture unit
|
||||
struct M2Texture {
|
||||
uint32_t type; // Texture type
|
||||
uint32_t flags; // Texture flags
|
||||
std::string filename; // Texture filename (from FileData or embedded)
|
||||
};
|
||||
|
||||
// Render batch (submesh)
|
||||
struct M2Batch {
|
||||
uint8_t flags;
|
||||
int8_t priorityPlane;
|
||||
uint16_t shader; // Shader ID
|
||||
uint16_t skinSectionIndex; // Submesh index
|
||||
uint16_t colorIndex; // Color animation index
|
||||
uint16_t materialIndex; // Material index
|
||||
uint16_t materialLayer; // Material layer
|
||||
uint16_t textureCount; // Number of textures
|
||||
uint16_t textureIndex; // First texture lookup index
|
||||
uint16_t textureUnit; // Texture unit
|
||||
uint16_t transparencyIndex; // Transparency animation index
|
||||
uint16_t textureAnimIndex; // Texture animation index
|
||||
|
||||
// Render data
|
||||
uint32_t indexStart; // First index
|
||||
uint32_t indexCount; // Number of indices
|
||||
uint32_t vertexStart; // First vertex
|
||||
uint32_t vertexCount; // Number of vertices
|
||||
|
||||
// Geoset info (from submesh)
|
||||
uint16_t submeshId = 0; // Submesh/geoset ID (determines body part group)
|
||||
uint16_t submeshLevel = 0; // Submesh level (0=base, 1+=LOD/alternate mesh)
|
||||
};
|
||||
|
||||
// Attachment point (bone-anchored position for weapons, effects, etc.)
|
||||
struct M2Attachment {
|
||||
uint32_t id; // 0=Head, 1=RightHand, 2=LeftHand, etc.
|
||||
uint16_t bone; // Bone index
|
||||
glm::vec3 position; // Offset from bone pivot
|
||||
};
|
||||
|
||||
// Complete M2 model structure
|
||||
struct M2Model {
|
||||
// Model metadata
|
||||
std::string name;
|
||||
uint32_t version;
|
||||
glm::vec3 boundMin; // Model bounding box
|
||||
glm::vec3 boundMax;
|
||||
float boundRadius; // Bounding sphere
|
||||
|
||||
// Geometry data
|
||||
std::vector<M2Vertex> vertices;
|
||||
std::vector<uint16_t> indices;
|
||||
|
||||
// Skeletal animation
|
||||
std::vector<M2Bone> bones;
|
||||
std::vector<M2Sequence> sequences;
|
||||
|
||||
// Rendering
|
||||
std::vector<M2Batch> batches;
|
||||
std::vector<M2Texture> textures;
|
||||
std::vector<uint16_t> textureLookup; // Batch texture index lookup
|
||||
|
||||
// Attachment points (for weapon/effect anchoring)
|
||||
std::vector<M2Attachment> attachments;
|
||||
std::vector<uint16_t> attachmentLookup; // attachment ID → index
|
||||
|
||||
// Flags
|
||||
uint32_t globalFlags;
|
||||
|
||||
bool isValid() const {
|
||||
return !vertices.empty() && !indices.empty();
|
||||
}
|
||||
};
|
||||
|
||||
class M2Loader {
|
||||
public:
|
||||
/**
|
||||
* Load M2 model from raw file data
|
||||
*
|
||||
* @param m2Data Raw M2 file bytes
|
||||
* @return Parsed M2 model
|
||||
*/
|
||||
static M2Model load(const std::vector<uint8_t>& m2Data);
|
||||
|
||||
/**
|
||||
* Load M2 skin file (contains submesh/batch data)
|
||||
*
|
||||
* @param skinData Raw M2 skin file bytes
|
||||
* @param model Model to populate with skin data
|
||||
* @return True if successful
|
||||
*/
|
||||
static bool loadSkin(const std::vector<uint8_t>& skinData, M2Model& model);
|
||||
|
||||
/**
|
||||
* Load external .anim file data into model bone tracks
|
||||
*
|
||||
* @param m2Data Original M2 file bytes (contains track headers)
|
||||
* @param animData Raw .anim file bytes
|
||||
* @param sequenceIndex Which sequence index this .anim file provides data for
|
||||
* @param model Model to patch with animation data
|
||||
*/
|
||||
static void loadAnimFile(const std::vector<uint8_t>& m2Data,
|
||||
const std::vector<uint8_t>& animData,
|
||||
uint32_t sequenceIndex,
|
||||
M2Model& model);
|
||||
};
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
109
include/pipeline/mpq_manager.hpp
Normal file
109
include/pipeline/mpq_manager.hpp
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
// Forward declare StormLib handle
|
||||
typedef void* HANDLE;
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
/**
|
||||
* MPQManager - Manages MPQ archive loading and file reading
|
||||
*
|
||||
* WoW 3.3.5a stores all game assets in MPQ archives.
|
||||
* This manager loads multiple archives and provides unified file access.
|
||||
*/
|
||||
class MPQManager {
|
||||
public:
|
||||
MPQManager();
|
||||
~MPQManager();
|
||||
|
||||
/**
|
||||
* Initialize the MPQ system
|
||||
* @param dataPath Path to WoW Data directory
|
||||
* @return true if initialization succeeded
|
||||
*/
|
||||
bool initialize(const std::string& dataPath);
|
||||
|
||||
/**
|
||||
* Shutdown and close all archives
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Load a single MPQ archive
|
||||
* @param path Full path to MPQ file
|
||||
* @param priority Priority for file resolution (higher = checked first)
|
||||
* @return true if archive loaded successfully
|
||||
*/
|
||||
bool loadArchive(const std::string& path, int priority = 0);
|
||||
|
||||
/**
|
||||
* Check if a file exists in any loaded archive
|
||||
* @param filename Virtual file path (e.g., "World\\Maps\\Azeroth\\Azeroth.wdt")
|
||||
* @return true if file exists
|
||||
*/
|
||||
bool fileExists(const std::string& filename) const;
|
||||
|
||||
/**
|
||||
* Read a file from MPQ archives
|
||||
* @param filename Virtual file path
|
||||
* @return File contents as byte vector (empty if not found)
|
||||
*/
|
||||
std::vector<uint8_t> readFile(const std::string& filename) const;
|
||||
|
||||
/**
|
||||
* Get file size without reading it
|
||||
* @param filename Virtual file path
|
||||
* @return File size in bytes (0 if not found)
|
||||
*/
|
||||
uint32_t getFileSize(const std::string& filename) const;
|
||||
|
||||
/**
|
||||
* Check if MPQ system is initialized
|
||||
*/
|
||||
bool isInitialized() const { return initialized; }
|
||||
|
||||
/**
|
||||
* Get list of loaded archives
|
||||
*/
|
||||
const std::vector<std::string>& getLoadedArchives() const { return archiveNames; }
|
||||
|
||||
private:
|
||||
struct ArchiveEntry {
|
||||
HANDLE handle;
|
||||
std::string path;
|
||||
int priority;
|
||||
};
|
||||
|
||||
bool initialized = false;
|
||||
std::string dataPath;
|
||||
std::vector<ArchiveEntry> archives;
|
||||
std::vector<std::string> archiveNames;
|
||||
|
||||
/**
|
||||
* Find archive containing a file
|
||||
* @param filename File to search for
|
||||
* @return Archive handle or nullptr if not found
|
||||
*/
|
||||
HANDLE findFileArchive(const std::string& filename) const;
|
||||
|
||||
/**
|
||||
* Load patch archives (e.g., patch.MPQ, patch-2.MPQ, etc.)
|
||||
*/
|
||||
bool loadPatchArchives();
|
||||
|
||||
/**
|
||||
* Load locale-specific archives
|
||||
* @param locale Locale string (e.g., "enUS")
|
||||
*/
|
||||
bool loadLocaleArchives(const std::string& locale);
|
||||
};
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
136
include/pipeline/terrain_mesh.hpp
Normal file
136
include/pipeline/terrain_mesh.hpp
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
#pragma once
|
||||
|
||||
#include "pipeline/adt_loader.hpp"
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
/**
|
||||
* Vertex format for terrain rendering
|
||||
*/
|
||||
struct TerrainVertex {
|
||||
float position[3]; // X, Y, Z
|
||||
float normal[3]; // Normal vector
|
||||
float texCoord[2]; // Base texture coordinates
|
||||
float layerUV[2]; // Layer texture coordinates
|
||||
uint8_t chunkIndex; // Which chunk this vertex belongs to
|
||||
|
||||
TerrainVertex() : chunkIndex(0) {
|
||||
position[0] = position[1] = position[2] = 0.0f;
|
||||
normal[0] = normal[1] = normal[2] = 0.0f;
|
||||
texCoord[0] = texCoord[1] = 0.0f;
|
||||
layerUV[0] = layerUV[1] = 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Triangle index (3 vertices)
|
||||
*/
|
||||
using TerrainIndex = uint32_t;
|
||||
|
||||
/**
|
||||
* Renderable terrain mesh for a single map chunk
|
||||
*/
|
||||
struct ChunkMesh {
|
||||
std::vector<TerrainVertex> vertices;
|
||||
std::vector<TerrainIndex> indices;
|
||||
|
||||
// Chunk position in world space
|
||||
float worldX;
|
||||
float worldY;
|
||||
float worldZ;
|
||||
|
||||
// Chunk grid coordinates
|
||||
int chunkX;
|
||||
int chunkY;
|
||||
|
||||
// Texture layer info
|
||||
struct LayerInfo {
|
||||
uint32_t textureId;
|
||||
uint32_t flags;
|
||||
std::vector<uint8_t> alphaData; // 64x64 alpha map
|
||||
};
|
||||
std::vector<LayerInfo> layers;
|
||||
|
||||
bool isValid() const { return !vertices.empty() && !indices.empty(); }
|
||||
size_t getVertexCount() const { return vertices.size(); }
|
||||
size_t getTriangleCount() const { return indices.size() / 3; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Complete terrain tile mesh (16x16 chunks)
|
||||
*/
|
||||
struct TerrainMesh {
|
||||
std::array<ChunkMesh, 256> chunks; // 16x16 grid
|
||||
std::vector<std::string> textures; // Texture filenames
|
||||
|
||||
int validChunkCount = 0;
|
||||
|
||||
const ChunkMesh& getChunk(int x, int y) const { return chunks[y * 16 + x]; }
|
||||
ChunkMesh& getChunk(int x, int y) { return chunks[y * 16 + x]; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Terrain mesh generator
|
||||
*
|
||||
* Converts ADT heightmap data into renderable triangle meshes
|
||||
*/
|
||||
class TerrainMeshGenerator {
|
||||
public:
|
||||
/**
|
||||
* Generate terrain mesh from ADT data
|
||||
* @param terrain Loaded ADT terrain data
|
||||
* @return Generated mesh (check validChunkCount)
|
||||
*/
|
||||
static TerrainMesh generate(const ADTTerrain& terrain);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Generate mesh for a single map chunk
|
||||
*/
|
||||
static ChunkMesh generateChunkMesh(const MapChunk& chunk, int chunkX, int chunkY, int tileX, int tileY);
|
||||
|
||||
/**
|
||||
* Generate vertices from heightmap
|
||||
* WoW heightmap layout: 9x9 outer + 8x8 inner vertices (145 total)
|
||||
*/
|
||||
static std::vector<TerrainVertex> generateVertices(const MapChunk& chunk, int chunkX, int chunkY, int tileX, int tileY);
|
||||
|
||||
/**
|
||||
* Generate triangle indices
|
||||
* Creates triangles that connect the heightmap vertices
|
||||
* Skips quads that are marked as holes in the chunk
|
||||
*/
|
||||
static std::vector<TerrainIndex> generateIndices(const MapChunk& chunk);
|
||||
|
||||
/**
|
||||
* Calculate texture coordinates for vertex
|
||||
*/
|
||||
static void calculateTexCoords(TerrainVertex& vertex, int x, int y);
|
||||
|
||||
/**
|
||||
* Convert WoW's compressed normals to float
|
||||
*/
|
||||
static void decompressNormal(const int8_t* compressedNormal, float* normal);
|
||||
|
||||
/**
|
||||
* Get height at grid position from WoW's 9x9+8x8 layout
|
||||
*/
|
||||
static float getHeightAt(const HeightMap& heightMap, int x, int y);
|
||||
|
||||
/**
|
||||
* Convert grid coordinates to vertex index
|
||||
*/
|
||||
static int getVertexIndex(int x, int y);
|
||||
|
||||
// Terrain constants
|
||||
// WoW terrain: 64x64 tiles, each tile = 533.33 yards, each chunk = 33.33 yards
|
||||
static constexpr float TILE_SIZE = 533.33333f; // One ADT tile = 533.33 yards
|
||||
static constexpr float CHUNK_SIZE = TILE_SIZE / 16.0f; // One chunk = 33.33 yards (16 chunks per tile)
|
||||
static constexpr float GRID_STEP = CHUNK_SIZE / 8.0f; // 8 quads per chunk = 4.17 yards per vertex
|
||||
};
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
222
include/pipeline/wmo_loader.hpp
Normal file
222
include/pipeline/wmo_loader.hpp
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
/**
|
||||
* WMO (World Model Object) Format
|
||||
*
|
||||
* WMO files contain buildings, dungeons, and large structures.
|
||||
* Structure:
|
||||
* - Root WMO file: Contains groups, materials, doodad sets
|
||||
* - Group WMO files: Individual rooms/sections (_XXX.wmo)
|
||||
*
|
||||
* Reference: https://wowdev.wiki/WMO
|
||||
*/
|
||||
|
||||
// WMO Material
|
||||
struct WMOMaterial {
|
||||
uint32_t flags;
|
||||
uint32_t shader;
|
||||
uint32_t blendMode;
|
||||
uint32_t texture1; // Diffuse texture index
|
||||
uint32_t color1;
|
||||
uint32_t texture2; // Environment/detail texture
|
||||
uint32_t color2;
|
||||
uint32_t texture3;
|
||||
uint32_t color3;
|
||||
float runtime[4]; // Runtime data
|
||||
};
|
||||
|
||||
// WMO Group Info
|
||||
struct WMOGroupInfo {
|
||||
uint32_t flags;
|
||||
glm::vec3 boundingBoxMin;
|
||||
glm::vec3 boundingBoxMax;
|
||||
int32_t nameOffset; // Group name in MOGN chunk
|
||||
};
|
||||
|
||||
// WMO Light
|
||||
struct WMOLight {
|
||||
uint32_t type; // 0=omni, 1=spot, 2=directional, 3=ambient
|
||||
uint8_t useAttenuation;
|
||||
uint8_t pad[3];
|
||||
glm::vec4 color;
|
||||
glm::vec3 position;
|
||||
float intensity;
|
||||
float attenuationStart;
|
||||
float attenuationEnd;
|
||||
float unknown[4];
|
||||
};
|
||||
|
||||
// WMO Doodad Set (collection of M2 models placed in WMO)
|
||||
struct WMODoodadSet {
|
||||
char name[20];
|
||||
uint32_t startIndex; // First doodad in MODD
|
||||
uint32_t count; // Number of doodads
|
||||
uint32_t padding;
|
||||
};
|
||||
|
||||
// WMO Doodad Instance
|
||||
struct WMODoodad {
|
||||
uint32_t nameIndex; // Index into MODN (doodad names)
|
||||
glm::vec3 position;
|
||||
glm::quat rotation; // Quaternion rotation
|
||||
float scale;
|
||||
glm::vec4 color; // BGRA color
|
||||
};
|
||||
|
||||
// WMO Fog
|
||||
struct WMOFog {
|
||||
uint32_t flags;
|
||||
glm::vec3 position;
|
||||
float smallRadius;
|
||||
float largeRadius;
|
||||
float endDist;
|
||||
float startFactor;
|
||||
glm::vec4 color1; // End fog color
|
||||
float endDist2;
|
||||
float startFactor2;
|
||||
glm::vec4 color2; // Start fog color (blend with color1)
|
||||
};
|
||||
|
||||
// WMO Portal
|
||||
struct WMOPortal {
|
||||
uint16_t startVertex;
|
||||
uint16_t vertexCount;
|
||||
uint16_t planeIndex;
|
||||
uint16_t padding;
|
||||
};
|
||||
|
||||
// WMO Portal Plane
|
||||
struct WMOPortalPlane {
|
||||
glm::vec3 normal;
|
||||
float distance;
|
||||
};
|
||||
|
||||
// WMO Group Vertex
|
||||
struct WMOVertex {
|
||||
glm::vec3 position;
|
||||
glm::vec3 normal;
|
||||
glm::vec2 texCoord;
|
||||
glm::vec4 color; // Vertex color
|
||||
};
|
||||
|
||||
// WMO Batch (render batch)
|
||||
struct WMOBatch {
|
||||
uint32_t startIndex; // First index (this is uint32 in file format)
|
||||
uint16_t indexCount; // Number of indices
|
||||
uint16_t startVertex;
|
||||
uint16_t lastVertex;
|
||||
uint8_t flags;
|
||||
uint8_t materialId;
|
||||
};
|
||||
|
||||
// WMO Group (individual room/section)
|
||||
struct WMOGroup {
|
||||
uint32_t flags;
|
||||
glm::vec3 boundingBoxMin;
|
||||
glm::vec3 boundingBoxMax;
|
||||
uint16_t portalStart;
|
||||
uint16_t portalCount;
|
||||
uint16_t batchCountA;
|
||||
uint16_t batchCountB;
|
||||
uint32_t fogIndices[4]; // Fog references
|
||||
uint32_t liquidType;
|
||||
uint32_t groupId;
|
||||
|
||||
// Geometry
|
||||
std::vector<WMOVertex> vertices;
|
||||
std::vector<uint16_t> indices;
|
||||
std::vector<WMOBatch> batches;
|
||||
|
||||
// Portals
|
||||
std::vector<WMOPortal> portals;
|
||||
std::vector<glm::vec3> portalVertices;
|
||||
|
||||
// BSP tree (for collision - optional)
|
||||
std::vector<uint8_t> bspNodes;
|
||||
|
||||
std::string name;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
// Complete WMO Model
|
||||
struct WMOModel {
|
||||
// Root WMO data
|
||||
uint32_t version;
|
||||
uint32_t nGroups;
|
||||
uint32_t nPortals;
|
||||
uint32_t nLights;
|
||||
uint32_t nDoodadNames;
|
||||
uint32_t nDoodadDefs;
|
||||
uint32_t nDoodadSets;
|
||||
|
||||
glm::vec3 boundingBoxMin;
|
||||
glm::vec3 boundingBoxMax;
|
||||
|
||||
// Materials and textures
|
||||
std::vector<WMOMaterial> materials;
|
||||
std::vector<std::string> textures;
|
||||
std::unordered_map<uint32_t, uint32_t> textureOffsetToIndex; // MOTX offset -> texture array index
|
||||
|
||||
// Groups (rooms/sections)
|
||||
std::vector<WMOGroupInfo> groupInfo;
|
||||
std::vector<WMOGroup> groups;
|
||||
|
||||
// Portals (visibility culling)
|
||||
std::vector<WMOPortal> portals;
|
||||
std::vector<WMOPortalPlane> portalPlanes;
|
||||
std::vector<glm::vec3> portalVertices;
|
||||
|
||||
// Lights
|
||||
std::vector<WMOLight> lights;
|
||||
|
||||
// Doodads (M2 models placed in WMO)
|
||||
// Keyed by byte offset into MODN chunk (nameIndex in MODD references these offsets)
|
||||
std::unordered_map<uint32_t, std::string> doodadNames;
|
||||
std::vector<WMODoodad> doodads;
|
||||
std::vector<WMODoodadSet> doodadSets;
|
||||
|
||||
// Fog
|
||||
std::vector<WMOFog> fogs;
|
||||
|
||||
// Group names
|
||||
std::vector<std::string> groupNames;
|
||||
|
||||
bool isValid() const {
|
||||
return nGroups > 0 && !groups.empty();
|
||||
}
|
||||
};
|
||||
|
||||
class WMOLoader {
|
||||
public:
|
||||
/**
|
||||
* Load root WMO file
|
||||
*
|
||||
* @param wmoData Raw WMO file bytes
|
||||
* @return Parsed WMO model (without group geometry)
|
||||
*/
|
||||
static WMOModel load(const std::vector<uint8_t>& wmoData);
|
||||
|
||||
/**
|
||||
* Load WMO group file
|
||||
*
|
||||
* @param groupData Raw WMO group file bytes
|
||||
* @param model Model to populate with group data
|
||||
* @param groupIndex Group index to load
|
||||
* @return True if successful
|
||||
*/
|
||||
static bool loadGroup(const std::vector<uint8_t>& groupData,
|
||||
WMOModel& model,
|
||||
uint32_t groupIndex);
|
||||
};
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
Loading…
Add table
Add a link
Reference in a new issue