Merge pull request 'test(M00): diagnose waterfall tile ownership' (#4) from work/sindo-main-codex/m00-waterfall-tile-ownership into master
Reviewed-on: #4
This commit was merged in pull request #4.
This commit is contained in:
@@ -0,0 +1,78 @@
|
|||||||
|
# M00-QAR-TILE-OWNERSHIP-001 — Waterfall terrain tile ownership
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M00-QAR-TILE-OWNERSHIP-001:sindo-main-codex:2026-07-13 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M00
|
||||||
|
- Program: QAR
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m00-waterfall-tile-ownership`
|
||||||
|
- Lease expires UTC: 2026-07-13
|
||||||
|
- Integrator: milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Identify the runtime transition that prevents waterfall tile `30_49` from exposing a terrain mesh to the height probe.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Changing streaming budgets or terrain behavior.
|
||||||
|
- Rebuilding caches or modifying extracted assets.
|
||||||
|
- Adding a production terrain query service.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: terrain probe runtime ownership diagnostics
|
||||||
|
- Shared/hotspots: renderer baseline documentation
|
||||||
|
- Generated/ignored: local probe reports and caches
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- Public API/events: additive diagnostic JSON fields
|
||||||
|
- Schema/format version: terrain report remains schema 1
|
||||||
|
- Migration/compatibility: additive fields only
|
||||||
|
- Consumers: M00 fidelity diagnosis
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: merged terrain-height probe and local cache inventory
|
||||||
|
- Blocks: waterfall terrain ownership classification
|
||||||
|
- External state: local extracted/cache data
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: isolated waterfall probe, coordination and documentation gates
|
||||||
|
- Fixtures: checkpoint tile `30_49`
|
||||||
|
- Fidelity evidence: runtime state correlated with build 12340 waterfall viewpoint
|
||||||
|
- Performance budget: offline diagnostic
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline public API docs: diagnostic output fields
|
||||||
|
- Module specification: verification/source map if behavior changes
|
||||||
|
- Data-flow diagram: update ownership transition if needed
|
||||||
|
- Sequence/state/dependency diagrams: document observed tile transition
|
||||||
|
- Source map/status updates: baseline findings
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important names introduced: `available`, `queued_index`, `loading`, `state_present`, `mesh_source`
|
||||||
|
- Simplest considered solution: inspect existing loader registries read-only
|
||||||
|
- Rejected complexity/abstractions: new tracing framework
|
||||||
|
- Unavoidable complexity and justification: none
|
||||||
|
- Measured optimization evidence: not applicable
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: ready
|
||||||
|
- Done: raw/cache inventory, isolated runtime state, mesh AABB/local probe and nearby sampling diagnosis
|
||||||
|
- Next: integrator review; placement/composition remains the actual paired-camera gap
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: branch HEAD
|
||||||
|
- Results: tile 30_49 ownership and meshes are healthy; exact ray misses a triangle seam/edge, while a 2-unit offset samples terrain at 113.872
|
||||||
|
- Remaining risks: nearby estimate is diagnostic and must not become a gameplay terrain-query contract
|
||||||
|
- Documentation updated: `docs/RENDER_BASELINE.md`, `docs/modules/world-renderer.md`
|
||||||
@@ -155,4 +155,6 @@ flowchart LR
|
|||||||
T --> H[Terrain height and camera clearance]
|
T --> H[Terrain height and camera clearance]
|
||||||
```
|
```
|
||||||
|
|
||||||
Измеренный clearance: terrain overview `89.044`, ADT boundary `44.788`, dense M2 `90.178`, large WMO `12.034` Godot units. Следовательно, эти четыре камеры находятся над rendered terrain; visual obstruction принадлежит placements/WMO/composition, а не terrain height. Waterfall tile `30_49` не публикует доступный terrain mesh даже после isolated 10-second settle; это отдельный streaming/terrain ownership gap. Default probe сохраняет partial report, а `--require-all` включает строгий ненулевой exit при любой неснятой точке.
|
Измеренный 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` остаётся строгим режимом для действительно неснятых точек.
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
|||||||
- Direct camera path remains until M01/M03.
|
- Direct camera path remains until M01/M03.
|
||||||
- Original-client paired fidelity evidence incomplete.
|
- Original-client paired fidelity evidence incomplete.
|
||||||
- Первый paired run выявил coordinate/placement mismatch: несколько совпадающих server-derived camera positions оказываются под terrain или внутри WMO/rocks OpenWC.
|
- Первый paired run выявил coordinate/placement mismatch: несколько совпадающих server-derived camera positions оказываются под terrain или внутри WMO/rocks OpenWC.
|
||||||
- Terrain-height probe исключил under-terrain состояние для четырёх точек; waterfall tile `30_49` не предоставил terrain mesh после isolated settle.
|
- Terrain-height probe исключил under-terrain состояние для всех пяти точек; waterfall exact-XZ miss классифицирован как TriangleMesh seam/edge и подтверждён nearby sample в 2 units.
|
||||||
- D3D12 descriptor and shutdown RID/resource issues remain.
|
- D3D12 descriptor and shutdown RID/resource issues remain.
|
||||||
- M2/WMO/material/particle/ribbon/portal parity incomplete.
|
- M2/WMO/material/particle/ribbon/portal parity incomplete.
|
||||||
- Public API is mostly exported configuration rather than stable contracts.
|
- Public API is mostly exported configuration rather than stable contracts.
|
||||||
|
|||||||
@@ -112,10 +112,18 @@ func _sample_terrain(world: Node3D, world_position: Vector3) -> Dictionary:
|
|||||||
highest_terrain_height = terrain_height
|
highest_terrain_height = terrain_height
|
||||||
intersected_tile_key = tile_key
|
intersected_tile_key = tile_key
|
||||||
if not is_finite(highest_terrain_height):
|
if not is_finite(highest_terrain_height):
|
||||||
return {
|
var missing_result := {
|
||||||
"status": "no_intersection" if ready_mesh_count > 0 else "mesh_not_ready",
|
"status": "no_intersection" if ready_mesh_count > 0 else "mesh_not_ready",
|
||||||
"tile": "%d_%d" % [tile_coordinate.x, tile_coordinate.y],
|
"tile": "%d_%d" % [tile_coordinate.x, tile_coordinate.y],
|
||||||
}
|
}
|
||||||
|
missing_result.merge(_tile_runtime_diagnostic(world, String(missing_result.tile), world_position), true)
|
||||||
|
if bool(missing_result.get("quality_mesh_present", false)) or bool(missing_result.get("tile_lod_mesh_present", false)):
|
||||||
|
missing_result["status"] = "no_intersection"
|
||||||
|
missing_result.merge(_nearest_terrain_sample(world, world_position), true)
|
||||||
|
if missing_result.get("nearest_sample_distance", null) != null:
|
||||||
|
missing_result["status"] = "sampled_nearby"
|
||||||
|
missing_result["terrain_height"] = float(missing_result.nearest_sample_height)
|
||||||
|
return missing_result
|
||||||
return {
|
return {
|
||||||
"status": "sampled",
|
"status": "sampled",
|
||||||
"tile": intersected_tile_key,
|
"tile": intersected_tile_key,
|
||||||
@@ -146,6 +154,70 @@ func _intersect_terrain_state(state: Dictionary, world_position: Vector3):
|
|||||||
return world_hit.y
|
return world_hit.y
|
||||||
|
|
||||||
|
|
||||||
|
func _tile_runtime_diagnostic(world: Node3D, tile_key: String, world_position: Vector3) -> Dictionary:
|
||||||
|
var available_tiles: Dictionary = world.get("_available_tiles")
|
||||||
|
var loading_tasks: Dictionary = world.get("_tile_loading_tasks")
|
||||||
|
var tile_states: Dictionary = world.get("_tile_states")
|
||||||
|
var load_queue: Array = world.get("_tile_load_queue")
|
||||||
|
var queued_index := -1
|
||||||
|
for index in load_queue.size():
|
||||||
|
var request: Dictionary = load_queue[index]
|
||||||
|
if String(request.get("key", "")) == tile_key:
|
||||||
|
queued_index = index
|
||||||
|
break
|
||||||
|
var diagnostic := {
|
||||||
|
"available": available_tiles.has(tile_key),
|
||||||
|
"queued_index": queued_index,
|
||||||
|
"loading": loading_tasks.has(tile_key),
|
||||||
|
"state_present": tile_states.has(tile_key),
|
||||||
|
"load_queue_size": load_queue.size(),
|
||||||
|
}
|
||||||
|
if tile_states.has(tile_key):
|
||||||
|
var state: Dictionary = tile_states[tile_key]
|
||||||
|
var quality_mesh: Mesh = state.get("quality_terrain_mesh", null)
|
||||||
|
var tile_lod_mesh: Mesh = state.get("tile_lod_mesh", null)
|
||||||
|
diagnostic["quality_mesh_present"] = quality_mesh != null
|
||||||
|
diagnostic["tile_lod_mesh_present"] = tile_lod_mesh != null
|
||||||
|
diagnostic["quality_source"] = String(state.get("quality_terrain_source", ""))
|
||||||
|
diagnostic["tile_lod"] = int(state.get("tile_lod", -1))
|
||||||
|
var tile_root := state.get("root", null) as Node3D
|
||||||
|
if tile_root != null:
|
||||||
|
var local_position := tile_root.global_transform.affine_inverse() * world_position
|
||||||
|
diagnostic["tile_root_position"] = _vector3_array(tile_root.global_position)
|
||||||
|
diagnostic["probe_local_position"] = _vector3_array(local_position)
|
||||||
|
var diagnostic_mesh := quality_mesh if quality_mesh != null else tile_lod_mesh
|
||||||
|
if diagnostic_mesh != null:
|
||||||
|
var mesh_aabb := diagnostic_mesh.get_aabb()
|
||||||
|
diagnostic["mesh_aabb_position"] = _vector3_array(mesh_aabb.position)
|
||||||
|
diagnostic["mesh_aabb_size"] = _vector3_array(mesh_aabb.size)
|
||||||
|
return diagnostic
|
||||||
|
|
||||||
|
|
||||||
|
func _nearest_terrain_sample(world: Node3D, world_position: Vector3) -> Dictionary:
|
||||||
|
var tile_states: Dictionary = world.get("_tile_states")
|
||||||
|
for radius in [2.0, 5.0, 10.0, 20.0, 40.0]:
|
||||||
|
for offset in [Vector2(radius, 0.0), Vector2(-radius, 0.0), Vector2(0.0, radius), Vector2(0.0, -radius)]:
|
||||||
|
var sample_position := world_position + Vector3(offset.x, 0.0, offset.y)
|
||||||
|
var sample_tile := Vector2i(
|
||||||
|
int(floor(sample_position.x / TILE_SIZE)),
|
||||||
|
int(floor(sample_position.z / TILE_SIZE)))
|
||||||
|
var sample_key := "%d_%d" % [sample_tile.x, sample_tile.y]
|
||||||
|
if not tile_states.has(sample_key):
|
||||||
|
continue
|
||||||
|
var height_variant = _intersect_terrain_state(tile_states[sample_key], sample_position)
|
||||||
|
if height_variant != null:
|
||||||
|
return {
|
||||||
|
"nearest_sample_distance": radius,
|
||||||
|
"nearest_sample_height": float(height_variant),
|
||||||
|
"nearest_sample_tile": sample_key,
|
||||||
|
}
|
||||||
|
return {"nearest_sample_distance": null}
|
||||||
|
|
||||||
|
|
||||||
|
func _vector3_array(value: Vector3) -> Array[float]:
|
||||||
|
return [value.x, value.y, value.z]
|
||||||
|
|
||||||
|
|
||||||
func _vector3(value_variant) -> Vector3:
|
func _vector3(value_variant) -> Vector3:
|
||||||
if not (value_variant is Array) or value_variant.size() != 3:
|
if not (value_variant is Array) or value_variant.size() != 3:
|
||||||
return Vector3.ZERO
|
return Vector3.ZERO
|
||||||
|
|||||||
Reference in New Issue
Block a user