From 71b99824415013b7a38b2e5b5e1bff83a5a760fd Mon Sep 17 00:00:00 2001 From: D4rkst3r Date: Fri, 31 Jul 2026 12:12:38 +0200 Subject: [PATCH] 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 --- README.md | 60 ++- blender_manifest.toml | 2 +- blender_manifest_grass.toml | 2 +- blender_manifest_tree.toml | 2 +- blender_manifest_utils.toml | 2 +- build.ps1 | 11 +- dist/index.html | 66 +++ dist/index.json | 32 +- ...0.0.zip => stylized_asset_utils-1.1.0.zip} | Bin 6340 -> 6351 bytes dist/stylized_grass_generator-1.0.0.zip | Bin 4752 -> 0 bytes dist/stylized_grass_generator-1.1.0.zip | Bin 0 -> 5027 bytes dist/stylized_rock_generator-2.12.0.zip | Bin 21754 -> 0 bytes dist/stylized_rock_generator-2.13.0.zip | Bin 0 -> 22263 bytes dist/stylized_tree_generator-1.23.0.zip | Bin 22135 -> 0 bytes dist/stylized_tree_generator-1.24.0.zip | Bin 0 -> 22352 bytes run_tests.ps1 | 49 +++ stylized_asset_utils.py | 2 +- stylized_grass_generator.py | 112 +++-- stylized_rock_generator.py | 407 +++++++++++------- stylized_tree_generator.py | 144 ++++--- tests/test_base_shapes.py | 18 +- tests/test_ecogame_tree.py | 10 +- tests/test_leaves.py | 11 +- tests/test_panels.py | 234 ++++++++++ tests/test_rock_gen.py | 6 +- tests/test_tree_addon.py | 16 +- 26 files changed, 902 insertions(+), 284 deletions(-) create mode 100644 dist/index.html rename dist/{stylized_asset_utils-1.0.0.zip => stylized_asset_utils-1.1.0.zip} (84%) delete mode 100644 dist/stylized_grass_generator-1.0.0.zip create mode 100644 dist/stylized_grass_generator-1.1.0.zip delete mode 100644 dist/stylized_rock_generator-2.12.0.zip create mode 100644 dist/stylized_rock_generator-2.13.0.zip delete mode 100644 dist/stylized_tree_generator-1.23.0.zip create mode 100644 dist/stylized_tree_generator-1.24.0.zip create mode 100644 run_tests.ps1 create mode 100644 tests/test_panels.py diff --git a/README.md b/README.md index 2c7571f..e49edd4 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,35 @@ --- +## 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: @@ -203,6 +232,19 @@ 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. @@ -291,14 +333,28 @@ Referenz: ## Tests +Alles auf einmal: + ```bash -blender --background --python tests/test_rock_gen.py +pwsh -File run_tests.ps1 ``` -Weitere Suiten (jeweils `blender --background --python `): +Einzeln geht weiterhin `blender --background --python tests/.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 `__S`, Ursprung, Lightmap-Default | diff --git a/blender_manifest.toml b/blender_manifest.toml index 73dd23d..135c553 100644 --- a/blender_manifest.toml +++ b/blender_manifest.toml @@ -1,7 +1,7 @@ schema_version = "1.0.0" id = "stylized_rock_generator" -version = "2.12.0" +version = "2.13.0" name = "Stylized Rock Generator" tagline = "Batch-Generator fuer stylized Rocks mit UE5-Export" maintainer = "D4rkst3r" diff --git a/blender_manifest_grass.toml b/blender_manifest_grass.toml index bd6496f..50fe427 100644 --- a/blender_manifest_grass.toml +++ b/blender_manifest_grass.toml @@ -1,7 +1,7 @@ schema_version = "1.0.0" id = "stylized_grass_generator" -version = "1.0.0" +version = "1.1.0" name = "Stylized Grass Generator" tagline = "Gras, Blumen und Farne mit Wind-UV fuer SimpleGrassWind" maintainer = "D4rkst3r" diff --git a/blender_manifest_tree.toml b/blender_manifest_tree.toml index 9cb4d80..424a455 100644 --- a/blender_manifest_tree.toml +++ b/blender_manifest_tree.toml @@ -1,7 +1,7 @@ schema_version = "1.0.0" id = "stylized_tree_generator" -version = "1.23.0" +version = "1.24.0" name = "Stylized Tree Generator" tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen" maintainer = "D4rkst3r" diff --git a/blender_manifest_utils.toml b/blender_manifest_utils.toml index 3dd78e3..bde04d8 100644 --- a/blender_manifest_utils.toml +++ b/blender_manifest_utils.toml @@ -1,7 +1,7 @@ schema_version = "1.0.0" id = "stylized_asset_utils" -version = "1.0.0" +version = "1.1.0" name = "Stylized Asset Utils" tagline = "Handgebaute Assets auf EcoGame-Konvention bringen" maintainer = "D4rkst3r" diff --git a/build.ps1 b/build.ps1 index 261bfa1..a952cbb 100644 --- a/build.ps1 +++ b/build.ps1 @@ -31,7 +31,11 @@ $extensions = @( @{ name = "utils"; manifest = "blender_manifest_utils.toml"; source = "stylized_asset_utils.py" } ) -# Alte Zips weg, damit geloeschte Versionen nicht im Listing haengen bleiben. +# Alte Zips weg. WICHTIG: zwei Zips derselben Extension-ID im selben Ordner sind +# KEIN Versions-Archiv zum Zurueckrollen - 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 braucht man wieder: per git checkout des Zips. Get-ChildItem $dist -Filter *.zip -ErrorAction SilentlyContinue | Remove-Item -Force foreach ($ext in $extensions) { @@ -52,7 +56,10 @@ foreach ($ext in $extensions) { } Write-Host "== server-generate (index.json) ==" -ForegroundColor Cyan -& $Blender --command extension server-generate --repo-dir $dist +# --html erzeugt zusaetzlich eine index.html: auf einem statischen Webserver +# (z.B. nginx) laesst sich das Repo damit im Browser durchblaettern. Gitea liefert +# rohes HTML als text/plain aus, dort sieht man nur den Quelltext - schadet aber nicht. +& $Blender --command extension server-generate --repo-dir $dist --html if ($LASTEXITCODE -ne 0) { throw "server-generate fehlgeschlagen" } Write-Host "Fertig." -ForegroundColor Green diff --git a/dist/index.html b/dist/index.html new file mode 100644 index 0000000..39878ea --- /dev/null +++ b/dist/index.html @@ -0,0 +1,66 @@ + + + + + Blender Extensions + + +

Blender Extension Listing:

+

Add-on

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDNameDescriptionWebsiteBlender VersionsPython VersionsPlatformsSize
stylized_asset_utils-1.1.0Stylized Asset UtilsHandgebaute Assets auf EcoGame-Konvention bringenlink4.2.0 - ~allall6.2KB
stylized_grass_generator-1.1.0Stylized Grass GeneratorGras, Blumen und Farne mit Wind-UV fuer SimpleGrassWindlink4.2.0 - ~allall4.9KB
stylized_rock_generator-2.13.0Stylized Rock GeneratorBatch-Generator fuer stylized Rocks mit UE5-Exportlink4.2.0 - ~allall21.7KB
stylized_tree_generator-1.24.0Stylized Tree GeneratorParametrische Baeume, Palmen, Bueschen mit Wachstums-Stufenlink4.2.0 - ~allall21.8KB
+ +

Built 2026-07-31, 10:09

