diff options
Diffstat (limited to 'docs/reference/environment-effects.md')
| -rw-r--r-- | docs/reference/environment-effects.md | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/docs/reference/environment-effects.md b/docs/reference/environment-effects.md index 8045d68..66ed052 100644 --- a/docs/reference/environment-effects.md +++ b/docs/reference/environment-effects.md @@ -143,6 +143,24 @@ slot `+6c`): ширина `RECT` делится на горизонтальныÐ исходную таблицу знаков и минимальный depth fade `0.1`. Renderer сам решает, как загрузить этот квадрат и как смешать его материал. +## Точечный свет из opcode 1 + +Preview вычисляет generic Op1 отдельно от визуальных команд. Переданный +владельцем `command_time` сравнивается с началом и концом интервала из body +opcode 1; обе границы включены, а коэффициент выборки равен +`(command_time - start) / (end - start)`. Заголовочная длительность FXID не +заменяет этот интервал. Для нулевой ширины адаптер не создаёт кадр света, +поскольку у команды нет конечного коэффициента интерполяции. + +Позиция, направление, цвет и range интерполируются по концам команды. +Позиция и направление переводятся через основу родителя; направление после +этого нормализуется, а нулевой вектор остаётся нулевым. Range сохраняет +авторское значение без ограничения снизу. Типы Op1 `1`, `2`, `5`, `6` и `7` +выбирают point-light selector `1`; их native flags сохраняются. Типы `3` и +`4` выбирают другие классы записей LightManager и не попадают в generic список +точечного света. У `LightningEffect` выборка тех же полей использует отдельные +ограничение по длительности FXID и правила безопасной обработки. + ## Молния и звуковые события `decode_env_lightning_fxid` извлекает из FXID длительность, ссылки opcode 3 и @@ -207,6 +225,112 @@ packed: [key.colors[10], key.colors[14]], `AtmosphereFrame` пока не несёт этот бит обновления, поэтому sampler выдаёт интерполированный цвет при каждом запросе. +## Экранная геометрия солнца и луны + +`CSun::Render` сначала переводит направление небесного тела в пиксели текущего +viewport, затем собирает quad с native XYZRHW-позицией. При положительной высоте +viewport Vulkan пиксели переводятся в clip-space так: + +```rust +position = [ + pixel[0] * (2.0 / width) - 1.0, + pixel[1] * (2.0 / height) - 1.0, + 0.999, +]; +``` + +Vertex shader пропускает `clip_from_world` для строк солнца и луны. CPU каждый +кадр пересчитывает центр и размер quad по текущему viewport; старые пиксельные +координаты не переносятся на новый размер. World-space billboards бликов +остаются отдельным путём. Материал экранного quad не освещается, vertex fog +отключён, а sky far-depth bit к нему не применяется. + +Native `z = 0.999` не означает, что спрайт гарантированно лежит за любой +поверхностью. Vulkan использует `LESS_OR_EQUAL` и не записывает depth для +прозрачного ряда. При near `0.5`, far `700` мировая поверхность на расстоянии +`320` получает depth +`700 / (700 - 0.5) * (1 - 0.5 / 320) ≈ 0.999151`; поэтому экранный `0.999` +чуть ближе этой поверхности и проходит depth test перед ней. Это фиксированное +native значение сохраняется для совместимости, но само по себе не обещает +окклюзию солнца всей геометрией местности. + +## Геометрия визуальных FX-команд + +`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. + +Evaluator получает готовые command body, phase, camera point и внешнюю +матрицу. Выбор Control row, manager gates, owner attachment, header jitter и +global RNG остаются задачей владельца. `fparkan-game` пока использует для Op9 +категорию 0 с наибольшей детализацией; выбор категории по projected size ещё +не реализован. + +Для 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`. + +## Частицы Op7 + +`fparkan-fx::op7` вычисляет фиксированный набор частиц Op7 по body команды, +фазе эффекта, матрице владельца, позиции камеры и render context. После +инициализации состояние команды сохраняет случайно изменённые треки позиции +и масштаба. Body задаёт время групп, opacity, фазу материала и вертикальное +ускорение; последнее масштабируется переданным world scalar и применяется к +копии матрицы владельца каждой активной группы. Текущий preview +`fparkan-game` передаёт `world_scalar = 10.0`; чтение действующей настройки +мира пока не подключено. + +Часы активной группы обновляются до применения detail cutoff отдельной +частицы. Cutoff сравнивает ordinal render context с постоянным порогом слота. +`Effect.dll` `0x1300` использует тот же порядок, поэтому скрытие слота не +останавливает часы его активной группы. Поток частиц последовательно задаёт +случайные изменения позиции и масштаба; для порогов видимости используется +отдельный поток. Шаг LFSR совпадает с нативным переходом двух половин, однако +семена отдельных экземпляров лишь детерминированно заменяют process-global +состояние и не воспроизводят общий порядок вызовов. + +## Частицы Op8 + +`fparkan-fx::smoke` разбирает body Op8, хранит кольцевой пул команды и выдаёт +quad-ы с ориентацией к камере для активных слотов. Нативная нормализованная +фаза окна ограничивается диапазоном 0–1 и задаёт интервал проверки следующего события; +сравнение строгое (`schedule_base + interval < update_clock`). Все события +catch-up в одном обновлении проверяются по тому же нормализованному интервалу; +после события нативный код продвигает базу расписания интервалом, вычисленным +по сырой фазе эффекта, а возраст слота считает по нормализованному интервалу. +Когда фаза равна единственной границе окна, деление `0/0` даёт NaN, который +нативный unordered-путь сравнения ограничивает нулём. + +Нулевая база расписания служит признаком начального состояния: обновление +сохраняет текущую опору эмиттера и базу времени, не создавая частицу. Если +первое время равно нулю, следующее обновление повторно захватывает опору. При +событии скорость опоры считается от сохранённой позиции до текущей за время +от базы расписания; слот смещает сохранённую опору на `скорость * due_interval`. +Обновление без событий не переносит сохранённую опору к текущему владельцу. +Откат часов корректирует только базу расписания до текущего времени: слоты, +опора и три потока случайных +чисел не сбрасываются. + +Evaluator принимает отдельные начальные состояния для эмиттера, знаковых +отклонений и материала. Переход каждого потока совпадает с общим native LFSR, +но эти локальные seed не воспроизводят порядок process-global вызовов игры. +Предел 100000 событий catch-up — защитная граница evaluator-а; соответствующего +лимита в исследованном native цикле не обнаружено. + ## Что остаётся за владельцем `EnvironmentSystem` не извлекает камеру из renderer, не разрешает материалы |
