95 lines
3.1 KiB
GDScript
95 lines
3.1 KiB
GDScript
extends Node3D
|
|
## Streckennetz aus Abschnitten. Ein Abschnitt hat einen oder zwei
|
|
## Nachfolger - bei zweien steht dort eine Weiche.
|
|
|
|
const Mat = preload("res://scripts/mat.gd")
|
|
const Deco = preload("res://scripts/deco.gd")
|
|
|
|
const BALLAST := Color("d98d4a")
|
|
const SLEEPER := Color("5e4130")
|
|
const RAIL := Color("9a9da2")
|
|
const STEP := 1.2
|
|
|
|
## Jeder Eintrag: { "name", "curve": Curve3D, "length", "next": [ids], "prev": [ids] }
|
|
var segments: Array = []
|
|
|
|
## Abschnitt anlegen. Anfangs- und Endrichtung werden vorgegeben, damit
|
|
## sich mehrere Abschnitte an einer Weiche ohne Knick treffen.
|
|
func add_segment(seg_name: String, points: Array, t_start: Vector2, t_end: Vector2) -> int:
|
|
var c := Curve3D.new()
|
|
var n := points.size()
|
|
for i in n:
|
|
var p: Vector2 = points[i]
|
|
var t: Vector2
|
|
if i == 0:
|
|
t = t_start.normalized() * p.distance_to(points[1]) / 3.0
|
|
elif i == n - 1:
|
|
t = t_end.normalized() * p.distance_to(points[n - 2]) / 3.0
|
|
else:
|
|
t = (points[i + 1] - points[i - 1]) / 6.0
|
|
c.add_point(Vector3(p.x, 0, p.y), Vector3(-t.x, 0, -t.y), Vector3(t.x, 0, t.y))
|
|
segments.append({"name": seg_name, "curve": c, "length": c.get_baked_length(),
|
|
"next": [], "prev": []})
|
|
return segments.size() - 1
|
|
|
|
func connect_segments(from: int, to: int) -> void:
|
|
segments[from]["next"].append(to)
|
|
segments[to]["prev"].append(from)
|
|
|
|
func finish() -> void:
|
|
for i in segments.size():
|
|
_build_visuals(i)
|
|
|
|
func seg_length(i: int) -> float:
|
|
return float(segments[i]["length"])
|
|
|
|
func seg_name(i: int) -> String:
|
|
return String(segments[i]["name"])
|
|
|
|
func next_of(i: int) -> Array:
|
|
return segments[i]["next"]
|
|
|
|
func prev_of(i: int) -> Array:
|
|
return segments[i]["prev"]
|
|
|
|
func is_switch(i: int) -> bool:
|
|
return segments[i]["next"].size() > 1
|
|
|
|
func sample(i: int, offset: float) -> Transform3D:
|
|
var c: Curve3D = segments[i]["curve"]
|
|
return c.sample_baked_with_rotation(clampf(offset, 0.0, seg_length(i)), true, false)
|
|
|
|
## Nächster Punkt auf irgendeinem Abschnitt - für die Deko-Verteilung.
|
|
func distance_to_track(p: Vector2) -> float:
|
|
var best := INF
|
|
for s in segments:
|
|
var c: Curve3D = s["curve"]
|
|
var q: Vector3 = c.get_closest_point(Vector3(p.x, 0, p.y))
|
|
best = minf(best, Vector2(q.x, q.z).distance_to(p))
|
|
return best
|
|
|
|
func _build_visuals(i: int) -> void:
|
|
var count := int(seg_length(i) / STEP)
|
|
var xf: Array[Transform3D] = []
|
|
for k in count:
|
|
xf.append(sample(i, k * STEP + STEP * 0.5))
|
|
_multi(Deco.box(Vector3(2.7, 0.16, STEP * 1.12)), BALLAST, xf, [Vector3(0, 0.08, 0)])
|
|
_multi(Deco.box(Vector3(2.1, 0.09, 0.34)), SLEEPER, xf, [Vector3(0, 0.20, 0)])
|
|
_multi(Deco.box(Vector3(0.09, 0.11, STEP * 1.08)), RAIL, xf,
|
|
[Vector3(-0.62, 0.29, 0), Vector3(0.62, 0.29, 0)])
|
|
|
|
func _multi(mesh: Mesh, color: Color, xf: Array[Transform3D], offsets: Array) -> void:
|
|
var mm := MultiMesh.new()
|
|
mm.transform_format = MultiMesh.TRANSFORM_3D
|
|
mm.mesh = mesh
|
|
mm.instance_count = xf.size() * offsets.size()
|
|
var i := 0
|
|
for t in xf:
|
|
for o in offsets:
|
|
mm.set_instance_transform(i, t.translated_local(o))
|
|
i += 1
|
|
var mmi := MultiMeshInstance3D.new()
|
|
mmi.multimesh = mm
|
|
mmi.material_override = Mat.flat(color)
|
|
add_child(mmi)
|