91 lines
7 KiB
Markdown
91 lines
7 KiB
Markdown
# Архитектура и математический пайплайн 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
|
||
```
|
||
- Обеспечивает честное ретро-разрешение и пиксельный вид с нулевой нагрузкой на видеокарту.
|