diff --git a/README.md b/README.md index 7798fbc..8353933 100644 --- a/README.md +++ b/README.md @@ -563,6 +563,36 @@ 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 einer leeren `_Leaf`-Ebene; jetzt sagt der Operator es und nennt den Regler. +### UCX bei LOD-Gruppen: der Name muss auf den MESH-Node zeigen + +Der Legacy-Importer von Unreal matcht `UCX_` - **nicht** gegen das +LodGroup-Empty. Eine Huelle mit dem Gruppennamen liegt zwar in der Datei und ein +Reimport nach Blender zeigt sie brav an, aber Unreal verwirft sie STILL: +gemeldet von der Spielseite mit `convex = 0` an 108 Stand-Baeumen und 57 +Fels-Meshes. Beweis von dort: dieselbe Datei, Huelle umbenannt auf den +LOD0-Node, liefert `convex = 1`. + +`UCX_` ist nur OHNE LOD-Kette richtig, weil der Mesh-Node dann genau +so heisst. Deshalb funktionierten die Faell-Teile und `_Pieces_All.fbx` die +ganze Zeit - die haben keine LODs. + +**Wie das durchrutschen konnte, ist die eigentliche Lehre.** Fuer 1.42 habe ich +die Konvention nicht geraten, sondern am Fels-Export nachgemessen: exportieren, +reimportieren, Namen und Hierarchie pruefen. Das war richtig - und hat trotzdem +nichts bewiesen, weil der Fels-Export denselben Fehler hatte. Gemessen wurde die +STRUKTUR ("liegt die Huelle in der Datei, heisst sie wie erwartet, haengt sie +richtig?"), nicht die WIRKUNG ("nimmt Unreal sie an?"). Eine abgeschaute +Konvention ist nur so gut wie die Quelle, von der sie stammt. + +Die Tests pruefen deshalb jetzt die REGEL statt des Namens: hinter `UCX_` muss +ein Mesh-Node aus derselben Datei stehen, und zwar LOD0. Das haette den Fehler +in beiden Addons gefunden, ohne dass jemand die Antwort vorher kennt. +Gegenproben: Baum 9 Fehler, Fels 1 Fehler. + +Was nur die Engine beantworten kann - ob `convex` nach dem Import wirklich +groesser 0 ist - bleibt bei der Spielseite. Der Test kann nur sicherstellen, +dass die Datei die Bedingung erfuellt, an der es lag. + ### Die Gesamtdatei steht jetzt auch auf der Stammbasis `_Pieces_All.fbx` trug den SZENEN-Nullpunkt mit: ein Baum bei x = 18 m diff --git a/blender_manifest.toml b/blender_manifest.toml index 3d6c709..998aabf 100644 --- a/blender_manifest.toml +++ b/blender_manifest.toml @@ -1,7 +1,7 @@ schema_version = "1.0.0" id = "stylized_rock_generator" -version = "2.19.0" +version = "2.20.0" name = "Stylized Rock Generator" tagline = "Batch-Generator fuer stylized Rocks mit UE5-Export" maintainer = "D4rkst3r" diff --git a/blender_manifest_tree.toml b/blender_manifest_tree.toml index 008ecd7..95dd365 100644 --- a/blender_manifest_tree.toml +++ b/blender_manifest_tree.toml @@ -1,7 +1,7 @@ schema_version = "1.0.0" id = "stylized_tree_generator" -version = "1.44.0" +version = "1.45.0" name = "Stylized Tree Generator" tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen" maintainer = "D4rkst3r" diff --git a/dist/index.html b/dist/index.html index e6ce996..e988979 100644 --- a/dist/index.html +++ b/dist/index.html @@ -40,27 +40,27 @@ 4.9KB - stylized_rock_generator-2.19.0 + stylized_rock_generator-2.20.0 Stylized Rock Generator Batch-Generator fuer stylized Rocks mit UE5-Export link 4.2.0 - ~ all all - 34.1KB + 34.5KB - stylized_tree_generator-1.44.0 + stylized_tree_generator-1.45.0 Stylized Tree Generator Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen link 4.2.0 - ~ all all - 49.5KB + 49.9KB -

Built 2026-08-07, 04:59

+

Built 2026-08-07, 05:18

