OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / maps / shapes / cone_shape.gd

75 lines · gd ref:
extends BrushShape
class_name ConeShape
## A cone standing on the aimed surface: an N-gon base on the surface, apex at the far
## side of the bounds along the normal (TrenchBroom BasicShapes Cone). One convex brush.


func id() -> String:
	return "cone"


func params() -> Array:
	return [{"id": "segments", "label": "Sides", "type": "int", "default": 12,
		"min": 3, "max": 32, "step": 1, "tip": "Sides of the base. More = rounder."}]


func pieces(lo: Vector3, hi: Vector3, ctx: Dictionary) -> Array:
	var vert: int = int(ctx["axis"])
	var ab := CylinderShape.across(vert)
	var n := clampi(int(param(ctx, "segments")), 3, 32)
	if hi[ab[0]] - lo[ab[0]] < 1.0e-4 or hi[ab[1]] - lo[ab[1]] < 1.0e-4 or hi[vert] - lo[vert] < 1.0e-4:
		return []
	# The base sits ON the aimed surface; the apex points away from it.
	var up: bool = (ctx["normal"] as Vector3)[vert] >= 0.0
	var base_h: float = lo[vert] if up else hi[vert]
	var apex_h: float = hi[vert] if up else lo[vert]
	var ring: Array[Vector3] = []
	for p: Vector2 in CylinderShape.ring2d(lo, hi, ab[0], ab[1], n):
		var v := Vector3.ZERO
		v[ab[0]] = p.x
		v[ab[1]] = p.y
		v[vert] = base_h
		ring.append(v)
	var apex := Vector3.ZERO
	apex[ab[0]] = (lo[ab[0]] + hi[ab[0]]) * 0.5
	apex[ab[1]] = (lo[ab[1]] + hi[ab[1]]) * 0.5
	apex[vert] = apex_h
	var piece := poly_piece(ring, apex)
	return [piece] if not piece.is_empty() else []


## A pyramid over a convex `ring`, apex `apex`: the base plane + one plane per ring edge
## through the apex. Plane signs from the centroid (the ShapeKit rule: an interior
## point cannot be got backwards).
static func poly_piece(ring: Array[Vector3], apex: Vector3) -> Dictionary:
	var n := ring.size()
	if n < 3:
		return {}
	var centroid := apex
	for p: Vector3 in ring:
		centroid += p
	centroid /= float(n + 1)
	var planes := PackedFloat32Array()
	var tris: Array = [[ring[0], ring[1], ring[2]]]
	for i in n:
		tris.append([apex, ring[i], ring[(i + 1) % n]])
	for t: Array in tris:
		var nn: Vector3 = ((t[1] as Vector3) - (t[0] as Vector3)).cross((t[2] as Vector3) - (t[0] as Vector3))
		if nn.length() < 1.0e-9:
			return {}
		nn = nn.normalized()
		var d: float = nn.dot(t[0])
		if nn.dot(centroid) > d:
			nn = -nn
			d = -d
		planes.append_array(PackedFloat32Array([nn.x, nn.y, nn.z, d]))
	var edges := PackedVector3Array()
	for i in n:
		edges.append(ring[i]); edges.append(ring[(i + 1) % n])
		edges.append(ring[i]); edges.append(apex)
	return {"planes": planes, "edges": edges}


func describe(_lo: Vector3, _hi: Vector3, ctx: Dictionary, size_text: String) -> String:
	return "cone, %d sides, %s" % [clampi(int(param(ctx, "segments")), 3, 32), size_text]