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 Rock Generator",
|
||||
"author": "D4rkst3r",
|
||||
"version": (2, 4, 0),
|
||||
"version": (2, 5, 0),
|
||||
"blender": (4, 2, 0),
|
||||
"location": "View3D > Sidebar > Rock Gen",
|
||||
"description": (
|
||||
@@ -51,7 +51,7 @@ PRESET_KEYS = (
|
||||
"batch_count", "spacing",
|
||||
"shading_mode", "autosmooth_angle",
|
||||
"randomize_shape", "noise_scale_max", "displace_strength_max", "nonuniform_scale",
|
||||
"flatten_bottom", "flatten_ratio", "set_origin_bottom",
|
||||
"flatten_bottom", "flatten_ratio", "set_origin_bottom", "keep_size",
|
||||
)
|
||||
|
||||
# Eingebaute Presets (nicht ueberschreibbar/loeschbar).
|
||||
@@ -410,9 +410,17 @@ def _apply_random_transform(context, obj, settings, rng):
|
||||
return
|
||||
lo, hi = sorted((settings.scale_min, settings.scale_max))
|
||||
if settings.nonuniform_scale:
|
||||
obj.scale = (rng.uniform(lo, hi), rng.uniform(lo, hi), rng.uniform(lo, hi))
|
||||
sx, sy, sz = (rng.uniform(lo, hi), rng.uniform(lo, hi), rng.uniform(lo, hi))
|
||||
if settings.keep_size:
|
||||
# Geometrisches Mittel auf 1.0 normieren: die Proportionen streuen,
|
||||
# das Volumen bleibt. Sonst multipliziert sich die Streuung mit den
|
||||
# ScaleMin/Max der Engine.
|
||||
g = (abs(sx) * abs(sy) * abs(sz)) ** (1.0 / 3.0)
|
||||
if g > 1e-6:
|
||||
sx, sy, sz = sx / g, sy / g, sz / g
|
||||
obj.scale = (sx, sy, sz)
|
||||
else:
|
||||
s = rng.uniform(lo, hi)
|
||||
s = 1.0 if settings.keep_size else rng.uniform(lo, hi)
|
||||
obj.scale = (s, s, s)
|
||||
obj.rotation_euler = (
|
||||
rng.uniform(0.0, TWO_PI),
|
||||
@@ -727,9 +735,16 @@ class RockGenSettings(PropertyGroup):
|
||||
scale_min: FloatProperty(name="Scale Min", default=0.8, min=0.1, max=5.0)
|
||||
scale_max: FloatProperty(name="Scale Max", default=1.2, min=0.1, max=5.0)
|
||||
nonuniform_scale: BoolProperty(
|
||||
name="Nicht-uniforme Skalierung", default=False,
|
||||
name="Nicht-uniforme Skalierung", default=True,
|
||||
description="Skaliert jede Achse einzeln (flache Platten vs. runde Brocken)",
|
||||
)
|
||||
keep_size: BoolProperty(
|
||||
name="Gesamtgroesse konstant halten", default=True,
|
||||
description=("Normiert die Achsen-Faktoren, sodass nur die FORM streut und nicht "
|
||||
"die Groesse. Wichtig fuer EcoGame: die Engine streut die Groesse "
|
||||
"zusaetzlich ueber ScaleMin/Max - beides zusammen multipliziert sich "
|
||||
"und erzeugt sonst Mini- und Riesen-Exemplare"),
|
||||
)
|
||||
|
||||
# --- Batch ---
|
||||
batch_count: IntProperty(name="Anzahl Rocks", default=5, min=1, max=500)
|
||||
@@ -819,6 +834,17 @@ class RockGenSettings(PropertyGroup):
|
||||
description="Exportiert nur die aktuell selektierten Rocks statt aller",
|
||||
)
|
||||
|
||||
# --- Zufall ---
|
||||
random_amount: FloatProperty(
|
||||
name="Streuung", default=0.35, min=0.0, max=1.0,
|
||||
description=("Wie stark 'Zufaellig' die Werte veraendert. 0 = nur neuer Seed, "
|
||||
"1 = volle Bandbreite"),
|
||||
)
|
||||
random_shape: BoolProperty(
|
||||
name="Auch Basis-Form/Noise-Typ", default=False,
|
||||
description="Wuerfelt zusaetzlich Cube/Ico-Sphere und den Noise-Typ",
|
||||
)
|
||||
|
||||
# --- Presets ---
|
||||
active_preset: EnumProperty(name="Preset", items=_preset_items)
|
||||
new_preset_name: StringProperty(name="Preset-Name", default="")
|
||||
@@ -1277,6 +1303,43 @@ class OBJECT_OT_bake_rock_textures(Operator):
|
||||
# Operatoren: Presets
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class ROCKGEN_OT_randomize(Operator):
|
||||
bl_idname = "rockgen.randomize"
|
||||
bl_label = "Zufaellig"
|
||||
bl_description = ("Wuerfelt Seed und Form-Parameter neu. 'Streuung' bestimmt, wie "
|
||||
"weit von den aktuellen Werten abgewichen wird")
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
s = context.scene.rock_gen_settings
|
||||
rng = random.Random()
|
||||
a = s.random_amount
|
||||
|
||||
def jitter(prop, lo, hi):
|
||||
"""Wert um +-a in Richtung der Grenzen ziehen, dann clampen."""
|
||||
cur = getattr(s, prop)
|
||||
span = (hi - lo) * a
|
||||
val = cur + rng.uniform(-span, span)
|
||||
setattr(s, prop, max(lo, min(hi, val)))
|
||||
|
||||
s.seed = rng.randint(0, 9999)
|
||||
jitter("noise_scale", 0.4, 3.0)
|
||||
jitter("displace_strength", 0.12, 0.7)
|
||||
jitter("decimate_ratio_1", 0.12, 0.45)
|
||||
jitter("decimate_ratio_2", 0.3, 0.7)
|
||||
jitter("dissolve_angle", 2.0, 10.0)
|
||||
jitter("subsurf_levels", 3, 5)
|
||||
s.subsurf_levels = int(round(s.subsurf_levels))
|
||||
if s.randomize_shape:
|
||||
jitter("noise_scale_max", 0.6, 4.0)
|
||||
jitter("displace_strength_max", 0.2, 0.9)
|
||||
if s.random_shape:
|
||||
s.base_shape = rng.choice(['CUBE', 'ICOSPHERE'])
|
||||
s.texture_type = rng.choice(['VORONOI', 'CLOUDS', 'DISTORTED_NOISE'])
|
||||
self.report({'INFO'}, "Neue Zufallswerte (Seed %d)." % s.seed)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class ROCKGEN_OT_preset_save(Operator):
|
||||
bl_idname = "rockgen.preset_save"
|
||||
bl_label = "Preset speichern"
|
||||
@@ -1452,6 +1515,7 @@ class VIEW3D_PT_rock_generator(Panel):
|
||||
row.prop(settings, "scale_min")
|
||||
row.prop(settings, "scale_max")
|
||||
box.prop(settings, "nonuniform_scale")
|
||||
box.prop(settings, "keep_size")
|
||||
|
||||
box = layout.box()
|
||||
box.label(text="Batch")
|
||||
@@ -1462,6 +1526,13 @@ class VIEW3D_PT_rock_generator(Panel):
|
||||
sub.enabled = settings.use_modal
|
||||
sub.prop(settings, "modal_threshold")
|
||||
|
||||
box = layout.box()
|
||||
box.label(text="Zufall", icon='FILE_REFRESH')
|
||||
row = box.row(align=True)
|
||||
row.prop(settings, "random_amount")
|
||||
row.prop(settings, "random_shape", text="", icon='MOD_NOISE')
|
||||
box.operator("rockgen.randomize", icon='FILE_REFRESH')
|
||||
|
||||
layout.separator()
|
||||
layout.operator("object.generate_rocks_batch", icon='MESH_ICOSPHERE')
|
||||
|
||||
@@ -1500,6 +1571,7 @@ classes = (
|
||||
OBJECT_OT_generate_rocks,
|
||||
OBJECT_OT_export_rocks_fbx,
|
||||
OBJECT_OT_bake_rock_textures,
|
||||
ROCKGEN_OT_randomize,
|
||||
ROCKGEN_OT_preset_save,
|
||||
ROCKGEN_OT_preset_load,
|
||||
ROCKGEN_OT_preset_delete,
|
||||
|
||||
Reference in New Issue
Block a user