diff --git a/dist/index.json b/dist/index.json index 9f9af94..ccc6576 100644 --- a/dist/index.json +++ b/dist/index.json @@ -57,7 +57,7 @@ "id": "stylized_rock_generator", "name": "Stylized Rock Generator", "tagline": "Batch-Generator fuer stylized Rocks mit UE5-Export", - "version": "2.19.0", + "version": "2.20.0", "type": "add-on", "maintainer": "D4rkst3r", "license": [ @@ -73,16 +73,16 @@ "Mesh", "Modeling" ], - "archive_url": "./stylized_rock_generator-2.19.0.zip", - "archive_size": 34968, - "archive_hash": "sha256:2b412f9324263a3951b1b519648ab46025e7511b09f54b6017edf6d834a17bc6" + "archive_url": "./stylized_rock_generator-2.20.0.zip", + "archive_size": 35362, + "archive_hash": "sha256:f88fa99b30bef7ae4ac1319e64e2374166dc8c37f2d42dc72352b15fbffdaae0" }, { "schema_version": "1.0.0", "id": "stylized_tree_generator", "name": "Stylized Tree Generator", "tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen", - "version": "1.44.0", + "version": "1.45.0", "type": "add-on", "maintainer": "D4rkst3r", "license": [ @@ -98,9 +98,9 @@ "Mesh", "Modeling" ], - "archive_url": "./stylized_tree_generator-1.44.0.zip", - "archive_size": 50734, - "archive_hash": "sha256:e10cf2efafc751b2e8b7cf6e15a793408f43ffff8b1dc0e98e43ad8ba809659a" + "archive_url": "./stylized_tree_generator-1.45.0.zip", + "archive_size": 51048, + "archive_hash": "sha256:c01f5c4e4b25e35ca8ffbe3b7266306bebe718f533784ffa490f581c373bba0b" } ] } \ No newline at end of file diff --git a/dist/stylized_rock_generator-2.19.0.zip b/dist/stylized_rock_generator-2.19.0.zip deleted file mode 100644 index 4fd493f..0000000 Binary files a/dist/stylized_rock_generator-2.19.0.zip and /dev/null differ diff --git a/dist/stylized_rock_generator-2.20.0.zip b/dist/stylized_rock_generator-2.20.0.zip new file mode 100644 index 0000000..cf842e6 Binary files /dev/null and b/dist/stylized_rock_generator-2.20.0.zip differ diff --git a/dist/stylized_tree_generator-1.44.0.zip b/dist/stylized_tree_generator-1.44.0.zip deleted file mode 100644 index 13db121..0000000 Binary files a/dist/stylized_tree_generator-1.44.0.zip and /dev/null differ diff --git a/dist/stylized_tree_generator-1.45.0.zip b/dist/stylized_tree_generator-1.45.0.zip new file mode 100644 index 0000000..e2662df Binary files /dev/null and b/dist/stylized_tree_generator-1.45.0.zip differ diff --git a/stylized_rock_generator.py b/stylized_rock_generator.py index 86c65b3..4031dc3 100644 --- a/stylized_rock_generator.py +++ b/stylized_rock_generator.py @@ -1,7 +1,7 @@ bl_info = { "name": "Stylized Rock Generator", "author": "D4rkst3r", - "version": (2, 19, 0), + "version": (2, 20, 0), "blender": (4, 2, 0), "location": "View3D > Sidebar > Rock Gen", "description": ( @@ -1695,10 +1695,25 @@ def _prepare_export_copies(context, top, lay_flat=True, want_ucx=True, for o in meshes: o.data.transform(off) - # 3) UCX. Bei einer LOD-Gruppe darf die Huelle NICHT unter das LodGroup-Empty: - # Unreal wuerde jedes Kind als LOD-Stufe lesen. Also als Geschwister. + # 3) UCX. Zwei Regeln, beide teuer gelernt: + # + # a) Bei einer LOD-Gruppe darf die Huelle NICHT unter das LodGroup-Empty: + # Unreal wuerde jedes Kind als LOD-Stufe lesen. Also als Geschwister. + # b) Der Name muss auf den MESH-Node zeigen, nicht auf das Empty. Der + # Legacy-Importer matcht `UCX_`; hiess die Huelle nach der + # LodGroup, lag sie zwar in der Datei, wurde aber STILL verworfen - + # gemeldet von der Spielseite mit convex = 0 an 57 Fels-Meshes. + # `UCX_` stimmt nur OHNE LOD-Kette, weil dort der Mesh-Node + # genau so heisst. Ein Reimport nach Blender zeigt die Huelle in + # beiden Faellen - er beweist nur, dass sie in der Datei liegt, nicht + # dass Unreal sie annimmt. + # + # `referenz` ist LOD0 (hoechste Vertexzahl) - derselbe Bezug, den oben + # schon der Ursprung benutzt. Ohne LOD-Kette ist es das Mesh selbst, dann + # faellt der Name mit export_name zusammen. if want_ucx and meshes: - ucx = _make_ucx(context, [meshes[0]], export_name, + ucx_ziel = referenz if referenz is not None else meshes[0] + ucx = _make_ucx(context, [ucx_ziel], ucx_ziel.name, max_verts=ucx_max_verts) if ucx is not None: made.append(ucx) diff --git a/stylized_tree_generator.py b/stylized_tree_generator.py index 5906d3b..4f46103 100644 --- a/stylized_tree_generator.py +++ b/stylized_tree_generator.py @@ -1,7 +1,7 @@ bl_info = { "name": "Stylized Tree Generator", "author": "D4rkst3r", - "version": (1, 44, 0), + "version": (1, 45, 0), "blender": (4, 2, 0), "location": "View3D > Sidebar > Tree Gen", "description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen", @@ -2943,8 +2943,20 @@ class TREEGEN_OT_export_game(Operator): # und bei LOD1-3 zusaetzlich dezimiert. Sie durchlaufen dieselbe # Kette wie die Stufen: eigene Weltmatrix, dann die gemeinsame # Zentrierung `versatz`. - stamm_ucx_name = (basis_name + "_LODGroup") if s.generate_lods \ - else basis_name + # + # NAME nach dem MESH-Node (LOD0), nicht nach dem LodGroup-Empty. + # Der Legacy-Importer matcht `UCX_`; mit dem + # LodGroup-Namen lag die Huelle zwar in der Datei, wurde von + # Unreal aber STILL verworfen - gemeldet mit convex = 0 an 108 + # Stand-Baeumen. + # + # 1.42 hatte die Konvention vom Fels-Export abgeschaut und dabei + # nur die STRUKTUR nachgemessen (Reimport nach Blender: Huelle + # da, kein Elternteil, richtiger Name). Dass Unreal sie ANNIMMT, + # war ungeprueft - und genau daran lag es. Ohne LOD-Kette heisst + # der Mesh-Node wie die Basis; deshalb funktionierten die + # Einzel-Teile und Pieces_All die ganze Zeit. + stamm_ucx_name = lods[0].name hull = None if s.stand_ucx: pts = _stamm_punkte(baum, baum.matrix_world) diff --git a/tests/test_export_robust.py b/tests/test_export_robust.py index 6745731..89cff34 100644 --- a/tests/test_export_robust.py +++ b/tests/test_export_robust.py @@ -226,6 +226,24 @@ else: elif hull[0].parent is not None and hull[0].parent.type == 'EMPTY': # Unter dem LodGroup-Empty wuerde Unreal die Huelle als LOD-Stufe lesen. fails.append("LOD: UCX haengt unter dem LodGroup-Empty") + if hull and stufen: + # DER Punkt, der lange gefehlt hat: der Legacy-Importer matcht + # `UCX_`, nicht das LodGroup-Empty. Eine Huelle mit dem + # Gruppennamen liegt zwar in der Datei - dieser Test hat sie brav + # gefunden und fuer gut befunden -, wird von Unreal aber STILL + # verworfen. Gemeldet von der Spielseite: convex = 0 an 57 Fels-Meshes. + # + # Struktur pruefen reicht nicht. Geprueft wird deshalb die REGEL: hinter + # "UCX_" muss ein Mesh-Node aus derselben Datei stehen, und zwar LOD0. + ziel = hull[0].name[4:] + namen = {o.name for o in stufen} + if ziel not in namen: + fails.append("LOD: Huelle heisst '%s' - '%s' ist kein Mesh-Node " + "(vorhanden: %s). Unreal verwirft sie still." + % (hull[0].name, ziel, sorted(namen))) + elif ziel != stufen[0].name: + fails.append("LOD: Huelle zeigt auf '%s', LOD0 heisst '%s'" + % (ziel, stufen[0].name)) if stufen: co = [stufen[0].matrix_world @ v.co for v in stufen[0].data.vertices] lo = [min(c[i] for c in co) for i in range(3)] diff --git a/tests/test_stamm_ucx.py b/tests/test_stamm_ucx.py index a82cf60..841ba67 100644 --- a/tests/test_stamm_ucx.py +++ b/tests/test_stamm_ucx.py @@ -6,9 +6,14 @@ Spieler, bevor er den Baum ueberhaupt beruehrt. Die Namens- und Hierarchie-Konvention ist nicht geraten, sondern vom Fels-Export abgeschaut, der in der Spiel-Pipeline bereits abgenommen ist: -`UCX_`, und zwar als GESCHWISTER der LodGroup, nicht als Kind - +`UCX_`, und zwar als GESCHWISTER der LodGroup, nicht als Kind - Unreal liest jedes Kind der Gruppe als LOD-Stufe. +WICHTIG, weil es genau hier schiefging: der Legacy-Importer matcht gegen den +MESH-Node, NICHT gegen das LodGroup-Empty. Eine Huelle mit dem LodGroup-Namen +liegt zwar in der Datei und ein Reimport nach Blender zeigt sie brav an - Unreal +verwirft sie aber still (convex = 0). Struktur pruefen reicht nicht. + Gemessen wird im reimportierten FBX. Aufruf: blender --background --factory-startup --python tests/test_stamm_ucx.py @@ -132,12 +137,33 @@ for preset, mit_lods, pfad, stamm_r, ansatz, blender_h, max_verts in ergebnisse: continue u = ucx[0] - # Name: exakt die Konvention des Fels-Exports. - erwartet = ("UCX_" + gruppe[0].name) if gruppe else ("UCX_" + rest[0].name) - if u.name != erwartet: - fails.append("%s: Huelle heisst '%s', erwartet '%s' - Unreal ordnet " - "die Kollision ueber den Namen zu" - % (preset, u.name, erwartet)) + # NAME: er muss auf einen MESH-Node zeigen, und zwar auf LOD0. + # + # Die erste Fassung dieses Tests prueft genau das NICHT scharf genug: sie + # verglich gegen den LodGroup-Namen, weil der Fels-Export es so machte. + # Struktur und Name waren damit stimmig - Unreal hat die Huelle trotzdem + # still verworfen (convex = 0 an 108 Baeumen), weil der Legacy-Importer + # gegen den MESH-Node matcht. Ein Reimport nach Blender beweist nur, dass + # die Huelle in der Datei liegt, nicht dass sie angenommen wird. + # + # Deshalb steht hier jetzt die Regel und nicht der Name: der Rest hinter + # "UCX_" muss ein Mesh im FBX sein. Und der Vollstaendigkeit halber: er darf + # NICHT das Empty sein. + ziel = u.name[4:] + mesh_namen = {o.name for o in rest} + if ziel not in mesh_namen: + fails.append("%s: Huelle heisst '%s' - '%s' ist kein Mesh-Node im FBX " + "(vorhanden: %s). Unreal verwirft sie dann still." + % (preset, u.name, ziel, sorted(mesh_namen))) + if gruppe and ziel == gruppe[0].name: + fails.append("%s: Huelle zeigt auf das LodGroup-Empty '%s' statt auf " + "einen Mesh-Node" % (preset, ziel)) + # Und zwar auf LOD0, nicht auf irgendeine Stufe: Unreal haengt die + # Kollision an das Render-Mesh der obersten Stufe. + lod0 = max(rest, key=lambda o: len(o.data.vertices)) + if ziel != lod0.name: + fails.append("%s: Huelle zeigt auf '%s', LOD0 heisst aber '%s'" + % (preset, ziel, lod0.name)) # Hierarchie: NICHT unter der LodGroup, sonst waere sie eine LOD-Stufe. if u.parent is not None: