diff options
Diffstat (limited to 'docs/reference')
| -rw-r--r-- | docs/reference/atmosphere.md | 65 | ||||
| -rw-r--r-- | docs/reference/environment-effects.md | 35 | ||||
| -rw-r--r-- | docs/reference/render-frame.md | 41 |
3 files changed, 118 insertions, 23 deletions
diff --git a/docs/reference/atmosphere.md b/docs/reference/atmosphere.md index 65ed176..dba20c2 100644 --- a/docs/reference/atmosphere.md +++ b/docs/reference/atmosphere.md @@ -50,7 +50,25 @@ key_time = floor(duration * (key_hour * 60 + key_minute) * 60 / 86400) theta = (clamp((now - birth_start) / lifetime, 0, 1) * 1.2 - 0.1) * pi ``` -Затем к нему применяется матрица рождения. У солнца и луны независимые +Затем к нему применяется матрица рождения как к вектору-столбцу. Если `A` и +`B` — углы рождения, а `x = cos(theta)` и `z = -sin(theta)`, результат равен + +```text +[x*cos(A) + z*sin(A)*sin(B), + x*sin(A) - z*cos(A)*sin(B), + z*cos(B)] +``` + +Порядок важен для всего освещения: транспонированное умножение переносит +солнце на другую сторону мира и меняет освещённые склоны, блики и тени. +В GOG `Terrain.dll` (SHA-256 +`af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead`) +матрицу строит RVA `0x4A550`, а вектор преобразует `CSun::Update`, RVA +`0x4B570`. Живой замер после 282184 мс при длительности 525000 мс и углах +`[90, 30]` дал направление `[-0.4950135, -0.1408745, -0.8573891]`; +Rust-тест проверяет этот результат с допуском `3e-6`. + +У солнца и луны независимые интервалы и независимые направления: при отсутствии активной луны нельзя подменять её направление противоположностью солнца. `SunFrame::color` — это единственный primary directional RGB. Второй источник получает направление @@ -131,6 +149,31 @@ gradient и clouds row 2. Для nebula/stars/clouds используются UV directional RGB из `sky.packed[0]`. Это четыре фиксированных pass, а не три независимых цветных слоя. +Все три текстурных слоя движутся. Обновление GOG `CSky` +(`Terrain.dll`, RVA `0x48B90`, запись UV `0x4942A–0x495B7`; +[сборка и SHA-256](original-binaries.md)) берёт разность текущего `u32` clock +и сохранённого поля объекта `+8` с переполнением. Это отдельные миллисекунды +жизни неба, а не абсолютная фаза суток из SKE. + +| Слой | Период | Добавка к исходным UV | +|---|---:|---| +| Фон | 300 000 мс | одинаковый сдвиг U и V | +| Звёзды | 100 000 мс | одинаковый сдвиг U и V | +| Облака | 50 000 мс | сдвиг только U | + +Добавка равна остатку от деления elapsed clock на период, умноженному на +нативный коэффициент `f32` (`3.3333333e-6`, `1e-5`, `2e-5`). Вершинные UV +каждый раз вычисляются от позиции купола заново; накопление приращений +создавало бы дрейф. `SkyMesh::update_uvs` получает текущие миллисекунды кадра +и сохраняет геометрию купола. В работающем оригинале проверены UV всех трёх +слоёв, включая разность clock с переполнением. + +Захват сохраняет `sky_uv_clock` на входе в рендер неба, проверяя его по +фактическим UV вершины зенита и clock того же кадра. Replay удерживает эту +фазу вместе с фазой суток. Без такого поля текстуры движутся по elapsed clock +приложения; начальная фаза оригинала неизвестна и явно считается как +`captured_sky_phase_unverified`. + Цвет первого screen-gradient и последнего пояса вычисляет `SkyFrame::screen_gradient(yaw_radians, glare_rgb_delta)`. Native `CSky` сначала округляет `(yaw + pi) * 180 / pi` режимом nearest-even, выбирает сектор @@ -167,21 +210,27 @@ RGB palette 12 с минимумом `80/255` на канал и alpha `150/255` ## Туман и свет -Нативное обновление `CSky` записывает две дальности и один packed RGB. +Нативное обновление `CSky` записывает две дальности и нижнюю границу освещения. `FogFrame` повторяет этот контракт: ```text fog.start = sky.values[0] * 700 fog.end = sky.values[1] * 700 -fog.color = RGB(sky.packed[1]) / 255 +lighting_floor = RGB(sky.packed[1]) / 255 ``` Поля не скрывают порядок исходных параметров: значения сохраняются в -`SkySample`, а преобразование выполняется только в `fog_frame`. Vulkan path -передаёт цвет и дальности в frame uniforms; vertex shader вычисляет линейный -коэффициент по расстоянию до камеры, а fragment shader смешивает RGB материала -с fog color в ветвях combiner, которые используют туман. Конкретный draw state -может обходить это смешивание. +`SkySample`, а преобразование выполняется только в `fog_frame`. Нижняя граница +освещения ограничивает результат расчёта света снизу; она не задаёт цвет тумана. +Обычный цвет тумана выбирается из `sky.colors[0..4]` по направлению камеры, +как цвет горизонта. Это подтверждено в `CSky::Render` и живым совпадением +его значения с `D3DRENDERSTATE_FOGCOLOR` в том же игровом тике. + +Vulkan path передаёт эти параметры раздельно. Vertex shader вычисляет +коэффициент по квадратам расстояния и границ, а fragment shader смешивает +RGB материала с цветом тумана. Конкретный native draw state может обходить +смешивание или временно менять его цвет, например на чёрный для отдельного +прохода. Полный выбор таких проходов ещё не воспроизведён. Цвет primary света солнца — RGB palette 12, нормированный делением на 255 и умноженный на trailing parameter 2. `SunFrame::direction` — нормированный diff --git a/docs/reference/environment-effects.md b/docs/reference/environment-effects.md index 45d7725..f3ecfb4 100644 --- a/docs/reference/environment-effects.md +++ b/docs/reference/environment-effects.md @@ -218,6 +218,41 @@ OneShot -> воспроизвести разовый звук молни отдельный spatial source, применяет DirectSound range/pan и текущий listener. Отсутствие устройства вывода не должно останавливать симуляцию. +### Звуки Control + +Команда Op2 может принадлежать не погоде, а экземпляру эффекта на узле модели. +`fparkan-fx::ambient_sound` хранит её временное окно и предыдущее значение +фазы. В режиме 0 разовый звук начинается при строгом пересечении начала окна: +`previous <= start && start < phase`, если фаза движется вперёд. Значение +`phase == 1` для этой проверки заменяется нулём и подготавливает следующий +цикл. Режим 2 включает loop на обеих границах окна; внутри окна уже работающий +источник обновляется, а не запускается заново. Выход за окно останавливает его. + +Позиция интерполируется между двумя локальными точками и проходит через ту же +матрицу владельца, что и графические команды. Минимальная и максимальная +дистанции остаются постоянными; частота интерполируется по фазе окна. +Каждая команда получает отдельный источник звука, даже если ресурс совпадает +с ресурсом соседней команды. Дождь сохраняет свой loop. Перемещение слушателя, +потеря фокуса и завершение приложения затрагивают все эти источники. + +Эти ветви восстановлены в GOG `Effect.dll` (сборка из +[таблицы бинарников](original-binaries.md)): init RVA `0x12D10`, update +`0x12EB0`. Живой loop `sounds.lib:f_defl.wav` передаёт в звуковой backend +диапазон `12/80`, частоту `1` и мировую позицию из матрицы владельца. +В режиме 2 признак loop берётся из первого DWORD body, а не из command flag. + +Обычное создание эффекта Control сохраняет time mode заголовка и задаёт +постоянную фазу нулём. Raw selector строки служит ID экземпляра, а не номером +режима времени. Поэтому `f_comp_sfx` с time mode 0 и началом окна `0.001` +остаётся тихим до управляющей команды Control. Preview такие команды ещё не +исполняет. Периодические звуки с time mode 2 уже следуют общим часам и текущей +позе узла. `render_coverage control_op2_supported` считает подготовленные +команды, а `control_sound_start/stop` показывает фактические переходы. + +Неизвестные backend modes 1/3, ненулевое поле body+76, неподдержанное время +или привязка владельца пропускаются с `render_coverage control_op2_unsupported`. +Они не заменяются разовым звуком или произвольным loop. + ## Цвет солнца Солнце берёт цвет спрайта и прозрачность бликов из `palette[10]` расписания diff --git a/docs/reference/render-frame.md b/docs/reference/render-frame.md index b2a6a93..df3f248 100644 --- a/docs/reference/render-frame.md +++ b/docs/reference/render-frame.md @@ -85,9 +85,13 @@ RVA `0x1B0F0`, заменяет diffuse alpha пакета без type 16 с bat квадраты границ по этому наблюдению. GLSL вычисляет квадрат расстояния вершины, после чего растеризатор интерполирует коэффициент по треугольнику. `native_fog.effective.sink_rgb` совпадает с `sky.packed[1]`, нижней границей -освещения CShade. Это не подтверждает значение GPU fog color. Приложение пока -сохраняет цвет экранного горизонта; выбор цвета и проходов требует отдельной -проверки GPU-состояния. +освещения CShade. Обычный GPU fog color подтверждён отдельно: `CSky::Render` +по RVA `0x48341` задаёт renderer default из цвета горизонта. В живом замере +`0xFF404064` дошёл до `IDirect3DDevice7::SetRenderState(34)` в том же игровом +тике. В другом ракурсе палитра `[0xFFA8A5CE, 0xFFABA9D5, 0xFFBAA9D5, +0xFFABA9D5]` дала default `0xFFA9A7D1`. Это не покрывает временные изменения +цвета отдельных проходов: например, native render path устанавливает чёрный +цвет и затем восстанавливает сохранённый. Выбор проходов по native shade-state пока приближён общим туманом мира и считается как `render_coverage fog_pass_eligibility_approx`. @@ -134,21 +138,17 @@ RVA `0xF6E0`, преобразует направление текущей маÑ глубину. За фиксированными диапазонами окружения сохраняются их явные состояния. Для MSH без потока цветов type 16 диапазон, цвет которого вычисляет CPU, при -raw-флаге `Batch20` `0x0008` получает переопределение alpha только в одном -подтверждённом случае. В GOG `Terrain.dll` с SHA-256 -`af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead` проверка -по RVA `0x1B0F0` задаёт alpha `0`, когда установлен bit `0x0040` и сброшен -`0x0010`; поэтому для `0x0148` alpha равна нулю. Для `0x0108` и `0x0118` -превью сохраняет alpha MAT0; для `0x0058` действует приоритет bit `0x0010`, -также оставляя alpha MAT0. При наличии type 16 или отсутствии `0x0008` -переопределения нет. Оно относится к одному draw range и не меняет общий +raw-флаге `Batch20` `0x0008` получает переопределение alpha по +[дистанционной кривой портала](#прозрачность-порталов-msh). Bit `0x0010` +имеет приоритет; иначе bit `0x0040` задаёт нулевую alpha, а оставшаяся +ветвь использует границы 75/95 с поправкой на FOV. При наличии type 16 или +отсутствии `0x0008` переопределения нет. Оно относится к одному draw range и не меняет общий материал; alpha `0` временно задаёт число индексов `0`, а при очистке переопределения исходное число индексов восстанавливается. -Общий fade по расстоянию пока не реализован. Открыты точная метрика расстояния, -выбор первого CPU-принятого треугольника диапазона и эквивалентность frustum- и -culling-отбору оригинала. Поэтому состояния `0x0108` и `0x0118` оставляют alpha -MAT0, пока эти входные данные не установлены. +Fade обновляется с камерой каждый кадр. Открыт выбор первой принятой вершины +после native CPU clipping: приложение пока берёт первую индексированную +вершину диапазона и считает это приближение в `render_coverage`. В превью с фиксированной камерой диапазоны туманности и звёзд, купола и облаков явно помечаются как прозрачный фон. Непрозрачные диапазоны мира остаются на @@ -225,6 +225,17 @@ RVA `0x4EBE0` использует соглашение о вызовах `__std `right - left` на `bottom - top`. Размер окна превью закреплён: если при запуске или после ненулевого изменения окна физический размер не совпадает с заданным, приложение завершает работу, а не растягивает изображение. + +Проекция во время рисования может отличаться от восстановленного состояния +камеры после обхода мира. В GOG у `CStridedPrimitive::RenderVB` (Terrain RVA +`0x1D110`) renderer содержит `near = 0.5`, а на границе World3D RVA `0x13CE4` +уже `near = 5`. Поэтому захват отдельно сохраняет `geometry_projection` при +первом вызове геометрии с той же мировой матрицей камеры. Поток, поколение, +renderer, viewport, far и FOV сверяются с последующей границей; только +подтверждённая запись используется при воспроизведении. Иначе пол в трёх +единицах от камеры может исчезнуть из-за чужой ближней плоскости. Старые JSON +без этой записи сохраняют прежнее поведение с явным счётчиком +`captured_projection_unverified`; их нужно переснять для проверки близких поверхностей. Сравнивайте результат с фрагментом исходного PNG по прямоугольнику `[left, top, right, bottom]`. В этом режиме `FreeFlightCamera` не создаётся, но при повторной отрисовке обновляются окружение и проецируемые тени для |
