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