# OpenWC

Портирование клиента **World of Warcraft 3.3.5a (12340)** на **Godot 4.6**.

Текущий фокус проекта: загрузка данных из клиента, разбор форматов WoW и отображение мира в Godot так, чтобы его можно было смотреть и дальше редактировать уже средствами движка.

Целевая архитектура клиента, Godot Editor-инструментов, Content Project, интеграции с TrinityCore/AzerothCore и тестовой стратегии собрана в [`docs/README.md`](docs/README.md). Фактическое состояние renderer-подсистемы ведётся в [`RENDER.md`](RENDER.md).

Текущая последовательность реализации и критерии завершения находятся в [`targets/README.md`](targets/README.md); правила работы агентов — в [`AGENTS.md`](AGENTS.md).

Сейчас проект уже умеет:

- открывать MPQ-архивы клиента через editor plugin;
- извлекать файлы из MPQ в `res://data/extracted`;
- читать `BLP2` текстуры;
- читать `ADT`-тайлы terrain;
- рендерить terrain из `ADT` с текстурными слоями `MCLY/MCAL`;
- учитывать `holes` в `MCNK`;
- читать и рендерить воду из `MH2O`;
- стримить мир чанками с LOD в runtime;
- стримить мир в **editor preview** по мере движения камеры в 3D viewport;
- читать `WDT` и собирать состав мира по данным карты, а не по сканированию папки;
- читать `WMO` и автоматически расставлять его в мире через world-level registry с дедупом по `unique_id` (WMO на границе тайлов не дублируется);
- подхватывать глобальный WMO из `WDT.MWMO/MODF` для WMO-only карт (инстансы);
- читать `M2` (MD20 + SKIN00) и ставить его через `MultiMesh` из ADT doodad placements.

Что пока не доведено до полноценного world renderer:

- вода без простого базового материала: нет нормального water shader, depth fade, shoreline blending, анимации;
- M2 renderer статический: material flags (cull/unshaded), blend modes 2+, billboards для листвы и UV-transform'ы не применяются; анимация/скиннинг не парсятся;
- импорт обратно в форматы WoW;
- полноценные инструменты редактирования мира в editor.

## Текущее состояние

### Реализовано

- `MPQManager`
  - открытие клиентских архивов;
  - список архивов по приоритету;
  - listing/extract по фильтру.
- `BLPLoader`
  - чтение `BLP2` из файла или из байтов;
  - поддержка основных сжатых форматов, достаточная для terrain textures.
- `ADTLoader`
  - `MTEX`, `MMDX/MMID`, `MWMO/MWID`;
  - `MDDF`, `MODF` (с `unique_id`, `flags`, `doodad_set`, `name_set`);
  - `MCVT`, `MCNR`, `MCLY`, `MCAL`;
  - `holes`;
  - `MH2O`.
- `WDTLoader`
  - `MVER`, `MPHD` (flags карты: global_wmo / big_alpha / sort_by_size_class);
  - `MAIN` — таблица присутствия 64×64 ADT-тайлов;
  - `MWMO` + `MODF` — глобальный WMO для инстансных/WMO-only карт.
- `ADTBuilder`
  - сборка terrain в `Node3D`;
  - terrain material с 4 слоями;
  - legacy/big/compressed alpha;
  - chunk-level LOD;
  - первый проход рендера воды.
- `StreamingWorld`
  - runtime streaming тайлов и чанков;
  - editor streaming вокруг камеры viewport;
  - состав мира берётся из `WDT` (fallback на папочный скан, если `.wdt` отсутствует);
  - отдельные `ADT`-узлы под `Terrain` в дереве сцены;
  - world-level WMO registry с refcount-дедупом по `unique_id`: WMO живёт пока на него ссылается хотя бы один загруженный тайл.
- `WMOLoader` / `WMOBuilder`
  - root/group parsing;
  - `WMOBuilder` собирает mesh по группам;
  - интегрирован в `StreamingWorld` через world-level registry;
  - глобальный WMO из `WDT` ставится один раз при загрузке карты.
- `M2Loader` / `M2Builder`
  - парсинг `MD20` + `SKIN00`: vertices/normals/UVs, materials (flags + blend_mode), texture combos, batches;
  - `M2Builder` собирает `ArrayMesh` + `StandardMaterial3D`;
  - в `StreamingWorld` расставляется через `MultiMesh` из `ADT.MDDF`.

