aboutsummaryrefslogtreecommitdiff
path: root/docs/reference/msh.md
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-10-31 00:05:42 +0300
committerValentin Popov <valentin@popov.link>2026-10-31 00:05:42 +0300
commitdbfdcf1e4cf5fd6adb43505ff95db10c927dd8ef (patch)
treeaf13e2215c15a745f531002702d555307ed4006d /docs/reference/msh.md
parent54105297809271cad7b03c784910dbe1e9b0de41 (diff)
downloadfparkan-dbfdcf1e4cf5fd6adb43505ff95db10c927dd8ef.tar.xz
fparkan-dbfdcf1e4cf5fd6adb43505ff95db10c927dd8ef.zip
fix(runtime): follow linked nodes after building helper hits
Diffstat (limited to 'docs/reference/msh.md')
-rw-r--r--docs/reference/msh.md58
1 files changed, 38 insertions, 20 deletions
diff --git a/docs/reference/msh.md b/docs/reference/msh.md
index 3cff999..945bdc4 100644
--- a/docs/reference/msh.md
+++ b/docs/reference/msh.md
@@ -153,7 +153,10 @@ struct Batch20 {
```
Byte `material_index` выбирает строку WEAR; high word at `+0x02` сохраняется
-отдельно для native overrides. Loader проверяет, что
+отдельно для native overrides. Слово `local_batch_index` остаётся raw-полем
+`+0x06`: в пути CBuilding helper retry оно вместе с базой модели задаёт
+связанный корень Node38, а у отдельной `ModelAsset` база равна нулю. Вне этого
+пути слово сохраняется без вывода о его значении. Loader проверяет, что
`index_start + index_count` остаётся внутри type 6, а каждый использованный
индекс через `base_vertex` попадает в position stream. `vertex_count` и
`local_batch_index` доступны renderer для исходного draw contract.
@@ -200,11 +203,10 @@ Rust использует обычное деление `f32`, поэтому п
### Мировой отрезок по выбранным узлам
Локальный запрос выше проверяет один узел и slot модели. Runtime-функция
-`query_building_segment_world` принимает мировой отрезок и список выбранных
-узлов со slot-ами и их преобразованиями `world_from_node`. Для каждого узла она
-переводит оба конца отрезка обратной матрицей, вызывает `query_slot_segment`, а
-найденную точку переносит назад в мир. Затем она выбирает ближайшее попадание
-среди переданных узлов.
+`query_building_segment_world` принимает мировой отрезок и исходный список
+выбранных узлов со slot-ами и их преобразованиями `world_from_node`. Первый
+проход сохраняет порядок этого списка, переводит концы отрезка обратной
+матрицей и выбирает ближайшее попадание в мире.
```text
segment_node = inverse(world_from_node) * segment_world
@@ -213,20 +215,36 @@ distance² = dot(hit_world - original_start, hit_world - original_start)
```
`original_start` остаётся началом первоначального мирового отрезка, даже когда
-запрос перезапускается. Нативный CBuilding query сохраняет raw
-`Batch20.batch_flags & 0x0008`: если ближайшее попадание помечено этим bit,
-следующий проход начинается в его мировой точке и идёт к исходному концу.
-Первичный запрос исключает из batch только `0x0200`, а тип-7 треугольники —
-`0x0024`. Счётчик ограничивает число всех пересечений: при посещении 200
-запрос завершается ошибкой до проверки bit `0x0008`.
-
-Эта схема восстановлена по GOG `Terrain.dll` SHA-256
-`af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead`:
-мировой обход проходит через `FUN_10016a20` (RVA `0x16A20`), CBuilding выполняет
-запрос и helper-перезапуски в `FUN_10035430` (RVA `0x35430`), а guard находится
-в участке `0x358FC..0x35926`. Runtime-модуль проверен на синтетических
-переносах, поворотах, масштабах и отражениях; подключение к текущим позам
-объектов и камере приложения пока не выполнено.
+запрос перезапускается. Если ближайший batch имеет bit `0x0008`, его ключ
+`local_batch_index` выбирает новый корень Node38, а отрезок начинается в
+мировой точке helper-попадания и идёт к исходному концу. Runtime обходит
+метаданные этого поддерева даже у узлов без переданной геометрии; сами slot-
+запросы выполняются только для узлов, присутствующих во входном списке. Обход
+проверяет сначала допустимых потомков, затем сам узел. Header bit `0x0010`
+прекращает обход его потомков; bit `0x0004` у дочернего узла исключает всю эту ветвь, но bit
+`0x0004` у текущего корня не мешает запросу его собственной геометрии. Слово
+родителя `0xFFFF` не связывает узел с дочерним корнем, а helper-ключ `0xFFFF`
+считается ошибкой данных.
+
+В каждом проходе batch `0x0200` и type-7 triangle flags `0x0024` исключаются.
+Ближайшее попадание и разрешение равных расстояний сохраняют правила локального
+запроса; расстояние измеряется от первоначального начала. Счётчик ограничивает
+все пересечения: при посещении 200 запрос завершается ошибкой до проверки bit
+`0x0008`.
+
+Эта схема сверена с GOG `Terrain.dll` SHA-256
+`af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead` (RVA
+`0x35430`, `0x35961`) и `AniMesh.dll` SHA-256
+`65659bf67f20e5be77d67a5b2f59968f61b94293143807fa59ca451c560c4f7e` (RVA
+`0x134D0`, `0x10A50`). Игнорируемый тест загружает настоящий объект PLANT 4
+миссии AutoDemo и проверяет, что сегмент пересекает геометрию Node3 в исходной
+закрытой позе, но проходит без попадания при явно заданной открытой позе frame
+`1`. Это проверка запроса для двух поз, а не открытие двери игровой командой.
+В загруженных Control-привязках корня PLANT нет tag-21 записи для перехода
+Node3; тест задаёт frame `1` напрямую и не исполняет команду открытия из
+миссии.
+Нативный выбор первого корня и полный мировой обход ещё не сопоставлены с Rust;
+app пока не передаёт этой функции живые runtime-позы и bindings.
## Сторона грани в цветовом проходе