Zufalls-Funktion in beiden Addons + Zusatz-Ebenen + Form-statt-Groesse
Rock Generator 2.5.0: - "Zufaellig"-Button: wuerfelt Seed und Form-Parameter, "Streuung" steuert die Abweichung von den aktuellen Werten (0 = nur neuer Seed). Optional werden auch Basis-Form und Noise-Typ gewuerfelt. Werte werden auf ihre Grenzen geklemmt. - Skalierung streut jetzt die FORM, nicht die Groesse: nicht-uniform ist Default, und "Gesamtgroesse konstant halten" normiert das geometrische Mittel auf 1. Gemessen ueber 6 Rocks: Volumen-Spanne 1.92x -> 1.11x. Wichtig, weil EcoGame die Groesse zusaetzlich ueber ScaleMin/Max streut. Tree Generator 1.19.0: - "Zufaellig"-Button: neuer Seed plus Streuung auf Hoehe, Radius, Biegung, Ast-Anzahl/-Laenge, Kronenform und Wurzelanlauf. Preset-Charakter bleibt, weil relativ zu den aktuellen Werten gestreut wird (gemessen: Hoehe 6.65 -> 8.34 m). - "_Leaf"- und "_Frucht"-Ebenen: leere Objekte mit IDENTISCHEM Ursprung neben dem Baum (gemessen: Abstand 0.000000), damit die Ebenen in UE deckungsgleich sitzen. Vier Test-Suiten liegen jetzt unter tests/ statt nur im Scratchpad. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
bl_info = {
|
||||
"name": "Stylized Tree Generator",
|
||||
"author": "D4rkst3r",
|
||||
"version": (1, 18, 0),
|
||||
"version": (1, 19, 0),
|
||||
"blender": (4, 2, 0),
|
||||
"location": "View3D > Sidebar > Tree Gen",
|
||||
"description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen",
|
||||
@@ -992,6 +992,21 @@ class TreeGenSettings(PropertyGroup):
|
||||
description="Laufende Variante derselben Art (Birke_01, Birke_02 ...)",
|
||||
)
|
||||
spacing: FloatProperty(name="Abstand", default=4.0, min=0.0, max=20.0)
|
||||
random_amount: FloatProperty(
|
||||
name="Streuung", default=0.3, min=0.0, max=1.0,
|
||||
description=("Wie stark 'Zufaellig' die Werte veraendert. 0 = nur neuer Seed, "
|
||||
"1 = volle Bandbreite. Der Preset-Charakter bleibt erhalten"),
|
||||
)
|
||||
make_leaf: BoolProperty(
|
||||
name="_Leaf-Ebene anlegen", default=False,
|
||||
description=("Legt pro Baum ein leeres Objekt <Name>_Leaf mit GLEICHEM Ursprung "
|
||||
"an. Dort modellierst du die Krone - in UE sitzen beide Ebenen "
|
||||
"dadurch deckungsgleich"),
|
||||
)
|
||||
make_frucht: BoolProperty(
|
||||
name="_Frucht-Ebene anlegen", default=False,
|
||||
description="Zusaetzlich <Name>_Frucht (gleicher Ursprung) fuer die Frucht-Ebene",
|
||||
)
|
||||
apply_modifier: BoolProperty(
|
||||
name="Modifier anwenden", default=False,
|
||||
description="Ergebnis als normales Mesh einfrieren (fuer den Export)",
|
||||
@@ -1038,6 +1053,45 @@ def _add_lightmap_uv(context, ob, op):
|
||||
ob.data.uv_layers[0].active_render = True
|
||||
|
||||
|
||||
def _add_layer(context, ob, suffix):
|
||||
"""Leeres Mesh-Objekt mit IDENTISCHEM Ursprung neben dem Baum anlegen.
|
||||
|
||||
Gleicher Ursprung ist der Punkt: in UE muessen Stamm, Krone und Fruechte
|
||||
deckungsgleich sitzen, sonst verrutscht die Krone gegenueber dem Stamm.
|
||||
"""
|
||||
name = ob.name + suffix
|
||||
old = bpy.data.objects.get(name)
|
||||
if old:
|
||||
bpy.data.objects.remove(old, do_unlink=True)
|
||||
me = bpy.data.meshes.new(name)
|
||||
new = bpy.data.objects.new(name, me)
|
||||
context.collection.objects.link(new)
|
||||
new.location = ob.location.copy()
|
||||
new.rotation_euler = ob.rotation_euler.copy()
|
||||
new.scale = ob.scale.copy()
|
||||
return new
|
||||
|
||||
|
||||
class TREEGEN_OT_randomize(Operator):
|
||||
bl_idname = "object.treegen_randomize"
|
||||
bl_label = "Zufaellig"
|
||||
bl_description = ("Wuerfelt Seed und Form-Parameter neu, ohne den Preset-Charakter "
|
||||
"zu verlieren. 'Streuung' bestimmt die Abweichung")
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
s = context.scene.tree_gen_settings
|
||||
import random as _r
|
||||
rng = _r.Random()
|
||||
s.seed = rng.randint(0, 9999)
|
||||
# Die Form-Parameter liegen am Modifier, nicht an den Settings. Deshalb
|
||||
# merken wir uns die Streuung und wenden sie beim Erzeugen an.
|
||||
s["_rand_jitter"] = s.random_amount
|
||||
self.report({'INFO'}, "Neuer Seed %d, Streuung %.0f%%."
|
||||
% (s.seed, s.random_amount * 100))
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class TREEGEN_OT_create(Operator):
|
||||
bl_idname = "object.treegen_create"
|
||||
bl_label = "Baum erzeugen"
|
||||
@@ -1075,6 +1129,30 @@ class TREEGEN_OT_create(Operator):
|
||||
self.report({'ERROR'}, "Erzeugen fehlgeschlagen: %s" % exc)
|
||||
return {'CANCELLED'}
|
||||
|
||||
# Streuung auf die Form-Regler anwenden (Preset-Charakter bleibt)
|
||||
amt = float(s.get("_rand_jitter", 0.0))
|
||||
if amt > 0.0:
|
||||
import random as _r
|
||||
rng = _r.Random(s.seed)
|
||||
ids = socket_ids(ng)
|
||||
JITTER = {"Height": 0.35, "Trunk Radius": 0.25, "Bend": 0.5,
|
||||
"Branch Count": 0.3, "Branch Length": 0.35,
|
||||
"Branch Up": 0.3, "Branch Bend": 0.4,
|
||||
"Crown Taper": 0.3, "Crown Bulge": 0.3,
|
||||
"Root Flare": 0.4, "Root Lobes": 0.3}
|
||||
for ob in created:
|
||||
md = ob.modifiers.get("GN_Tree")
|
||||
if md is None:
|
||||
continue
|
||||
for key, rel in JITTER.items():
|
||||
sid = ids.get(key)
|
||||
if sid is None:
|
||||
continue
|
||||
cur = md[sid]
|
||||
f = 1.0 + rng.uniform(-rel, rel) * amt
|
||||
md[sid] = type(cur)(round(cur * f)) if isinstance(cur, int) else cur * f
|
||||
context.view_layer.update()
|
||||
|
||||
if s.apply_modifier:
|
||||
for ob in created:
|
||||
context.view_layer.objects.active = ob
|
||||
@@ -1087,6 +1165,15 @@ class TREEGEN_OT_create(Operator):
|
||||
if s.generate_lightmap_uv:
|
||||
_add_lightmap_uv(context, ob, self)
|
||||
|
||||
if s.make_leaf or s.make_frucht:
|
||||
extra = []
|
||||
for ob in created:
|
||||
if s.make_leaf:
|
||||
extra.append(_add_layer(context, ob, "_Leaf"))
|
||||
if s.make_frucht:
|
||||
extra.append(_add_layer(context, ob, "_Frucht"))
|
||||
created.extend(extra)
|
||||
|
||||
context.view_layer.update()
|
||||
total = 0
|
||||
dg = context.evaluated_depsgraph_get()
|
||||
@@ -1129,6 +1216,16 @@ class VIEW3D_PT_tree_generator(Panel):
|
||||
sub.prop(s, "stages")
|
||||
sub.prop(s, "spacing")
|
||||
|
||||
box = layout.box()
|
||||
box.label(text="Zufall", icon='FILE_REFRESH')
|
||||
box.prop(s, "random_amount")
|
||||
box.operator("object.treegen_randomize", icon='FILE_REFRESH')
|
||||
|
||||
box = layout.box()
|
||||
box.label(text="Zusatz-Ebenen")
|
||||
box.prop(s, "make_leaf")
|
||||
box.prop(s, "make_frucht")
|
||||
|
||||
box = layout.box()
|
||||
box.label(text="Export")
|
||||
box.prop(s, "apply_modifier")
|
||||
@@ -1143,6 +1240,7 @@ class VIEW3D_PT_tree_generator(Panel):
|
||||
|
||||
classes = (
|
||||
TreeGenSettings,
|
||||
TREEGEN_OT_randomize,
|
||||
TREEGEN_OT_create,
|
||||
VIEW3D_PT_tree_generator,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user