OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scripts / probe_node_influence.gd

80 lines · gd ref:
## Does changing each node's parameters change the baked mesh at all?
##
## Bakes the graph, perturbs one input default, re-bakes, reports how far
## vertices moved. A node whose every parameter moves nothing is not
## contributing to the result.
##
## TWO instrument bugs were found by its own controls before it said anything
## true. First it freed each host after baking, every bake after the second
## returned an empty mesh, and the size sentinel read `inf` everywhere -- which
## looks exactly like "nothing has any effect". Then, with that fixed, every
## perturbation ACCUMULATED: ResourceLoader.load returns the CACHED resource, so
## "a fresh load" was the same object each time and node 0's vertex-count change
## leaked into every later row. Hence CACHE_MODE_IGNORE_DEEP below.
##
## FIRST LINE IS A CONTROL: re-bake with NO perturbation. It must read 0.
## The first version of this script freed each host after baking, every bake
## after the second returned an empty mesh, and the size-mismatch sentinel
## printed `inf` for nearly every socket -- which reads exactly like "nothing
## has any effect". Without the control I would have reported my own broken
## instrument as the finding.
##
## ⚠ RUNNING GODOT DIRECTLY REWRITES project.godot. The editor strips every
## comment and drops any setting that equals its default -- it ate the 30-line
## Jolt block and `screen_space_roughness_limiter/enabled` during this script's
## own development. scripts/dev-editor.sh restores it; a bare `godot --headless
## --path .` does not. Check `git status project.godot` after running this
## locally. CI is unaffected: it runs in a throwaway git-archive extraction.
extends SceneTree

var _hosts := []


func _bake(g: ProcNodeGraph) -> PackedVector3Array:
	var host := ProcGeometryNode.new()
	host.use_gpu = false
	host.set_graph(g)
	get_root().add_child(host)
	_hosts.append(host)          # deliberately kept alive; short-lived process
	var m: ArrayMesh = host.bake_mesh()
	if m == null or m.get_surface_count() == 0:
		return PackedVector3Array()
	return m.surface_get_arrays(0)[Mesh.ARRAY_VERTEX]


func _diff(a: PackedVector3Array, b: PackedVector3Array) -> String:
	if a.is_empty() or b.is_empty():
		return "BAKE FAILED"
	if a.size() != b.size():
		return "count %d -> %d" % [a.size(), b.size()]
	var d := 0.0
	for i in range(a.size()):
		d = max(d, (a[i] - b[i]).length())
	return "%12.6f%s" % [d, "" if d > 1e-6 else "   <-- NO EFFECT"]


func _init() -> void:
	var a := OS.get_cmdline_user_args()
	var path: String = a[0]
	var ref := _bake(ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_IGNORE_DEEP))
	print("baseline: ", ref.size(), " vertices")
	print("CONTROL (no perturbation): ", _diff(ref, _bake(ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_IGNORE_DEEP))))

	var base: ProcNodeGraph = ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_IGNORE_DEEP)
	print("\n%-5s %-15s %-14s %s" % ["node", "type", "socket", "max vertex move"])
	for ni in range(base.get_node_count()):
		var node: ProcNode = base.get_node(ni)
		if node == null:
			continue
		for si in range(node.get_input_count()):
			var cur = node.get_input_default(si)
			var t := typeof(cur)
			if t != TYPE_FLOAT and t != TYPE_INT:
				continue
			var nv = (float(cur) * 1.7 + 0.9) if t == TYPE_FLOAT else int(cur) + 1
			var p: ProcNodeGraph = ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_IGNORE_DEEP)
			p.get_node(ni).set_input_default(si, nv)
			print("%-5d %-15s %-14s %s" % [ni, node.get_node_type(),
					node.get_input_name(si), _diff(ref, _bake(p))])
	quit()