EBLAN_ENGINE/docs/ENGINE_ARCHITECTURE.md

91 lines
7 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
## 1. Системы координат и единицы измерения
### Мировое пространство (World Space)
- Мир разделен на клетки сетки размером **64x64 единицы** (`CELL_SIZE equ 64`).
- Координаты игрока `playerX` и `playerY` хранятся в формате **16.16 signed fixed point**:
- Целая часть = `координата / 64` (индекс клетки в сетке `gameMap`).
- Дробная часть = смещение внутри клетки.
- Карта `gameMap` имеет размер **32x32 клетки** (всего 1024 байта). Индекс клетки вычисляется через сдвиг: `index = (mapY << 5) + mapX`.
### Углы и тригонометрия (Angles & Trig)
- Полная окружность (360°) разделена на **512 дискретных угловых шагов** (`ANGLE_512`):
- `0` = Восток (вправо, +X)
- `128` = Юг (вниз, +Y)
- `256` = Запад (влево, -X)
- `384` = Север (вверх, -Y)
- Угол всегда маскируется: `and eax, 511` (исключает переполнение и ветвления).
- Таблицы `tableSin` и `tableCos` предвычисляются на старте через математический сопроцессор FPU:
```masm
rad = (angle * PI) / 256
tableCos[angle] = round(cos(rad) * 65536)
tableSin[angle] = round(sin(rad) * 65536)
```
---
## 2. Конвейер лучевого рендеринга (2.5D Raycasting)
Каждый кадр для экрана шириной 320 пикселей выпускается ровно **320 лучей** (по 1 лучу на каждый вертикальный столбец экрана):
```
Левый край экрана Правый край экрана
(screenX = 0) Игрок (screenX = 319)
\ | /
\ | /
\ Угол обзора (FOV = 96) /
\ | /
\ v /
```
### Алгоритм на столбец:
1. **Вычисление угла луча**:
$$\text{rayAngle} = \left(\text{playerAngle} - \frac{\text{FOV}}{2} + \frac{\text{screenX} \cdot \text{FOV}}{320}\right) \ \& \ 511$$
2. **Трассировка по сетке (DDA - Digital Differential Analysis)**:
- Вычисляются шаг сетки (`stepX = \pm 1`, `stepY = \pm 1`) и расстояния до следующих линий сетки (`sideDistX`, `sideDistY`).
- В цикле луч шагает от клетки к клетке, пока не встретит твердую ячейку (`IsSolidCell`).
- Флаг `sideHit` фиксирует, была ли пересечена вертикальная грань ($X$) или горизонтальная ($Y$).
3. **Устранение дисторсии «рыбий глаз»**:
- Расстояние по лучу умножается на косинус разницы между углом луча и углом камеры:
$$\text{dist}_{\text{perp}} = \text{dist} \cdot \cos(\text{rayAngle} - \text{playerAngle})$$
4. **Проекция высоты стены**:
$$\text{lineHeight} = \frac{\text{CELL\_SIZE} \cdot \text{PROJ\_DIST}}{\text{dist}_{\text{perp}}}$$
- Центр проекции — середина экрана ($Y = 120$).
- Верхняя граница: $\text{drawStart} = 120 - \frac{\text{lineHeight}}{2}$.
- Нижняя граница: $\text{drawEnd} = 120 + \frac{\text{lineHeight}}{2}$.
5. **Текстурирование и затенение**:
- Вычисляется горизонтальная координата текстуры $U \in [0, 127]$.
- Текстура интерполируется по вертикали с шагом $\text{texStep} = \frac{128 \ll 16}{\text{lineHeight}}$.
- $Y$-грани затемняются на 50% (`>> 1`) для создания ощущения трехмерного объема.
- Далекие стены плавно уходят в туман (половинная яркость от 350 единиц, почти черный от 700).
---
## 3. Билборд-спрайты (Sprites)
0. Спрайты хранят позицию в **сырых мировых единицах** (1 клетка = 64 единицы, как `playerX`/`playerY`). Перед отрисовкой все спрайты сортируются по убыванию квадрата расстояния (алгоритм художника, insertion sort по `spriteOrder`).
1. Позиция каждого спрайта переводится в систему координат камеры:
$$X_{\text{cam}} = \Delta X \cdot \cos(-\text{ang}) - \Delta Y \cdot \sin(-\text{ang})$$
$$Y_{\text{cam}} = \Delta X \cdot \sin(-\text{ang}) + \Delta Y \cdot \cos(-\text{ang})$$
2. Если $Y_{\text{cam}} > 8$ (спрайт находится перед камерой):
- Проекция на экран: $\text{screenX} = \frac{X_{\text{cam}} \cdot \text{PROJ\_DIST}}{Y_{\text{cam}}} + 160$.
- Размер: $\text{size} = \frac{64 \cdot \text{PROJ\_DIST}}{Y_{\text{cam}}} \cdot \text{scale}$ (scale — 16.16 fixed).
3. Спрайт «стоит на полу»: нижняя граница = горизонт + size/2 (как низ стены на той же глубине), верхняя = низ − size.
4. Отрисовка вертикальных текстурированных полос с проверкой буфера глубины `depthBuffer[stripeX]`: если спрайт ближе стены, пиксели выводятся в `screenBuffer`. Тексели `00FF00FFh` (маджента) прозрачны. Применяется тот же дистанционный туман, что и у стен.
---
## 4. 2D Буфер кадра и вывод на экран
- `screenBuffer` — плоский массив в оперативной памяти размером $320 \times 240 \times 4 = 307\,200$ байт.
- Формат пикселя — 32-битный BGRA (`00RRGGBBh`).
- Передача кадра в окно Windows производится аппаратным вызовом GDI:
```masm
invoke StretchDIBits, hEngineDC,
0, 0, 1280, 960, ; Область окна назначения
0, 0, 320, 240, ; Область исходного буфера
OFFSET screenBuffer, OFFSET bmiEngine,
DIB_RGB_COLORS, SRCCOPY
```
- Обеспечивает честное ретро-разрешение и пиксельный вид с нулевой нагрузкой на видеокарту.