### Частично реализовано

- `M2` renderer
  - статическая геометрия рендерится корректно;
  - material flags пока не применяются (cull_mode, unshaded, depth_write);
  - blend modes 2+ (additive / modulate) схлопнуты в обычный alpha;
  - билборды (leaf bones) не распознаются — листва деревьев плоская;
  - UV-трансформы (waterfalls, флаги, порталы) не применяются.

### Не реализовано

- скиннинг и анимации `M2` (bones, key frames);
- particles / ribbons;
- сохранение terrain/liquid/object placement обратно в WoW-форматы;
- редакторские инструменты изменения ADT/WMO данных.

## Структура проекта

- `sources/`
  - исходный клиент WoW 3.3.5a, без модификаций.
- `data/extracted/`
  - извлечённые из MPQ файлы, с которыми работает Godot.
- `data/cache/`
  - generated/offline cache: baked terrain, M2 GLB cache, WMO scene cache. Эти файлы не являются исходниками проекта и игнорируются git.
- `addons/mpq_extractor/`
  - editor plugin и GDScript-сборщики сцен.
- `src/native/`
  - C++ GDExtension: MPQ, BLP, ADT, WMO.
- `src/scenes/`
  - готовые сцены для просмотра мира.
- `src/tools/`
  - bake/conversion scripts.
- `src/resources/`
  - Godot resources, generated GLB assets and runtime resource scripts.
- `reference/`
  - external reference sources/tools: Blender WoW Studio, wow.export and Noggit RED. Runtime-project must not depend on this directory.
- `reference/noggit-red/`
  - Noggit RED source reference for WoW 3.3.5 map editing, ADT/WMO/M2 placement behavior and UID workflows.
  - внешние reference sources и tools для сверки форматов. Runtime-проект не должен зависеть от этой директории.
- `third_party/`
  - `godot-cpp`, `StormLib` и другие зависимости.

## Требования

- Windows
- Godot `4.6`
- Visual Studio 2022 с C++ workload
- CMake
- клиент **WoW 3.3.5a (12340)**

## Сборка

Собрать GDExtension:

```bat
cd src\native
build.bat Release
```

После успешной сборки DLL появится в:

```text
addons/mpq_extractor/bin/
```

VS Code/CMake использует `src/native` как `cmake.sourceDirectory`.

## Подготовка данных

По умолчанию plugin смотрит на клиент в:

```text
res://sources
```

И извлекает файлы в:

```text
res://data/extracted
```

### Через editor plugin

1. Открой проект в Godot.
2. Убедись, что plugin `MPQ Extractor` включён.
3. Внизу editor открой панель `MPQ Extractor`.
4. Укажи путь к клиенту WoW 3.3.5a.
5. Выбери locale.
6. Нажми `Open Client Archives`.
7. Для полной распаковки используй фильтр `*` и `Extract`.

Для terrain/world обычно нужны как минимум:

- `World/Maps/...`
- `Textures/...`
- `World/wmo/...`
- `DBFilesClient/...` если позже понадобится работа с DBC

## Основные классы GDExtension

### `MPQManager`

Доступен из GDScript. Основные методы:

- `open_client(base_path, locale)`
- `list_files(filter="*")`
- `extract_files(filter, output_dir)`
- `read_file(internal_path)`

### `BLPLoader`

Читает WoW `BLP2` и возвращает `Image`.

Основные методы:

- `load_image(path)`
- `load_image_from_bytes(bytes)`

### `ADTLoader`

Читает `ADT` и возвращает `Dictionary` с terrain, texture layers, placements и liquid.

Основные поля результата:

- `textures`
- `m2_names`
- `wmo_names`
- `m2_placements`
- `wmo_placements`
- `chunks`

Каждый chunk содержит:

- `index_x`, `index_y`
- `origin`
- `heights`
- `normals`
- `holes`
- `layers`
- `alpha_maps`
- `liquids`

### `WMOLoader`

Читает root/group файлы `WMO` и возвращает `Dictionary` с `materials` и `groups`.

Подключён в `StreamingWorld` через world-level registry. Дедуп по `unique_id` из `ADT.MODF`, refcount по тайлам.

### `WDTLoader`

Читает `WDT` карты и возвращает `Dictionary`:

- `version` — `MVER` (ожидается 18);
- `flags`, `has_global_wmo`, `big_alpha`, `sort_by_size_class` — из `MPHD`;
- `tiles: Array[Dictionary]` — присутствующие тайлы `{x, y, flags}` из `MAIN`;
- `global_wmo_name: String` + `global_wmo_placement: Dictionary` — если карта WMO-only.

`StreamingWorld` по умолчанию строит мир из этого списка. Папочный скан остаётся fallback'ом.

### `M2Loader`

Читает `.m2` (`MD20`) + `<basename>00.skin` и возвращает `Dictionary` с геометрией:

- `textures`, `materials` (`flags`, `blend_mode`), `texture_combos`;
- `vertices`, `normals`, `uvs`, `indices`;
- `batches: Array[Dictionary]` — `{index_start, index_count, material_id, texture_combo_index}`.

Кости, анимации и частицы пока не читаются.

## Export/bake scripts

Все проектные скрипты экспорта и запекания лежат в `src/tools/`. Скрипты с расширением `.gd` запускаются через Godot headless и требуют собранный GDExtension, потому что используют `ADTLoader`, `M2Loader` и `WMOLoader`. Перед запуском данные клиента должны быть извлечены в `res://data/extracted`.

Если `godot` не добавлен в `PATH`, используй путь из `.vscode/settings.json`, например:

```powershell
& 'c:\Godot 4.6\Godot_v4.6.1-stable_win64.exe' --headless --path . --script res://src/tools/bake_all.gd -- --map Azeroth --force
```

### `src/tools/bake_all.gd`

Запекает terrain, M2 doodads и WMO objects за один проход.

```powershell
godot --headless --path . --script res://src/tools/bake_all.gd -- --map Azeroth --force
```

Основные аргументы:

- `--map Azeroth`
- `--extracted res://data/extracted`
- `--terrain-output res://data/cache/baked_terrain_v2`
- `--m2-output res://data/cache/m2_glb`
- `--wmo-output res://data/cache/wmo_tscn`
- `--full-texture-size 2048`
- `--coarse-texture-size 512`
- `--skip-terrain`, `--skip-m2`, `--skip-wmo`
- `--force`

### `src/tools/bake_adt_terrain_cache.gd`

Запекает terrain tiles в `.res` ресурсы `BakedADTTile`. Runtime streaming читает их из `baked_tile_cache_dir`.

```powershell
godot --headless --path . --script res://src/tools/bake_adt_terrain_cache.gd -- --map Azeroth --output res://data/cache/baked_terrain_v2 --force
```

Основные аргументы:

- `--map Azeroth`
- `--extracted res://data/extracted`
- `--output res://data/cache/baked_terrain_v2`
- `--tile-x 32`, `--tile-y 48` для одного тайла
- `--full-texture-size 2048`
- `--coarse-texture-size 512`
- `--texture-size N` legacy alias для обоих размеров
- `--force`

### `src/tools/bake_m2_cache.gd`

Сканирует ADT tiles, собирает уникальные M2 paths и запекает M2 doodads в `PackedScene` `.tscn`.

```powershell
godot --headless --path . --script res://src/tools/bake_m2_cache.gd -- --map Azeroth --output res://data/cache/m2_glb --force
```

Основные аргументы:

- `--map Azeroth`
- `--extracted res://data/extracted`
- `--output res://data/cache/m2_glb`
- `--force`

### `src/tools/bake_wmo_cache.gd`

Сканирует ADT tiles, собирает уникальные WMO paths и запекает WMO world objects в `PackedScene` `.tscn`.

```powershell
godot --headless --path . --script res://src/tools/bake_wmo_cache.gd -- --map Azeroth --output res://data/cache/wmo_tscn --force
```

Основные аргументы:

- `--map Azeroth`
- `--extracted res://data/extracted`
- `--output res://data/cache/wmo_tscn`
- `--force`

### `src/tools/collect_world_m2_list.gd`

Сканирует ADT tiles и сохраняет список уникальных M2 paths. Полезно перед массовой конвертацией или для оценки объема M2 cache.

```powershell
godot --headless --path . --script res://src/tools/collect_world_m2_list.gd -- --map Azeroth --output res://data/cache/m2_glb/Azeroth_m2_list.txt
```

Основные аргументы:

- `--map Azeroth`
- `--extracted res://data/extracted`
- `--output res://data/cache/m2_glb/Azeroth_m2_list.txt`

