88 lines
No EOL
3.5 KiB
GDScript
88 lines
No EOL
3.5 KiB
GDScript
extends RefCounted
|
|
class_name MeshShapes
|
|
## Platzhalter-Meshes für Bibliotheks-Objekte (Form aus dem Kollider-Eintrag,
|
|
## wie der Server-Sichtbarkeit) — geteilt von Viewport-Bauer und Ghost-Preview,
|
|
## damit Objekte im Editor überall identisch aussehen.
|
|
|
|
const SHAPE_BOX := "box"
|
|
const SHAPE_SPHERE := "sphere"
|
|
const SHAPE_CYLINDER := "cylinder"
|
|
const SHAPE_COMPOUND := "compound"
|
|
const SHAPE_NONE := "none"
|
|
|
|
|
|
static func make_placeholder(shape: String, col: Dictionary, scale: float) -> Mesh:
|
|
match shape:
|
|
SHAPE_NONE:
|
|
# Rein dekorativ (Natur-Update, t_7cf80244): kein Kollider — aber
|
|
# im Editor trotzdem sichtbar/wählbar: flache Fußpunkt-Scheibe.
|
|
var d := CylinderMesh.new()
|
|
d.top_radius = 0.4 * scale
|
|
d.bottom_radius = d.top_radius
|
|
d.height = maxf(0.02, 0.02 * scale)
|
|
return d
|
|
SHAPE_SPHERE:
|
|
var s := SphereMesh.new()
|
|
var r := maxf(0.01, float(col.get("radius", 1.0)) * scale)
|
|
s.radius = r
|
|
s.height = r * 2.0
|
|
return s
|
|
SHAPE_CYLINDER:
|
|
var c := CylinderMesh.new()
|
|
c.top_radius = maxf(0.01, float(col.get("radius", 1.0)) * scale)
|
|
c.bottom_radius = c.top_radius
|
|
c.height = maxf(0.01, float(col.get("height", 1.0)) * scale)
|
|
return c
|
|
SHAPE_COMPOUND:
|
|
# Struktur-Objekte (t_d8be9ed7): EIN Mesh aus allen Verbund-Teilen,
|
|
# gebacken in Fußpunkt-Koordinaten (Teil-pos = Versatz vom Fußpunkt,
|
|
# rot_deg = Euler-Grad) — Rampe/Durchgänge also sichtbar wie die
|
|
# echte Kollision. half_height() liefert dafür 0 (schon gebacken).
|
|
var st := SurfaceTool.new()
|
|
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
|
st.set_smooth_group(-1)
|
|
for teil_v in col.get("parts", []):
|
|
var teil: Dictionary = teil_v if teil_v is Dictionary else {}
|
|
var teil_mesh := make_placeholder(str(teil.get("shape", "box")), teil, scale)
|
|
if teil_mesh == null:
|
|
continue
|
|
var off: Array = teil.get("pos", [0.0, 0.0, 0.0])
|
|
var rot: Array = teil.get("rot_deg", [0.0, 0.0, 0.0])
|
|
var xf := Transform3D(
|
|
Basis.from_euler(Vector3(deg_to_rad(float(rot[0])), deg_to_rad(float(rot[1])), deg_to_rad(float(rot[2])))),
|
|
Vector3(float(off[0]), float(off[1]), float(off[2])) * scale)
|
|
# PrimitiveMesh → Roht_arrays (MeshDataTool bräuchte ArrayMesh).
|
|
var arrays: Array = teil_mesh.surface_get_arrays(0)
|
|
var verts: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX]
|
|
var norms: PackedVector3Array = arrays[Mesh.ARRAY_NORMAL]
|
|
var idx: PackedInt32Array = arrays[Mesh.ARRAY_INDEX]
|
|
for ii in idx:
|
|
st.set_normal(xf.basis * norms[ii])
|
|
st.add_vertex(xf * verts[ii])
|
|
return st.commit()
|
|
_:
|
|
var b := BoxMesh.new()
|
|
var size: Array = col.get("size", [1.0, 1.0, 1.0])
|
|
b.size = Vector3(maxf(0.01, float(size[0])), maxf(0.01, float(size[1])),
|
|
maxf(0.01, float(size[2]))) * scale
|
|
return b
|
|
|
|
|
|
## Halbe Höhe des Meshes über dem Fußpunkt (Mesh-Ursprung = Objekt-Fuß).
|
|
## Compound-ArrayMeshes sind bereits in Fußpunkt-Koordinaten gebacken → 0.
|
|
static func half_height(mesh: Mesh) -> float:
|
|
if mesh is BoxMesh:
|
|
return (mesh as BoxMesh).size.y * 0.5
|
|
if mesh is CylinderMesh:
|
|
return (mesh as CylinderMesh).height * 0.5
|
|
if mesh is SphereMesh:
|
|
return (mesh as SphereMesh).radius
|
|
return 0.0
|
|
|
|
|
|
## Höhe des Mesh-MITTELPUNKTS über dem Fußpunkt — für das Drehzentrum der
|
|
## Objekt-Auswahl. Bei Grundformen = half_height (Mesh ist zentriert); beim
|
|
## Compound-ArrayMesh = AABB-Mitte (schon in Fußpunkt-Koordinaten gebacken).
|
|
static func center_height(mesh: Mesh) -> float:
|
|
var aabb := mesh.get_aabb()
|
|
return half_height(mesh) + aabb.position.y + aabb.size.y * 0.5 |