## 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