Tree Gen 1.44.0: _Pieces_All.fbx steht auf der Stammbasis
Die Zusammenbau-Datei trug den SZENEN-Nullpunkt mit: ein Baum bei x = 18 m exportierte gemessen mit X 16.5..19.3, Y -8.7..-6.2. Die Einzel-Teile hatten das Problem nie (sie sitzen auf ihrem Massenmittelpunkt), der Stand-Baum folgt der Stammbasis seit 1.36 - nur diese eine Datei fiel heraus. Der Versatz kommt aus DERSELBEN Funktion wie beim Stand-Baum: die Rechnung ist aus _origin_bottom_center als _stammbasis_versatz herausgeloest und wird von beiden Wegen benutzt. Zwei Rechnungen fuer denselben Nullpunkt waeren genau die Fehlerklasse, die hier schon dreimal zugeschlagen hat - und hier haette sie besonders weh getan, weil die zusammengesetzten Truemmer exakt auf dem Platz des Baums liegen muessen. Angewendet nur fuer diesen Export, danach zurueckgenommen: mit "Teile behalten" staenden sie sonst verschoben neben dem Baum in der Szene. test_faell_teile stellt den Baum jetzt bewusst auf (18, -7.5) und prueft den Stammfuss ueber den QUERSCHNITT auf Fusshoehe, nicht ueber die Bbox-Mitte - die Krone haengt einseitig und wuerde den Fehler verwischen. Gegenprobe ohne Versatz: Stammfuss bei 18.000 / -7.500, exakt der gemeldete Wert. 27 Tests bestehen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -563,6 +563,30 @@ Der Kaktus erzeugt regulaer **0 Cards** — kein Ast ist duenner als `Max Radius
|
|||||||
und das ist botanisch richtig. Vorher lief das still durch und man stand vor
|
und das ist botanisch richtig. Vorher lief das still durch und man stand vor
|
||||||
einer leeren `_Leaf`-Ebene; jetzt sagt der Operator es und nennt den Regler.
|
einer leeren `_Leaf`-Ebene; jetzt sagt der Operator es und nennt den Regler.
|
||||||
|
|
||||||
|
### Die Gesamtdatei steht jetzt auch auf der Stammbasis
|
||||||
|
|
||||||
|
`_Pieces_All.fbx` trug den SZENEN-Nullpunkt mit: ein Baum bei x = 18 m
|
||||||
|
exportierte gemessen mit X 16.5..19.3, Y -8.7..-6.2. Die Einzel-Teile hatten
|
||||||
|
das Problem nie - sie sitzen ohnehin auf ihrem Massenmittelpunkt -, und der
|
||||||
|
Stand-Baum folgt der Stammbasis-Konvention seit 1.36. Nur die
|
||||||
|
Zusammenbau-Datei fiel heraus.
|
||||||
|
|
||||||
|
Der Versatz kommt aus **derselben Funktion** wie beim Stand-Baum
|
||||||
|
(`_stammbasis_versatz`, aus `_origin_bottom_center` herausgeloest). Zwei
|
||||||
|
Rechnungen fuer denselben Nullpunkt waeren genau die Fehlerklasse, die hier
|
||||||
|
schon dreimal zugeschlagen hat - und hier haette sie besonders weh getan: die
|
||||||
|
zusammengesetzten Truemmer muessen exakt auf dem Platz liegen, wo der Baum
|
||||||
|
stand.
|
||||||
|
|
||||||
|
Angewendet wird der Versatz nur fuer diesen Export und danach zurueckgenommen.
|
||||||
|
Mit "Teile behalten" staenden sie sonst verschoben neben dem Baum in der Szene.
|
||||||
|
|
||||||
|
Geprueft wird ueber den STAMMQUERSCHNITT auf Fusshoehe, nicht ueber die
|
||||||
|
Bbox-Mitte: die Krone haengt einseitig, eine Bbox-Mitte wuerde den Fehler
|
||||||
|
verwischen. Der Test stellt den Baum dafuer bewusst auf (18, -7.5) - bei
|
||||||
|
(0,0,0) faellt so etwas nicht auf. Gegenprobe ohne den Versatz: Stammfuss bei
|
||||||
|
18.000 / -7.500, genau der gemeldete Wert.
|
||||||
|
|
||||||
### UCX auch in der Gesamtdatei - und zwei Fehler, die dabei auffielen
|
### UCX auch in der Gesamtdatei - und zwei Fehler, die dabei auffielen
|
||||||
|
|
||||||
`_Pieces_All.fbx` kam ohne Kollision an, waehrend die Einzeldateien welche
|
`_Pieces_All.fbx` kam ohne Kollision an, waehrend die Einzeldateien welche
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
schema_version = "1.0.0"
|
schema_version = "1.0.0"
|
||||||
|
|
||||||
id = "stylized_tree_generator"
|
id = "stylized_tree_generator"
|
||||||
version = "1.43.0"
|
version = "1.44.0"
|
||||||
name = "Stylized Tree Generator"
|
name = "Stylized Tree Generator"
|
||||||
tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen"
|
tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen"
|
||||||
maintainer = "D4rkst3r"
|
maintainer = "D4rkst3r"
|
||||||
|
|||||||
Vendored
+3
-3
@@ -50,17 +50,17 @@
|
|||||||
<td>34.1KB</td>
|
<td>34.1KB</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><tt><a href="./stylized_tree_generator-1.43.0.zip?repository=.%2Findex.json&blender_version_min=4.2.0">stylized_tree_generator-1.43.0</a></tt></td>
|
<td><tt><a href="./stylized_tree_generator-1.44.0.zip?repository=.%2Findex.json&blender_version_min=4.2.0">stylized_tree_generator-1.44.0</a></tt></td>
|
||||||
<td>Stylized Tree Generator</td>
|
<td>Stylized Tree Generator</td>
|
||||||
<td>Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen</td>
|
<td>Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen</td>
|
||||||
<td><a href="https://git.d4rkst3r.de/D4rkst3r/stylized-rock-generator">link</a></td>
|
<td><a href="https://git.d4rkst3r.de/D4rkst3r/stylized-rock-generator">link</a></td>
|
||||||
<td>4.2.0 - ~</td>
|
<td>4.2.0 - ~</td>
|
||||||
<td>all</td>
|
<td>all</td>
|
||||||
<td>all</td>
|
<td>all</td>
|
||||||
<td>49.0KB</td>
|
<td>49.5KB</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<center><p>Built 2026-08-07, 04:53</p></center>
|
<center><p>Built 2026-08-07, 04:59</p></center>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Vendored
+4
-4
@@ -82,7 +82,7 @@
|
|||||||
"id": "stylized_tree_generator",
|
"id": "stylized_tree_generator",
|
||||||
"name": "Stylized Tree Generator",
|
"name": "Stylized Tree Generator",
|
||||||
"tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen",
|
"tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen",
|
||||||
"version": "1.43.0",
|
"version": "1.44.0",
|
||||||
"type": "add-on",
|
"type": "add-on",
|
||||||
"maintainer": "D4rkst3r",
|
"maintainer": "D4rkst3r",
|
||||||
"license": [
|
"license": [
|
||||||
@@ -98,9 +98,9 @@
|
|||||||
"Mesh",
|
"Mesh",
|
||||||
"Modeling"
|
"Modeling"
|
||||||
],
|
],
|
||||||
"archive_url": "./stylized_tree_generator-1.43.0.zip",
|
"archive_url": "./stylized_tree_generator-1.44.0.zip",
|
||||||
"archive_size": 50130,
|
"archive_size": 50734,
|
||||||
"archive_hash": "sha256:2d87c780f90befc057e6895762c151bd6f4e9514485f5eb98b64993f6304893f"
|
"archive_hash": "sha256:e10cf2efafc751b2e8b7cf6e15a793408f43ffff8b1dc0e98e43ad8ba809659a"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,7 +1,7 @@
|
|||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "Stylized Tree Generator",
|
"name": "Stylized Tree Generator",
|
||||||
"author": "D4rkst3r",
|
"author": "D4rkst3r",
|
||||||
"version": (1, 43, 0),
|
"version": (1, 44, 0),
|
||||||
"blender": (4, 2, 0),
|
"blender": (4, 2, 0),
|
||||||
"location": "View3D > Sidebar > Tree Gen",
|
"location": "View3D > Sidebar > Tree Gen",
|
||||||
"description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen",
|
"description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen",
|
||||||
@@ -2804,8 +2804,22 @@ def _origin_bottom_center(ob):
|
|||||||
# Baum ohnehin fuer die Faell-Teile traegt. Ein Schwellwert auf den
|
# Baum ohnehin fuer die Faell-Teile traegt. Ein Schwellwert auf den
|
||||||
# Achsabstand waere die naheliegende Alternative und faellt beim Kaktus um:
|
# Achsabstand waere die naheliegende Alternative und faellt beim Kaktus um:
|
||||||
# dessen Stamm ist dicker als jeder sinnvolle Schwellwert.
|
# dessen Stamm ist dicker als jeder sinnvolle Schwellwert.
|
||||||
stamm_verts = _stamm_koordinaten(me)
|
versatz = _stammbasis_versatz(_stamm_koordinaten(me))
|
||||||
|
me.transform(versatz)
|
||||||
|
return versatz
|
||||||
|
|
||||||
|
|
||||||
|
def _stammbasis_versatz(stamm_verts):
|
||||||
|
"""Versatz, der die STAMMBASIS auf den Ursprung legt.
|
||||||
|
|
||||||
|
Eine Stelle fuer beide Wege: der Stand-Baum-Export und die Gesamtdatei der
|
||||||
|
Faell-Teile muessen denselben Nullpunkt benutzen, sonst passen die
|
||||||
|
zusammengesetzten Truemmer nicht auf den Platz des Baums. Zwei Rechnungen
|
||||||
|
fuer dieselbe Frage waeren genau die Fehlerklasse, die hier schon dreimal
|
||||||
|
zugeschlagen hat.
|
||||||
|
"""
|
||||||
|
if not stamm_verts:
|
||||||
|
return Matrix.Identity(4)
|
||||||
zs = [c.z for c in stamm_verts]
|
zs = [c.z for c in stamm_verts]
|
||||||
z_fuss = min(zs)
|
z_fuss = min(zs)
|
||||||
# X/Y aus dem UNTERSTEN RING, nicht aus der Bbox des ganzen Stammes: ein
|
# X/Y aus dem UNTERSTEN RING, nicht aus der Bbox des ganzen Stammes: ein
|
||||||
@@ -2823,9 +2837,7 @@ def _origin_bottom_center(ob):
|
|||||||
mx = (min(xs) + max(xs)) * 0.5
|
mx = (min(xs) + max(xs)) * 0.5
|
||||||
my = (min(ys) + max(ys)) * 0.5
|
my = (min(ys) + max(ys)) * 0.5
|
||||||
|
|
||||||
versatz = Matrix.Translation((-mx, -my, -z_fuss))
|
return Matrix.Translation((-mx, -my, -z_fuss))
|
||||||
me.transform(versatz)
|
|
||||||
return versatz
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -3126,6 +3138,21 @@ class TREEGEN_OT_export_pieces(Operator):
|
|||||||
# Sie entstehen HIER und nicht in der Schleife weiter unten: dort
|
# Sie entstehen HIER und nicht in der Schleife weiter unten: dort
|
||||||
# sitzt jedes Teil schon auf seinem eigenen Massenmittelpunkt, in
|
# sitzt jedes Teil schon auf seinem eigenen Massenmittelpunkt, in
|
||||||
# der Gesamtdatei stehen alle noch an ihrer Originalposition.
|
# der Gesamtdatei stehen alle noch an ihrer Originalposition.
|
||||||
|
# URSPRUNG auf die STAMMBASIS, wie beim Stand-Baum seit 1.36.
|
||||||
|
# Vorher trug die Datei den Szenen-Nullpunkt mit: ein Baum bei
|
||||||
|
# x = 18 m exportierte gemessen mit X 16.5..19.3, und die Spielseite
|
||||||
|
# musste beim Import nachzentrieren. Die EINZEL-Teile hatten das
|
||||||
|
# Problem nie - sie sitzen ohnehin auf ihrem Massenmittelpunkt.
|
||||||
|
#
|
||||||
|
# Der Versatz kommt aus DERSELBEN Funktion wie beim Stand-Baum, denn
|
||||||
|
# die zusammengesetzten Truemmer muessen genau auf dem Platz liegen,
|
||||||
|
# wo der Baum stand.
|
||||||
|
basis_versatz = _stammbasis_versatz(
|
||||||
|
[stamm_src.matrix_world @ c
|
||||||
|
for c in _stamm_koordinaten(stamm_src.data)])
|
||||||
|
for t in teile:
|
||||||
|
t.data.transform(basis_versatz)
|
||||||
|
|
||||||
hullen = []
|
hullen = []
|
||||||
if s.piece_ucx:
|
if s.piece_ucx:
|
||||||
for t in teile:
|
for t in teile:
|
||||||
@@ -3151,6 +3178,13 @@ class TREEGEN_OT_export_pieces(Operator):
|
|||||||
bpy.data.objects.remove(h, do_unlink=True)
|
bpy.data.objects.remove(h, do_unlink=True)
|
||||||
if hm.users == 0:
|
if hm.users == 0:
|
||||||
bpy.data.meshes.remove(hm)
|
bpy.data.meshes.remove(hm)
|
||||||
|
# Versatz zurueck. Fuer die Einzeldateien ist er egal - die setzen
|
||||||
|
# ihren Pivot ohnehin auf den Massenmittelpunkt -, aber mit "Teile
|
||||||
|
# behalten" staenden sie sonst verschoben neben dem Baum in der
|
||||||
|
# Szene.
|
||||||
|
zurueck = basis_versatz.inverted()
|
||||||
|
for t in teile:
|
||||||
|
t.data.transform(zurueck)
|
||||||
|
|
||||||
# --- Je Teil eine Datei, Pivot = Massenmittelpunkt --------------
|
# --- Je Teil eine Datei, Pivot = Massenmittelpunkt --------------
|
||||||
geschrieben = 0
|
geschrieben = 0
|
||||||
|
|||||||
@@ -58,12 +58,19 @@ bpy.ops.object.treegen_create()
|
|||||||
baum = [o for o in bpy.context.scene.objects
|
baum = [o for o in bpy.context.scene.objects
|
||||||
if o.type == 'MESH' and o.name.startswith("Eiche_01")
|
if o.type == 'MESH' and o.name.startswith("Eiche_01")
|
||||||
and not o.name.endswith(("_Leaf", "_Frucht"))][0]
|
and not o.name.endswith(("_Leaf", "_Frucht"))][0]
|
||||||
|
# Der Baum steht BEWUSST nicht am Ursprung. Die Gesamtdatei trug den
|
||||||
|
# Szenen-Nullpunkt mit (gemessen X 16.5..19.3 bei einem Baum auf x = 18), und
|
||||||
|
# bei (0,0,0) faellt genau das nicht auf.
|
||||||
|
STANDORT = (18.0, -7.5, 0.0)
|
||||||
|
baum.location = STANDORT
|
||||||
for o in bpy.context.scene.objects:
|
for o in bpy.context.scene.objects:
|
||||||
o.select_set(o is baum)
|
o.select_set(o is baum)
|
||||||
bpy.context.view_layer.objects.active = baum
|
bpy.context.view_layer.objects.active = baum
|
||||||
s.leaf_card = card
|
s.leaf_card = card
|
||||||
bpy.ops.object.treegen_leaves()
|
bpy.ops.object.treegen_leaves()
|
||||||
blatt = bpy.data.objects.get(baum.name + "_Leaf")
|
blatt = bpy.data.objects.get(baum.name + "_Leaf")
|
||||||
|
if blatt is not None:
|
||||||
|
blatt.location = STANDORT
|
||||||
|
|
||||||
# Referenz: wie viele Blatt-Faces hat der Stand-Baum?
|
# Referenz: wie viele Blatt-Faces hat der Stand-Baum?
|
||||||
dg = bpy.context.evaluated_depsgraph_get()
|
dg = bpy.context.evaluated_depsgraph_get()
|
||||||
@@ -236,6 +243,26 @@ if gesamt:
|
|||||||
fails.append("Gesamtdatei nur %.2f m hoch - die Teile stehen nicht an "
|
fails.append("Gesamtdatei nur %.2f m hoch - die Teile stehen nicht an "
|
||||||
"ihrer Originalposition" % hoehe)
|
"ihrer Originalposition" % hoehe)
|
||||||
|
|
||||||
|
# URSPRUNG auf der Stammbasis, wie beim Stand-Baum. Geprueft ueber den
|
||||||
|
# STAMMQUERSCHNITT, nicht ueber die Bbox-Mitte: die Krone haengt einseitig,
|
||||||
|
# und eine Bbox-Mitte wuerde den Fehler verwischen. Gesucht wird auf
|
||||||
|
# Fusshoehe ein Ring von Vertices, der die Achse umschliesst.
|
||||||
|
stamm_teile = [o for o in teile if "_Piece_Stamm" in o.name]
|
||||||
|
if stamm_teile:
|
||||||
|
sco = [o.matrix_world @ v.co for o in stamm_teile
|
||||||
|
for v in o.data.vertices]
|
||||||
|
zmin = min(c.z for c in sco)
|
||||||
|
band = [c for c in sco if c.z <= zmin + hoehe * 0.03]
|
||||||
|
mx = sum(c.x for c in band) / len(band)
|
||||||
|
my = sum(c.y for c in band) / len(band)
|
||||||
|
print("Gesamtdatei: Stammfuss bei %.3f / %.3f / %.3f" % (mx, my, zmin))
|
||||||
|
if math.hypot(mx, my) > hoehe * 0.02:
|
||||||
|
fails.append("Gesamtdatei: Stammfuss liegt bei %.2f/%.2f statt "
|
||||||
|
"ueber dem Ursprung - die Datei traegt den "
|
||||||
|
"Szenen-Nullpunkt mit" % (mx, my))
|
||||||
|
if abs(zmin) > hoehe * 0.02:
|
||||||
|
fails.append("Gesamtdatei: Stammfuss bei Z %.3f statt 0" % zmin)
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
if fails:
|
if fails:
|
||||||
print("ERGEBNIS: %d FEHLER" % len(fails))
|
print("ERGEBNIS: %d FEHLER" % len(fails))
|
||||||
|
|||||||
Reference in New Issue
Block a user