EBLAN_ENGINE/TODO_ARTEM.md

189 lines
16 KiB
Markdown
Raw 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. [Задача 1: Устранение Fish-Eye эффекта и фикс рендера](#задача-1-устранение-fish-eye-эффекта-и-фикс-рендера)
2. [Задача 2: Полноценный импорт ассетов в EblanStudio](#задача-2-полноценный-импорт-ассетов-в-eblanstudio)
3. [Задача 3: Многоуровневые локации и стриминг подуровней](#задача-3-многоуровневые-локации-и-стриминг-подуровней)
4. [Задача 4: Система кат-сцен и скриптовых последовательностей](#задача-4-система-кат-сцен-и-скриптовых-последовательностей)
5. [Чек-лист готовности и порядок реализации](#чек-лист-готовности-и-порядок-реализации)
---
## Задача 1: Устранение Fish-Eye эффекта и фикс рендера
### 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
### 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: Многоуровневые локации и стриминг подуровней
### 3.1. Секторно-комнатная модель уровней
Вместо одной изолированной сетки $32 \times 32$ мир организуется как **граф связанных секторов (комнат)**.
- Каждый сектор — это отдельный бинарный файл подуровня (например, `sublevel_01.dat`, `sublevel_02.dat`) со своей сеткой $32 \times 32$, цветами пола/потолка и локальными спрайтами.
- Сектора соединяются **Порталами / Дверями перехода (Sector Portals)**.
### 3.2. Структура данных перехода (Portal Transition)
В заголовок или массив сущностей карты добавляется тип `CellType::CELL_PORTAL`:
```cpp
// Структура перехода между подуровнями
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)
};
```
### 3.3. Логика динамической подгрузки и выгрузки в Ассемблере
В [`include/engine_map.inc`](include/engine_map.inc):
1. **Процедура перехода `SwitchSublevel(szNextMap, spawnX, spawnY, spawnAngle)`**:
- **Шаг 1 (Fade Out)**: Плавное затемнение буфера экрана `screenBuffer` за 10-15 кадров (уменьшение яркости RGB).
- **Шаг 2 (Unload)**: Очистка текущих спрайтов (`mov spriteCount, 0`), сброс флагов дверей.
- **Шаг 3 (Load)**: Чтение нового файла карты в `gameMap`, обновление цветов неба/пола (`mapCeilingColor`, `mapFloorColor`), загрузка массива спрайтов нового сектора.
- **Шаг 4 (Spawn)**: Установка новых координат игрока `playerX = spawnX * 64 + 32`, `playerY = spawnY * 64 + 32`, `playerAngle = spawnAngle`.
- **Шаг 5 (Fade In)**: Плавное осветление экрана.
2. **Память**:
- Оперативная память остается постоянной (~1-2 МБ), старые комнаты полностью освобождают буферы, что позволяет создавать гигантские замки и комплексы из сотен связанных комнат.
---
## Задача 4: Система кат-сцен и скриптовых последовательностей
### 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_data.inc`, `include/engine_render2d.inc`, `main.asm` | Пролет камеры по ключевым точкам, показ диалоговых субтитров, блокировка инпута. |
| **Этап 5** | Визуальный таймлайн в EblanStudio | `editor/src/editor_app.cpp`, `editor/src/editor_app.h` | Визуальное редактирование траектории камеры и сценария кат-сцены в UI. |
---
> *Файл подготовлен и структурирован для передачи в работу Артему.*