M2-Karte t_d8be9ed7: Struktur-Objekte im Editor. Bibliothek +15 Objekte: bruecke_holz (Bruecke Holz), hoehleneingang, felsbogen — deutsche Namen, Gruppe 'bauwerke', Thumbnails gerendert. mesh_shapes: SHAPE_COMPOUND-Platzhalter — EIN Mesh aus allen Verbund-Teilen, in Fusspunkt-Koordinaten gebacken (pos-Versatz + rot_deg), so sind Rampe/Durchgaenge im Editor sichtbar wie die echte Kollision; neu center_height() fuer das Drehzentrum der Auswahl (map_view_builder nutzt es). test_library: 15 Objekte, Undo/Redo-Zaehler dynamisiert -> Suite 334 Checks gruen.
This commit is contained in:
parent
cddebb8c26
commit
5dd37e32d7
8 changed files with 50 additions and 6 deletions
|
|
@ -13,6 +13,9 @@
|
||||||
"boulder_small": { "poly": 96, "collision": { "shape": "sphere", "radius": 0.8 } },
|
"boulder_small": { "poly": 96, "collision": { "shape": "sphere", "radius": 0.8 } },
|
||||||
"tree_pine": { "poly": 180, "collision": { "shape": "cylinder", "radius": 0.4, "height": 12.0 } },
|
"tree_pine": { "poly": 180, "collision": { "shape": "cylinder", "radius": 0.4, "height": 12.0 } },
|
||||||
"bush_small": { "poly": 64, "collision": { "shape": "sphere", "radius": 0.8 } },
|
"bush_small": { "poly": 64, "collision": { "shape": "sphere", "radius": 0.8 } },
|
||||||
"fass": { "poly": 128, "collision": { "shape": "cylinder", "radius": 0.31, "height": 0.88 } }
|
"fass": { "poly": 128, "collision": { "shape": "cylinder", "radius": 0.31, "height": 0.88 } },
|
||||||
|
"bruecke_holz": { "poly": 228, "collision": { "shape": "compound", "parts": [{ "shape": "box", "pos": [0, 0.85, 0], "size": [2, 0.3, 3] }, { "shape": "box", "pos": [0, 0.361, -2.806], "size": [2, 0.3, 2.89], "rot_deg": [21.8, 0, 0] }, { "shape": "box", "pos": [0, 0.361, 2.806], "size": [2, 0.3, 2.89], "rot_deg": [-21.8, 0, 0] }, { "shape": "box", "pos": [-0.93, 1.5, 0], "size": [0.14, 0.12, 3] }, { "shape": "box", "pos": [-0.93, 0.964, -2.564], "size": [0.14, 0.12, 2.69], "rot_deg": [21.8, 0, 0] }, { "shape": "box", "pos": [-0.93, 0.964, 2.564], "size": [0.14, 0.12, 2.69], "rot_deg": [-21.8, 0, 0] }, { "shape": "box", "pos": [0.93, 1.5, 0], "size": [0.14, 0.12, 3] }, { "shape": "box", "pos": [0.93, 0.964, -2.564], "size": [0.14, 0.12, 2.69], "rot_deg": [21.8, 0, 0] }, { "shape": "box", "pos": [0.93, 0.964, 2.564], "size": [0.14, 0.12, 2.69], "rot_deg": [-21.8, 0, 0] }] } },
|
||||||
|
"hoehleneingang": { "poly": 204, "collision": { "shape": "compound", "parts": [{ "shape": "box", "pos": [-1.8, 1.6, 0], "size": [1.4, 3.2, 4] }, { "shape": "box", "pos": [1.8, 1.6, 0], "size": [1.4, 3.2, 4] }, { "shape": "box", "pos": [0, 2.8, 0], "size": [5, 0.8, 4] }, { "shape": "box", "pos": [0, 1.2, 1.8], "size": [2.2, 2.4, 0.4] }] } },
|
||||||
|
"felsbogen": { "poly": 156, "collision": { "shape": "compound", "parts": [{ "shape": "box", "pos": [-1.9, 1.3, 0], "size": [1.2, 2.6, 2.4] }, { "shape": "box", "pos": [1.9, 1.3, 0], "size": [1.2, 2.6, 2.4] }, { "shape": "box", "pos": [0, 3.1, 0], "size": [5, 1, 2.4] }] } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
library/thumbs/bruecke_holz.png
Normal file
BIN
library/thumbs/bruecke_holz.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1 KiB |
BIN
library/thumbs/felsbogen.png
Normal file
BIN
library/thumbs/felsbogen.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 941 B |
BIN
library/thumbs/hoehleneingang.png
Normal file
BIN
library/thumbs/hoehleneingang.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1,001 B |
|
|
@ -22,6 +22,9 @@ const NAMES := {
|
||||||
"boulder_small": "Kleiner Felsen",
|
"boulder_small": "Kleiner Felsen",
|
||||||
"tree_pine": "Nadelbaum",
|
"tree_pine": "Nadelbaum",
|
||||||
"bush_small": "Strauch",
|
"bush_small": "Strauch",
|
||||||
|
"bruecke_holz": "Brücke (Holz)",
|
||||||
|
"hoehleneingang": "Höhleneingang",
|
||||||
|
"felsbogen": "Felsbogen",
|
||||||
}
|
}
|
||||||
|
|
||||||
## Gruppen für den Bibliothek-Browser (Anzeige-Reihenfolge = Reihenfolge hier).
|
## Gruppen für den Bibliothek-Browser (Anzeige-Reihenfolge = Reihenfolge hier).
|
||||||
|
|
@ -34,7 +37,8 @@ const GROUP_LABELS := {
|
||||||
}
|
}
|
||||||
const GROUP_IDS := {
|
const GROUP_IDS := {
|
||||||
"kisten": ["crate_wood", "crate_metal", "barrier_sandbag", "wall_concrete"],
|
"kisten": ["crate_wood", "crate_metal", "barrier_sandbag", "wall_concrete"],
|
||||||
"bauwerke": ["bunker_concrete", "watchtower_wood", "fence_metal"],
|
"bauwerke": ["bunker_concrete", "watchtower_wood", "fence_metal",
|
||||||
|
"bruecke_holz", "hoehleneingang", "felsbogen"],
|
||||||
"natur": ["tree_pine", "bush_small", "rock_large", "boulder_small"],
|
"natur": ["tree_pine", "bush_small", "rock_large", "boulder_small"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,7 @@ func _build_objects(objects: Array, manifest: Dictionary) -> void:
|
||||||
marker.add_child(mi)
|
marker.add_child(mi)
|
||||||
var half := (mesh.get_aabb().size * 0.5) as Vector3
|
var half := (mesh.get_aabb().size * 0.5) as Vector3
|
||||||
marker.set_meta("pick_radius", half.length() + maxf(0.6, half.y * 0.25))
|
marker.set_meta("pick_radius", half.length() + maxf(0.6, half.y * 0.25))
|
||||||
marker.set_meta("half_height", MeshShapes.half_height(mesh))
|
marker.set_meta("half_height", MeshShapes.center_height(mesh))
|
||||||
object_markers.append(marker)
|
object_markers.append(marker)
|
||||||
add_child(marker)
|
add_child(marker)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ class_name MeshShapes
|
||||||
const SHAPE_BOX := "box"
|
const SHAPE_BOX := "box"
|
||||||
const SHAPE_SPHERE := "sphere"
|
const SHAPE_SPHERE := "sphere"
|
||||||
const SHAPE_CYLINDER := "cylinder"
|
const SHAPE_CYLINDER := "cylinder"
|
||||||
|
const SHAPE_COMPOUND := "compound"
|
||||||
|
|
||||||
|
|
||||||
static func make_placeholder(shape: String, col: Dictionary, scale: float) -> Mesh:
|
static func make_placeholder(shape: String, col: Dictionary, scale: float) -> Mesh:
|
||||||
|
|
@ -23,6 +24,33 @@ static func make_placeholder(shape: String, col: Dictionary, scale: float) -> Me
|
||||||
c.bottom_radius = c.top_radius
|
c.bottom_radius = c.top_radius
|
||||||
c.height = maxf(0.01, float(col.get("height", 1.0)) * scale)
|
c.height = maxf(0.01, float(col.get("height", 1.0)) * scale)
|
||||||
return c
|
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 b := BoxMesh.new()
|
||||||
var size: Array = col.get("size", [1.0, 1.0, 1.0])
|
var size: Array = col.get("size", [1.0, 1.0, 1.0])
|
||||||
|
|
@ -32,6 +60,7 @@ static func make_placeholder(shape: String, col: Dictionary, scale: float) -> Me
|
||||||
|
|
||||||
|
|
||||||
## Halbe Höhe des Meshes über dem Fußpunkt (Mesh-Ursprung = Objekt-Fuß).
|
## 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:
|
static func half_height(mesh: Mesh) -> float:
|
||||||
if mesh is BoxMesh:
|
if mesh is BoxMesh:
|
||||||
return (mesh as BoxMesh).size.y * 0.5
|
return (mesh as BoxMesh).size.y * 0.5
|
||||||
|
|
@ -40,3 +69,11 @@ static func half_height(mesh: Mesh) -> float:
|
||||||
if mesh is SphereMesh:
|
if mesh is SphereMesh:
|
||||||
return (mesh as SphereMesh).radius
|
return (mesh as SphereMesh).radius
|
||||||
return 0.0
|
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
|
||||||
|
|
@ -52,7 +52,7 @@ func _test_manifest_load() -> void:
|
||||||
var manifest := _load_manifest()
|
var manifest := _load_manifest()
|
||||||
_check(not manifest.is_empty(), "Manifest lädt und ist ein Objekt")
|
_check(not manifest.is_empty(), "Manifest lädt und ist ein Objekt")
|
||||||
var lib: Dictionary = manifest.get("objects", {})
|
var lib: Dictionary = manifest.get("objects", {})
|
||||||
_check(lib.size() == 12, "Katalog hat die 12 etablierten Objekte (ist %d)" % lib.size())
|
_check(lib.size() == 15, "Katalog hat die 15 etablierten Objekte (ist %d)" % lib.size())
|
||||||
var all_ok := true
|
var all_ok := true
|
||||||
for oid in lib.keys():
|
for oid in lib.keys():
|
||||||
var entry: Dictionary = lib[oid]
|
var entry: Dictionary = lib[oid]
|
||||||
|
|
@ -254,7 +254,7 @@ func _test_crud_undo_all_objects() -> void:
|
||||||
all_ok = false
|
all_ok = false
|
||||||
if first:
|
if first:
|
||||||
first = false
|
first = false
|
||||||
_check(all_ok, "alle 11 Objekte: setzen/drehen/verschieben/löschen je mit Undo+Redo sauber")
|
_check(all_ok, "alle %d Objekte: setzen/drehen/verschieben/löschen je mit Undo+Redo sauber" % lib.size())
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------- svmap-Roundtrip ----
|
# -------------------------------------------------------- svmap-Roundtrip ----
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue