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