OverviewFilesCommitsIssuesDocsReleasesPeers

proc-nodes / demo / sdf_grid_gate.gd

50 lines · gd ref:
## The SDF-grid family, end to end: mesh -> grid -> fillet -> mesh.
##
## This is MTN_Solidify's chain, which mountain_voronoi1.blend is built from and
## which was skipped at import until these three nodes existed.
extends SceneTree
var _fail := 0
func _ck(n: String, ok: bool, d: String) -> void:
	print("  %-34s %s  %s" % [n, "PASS" if ok else "FAIL", d])
	if not ok: _fail += 1

func _init() -> void:
	var g := ProcNodeGraph.new()
	var sphere := g.add_node(MeshUVSphereNode.new())
	g.get_node(sphere).set_input_default(2, 1.0)          # radius
	var to_grid := g.add_node(MeshToSDFGridNode.new())
	g.get_node(to_grid).set_input_default(1, 0.25)        # voxel size (Blender's default is 0.30)
	g.connect_sockets(sphere, 0, to_grid, 0)
	var fillet := g.add_node(SDFGridFilletNode.new())
	g.get_node(fillet).set_input_default(1, 1)            # iterations
	g.connect_sockets(to_grid, 0, fillet, 0)
	var to_mesh := g.add_node(GridToMeshNode.new())
	g.connect_sockets(fillet, 0, to_mesh, 0)

	var grid_geo = g.evaluate_socket(to_grid, 0)
	var vol = grid_geo.get_volume() if grid_geo != null else null
	_ck("mesh -> SDF grid", vol != null and vol.get_density_grid().size() > 0,
			"%d voxels, res %v" % [vol.get_density_grid().size() if vol != null else 0,
								   vol.get_grid_resolution() if vol != null else Vector3i.ZERO])

	var out = g.evaluate_socket(to_mesh, 0)
	var m = out.get_mesh() if out != null else null
	_ck("grid -> mesh", m != null and m.get_vertex_count() > 0 and m.get_triangle_count() > 0,
			"%d verts, %d tris" % [m.get_vertex_count() if m != null else -1,
								   m.get_triangle_count() if m != null else -1])
	if m != null and m.get_vertex_count() > 0:
		# ProcMesh.positions is a C++ member, not a bound property; the only
		# way into the vertices from GDScript is the ArrayMesh it builds.
		var verts: PackedVector3Array = m.to_array_mesh().surface_get_arrays(0)[Mesh.ARRAY_VERTEX]
		var worst := 0.0
		for i in range(verts.size()):
			worst = max(worst, abs(verts[i].length() - 1.0))
		# A filleted unit sphere is still about a unit sphere; the fillet rounds
		# creases and a sphere has none, so this also says the fillet did not
		# eat the field.
		_ck("round trip is still a sphere", worst < 0.25, "worst |r-1| = %.4f" % worst)
	else:
		_ck("round trip is still a sphere", false, "no mesh")
	print("\n%d/%d passed" % [3 - _fail, 3])
	quit(1 if _fail > 0 else 0)