diff --git a/coordination/claims/M00-QAR-COORD-CALIBRATION-001.md b/coordination/claims/M00-QAR-COORD-CALIBRATION-001.md new file mode 100644 index 0000000..1722ca2 --- /dev/null +++ b/coordination/claims/M00-QAR-COORD-CALIBRATION-001.md @@ -0,0 +1,78 @@ +# M00-QAR-COORD-CALIBRATION-001 — Renderer coordinate calibration + + + +## Ownership + +- Target: M00 +- Program: QAR +- Owner/Agent ID: sindo-main-codex +- Branch: `work/sindo-main-codex/m00-coordinate-calibration` +- Lease expires UTC: 2026-07-13 +- Integrator: milestone integrator + +## Outcome + +Record the five observed build 12340 camera positions as non-proprietary golden coordinates and determine whether the existing WoW/Godot position formula round-trips them. + +## Non-goals + +- Introducing the M01 production `CoordinateMapper` contract. +- Changing terrain, placement, WMO, liquid, or streaming behavior. +- Claiming camera composition parity from coordinate round-trip alone. + +## Paths + +- Exclusive: `src/tools/verify_render_coordinate_calibration.gd` +- Shared/hotspots: `src/tools/render_baseline_manifest.json`, renderer baseline documentation +- Generated/ignored: original-client screenshots and local reports + +## Contracts and data + +- Public API/events: none; headless diagnostic only +- Schema/format version: additive checkpoint calibration metadata, manifest schema remains 1 +- Migration/compatibility: existing capture consumers ignore additive fields +- Consumers: M00 fidelity workflow and future M01 golden fixtures + +## Dependencies + +- Requires: five accepted original-client build 12340 viewpoints +- Blocks: diagnosis of paired camera/placement mismatch +- External state: screenshots remain outside Git + +## Verification + +- Commands: coordinate calibration probe, baseline manifest, coordination and documentation gates +- Fixtures: five `reference_wow_camera` values in renderer manifest +- Fidelity evidence: positions were observed in build 12340 during the paired session +- Performance budget: negligible headless arithmetic + +## Documentation deliverables + +- Inline public API docs: diagnostic script header +- Module specification: renderer verification/source map update +- Data-flow diagram: baseline calibration flow update +- Sequence/state/dependency diagrams: not applicable; stateless synchronous probe +- Source map/status updates: renderer module and baseline document + +## Simplicity and naming + +- Important names introduced: `reference_wow_camera`, `maximum_round_trip_error` +- Simplest considered solution: direct formula probe over manifest fixtures +- Rejected complexity/abstractions: production domain mapper before M01 +- Unavoidable complexity and justification: none +- Measured optimization evidence: not applicable + +## Status + +- State: ready +- Done: five golden camera points, headless round-trip probe, runner integration and documentation +- Next: integrator review; diagnose terrain height/placement/composition separately +- Blocked by: + +## Handoff + +- Commit: branch HEAD +- Results: five points passed with maximum mapping/round-trip error 0.000015 +- Remaining risks: correct position arithmetic does not prove terrain height, placement or camera direction parity +- Documentation updated: `docs/RENDER_BASELINE.md`, `docs/modules/world-renderer.md` diff --git a/docs/RENDER_BASELINE.md b/docs/RENDER_BASELINE.md index 3c00639..b1fbbb2 100644 --- a/docs/RENDER_BASELINE.md +++ b/docs/RENDER_BASELINE.md @@ -120,3 +120,21 @@ Baseline пока не имеет approved парных кадров ориги Первый 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. diff --git a/docs/modules/world-renderer.md b/docs/modules/world-renderer.md index 3394cb7..0b56fa9 100644 --- a/docs/modules/world-renderer.md +++ b/docs/modules/world-renderer.md @@ -192,7 +192,7 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m ## Verification -- Unit/contract tests: material mapping, unique-ID dedupe, placement probes, baseline manifest, synthetic perceptual checkpoint diff. +- Unit/contract tests: material mapping, unique-ID dedupe, placement probes, baseline manifest, five-point coordinate calibration, synthetic perceptual checkpoint diff. - Integration/E2E: Eastern Kingdoms/Kalimdor streaming scenes and seven cold/warm checkpoints. - Fidelity evidence: пять локальных build 12340 reference JPG откалибровали terrain/ADT/M2/WMO/liquid viewpoints; automated paired-image metrics exist, но synthetic animation/dusk и полный human approval ещё не закрыты. - Performance budgets: M00 report records cold/warm p95 and max hitch; no final acceptance threshold yet. @@ -242,6 +242,7 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m | `tools/run_render_baseline.ps1` | Unified M00 baseline runner | | `src/tools/compare_render_checkpoints.gd` | Offline JPG/PNG paired-image perceptual metrics and JSON pass/fail report | | `src/tools/capture_render_checkpoints.gd` | Deterministic no-roll checkpoint camera, performance and visual capture | +| `src/tools/verify_render_coordinate_calibration.gd` | Build 12340 camera-coordinate golden point round-trip diagnostic | ## Related decisions and references diff --git a/src/tools/render_baseline_manifest.json b/src/tools/render_baseline_manifest.json index 4a53993..bcbaf15 100644 --- a/src/tools/render_baseline_manifest.json +++ b/src/tools/render_baseline_manifest.json @@ -43,6 +43,7 @@ "name": "elwynn_terrain_overview", "coverage": ["terrain"], "camera": [16680.0, 180.0, 26220.0], + "reference_wow_camera": [-9153.334, 386.666, 180.0], "target": [16800.0, 62.0, 26400.0], "player": [16800.0, 58.0, 26400.0], "time_hours": 13.0 @@ -51,6 +52,7 @@ "name": "elwynn_adt_boundary", "coverage": ["adt_boundary"], "camera": [17020.0, 105.0, 26590.0], + "reference_wow_camera": [-9523.334, 46.666, 105.0], "target": [17066.666, 62.0, 26666.666], "player": [17060.0, 58.0, 26660.0], "time_hours": 13.0 @@ -59,6 +61,7 @@ "name": "goldshire_dense_m2", "coverage": ["dense_m2"], "camera": [16956.666, 150.0, 26466.666], + "reference_wow_camera": [-9400.0, 110.0, 150.0], "target": [17015.0, 62.0, 26525.0], "player": [17015.0, 58.0, 26525.0], "time_hours": 13.0 @@ -67,6 +70,7 @@ "name": "goldshire_inn_large_wmo", "coverage": ["large_wmo"], "camera": [17013.666, 72.0, 26451.666], + "reference_wow_camera": [-9385.0, 53.0, 72.0], "target": [17042.27, 66.0, 26530.91], "player": [17042.27, 58.0, 26530.91], "time_hours": 13.0 @@ -75,6 +79,7 @@ "name": "elwynn_waterfall_liquid", "coverage": ["liquid"], "camera": [16481.666, 190.0, 26366.666], + "reference_wow_camera": [-9300.0, 585.0, 190.0], "target": [16518.84, 68.0, 26427.27], "player": [16518.84, 55.0, 26427.27], "time_hours": 13.0 diff --git a/src/tools/verify_render_coordinate_calibration.gd b/src/tools/verify_render_coordinate_calibration.gd new file mode 100644 index 0000000..fbe1078 --- /dev/null +++ b/src/tools/verify_render_coordinate_calibration.gd @@ -0,0 +1,88 @@ +extends SceneTree + +## Verifies observed build 12340 camera coordinates against the current renderer +## WoW X/Y/Z to Godot X/Y/Z convention. This diagnostic does not define the +## future M01 production CoordinateMapper contract. + +const MANIFEST_PATH := "res://src/tools/render_baseline_manifest.json" +const WOW_WORLD_CENTER := 17066.666 +const MAXIMUM_POSITION_ERROR := 0.002 + + +func _initialize() -> void: + var manifest := _load_manifest() + if manifest.is_empty(): + quit(1) + return + + var failures: Array[String] = [] + var calibrated_count := 0 + var maximum_round_trip_error := 0.0 + for checkpoint_variant in manifest.get("checkpoints", []): + if not (checkpoint_variant is Dictionary): + continue + var checkpoint: Dictionary = checkpoint_variant + var reference_wow_camera_variant = checkpoint.get("reference_wow_camera", null) + if not (reference_wow_camera_variant is Array): + continue + var reference_wow_camera: Array = reference_wow_camera_variant + var checkpoint_name := String(checkpoint.get("name", "checkpoint")) + if reference_wow_camera.size() != 3: + failures.append("%s reference_wow_camera must contain three coordinates" % checkpoint_name) + continue + var expected_godot_camera := _array_to_vector3(checkpoint.get("camera", [])) + var reference_wow_position := _array_to_vector3(reference_wow_camera) + var mapped_godot_position := _wow_to_godot(reference_wow_position) + var round_trip_wow_position := _godot_to_wow(mapped_godot_position) + var mapping_error := mapped_godot_position.distance_to(expected_godot_camera) + var round_trip_error := round_trip_wow_position.distance_to(reference_wow_position) + maximum_round_trip_error = maxf(maximum_round_trip_error, maxf(mapping_error, round_trip_error)) + if mapping_error > MAXIMUM_POSITION_ERROR: + failures.append("%s maps %.6f units away from manifest camera" % [checkpoint_name, mapping_error]) + if round_trip_error > MAXIMUM_POSITION_ERROR: + failures.append("%s round-trip error is %.6f" % [checkpoint_name, round_trip_error]) + calibrated_count += 1 + + if calibrated_count < 5: + failures.append("expected at least five build 12340 camera calibrations, found %d" % calibrated_count) + if not failures.is_empty(): + for failure in failures: + push_error("RENDER_COORDINATE_CALIBRATION: %s" % failure) + quit(1) + return + print("RENDER_COORDINATE_CALIBRATION PASS points=%d maximum_error=%.6f" % [calibrated_count, maximum_round_trip_error]) + quit(0) + + +func _wow_to_godot(wow_position: Vector3) -> Vector3: + return Vector3( + WOW_WORLD_CENTER - wow_position.y, + wow_position.z, + WOW_WORLD_CENTER - wow_position.x + ) + + +func _godot_to_wow(godot_position: Vector3) -> Vector3: + return Vector3( + WOW_WORLD_CENTER - godot_position.z, + WOW_WORLD_CENTER - godot_position.x, + godot_position.y + ) + + +func _array_to_vector3(value_variant) -> Vector3: + if not (value_variant is Array) or value_variant.size() != 3: + return Vector3.ZERO + return Vector3(float(value_variant[0]), float(value_variant[1]), float(value_variant[2])) + + +func _load_manifest() -> Dictionary: + var manifest_file := FileAccess.open(MANIFEST_PATH, FileAccess.READ) + if manifest_file == null: + push_error("Cannot open renderer baseline manifest: %s" % MANIFEST_PATH) + return {} + var parsed = JSON.parse_string(manifest_file.get_as_text()) + if not (parsed is Dictionary): + push_error("Renderer baseline manifest is not a JSON object") + return {} + return parsed diff --git a/tools/run_render_baseline.ps1 b/tools/run_render_baseline.ps1 index 629348b..68e8d69 100644 --- a/tools/run_render_baseline.ps1 +++ b/tools/run_render_baseline.ps1 @@ -46,6 +46,7 @@ try { Invoke-GodotStep "render-materials" @("--headless", "--path", ".", "--script", "res://src/tools/verify_render_materials.gd") Invoke-GodotStep "m2-unique-dedupe" @("--headless", "--path", ".", "--script", "res://src/tools/verify_m2_unique_dedupe.gd") Invoke-GodotStep "baseline-manifest" @("--headless", "--path", ".", "--script", "res://src/tools/verify_render_baseline_manifest.gd") + Invoke-GodotStep "coordinate-calibration" @("--headless", "--path", ".", "--script", "res://src/tools/verify_render_coordinate_calibration.gd") $captureArgs = @( "--path", ".",