diff --git a/blender_manifest_tree.toml b/blender_manifest_tree.toml index 9e7187d..8d72878 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.6.0" +version = "1.7.0" name = "Stylized Tree Generator" tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen" maintainer = "D4rkst3r" diff --git a/dist/index.json b/dist/index.json index e593669..ecb43ac 100644 --- a/dist/index.json +++ b/dist/index.json @@ -32,7 +32,7 @@ "id": "stylized_tree_generator", "name": "Stylized Tree Generator", "tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen", - "version": "1.6.0", + "version": "1.7.0", "type": "add-on", "maintainer": "D4rkst3r", "license": [ @@ -48,9 +48,9 @@ "Mesh", "Modeling" ], - "archive_url": "./stylized_tree_generator-1.6.0.zip", - "archive_size": 12916, - "archive_hash": "sha256:48d7072d65d44c944fe7dd160147f37f496132d6c2583c5251f0d6aba499476b" + "archive_url": "./stylized_tree_generator-1.7.0.zip", + "archive_size": 13398, + "archive_hash": "sha256:3817ce7433ef63918ddf75a827591b3bb712c8659141ae85f73f4f1fd19e20fb" } ] } \ No newline at end of file diff --git a/dist/stylized_tree_generator-1.6.0.zip b/dist/stylized_tree_generator-1.6.0.zip deleted file mode 100644 index bd362ce..0000000 Binary files a/dist/stylized_tree_generator-1.6.0.zip and /dev/null differ diff --git a/dist/stylized_tree_generator-1.7.0.zip b/dist/stylized_tree_generator-1.7.0.zip new file mode 100644 index 0000000..197f40d Binary files /dev/null and b/dist/stylized_tree_generator-1.7.0.zip differ diff --git a/stylized_tree_generator.py b/stylized_tree_generator.py index ea2e5c2..a913bbf 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, 6, 0), + "version": (1, 7, 0), "blender": (4, 2, 0), "location": "View3D > Sidebar > Tree Gen", "description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen", @@ -40,12 +40,14 @@ DEFAULTS = { "Branch Droop": 0.0, # >0 haengt nach unten (Palme), <0 kruemmt nach oben (Kaktus) "Branch Thickness": 0.32, # Ast-Dicke relativ zum Stamm "Branch Taper": 0.55, # schwach verjuengen -> Roehre statt Kegel/Dorn - "Sub Count": 3, # Sub-Aeste je Hauptast (0 = aus) + "Sub Count": 2, # Sub-Aeste je Hauptast (0 = aus) "Sub Length": 0.55, "Sub Up": 1.4, - "Sides": 6, + "Sides": 5, "UV Scale": 1.0, # Rinden-Dichte; UVs sind world-space (m) "Tip Blunt": 0.18, # >0 verhindert Nadelspitzen (0 = spitz, 0.45 = Kaktus) + "Root Flare": 1.1, # Wurzelanlauf: Stamm verbreitert sich am Fuss + "Detail": 0.55, # skaliert alle Resample-Counts = Tri-Budget-Regler "Branch Collar": 0.4, # Ansatz-Verdickung: Uebergang Ast->Stamm (0 = aus) "Merge": 0.0, # 1 = Aeste per Voxel-Remesh mit dem Stamm verschmelzen "Merge Voxel": 0.03, # Voxelgroesse (m): klein = feiner, aber teuer @@ -69,7 +71,8 @@ PRESETS = { "Branch Count": 14, "Branch Length": 0.9, "Branch Up": 1.0, "Branch Start": 0.15, "Branch End": 0.95, "Branch Bend": 0.6, "Branch Droop": 0.0, - "Sub Count": 2, "Sub Length": 0.35, "Sub Up": 1.6, "Sides": 5, + "Sub Count": 1, "Sub Length": 0.35, "Sub Up": 1.6, "Sides": 4, + "Detail": 0.3, "Branch Count": 10, }, # Kaktus: dicker, kaum verjuengter Stamm, wenige Arme, die per NEGATIVEM # Droop nach oben kruemmen (Saguaro-Silhouette). Keine Sub-Aeste. @@ -112,6 +115,18 @@ def build_group(): N, L = ng.nodes.new, ng.links.new iface = ng.interface + def _count(base, x, y): + """Resample-Count = base * Detail, mindestens 3. Der zentrale + Tri-Budget-Regler: die Segmentzahl entlang der Kurven dominiert die + Dreiecke (Tris ~ Segmente * Sides * 2).""" + mul = N("ShaderNodeMath"); mul.location = (x, y); mul.operation = 'MULTIPLY' + mul.inputs[0].default_value = float(base) + L(V["Detail"], mul.inputs[1]) + mx = N("ShaderNodeMath"); mx.location = (x + 150, y); mx.operation = 'MAXIMUM' + mx.inputs[1].default_value = 3.0 + L(mul.outputs[0], mx.inputs[0]) + return mx + def _new_any(*bl_idnames): """Falle 2: erster Node-Typ, den diese Blender-Version kennt.""" for bid in bl_idnames: @@ -153,6 +168,8 @@ def build_group(): add_in("Sides", 'NodeSocketInt', DEFAULTS["Sides"], 3, 32) add_in("Tip Blunt", 'NodeSocketFloat', DEFAULTS["Tip Blunt"], 0.0, 0.9) add_in("UV Scale", 'NodeSocketFloat', DEFAULTS["UV Scale"], 0.01, 20.0) + add_in("Detail", 'NodeSocketFloat', DEFAULTS["Detail"], 0.2, 2.0) + add_in("Root Flare", 'NodeSocketFloat', DEFAULTS["Root Flare"], 0.0, 4.0) add_in("Branch Collar", 'NodeSocketFloat', DEFAULTS["Branch Collar"], 0.0, 3.0) add_in("Merge", 'NodeSocketFloat', DEFAULTS["Merge"], 0.0, 1.0) add_in("Merge Voxel", 'NodeSocketFloat', DEFAULTS["Merge Voxel"], 0.005, 0.2) @@ -170,7 +187,7 @@ def build_group(): L(top.outputs[0], _sock(line, "End")) res = N("GeometryNodeResampleCurve"); res.location = (-950, 200) - _sock(res, "Count").default_value = 24 # genug Punkte, damit die Kuppel rund wird + L(_count(24, -1150, 380).outputs[0], _sock(res, "Count")) L(_out(line, "Curve"), _sock(res, "Curve")) spar = N("GeometryNodeSplineParameter"); spar.location = (-950, -60) @@ -270,8 +287,25 @@ def build_group(): L(inv.outputs[0], tmulr.inputs[0]); L(tdome.outputs[0], tmulr.inputs[1]) tmaxr = N("ShaderNodeMath"); tmaxr.location = (-110, 60); tmaxr.operation = 'MAXIMUM' L(tmulr.outputs[0], tmaxr.inputs[0]); L(ttip.outputs[0], tmaxr.inputs[1]) - trad = N("ShaderNodeMath"); trad.location = (40, 60); trad.operation = 'MULTIPLY' - L(V["Trunk Radius"], trad.inputs[0]); L(tmaxr.outputs[0], trad.inputs[1]) + + # Wurzelanlauf: der Stamm verbreitert sich am Fuss. Hoher Exponent -> nur + # das unterste Stueck ist betroffen. Kostet keine Tris, nur Radius. + rfi = N("ShaderNodeMath"); rfi.location = (-420, -220); rfi.operation = 'SUBTRACT' + rfi.inputs[0].default_value = 1.0 + L(spar.outputs["Factor"], rfi.inputs[1]) + rfp = N("ShaderNodeMath"); rfp.location = (-270, -220); rfp.operation = 'POWER' + rfp.inputs[1].default_value = 9.0 + L(rfi.outputs[0], rfp.inputs[0]) + rfm = N("ShaderNodeMath"); rfm.location = (-120, -220); rfm.operation = 'MULTIPLY' + L(V["Root Flare"], rfm.inputs[0]); L(rfp.outputs[0], rfm.inputs[1]) + rfa = N("ShaderNodeMath"); rfa.location = (30, -220); rfa.operation = 'ADD' + rfa.inputs[0].default_value = 1.0 + L(rfm.outputs[0], rfa.inputs[1]) + tflare = N("ShaderNodeMath"); tflare.location = (30, -60); tflare.operation = 'MULTIPLY' + L(tmaxr.outputs[0], tflare.inputs[0]); L(rfa.outputs[0], tflare.inputs[1]) + + trad = N("ShaderNodeMath"); trad.location = (190, 60); trad.operation = 'MULTIPLY' + L(V["Trunk Radius"], trad.inputs[0]); L(tflare.outputs[0], trad.inputs[1]) setrad = N("GeometryNodeSetCurveRadius"); setrad.location = (-80, 200) L(_out(setpos, "Geometry"), _sock(setrad, "Curve")) @@ -301,7 +335,7 @@ def build_group(): L(V["Branch Length"], btop.inputs["Z"]) L(btop.outputs[0], _sock(bline, "End")) bres = N("GeometryNodeResampleCurve"); bres.location = (280, 40) - _sock(bres, "Count").default_value = 14 # zu wenig -> Kuppel wird zum Kegel + L(_count(14, 100, 200).outputs[0], _sock(bres, "Count")) L(_out(bline, "Curve"), _sock(bres, "Curve")) # Ast-Verjuengung: 1 - BranchTaper*factor. Bei Kakteen klein halten, @@ -453,7 +487,7 @@ def build_group(): L(V["Sub Length"], stop.inputs["Z"]) L(stop.outputs[0], _sock(sline, "End")) sres = N("GeometryNodeResampleCurve"); sres.location = (2250, 760) - _sock(sres, "Count").default_value = 9 # genug Punkte, damit die Biegung sichtbar wird + L(_count(9, 2100, 700).outputs[0], _sock(sres, "Count")) L(_out(sline, "Curve"), _sock(sres, "Curve")) # Sub-Aeste: gleiche Verjuengungs-Logik wie Hauptaeste (Branch Taper + Kuppel).