EBLAN_ENGINE/TODO_ARTEM.md
Artvasil09 a1d1baa869 feat: complete engine and editor subsystems (Stages 1-5 from TODO_ARTEM)
- Stage 1: Eliminate fish-eye distortion with precomputed tangential ray offset table and fractional DDA depth
- Stage 2: Full asset management in EblanStudio (stb_image/stb_image_write, 128x128 BGRA texture packaging, WAV sound manager)
- Stage 3: Multi-level room streaming, LevelTransition/CELL_PORTAL trigger system, 16-frame screen fade to black
- Stage 4: 60Hz cutscene engine in pure x86 ASM (v4 binary track, waypoint camera motion, shortest-arc pan, CP1251 Russian subtitles, letterbox overlay, audio)
- Stage 5: EblanStudio cutscene timeline (F6) and windowless Live ASM Viewport over shared-memory IPC directly rendered into DX11
- Tooling & Tests: comprehensive regression test suite (94 editor + 107 engine tests), IPC tests, and cutscene demo
2026-10-22 16:24:22 +03:00

225 lines
24 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ТЕХНИЧЕСКОЕ ЗАДАНИЕ ДЛЯ АРТЕМА: EBLAN ENGINE 3D & EBLANSTUDIO
> **Назначение документа**: Детальный план разработки следующих ключевых подсистем игрового 2.5D движка на ассемблере (**EBLAN ENGINE 3D**) и сопутствующего C++ визуального редактора (**EblanStudio**).
> **Целевой стек**: x86 MASM32 (движок), C++17 + Dear ImGui + DirectX 11 (редактор).
---
## Проверка реализации этапов 1–3 — 21.10.2026
- Этап 1: тангенциальные углы лучей, единственная косинусная коррекция, DDA с 8 дробными битами до проекции. В регрессионной сцене прямая стена имеет одинаковую глубину во всех 320 столбцах.
- Этап 2: PNG/JPG/BMP, BGRA и хромакей, безопасное создание и обновление пакета, стандартные слоты при первом импорте, WAV и манифесты. Портал не использует слот фонаря.
- Этап 3: EBLN v3 с чтением v1/v2, полная проверка перед заменой комнаты, обработка ошибочного перехода, спавны, затемнение и защита от повторного срабатывания при появлении в портале. F5 работает с временной копией карт и ресурсов.
- Регрессионные проверки: `python tools/test_regressions.py`. Ручная проверка интерфейса и длительный игровой прогон остаются отдельной проверкой качества.
- Этапы 4 и 5 реализованы следующим изменением, см. статус ниже.
## Реализация этапов 4–5 — 22.10.2026
- Этап 4: бинарный трек EBLN v4, чистый ASM, линейные пролёты по точкам, повороты по кратчайшей дуге, паузы, русские субтитры CP1251, letterbox 2.35:1, WAV, затемнения и переходы. Автозапуск при входе, C для повтора и Tab для пропуска; управление игроком блокируется на время сцены. Старые карты v1/v2/v3 читаются.
- Этап 5: F6 открывает таймлайн с дорожками, перетаскиванием ключей, списком команд, инспектором, установкой точек на карте, автозапуском, Undo/Redo и сохранением. Предпросмотр и перемотка показывают сцену глазами камеры во встроенном игровом виде, а на карте — траекторию. F5 запускает полную игру и связанные комнаты с проверкой WAV.
- Пример `examples/cutscene_demo/run_demo.bat` показывает пролёт, текст, звук и переход во вторую комнату; основные карты проекта он не меняет.
- Регрессионный набор расширен проверками формата v4, проигрывателей ASM/C++, оверлея, переходов, редактора и покадрового совпадения состояния.
- Уточнения исходного ТЗ: сериализуемый `dataPtr` заменён на `dataId`, строки хранятся в отдельном пуле; движение задаётся длительностью. Использованы линейные waypoints, камера во время пролёта не сталкивается со стенами. WAV воспроизводится одним каналом PlaySoundA без внешних библиотек.
## Завершение редактора и исправление F5 — 22.10.2026
- Встроен игровой вид, который получает кадры от настоящего ASM-рендерера. Доступны режимы «Рядом», «Игровой вид», «Карта», свободный осмотр и камера таймлайна. Изменения стен, цветов, спрайтов и текстур видны без сохранения.
- Исправлена блокировка F5/F6 при захвате клавиатуры интерфейсом ImGui. Ошибки запуска показываются отдельным диалогом. Начальная карта открывается относительно расположения редактора, независимо от рабочей папки.
- Автопроверки проверяют горячие клавиши с фокусом в интерфейсе, реальную передачу кадров из x86 ASM в x64 редактор и DX11, обновление геометрии, перемотку кат-сцены, точное совпадение пикселей с отдельным движком и освобождение процесса рендерера.
- Все пять этапов реализованы. Предпросмотр редактора останавливается на команде перехода между комнатами; полный сценарий переходов и игровой ввод доступны через F5. Это ограничение предпросмотра, а не отсутствие переходов в движке.
- Итоговая проверка: 94/94 сценария редактора и 107/107 сценариев ASM, совпадение всех 320 углов лучей, покадровое совпадение проигрывателей и точное совпадение пикселей встроенного вида с движком. Обе программы пересобраны. Исходные ресурсы проекта при проверках остались без изменений. Ручное управление окнами было остановлено пользователем через Esc; полный визуальный прогон интерфейса не завершён.
Разделы ниже сохраняют исходное ТЗ и описание прежних проблем как историю требований. Актуальные формат и поведение описаны в `AGENTS.md` и `docs/ENGINE_ARCHITECTURE.md`.
---
## 📋 Оглавление
1. [Задача 1: Устранение Fish-Eye эффекта и фикс рендера](#задача-1-устранение-fish-eye-эффекта-и-фикс-рендера)
2. [Задача 2: Полноценный импорт ассетов в EblanStudio](#задача-2-полноценный-импорт-ассетов-в-eblanstudio)
3. [Задача 3: Многоуровневые локации и стриминг подуровней](#задача-3-многоуровневые-локации-и-стриминг-подуровней)
4. [Задача 4: Система кат-сцен и скриптовых последовательностей](#задача-4-система-кат-сцен-и-скриптовых-последовательностей)
5. [Чек-лист готовности и порядок реализации](#чек-лист-готовности-и-порядок-реализации)
---
## Задача 1: Устранение Fish-Eye эффекта и фикс рендера [ВЫПОЛНЕНО / DONE]
### 1.1. Суть проблемы в текущем коде
В файле [`include/engine_raycaster.inc`](include/engine_raycaster.inc) лучи выпускаются с равномерным шагом угла:
```masm
; Линейное распределение углов по ширине экрана:
rayAngle = playerAngle - FOV_HALF + (screenX * FOV_ANGLES / SCREEN_W)
```
- **Геометрическое искажение**: когда экран плоский, равные интервалы пикселей по оси $X$ соответствуют разным угловым приращениям ($\tan \theta \ne \theta$). Лучи, выпущенные с постоянным шагом угла, проецируются на изогнутую цилиндрическую поверхность.
- **Артефакт "рыбьего глаза"**: стены по краям экрана кажутся вздутыми, а при повороте камеры прямые стены начинают "плавать" и изгибаться волной.
- **Ошибки DDA**: в секции `apply_fisheye_fix` расстояние повторно умножается на косинус, что при некоторых углах дает артефакт двойного искажения.
### 1.2. Решение: Тангенциальная коррекция углов лучей
Сохраняя радиальную архитектуру лучей (512 дискретных углов), пересчитать угол каждого столбца $x \in [0..319]$ через тангенс:
$$\theta_x = \text{playerAngle} + \arctan\left(\left(\frac{2x}{\text{SCREEN\_W}} - 1\right) \cdot \tan\left(\frac{\text{FOV}}{2}\right)\right)$$
#### Что нужно сделать:
1. **Таблица предвычисленных смещений лучей (`tableRayAngleOffset`)**:
- В [`include/engine_math.inc`](include/engine_math.inc) при вызове `InitMathTables` сгенерировать через FPU таблицу из 320 значений `DWORD`.
- Для каждого столбца $x$:
```c
float cameraX = (2.0f * x / 320.0f) - 1.0f;
float radOffset = atanf(cameraX * tanf(FOV_RAD / 2.0f));
int angleOffset = (int)roundf(radOffset * 512.0f / (2.0f * M_PI));
tableRayAngleOffset[x] = angleOffset;
```
2. **Использование в цикле лучей (`include/engine_raycaster.inc`)**:
- Заменить линейный расчет угла на быстрый табличный:
```masm
mov eax, screenX
mov edx, [tableRayAngleOffset + eax*4]
add edx, playerAngle
and edx, ANGLE_MASK
mov rayAngle, edx
```
3. **Коррекция перпендикулярного расстояния**:
- Перпендикулярная дистанция:
$$\text{perpWallDist} = \text{rawDist} \cdot \cos(\text{rayAngle} - \text{playerAngle})$$
- Убедиться, что `sideDistX` и `deltaDistX` не накапливают масштабирующий множитель 64 дважды при вычислении текстурных координат (`texX`).
---
## Задача 2: Полноценный импорт ассетов в EblanStudio [ВЫПОЛНЕНО / DONE]
### 2.1. Требования
У Артема и дизайнеров уровней должна быть возможность в пару кликов мыши в редакторе загружать свои изображения (PNG) и звуки (WAV) без ручного запуска сторонних питоновских скриптов.
### 2.2. Архитектура Asset Manager в C++ (`editor/`)
1. **Библиотека декодирования картинок**:
- Подключить `stb_image.h` (однофайловая C++ библиотека без внешних зависимостей) в папку `editor/src/stb_image.h`.
- Загрузка любого PNG, JPG, BMP в 32-битный массив пикселей:
```cpp
int w, h, channels;
unsigned char* data = stbi_load(filename, &w, &h, &channels, 4); // RGBA32
```
2. **Диалоговые окна выбора файлов Win32**:
- Использовать стандартный `GetOpenFileNameA` для выбора PNG/WAV файлов.
3. **Конвертация и упаковка текстур (Wall Textures & Sprites)**:
- Входное изображение ресемплится/обрезается до стандарта движка: **128x128 пикселей**.
- Цветовые каналы конвертируются из RGBA в BGRA (`00RRGGBBh`), где для прозрачных спрайтов альфа = 0 заменяется на ключевой цвет мадженты `0x00FF00FF` (`COLOR_CHROMA_KEY`).
- Автоматическая перезапись и сохранение в `assets/textures/texturepack.bin` (заголовок `EBTX` + 16 слотов по 65536 байт).
4. **Интерфейс в EblanStudio**:
- Вкладка меню: `Ассеты -> Импорт текстуры стены...` (выбор слота 1..15).
- Вкладка меню: `Ассеты -> Импорт спрайта...` (добавление в список доступных спрайтов).
- Вкладка меню: `Ассеты -> Импорт аудио (WAV)...` (копирование звука в `assets/sounds/` с автоматической генерацией алиаса MCI).
- Превью текстуры прямо в палитре тайлов в левой панели редактора.
---
---
## Задача 3: Многоуровневые локации и стриминг подуровней [ВЫПОЛНЕНО / DONE]
### 3.1. Секторно-комнатная модель уровней
Вместо одной изолированной сетки $32 \times 32$ мир организуется как **граф связанных секторов (комнат)**.
- Каждый сектор — это отдельный бинарный файл подуровня (например, `map.dat`, `sublevel_02.dat`) со своей сеткой $32 \times 32$, цветами пола/потолка и локальными спрайтами.
- Сектора соединяются **Порталами / Дверями перехода (Sector Portals)**.
### 3.2. Структура данных перехода (Portal Transition)
В заголовок и сущности карты добавлен тип `CELL_PORTAL = 10` и структура:
```cpp
// Структура перехода между подуровнями (42 байта packed)
struct LevelTransition {
uint8_t triggerCellX; // Координата X двери/триггера в текущем секторе
uint8_t triggerCellY; // Координата Y двери/триггера в текущем секторе
char targetMap[32]; // Имя целевого файла: "sublevel_02.dat"
uint16_t targetSpawnX; // Точка появления в новом секторе (X)
uint16_t targetSpawnY; // Точка появления в новом секторе (Y)
uint16_t targetAngle; // Направление взгляда после перехода
uint8_t fadeEffect; // 0 = мгновенно, 1 = затемнение экрана (Fade to Black)
uint8_t reserved; // Выравнивание
};
```
### 3.3. Логика динамической подгрузки и выгрузки в Ассемблере
В [`include/engine_map.inc`](include/engine_map.inc):
1. **Процедура перехода `SwitchSublevel(szNextMap, spawnX, spawnY, spawnAngle, fadeEffect)`**:
- **Шаг 1 (Fade Out)**: Плавное затемнение буфера экрана `screenBuffer` за 16 кадров с помощью целочисленного масштабирования цветовых каналов RGB.
- **Шаг 2 (Unload)**: Очистка текущих спрайтов (`mov spriteCount, 0`), сброс флагов и переходных триггеров.
- **Шаг 3 (Load)**: Чтение нового файла карты через `LoadSublevelBinary` в `gameMap`, обновление цветов неба/пола (`mapCeilingColor`, `mapFloorColor`), загрузка массива спрайтов нового сектора.
- **Шаг 4 (Spawn)**: Установка новых координат игрока `playerX = spawnX * 64 + 32`, `playerY = spawnY * 64 + 32`, `playerAngle = spawnAngle`.
- **Шаг 5 (Fade In)**: Плавное 16-кадровое осветление экрана.
2. **Память и производительность**:
- Постоянный объем оперативной памяти (~1-2 МБ), буферы очищаются и заполняются новым сектором на лету, позволяя создавать бесконечные цепочки и комплексы комнат.
- Во время обычного геймплея оверхед пост-процесса затемнения равен нулю (`cmp fadeState, 0; je fade_done`).
3. **Интеграция в EblanStudio**:
- Добавлен тайл `10. Портал перехода` с неоново-фиолетовой палитрой и меткой портала на 2D-сетке.
- Инспектор свойств перехода: выбор файла целевого подуровня (`.dat`), кнопка обзора файловой системы, слайдеры целевого спавна и угла, переключатель Fade to Black, быстрое удаление и кнопка мгновенного перехода к подуровню в редакторе.
- Поддержка сериализации и десериализации переходов в бинарный формат с сохранением 100% обратной совместимости.
---
## Задача 4: Система кат-сцен и скриптовых последовательностей [ВЫПОЛНЕНО / DONE]
### 4.1. Архитектура: Бинарный трек событий (Keyframe Track)
Кат-сцена представляет собой упорядоченный во времени список событий (Action Track). Во время кат-сцены управление игрока блокируется, а виртуальный режиссер интерполирует камеру, спавнит звуки и выводит диалоговые субтитры.
### 4.2. Формат команд кат-сцены
Каждая команда — это фиксированная 16-байтовая структура:
```cpp
enum CutsceneOpCode : uint8_t {
OP_END = 0, // Конец кат-сцены, возврат управления
OP_WAIT, // Ждать N кадров/тиков
OP_MOVE_CAM_TO, // Плавное перемещение камеры в (X, Y) со скоростью V
OP_PAN_CAM_TO, // Плавный поворот камеры на угол Angle
OP_SHOW_SUBTITLE, // Вывести текст субтитра на экране (ID строки)
OP_HIDE_SUBTITLE, // Скрыть субтитры
OP_PLAY_SOUND, // Проиграть звук (WAV алиас)
OP_FADE_SCREEN, // Затемнить / осветлить экран
OP_CHANGE_SUBLEVEL // Инициировать переход на другой подуровень
};
struct CutsceneEvent {
uint16_t tickTimestamp; // Кадр запуска от старта сцены
uint8_t opCode; // Тип команды (CutsceneOpCode)
uint8_t param8; // Доп. байт параметров (напр. флаг fade)
int32_t paramA; // Целевой X / угол / длительность
int32_t paramB; // Целевой Y / скорость
int32_t dataPtr; // Указатель на текст / имя звука
};
```
### 4.3. Реализация в движке (MASM32)
1. **Флаг состояния `isCutsceneActive:DWORD`**:
- Когда `isCutsceneActive == 1`, в `PollInput` игнорируются клавиши `WASD` и стрелочки.
2. **Интерполятор камеры `UpdateCutscene`**:
- Вызывается в `UpdatePlayer`.
- Если задано `targetCamX`, плавно сдвигает `playerX` к цели:
```masm
; Движение playerX -> targetCamX
mov eax, targetCamX
sub eax, playerX
; нормализация шага и добавление скорости
```
- Аналогично для угла поворота `playerAngle` по кратчайшей дуге окружности 512.
3. **Отрисовка 2D-субтитров**:
- В [`include/engine_render2d.inc`](include/engine_render2d.inc) использовать существующий `DrawString8x8` для вывода полупрозрачной плашки внизу экрана (Letterbox 2.35:1) и текста диалогов с центрированием.
### 4.4. Инструментарий в EblanStudio
- Добавить в редактор панель **"Cutscene Timeline"**:
- Горизонтальная шкала времени (в секундах / кадрах).
- Кнопки добавления ключевых точек: `[+ Камера]`, `[+ Текст]`, `[+ Звук]`, `[+ Переход]`.
- Возможность кликом по карте расставить контрольные точки пролета камеры (Spline/Waypoints).
- Кнопка предпросмотра пролета прямо в окне редактора.
---
## Чек-лист готовности и порядок реализации
| Этап | Задача | Затронутые файлы | Ожидаемый результат |
|---|---|---|---|
| **Этап 1 — готово** | Исправление Fish-Eye и угла лучей | `include/engine_math.inc`, `include/engine_raycaster.inc` | Стены остаются ровными по краям экрана, нет искажения при вращении камеры. |
| **Этап 2 — готово** | Импорт текстур и спрайтов в редакторе | `editor/src/stb_image.h`, `editor/src/editor_app.cpp` | Кнопка импорта в GUI загружает PNG и обновляет `assets/textures/texturepack.bin`. |
| **Этап 3 — готово** | Система связанных секторов (комнат) | `editor/src/level_format.h`, `include/engine_map.inc` | Переход между комнатами по триггеру с выгрузкой старой карты и загрузкой новой. |
| **Этап 4 — готово** | Движок кат-сцен и диалогов | `include/engine_cutscene.inc`, `include/engine_data.inc`, `main.asm` | Пролёт камеры по ключевым точкам, субтитры, звук, затемнения, переходы, блокировка инпута. |
| **Этап 5 — готово** | Визуальный таймлайн в EblanStudio | `editor/src/editor_cutscene.inl`, `editor/src/editor_viewport.inl`, `editor/src/cutscene_preview.h` | Редактирование траектории и сценария, предпросмотр игрового вида внутри редактора, Undo/Redo, F5 для полной игры. |
---
> *Файл подготовлен и структурирован для передачи в работу Артему.*