Tree Gen 1.28.0: Faell-Teile mit exakter Ast-Zuordnung (Punkt 4)

Knopf "Faell-Teile exportieren": Stamm in 2-3 Segmente, jeder Ast als eigenes
Teil SAMT seinen Sub-Aesten und Blatt-Cards, dazu <Name>_Pieces_All.fbx mit
gemeinsamem Ursprung. Je Teil zwei Material-Slots, Pivot am Massenmittelpunkt,
eigene UCX-Huelle.

Die Ast-Zugehoerigkeit ist ein ATTRIBUT, keine Schaetzung - wie von der
UE-Seite vorgeschlagen. Der Generator legt "ast_id" an den Astpunkten ab
(0 = Stamm, 1..N = Aeste). Sub-Aeste erben die ID ihres Astes automatisch, weil
sie aus Punkten auf dessen Kurve entstehen; nachgemessen: bei Sub Count 0 hat
jeder Ast 28 Verts, bei 3 dann 64, der Stamm bleibt konstant bei 156. Beim
Blatt-Streuen wird ast_id am Punkt abgegriffen und auf die Card gespeichert.

Damit ist die Zuordnung exakt nachzaehlbar, und genau das prueft der Test:
Summe der Cards ueber alle Teile == Cards des Stand-Baums. 170 = 170.

Die Falle, die diese Bilanz aufgedeckt hat: die Z-Schnitte am Stamm zerschnitten
anfangs auch Blatt-Cards. Aus einer wurden zwei, die Bilanz stand auf 178 gegen
170 - und die Atlas-UVs der Haelften waeren kaputt gewesen. Eine Card ist ein
unteilbares Billboard; sie wandert jetzt als GANZES in das Segment, in dem ihre
Inselmitte liegt. Nur die Rinde wird geschnitten, ihre Schlagflaechen werden
gedeckelt.

Massenmittelpunkt flaechengewichtet statt Vertex-Mittelwert: wo das Mesh dicht
tesselliert ist (Astansatz), wuerde der Mittelwert den Pivot dorthin ziehen und
das Teil torkelte beim Fallen.

Alles am REIMPORTIERTEN FBX gemessen: 12 Teile (3 Stamm, 9 Ast), UCX 18-32
Verts im Budget, Pivot maximal 16 mm vom Schwerpunkt, 0 non-manifold Kanten,
Gesamtdatei 5.41 m hoch - also volle Baumhoehe und deckungsgleich
rekonstruierbar.

Nebenbei: _deselect fiel ueber None-Eintraege in der Objektliste, die beim
Entfernen von Objekten zwischen den Teilen staendig auftreten.

Neu: tests/test_faell_teile.py. 21 Tests bestehen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 07:40:57 +02:00
co-authored by Claude Opus 4.8
parent 29899c0aa0
commit 95ddcb40b3
8 changed files with 679 additions and 12 deletions
+36
View File
@@ -385,6 +385,41 @@ Zwei Fallen beim Bauen:
- `join` loescht das zweite Objekt; jede gehaltene Referenz darauf ist danach
tot. Die Aufraeumliste fuehrt deshalb **Namen** statt Objekte.
### Faell-Teile (Punkt 4)
Kein Laufzeit-Zerlegen: beim Faellen tauscht das Spiel die ISM-Instanz gegen
einen Actor aus vordefinierten Teilen. Knopf **Faell-Teile exportieren**:
| Teil | Inhalt |
|---|---|
| `<Name>_Piece_Stamm01..03` | Stammsegmente, Schnittflaechen mit Deckel |
| `<Name>_Piece_Ast01..N` | je ein Ast **samt seinen Sub-Aesten und Blatt-Cards** |
| `<Name>_Pieces_All.fbx` | alle Teile an ihrer Originalposition, gemeinsamer Ursprung |
Je Teil: zwei Material-Slots, Pivot = **Massenmittelpunkt**, eigene `UCX_`-Huelle.
**Die Ast-Zugehoerigkeit ist ein Attribut, keine Schaetzung.** Der Generator legt
`ast_id` an den Astpunkten ab (0 = Stamm, 1..N = Aeste); Sub-Aeste erben die ID
ihres Astes automatisch, weil sie aus Punkten auf dessen Kurve entstehen —
gemessen: bei `Sub Count` 0 hat jeder Ast 28 Verts, bei 3 dann 64, der Stamm
bleibt bei 156. Beim Blatt-Streuen wird `ast_id` am Punkt abgegriffen und auf
die Card gespeichert. „Welche Card gehoert zu welchem Ast" ist damit eine
Nachschlage-Frage statt einer Nearest-Neighbor-Heuristik — und **exakt
nachzaehlbar**:
> `tests/test_faell_teile.py` prueft die **Blatt-Bilanz**: Summe der Cards ueber
> alle Teile == Cards des Stand-Baums. Gemessen **170 = 170**.
**Falle dabei:** Die Z-Schnitte am Stamm zerschnitten anfangs auch Blatt-Cards —
aus einer wurden zwei, die Bilanz stand auf 178 gegen 170, und die Atlas-UVs der
Haelften waeren kaputt gewesen. Eine Card ist ein **unteilbares** Billboard:
sie wandert jetzt als Ganzes in das Segment, in dem ihre Inselmitte liegt.
Nur die Rinde wird geschnitten.
Alles am **reimportierten** FBX gemessen: 12 Teile, UCX 1832 Verts im Budget,
Pivot maximal 16 mm vom Schwerpunkt, 0 non-manifold Kanten, Gesamtdatei 5.41 m
hoch (= volle Baumhoehe, die Teile sind also deckungsgleich rekonstruierbar).
### Proportionen: gemessen, nicht geschaetzt
Drei Presets sahen falsch aus. Die Ursachen wurden gemessen bzw. belegt, nicht
@@ -606,6 +641,7 @@ gefiltert `.\run_tests.ps1 -Filter panels`.
| Datei | prueft |
|---|---|
| `tests/test_baum_export.py` | Stand-Baum: ein Mesh, zwei Slots, Ursprung, **Kugel-Normalen im exportierten FBX** |
| `tests/test_faell_teile.py` | Faell-Teile: **Blatt-Bilanz 170=170**, Pivot am Massenmittelpunkt, UCX je Teil, Gesamtdatei mit gemeinsamem Ursprung |
| `tests/test_blatt_varianten.py` | Card-Sammlung: alle Varianten werden gezogen, seed-stabil, Einzelobjekt weiter moeglich |
| `tests/test_proportionen.py` | Baum-Proportionen: Palmenkrone, Strauch-Breite, Setzling-Schlankheit, Branch Count 0 |
| `tests/test_bruchstuecke.py` | Bruchstuecke: Volumen 100 % des Muttersteins, dichte Topologie, genau ein Erz-Stueck, Material-Slots, Ursprung, UCX je Teil |