Files
stylized-rock-generator/README.md
T
D4rkst3randClaude Opus 4.8 71b9982441 Panels aufgeraeumt (alle vier Addons) + Test-Suite instandgesetzt
Panel-Umbau
-----------
Jedes Addon hatte seine Einstellungen als Kette dauerhaft offener Boxen - beim
Rock Generator 15 Stueck, man musste am ganzen Panel vorbeiscrollen, um an den
Erzeugen-Knopf zu kommen. Jetzt steht oben nur, was man bei jedem Asset anfasst
(Vorlage, Name, grosser Erzeugen-Knopf, Zufall), der Rest liegt in nummerierten,
eingeklappten Unter-Panels in der Reihenfolge des Arbeitsablaufs:

  Rock Gen    9 Unter-Panels (Benennung ... Textur-Bake)
  Tree Gen    4 (Wachstum, Blattwerk, Zusatz-Ebenen, Export)
  Grass Gen   4 (Form, Haltung, Batch, Kanal-Belegung)
  Asset Utils 5 (war schon so, dient als Vorlage)

Schalter, die ein ganzes Unter-Panel steuern (Vertex-Farben backen,
Wachstums-Stufen), sitzen jetzt als Haekchen im Panel-Kopf - Zustand sichtbar
ohne aufzuklappen.

Gemessen statt geglaubt: test_panels.py registriert alle vier Addons, laesst
jedes Panel zeichnen, prueft Tab-Zugehoerigkeit und Auf-/Zugeklappt-Absicht,
dass jeder angebotene Operator existiert - und vor allem, dass JEDE definierte
Property irgendwo erreichbar bleibt (Zweige wie base_shape='BOX' werden dabei
durchgeschaltet). Ergebnis: 0 von 122 Properties beim Umbau verloren.

Test-Suite
----------
Beim Gegenpruefen kam heraus, dass fuenf Tests still veraltet waren und eine
echte Regression nicht gemeldet haetten:

  test_rock_gen      suchte Objekte mit Praefix "Rock_" - die heissen seit dem
                     Benennungssystem "Fels_A_M_01". Fand 0 von 3.
  test_base_shapes   loeschte am Schleifenanfang ALLE Objekte, auch die eigenen
                     Kopien aus vorigen Durchlaeufen -> StructRNA removed.
  test_ecogame_tree  erwartete eine feste Namensliste ohne die spaeter
                     dazugekommenen _Leaf-Objekte.
  test_tree_addon    dito, ausserdem Zip-Version 1.22.0 fest verdrahtet.
  test_leaves        Zip-Version 1.23.0 fest verdrahtet.

Drei Muster vermieden statt nur die Symptome geflickt: neue Objekte ueber die
Differenz vorher/nachher finden statt ueber ein Namens-Praefix, Zips per glob
statt mit fester Version, Zusatz-Ebenen beim Zaehlen herausfiltern.

Neu: run_tests.ps1 faehrt die komplette Suite mit einem Befehl (-Filter fuer
einzelne). Genau das fehlte - deshalb war das Veralten nicht aufgefallen.
13 von 13 Tests bestehen.

Nebenbei
--------
build.ps1 ruft server-generate jetzt mit --html auf (browsbare index.html fuer
statisches Hosting). Nachgemessen und im README festgehalten: zwei Zips
derselben Extension-ID sind KEIN Versions-Archiv - server-generate warnt
("archive found with duplicates ... conflicting blender versions"), mehrere
Versionen einer ID gehen nur bei verschiedenen Blender-Bereichen.

Versionen: Rock 2.13.0, Tree 1.24.0, Grass 1.1.0, Asset Utils 1.1.0

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-31 12:12:38 +02:00