### `src/tools/m2_to_gltf.py`

Python converter для WoW 3.3.5 M2 v264 в GLTF 2.0 `.glb`. Также экспортирует texture layers рядом с GLB. Для DXT BLP decoding нужен `Pillow`.

Один M2:

```powershell
python src/tools/m2_to_gltf.py data/extracted/Character/BloodElf/Female/BloodElfFemale.M2 src/resources/characters/BloodElf/Female
```

Рекурсивная конвертация:

```powershell
python src/tools/m2_to_gltf.py --all data/extracted/Character --out src/resources/characters
```

Аргументы:

- `input` путь к одному `.M2`
- `output_dir` директория вывода для одного `.M2`
- `--all SEARCH_DIR` рекурсивно конвертирует все `.M2` под директорией
- `--out OUT_ROOT` root вывода для `--all`; по умолчанию `src/resources`

### Reference-only export scripts

В `reference/` лежат внешние скрипты и исходники для сверки форматов, включая Blender/WoW export tools из `reference/` и `reference/wow.export/`. Они не являются частью project pipeline, не вызываются runtime-кодом и не должны использоваться как источник project cache без явного переноса в `src/tools/`.

Найденные reference scripts, связанные с export/conversion:

- `reference/blender-wow-studio-3.4-1.1.0_Experimental/io_scene_wmo/m2/export_m2.py`
- `reference/blender-wow-studio-3.4-1.1.0_Experimental/io_scene_wmo/m2/operations/convert_m2_bones.py`
- `reference/blender-wow-studio-3.4-1.1.0_Experimental/io_scene_wmo/m2/operations/m2_export_validation.py`
- `reference/blender-wow-studio-3.4-1.1.0_Experimental/io_scene_wmo/wmo/export_wmo.py`
- `reference/wow.export/addons/blender/io_scene_wowobj/animation_processor.py`
- `reference/wow.export/addons/blender/io_scene_wowobj/import_wowobj.py`
- `reference/wow.export/addons/blender/io_scene_wowobj/__init__.py`

`toolbar.py` files в reference UI directories относятся к Blender UI этих внешних tools и не являются самостоятельными export/bake entrypoints.

`reference/noggit-red/` is not an export script collection. It is a source reference for WoW 3.3.5 map editing behavior: ADT/WMO/M2 placement, UID handling, map editing workflows and related editor code.

## GDScript-сборщики

### `ADTBuilder`

Файл: `addons/mpq_extractor/loaders/adt_builder.gd`

Умеет:

- собрать весь `ADT` в `Node3D`;
- собрать отдельный chunk с нужным LOD;
- собрать water root для тайла;
- создать layered terrain material;
- загрузить BLP-текстуры через `BLPLoader`.

### `WMOBuilder`

Файл: `addons/mpq_extractor/loaders/wmo_builder.gd`

Умеет:

- собрать `Node3D` из данных `WMOLoader`;
- создать по одному `MeshInstance3D` на группу;
- назначить базовые материалы.

## Сцены просмотра

### `src/scenes/streaming/test.tscn`

Файл: `src/scenes/streaming/test.tscn`

Тестовая сцена, которая грузит квадрат тайлов вокруг центра карты.

Скрипт: `src/scenes/streaming/test_world_loader.gd`

Подходит для:

- быстрой проверки terrain;
- отладки парсинга `ADT`;
- проверки AABB и координат.

### `src/scenes/preview/adt_tile_preview.tscn`

Файл: `src/scenes/preview/adt_tile_preview.tscn`

Preview одного `ADT` прямо в editor.

Скрипт: `src/scenes/preview/adt_tile_preview.gd`

Полезно для:

- проверки одного конкретного тайла;
- отладки terrain layers;
- проверки воды и дыр по отдельному `ADT`.

Основные export-поля:

- `map_name`
- `tile_x`
- `tile_y`
- `reload_now`

### `src/scenes/streaming/eastern_kingdoms_streaming.tscn`

Файл: `src/scenes/streaming/eastern_kingdoms_streaming.tscn`

Главная сцена для стриминга мира.

Скрипт: `src/scenes/streaming/streaming_world_loader.gd`

Умеет:

- находить все доступные `ADT` тайлы в `World/Maps/Azeroth`;
- загружать/выгружать тайлы по мере перемещения;
- строить chunk mesh по LOD;
- отдельно добавлять воду на тайл;
- работать и в runtime, и в editor.

