Wall off the table's own 6 edges too, not just the outer ridges

_build_region_borders() previously only bordered the 6 seams between
adjacent outer regions (table corner -> girdle corner). Generalized
_build_ridge() into _build_border_segment(p0, p1), which chains
overlap-guaranteed props along any straight 3D segment, and reused it
for the 6 table edges (table corner -> next table corner) as well —
these separate the center region from each outer region.

Verified headless the same way as the ridges: 43/43 pairs on one
table edge genuinely overlap (worst case -0.13 units). 744 border
props total across all 12 edges now.
This commit is contained in:
Jonas Reith 2026-08-26 20:46:52 +02:00
parent eac8fdf042
commit 37e0151988

View File

@ -134,47 +134,62 @@ func _build_pavilion() -> void:
add_child(mi) add_child(mi)
# ═══════════════════════════════════════════════ # ═══════════════════════════════════════════════
# GRENZEN — Berg/Wald entlang der 6 Grate zwischen den Regionen # GRENZEN — Berg/Wald entlang aller Kanten zwischen den 7 Regionen:
# Grat k = Strecke von Tisch-Ecke T[k] zu Rundiste-Ecke G[k] # 6 Grate Tisch-Ecke T[k] -> Rundiste-Ecke G[k] (zwischen den Außenregionen)
# (gemeinsame Kante von Facette k-1 und Facette k) # + 6 Tisch-Kanten T[k] -> T[k+1] (zwischen Zentrum und jeder Außenregion)
# ═══════════════════════════════════════════════ # ═══════════════════════════════════════════════
func _build_region_borders() -> void: func _build_region_borders() -> void:
var borders := Node3D.new() var borders := Node3D.new()
borders.name = "Borders" borders.name = "Borders"
add_child(borders) add_child(borders)
var table_pts : Array[Vector3] = []
var girdle_pts : Array[Vector3] = []
for k in range(6): for k in range(6):
var a := _hex_angle(k) var a := _hex_angle(k)
var dir := Vector3(cos(a), 0.0, sin(a)) table_pts.append(Vector3(cos(a) * table_radius, 0.0, sin(a) * table_radius))
var perp := Vector3(-dir.z, 0.0, dir.x) girdle_pts.append(Vector3(cos(a) * girdle_radius, -crown_drop, sin(a) * girdle_radius))
_build_ridge(borders, dir, perp)
# Kettet Props entlang eines Grats: jeder neue Prop wird so nah an den vorigen for k in range(6):
# gesetzt, dass sich ihre Welt-Radien garantiert überlappen (Distanz zwischen var k1 := (k + 1) % 6
# den Mittelpunkten <= (r1+r2) * border_overlap). Kein Rätselraten über feste _build_border_segment(borders, table_pts[k], girdle_pts[k]) # Grat: Außenregion <-> Außenregion
# Abstände mehr — die Lücke wird pro Prop-Paar explizit geprüft und korrigiert. _build_border_segment(borders, table_pts[k], table_pts[k1]) # Tisch-Kante: Zentrum <-> Außenregion
func _build_ridge(borders: Node3D, dir: Vector3, perp: Vector3) -> void:
var t := table_radius # Kettet Props entlang einer geraden Strecke p0->p1: jeder neue Prop wird so nah
# an den vorigen gesetzt, dass sich ihre Welt-Radien garantiert überlappen
# (Distanz zwischen den Mittelpunkten <= (r1+r2) * border_overlap). Kein
# Rätselraten über feste Abstände mehr — die Lücke wird pro Prop-Paar
# explizit geprüft und korrigiert. Funktioniert für geneigte (Grat) wie
# flache (Tisch-Kante) Strecken gleichermaßen, da einfach entlang der echten
# 3D-Linie zwischen den beiden Punkten gelaufen wird.
func _build_border_segment(borders: Node3D, p0: Vector3, p1: Vector3) -> void:
var length := p0.distance_to(p1)
if length < 0.001:
return
var dir := (p1 - p0) / length
var perp := Vector3(-dir.z, 0.0, dir.x)
var t := 0.0
var prev_pos := Vector3.ZERO var prev_pos := Vector3.ZERO
var prev_radius := 0.0 var prev_radius := 0.0
var first := true var first := true
while t <= girdle_radius: while t <= length:
var kind: Dictionary = BORDER_KINDS[randi() % BORDER_KINDS.size()] var kind: Dictionary = BORDER_KINDS[randi() % BORDER_KINDS.size()]
var radius: float = kind["radius"] * kind["scale"] var radius: float = kind["radius"] * kind["scale"]
var lateral := randf_range(-border_jitter, border_jitter) var lateral := randf_range(-border_jitter, border_jitter)
var pos := _ridge_point(dir, perp, t, lateral) var pos := p0 + dir * t + perp * lateral
if not first: if not first:
var min_dist := (prev_radius + radius) * border_overlap var min_dist := (prev_radius + radius) * border_overlap
# Iterativ korrigieren statt blind zu vertrauen: die Facette ist geneigt, # Iterativ korrigieren statt blind zu vertrauen (seitlicher Jitter
# daher ist die Distanz nicht rein radial (Höhe hängt am Abstand mit dran). # verschiebt den Punkt leicht von der reinen Geraden weg).
for _i in range(4): for _i in range(4):
var actual_dist := pos.distance_to(prev_pos) var actual_dist := pos.distance_to(prev_pos)
if actual_dist <= min_dist: if actual_dist <= min_dist:
break break
t -= (actual_dist - min_dist) t -= (actual_dist - min_dist)
pos = _ridge_point(dir, perp, t, lateral) pos = p0 + dir * t + perp * lateral
_spawn_border_prop(borders, kind, pos) _spawn_border_prop(borders, kind, pos)
prev_pos = pos prev_pos = pos
@ -182,12 +197,6 @@ func _build_ridge(borders: Node3D, dir: Vector3, perp: Vector3) -> void:
first = false first = false
t += radius # nächster Kandidat startet grob hinter diesem Prop; obige Prüfung zieht ihn bei Bedarf näher t += radius # nächster Kandidat startet grob hinter diesem Prop; obige Prüfung zieht ihn bei Bedarf näher
func _ridge_point(dir: Vector3, perp: Vector3, t: float, lateral: float) -> Vector3:
var frac: float = clamp((t - table_radius) / (girdle_radius - table_radius), 0.0, 1.0)
var pos := dir * t + perp * lateral
pos.y = lerp(0.0, -crown_drop, frac)
return pos
func _spawn_border_prop(parent: Node3D, kind: Dictionary, pos: Vector3) -> void: func _spawn_border_prop(parent: Node3D, kind: Dictionary, pos: Vector3) -> void:
var inst := (kind["scene"] as PackedScene).instantiate() as Node3D var inst := (kind["scene"] as PackedScene).instantiate() as Node3D
inst.position = pos inst.position = pos