Files
stylized-rock-generator/tests/test_part_mask.py
T
D4rkst3randClaude Opus 4.8 512d61e370 Rock Generator 2.12.0: Vertex-Cavity auf beliebige Meshes + Teil-Maske
Der User baut Pilze von Hand (Hut und Stiel als getrennte Objekte, mit Strg+J
verbunden) und fragte, was er wegen Vertex-Farben beachten muss.

Zwei Ergaenzungen:

1) Button "Vertex-Cavity auf Auswahl": backt R=Cavity / G / B=Hoehe auf JEDES
   markierte Mesh - nicht mehr nur auf generierte Steine. Damit lassen sich
   handgebaute Assets (Pilze, Baumstumpf, Erzfels) auf dasselbe Layout bringen.

2) Option "G = Teil-Maske (Shells)": statt der Moos-Maske werden getrennte
   Mesh-Teile markiert. Der oberste/breiteste Teil bekommt G=1, der Rest 0.
   Bei Strg+J-verbundenen Assets ist die Trennung dadurch EXAKT statt ueber
   Normalen geschaetzt - beim Pilz also Hut vs Stiel. Ein Material toent beide
   unterschiedlich, ein zweiter Material-Slot entfaellt.
   Einteilige Meshes bleiben bewusst unmarkiert (G=0), damit die Maske nichts
   Falsches behauptet.

Am nachgebauten Pilz geprueft: 2 Shells erkannt, G im obersten Teil ausschliess-
lich 1.0, im Rest ausschliesslich 0.0, Maske binaer, Einzelteil-Mesh -> nur 0.0.

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

71 lines
3.4 KiB
Python

import bpy, sys, importlib.util, bmesh
spec=importlib.util.spec_from_file_location("srg", r"A:\eco\stylized_rock_generator\stylized_rock_generator.py")
r=importlib.util.module_from_spec(spec); sys.modules["srg"]=r; spec.loader.exec_module(r)
try: r.unregister()
except Exception: pass
r.register()
fails=[]
for o in list(bpy.data.objects): bpy.data.objects.remove(o,do_unlink=True)
# Pilz nachbauen: Stiel (Zylinder) + Hut (UV-Kugel oben), dann Strg+J
bpy.ops.mesh.primitive_cylinder_add(vertices=10, radius=0.028, depth=0.126, location=(0,0,0.063))
stiel=bpy.context.active_object; stiel.name="Stiel"
bpy.ops.mesh.primitive_uv_sphere_add(segments=14, ring_count=7, radius=0.042, location=(0,0,0.13))
hut=bpy.context.active_object; hut.name="Hut"
hut.scale=(1,1,0.5); bpy.ops.object.transform_apply(scale=True)
for o in bpy.context.selected_objects: o.select_set(False)
stiel.select_set(True); hut.select_set(True)
bpy.context.view_layer.objects.active=stiel
bpy.ops.object.join()
pilz=bpy.context.active_object; pilz.name="Mushroom_Test"
print("Pilz: %d Tris" % sum(len(p.vertices)-2 for p in pilz.data.polygons))
s=bpy.context.scene.rock_gen_settings
s.bake_vertex_cavity=True; s.cavity_strength=0.7
s.part_mask_from_shells=True
pilz.select_set(True); bpy.context.view_layer.objects.active=pilz
res=bpy.ops.rockgen.bake_cavity_selection()
print("Operator:", list(res))
me=pilz.data
ca=me.color_attributes.get("Cavity")
if ca is None: fails.append("kein_attribut")
else:
# Hut liegt oben -> dort muss G=1 sein, am Stiel G=0
# Direkt gegen die Shell-Zerlegung pruefen statt ueber z-Schwellen:
# der oberste Teil (Hut) muss ueberall G=1 haben, alle anderen Teile G=0.
shells = r._mesh_shells(me)
infos = []
for sh in shells:
vs = {vi for pi in sh for vi in me.polygons[pi].vertices}
infos.append((sh, max(me.vertices[v].co.z for v in vs)))
infos.sort(key=lambda t: -t[1])
top_sh = infos[0][0]
g_top = {round(ca.data[li].color[1],3)
for pi in top_sh for li in me.polygons[pi].loop_indices}
g_rest = {round(ca.data[li].color[1],3)
for sh,_ in infos[1:] for pi in sh for li in me.polygons[pi].loop_indices}
print("Shells: %d | G im obersten Teil: %s | G im Rest: %s"
% (len(shells), sorted(g_top), sorted(g_rest)))
if g_top != {1.0}: fails.append("hut_nicht_1")
if g_rest != {0.0}: fails.append("stiel_nicht_0")
# G darf NUR 0 oder 1 sein (harte Maske)
vals={round(d.color[1],3) for d in ca.data}
print("G Werte insgesamt: %s" % sorted(vals))
if not vals <= {0.0,1.0}: fails.append("G_nicht_binaer")
R=[d.color[0] for d in ca.data]; B=[d.color[2] for d in ca.data]
print("R %.2f..%.2f B %.2f..%.2f" % (min(R),max(R),min(B),max(B)))
if max(B)-min(B) < 0.8: fails.append("B_kein_verlauf")
# Einzel-Shell-Fall: darf nichts faelschlich markieren
bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=2, radius=0.1, location=(1,0,0.1))
solo=bpy.context.active_object
for o in bpy.context.selected_objects: o.select_set(False)
solo.select_set(True); bpy.context.view_layer.objects.active=solo
bpy.ops.rockgen.bake_cavity_selection()
g2={round(d.color[1],3) for d in solo.data.color_attributes["Cavity"].data}
print("Einzelteil-Mesh -> G Werte: %s" % sorted(g2))
if g2 != {0.0}: fails.append("einzelshell_markiert")
r.unregister()
print("ERGEBNIS:", "ALLE CHECKS OK" if not fails else "FEHLER: "+", ".join(fails))