418 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Stylized Asset Generators (Blender Addons)
> Dieses Repo liefert **zwei** Extensions ueber **eine** URL:
>
> | Addon | Panel | Zweck |
> |---|---|---|
> | **Stylized Rock Generator** | *Rock Gen* | Batch-Rocks + Textur-Bake + FBX/LOD-Export |
> | **Stylized Tree Generator** | *Tree Gen* | Baeume/Palmen/Bueschen/Kakteen (Geometry Nodes) mit Wachstums-Stufen |
> | **Stylized Grass Generator** | *Grass Gen* | Gras/Blumen/Farne mit Wind-UV fuer `SimpleGrassWind` |
> | **Stylized Asset Utils** | *Asset Utils* | **Handgebaute** Assets auf die Konvention bringen |
>
> Ein Blender-Extension-Repo kann mehrere Extensions ausliefern — `server-generate`
> listet jedes Zip in `dist/`. Ein „Check for Updates" aktualisiert also beide.
---
## Panel-Aufbau (alle vier Addons)
Jedes Addon hat oben im N-Panel nur das, was man **bei jedem Asset** anfasst -
Vorlage, Name, den grossen Erzeugen-Knopf, Zufall. Alles andere sitzt in
nummerierten, eingeklappten Unter-Panels in der Reihenfolge des Arbeitsablaufs:
| Addon | Unter-Panels |
|---|---|
| Rock Gen | 1 Benennung & Groesse · 2 Form · 3 Detail & Cleanup · 4 Shading · 5 Vertex-Farben · 6 UV/LOD/Material · 7 Batch & Transform · 8 FBX-Export · 9 Textur-Bake |
| Tree Gen | 1 Wachstum · 2 Blattwerk · 3 Zusatz-Ebenen · 4 Export |
| Grass Gen | 1 Form · 2 Haltung · 3 Batch & Budget · 4 Kanal-Belegung |
| Asset Utils | 1 Vorbereiten · 2 Vertex-Farben · 3 Benennung · 4 Pruefen · 5 Export |
Schalter, die ein ganzes Unter-Panel an- und ausknipsen (Vertex-Farben backen,
Wachstums-Stufen), sitzen als Haekchen **im Panel-Kopf** - so sieht man den
Zustand, ohne aufzuklappen.
In den Generatoren sind alle Unter-Panels zugeklappt, weil es Einstellungen sind,
die man selten aendert. In Asset Utils ist jedes Unter-Panel ein Arbeitsschritt;
1, 2 und 4 macht man bei jedem Asset und bleiben deshalb offen. `tests/test_panels.py`
haelt diese Absicht fest, damit sie nicht versehentlich verrutscht.
**Falle beim Bauen:** Unter-Panels brauchen dieselbe `bl_category` wie das
Eltern-Panel - sonst landen sie in einem anderen Tab und sind praktisch
unsichtbar. Und das Eltern-Panel muss **vor** seinen Kindern registriert werden,
sonst findet Blender die `bl_parent_id` nicht.
---
## EcoGame-Konventionen (beide Addons)
Abgestimmt auf `A:\eco\EcoGame` (UE 5.8). Kurzfassung:
**Benennung**
- Felsen: `Fels_<Form>_<Klasse>` — Form = Buchstabe (A, B, …), Klasse = `S/M/L/XL`.
Die Klasse setzt eine **Basis-Groesse** (S=0.45, M=1.0, L=1.9, XL=3.2) und ist
damit ein bewusst anderes Asset — gemessen: S 0.85 m, M 1.9 m, L 3.5 m, XL 6.0 m.
Davon strikt getrennt ist die **Zufalls-Streuung** (`scale_min/max`): die variiert
nur die FORM und wird von „Gesamtgroesse konstant halten" auf konstantes Volumen
normiert (1.11× statt 1.92×), damit sie sich nicht mit den ScaleMin/Max der Engine
multipliziert.
- Baeume: `<Art>_<Variante>_S<Stufe>`, z. B. `Birke_01_S1``Birke_01_S4`.
Zusatz-Ebenen haengen hinten an: `_Leaf`, `_Frucht`.
- Texturen: `<name>_albedo` und `<name>_normal` — mehr nicht.
**Textur-Bake**
- Nur **Albedo + Normal**. Roughness ist im Master-Material eine **Zahl**,
keine Textur.
- **AO/Cavity wird ins Albedo multipliziert** (Option „AO ins Albedo einrechnen",
Staerke regelbar). Dunkle Ritzen und helle Platten stecken im Farbbild — das
traegt den Stylized-Look. Separate Rough-/AO-Paesse sind Default **aus**.
- **Plattenstil (Genshin-Referenz):** Die Textur besteht aus **wenigen grossen,
fast leeren Platten** mit **schmalen dunklen Fugen** — das Detail steckt in der
Fuge, nicht in der Flaeche. Regler: `Plattengroesse` (kleiner = groessere
Platten), `Fugenbreite` (klein = harte Fuge, flache Platte), `Platten flach`
(1 = Flaeche nahezu einfarbig). Ein dichtes Zellmuster ueber die ganze
Oberflaeche erschlaegt die Form und passt NICHT zu diesem Look.
- **Vertex-Farb-Layout v2 (kanal-getrennt), Attribut `Cavity`:**
| Kanal | Inhalt |
|---|---|
| **R** | Cavity pur — 1 = offene Flaeche, 0 = tiefe Fuge (**ohne** Hoehen-Verlauf) |
| **G** | Moos-/Schmutzmaske — 1 = hier waechst was (Oberseite + Fugennaehe + Noise) |
| **B** | Hoehen-Verlauf — 0 = Unterseite, 1 = oben |
| **A** | 1 (Reserve) |
Getrennt statt eingebacken: der erdige **Boden-Saum** am Stein-Fuss und die
**Moosfarbe** werden pro Material-Instanz geregelt, nicht im Mesh festgelegt.
Regler: `Cavity-Staerke`, `Weichzeichnen`, `Moos-Menge`, `Moos-Aufbruch`,
`Moos-Seed`.
**G alternativ als Teil-Maske:** Haken `G = Teil-Maske (Shells)` markiert
getrennte Mesh-Teile statt Moos — der oberste/breiteste Teil bekommt 1, der
Rest 0. Assets, die aus mehreren Objekten mit **Strg+J** verbunden wurden
(Pilz = Hut + Stiel), lassen sich damit EXAKT trennen statt ueber Normalen
geschaetzt. Ein Material toent dann beide Teile unterschiedlich — **kein
zweiter Material-Slot noetig**. Einteilige Meshes bleiben unmarkiert (G=0).
**Der Button `Vertex-Cavity auf Auswahl` backt das Layout auf JEDES markierte
Mesh** — also auch auf handgebaute Assets, nicht nur auf generierte Steine.
Verifiziert: R ohne Hoehenabhaengigkeit (unten 0.62 / oben 0.59), B steigt
sauber (0.08 -> 0.92), G oben staerker (0.20 -> 0.57), A konstant 1,
Seed aendert die Verteilung, und alle Kanaele ueberleben den FBX-Roundtrip.
- **Texturfreie Alternative — Vertex-Cavity:** Statt zu baken laesst sich die
Cavity in die **Vertex-Farben** schreiben (Attribut `Cavity`, ueberlebt den
FBX-Roundtrip verlustfrei — gemessen 0.250..0.947 vor und nach dem Import).
Das Material braucht dann **keine Textur**:
`Base Color = Farbton x VertexColor`, dazu Kantenlicht (Fresnel) und
Schattentint. Vorteil gegenueber Triplanar: die dunklen Fugen sitzen auf den
ECHTEN Kanten des Meshes statt darueberzuschweben. Grenze: die Aufloesung
haengt an der Vertex-Dichte — fuer flaechige Platten reicht sie, fuer feine
Struktur nicht.
- 1024 px reicht fuer stylized Formen.
- Tileable (4D-Torus) passt zum triplanar mappenden Master-Material.
**Mesh**
- Echte Meter, Ursprung unten-mittig, FBX pro Asset bzw. LOD-Gruppe.
- **UV1 Lightmap ist Default aus** — EcoGame beleuchtet dynamisch.
- Meshes brauchen **keine eigenen Materialien**; in UE bekommt alles die
Material-Instanz vom Master. Ein Platzhalter im FBX ist okay.
- Scale-Randomisierung nur fuer die **Form** (nicht-uniform) nutzen, nicht fuer
die Gesamtgroesse — sonst multipliziert sie sich mit ScaleMin/Max der Engine.
---
## Stylized Asset Utils
Fuer alles, was **von Hand** gebaut wird (Pilze, Baumstumpf, Erzfels, Props) und
deshalb nicht durch einen Generator laeuft. Panel in fuenf einklappbaren Schritten:
1. **Vorbereiten** — Rotation/Scale anwenden, Ursprung unten-mittig, optional
Normalen neu berechnen. Damit steht das Asset in UE bei Scale 1 auf dem Boden.
2. **Vertex-Farben** — dasselbe Layout wie beim Rock Generator
(`R` = Cavity, `G` = Maske, `B` = Hoehe, `A` = 1).
`G` wahlweise als **Teil-Maske**: bei Assets, die mit **Strg+J** aus mehreren
Objekten verbunden wurden (Pilz = Hut + Stiel), bekommt der oberste Teil 1 und
der Rest 0 — exakt statt geschaetzt. **Ein** Material toent damit beide Teile,
ein zweiter Material-Slot entfaellt. Einteilige Meshes bleiben unmarkiert.
3. **Benennung** — Auswahl nach `<Basis>_<NN>` umbenennen, Mesh-Daten ziehen mit.
4. **Pruefen** — Asset-Check: nicht angewendete Transforms, Ursprung nicht unten,
Tri-Budget, fehlende UV/Vertex-Farben, lose Vertices, Custom Split Normals.
Details in der System-Konsole.
5. **Export** — FBX je Mesh mit `colors_type='SRGB'`, Triangulate und waehlbarem
Smoothing.
**Falle, die dabei auffiel:** `bpy.ops.paint.vertex_color_set` braucht den
Vertex-Paint-Modus und scheitert im Object-Mode mit „poll() failed". Steht es mit
`vertex_color_dirt` im selben `try`, wird der ganze Bake stillschweigend
uebersprungen. Die Initialisierung laeuft deshalb direkt ueber Python.
---
## Stylized Grass Generator
Gras-, Blumen- und Farn-Bueschel. Der Kern ist die **Wind-UV**:
- **UV0.V = 0 an der Wurzel, 1 an der Spitze** — genau die Maske, die UEs
`SimpleGrassWind` als *WindWeight* braucht. Ohne sie rutscht der Halm beim
Wehen vom Boden weg oder bewegt sich gar nicht.
Am Zielmaterial `M_Grass` geprueft: es liest `TextureCoordinate`, aber **keine**
`VertexColor` — deshalb liefert der Generator die Maske PRIMAER ueber die UV.
Die Assets funktionieren damit **ohne Materialaenderung**.
- **Vertex-Farben zusaetzlich** (nicht als einzige Quelle):
`R` = Kopie der Windmaske · `G` = Variation je HALM · `B` = AO (unten dunkel).
`G` ist die Ebene **unter** dem Biom-Tint, den die Engine ueber
`PerInstanceCustomData 0/1` pro Instanz setzt — im Material **multiplizieren**,
nicht ersetzen.
- Presets: `gras`, `gras_hoch`, `steppengras`, `farn`, `blume`.
- Tri-Budget-Warnung (Default 400 laut `ASSETS.md` fuer Gras/Blume).
Gemessen: ein Standard-Bueschel mit 14 Halmen = **112 Tris**.
- Ursprung unten-mittig (`min_z = 0.0000`), UV und Vertex-Farben ueberleben den
FBX-Roundtrip.
---
## Stylized Tree Generator
Parametrischer **Geaest**-Generator (Stamm + Aeste + Sub-Aeste) als Geometry-Nodes-
Gruppe. Blaetter/Krone macht man selbst — das Addon liefert die Struktur.
- **Presets:** `baum`, `palme`, `busch`, `tanne`, `eiche`, `birke`, `weide`,
`toter_baum`, `kaktus` — die Silhouette steuert die **Kronenform**
(`Crown Taper` / `Crown Bulge`, Idee aus modular_tree / Weber-Penn).
- **Tri-Budget:** alle Presets liegen unter dem Projektlimit (Baum ≤2500,
Busch ≤800). Stellschrauben: `Detail` (Segmente) und `Sides` / `Branch Sides` /
`Sub Sides` (Profilaufloesung je Ebene — Stamm rund, Zweige nur Dreiecke).
- **Wachstums-Stufen:** Haken setzen + Anzahl waehlen → Setzling bis ausgewachsen,
**gleicher Seed = dieselbe Baum-Identitaet** (fuer Wachstums-Systeme im Spiel).
- **Live bearbeiten:** Nach dem Erzeugen liegen alle Regler am Modifier `GN_Tree`
im Viewport ziehen, der Baum aktualisiert sich sofort.
- **Blattwerk:** Ein Blatt-/Billboard-Objekt als *Blatt-Card* waehlen (z. B. eine
Diamond-Plane mit Alpha-Textur) → der Button streut es auf die **Zweige** und legt
das Ergebnis als `<Name>_Leaf` mit identischem Ursprung ab. Gefiltert wird ueber
den Astradius (`Max Radius`), damit keine Blaetter am Stamm kleben. Ohne Card
bleibt `_Leaf` leer zum Selbermodellieren.
- **Tri-Budget:** Der Blattwerk-Button meldet `Stamm + Blatt = Summe` und **warnt**
bei Ueberschreitung. Default-Dichte 150 haelt auch das schwerste Preset (Eiche,
1428 Tris) mit 2460 von 2500 Tris im Budget.
- **Export:** „Modifier anwenden" friert das Ergebnis als normales Mesh ein.
**v1.1.0:** Aeste folgen jetzt der Elternkurve (Tangent/Normal aus *Curve to
Points*, Goldener Winkel = Phyllotaxis) statt einer aus Weltkoordinaten
gerechneten Richtung — Sub-Aeste standen vorher wie Nadeln quer zum Ast.
Neu: `Tip Blunt` (Baum laeuft spitz aus, Kaktus endet stumpf).
Der `kaktus` nutzt `Droop Curve` fuer den Saguaro-Ellbogen (Arm laeuft flach raus
und knickt dann steil hoch). Offen ist dort nur, dass die Rippen (`Ribs`) im
Shading kaum sichtbar sind.
---
# Stylized Rock Generator (Blender Addon)
Batch-Generator fuer stylized Rocks in Blender, ausgelegt auf den Export nach
**Unreal Engine 5** ueber FBX.
Pipeline pro Rock: Basis-Mesh (Cube / Ico-Sphere) → Subdivision Surface →
Displace mit prozeduraler Noise-Textur (Voronoi / Clouds / Distorted Noise) →
zweistufiges Decimate → Limited Dissolve → Normalen-Reparatur →
UV0 (Smart Project) + UV1 (Lightmap) → optional Bevel/Weighted Normal, Material,
LOD-Set, randomisierte Skalierung/Rotation.
Getestet mit **Blender 5.1.2** (Minimum laut `bl_info`: 4.2).
---
## Installation
1. `stylized_rock_generator.py` herunterladen.
2. Blender → *Edit → Preferences → Add-ons → Install…* → die `.py` waehlen.
3. Haken bei **Stylized Rock Generator** setzen.
4. Panel: *View3D → Sidebar (N) → Reiter "Rock Gen"*.
Update: einfach die neue `.py` erneut installieren (gleicher Modulname) oder das
Repo lokal ziehen und den Addon-Pfad in den Preferences auf den Repo-Ordner
zeigen lassen.
## Installation als Extension (via URL, mit Auto-Update)
> **Ein Repo, vier Extensions.** `blender --command extension server-generate`
> scannt `dist/` und listet jedes gefundene Zip - eine URL liefert in Blender
> alle vier Addons. `build.ps1` ruft das mit `--html` auf, dann entsteht
> zusaetzlich eine `index.html` zum Durchblaettern (auf einem statischen
> Webserver; Gitea liefert rohes HTML als Text aus).
>
> **Kein Versions-Archiv:** zwei Zips derselben Extension-ID nebeneinander sind
> nicht der Weg, eine alte Version zum Zurueckrollen bereitzuhalten -
> server-generate warnt dann (`archive found with duplicates ... conflicting
> blender versions`). Mehrere Versionen einer ID sind nur erlaubt, wenn sie
> verschiedene Blender-Bereiche abdecken. Alte Version wieder her: per
> `git checkout` des Zips.
Ab Blender 4.2 / 5.x als **Remote-Extension-Repository** einmal die URL
eintragen, danach gehen Updates per Klick.
1. Das Gitea-Repo muss **oeffentlich** sein (sonst kann Blender die Raw-URL nicht
ohne Login laden).
2. Blender → *Edit → Preferences → Get Extensions → oben rechts das Dropdown →
Repositories → „Add Remote Repository"*.
3. Als URL eintragen:
```
https://git.d4rkst3r.de/D4rkst3r/stylized-rock-generator/raw/branch/main/dist/index.json
```
4. „Check for Updates" aktivieren, bestaetigen. Die Extension erscheint in der
Liste → *Install*.
**Updaten (Maintainer-Seite):**
1. `version` in `blender_manifest.toml` (und `bl_info["version"]` in der `.py`) erhoehen.
2. `./build.ps1` (oder `blender --command extension build …` + `… server-generate …`).
3. `git add -A && git commit -m "vX.Y.Z" && git push`.
4. Nutzer: in Blender *Check for Updates* → *Update*.
Das gebaute Zip + `index.json` liegen in `dist/` und werden bewusst mitversioniert,
damit die Raw-URL sie ausliefert.
---
## Features
- **Basis-Formen:** Wuerfel, **Quader** oder Ico-Sphere, einstellbare Subsurf-Stufen.
Der Quader hat frei einstellbare `Quader-Proportionen` (X/Y/Z) plus
`Proportions-Streuung` — damit entstehen flache **Platten** und langgestreckte
**Riegel** statt nur runder Brocken. Gemessen bei Proportion (2.0, 1.5, 0.35):
3.43 / 2.56 / 0.79 m — Z also 23 % von X.
Die Proportionen werden auf **gleiches Volumen normiert** — „M" bedeutet damit
bei jeder Form dasselbe. Eine Platte ist dann flacher UND breiter als ein
Wuerfel derselben Klasse, aber nicht schwerer; ihre laengste Kante ist
entsprechend groesser (das ist Absicht, kein Fehler).
Die Proportionen werden auf die MESH-DATEN angewendet, nicht auf das Objekt:
der Displace nutzt globale Texturkoordinaten, eine Objekt-Skalierung wuerde
das Rauschen mitverzerren statt die Form zu aendern.
- **Noise/Displace:** Voronoi / Clouds / Distorted Noise, Scale + Staerke, Seed.
Seed-Variation ueber einen moderaten Welt-Offset (±25) vor dem Displace-Apply
Blenders prozedurale Texturen haben keinen echten Seed-Parameter.
- **Cleanup:** zweistufiges Decimate + Limited Dissolve, danach
`normals_make_consistent` (verhindert eingefallene/dunkle Flaechen).
- **UV fuer Unreal:** UV0 (Smart Project) fuer Material-Texturen, UV1 (Lightmap)
als separater, nicht ueberlappender Kanal fuer Static-Mesh-Lightmaps.
- **Hard-Surface (optional):** Bevel + Weighted Normal fuer einen "clean"
Kantenlook.
- **Material (Platzhalter):** weist pro Rock ein Material zu und rotiert durch
eine kleine Palette (Granite/Basalt/Sandstone/Mossy), damit nichts materiallos
exportiert wird.
- **LOD-Set:** erzeugt LOD0LODn als LOD-Gruppe fuer Unreal (siehe unten).
- **Presets:** eingebaute Presets (Kleiner Kiesel, Grosser Bruchfels, Lava-Rock)
plus eigene Presets als JSON (`rock_presets.json` neben dem Addon).
- **Batch:** synchron fuer kleine Mengen, ab `modal_threshold` (Default 25)
automatisch **modal mit Progress-Bar** die UI bleibt bedienbar, ESC bricht ab.
- **Robustheit:** Faellt ein einzelner Rock aus (z. B. 0 Faces nach zu
aggressivem Decimate), wird nur dieser uebersprungen und aufgeraeumt, der Rest
des Batches bleibt erhalten. Fehler landen in der System-Konsole.
---
## LOD-Export nach Unreal
Blenders FBX-Exporter kennt **keine** eigene LOD-Option. Unreal erkennt eine
LOD-Gruppe aber, wenn das Eltern-**Empty** die Custom Property
`fbx_type = "LodGroup"` traegt und die LOD-Meshes in Reihenfolge (LOD0…LODn)
darunter geparentet sind. Genau das erzeugt der Generator.
Beim FBX-Export darauf achten:
- **Custom Properties** aktivieren (sonst geht `fbx_type` verloren).
- Nur die LOD-Gruppe (Empty + Kinder) selektieren, `use_selection` an.
Beim Import in Unreal:
- Im FBX-Import-Dialog **Import LODs** aktivieren.
Referenz:
[Importing Static Mesh LODs Using FBX (UE Docs)](https://dev.epicgames.com/documentation/en-us/unreal-engine/importing-static-mesh-lods-using-fbx-in-unreal-engine).
---
## Tests
Alles auf einmal:
```bash
pwsh -File run_tests.ps1
```
Einzeln geht weiterhin `blender --background --python tests/<datei>.py`,
gefiltert `.\run_tests.ps1 -Filter panels`.
> **Warum der Runner existiert.** Die Tests lagen einzeln herum und wurden nur
> beim Bauen des jeweiligen Features gestartet. Beim Aufraeumen der Panels stellte
> sich heraus, dass **fuenf davon still veraltet waren**: sie prueften gegen
> Objekt-Namen aus der Zeit vor dem Benennungssystem (`Rock_` statt `Fels_A_M_01`),
> gegen fest verdrahtete Zip-Versionen, oder zaehlten die spaeter dazugekommenen
> `_Leaf`-Objekte mit. Sie haetten eine echte Regression nicht gemeldet. Die drei
> Muster, die das ausgeloest haben, sind jetzt vermieden: Objekte ueber die
> **Differenz** vorher/nachher finden statt ueber ein Namens-Praefix, Zips per
> **glob** statt mit fester Version, und Zusatz-Ebenen beim Zaehlen herausfiltern.
| Datei | prueft |
|---|---|
| `tests/test_panels.py` | Alle vier Addons: registrieren, jedes Panel zeichnet, Unter-Panels im richtigen Tab, **jede Property irgendwo erreichbar** |
| `tests/test_tree_addon.py` | Tree-Addon: Presets, Wachstums-Stufen, UVs, Modifier-Apply |
| `tests/test_ecogame_rock.py` | Fels-Benennung, Bake liefert nur `_albedo`/`_normal`, AO im Albedo |
| `tests/test_ecogame_tree.py` | Baum-Benennung `<Art>_<Variante>_S<Stufe>`, Ursprung, Lightmap-Default |
| `tests/test_randomize.py` | Zufalls-Funktion beider Addons, `_Leaf`/`_Frucht` mit gleichem Ursprung |
| `tests/test_leaves.py` | Blattwerk: Streuung, Ursprung, Tri-Budget, leeres `_Leaf` ohne Card |
| `tests/test_rock_classes.py` | Groessen-Klassen S/M/L/XL unterscheiden sich, Streuung bleibt formbezogen |
Die Suiten erwarten die gebauten Zips in `dist/` bzw. lesen die `.py` direkt.
Erzeugt 3 Rocks, prueft je Rock: keine losen Vertices, manifold, Normalen nach
aussen (signed volume > 0), UV0 + UV1 vorhanden, und gibt eine Zusammenfassung
(Vertex-/Face-Count) aus. Exit-Code 0 = alles bestanden, 1 = Fehler.
---
## Changelog v2.3.0
- **Textur-Bake auf Zell-Look umgestellt:** Voronoi „Distance to Edge" liefert
hand-painted Zellen mit dunklen Outlines statt organischem fBm-Rauschen.
Neu regelbar: **Grundfarbe** und **Zellgroesse**. Weiterhin nahtlos tileable.
## Changelog v2.2.0
- **Textur-Bake-Button:** backt ein nahtlos **tileables** Stylized-Rock-Set
(BaseColor / Normal / Roughness / AO) als PNG. Nahtlos durch **4D-Torus-
Projektion** der UV (`cos/sin` von U und V) → tilet in beide Richtungen ohne
Naht, ideal fuer **Triplanar** in Unreal.
- Anleitung zur UE-Material-Verdrahtung: siehe
[docs/UE_Material_Triplanar.md](docs/UE_Material_Triplanar.md).
## Changelog v2.1.0
- **FBX-Export-Button (UE-ready):** exportiert jeden Rock / jede LOD-Gruppe als
eigene FBX mit `use_custom_props=True` (fuer `fbx_type=LodGroup`), Triangulate
und passendem Smoothing (`EDGE` bei Auto-Smooth, sonst `FACE`).
*UE-Import-Tipp:* Normal Import Method auf „Import Normals" stellen, „Import LODs" an.
- **Shading-Toggle:** Faceted / Smooth / Auto-Smooth-nach-Winkel.
- **Pro-Rock-Varianz:** Noise-Scale & Displace-Staerke zwischen Min/Max plus
optionale nicht-uniforme Skalierung → ein Batch wirkt variantenreich.
- **Boden abflachen (Bisect) + Origin unten-mittig** → Rocks stehen in UE plan auf.
## Changelog v2.0.0
Gehaertet & erweitert gegenueber v1.0.0:
- **Bugfix:** `smart_project(angle_limit=…)` ist in Blender 4.x/5.x in **Radiant**
(`[0, 1.5708]`) der alte Wert `66.0` war ungueltig. Jetzt `radians(66°)`.
- **Bugfix:** `dissolve_limited(angle_limit=…)` erwartet **Radiant**; der
Grad-Wert wurde ungewandelt durchgereicht → uebermaessiges Dissolve. Jetzt
`radians(dissolve_angle)`.
- **Bugfix:** Der Lightmap-Kanal wurde vor `lightmap_pack` nicht als aktiver
UV-Layer gesetzt → UV0 konnte ueberschrieben werden. Jetzt explizit gesetzt,
UV0 danach wieder als Render-Kanal.
- Fehlerbehandlung um jeden `bpy.ops.*`-Aufruf; einzelne Rock-Fehler brechen den
Batch nicht mehr ab.
- Nicht-blockierender modaler Batch-Modus mit Progress-Bar.
- Bevel + Weighted Normal als Toggle.
- LOD-Set als `fbx_type=LodGroup`-Gruppe.
- Preset-System (JSON) mit Panel-Dropdown.
- Platzhalter-Material-Zuweisung.