Fix material categories and transparent background ordering
Docs Deploy / Build and Deploy MkDocs (push) Successful in 40s

This commit is contained in:
2026-10-11 21:41:37 +04:00
parent bfe4db1611
commit c90ec80dc0
6 changed files with 496 additions and 53 deletions
+13 -4
View File
@@ -57,10 +57,12 @@ struct Phase34 {
### Pipeline category
Call site извлекает native category как `(attr1 >> 2) & 0xF` и передаёт её в
`LegacyPipelineState::with_material_category`. Таблица содержит пять
подтверждённых категорий; helper возвращает `None` для остальных значений и не
меняет уже выбранные depth и cull state.
Call site извлекает native category как `(attr1 >> 2) & 0xF` и сохраняет её на
разрешённом phase material. World draw ranges получают состояние для начального
selector при подготовке, а animated selector обновляет category вместе с
material selector. Fixed environment ranges keep their authored pipeline state.
Таблица содержит пять подтверждённых категорий; helper возвращает `None` для
остальных значений и не меняет уже выбранные depth и cull state.
| category | source/destination colour factors | alpha test |
| ---: | --- | --- |
@@ -75,6 +77,13 @@ material category. Alpha reference remains draw-range data, while the Vulkan
pipeline key includes the blend mode, depth mode, cull mode and alpha-test
variant so ranges with different native state do not share a pipeline.
World transparency is a separate draw-queue decision. A world range joins that
queue when sampled diffuse opacity is below one or any bit in the native
`0x108` batch-flag mask is set; the MAT0 category alone does not make it
transparent. A queued range
with a depth attachment tests depth without writing it. Unsupported categories
fail world-range setup instead of silently falling back to opaque state.
Каждая phase занимает 34 байта: 18 parameter bytes и 16-byte C string имени
текстуры. Loader переводит параметры в коэффициенты так:
+41
View File
@@ -40,6 +40,47 @@ end-of-render.
Подготовленный item должен ссылаться на immutable данные кадра. Изменение phase
или texture cache посреди прохода не должно менять уже собранную очередь.
## MAT0 state and transparent backgrounds
For mission world ranges, the selected MAT0 material supplies its native
pipeline category. The category sets blend and alpha-test state while preserving
the range's depth and cull state; an animated material selector updates the
category with the selector. Queue membership is independent: sampled opacity
below one or native batch flags `0x108` mark a world range transparent, and a
queued range with depth enabled tests depth without writing it. Fixed
environment ranges keep their explicit states.
The fixed-camera preview explicitly marks nebula/stars, dome and cloud ranges as
transparent backgrounds. Opaque world ranges stay in their world slots; the
transparent world queue is sorted back-to-front and inserted immediately after
the last marked background. Other fixed ranges retain their relative order.
Thus cloud layers can be drawn before transparent foliage, while later sun,
weather and other foreground ranges keep their existing relative order. This
is a narrow queue rule, not a reordering of every sky or effects pass.
## Regression evidence
`cargo test -p fparkan-render-vulkan --lib material_category_and_transparency_preserve_world_state_in_either_order --offline`
checks that category 1 remains source-alpha with alpha testing, retains depth
and cull settings in either update order, and rejects unsupported categories.
`cargo test -p fparkan-render-vulkan --lib tree_canopy_queue_follows_sky_and_precedes_foreground_effects --offline`
checks the explicit queue order: screen gradient, opaque terrain/building,
shadow, nebula/dome/clouds, far-to-near transparent canopy, then sun/weather.
Viewer-only paired readbacks used `--frames 3 --validation
--legacy-camera-capture` with the pre-fix and patched builds. On AutoDemo TMA
object 4 (`wor_l_n1.dat`, `fr_l_plant`), the 1280x720 camera at eye
`[464.319, 904.719, 38.0]`, aimed at `[374.319, 814.719, 12.0]` with FOV
`0.62`, changed the two black facade panels to tinted, textured portal
surfaces. On Mission.01 object 12 (`s_tree_04`), the 1280x1024 camera at eye
`[492.63855, 312.90244, 46.5]` aimed at
`[492.63855, 442.90244, 55.0]` crosses the canopy against open sky; paired
three-frame captures at the same synthetic atmosphere phase (600 seconds) show
the patched build restoring the missing upper/right fronds along that sky
boundary. Both runs reported clean Vulkan validation. These captures verify our
preview behavior and do not prove the native renderer's pass order, portal
transmittance or exact color.
## GOG camera dispatch evidence
For the GOG `World3D.dll` baseline with SHA-256
+10 -7
View File
@@ -275,14 +275,17 @@ buffers и передаёт собранную settings-структуру да
`ret 0x0c`; `CreateCollManager` и `CreateCollObject` возвращают interface
pointer с поправкой `+4` после внутренней инициализации.
Headless Ghidra 12.1.2 decompile GOG binary подтверждает ABI формой экспортов
`LoadControlSystem(char*, char*, char*, char*, char*, u32, void*, i32)`,
Инструкциям x86 в GOG `Control.dll` (`+0x32280`) и caller-у в `AniMesh.dll`
(`+0x32e7`) соответствует ABI `LoadControlSystem(char*, char*, char*, char*,
char*, char*, void*, i32)`: шесть C-string pointers, resource context и mode.
Функция заканчивается `ret 0x20`, то есть снимает все восемь 32-bit stack
arguments; arg7 передаётся constructor-у как context, arg8 выбирает mode.
Прежняя декомпиляция ошибочно определяла шестой строковый аргумент как `u32`.
Остальные exports имеют формы
`LoadPhysicalModel(u32, u32, u32)`, `CreateCollManager(u32)` и
`CreateCollObject(u32, u32)`. Первые пять параметров Control loader — строки,
а mode передаётся последним; decompiler не восстанавливает предметные имена
остальных слов. `InitializeSettings` получает `CreateGameSettings()` из
World3D и делает virtual call slot `+0x24` с literal `0x15` и строкой по RVA
`0x42478`.
`CreateCollObject(u32, u32)`. `InitializeSettings` получает
`CreateGameSettings()` из World3D и делает virtual call slot `+0x24` с literal
`0x15` и строкой по RVA `0x42478`.
Именно update methods этих private objects, а не пять exports, остаются
следующим объектом динамической трассировки. Поэтому reference movement в