+ + diff --git a/dist/index.json b/dist/index.json index fe9de26..a5e52c1 100644 --- a/dist/index.json +++ b/dist/index.json @@ -7,7 +7,7 @@ "id": "stylized_asset_utils", "name": "Stylized Asset Utils", "tagline": "Handgebaute Assets auf EcoGame-Konvention bringen", - "version": "1.0.0", + "version": "1.1.0", "type": "add-on", "maintainer": "D4rkst3r", "license": [ @@ -23,16 +23,16 @@ "Mesh", "Modeling" ], - "archive_url": "./stylized_asset_utils-1.0.0.zip", - "archive_size": 6340, - "archive_hash": "sha256:af8f9e95054432e4e7eea09b520b30a659ab354795191dcab0e5504c518306d3" + "archive_url": "./stylized_asset_utils-1.1.0.zip", + "archive_size": 6351, + "archive_hash": "sha256:71f3d1ee0db0b8ebb8a5e042a763e27de7accb83b655cb374bd2e8a3c6a3d94a" }, { "schema_version": "1.0.0", "id": "stylized_grass_generator", "name": "Stylized Grass Generator", "tagline": "Gras, Blumen und Farne mit Wind-UV fuer SimpleGrassWind", - "version": "1.0.0", + "version": "1.1.0", "type": "add-on", "maintainer": "D4rkst3r", "license": [ @@ -48,16 +48,16 @@ "Mesh", "Modeling" ], - "archive_url": "./stylized_grass_generator-1.0.0.zip", - "archive_size": 4752, - "archive_hash": "sha256:ed8d7dda2f090f93c4ecad3b81af46c007578701e0851035366404399d13e14f" + "archive_url": "./stylized_grass_generator-1.1.0.zip", + "archive_size": 5027, + "archive_hash": "sha256:7a6335f0fabc8620d42a234a1c70856e1cf05b9fbd555306eec15e11d7f7ee7e" }, { "schema_version": "1.0.0", "id": "stylized_rock_generator", "name": "Stylized Rock Generator", "tagline": "Batch-Generator fuer stylized Rocks mit UE5-Export", - "version": "2.12.0", + "version": "2.13.0", "type": "add-on", "maintainer": "D4rkst3r", "license": [ @@ -73,16 +73,16 @@ "Mesh", "Modeling" ], - "archive_url": "./stylized_rock_generator-2.12.0.zip", - "archive_size": 21754, - "archive_hash": "sha256:6e18e32b841d627edfa6530587c99080c50d02ac4b0a532ed6079796bfa2aaf4" + "archive_url": "./stylized_rock_generator-2.13.0.zip", + "archive_size": 22263, + "archive_hash": "sha256:5ae39f5035435ae51803f63e1a0d06b67d8c68a5477e9fdb663a4a21c65d2492" }, { "schema_version": "1.0.0", "id": "stylized_tree_generator", "name": "Stylized Tree Generator", "tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen", - "version": "1.23.0", + "version": "1.24.0", "type": "add-on", "maintainer": "D4rkst3r", "license": [ @@ -98,9 +98,9 @@ "Mesh", "Modeling" ], - "archive_url": "./stylized_tree_generator-1.23.0.zip", - "archive_size": 22135, - "archive_hash": "sha256:a9b986aaf5e95f7a9600d529eb3eb305af3d1edc6b327862035cecde50fed6e9" + "archive_url": "./stylized_tree_generator-1.24.0.zip", + "archive_size": 22352, + "archive_hash": "sha256:abdce976729a528b87f4eba56917f26b74a5f7e0c50877a0a427ff94bda40032" } ] } \ No newline at end of file diff --git a/dist/stylized_asset_utils-1.0.0.zip b/dist/stylized_asset_utils-1.1.0.zip similarity index 84% rename from dist/stylized_asset_utils-1.0.0.zip rename to dist/stylized_asset_utils-1.1.0.zip index 0e017fea78e55839d0857b8a05568aa9520ee7a2..dda096caeeb38852b717b65fea5b973ed680c2ec 100644 GIT binary patch delta 418 zcmX?Nc-~Mtz?+#xgn@y9gFzH6n3}fluI!rV7pav@?YE@L z&i}B{-qf_*b5@3E){>2v>lb)+a)j_LvHo)3ysxrE;->S8`|9k~9DLslEj`TUngxDT z>CIX&iTPk;ix1!ZHygLrN1qose{IfzfCJ%vv)OvKe-D!W#2%xN6ZWoQOG%@(oN|bk z>YC2U|D9TAJ<9ejIZ`^$e3p^vOe5BxtP|qD37@;h{;;}na!Txz9}_P+fBUocSL3W7 zJs-mlnB5C&j9>mR-Z7#yL1c$_U+Vf~qt!WvsW)Fr@w9b*%RE%$w)^b!s@+D_f~VIA zT+_Cy?Vacw{m#gAMx~BEcU{T3=GSFSQIFll#`m5l3Em>o0ylGmRek* zSCXHblR6>L`>=t)@%x`Oo#sv7R(80(k^fLiQx~&fi=wLUIkR=XUhk}C&cC-Ly7s0s zhj`uh(|6@dx%aN>6Sz=y{QZs{C%e`N^h8e9S`wUIUw<%!OK^qM$uf-_rtd=dSJv$Q z@W<}spTB1&XIZ^gdk|=O*m z%(c6A`|jqLwAL?M0S*J(8r>$ZmXKpJk^qSS00^nGC;$Ke diff --git a/dist/stylized_grass_generator-1.0.0.zip b/dist/stylized_grass_generator-1.0.0.zip deleted file mode 100644 index 08e4389a10c4a381fbdd3fdd86e17d1259ac0bca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4752 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 diff --git a/dist/stylized_rock_generator-2.13.0.zip b/dist/stylized_rock_generator-2.13.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..193e963333b6aadf34ffc97321ed8f31d86bd594 GIT binary patch literal 22263 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
5#`7=PAUd<mQRlaS%5qWk>w1P%v@ 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 literal 0 HcmV?d00001 diff --git a/dist/stylized_tree_generator-1.23.0.zip b/dist/stylized_tree_generator-1.23.0.zip deleted file mode 100644 index c3f0394f86ed6cfaf644d6c99351a1f78c86a793..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22135 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 W10L=ja02KfL0Ag%qZe(S0Uu|J-X=Y_} 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 literal 0 HcmV?d00001 diff --git a/run_tests.ps1 b/run_tests.ps1 new file mode 100644 index 0000000..656c2ce --- /dev/null +++ b/run_tests.ps1 @@ -0,0 +1,49 @@ +# Faehrt die komplette Test-Suite gegen die Addon-Dateien. +# +# Warum es das gibt: die Tests lagen einzeln herum und wurden nur beim Bauen des +# jeweiligen Features gestartet. Dadurch waren fuenf davon still veraltet - sie +# pruesten gegen Namen und Zip-Versionen, die es laengst nicht mehr gab, und +# haetten eine echte Regression nicht gemeldet. Ein Befehl fuer alles. +# +# Aufruf: +# .\run_tests.ps1 +# .\run_tests.ps1 -Blender "E:\SteamLibrary\steamapps\common\Blender\blender.exe" +# .\run_tests.ps1 -Filter panels # nur tests\test_panels.py +# +# Hinweis: test_tree_addon.py prueft das GEBAUTE Zip aus dist\ - vorher +# .\build.ps1 laufen lassen, sonst meldet der Test das fehlende Zip. + +param( + [string]$Blender = "blender", + [string]$Filter = "*" +) + +$root = $PSScriptRoot +$tests = Get-ChildItem (Join-Path $root "tests") -Filter "test_$Filter.py" | Sort-Object Name +if (-not $tests) { throw "Keine Tests gefunden fuer Filter '$Filter'" } + +$failed = @() +foreach ($t in $tests) { + $out = & $Blender --background --factory-startup --python $t.FullName 2>&1 | Out-String + + # Die Tests melden ihr Ergebnis als letzte Zeile mit "ERGEBNIS:" bzw. + # "Alle Checks bestanden." - alles andere ist Blender-Rauschen. + $line = ($out -split "`n" | + Where-Object { $_ -match "ERGEBNIS|Alle Checks bestanden|FEHLGESCHLAGEN" } | + Select-Object -Last 1) + if (-not $line) { $line = "" } + $line = $line.Trim() + + $ok = ($line -match "ALLE|OK$|bestanden") -and ($line -notmatch "FEHLER|FEHLGESCHLAGEN") + $color = if ($ok) { "Green" } else { "Red" } + if (-not $ok) { $failed += $t.BaseName } + + Write-Host ("{0,-24} {1}" -f $t.BaseName, $line) -ForegroundColor $color +} + +Write-Host "" +if ($failed) { + Write-Host ("FEHLGESCHLAGEN ({0}): {1}" -f $failed.Count, ($failed -join ", ")) -ForegroundColor Red + exit 1 +} +Write-Host ("Alle {0} Tests bestanden." -f $tests.Count) -ForegroundColor Green diff --git a/stylized_asset_utils.py b/stylized_asset_utils.py index c7d781a..3f4c481 100644 --- a/stylized_asset_utils.py +++ b/stylized_asset_utils.py @@ -1,7 +1,7 @@ bl_info = { "name": "Stylized Asset Utils", "author": "D4rkst3r", - "version": (1, 0, 0), + "version": (1, 1, 0), "blender": (4, 2, 0), "location": "View3D > Sidebar > Asset Utils", "description": ( diff --git a/stylized_grass_generator.py b/stylized_grass_generator.py index 788b586..26861c7 100644 --- a/stylized_grass_generator.py +++ b/stylized_grass_generator.py @@ -1,7 +1,7 @@ bl_info = { "name": "Stylized Grass Generator", "author": "D4rkst3r", - "version": (1, 0, 0), + "version": (1, 1, 0), "blender": (4, 2, 0), "location": "View3D > Sidebar > Grass Gen", "description": ( @@ -270,6 +270,7 @@ class GRASSGEN_OT_create(Operator): # --------------------------------------------------------------------------- class VIEW3D_PT_grass_generator(Panel): + """Haupt-Panel: Vorlage, Name, Erzeugen. Der Rest sitzt in Unter-Panels.""" bl_label = "Grass Generator" bl_idname = "VIEW3D_PT_grass_generator" bl_space_type = 'VIEW_3D' @@ -280,46 +281,86 @@ class VIEW3D_PT_grass_generator(Panel): layout = self.layout s = context.scene.grass_gen_settings - box = layout.box() - box.label(text="Vorlage", icon='PRESET') - row = box.row(align=True) + row = layout.row(align=True) row.prop(s, "preset", text="") row.operator("grassgen.apply_preset", text="", icon='IMPORT') - row = box.row(align=True) + row = layout.row(align=True) row.prop(s, "art_name", text="") row.prop(s, "variant") - box.label(text="Name: %s_%02d" % (_art(s), s.variant), icon='INFO') - - box = layout.box() - box.label(text="Form") - box.prop(s, "blades") - box.prop(s, "height") - box.prop(s, "height_var") - box.prop(s, "width") - box.prop(s, "taper") - box.prop(s, "segments") - - box = layout.box() - box.label(text="Haltung") - box.prop(s, "bend") - box.prop(s, "tilt") - box.prop(s, "spread") - box.prop(s, "curl") - - box = layout.box() - box.label(text="Zufall", icon='FILE_REFRESH') - row = box.row(align=True) - row.prop(s, "seed") - row.operator("grassgen.randomize", text="", icon='FILE_REFRESH') - box.prop(s, "batch_count") - box.prop(s, "spacing") - box.prop(s, "tri_budget") layout.separator() - layout.operator("object.grassgen_create", icon='OUTLINER_OB_CURVES') col = layout.column(align=True) - col.label(text="UV0.V = Windmaske (Wurzel 0 - Spitze 1)", icon='INFO') + col.scale_y = 1.4 + col.operator("object.grassgen_create", text="Gras erzeugen", + icon='OUTLINER_OB_CURVES') + layout.label(text="%s_%02d" % (_art(s), s.variant), icon='INFO') + + row = layout.row(align=True) + row.prop(s, "seed") + row.operator("grassgen.randomize", text="", icon='FILE_REFRESH') + + +class _GrassSub(Panel): + """Basis aller Unter-Panels.""" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "Grass Gen" + bl_parent_id = "VIEW3D_PT_grass_generator" + bl_options = {'DEFAULT_CLOSED'} + + +class VIEW3D_PT_grass_shape(_GrassSub): + bl_label = "1 - Form" + + def draw(self, context): + layout = self.layout + s = context.scene.grass_gen_settings + layout.prop(s, "blades") + col = layout.column(align=True) + col.prop(s, "height") + col.prop(s, "height_var") + col.prop(s, "width") + col.prop(s, "taper") + layout.prop(s, "segments") + + +class VIEW3D_PT_grass_pose(_GrassSub): + bl_label = "2 - Haltung" + + def draw(self, context): + layout = self.layout + s = context.scene.grass_gen_settings + col = layout.column(align=True) + col.prop(s, "bend") + col.prop(s, "tilt") + col.prop(s, "spread") + col.prop(s, "curl") + + +class VIEW3D_PT_grass_batch(_GrassSub): + bl_label = "3 - Batch & Budget" + + def draw(self, context): + layout = self.layout + s = context.scene.grass_gen_settings + col = layout.column(align=True) + col.prop(s, "batch_count") + col.prop(s, "spacing") + layout.prop(s, "tri_budget") + + +class VIEW3D_PT_grass_info(_GrassSub): + bl_label = "4 - Kanal-Belegung" + + def draw(self, context): + layout = self.layout + col = layout.column(align=True) + col.label(text="UV0.V = Windmaske", icon='INFO') + col.label(text="Wurzel 0 - Spitze 1") + col.separator() col.label(text="VCol: R=Wind G=Halm B=AO") + col.label(text="Biom-Tint laeuft ueber") + col.label(text="PerInstanceCustomData 0/1") classes = ( @@ -327,7 +368,12 @@ classes = ( GRASSGEN_OT_apply_preset, GRASSGEN_OT_randomize, GRASSGEN_OT_create, + # Eltern-Panel zuerst, sonst findet Blender die bl_parent_id nicht. VIEW3D_PT_grass_generator, + VIEW3D_PT_grass_shape, + VIEW3D_PT_grass_pose, + VIEW3D_PT_grass_batch, + VIEW3D_PT_grass_info, ) diff --git a/stylized_rock_generator.py b/stylized_rock_generator.py index b1ef431..be1b22c 100644 --- a/stylized_rock_generator.py +++ b/stylized_rock_generator.py @@ -1,7 +1,7 @@ bl_info = { "name": "Stylized Rock Generator", "author": "D4rkst3r", - "version": (2, 12, 0), + "version": (2, 13, 0), "blender": (4, 2, 0), "location": "View3D > Sidebar > Rock Gen", "description": ( @@ -1725,6 +1725,12 @@ class ROCKGEN_OT_preset_delete(Operator): # --------------------------------------------------------------------------- class VIEW3D_PT_rock_generator(Panel): + """Haupt-Panel: nur was man bei JEDEM Stein anfasst. + + Alles andere sitzt in eingeklappten Unter-Panels (bl_parent_id). Vorher war + das eine Kette aus 15 dauerhaft offenen Boxen - man musste scrollen, um an + den Erzeugen-Knopf zu kommen. + """ bl_label = "Rock Generator" bl_idname = "VIEW3D_PT_rock_generator" bl_space_type = 'VIEW_3D' @@ -1733,173 +1739,237 @@ class VIEW3D_PT_rock_generator(Panel): def draw(self, context): layout = self.layout - settings = context.scene.rock_gen_settings + s = context.scene.rock_gen_settings - box = layout.box() - box.label(text="Presets", icon='PRESET') - box.prop(settings, "active_preset", text="") - row = box.row(align=True) - row.operator("rockgen.preset_load", text="Laden", icon='IMPORT') - row.operator("rockgen.preset_delete", text="Loeschen", icon='TRASH') - row = box.row(align=True) - row.prop(settings, "new_preset_name", text="") - row.operator("rockgen.preset_save", text="Speichern", icon='ADD') - - box = layout.box() - box.label(text="Benennung", icon='SORTALPHA') - row = box.row(align=True) - row.prop(settings, "name_prefix", text="") - row.prop(settings, "name_form", text="") - row.prop(settings, "name_class", text="") - box.prop(settings, "name_numbered") - row = box.row(align=True) - row.prop(settings, "use_class_size") - sub = row.row() - sub.enabled = not settings.use_class_size - sub.prop(settings, "base_size", text="") - if settings.use_class_size: - box.label(text="Basis-Groesse aus Klasse %s: x%.2f" - % (settings.name_class, CLASS_SIZES.get(settings.name_class, 1.0)), - icon='FIXED_SIZE') - box.label(text="Ergebnis: %s" % _asset_name(settings, 0, settings.batch_count), - icon='INFO') - - box = layout.box() - box.label(text="Basis") - box.prop(settings, "base_shape") - if settings.base_shape == 'BOX': - box.prop(settings, "box_proportions") - box.prop(settings, "box_variation") - box.prop(settings, "subsurf_levels") - - box = layout.box() - box.label(text="Noise / Displace") - box.prop(settings, "texture_type") - box.prop(settings, "noise_scale") - box.prop(settings, "displace_strength") - box.prop(settings, "seed") - box.prop(settings, "randomize_shape") - sub = box.column(align=True) - sub.enabled = settings.randomize_shape - sub.prop(settings, "noise_scale_max") - sub.prop(settings, "displace_strength_max") - - box = layout.box() - box.label(text="Decimate / Cleanup") - box.prop(settings, "decimate_ratio_1") - box.prop(settings, "decimate_ratio_2") - box.prop(settings, "dissolve_angle") - - box = layout.box() - box.label(text="Hard-Surface (optional)") - box.prop(settings, "add_bevel") - sub = box.column(align=True) - sub.enabled = settings.add_bevel - sub.prop(settings, "bevel_width") - sub.prop(settings, "bevel_segments") - box.prop(settings, "add_weighted_normal") - - box = layout.box() - box.label(text="Shading / Form") - box.prop(settings, "shading_mode") - row = box.row() - row.enabled = settings.shading_mode == 'AUTO' - row.prop(settings, "autosmooth_angle") - box.prop(settings, "flatten_bottom") - row = box.row() - row.enabled = settings.flatten_bottom - row.prop(settings, "flatten_ratio") - box.prop(settings, "set_origin_bottom") - - box = layout.box() - box.label(text="UV (Unreal Export)") - box.prop(settings, "generate_uv0") - box.prop(settings, "generate_lightmap_uv") - - box = layout.box() - box.label(text="LOD") - box.prop(settings, "generate_lods") - sub = box.column(align=True) - sub.enabled = settings.generate_lods - sub.prop(settings, "lod_count") - sub.prop(settings, "lod_ratio_step") - - box = layout.box() - box.label(text="Vertex-Cavity (ohne Textur)") - box.prop(settings, "bake_vertex_cavity") - sub = box.column(align=True) - sub.enabled = settings.bake_vertex_cavity - sub.prop(settings, "cavity_strength") - sub.prop(settings, "cavity_blur") - sub.separator() - sub.prop(settings, "moss_amount") - sub.prop(settings, "moss_noise") - sub.prop(settings, "moss_seed") - sub.prop(settings, "part_mask_from_shells") - sub.label(text="R=Cavity G=%s B=Hoehe" - % ("Teil-Maske" if settings.part_mask_from_shells else "Moos"), - icon='INFO') - box.separator() - box.operator("rockgen.bake_cavity_selection", icon='BRUSH_DATA') - - box = layout.box() - box.label(text="Material") - box.prop(settings, "assign_material") - - box = layout.box() - box.label(text="Transform") - box.prop(settings, "randomize_transform") - row = box.row(align=True) - row.prop(settings, "scale_min") - row.prop(settings, "scale_max") - box.prop(settings, "nonuniform_scale") - box.prop(settings, "keep_size") - - box = layout.box() - box.label(text="Batch") - box.prop(settings, "batch_count") - box.prop(settings, "spacing") - box.prop(settings, "use_modal") - sub = box.row() - sub.enabled = settings.use_modal - sub.prop(settings, "modal_threshold") - - box = layout.box() - box.label(text="Zufall", icon='FILE_REFRESH') - row = box.row(align=True) - row.prop(settings, "random_amount") - row.prop(settings, "random_shape", text="", icon='MOD_NOISE') - box.operator("rockgen.randomize", icon='FILE_REFRESH') + row = layout.row(align=True) + row.prop(s, "active_preset", text="") + row.operator("rockgen.preset_load", text="", icon='IMPORT') + row.operator("rockgen.preset_delete", text="", icon='TRASH') + row = layout.row(align=True) + row.prop(s, "new_preset_name", text="") + row.operator("rockgen.preset_save", text="", icon='ADD') layout.separator() - layout.operator("object.generate_rocks_batch", icon='MESH_ICOSPHERE') + col = layout.column(align=True) + col.scale_y = 1.4 + col.operator("object.generate_rocks_batch", + text="%d Stein(e) erzeugen" % s.batch_count, + icon='MESH_ICOSPHERE') + layout.label(text=_asset_name(s, 0, s.batch_count), icon='INFO') - box = layout.box() - box.label(text="FBX-Export (Unreal)") - box.prop(settings, "export_dir") - box.prop(settings, "export_selected_only") - box.operator("object.export_rocks_fbx", icon='EXPORT') + row = layout.row(align=True) + row.prop(s, "random_amount", text="Zufall") + row.prop(s, "random_shape", text="", icon='MOD_NOISE') + row.operator("rockgen.randomize", text="", icon='FILE_REFRESH') - box = layout.box() - box.label(text="Textur-Bake (tileable / Triplanar)") - box.prop(settings, "bake_dir") - box.prop(settings, "bake_prefix") - box.prop(settings, "bake_resolution") - box.prop(settings, "bake_base_color") - box.prop(settings, "bake_cell_scale") - box.prop(settings, "bake_seam_width") - box.prop(settings, "bake_plate_flat") - row = box.row(align=True) - row.prop(settings, "bake_color", toggle=True) - row.prop(settings, "bake_normal", toggle=True) - box.prop(settings, "bake_ao_into_albedo") - sub = box.row() - sub.enabled = settings.bake_ao_into_albedo - sub.prop(settings, "bake_ao_strength") - row = box.row(align=True) - row.prop(settings, "bake_roughness", toggle=True) - row.prop(settings, "bake_ao", toggle=True) - box.operator("object.bake_rock_textures", icon='TEXTURE') + +class _RockSub(Panel): + """Basis aller Unter-Panels - spart neunmal dieselben vier Zeilen.""" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "Rock Gen" + bl_parent_id = "VIEW3D_PT_rock_generator" + bl_options = {'DEFAULT_CLOSED'} + + +class VIEW3D_PT_rock_naming(_RockSub): + bl_label = "1 - Benennung & Groesse" + + def draw(self, context): + layout = self.layout + s = context.scene.rock_gen_settings + row = layout.row(align=True) + row.prop(s, "name_prefix", text="") + row.prop(s, "name_form", text="") + row.prop(s, "name_class", text="") + layout.prop(s, "name_numbered") + layout.separator() + layout.prop(s, "use_class_size") + sub = layout.row() + sub.active = not s.use_class_size + sub.prop(s, "base_size") + if s.use_class_size: + layout.label(text="Klasse %s: x%.2f" + % (s.name_class, CLASS_SIZES.get(s.name_class, 1.0)), + icon='FIXED_SIZE') + + +class VIEW3D_PT_rock_shape(_RockSub): + bl_label = "2 - Form" + + def draw(self, context): + layout = self.layout + s = context.scene.rock_gen_settings + layout.prop(s, "base_shape") + if s.base_shape == 'BOX': + layout.prop(s, "box_proportions") + layout.prop(s, "box_variation") + layout.prop(s, "subsurf_levels") + layout.separator() + layout.prop(s, "texture_type") + col = layout.column(align=True) + col.prop(s, "noise_scale") + col.prop(s, "displace_strength") + layout.prop(s, "seed") + layout.separator() + layout.prop(s, "randomize_shape") + sub = layout.column(align=True) + sub.active = s.randomize_shape + sub.prop(s, "noise_scale_max") + sub.prop(s, "displace_strength_max") + + +class VIEW3D_PT_rock_detail(_RockSub): + bl_label = "3 - Detail & Cleanup" + + def draw(self, context): + layout = self.layout + s = context.scene.rock_gen_settings + col = layout.column(align=True) + col.prop(s, "decimate_ratio_1") + col.prop(s, "decimate_ratio_2") + layout.prop(s, "dissolve_angle") + layout.separator() + layout.prop(s, "add_bevel") + sub = layout.column(align=True) + sub.active = s.add_bevel + sub.prop(s, "bevel_width") + sub.prop(s, "bevel_segments") + layout.prop(s, "add_weighted_normal") + + +class VIEW3D_PT_rock_shading(_RockSub): + bl_label = "4 - Shading & Ausrichtung" + + def draw(self, context): + layout = self.layout + s = context.scene.rock_gen_settings + layout.prop(s, "shading_mode") + row = layout.row() + row.active = s.shading_mode == 'AUTO' + row.prop(s, "autosmooth_angle") + layout.separator() + layout.prop(s, "flatten_bottom") + row = layout.row() + row.active = s.flatten_bottom + row.prop(s, "flatten_ratio") + layout.prop(s, "set_origin_bottom") + + +class VIEW3D_PT_rock_vcol(_RockSub): + bl_label = "5 - Vertex-Farben" + + def draw_header(self, context): + # Schalter im Panel-Kopf: man sieht auch eingeklappt, ob gebacken wird. + self.layout.prop(context.scene.rock_gen_settings, + "bake_vertex_cavity", text="") + + def draw(self, context): + layout = self.layout + s = context.scene.rock_gen_settings + col = layout.column() + col.active = s.bake_vertex_cavity + sub = col.column(align=True) + sub.prop(s, "cavity_strength") + sub.prop(s, "cavity_blur") + col.separator() + sub = col.column(align=True) + sub.prop(s, "moss_amount") + sub.prop(s, "moss_noise") + sub.prop(s, "moss_seed") + col.prop(s, "part_mask_from_shells") + col.label(text="R=Cavity G=%s B=Hoehe" + % ("Teil-Maske" if s.part_mask_from_shells else "Moos"), + icon='INFO') + # Nicht in der ausgegrauten Spalte: der Knopf arbeitet auf der Auswahl + # und ist auch dann sinnvoll, wenn beim Erzeugen nicht gebacken wird. + layout.separator() + layout.operator("rockgen.bake_cavity_selection", icon='BRUSH_DATA') + + +class VIEW3D_PT_rock_uv(_RockSub): + bl_label = "6 - UV, LOD & Material" + + def draw(self, context): + layout = self.layout + s = context.scene.rock_gen_settings + layout.prop(s, "generate_uv0") + layout.prop(s, "generate_lightmap_uv") + layout.separator() + layout.prop(s, "generate_lods") + sub = layout.column(align=True) + sub.active = s.generate_lods + sub.prop(s, "lod_count") + sub.prop(s, "lod_ratio_step") + layout.separator() + layout.prop(s, "assign_material") + + +class VIEW3D_PT_rock_batch(_RockSub): + bl_label = "7 - Batch & Transform" + + def draw(self, context): + layout = self.layout + s = context.scene.rock_gen_settings + col = layout.column(align=True) + col.prop(s, "batch_count") + col.prop(s, "spacing") + layout.separator() + layout.prop(s, "use_modal") + row = layout.row() + row.active = s.use_modal + row.prop(s, "modal_threshold") + layout.separator() + layout.prop(s, "randomize_transform") + sub = layout.column(align=True) + sub.active = s.randomize_transform + row = sub.row(align=True) + row.prop(s, "scale_min") + row.prop(s, "scale_max") + sub.prop(s, "nonuniform_scale") + sub.prop(s, "keep_size") + + +class VIEW3D_PT_rock_export(_RockSub): + bl_label = "8 - FBX-Export (Unreal)" + + def draw(self, context): + layout = self.layout + s = context.scene.rock_gen_settings + layout.prop(s, "export_dir") + layout.prop(s, "export_selected_only") + layout.operator("object.export_rocks_fbx", icon='EXPORT') + + +class VIEW3D_PT_rock_bake(_RockSub): + bl_label = "9 - Textur-Bake (tileable)" + + def draw(self, context): + layout = self.layout + s = context.scene.rock_gen_settings + layout.prop(s, "bake_dir") + row = layout.row(align=True) + row.prop(s, "bake_prefix", text="") + row.prop(s, "bake_resolution", text="") + layout.separator() + col = layout.column(align=True) + col.prop(s, "bake_base_color") + col.prop(s, "bake_cell_scale") + col.prop(s, "bake_seam_width") + col.prop(s, "bake_plate_flat") + layout.separator() + row = layout.row(align=True) + row.prop(s, "bake_color", toggle=True) + row.prop(s, "bake_normal", toggle=True) + row = layout.row(align=True) + row.prop(s, "bake_roughness", toggle=True) + row.prop(s, "bake_ao", toggle=True) + layout.prop(s, "bake_ao_into_albedo") + row = layout.row() + row.active = s.bake_ao_into_albedo + row.prop(s, "bake_ao_strength") + layout.separator() + layout.operator("object.bake_rock_textures", icon='TEXTURE') # --------------------------------------------------------------------------- @@ -1916,7 +1986,18 @@ classes = ( ROCKGEN_OT_preset_save, ROCKGEN_OT_preset_load, ROCKGEN_OT_preset_delete, + # Das Eltern-Panel MUSS vor seinen Unter-Panels stehen, sonst findet Blender + # die bl_parent_id nicht. Die Reihenfolge hier ist die Reihenfolge im N-Panel. VIEW3D_PT_rock_generator, + VIEW3D_PT_rock_naming, + VIEW3D_PT_rock_shape, + VIEW3D_PT_rock_detail, + VIEW3D_PT_rock_shading, + VIEW3D_PT_rock_vcol, + VIEW3D_PT_rock_uv, + VIEW3D_PT_rock_batch, + VIEW3D_PT_rock_export, + VIEW3D_PT_rock_bake, ) diff --git a/stylized_tree_generator.py b/stylized_tree_generator.py index a8e1c9a..364915d 100644 --- a/stylized_tree_generator.py +++ b/stylized_tree_generator.py @@ -1,7 +1,7 @@ bl_info = { "name": "Stylized Tree Generator", "author": "D4rkst3r", - "version": (1, 23, 0), + "version": (1, 24, 0), "blender": (4, 2, 0), "location": "View3D > Sidebar > Tree Gen", "description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen", @@ -1518,6 +1518,7 @@ class TREEGEN_OT_create(Operator): class VIEW3D_PT_tree_generator(Panel): + """Haupt-Panel: Vorlage, Name, Erzeugen. Der Rest sitzt in Unter-Panels.""" bl_label = "Tree Generator" bl_idname = "VIEW3D_PT_tree_generator" bl_space_type = 'VIEW_3D' @@ -1528,60 +1529,96 @@ class VIEW3D_PT_tree_generator(Panel): layout = self.layout s = context.scene.tree_gen_settings - box = layout.box() - box.label(text="Vorlage", icon='PRESET') - box.prop(s, "preset", text="") - box.prop(s, "seed") - row = box.row(align=True) + layout.prop(s, "preset", text="") + row = layout.row(align=True) row.prop(s, "art_name", text="") row.prop(s, "variant") - box.label(text="Name: %s_%02d%s" % (_art(s), s.variant, - "_S1..S%d" % s.stages if s.use_growth else ""), - icon='INFO') - - box = layout.box() - box.label(text="Wachstum") - box.prop(s, "use_growth") - sub = box.column(align=True) - sub.enabled = s.use_growth - sub.prop(s, "stages") - sub.prop(s, "spacing") - - box = layout.box() - box.label(text="Zufall", icon='FILE_REFRESH') - box.prop(s, "random_amount") - box.operator("object.treegen_randomize", icon='FILE_REFRESH') - - box = layout.box() - box.label(text="Blattwerk", icon='OUTLINER_OB_POINTCLOUD') - box.prop(s, "leaf_card") - sub = box.column(align=True) - sub.enabled = s.leaf_card is not None - sub.prop(s, "leaf_density") - sub.prop(s, "leaf_size") - sub.prop(s, "leaf_size_var") - sub.prop(s, "leaf_max_radius") - sub.prop(s, "leaf_tilt") - sub.prop(s, "leaf_budget") - box.operator("object.treegen_leaves", icon='OUTLINER_OB_POINTCLOUD') - if s.leaf_card is None: - box.label(text="Ohne Card bleibt _Leaf leer", icon='INFO') - - box = layout.box() - box.label(text="Zusatz-Ebenen") - box.prop(s, "make_leaf") - box.prop(s, "make_frucht") - - box = layout.box() - box.label(text="Export") - box.prop(s, "apply_modifier") - row = box.row() - row.enabled = s.apply_modifier - row.prop(s, "generate_lightmap_uv") layout.separator() - layout.operator("object.treegen_create", icon='OUTLINER_OB_MESH') - layout.label(text="Feinjustierung: am Modifier 'GN_Tree'", icon='INFO') + col = layout.column(align=True) + col.scale_y = 1.4 + col.operator("object.treegen_create", text="Baum erzeugen", + icon='OUTLINER_OB_MESH') + layout.label(text="%s_%02d%s" % (_art(s), s.variant, + "_S1..S%d" % s.stages if s.use_growth else ""), + icon='INFO') + + row = layout.row(align=True) + row.prop(s, "seed") + row.prop(s, "random_amount", text="") + row.operator("object.treegen_randomize", text="", icon='FILE_REFRESH') + + layout.label(text="Feinjustierung: Modifier 'GN_Tree'", icon='MODIFIER') + + +class _TreeSub(Panel): + """Basis aller Unter-Panels.""" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "Tree Gen" + bl_parent_id = "VIEW3D_PT_tree_generator" + bl_options = {'DEFAULT_CLOSED'} + + +class VIEW3D_PT_tree_growth(_TreeSub): + bl_label = "1 - Wachstum" + + def draw_header(self, context): + self.layout.prop(context.scene.tree_gen_settings, "use_growth", text="") + + def draw(self, context): + layout = self.layout + s = context.scene.tree_gen_settings + col = layout.column(align=True) + col.active = s.use_growth + col.prop(s, "stages") + col.prop(s, "spacing") + if s.use_growth: + layout.label(text="Setzling bis Baum in %d Stufen" % s.stages, + icon='INFO') + + +class VIEW3D_PT_tree_leaves(_TreeSub): + bl_label = "2 - Blattwerk" + + def draw(self, context): + layout = self.layout + s = context.scene.tree_gen_settings + layout.prop(s, "leaf_card") + col = layout.column(align=True) + col.active = s.leaf_card is not None + col.prop(s, "leaf_density") + col.prop(s, "leaf_size") + col.prop(s, "leaf_size_var") + col.prop(s, "leaf_max_radius") + col.prop(s, "leaf_tilt") + col.prop(s, "leaf_budget") + layout.separator() + layout.operator("object.treegen_leaves", icon='OUTLINER_OB_POINTCLOUD') + if s.leaf_card is None: + layout.label(text="Ohne Card bleibt _Leaf leer", icon='INFO') + + +class VIEW3D_PT_tree_layers(_TreeSub): + bl_label = "3 - Zusatz-Ebenen" + + def draw(self, context): + layout = self.layout + s = context.scene.tree_gen_settings + layout.prop(s, "make_leaf") + layout.prop(s, "make_frucht") + + +class VIEW3D_PT_tree_export(_TreeSub): + bl_label = "4 - Export" + + def draw(self, context): + layout = self.layout + s = context.scene.tree_gen_settings + layout.prop(s, "apply_modifier") + row = layout.row() + row.active = s.apply_modifier + row.prop(s, "generate_lightmap_uv") classes = ( @@ -1589,7 +1626,12 @@ classes = ( TREEGEN_OT_randomize, TREEGEN_OT_leaves, TREEGEN_OT_create, + # Eltern-Panel zuerst, sonst findet Blender die bl_parent_id nicht. VIEW3D_PT_tree_generator, + VIEW3D_PT_tree_growth, + VIEW3D_PT_tree_leaves, + VIEW3D_PT_tree_layers, + VIEW3D_PT_tree_export, ) diff --git a/tests/test_base_shapes.py b/tests/test_base_shapes.py index 4733467..18c59d9 100644 --- a/tests/test_base_shapes.py +++ b/tests/test_base_shapes.py @@ -26,21 +26,31 @@ VARS=[("Wuerfel",'CUBE',None), print("%-9s %-6s %s" % ("Variante","Form","Abmessungen X/Y/Z (m)")) keep=[]; x=0.0 for name,shape,prop in VARS: - for o in list(bpy.data.objects): bpy.data.objects.remove(o,do_unlink=True) + # FALLE: das hier loeschte frueher ALLE Objekte - auch die Kopien aus den + # vorigen Durchlaeufen. Die waren beim Rendern tot ("StructRNA ... removed"). + kept = {k[0] for k in keep} + for o in list(bpy.data.objects): + if o not in kept: bpy.data.objects.remove(o,do_unlink=True) s.base_shape=shape; s.box_variation=0.0 if prop: s.box_proportions=prop s.seed=7 + # Die behaltenen Kopien stehen weiter in der Szene - [0] wuerde eine davon + # greifen statt des frisch erzeugten Steins. Deshalb ueber die Differenz. + before=set(bpy.context.scene.objects) bpy.ops.object.generate_rocks_batch() - ob=[o for o in bpy.context.scene.objects if o.type=='MESH'][0] + ob=[o for o in bpy.context.scene.objects + if o.type=='MESH' and o not in before][0] bb=ob.bound_box d=[max(v[i] for v in bb)-min(v[i] for v in bb) for i in range(3)] print("%-9s %-6s %.2f / %.2f / %.2f" % (name,shape,d[0],d[1],d[2])) if name=="Platte" and not (d[2] < d[0]*0.45): fails.append("platte_nicht_flach") if name=="Riegel" and not (d[0] > d[1]*1.5): fails.append("riegel_nicht_laenglich") - cp=ob.copy(); cp.data=ob.data.copy(); keep.append((cp,x)) + cp=ob.copy(); cp.data=ob.data.copy() + bpy.context.collection.objects.link(cp) # sofort verlinken, nicht spaeter + keep.append((cp,x)) x+=2.4 for cp,px in keep: - bpy.context.collection.objects.link(cp); cp.location=(px,0,0) + cp.location=(px,0,0) cp.data.materials.clear(); cp.data.materials.append(mat) for o in list(bpy.context.scene.objects): if o.type=='MESH' and o not in [k[0] for k in keep]: bpy.data.objects.remove(o,do_unlink=True) diff --git a/tests/test_ecogame_tree.py b/tests/test_ecogame_tree.py index 5811890..b248929 100644 --- a/tests/test_ecogame_tree.py +++ b/tests/test_ecogame_tree.py @@ -16,17 +16,21 @@ if m.TreeGenSettings.bl_rna.properties['generate_lightmap_uv'].default: fails.ap s.preset='birke'; s.art_name="Birke"; s.variant=1 s.use_growth=True; s.stages=4; s.apply_modifier=False bpy.ops.object.treegen_create() +# Seit der Blattwerk-Ebene entsteht zu jeder Stufe ein zweites Objekt "_Leaf". +# Deshalb Stamm und Ebene getrennt pruefen statt eine feste Gesamtliste. names=sorted(o.name for o in bpy.context.scene.objects if o.type=='MESH') print("STUFEN:", names) -if names != ["Birke_01_S1","Birke_01_S2","Birke_01_S3","Birke_01_S4"]: fails.append("stufen_namen") +stems=[n for n in names if not n.endswith(("_Leaf","_Frucht"))] +if stems != ["Birke_01_S1","Birke_01_S2","Birke_01_S3","Birke_01_S4"]: fails.append("stufen_namen") +if [n for n in names if n.endswith("_Leaf")] != [s_+"_Leaf" for s_ in stems]: fails.append("stufen_leaf") # Einzelbaum, Art aus Preset abgeleitet for o in list(bpy.data.objects): bpy.data.objects.remove(o,do_unlink=True) s.use_growth=False; s.art_name=""; s.preset='eiche'; s.variant=2 bpy.ops.object.treegen_create() -single=[o.name for o in bpy.context.scene.objects if o.type=='MESH'] +single=sorted(o.name for o in bpy.context.scene.objects if o.type=='MESH') print("EINZELN:", single) -if single != ["Eiche_02"]: fails.append("einzel_name") +if [n for n in single if not n.endswith(("_Leaf","_Frucht"))] != ["Eiche_02"]: fails.append("einzel_name") # Ursprung unten-mittig + echte Meter ob=bpy.context.scene.objects[single[0]] diff --git a/tests/test_leaves.py b/tests/test_leaves.py index 1fff081..c3c877e 100644 --- a/tests/test_leaves.py +++ b/tests/test_leaves.py @@ -1,5 +1,12 @@ -import bpy, sys, zipfile, os, tempfile, importlib.util, math, bmesh -ZIP=r"A:\eco\stylized_rock_generator\dist\stylized_tree_generator-1.23.0.zip" +import bpy, glob, sys, zipfile, os, tempfile, importlib.util, math, bmesh +# Version NICHT fest verdrahten - sonst schlaegt der Test nach jedem +# Versions-Bump fehl, obwohl am Addon nichts kaputt ist. +_d = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "dist") +_z = sorted(glob.glob(os.path.join(_d, "stylized_tree_generator-*.zip"))) +if not _z: + print("ERGEBNIS: FEHLER: kein gebautes Tree-Zip in dist/ (erst build.ps1)") + sys.exit(1) +ZIP = _z[-1] tmp=tempfile.mkdtemp() with zipfile.ZipFile(ZIP) as z: z.extractall(tmp) init=[os.path.join(r,"__init__.py") for r,_,f in os.walk(tmp) if "__init__.py" in f][0] diff --git a/tests/test_panels.py b/tests/test_panels.py new file mode 100644 index 0000000..ec514c0 --- /dev/null +++ b/tests/test_panels.py @@ -0,0 +1,234 @@ +"""Registriert alle vier Addons und prueft die Panel-Struktur. + +Warum das eigene Test-Datei ist: py_compile sagt nur, dass die Datei syntaktisch +gueltig ist. Ob Blender die Panels ANNIMMT, ist eine andere Frage - bl_parent_id +muss auf ein bereits registriertes Panel zeigen, bl_category muss zwischen +Eltern- und Unter-Panel uebereinstimmen (sonst landet das Unter-Panel in einem +anderen Tab und ist praktisch unsichtbar), und jedes Panel muss zeichnen koennen, +ohne zu werfen. + +Aufruf: + blender --background --factory-startup --python tests/test_panels.py +""" +import os +import sys +import importlib.util + +import bpy + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# Addon-Datei -> (Haupt-Panel-idname, Anzahl Unter-Panels, die offen sein DUERFEN) +# +# In den Generatoren sind Unter-Panels Einstellungen, die man selten anfasst - +# die gehoeren alle zugeklappt, damit der Erzeugen-Knopf oben sichtbar bleibt. +# In Asset Utils ist jedes Unter-Panel ein Arbeitsschritt: 1/2/4 macht man bei +# JEDEM Asset, die bleiben offen. Deshalb steht die Erwartung hier pro Addon +# statt als pauschale Regel. +ADDONS = { + "stylized_rock_generator.py": ("VIEW3D_PT_rock_generator", 9, + "rock_gen_settings", set()), + "stylized_tree_generator.py": ("VIEW3D_PT_tree_generator", 4, + "tree_gen_settings", set()), + "stylized_grass_generator.py": ("VIEW3D_PT_grass_generator", 4, + "grass_gen_settings", set()), + "stylized_asset_utils.py": ("VIEW3D_PT_asset_utils", 5, + "asset_utils_settings", + {"1 - Vorbereiten", "2 - Vertex-Farben", + "4 - Pruefen"}), +} + +failures = [] + + +def load(path, name): + spec = importlib.util.spec_from_file_location(name, path) + mod = importlib.util.module_from_spec(spec) + sys.modules[name] = mod + spec.loader.exec_module(mod) + return mod + + +class FakeSelf: + """Ersatz fuer die Panel-Instanz: draw() braucht nur self.layout.""" + + def __init__(self, layout): + self.layout = layout + + +class FakeLayout: + """Minimal-Layout, das jeden UI-Aufruf schluckt und mitzaehlt. + + Damit laesst sich draw() ohne echten UI-Kontext ausfuehren - der ist im + --background-Modus nicht verfuegbar. + """ + + def __init__(self, counter): + self._c = counter + + def _child(self, *a, **k): + return FakeLayout(self._c) + + row = column = box = split = _child + + def prop(self, data, name, **k): + # Zugriff erzwingen: schlaegt fehl, wenn die Property nicht existiert. + getattr(data, name) + self._c["prop"] += 1 + + def operator(self, idname, **k): + self._c["op"] += 1 + self._c["ops"].add(idname) + return self + + def label(self, **k): + self._c["label"] += 1 + + def separator(self, **k): + pass + + def menu(self, *a, **k): + pass + + def template_ID(self, *a, **k): + pass + + def __setattr__(self, k, v): + # scale_y / active / enabled einfach annehmen + object.__setattr__(self, k, v) + + +class CollectLayout(FakeLayout): + """Wie FakeLayout, sammelt aber die Namen der gezeichneten Properties.""" + + def __init__(self, names): + FakeLayout.__init__(self, {"prop": 0, "op": 0, "label": 0, "ops": set()}) + self._names = names + + def _child(self, *a, **k): + return CollectLayout(self._names) + + row = column = box = split = _child + + def prop(self, data, name, **k): + self._names.add(name) + + +for filename, (parent_id, want_subs, settings_attr, open_ok) in ADDONS.items(): + path = os.path.join(ROOT, filename) + mod = load(path, filename[:-3]) + try: + mod.register() + except Exception as exc: + failures.append("%s: register() wirft %s" % (filename, exc)) + continue + + panels = [c for c in mod.classes if issubclass(c, bpy.types.Panel)] + parent = [p for p in panels if getattr(p, "bl_idname", "") == parent_id] + if not parent: + failures.append("%s: Haupt-Panel %s fehlt" % (filename, parent_id)) + mod.unregister() + continue + parent = parent[0] + + subs = [p for p in panels if getattr(p, "bl_parent_id", "") == parent_id] + if len(subs) != want_subs: + failures.append("%s: %d Unter-Panels, erwartet %d" + % (filename, len(subs), want_subs)) + + for sub in subs: + # Gleicher Tab wie das Eltern-Panel? + if sub.bl_category != parent.bl_category: + failures.append("%s/%s: bl_category '%s' != Eltern-Tab '%s'" + % (filename, sub.__name__, sub.bl_category, + parent.bl_category)) + # Eingeklappt - ausser bei den ausdruecklich als offen gewollten. + is_closed = 'DEFAULT_CLOSED' in getattr(sub, "bl_options", set()) + if not is_closed and sub.bl_label not in open_ok: + failures.append("%s/%s (%s): offen, sollte eingeklappt sein" + % (filename, sub.__name__, sub.bl_label)) + if is_closed and sub.bl_label in open_ok: + failures.append("%s/%s (%s): eingeklappt, sollte offen sein" + % (filename, sub.__name__, sub.bl_label)) + # Wirklich registriert? Blender haengt bl_rna an registrierte Klassen. + if not hasattr(sub, "bl_rna"): + failures.append("%s/%s: nicht registriert" + % (filename, sub.__name__)) + + # Jedes Panel einmal zeichnen lassen. + total = {"prop": 0, "op": 0, "label": 0, "ops": set()} + for p in [parent] + subs: + counter = {"prop": 0, "op": 0, "label": 0, "ops": set()} + # FALLE: registrierte Panel-Klassen lassen sich nicht instanziieren + # ("bpy_struct.__new__(struct): expected a single argument"). draw() ist + # aber eine normale Python-Funktion - self darf alles sein, was .layout hat. + inst = FakeSelf(FakeLayout(counter)) + try: + p.draw(inst, bpy.context) + if "draw_header" in p.__dict__: + p.draw_header(inst, bpy.context) + except Exception as exc: + failures.append("%s/%s: draw() wirft %s: %s" + % (filename, p.__name__, type(exc).__name__, exc)) + continue + if counter["prop"] + counter["op"] + counter["label"] == 0: + failures.append("%s/%s: zeichnet gar nichts" + % (filename, p.__name__)) + for k in ("prop", "op", "label"): + total[k] += counter[k] + total["ops"] |= counter["ops"] + + # Jede definierte Property muss irgendwo im Panel erreichbar sein. + # + # Das ist der eigentliche Regressionstest beim Umbauen der Panels: beim + # Verschieben von Reglern zwischen Panels faellt leicht einer heraus, und + # dann gibt es eine Einstellung, an die man in Blender nicht mehr rankommt. + # Ein paar Regler haengen an einem Zweig (BOX-Proportionen nur bei + # base_shape='BOX'), deshalb werden diese Zweige vorher durchgeschaltet. + st = getattr(bpy.context.scene, settings_attr, None) + if st is not None: + drawn = set() + branches = [{}] + for attr, values in (("base_shape", ('CUBE', 'BOX', 'ICOSPHERE')), + ("g_mode", ('SHELLS', 'MOSS')), + ("shading_mode", ('AUTO', 'FLAT', 'SMOOTH'))): + if hasattr(st, attr): + branches += [{attr: v} for v in values] + for combo in branches: + for attr, val in combo.items(): + try: + setattr(st, attr, val) + except TypeError: + continue + for p in [parent] + subs: + try: + p.draw(FakeSelf(CollectLayout(drawn)), bpy.context) + if "draw_header" in p.__dict__: + p.draw_header(FakeSelf(CollectLayout(drawn)), bpy.context) + except Exception: + pass + defined = set(type(st).__annotations__) + unreachable = sorted(defined - drawn) + if unreachable: + failures.append("%s: Property(s) in keinem Panel erreichbar: %s" + % (filename, ", ".join(unreachable))) + + # Jeder Operator, den ein Panel anbietet, muss es auch geben. + for idname in sorted(total["ops"]): + grp, _, op = idname.partition(".") + if not hasattr(getattr(bpy.ops, grp, None), op): + failures.append("%s: Panel ruft unbekannten Operator %s" + % (filename, idname)) + + print("%-32s %d Unter-Panels %3d Props %2d Buttons %2d Labels" + % (filename, len(subs), total["prop"], total["op"], total["label"])) + + mod.unregister() + +print("") +if failures: + print("ERGEBNIS: %d FEHLER" % len(failures)) + for f in failures: + print(" - " + f) + sys.exit(1) +print("ERGEBNIS: ALLE PANELS OK") diff --git a/tests/test_rock_gen.py b/tests/test_rock_gen.py index 91bfe3b..512ade9 100644 --- a/tests/test_rock_gen.py +++ b/tests/test_rock_gen.py @@ -81,12 +81,16 @@ def main(): settings.assign_material = False # 3. Batch erzeugen + # Vorher merken, was schon da ist: das Benennungsschema kommt aus den + # Einstellungen (Fels_A_M_01 ...), ein fest verdrahtetes Praefix wie "Rock_" + # geht schief, sobald jemand die Benennung aendert. Differenz statt Ratename. + before = set(bpy.context.scene.objects) result = bpy.ops.object.generate_rocks_batch() if 'FINISHED' not in result: failures.append("Operator lieferte nicht {'FINISHED'}: %s" % (result,)) rocks = [o for o in bpy.context.scene.objects - if o.type == 'MESH' and o.name.startswith("Rock_")] + if o.type == 'MESH' and o not in before] if len(rocks) != BATCH: failures.append("Erwartet %d Rocks, gefunden %d" % (BATCH, len(rocks))) diff --git a/tests/test_tree_addon.py b/tests/test_tree_addon.py index 8cd043e..3f3164e 100644 --- a/tests/test_tree_addon.py +++ b/tests/test_tree_addon.py @@ -1,7 +1,15 @@ """Prueft das gebaute Tree-Addon: registrieren, Operator ausfuehren, Ergebnis messen.""" import bpy, sys, zipfile, os, tempfile, importlib.util, traceback -ZIP = r"A:\eco\stylized_rock_generator\dist\stylized_tree_generator-1.22.0.zip" +# Version NICHT fest verdrahten - sonst schlaegt der Test bei jedem Bump fehl, +# obwohl am Addon nichts kaputt ist. +import glob +_d = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "dist") +_z = sorted(glob.glob(os.path.join(_d, "stylized_tree_generator-*.zip"))) +if not _z: + print("ERGEBNIS: FEHLER: kein gebautes Tree-Zip in dist/ (erst build.ps1 laufen lassen)") + sys.exit(1) +ZIP = _z[-1] fails = [] tmp = tempfile.mkdtemp() @@ -46,7 +54,11 @@ for o in list(bpy.data.objects): bpy.data.objects.remove(o, do_unlink=True) s.preset = "baum"; s.use_growth = True; s.stages = 4 r = bpy.ops.object.treegen_create() -obs = sorted([o for o in bpy.data.objects if o.type == 'MESH'], key=lambda o: o.name) +# Nur die Staemme: seit der Blattwerk-Ebene gibt es zu jeder Stufe noch ein +# "_Leaf"-Objekt. Mit denen zusammen ist die Vertex-Folge nicht mehr aufsteigend. +obs = sorted([o for o in bpy.data.objects + if o.type == 'MESH' and not o.name.endswith(("_Leaf", "_Frucht"))], + key=lambda o: o.name) bpy.context.view_layer.update() dg = bpy.context.evaluated_depsgraph_get() counts = [len(o.evaluated_get(dg).data.vertices) for o in obs]