extends RefCounted ## Formen aus Regeln. Der Kern des Low-Poly-Looks sind runde Kanten: jede ## Form wird als Profil (Radius, Höhe) beschrieben und um die Achse gedreht. ## Fasen sind Viertelkreise im Profil. Kein Mesh kommt aus einer Datei. ## Kein class_name: den Klassencache baut nur der Editor, headless fehlt er. const Stoffe = preload("res://scripts/stoffe.gd") const FASE_SEG := 4 ## Punkte je Viertelkreis - vier reichen bei glatten Normalen ## Ein Profil um die Y-Achse drehen. `profil` läuft von unten (Achse) nach ## oben (Achse), Punkte sind (radius, höhe). Glatte Normalen, damit die ## Fasen weich wirken und nicht wie Absätze. static func drehen(profil: PackedVector2Array, segmente := 40) -> ArrayMesh: var st := SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLES) st.set_smooth_group(0) for i in range(profil.size() - 1): var a := profil[i] var b := profil[i + 1] for k in segmente: var t0 := TAU * float(k) / segmente var t1 := TAU * float(k + 1) / segmente var p00 := Vector3(cos(t0) * a.x, a.y, sin(t0) * a.x) var p10 := Vector3(cos(t1) * a.x, a.y, sin(t1) * a.x) var p01 := Vector3(cos(t0) * b.x, b.y, sin(t0) * b.x) var p11 := Vector3(cos(t1) * b.x, b.y, sin(t1) * b.x) if a.x > 0.0001: st.add_vertex(p00) st.add_vertex(p10) st.add_vertex(p01) if b.x > 0.0001: st.add_vertex(p10) st.add_vertex(p11) st.add_vertex(p01) st.index() st.generate_normals() return st.commit() ## Viertelkreis von `von` nach `bis` um die Ecke `mitte` (Profilkoordinaten). static func _bogen(mitte: Vector2, r: float, a0: float, a1: float) -> PackedVector2Array: var out := PackedVector2Array() for k in range(1, FASE_SEG + 1): var t := lerpf(a0, a1, float(k) / FASE_SEG) out.append(mitte + Vector2(cos(t), sin(t)) * r) return out ## Zylinder oder Kegelstumpf mit runden Kanten oben und unten. static func rund_zylinder(r_unten: float, r_oben: float, hoehe: float, fase: float) -> ArrayMesh: fase = minf(fase, minf(hoehe * 0.5, minf(r_unten, r_oben) * 0.9)) var p := PackedVector2Array() p.append(Vector2(0.0, 0.0)) p.append(Vector2(r_unten - fase, 0.0)) p.append_array(_bogen(Vector2(r_unten - fase, fase), fase, -PI * 0.5, 0.0)) p.append_array(_bogen(Vector2(r_oben - fase, hoehe - fase), fase, 0.0, PI * 0.5)) p.append(Vector2(0.0, hoehe)) return drehen(p) static func scheibe(radius: float, hoehe: float, fase: float) -> ArrayMesh: return rund_zylinder(radius, radius, hoehe, fase) ## Ring mit rundem Querschnitt - für Ränder und Bänder. static func ring(radius: float, dicke: float) -> TorusMesh: var t := TorusMesh.new() t.inner_radius = radius - dicke t.outer_radius = radius + dicke t.rings = 40 t.ring_segments = 10 return t static func _teil(mesh: Mesh, stoff: Material, pos: Vector3, eltern: Node3D) -> MeshInstance3D: var mi := MeshInstance3D.new() mi.mesh = mesh mi.material_override = stoff mi.position = pos eltern.add_child(mi) return mi ## Würfelbecher: Kegelstumpf mit runden Kanten, oben ein Messingrand, ein ## Band um den Bauch. Geschlossen - die Kamera schaut von oben. static func becher(hoehe := 0.44, oben := 0.33, unten := 0.42) -> Node3D: var n := Node3D.new() _teil(rund_zylinder(unten, oben, hoehe, 0.05), Stoffe.holz_dunkel(), Vector3.ZERO, n) _teil(ring(oben - 0.01, 0.018), Stoffe.messing(), Vector3(0, hoehe - 0.02, 0), n) _teil(ring(lerpf(unten, oben, 0.35) + 0.004, 0.012), Stoffe.messing(), Vector3(0, hoehe * 0.35, 0), n) return n ## Laterne: Sockel, leuchtendes Glas, Kappe, Bügel. static func laterne(hoehe := 0.5) -> Node3D: var n := Node3D.new() _teil(rund_zylinder(0.11, 0.09, 0.05, 0.015), Stoffe.messing(), Vector3.ZERO, n) var glas := Stoffe.eigene("glas") glas.emission_enabled = true glas.emission = Color(1.0, 0.80, 0.40) glas.emission_energy_multiplier = 1.6 _teil(rund_zylinder(0.08, 0.07, hoehe * 0.6, 0.02), glas, Vector3(0, 0.05, 0), n) _teil(rund_zylinder(0.10, 0.03, 0.08, 0.015), Stoffe.messing(), Vector3(0, 0.05 + hoehe * 0.6, 0), n) _teil(ring(0.035, 0.007), Stoffe.messing(), Vector3(0, 0.05 + hoehe * 0.6 + 0.10, 0), n) var licht := OmniLight3D.new() licht.light_color = Color(1.0, 0.85, 0.55) licht.light_energy = 0.8 licht.omni_range = 1.6 licht.position = Vector3(0, 0.05 + hoehe * 0.3, 0) n.add_child(licht) return n