OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_terrain_material.gd

80 lines · gd ref:
extends SceneTree
## GATE — the Terrain material panel's model (TerrainMaterial) drives the textured
## Geiss terrain shader (terrain_layers.gdshader) and saves what it drives.
##   T1  defaults() + apply(): five layers, every shader input set, no error
##   T2  a control change (rotation, offset, parallax ×) lands in the shader at once
##       AND is handed to `save` with the new value
##   T3  a texture-set change REBUILDS the atlases (new Texture2DArray, new layer index);
##       a non-set change does NOT (TexturePacks.build is a hitch)
##   T4  merged(): a saved cfg overrides the defaults; an unknown set id is ignored
##   T5  the panel builds (one OptionButton per layer)
##
## Run: CI=true HYPERTPS_DAEMON_TCP=127.0.0.1:1 "$HYPER_TPS_GODOT" --headless --path . --script res://scenes/test/gate_terrain_material.gd

const SETS := ["polyhaven/clean_pebbles", "trimsheets/stone_base", "polyhaven/cliff_side",
		"polyhaven/castle_wall_slates", "polyhaven/clean_pebbles"]

var ok := true
var checks := 0


func _p(c: bool, m: String, got: Variant = "") -> void:
	checks += 1
	print(("ok   " if c else "FAIL: ") + m + ("" if str(got) == "" else "  (" + str(got) + ")"))
	if not c:
		ok = false


func _init() -> void:
	var mat := ShaderMaterial.new()
	mat.shader = load("res://scenes/maps/undersea/terrain_layers.gdshader")
	var tm := TerrainMaterial.new(mat, 64)
	var saved: Array = []
	tm.save = func(c: Dictionary) -> void: saved.append(c.duplicate(true))
	# ---- T1 ----
	var why := tm.apply(TerrainMaterial.defaults(SETS))
	var idx: PackedInt32Array = mat.get_shader_parameter(&"layer_index")
	_p(why == "" and idx.size() == 5, "T1 defaults apply: 5 layers", "%s %s" % [why, str(idx)])
	_p(mat.get_shader_parameter(&"tex_atlas") is Texture2DArray and (mat.get_shader_parameter(&"uv_rot_deg") as PackedFloat32Array).size() == 5,
		"T1 atlases and per-layer UV transforms are set")
	# ---- T2 ----
	var atlas_before: Variant = mat.get_shader_parameter(&"tex_atlas")
	tm.set_value(tm.cfg["layers"][2], "rot", 45.0)
	tm.set_value(tm.cfg["layers"][1], "off", [0.25, -0.5])
	tm.set_value(tm.cfg, "pom_scale", 2.5)
	tm.set_value(tm.cfg, "far_blend", 0.8)
	tm.set_value(tm.cfg, "macro", 0.1)
	var rot: PackedFloat32Array = mat.get_shader_parameter(&"uv_rot_deg")
	var off: PackedVector2Array = mat.get_shader_parameter(&"uv_off")
	_p(is_equal_approx(rot[2], 45.0) and off[1].is_equal_approx(Vector2(0.25, -0.5))
			and is_equal_approx(float(mat.get_shader_parameter(&"pom_scale")), 2.5),
		"T2 rotation / offset / parallax × reach the shader", "%s %s %s" % [rot[2], off[1], mat.get_shader_parameter(&"pom_scale")])
	_p(is_equal_approx(float(mat.get_shader_parameter(&"far_blend")), 0.8) and is_equal_approx(float(mat.get_shader_parameter(&"macro_strength")), 0.1),
		"T2 anti-tiling controls (far tiling mix, macro variation) reach the shader")
	_p(saved.size() == 5 and is_equal_approx(float(saved[2]["layers"][2]["rot"]), 45.0) and is_equal_approx(float(saved[2]["pom_scale"]), 2.5),
		"T2 every change is handed to save with the new values", saved.size())
	# ---- T3 ----
	_p(mat.get_shader_parameter(&"tex_atlas") == atlas_before, "T3 a non-set change does not rebuild the atlases")
	var other := ""
	for id in TexturePacks.ids():
		if not SETS.has(id):
			other = id
			break
	tm.set_value(tm.cfg["layers"][0], "set", other)
	var idx2: PackedInt32Array = mat.get_shader_parameter(&"layer_index")
	_p(other != "" and mat.get_shader_parameter(&"tex_atlas") != atlas_before and idx2.size() == 5,
		"T3 a texture-set change rebuilds the atlases (%s)" % other, str(idx2))
	# ---- T4 ----
	var m := TerrainMaterial.merged(TerrainMaterial.defaults(SETS),
			{"normal": 0.4, "layers": [{"set": "no/such_set", "rot": 30.0}, {"tile": 7.0}]})
	_p(is_equal_approx(float(m["normal"]), 0.4) and String(m["layers"][0]["set"]) == SETS[0]
			and is_equal_approx(float(m["layers"][0]["rot"]), 30.0) and is_equal_approx(float(m["layers"][1]["tile"]), 7.0),
		"T4 a saved cfg overrides the defaults; an unknown set id is ignored")
	# ---- T5 ----
	var ui := tm.build_ui()
	var opts := ui.find_children("*", "OptionButton", true, false).size()
	_p(opts == 5, "T5 the panel builds with one texture picker per layer", opts)
	ui.free()
	print("\n%d checks — %s" % [checks, "PASS" if ok else "FAIL"])
	quit(0 if ok else 1)