## Editor streaming

`StreamingWorld` теперь умеет подгружать мир прямо в editor по мере движения камеры viewport.

Ключевые export-поля:

- `editor_preview_enabled`
- `editor_follow_view_camera`
- `editor_preview_center_x`
- `editor_preview_center_y`
- `editor_preview_tile_radius`
- `editor_reload_now`

Логика такая:

- `editor_preview_center_x/y` задают anchor, который сдвигается к `origin`, чтобы мир было удобно смотреть в Godot;
- при `editor_follow_view_camera = true` стриминг идёт вокруг положения editor camera в 3D viewport;
- тайлы и их children появляются под `Terrain` в Scene Tree.

Это нужно, чтобы визуально проверить, что мир действительно собирается по мере перемещения по карте, а не только как статичный превью-блок.

## Runtime streaming и LOD

В runtime `StreamingWorld`:

- подгружает `ADT` тайлы по расстоянию;
- для terrain использует chunk-level LOD;
- хранит дальний мир на грубом chunk LOD;
- отдельно создаёт воду на тайл.

Основные параметры:

- `tiles_per_tick`
- `chunk_ops_per_tick`
- `lod0_radius_chunks`
- `lod1_radius_chunks`
- `lod2_radius_chunks`
- `lod2_tile_radius`

## Управление камерой

Файл: `src/scenes/camera/fly_camera.gd`

Управление:

- `RMB` удерживать для обзора;
- `WASD` движение;
- `Q/E` вниз/вверх;
- `Shift` ускорение.

## Что именно уже исправлено в terrain pipeline

По сравнению с самым ранним состоянием проекта уже поправлены:

- layout `MCNKHeader`;
- offsets внутри `MCNK`;
- чтение `MCVT`;
- чтение `MCNR`;
- legacy `MCAL`;
- big alpha / compressed alpha;
- корректные local coordinates внутри `ADT`-узла;
- holes;
- chunk winding/culling;
- editor preview одного тайла;
- streaming всего `Azeroth`;
- первый проход `MH2O`.

## Ограничения текущей версии

- terrain текстурирование уже рабочее, но ещё не 1:1 как в клиенте/Noggit во всех edge cases;
- water renderer сейчас базовый, без настоящих WoW water materials;
- `M2` рендерится статикой без билбордов, UV-анимаций и скелетной анимации;
- старые `baked_terrain_v2` tile-кэши (до добавления `unique_id` в `MODF`) не дедупят WMO на границах тайлов до перезапечки через `bake_all.gd`;
- проект сейчас ориентирован на просмотр и исследование данных, а не на полный round-trip import/export.

## Ближайшие логичные шаги

1. Довести M2 renderer:
   - **A.** Применять material flags: корректный `cull_mode`, `unshaded`, `depth_write`; blend modes 2+ (`additive`, `modulate`) вместо плоского alpha.
   - **B.** Билборды: парсить bones из `M2`, помечать billboard-флаги, для батчей ссылающихся на billboard-bone выдавать отдельные ноды с `BillboardMode = BILLBOARD_ENABLED`. Листва деревьев перестанет быть плоской.
   - **C.** UV-трансформы и vertex colors — custom shader, per-instance UV offset в `MultiMesh`. Waterfalls, порталы, флаги.
2. Скелетная анимация и скиннинг `M2` (bones, key frames, attachments) — отдельный крупный кусок.
3. Довести water shader (depth fade, shoreline blending, анимация).
4. Editor-side инструменты редактирования terrain/liquid/object placement.
5. Подготовить формат сохранения обратно в WoW-совместимые структуры.

## Быстрый старт

1. Положить клиент WoW 3.3.5a в `sources/` или указать путь через plugin.
2. Собрать расширение:

```bat
cd src\native
build.bat Release
```

3. Извлечь данные в `res://data/extracted` через `MPQ Extractor`.
4. Открыть одну из сцен:
   - `src/scenes/preview/adt_tile_preview.tscn`
   - `src/scenes/streaming/test.tscn`
   - `src/scenes/streaming/eastern_kingdoms_streaming.tscn`

Если нужен именно просмотр мира в editor по движению камеры, используй:

- `src/scenes/streaming/eastern_kingdoms_streaming.tscn`
- `editor_preview_enabled = true`
- `editor_follow_view_camera = true`
