"""Schnellstart-Sorten + Dissolve-Bereich. Der Schnellstart soll den Alltag abdecken, ohne dass man sich durch neun Unter-Panels klickt. Damit das etwas wert ist, muss jede Sorte OHNE Nacharbeit ein brauchbares Ergebnis liefern - genau das prueft diese Datei. Der Dissolve-Winkel ist ein BEREICH, weil ein fester Wert nie fuer alle Steine einer Sorte passt. Geprueft wird deshalb auch, dass der Bereich tatsaechlich je Stein streut und nicht nur als Zahl im Panel steht. Aufruf: blender --background --factory-startup --python tests/test_schnellstart.py """ import os import sys importlib_util = __import__("importlib.util", fromlist=["util"]) import bpy ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) spec = importlib_util.spec_from_file_location( "rg", os.path.join(ROOT, "stylized_rock_generator.py")) rg = importlib_util.module_from_spec(spec) sys.modules["rg"] = rg spec.loader.exec_module(rg) rg.register() fails = [] s = bpy.context.scene.rock_gen_settings s.use_modal = False def bauen(anzahl=3, seed=0): for o in list(bpy.data.objects): bpy.data.objects.remove(o, do_unlink=True) s.batch_count = anzahl s.seed = seed bpy.ops.object.generate_rocks_batch() return sorted((o for o in bpy.context.scene.objects if o.type == 'MESH'), key=lambda o: o.name) print("%-12s %7s %7s %10s %s" % ("Sorte", "Verts", "Tris", "Dissolve", "Slot")) for kind, eintrag in rg.ROCK_KINDS.items(): s.rock_kind = kind res = bpy.ops.rockgen.apply_kind() if 'FINISHED' not in res: fails.append("Sorte %s: apply_kind lieferte %s" % (kind, res)) continue obs = bauen() if not obs: fails.append("Sorte %s erzeugt keine Geometrie" % kind) continue verts = sum(len(o.data.vertices) for o in obs) // len(obs) tris = sum(sum(len(p.vertices) - 2 for p in o.data.polygons) for o in obs) // len(obs) slots = [m.name for m in obs[0].data.materials] print("%-12s %7d %7d %5.0f-%-4.0f %s" % (eintrag["label"], verts, tris, s.dissolve_angle, s.dissolve_angle_max, slots or "KEINE")) if not slots: fails.append("Sorte %s: kein Material-Slot" % eintrag["label"]) elif not slots[0].startswith("M_Rock_"): fails.append("Sorte %s: Slot '%s' folgt nicht M_Rock_" % (eintrag["label"], slots[0])) # Der Bereich muss sinnvoll sein - untere Grenze nicht ueber der oberen. if s.dissolve_angle > s.dissolve_angle_max: fails.append("Sorte %s: Dissolve-Bereich verdreht (%.0f > %.0f)" % (eintrag["label"], s.dissolve_angle, s.dissolve_angle_max)) # Die Erz-Sorte muss dicht genug fuer eine saubere Nugget-Maske sein - # sonst loest die eigene Vorlage die eigene Warnung aus. if eintrag["values"].get("bake_ore"): if slots != [rg.ORE_MATERIAL[0]]: fails.append("Sorte %s: Slot %s, erwartet ['%s']" % (eintrag["label"], slots, rg.ORE_MATERIAL[0])) duenn = [o for o in obs if len(o.data.vertices) < rg.ORE_MIN_VERTS] if duenn: fails.append("Sorte %s: %d von %d Steinen unter %d Verts - die " "eigene Vorlage wuerde die eigene Dichte-Warnung " "ausloesen" % (eintrag["label"], len(duenn), len(obs), rg.ORE_MIN_VERTS)) else: if slots == [rg.ORE_MATERIAL[0]]: fails.append("Sorte %s traegt den Erz-Slot ohne Erz" % eintrag["label"]) # --- Der Dissolve-Bereich muss je Stein streuen -------------------------- s.rock_kind = 'FINDLING' bpy.ops.rockgen.apply_kind() obs = bauen(anzahl=6, seed=3) tris = [sum(len(p.vertices) - 2 for p in o.data.polygons) for o in obs] print("") print("Findling x6 bei %.0f-%.0f Grad: %s" % (s.dissolve_angle, s.dissolve_angle_max, tris)) if len(set(tris)) < 3: fails.append("Dissolve-Bereich streut nicht je Stein: %s" % tris) # Bereich zu = fester Winkel wie frueher. Muss weiter funktionieren. s.dissolve_angle_max = s.dissolve_angle obs = bauen(anzahl=2, seed=3) if not obs: fails.append("Geschlossener Dissolve-Bereich erzeugt keine Geometrie") else: print("Bereich zu (%.0f Grad): %d Steine, %d Tris" % (s.dissolve_angle, len(obs), sum(len(p.vertices) - 2 for p in obs[0].data.polygons))) print("") if fails: print("ERGEBNIS: %d FEHLER" % len(fails)) for x in fails: print(" - " + x) sys.exit(1) print("ERGEBNIS: ALLE CHECKS OK")