# Renderer baseline M00 Этот документ описывает воспроизводимый baseline renderer перед архитектурной декомпозицией. Он не является заявлением о визуальной совместимости `1:1` с WoW 3.3.5a. ## Единый запуск Из корня репозитория: ```powershell .\tools\run_render_baseline.ps1 ``` Скрипт последовательно проверяет headless-загрузку проекта, renderer materials, M2 unique-id dedupe, regression manifest, coordinate boundaries и dedicated server-spawn renderer contract, затем снимает checkpoint-ы. Для CI или быстрой проверки контракта без PNG: ```powershell .\tools\run_render_baseline.ps1 -DryRun -WaitSeconds 0.1 -MeasureSeconds 0.1 ``` Если `godot` отсутствует в `PATH`, скрипт использует локальный executable, указанный в `RENDER.md`. Другой executable задаётся через `-GodotPath`. Отдельный M01 server-spawn checkpoint использует тот же capture pipeline, но не расширяет семичастный M00 visual baseline: ```powershell godot --headless --path . --script res://src/tools/verify_server_spawn_renderer.gd godot --path . --script res://src/tools/capture_render_checkpoints.gd -- ` --manifest res://src/tools/server_spawn_render_manifest.json ` --output user://server_spawn_renderer ``` Manifest связывает pinned AzerothCore Human Warrior spawn с Godot position, ADT tile/chunk и renderer-native diagnostic marker. Маркер начинается точно в spawn origin и проходит обычный depth test, поэтому screenshot показывает его отношение к terrain; это integration evidence, а не заявление visual parity. Результат по умолчанию находится в `user://render_baseline`: - `report.json` — Godot, OS, CPU, GPU/backend, viewport, revision, cache inventory, cache versions, параметры checkpoint-ов и результаты; - `__cold_process.png` — первый визит в текущем процессе; - `__warm_revisit.png` — повторный визит в том же процессе после обхода контрольных точек. ## Парное визуальное сравнение Одобренные снимки оригинального клиента хранятся вне Git. Если каталог с ними доступен локально, единый запуск может сразу сравнить PNG с одинаковыми именами: ```powershell .\tools\run_render_baseline.ps1 ` -ReferenceCheckpointDirectory 'D:\private-fixtures\wow-3.3.5a-checkpoints' ``` Результат записывается в `user://render_baseline/visual_comparison.json`. Другой путь задаётся через `-VisualComparisonReport`. `-DryRun` несовместим с визуальным сравнением, потому что не создаёт PNG. Runner сохраняет обычный performance capture в нормативном `1280×900`, затем отдельно снимает visual candidates в `2560×1440`; разрешение можно переопределить через `-VisualComparisonWidth` и `-VisualComparisonHeight`, не меняя performance baseline. Отдельный запуск сравнения: ```powershell godot --headless --path . ` --script res://src/tools/compare_render_checkpoints.gd -- ` --reference 'D:\private-fixtures\wow-3.3.5a-checkpoints' ` --candidate "$env:APPDATA\Godot\app_userdata\OpenWC\render_baseline" ` --output user://render_baseline/visual_comparison.json ``` Reference-каталог принимает JPG/JPEG/PNG оригинального клиента с именем checkpoint, например `goldshire_dense_m2.jpg`. Диагностические кадры с префиксом `diagnostic_` игнорируются. Один reference автоматически сопоставляется с `__cold_process.png` и `__warm_revisit.png` OpenWC. Сравнение переводит sRGB в linear color space, вычисляет взвешенную ошибку яркости/цвета для каждого пикселя, среднюю ошибку кадра и долю пикселей выше локального порога. Нормативные defaults находятся в `comparison_budgets` manifest: mean error `0.015`, changed-pixel ratio `0.01`, pixel error threshold `0.04`. Любое превышение, несовпадение размеров, отсутствие пары или пустой reference-каталог возвращает ненулевой exit code. Автоматический pass не является доказательством parity: animation phase, weather, camera alignment и intentional gaps требуют human approval. Для проверки алгоритма без proprietary данных используется: ```powershell godot --headless --path . --script res://src/tools/compare_render_checkpoints.gd -- --self-test ``` ```mermaid flowchart LR R[Private approved reference PNGs] --> C[Checkpoint comparator] N[New baseline PNGs] --> C M[Manifest tolerance defaults] --> C C --> J[JSON metrics and pass/fail] J --> H[Human fidelity approval] ``` `cold_process` не означает очищенный Windows filesystem cache. Поле `cache_state` и полный inventory обязаны интерпретироваться вместе с результатом. Удаление или принудительная пересборка cache не входит в baseline-команду. Для отдельного запуска после контролируемой очистки безопасного локального cache следует передать осмысленную метку, например `-CacheState rebuilt-clean`; proprietary source assets команда не изменяет. ## Контрольные точки и детерминизм Нормативный manifest: `res://src/tools/render_baseline_manifest.json`. Он не содержит proprietary assets и фиксирует семь обязательных классов: - Elwynn terrain overview; - граница ADT около world center; - dense M2 в Goldshire; - Goldshire Inn как large WMO; - Elwynn waterfall как liquid; - синтетический native-animation probe `GryphonRoost01.m2`; - тот же Elwynn overview в 19:00 для sky transition. Зафиксированы camera/target/player position, viewport, FOV, quality preset и время мира. Для temporal shader/animation кадров SHA-256 служит идентификатором артефакта, но не самостоятельным pass-критерием: автоматическая визуальная проверка использует perceptual diff и согласованный tolerance. Синтетический animation probe существует только внутри capture tool и не меняет runtime streaming scene. ## Метрики и budget Каждая точка записывает load/wait time, frame time p50/p95/p99, максимальный frame hitch, static/video memory, draw calls, rendered objects и снимок streaming queues. При сравнении следующего renderer с этим baseline p95, p99, max hitch, load time и memory не должны ухудшаться более чем на 10% без отдельного принятого объяснения и обновления baseline. Измерение считается неполным, если snapshot показывает незавершённые очереди: это состояние остаётся в `report.json` и не должно скрываться. Значения сравниваются только на одинаковых Godot, backend, hardware/profile, cache state, viewport и manifest revision. ## Cache contract Manifest фиксирует проверяемые версии и причины invalidation: | Cache | Version | Invalidate when | |---|---:|---| | baked terrain | 5 | изменились baked geometry или placement payload | | streaming terrain | 2 | изменились streaming geometry или M2 `unique_id` payload | | terrain splat | 1 | изменился splat resource payload | | terrain control splat | 3 | изменились control atlas, layer map или texture array | | WMO streaming | 2 | изменилась WMO render geometry | | WMO builder | 2 | изменились scene transform или builder payload | | M2 material | 2 | изменились material или custom vertex payload | `verify_render_baseline_manifest.gd` сравнивает таблицу с runtime constants. Shader-only refresh versions, не изменяющие serialized payload, обновляются по правилам `RENDER.md`; изменение payload требует version bump и rebake. ## Fidelity gaps относительно 3.3.5a Baseline пока не имеет approved парных кадров оригинального клиента build 12340. До их появления нельзя утверждать parity. Известны следующие классы расхождений: - неполные M2 animation/skinning, particles и ribbons; - приближённые M2/WMO material combiners и pass ordering; - отсутствие настоящего WMO portal/room culling; - упрощённые liquid depth/shore rules и texture selection; - неполная проверка skybox/lighting transition по зонам; - возможная смена D3D12 на Vulkan при ошибке descriptor heap — фактический backend всегда берётся из `report.json`. Парное сравнение с клиентом 3.3.5a должно использовать те же map position, local time и weather. Каждый gap регистрируется как terrain/material, placement, animation, lighting, liquid или culling; baseline сам по себе gap не закрывает. Локальная сессия build 12340 от 2026-07-11 откалибровала три непригодные исходные позиции: dense-M2 camera была полностью закрыта кронами, large-WMO camera находилась у дымохода кузни, liquid camera попадала под terrain оригинального клиента. Manifest теперь содержит проверенные replacement camera positions. Пять approved локальных reference JPG хранятся вне Git; synthetic animated probe и dusk checkpoint всё ещё требуют отдельной процедуры. Первый paired run после калибровки создал десять сравнений (пять reference × cold/warm), без missing pairs. Все пары ожидаемо превысили строгий tolerance: mean perceptual error `0.0707..0.1746`, changed-pixel ratio `0.5504..0.8187`. Human inspection показал, что это пока не чистая material/lighting ошибка: при тех же WoW-derived координатах OpenWC terrain-overview camera находится под terrain/placements, WMO camera — внутри таверны, liquid camera — внутри скал; ADT и dense-M2 композиции также существенно смещены. До исправления coordinate/placement mismatch эти значения являются gap evidence, а не основанием расширять tolerance. Capture tool строит camera basis явно из target и world-up. Это исключает неоднозначный roll `look_at` при автоматической съёмке. `ViewportTexture.get_image()` сохраняется без дополнительного vertical flip для Godot 4.6.1. ## Coordinate calibration Пять принятых build 12340 viewpoints записаны в manifest как `reference_wow_camera`. Они содержат только числовые world coordinates и не включают proprietary данные. Headless probe: ```powershell godot --headless --path . --script res://src/tools/verify_render_coordinate_calibration.gd ``` ```mermaid flowchart LR O[Observed build 12340 WoW XYZ] --> W[WoW to Godot formula] W --> G[Manifest Godot camera XYZ] G --> R[Godot to WoW round-trip] R --> E[Maximum numeric error] ``` На пяти точках maximum mapping/round-trip error равен `0.000015`. Это исключает текущую формулу `gx = center - wy`, `gy = wz`, `gz = center - wx` как источник крупного paired mismatch. Результат не доказывает renderer parity: следующая диагностика должна отдельно проверить terrain height, placement transforms и фактическое camera direction/FOV. Production `CoordinateMapper` остаётся задачей M01; M00 probe не создаёт второй публичный coordinate contract. ## Terrain height diagnostic Rendered terrain проверяется без нового runtime API: offline probe использует уже загруженный tile mesh, строит `TriangleMesh` и выполняет вертикальный ray в tile-local space. ```powershell godot --headless --path . --script res://src/tools/probe_render_terrain_height.gd -- --wait 2 ``` ```mermaid flowchart LR C[Calibrated camera XZ] --> S[Streaming tile state] S --> M[Active terrain mesh] M --> T[Tile-local TriangleMesh ray] T --> H[Terrain height and camera clearance] ``` Измеренный clearance: terrain overview `89.044`, ADT boundary `44.788`, dense M2 `90.178`, large WMO `12.034`, waterfall примерно `76.128` Godot units. Следовательно, все пять камер находятся над rendered terrain; visual obstruction принадлежит placements/WMO/composition, а не terrain height. Waterfall XZ сначала давал `no_intersection`, хотя tile `30_49` был available, полностью загружен, имел `control_splat_cache` quality mesh и LOD0 mesh, а probe находился внутри mesh AABB. Ray со смещением `2.0` units пересёк тот же mesh на высоте `113.872`; точная XZ попала на triangle seam/edge numerical miss. Probe теперь сообщает `sampled_nearby`, distance и source tile вместо ложного streaming ownership gap. `--require-all` остаётся строгим режимом для действительно неснятых точек. ## Camera occluder diagnostic Scene-tree placement composition проверяется transformed AABB без изменения renderer: ```powershell godot --headless --path . --script res://src/tools/probe_render_camera_occluders.gd -- --wait 3 ``` ```mermaid flowchart LR C[Calibrated camera] --> A[Published Mesh/MultiMesh AABBs] T[Manifest target] --> S[Camera-to-target segment] A --> P[Camera containment test] A --> I[Segment intersection test] S --> I P --> J[JSON occluder report] I --> J ``` Ни одна из пяти камер не находится внутри опубликованной scene-tree geometry. Terrain-overview segment пересекает четыре Stormwind WMO groups, large-WMO segment — три Goldshire Inn groups, waterfall segment — liquid surface; ADT boundary и dense-M2 segments не пересекают placement AABB. Поэтому прежнее визуальное впечатление «камера внутри WMO/placements» не подтверждается. Основной paired gap сейчас — неточно воспроизведённые manual look direction/target/FOV reference-кадров: например, автоматический Goldshire target направляет луч через фасад внутрь WMO, тогда как reference был вручную скадрирован на весь фасад. Probe охватывает только scene-tree MeshInstance/MultiMesh; RID-only instances не имеют доступного semantic path и явно исключены из coverage. ## Empirical FOV sweep В build 12340 `GetCVar("cameraFoV")` возвращает `nil`, а `/console cameraFoV` и `ConsoleExec("cameraFoV")` не выдают значения. Поэтому capture tool поддерживает additive `--camera-fov`, а comparator — `--only`, позволяющий ограничить sweep одним reference checkpoint. ```mermaid flowchart LR F[Candidate vertical FOV] --> C[Dedicated checkpoint camera] C --> P[Filtered checkpoint PNGs] R[One original-client reference] --> D[Perceptual comparator --only] P --> D D --> M[Ranked metrics] ``` Первый sweep обнаружил capture defect: разные FOV иногда создавали одинаковые hashes, потому что scene camera могла перехватить viewport после входа в tree. Capture теперь вызывает `camera.make_current()` после добавления world и перед каждым checkpoint. После исправления ADT-boundary ranking стал: `26° → 0.079588`, `38° → 0.079633`, `50° → 0.084353`, `62° → 0.088360`, `86° → 0.097993` mean error. Plateau `26–38°` и несовпавший changed-pixel optimum показывают доминирование manual look direction/framing; эти данные не обосновывают изменение нормативного manifest FOV `62°`. Для настоящей калибровки reference capture должен сохранять воспроизводимые yaw/pitch/zoom или независимый projection fixture.