feat(render): integrate Control effects and animated world scene
Docs Deploy / Build and Deploy MkDocs (push) Successful in 37s
Docs Deploy / Build and Deploy MkDocs (push) Successful in 37s
This commit is contained in:
7 files changed
+8364
-2601
No files matched your search
@@ -236,11 +236,19 @@ packed: [key.colors[10], key.colors[14]],
|
||||
`AtmosphereFrame` пока не несёт этот бит обновления, поэтому sampler
|
||||
выдаёт интерполированный цвет при каждом запросе.
|
||||
|
||||
## Экранная геометрия солнца и луны
|
||||
## Отрисовка солнца, луны и бликов
|
||||
|
||||
`CSun::Render` сначала переводит направление небесного тела в пиксели текущего
|
||||
viewport, затем собирает quad с native XYZRHW-позицией. При положительной высоте
|
||||
viewport Vulkan пиксели переводятся в clip-space так:
|
||||
`CSun::Render` получает текущий viewport, проецирует направление солнца или
|
||||
луны и собирает quad уже в его пикселях. `Terrain.dll` VA `0x1004AA40` передаёт
|
||||
эту геометрию через shade callback; native FVF `0x1c4` задаёт
|
||||
`XYZRHW | DIFFUSE | SPECULAR | TEX1`. Конвертер `0x10020700` сохраняет входные
|
||||
`x`, `y` и `z`, а в `RHW` записывает `1.0`; исходный `z` равен `0.999`. Callback
|
||||
включает depth test и выключает запись depth, поэтому более близкая поверхность
|
||||
может закрыть спрайт, а сам спрайт не меняет глубину для последующих слоёв.
|
||||
|
||||
В Vulkan ряды 3 и 4 сохраняют этот contract отдельным bit 8 material mode.
|
||||
CPU каждый кадр преобразует viewport-пиксели к clip space текущего размера
|
||||
swapchain:
|
||||
|
||||
```rust
|
||||
position = [
|
||||
@@ -250,13 +258,17 @@ position = [
|
||||
];
|
||||
```
|
||||
|
||||
Vertex shader пропускает `clip_from_world` для строк солнца и луны. CPU каждый
|
||||
кадр пересчитывает центр и размер quad по текущему viewport; старые пиксельные
|
||||
координаты не переносятся на новый размер. World-space billboards бликов
|
||||
остаются отдельным путём. Материал экранного quad не освещается, vertex fog
|
||||
отключён, а sky far-depth bit к нему не применяется.
|
||||
Положительная высота Vulkan viewport отображает эти координаты в тот же
|
||||
top-down экран. Vertex shader пропускает мировую матрицу только для этого
|
||||
режима:
|
||||
|
||||
Для мировой геометрии
|
||||
```glsl
|
||||
vec4 clip_position = screen_tl
|
||||
? vec4(in_position, 1.0)
|
||||
: frame.clip_from_world * vec4(in_position, 1.0);
|
||||
```
|
||||
|
||||
Материал остаётся unlit, fog factor равен нулю. Для мировой геометрии
|
||||
оригинал задаёт viewport `MinZ = 0.1`, `MaxZ = 0.99`: после отсечения
|
||||
проекционная глубина отображается в этот диапазон:
|
||||
|
||||
@@ -280,35 +292,39 @@ RVA `0x7170`, участок `0x71E7–0x723C`, копирует MinZ/MaxZ в re
|
||||
[таблице бинарников](original-binaries.md). Нативные значения этих двух полей
|
||||
имеют биты `0x3DCCCCCD` и `0x3F7D70A4`.
|
||||
|
||||
Sky-background bit 4 продолжает помещать слои на дальнюю плоскость. Отдельные
|
||||
flare rows 5 и 6 пока используют world-space billboard builder: для них не
|
||||
подтверждён этот же native TL путь.
|
||||
|
||||
## Геометрия визуальных FX-команд
|
||||
## Эффекты Control
|
||||
|
||||
`fparkan-fx::fx_visual` формирует кадры для двух подтверждённых вариантов
|
||||
`f_gener_ball`. Op4 принимает mode 0 с положительным знаменателем в `body+0xC4`
|
||||
и выдаёт camera-facing quad; расстояние камеры считается через native
|
||||
component-wise projection родительской матрицы. Op9 принимает mode 2:
|
||||
селектор `body+0xC4` со значением 0, 1 или 2 отображается в native flag base
|
||||
3, 5 или 6 и выбирает одну из трёх сеток; категория render flags выбирает её
|
||||
детализацию. `body+0xC8` выбирает UV path:
|
||||
нулевое значение проецирует позицию вершины на XY, ненулевое использует
|
||||
фиксированные координаты quad и cap. Кадр сохраняет MAT0 resource, фазу,
|
||||
opacity, вершины и triangle indices для renderer.
|
||||
Погодное расписание `sky.ske` — только один источник эффектов. У объектов
|
||||
миссии таблица Control (`.ctl`) связывает строки с FXID-командами. Текущий
|
||||
preview выбирает начальную группу Control и исполняет только строки без
|
||||
predicate, chain и state gate. Условия игровых событий и переходы между
|
||||
группами принадлежат полному игровому Control runtime и здесь не моделируются.
|
||||
|
||||
Evaluator получает готовые command body, phase, camera point и внешнюю
|
||||
матрицу. Выбор Control row, manager gates, owner attachment, header jitter и
|
||||
global RNG остаются задачей владельца. `fparkan-game` пока использует для Op9
|
||||
категорию 0 с наибольшей детализацией; выбор категории по projected size ещё
|
||||
не реализован.
|
||||
Type 4 row задаёт локальную basis эффекта через три точки CTL point list. Для
|
||||
валидной привязки первая точка также задаёт owner node. Путь MSH выбирает позу
|
||||
этого узла из текущей pose hierarchy; для внешнего mounted-компонента к ней
|
||||
добавляется поза socket. После этого применяются object transform, header
|
||||
scale и локальная матрица FX. Подробнее о порядке матриц см.
|
||||
[описание MSH](msh.md#control-fx-и-owner-transform).
|
||||
|
||||
Для Op3 поддерживаются body-типы 0, 1 и 2. Поля отсчитываются от
|
||||
`FxCommand.raw_body`, то есть после 4-байтового command word. Типы 0 и 1
|
||||
используют общие position/scale/phase/opacity tracks и camera-facing основу;
|
||||
тип 1 и тип 2 также интерполируют ось, а тип 2 строит basis через native
|
||||
D110-порядок. Все три evaluator-а принимают уже собранную матрицу владельца и
|
||||
не выбирают Control row или attachment. Текущий путь для типов 0 и 1
|
||||
предполагает native render context 0: `Effect.dll` проверяет общий gate
|
||||
`context + 0x60 < 8`, а API этих evaluator-ов не выбирает другой context.
|
||||
Evaluator типа 2 принимает индекс context явно и учитывает этот gate в `active`.
|
||||
Renderer preview поддерживает FXID-команды Op3 modes 0–2, Op4, Op7, Op8 и Op9.
|
||||
Op3 mode0 строит camera-facing поверхность из анимируемых локальных позиции и
|
||||
масштаба; Op7/Op8 обновляют отдельные элементы particle pool; Op9 создаёт
|
||||
индексированную полусферическую сетку. Op1 вычисляется отдельно как список
|
||||
point lights: selector 1 и записи без `flags & 0x80000000` входят в общий
|
||||
diffuse light set. Записи с этим битом сохраняют native flags для специальных
|
||||
путей и projected shadows, но не добавляются в общий diffuse список.
|
||||
|
||||
Body discriminator Op3 читается из первого DWORD `FxCommand.raw_body`; offsets
|
||||
в evaluator-е относительны к данным после четырёхбайтового command word. Mode 1
|
||||
добавляет анимируемую ось к camera-facing basis, mode 2 использует native
|
||||
D110 basis. Для modes 0 и 1 preview предполагает render context 0, который
|
||||
проходит общий gate `Effect.dll` `0x106C0`; их API не выбирает другой context.
|
||||
Evaluator mode 2 принимает индекс context и учитывает gate при расчёте `active`.
|
||||
|
||||
У Op3 mode 1 ось задаёт длину прямоугольника, а не его нормаль. Для
|
||||
локального вектора к камере `v` и анимируемой оси `a` столбцы базиса имеют
|
||||
@@ -359,27 +375,66 @@ RVA `0x93D0`, ветка mode 1 с `0x9739`, сборка столбцов `0x97
|
||||
семена отдельных экземпляров лишь детерминированно заменяют process-global
|
||||
состояние и не воспроизводят общий порядок вызовов.
|
||||
|
||||
`f_gener_ball` evaluator покрывает Op4 mode 0 с положительным знаменателем в
|
||||
`body+0xC4` и Op9 mode 2. Для Op9 selectors `body+0xC4` со значениями 0, 1 и 2
|
||||
отображаются в native flag bases 3, 5 и 6. Нулевой `body+0xC8` проецирует UV на
|
||||
XY, ненулевой выбирает статические quad/cap координаты. Op4 вычисляет локальное
|
||||
расстояние камеры тем же component-wise projection, что использует native path.
|
||||
|
||||
FX-команды используют общий preview clock и ограниченный набор time modes:
|
||||
mode 0 оставляет фазу равной нулю, mode 1 вычисляет `elapsed / duration` без
|
||||
зажатия, mode 2 повторяет цикл. Header flags `0x200` и `0x20` преобразуют эту
|
||||
фазу; command path `0x8120` зажимает результат только после преобразований.
|
||||
Effect renderer проверяет header bit `0x800` только при
|
||||
render selector 0; native AniMesh component path был захвачен с selector 1,
|
||||
поэтому preview использует selector 1 и не подавляет такие visual commands.
|
||||
Этот capture также показал `0xD00` FX entries в 53-entry manager. Динамическое
|
||||
состояние manager `A8`, связанное с header gate и фактической FX phase, пока не
|
||||
моделируется. Header bit `0x10` относится к activation lifecycle и сам по себе
|
||||
не отключает draw.
|
||||
Header jitter и random shift хранятся один раз на Control row и совместно
|
||||
применяются к его sibling-командам; отдельный детерминированный поток каждой
|
||||
строки сохраняет эту связь, но не повторяет порядок глобального native RNG.
|
||||
Неразобранные группы, event predicates и неразрешённые owner attachments
|
||||
пропускаются.
|
||||
|
||||
Для visual FX material берётся из resource reference команды. MAT0 выбирает
|
||||
фазу по normalized FX phase, независимо от wall-clock анимации world materials.
|
||||
Command opacity передаётся vertex alpha, а FX MAT0 alpha не умножает её второй
|
||||
раз; texture alpha продолжает участвовать в shader. Текущая preview-ветка
|
||||
использует SourceAlpha и read-only depth state и допускает только MAT0-фазы,
|
||||
проходящие локальный predicate additive-RGB approximation. Семантика native
|
||||
`flag40` и directional-light ветки остаётся открытой.
|
||||
|
||||
Generated Op9 mesh разделяется на отдельный draw range для каждого native
|
||||
quad и cap. Каждый range получает ключ по камере до первой индексированной
|
||||
вершины, в порядке убывания расстояния. Равные ключи используют midpoint
|
||||
insertion из native queue, поэтому их порядок не является стабильным по
|
||||
первоначальному времени поступления. Preview использует category 0 как
|
||||
highest-detail mesh; projected-size LOD и полное визуальное совпадение размера
|
||||
и формы пока не реализованы.
|
||||
|
||||
## Частицы Op8
|
||||
|
||||
`fparkan-fx::smoke` разбирает body Op8, хранит кольцевой пул команды и выдаёт
|
||||
quad-ы с ориентацией к камере для активных слотов. Нативная нормализованная
|
||||
фаза окна ограничивается диапазоном 0–1 и задаёт интервал проверки следующего события;
|
||||
сравнение строгое (`schedule_base + interval < update_clock`). Все события
|
||||
catch-up в одном обновлении проверяются по тому же нормализованному интервалу;
|
||||
после события нативный код продвигает базу расписания интервалом, вычисленным
|
||||
по сырой фазе эффекта, а возраст слота считает по нормализованному интервалу.
|
||||
Когда фаза равна единственной границе окна, деление `0/0` даёт NaN, который
|
||||
нативный unordered-путь сравнения ограничивает нулём.
|
||||
фаза окна ограничивается диапазоном 0–1 и задаёт интервал проверки следующего
|
||||
события; сравнение строгое (`schedule_base + interval < update_clock`). Все
|
||||
события catch-up в одном обновлении проверяются по тому же нормализованному
|
||||
интервалу; после события нативный код продвигает базу расписания интервалом,
|
||||
вычисленным по сырой фазе эффекта, а возраст слота считает по нормализованному
|
||||
интервалу. Когда фаза равна единственной границе окна, деление `0/0` даёт NaN,
|
||||
который нативный unordered-путь сравнения ограничивает нулём.
|
||||
|
||||
Нулевая база расписания служит признаком начального состояния: обновление
|
||||
сохраняет текущую опору эмиттера и базу времени, не создавая частицу. Если
|
||||
первое время равно нулю, следующее обновление повторно захватывает опору. При
|
||||
событии скорость опоры считается от сохранённой позиции до текущей за время
|
||||
от базы расписания; слот смещает сохранённую опору на `скорость * due_interval`.
|
||||
Обновление без событий не переносит сохранённую опору к текущему владельцу.
|
||||
Откат часов корректирует только базу расписания до текущего времени: слоты,
|
||||
опора и три потока случайных
|
||||
чисел не сбрасываются.
|
||||
При нулевой скорости оригинал подставляет ось X владельца и для направления,
|
||||
и для этого смещения. Обновление без событий не переносит сохранённую опору к
|
||||
текущему владельцу. Откат часов корректирует только базу расписания до текущего
|
||||
времени: слоты, опора и три потока случайных чисел не сбрасываются.
|
||||
|
||||
Evaluator принимает отдельные начальные состояния для эмиттера, знаковых
|
||||
отклонений и материала. Переход каждого потока совпадает с общим native LFSR,
|
||||
|
||||
Reference in new issue
Block a user