Files
D4rkst3randClaude Opus 4.8 2c339b5d0c Tree Gen 1.31.0: LOD1-3 trugen die Standposition mit (aus UE gemeldet)
Fehler: _origin_bottom_center backt zuerst die Weltmatrix ins Mesh und wendet
DANN die Zentrierung an - zurueck gibt es aber nur die Zentrierung. Den Stufen
1-3 wurde nur diese aufgedrueckt, ihre Weltmatrix blieb ungebacken. Ergebnis:
sie landeten bei MINUS der Standposition. Gemeldet fuer einen Baum bei x = 18 m,
dessen LOD1-3 bei x = -1894 cm sassen - exakt die Standposition, in Zentimetern
wegen FBX_SCALE_ALL. Jetzt backt jede Stufe erst ihre EIGENE Weltmatrix, dann
kommt die gemeinsame Zentrierung aus LOD0.

Warum der Test das nicht gefunden hat: er baute den Baum am URSPRUNG. Dort ist
minus null eben null. Der Baum steht jetzt bewusst bei (18, -7.5), und je Stufe
wird die X/Y-Mitte im reimportierten FBX gemessen - an derselben Stelle wie die
Radialitaets-Pruefung.

Gegenprobe gemacht: mit zurueckgedrehtem Fix meldet der Test LOD1-3 bei
-17.87/7.46, also exakt minus der Standposition, und faellt mit 6 Fehlern durch.
Mit Fix liegen alle Stufen bei 0.000-0.135 um den Ursprung.

Ausserdem Dichte 170 -> 150. Grund: mit der REALEN Diamond-Card (6 Tris, nicht
die 2-Tri-Attrappe aus meinen Sweeps) landet die Eiche bei Dichte 170 auf 4206
Tris - 2778 Krone plus 1428 Stamm - und damit ueber dem 4000er-Budget aus
ASSETS.md Teil 6. Die abgenommene Krone (2778) und das dokumentierte Budget
(4000) sind also nicht gleichzeitig haltbar. 150 liegt im abgesegneten
Sweetspot 130-170 und haelt das Budget mit 3816. Wenn der Look bei 170 wichtiger
ist, muesste das Budget auf ~4300 - das ist eine Entscheidung der Spielseite.

Nebenbei: test_leaves hatte leaf_size fest auf 0.22 stehen (alter
Blaettchen-Wert) und pruefte gegen ein fest verdrahtetes 2500er-Budget. Beides
kommt jetzt aus den Einstellungen.

23 Tests bestehen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-06 09:20:18 +02:00

79 lines
3.7 KiB
Python

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]
spec=importlib.util.spec_from_file_location("stg",init)
m=importlib.util.module_from_spec(spec); sys.modules["stg"]=m; spec.loader.exec_module(m)
try: m.unregister()
except Exception: pass
m.register()
fails=[]
for o in list(bpy.data.objects): bpy.data.objects.remove(o,do_unlink=True)
# Card wie beim User
me=bpy.data.meshes.new("DiamondCard"); bm=bmesh.new()
for ang in (0.0, math.pi/3, 2*math.pi/3):
c,s_=math.cos(ang), math.sin(ang)
vs=[bm.verts.new(p) for p in [(-c,-s_,-1.41),(c,s_,-1.41),(c,s_,1.41),(-c,-s_,1.41)]]
bm.faces.new(vs)
bm.to_mesh(me); bm.free(); me.uv_layers.new(name="UVMap")
card=bpy.data.objects.new("Diamon Plane", me); bpy.context.collection.objects.link(card)
s=bpy.context.scene.tree_gen_settings
s.preset='eiche'; s.art_name="Eiche"; s.variant=1
# Groesse und Dichte aus den Defaults nehmen. Frueher stand hier fest
# leaf_size=0.22 - der alte Blaettchen-Wert. Zusammen mit der neuen Dichte 170
# ergab das genau die Kombination, die wir loswerden wollten: hunderte winziger
# Cards (gemessen 2778 Blatt-Tris statt ~1000).
s.leaf_card=card
s.make_leaf=True; s.use_growth=False
r=bpy.ops.object.treegen_create()
names=sorted(o.name for o in bpy.context.scene.objects if o.type=='MESH')
print("OBJEKTE:", [n for n in names if n!="Diamon Plane"])
if "Eiche_01" not in names or "Eiche_01_Leaf" not in names: fails.append("objekte")
bpy.context.view_layer.update()
dg=bpy.context.evaluated_depsgraph_get()
def tris(n):
ob=bpy.context.scene.objects[n]
return sum(len(p.vertices)-2 for p in ob.evaluated_get(dg).data.polygons)
tt,tl=tris("Eiche_01"), tris("Eiche_01_Leaf")
# Budget aus den Einstellungen holen, nicht fest verdrahten: es steht seit
# ASSETS.md Teil 6 pro Wachstumsstufe (S1 800 / S2 1200 / S3+S4 4000) und wurde
# von 2500 angehoben, weil bei ISM-Instanzen mit LOD-Kette nicht die Dreiecke
# der Engpass sind, sondern Overdraw und Schatten der Cards.
budget = s.leaf_budget
print("TRIS Stamm=%d Blatt=%d Summe=%d / %d %s (Dichte %.0f)"
% (tt,tl,tt+tl,budget,"OK" if tt+tl<=budget else "UEBER", s.leaf_density))
if tl==0: fails.append("blattwerk_leer")
if tt+tl>budget: fails.append("budget")
# Ursprung deckungsgleich
a=bpy.context.scene.objects["Eiche_01"]; b=bpy.context.scene.objects["Eiche_01_Leaf"]
d=(a.location-b.location).length
print("URSPRUNG-Abstand: %.6f" % d)
if d>1e-6: fails.append("ursprung")
# Operator auf Auswahl
for o in bpy.context.selected_objects: o.select_set(False)
a.select_set(True); bpy.context.view_layer.objects.active=a
r2=bpy.ops.object.treegen_leaves()
print("OPERATOR treegen_leaves:", list(r2))
if 'FINISHED' not in r2: fails.append("operator")
# Ohne Card -> leeres _Leaf
for o in list(bpy.data.objects):
if o.name.startswith("Eiche"): bpy.data.objects.remove(o,do_unlink=True)
s.leaf_card=None; s.art_name="Birke"; s.preset='birke'
bpy.ops.object.treegen_create()
lf=bpy.context.scene.objects.get("Birke_01_Leaf")
print("ohne Card -> _Leaf vorhanden:", lf is not None, "| leer:", lf and len(lf.data.polygons)==0)
if lf is None: fails.append("leer_leaf")
m.unregister()
print("ERGEBNIS:", "ALLE CHECKS OK" if not fails else "FEHLER: "+", ".join(fails))
sys.exit(1 if fails else 0)