Files
train-game/scripts/deco.gd
2026-09-16 17:55:38 +02:00

107 lines
3.3 KiB
GDScript

extends RefCounted
## Low-Poly Bausteine aus Godot-Primitiven.
const Mat = preload("res://scripts/mat.gd")
const TRUNK := Color("6b4a2f")
const PINE := [Color("2f7a45"), Color("35894e"), Color("276b3c")]
const LEAF := [Color("58a844"), Color("e0932f"), Color("d8c33c"), Color("7cb85a")]
const ROCK := Color("8c8b86")
static func _mi(mesh: Mesh, color: Color, pos: Vector3) -> MeshInstance3D:
var m := MeshInstance3D.new()
m.mesh = mesh
m.material_override = Mat.flat(color)
m.position = pos
return m
static func cone(bottom_r: float, height: float, sides: int = 7) -> CylinderMesh:
var c := CylinderMesh.new()
c.top_radius = 0.0
c.bottom_radius = bottom_r
c.height = height
c.radial_segments = sides
c.rings = 1
return c
static func cyl(r: float, height: float, sides: int = 7) -> CylinderMesh:
var c := CylinderMesh.new()
c.top_radius = r
c.bottom_radius = r
c.height = height
c.radial_segments = sides
c.rings = 1
return c
static func box(size: Vector3) -> BoxMesh:
var b := BoxMesh.new()
b.size = size
return b
static func blob(r: float) -> SphereMesh:
var s := SphereMesh.new()
s.radius = r
s.height = r * 1.9
s.radial_segments = 7
s.rings = 4
return s
## Nadelbaum: Stamm + zwei bis drei Kegel-Etagen.
static func pine_tree(rng: RandomNumberGenerator) -> Node3D:
var n := Node3D.new()
var h := rng.randf_range(3.4, 6.2)
var col: Color = PINE[rng.randi() % PINE.size()]
n.add_child(_mi(cyl(0.18, h * 0.3, 6), TRUNK, Vector3(0, h * 0.15, 0)))
var tiers := 3 if h > 4.6 else 2
for i in tiers:
var f := float(i) / float(tiers)
var r := lerpf(h * 0.28, h * 0.13, f)
var th := h * 0.42
var y := h * 0.26 + f * h * 0.32 + th * 0.5
n.add_child(_mi(cone(r, th, 7), col, Vector3(0, y, 0)))
n.rotate_y(rng.randf() * TAU)
return n
## Laubbaum: Stamm + facettierte Krone.
static func round_tree(rng: RandomNumberGenerator) -> Node3D:
var n := Node3D.new()
var h := rng.randf_range(2.6, 4.4)
var col: Color = LEAF[rng.randi() % LEAF.size()]
n.add_child(_mi(cyl(0.16, h * 0.55, 6), TRUNK, Vector3(0, h * 0.275, 0)))
var r := h * 0.42
n.add_child(_mi(blob(r), col, Vector3(0, h * 0.55 + r * 0.55, 0)))
n.rotate_y(rng.randf() * TAU)
return n
static func rock(rng: RandomNumberGenerator) -> Node3D:
var n := Node3D.new()
var r := rng.randf_range(0.4, 1.1)
var m := _mi(blob(r), ROCK, Vector3(0, r * 0.35, 0))
m.scale = Vector3(1.0, rng.randf_range(0.4, 0.7), rng.randf_range(0.8, 1.2))
n.add_child(m)
n.rotate_y(rng.randf() * TAU)
return n
## Haus: Quader + Giebeldach (+ optionaler Schornstein).
static func house(wall: Color, roof: Color, w: float, d: float, h: float, chimney := false) -> Node3D:
var n := Node3D.new()
n.add_child(_mi(box(Vector3(w, h, d)), wall, Vector3(0, h * 0.5, 0)))
var p := PrismMesh.new()
p.size = Vector3(w * 1.14, h * 0.6, d * 1.06)
n.add_child(_mi(p, roof, Vector3(0, h + h * 0.3, 0)))
if chimney:
n.add_child(_mi(box(Vector3(w * 0.16, h * 0.7, w * 0.16)), roof.darkened(0.3),
Vector3(w * 0.25, h + h * 0.45, 0)))
# Kollision nur fuer die Aussenkamera, damit sie nicht durch Waende schaut
var body := StaticBody3D.new()
body.collision_layer = 2
body.collision_mask = 0
var shape := CollisionShape3D.new()
var bs := BoxShape3D.new()
bs.size = Vector3(w * 1.14, h * 1.6, d * 1.06)
shape.shape = bs
shape.position = Vector3(0, h * 0.8, 0)
body.add_child(shape)
n.add_child(body)
return n