OverviewFilesCommitsIssuesDocsReleasesPeers

proc-nodes / demo / mesh_channel_gate.gd

134 lines · gd ref:
## The mesh channel, inside proc-nodes: does a MESH survive the Dictionary?
##
## The bridge hands ProcMesh points, a corner array, faces as corner ranges,
## edges and attributes on their domains. This asserts each of those arrives as
## itself -- especially the two that cannot be checked by counting:
##
##   FACES     a quad must stay one face, not become two triangles. Everything
##             that crossed as an ArrayMesh became triangles, and a per-face
##             value then had nowhere to agree.
##   EDGES     ProcMesh DERIVES its edges in its own sorted order, so an edge
##             value copied by index lands on the wrong edge with the counts
##             still matching. The sender's edge array is shipped so it can be
##             remapped; this gate gives every edge a value that encodes its own
##             vertex pair, so a mis-landed value is arithmetic, not luck.
extends SceneTree

var passed := 0
var failed := 0

func ck(name: String, ok: bool, detail: String = "") -> void:
	print("  %-46s %s  %s" % [name, "PASS" if ok else "FAIL", detail])
	if ok: passed += 1
	else: failed += 1

func _init() -> void:
	# A 2x2 grid of QUADS: 9 points, 4 faces, 16 corners, 12 edges.
	var pts := PackedVector3Array()
	for y in range(3):
		for x in range(3):
			pts.append(Vector3(float(x), 0.0, float(y)))
	var corners := PackedInt32Array()
	var faces := PackedInt32Array()
	for fy in range(2):
		for fx in range(2):
			faces.append(corners.size())
			var v0 := fy * 3 + fx
			corners.append_array(PackedInt32Array([v0, v0 + 1, v0 + 4, v0 + 3]))
	faces.append(corners.size())

	# Edges in a deliberately UNSORTED order, some stored high-to-low, so the
	# sender's order cannot accidentally equal ProcMesh's.
	var edge_pairs := [[4,3],[0,1],[1,2],[3,0],[4,1],[5,2],[3,4],[8,5],[6,7],[7,8],[6,3],[7,4],[8,7],[5,4],[2,1]]
	var seen := {}
	var edges := PackedInt32Array()
	var edge_vals := PackedFloat32Array()
	for e in edge_pairs:
		var lo: int = min(e[0], e[1])
		var hi: int = max(e[0], e[1])
		var key := lo * 100 + hi
		if seen.has(key): continue
		seen[key] = true
		edges.append_array(PackedInt32Array([e[0], e[1]]))
		# The value IS the pair: any mis-landing is visible as a wrong pair.
		edge_vals.append(float(key))

	var d := {}
	d["points"] = pts
	d["corners"] = corners
	d["faces"] = faces
	d["edges"] = edges
	d["attributes"] = [
		{"name": "pt_h", "domain": 0, "type": 0,
		 "data": PackedFloat32Array([0,1,2,3,4,5,6,7,8]).to_byte_array()},
		{"name": "edge_pair", "domain": 1, "type": 0, "data": edge_vals.to_byte_array()},
		{"name": "face_id", "domain": 2, "type": 4,
		 "data": PackedInt32Array([10,11,12,13]).to_byte_array()},
		{"name": "uvish", "domain": 3, "type": 1,
		 "data": PackedVector2Array([Vector2(0,0),Vector2(1,0),Vector2(1,1),Vector2(0,1),
			Vector2(0,0),Vector2(1,0),Vector2(1,1),Vector2(0,1),
			Vector2(0,0),Vector2(1,0),Vector2(1,1),Vector2(0,1),
			Vector2(0,0),Vector2(1,0),Vector2(1,1),Vector2(0,1)]).to_byte_array()},
	]

	var m: ProcMesh = ProcMesh.from_mesh_data(d)
	ck("points survive", m.get_vertex_count() == 9, "%d verts" % m.get_vertex_count())
	ck("corners survive", m.get_corner_count() == 16, "%d corners" % m.get_corner_count())
	# ⛔ THE ONE THE ARRAYMESH PATH CANNOT PASS: 4 quads, not 8 triangles.
	ck("a quad stays one face", m.get_face_count() == 4, "%d faces" % m.get_face_count())
	ck("edges are derived", m.get_domain_size(1) == 12, "%d edges" % m.get_domain_size(1))

	ck("point attribute", m.get_attribute_values(0, "pt_h").size() == 9,
		"%d values" % m.get_attribute_values(0, "pt_h").size())
	ck("face attribute", str(m.get_attribute_values(2, "face_id")) == "[10, 11, 12, 13]",
		str(m.get_attribute_values(2, "face_id")))
	ck("corner attribute", m.get_attribute_values(3, "uvish").size() == 16,
		"%d values" % m.get_attribute_values(3, "uvish").size())

	# Every edge value must equal the key of the pair it now sits on, under
	# ProcMesh's OWN edge order.
	var vals: Array = m.get_attribute_values(1, "edge_pair")
	var ours: PackedInt32Array = m.get_indices()  # placeholder, replaced below
	var bad := 0
	var our_edges := _our_edges(m)
	for i in range(our_edges.size()):
		var pair: Array = our_edges[i]
		var want := float(pair[0] * 100 + pair[1])
		if i >= vals.size() or abs(float(vals[i]) - want) > 1e-6:
			bad += 1
	ck("every edge value landed on ITS OWN edge", bad == 0 and vals.size() == 12,
		"%d of %d wrong" % [bad, vals.size()])

	# And out again.
	var back: Dictionary = m.to_mesh_data()
	var f2: PackedInt32Array = back["faces"]
	ck("round trip keeps the faces", f2.size() == 5 and f2[4] == 16,
		"%d offsets, last %d" % [f2.size(), f2[f2.size() - 1] if f2.size() > 0 else -1])
	var names := []
	for a in back["attributes"]:
		names.append(a["name"])
	names.sort()
	ck("round trip keeps every attribute", names == ["edge_pair", "face_id", "pt_h", "uvish"],
		str(names))

	print("%d/%d passed" % [passed, passed + failed])
	quit(0 if failed == 0 else 1)


## ProcMesh's derived edge order, rebuilt here the way the class documents it:
## each pair low-to-high, the set sorted.
func _our_edges(m: ProcMesh) -> Array:
	var set := {}
	var corners: PackedInt32Array = m.get_indices()
	var fo: PackedInt32Array = m.get_face_offsets()
	for f in range(m.get_face_count()):
		var b: int = fo[f] if fo.size() > 1 else f * 3
		var e: int = fo[f + 1] if fo.size() > 1 else f * 3 + 3
		for c in range(b, e):
			var a: int = corners[c]
			var bb: int = corners[b + ((c - b + 1) % (e - b))]
			set[[min(a, bb), max(a, bb)]] = true
	var out := set.keys()
	out.sort()
	return out