fix(render): select terrain LOD from native camera cells
Docs Deploy / Build and Deploy MkDocs (push) Successful in 36s

This commit is contained in:
Valentin Popov committed 2026-11-01 00:09:07 +04:00
1 parent 853d9b115a
commit d7c18d4c1a
9 files changed
+776 -21

No files matched your search

+37 -8
View File
@@ -1030,11 +1030,39 @@ Node38, а диапазоны граней — в 68-байтовых Slot68. В
проверяются на единственное назначение, а выходящий за массив, повторный или
неподдерживаемый диапазон считается ошибкой загрузки.
Первый рендер карты, terrain-запросы высоты и пересечения, receiver-ы теней и
обрезка контуром зданий используют только LOD0. Отбор сохраняет порядок
исходных граней, а контурный проход отдельно переводит индекс спроецированной
грани обратно в исходный. LOD1 пока не подставляется по расстоянию: такой
переход не реализован.
Рисуемый уровень выбирается по ячейкам, а не по расстоянию до отдельного
треугольника. Начало сетки находится в первых двух float заголовка Slot68,
размер ячейки — в границах первого слота LOD0. Атрибут 2 потока Node38 задаёт
число ячеек по X; индекс ячейки равен `x * count_y + y`. Приложение проверяет,
что границы всех слотов образуют эту регулярную сетку. Все 33 карты GOG
проходят такую проверку, включая вырожденные грани на общей границе ячеек:
принадлежность грани берётся из слота, а не угадывается по её центру.
Координаты камеры переводятся в целые координаты сетки округлением вниз.
Если `dx * dx + dy * dy >= 9` и горизонтальный FOV не меньше 0,5 радиана,
выбирается LOD1. При увеличении масштаба, отсутствии дальнего слота или
наличии геометрического patch остаётся LOD0. Камера за границей карты не
прижимается к крайней ячейке для этого расчёта. Правило восстановлено по
GOG `Terrain.dll` ([сборка и SHA-256](../reference/original-binaries.md)):
`CLandscape::Render`, RVA `0x11340`, выбор уровня `0x11878–0x11A03`.
Исполнение оригинала подтверждает выбор дальнего слота, а getter камеры
`Ngi32.dll` RVA `0x4D960` возвращает именно FOV.
Основания строений сохраняют подробную геометрию. Нативная регистрация
patch (`Terrain.dll` RVA `0x12E20`) проверяет его треугольники против каждой
ячейки через `mrnParaIntersectsPoly`. Поэтому учитываются и соседние ячейки,
которых касается край треугольника. Перед проверкой координаты нормализуются
в `f32` относительно центра ячейки; замена этой операции сравнением мировых
границ меняет результат на общих рёбрах. На «Конструкторе» игнорируемый
ресурсный тест сверяет все 22 ячейки с принудительным LOD0 с набором,
полученным в живом оригинале.
Vulkan получает оба уровня один раз. Диапазоны индексов разделены по
материалу и исходной ячейке; на каждом кадре включается только выбранный
уровень. Сгенерированные отмостки остаются включены. Terrain-запросы высоты
и пересечения, receiver-ы теней и обрезка контуром зданий используют
канонический LOD0. Отбор сохраняет порядок исходных граней, а контурный проход
отдельно переводит индекс спроецированной грани обратно в исходный.
```rust
let assignments = mesh.face_lod_assignments()?;
@@ -1052,9 +1080,10 @@ for (face, assignment) in mesh.faces.iter().zip(assignments.iter()) {
исходные фрагменты рельефа вместе с треугольниками отмостки в одном наборе.
`TerrainWorld::with_basement_foundation` принимает этот набор во владение и
устанавливает те же координаты и маски в BVH высотных запросов и пересечений.
Предпросмотр миссии на Vulkan пока строит только LOD0: связывание BAS-записей
с этой подготовкой ещё не подключено, поэтому наличие API не означает, что
геометрия миссии уже обрезана.
Предпросмотр миссии связывает BAS-записи с этой подготовкой до загрузки
материалов. Полученная поверхность общая для изображения, теней и запросов;
поправка размещения основания один раз применяется к корню строения и его
присоединённым компонентам.
Внешнее кольцо должно лежать на области LOD0, а каждый его сегмент — оставаться
покрытым текущей поверхностью. Непокрытый сегмент или пересекающиеся контуры