OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / hblk_field_gate.gd

101 lines · gd ref:
extends SceneTree
## Is the heightfield actually the terrain?
##
## With heightfield_only the density is height(x,z) - y on BOTH paths, so
## sample_density() -- which is the CPU generator -- finally reports the same
## field the GPU shader reads. That makes this runnable headless, with no GPU,
## and it checks the whole chain: the .hblk parsed, addressed on the right axes,
## and the sign convention right.
##
## It CAN fail: the block's own value at the probe column is read straight from
## the file by an independent path (a raw FileAccess read below), so a wrong
## axis order, a wrong origin or a half-texel slip all show up as a mismatch
## rather than as agreement between two copies of the same mistake.
##
##   "$HYPER_TPS_GODOT" --headless --path . --script res://scenes/test/hblk_field_gate.gd

const HBLK := "res://assets/terrain/diffusion_gully_30km.hblk"
const PX := 53760.0
const PZ := 38400.0

var _map: Node = null
var _fails := 0


func _init() -> void:
	var packed := load("res://scenes/maps/map_dc_diffusion.tscn") as PackedScene
	if packed == null:
		print("FAIL — map_dc_diffusion.tscn did not load"); quit(2); return
	_map = packed.instantiate()
	root.add_child(_map)


## Read the block directly, so the expected value does not come from the code
## under test. Row = world Z, column = world X (stitch_block.py).
func _raw_height(x: float, z: float) -> float:
	var f := FileAccess.open(HBLK, FileAccess.READ)
	f.seek(4); var _ver := f.get_32()
	var n := f.get_32()
	var mpp := f.get_float()
	var ox := f.get_float(); var oz := f.get_float()
	var u := (x - ox) / mpp
	var v := (z - oz) / mpp
	var i0 := int(u); var j0 := int(v)
	var fu := u - float(i0); var fv := v - float(j0)
	var base := 32
	f.seek(base + (j0 * n + i0) * 2); var h00 := float(f.get_16())
	f.seek(base + (j0 * n + i0 + 1) * 2); var h10 := float(f.get_16())
	f.seek(base + ((j0 + 1) * n + i0) * 2); var h01 := float(f.get_16())
	f.seek(base + ((j0 + 1) * n + i0 + 1) * 2); var h11 := float(f.get_16())
	f.close()
	var a := h00 + (h10 - h00) * fu
	var b := h01 + (h11 - h01) * fu
	return a + (b - a) * fv


func _process(_dt: float) -> bool:
	var terrain = _map.get_node_or_null("DCTerrain")
	if terrain == null:
		print("FAIL — no DCTerrain"); _fails += 1; _done(); return true

	var expect := _raw_height(PX, PZ)
	print("  block file says the surface at (%.0f, %.0f) is %.1f m" % [PX, PZ, expect])

	# get_16() is unsigned; elevations below sea level are negative int16.
	if expect > 32767.0:
		expect -= 65536.0
		print("  (corrected for unsigned read: %.1f m)" % expect)

	var at_surface: float = terrain.sample_density(Vector3(PX, expect, PZ))
	var below: float = terrain.sample_density(Vector3(PX, expect - 100.0, PZ))
	var above: float = terrain.sample_density(Vector3(PX, expect + 100.0, PZ))
	print("  density at surface %+.2f, 100 m below %+.2f, 100 m above %+.2f"
			% [at_surface, below, above])

	if abs(at_surface) > 1.0:
		print("  FAIL — the surface is not where the block says it is"); _fails += 1
	if below <= 0.0:
		print("  FAIL — 100 m below the surface is not solid"); _fails += 1
	if above >= 0.0:
		print("  FAIL — 100 m above the surface is not air"); _fails += 1

	# CONTROL: a column far outside the block must clamp to the edge, not match
	# the probe column. If these agree, the sampler is ignoring position.
	var far: float = terrain.sample_density(Vector3(PX + 400000.0, expect, PZ))
	print("  CONTROL 400 km away: density %+.2f (must differ from %+.2f)" % [far, at_surface])
	if abs(far - at_surface) < 1.0:
		print("  FAIL — the field does not vary with position at all"); _fails += 1

	_done()
	return true


func _done() -> void:
	print("")
	if _fails == 0:
		print("VERDICT: the heightfield IS the terrain — right axes, right origin,")
		print("         right sign. Whatever is on screen, the field is in.")
	else:
		print("VERDICT: ", _fails, " check(s) failed.")
	quit(0 if _fails == 0 else 1)