Merge pull request 'test(M00): add renderer coordinate calibration' (#2) from work/sindo-main-codex/m00-coordinate-calibration into master
Reviewed-on: #2
This commit was merged in pull request #2.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
# M00-QAR-COORD-CALIBRATION-001 — Renderer coordinate calibration
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-COORD-CALIBRATION-001:sindo-main-codex:2026-07-13 -->
|
||||
|
||||
## 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`
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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", ".",
|
||||
|
||||
Reference in New Issue
Block a user