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>
This commit is contained in:
2026-07-31 12:12:38 +02:00
co-authored by Claude Opus 4.8
parent ef57050739
commit 71b9982441
26 changed files with 902 additions and 284 deletions
+79 -33
View File
@@ -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,
)