Rock Generator 2.6.0: Groessen-Klassen wirken jetzt + Randomize-Bug gefixt
1) KLASSEN-GROESSE (vom User gemeldet): S und M kamen gleich gross raus. Ursache war eine Luecke im Design, nicht in der Bedienung: "keep_size" normiert die Zufalls-Streuung auf konstantes Volumen - damit wurde alles, was ueber scale_min/max eingestellt war, wieder herausgerechnet. Fuer die Groesse der KLASSE gab es schlicht keinen Regler. Neu: CLASS_SIZES (S=0.45, M=1.0, L=1.9, XL=3.2) plus "Groesse aus Klasse" und eine freie "Basis-Groesse". Die Klassengroesse ist bewusst gesetzt und von der Streuung getrennt; sie greift auch bei abgeschalteter Randomisierung. Gemessen: S 0.85 m | M 1.9 m | L 3.5 m | XL 6.0 m, Volumen-Streuung INNERHALB jeder Klasse bleibt bei 1.11x (nur Form). 2) BUG im Zufalls-Button (kam mit 2.5.0): jitter() schrieb einen float in die int-Property subsurf_levels -> TypeError, der Operator brach komplett ab. jitter() rundet jetzt fuer Int-Properties. Von tests/test_randomize.py gefangen. Neuer Test tests/test_rock_classes.py deckt die Klassengroessen ab. 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, 5, 0),
|
||||
"version": (2, 6, 0),
|
||||
"blender": (4, 2, 0),
|
||||
"location": "View3D > Sidebar > Rock Gen",
|
||||
"description": (
|
||||
@@ -40,6 +40,14 @@ MATERIAL_PALETTE = (
|
||||
("M_Rock_Mossy", (0.24, 0.30, 0.18, 1.0)),
|
||||
)
|
||||
|
||||
# Basis-Groesse je Groessen-Klasse (Meter-Faktor auf das Grundmesh).
|
||||
# WICHTIG - Unterschied zur Zufalls-Streuung: DIESE Groesse ist gewollt und
|
||||
# unterscheidet die Klassen. Die Streuung (scale_min/max) variiert dagegen nur
|
||||
# die FORM innerhalb einer Klasse und wird von "keep_size" auf konstantes
|
||||
# Volumen normiert, damit sie sich nicht mit den ScaleMin/Max der Engine
|
||||
# multipliziert.
|
||||
CLASS_SIZES = {'S': 0.45, 'M': 1.0, 'L': 1.9, 'XL': 3.2}
|
||||
|
||||
# Property-Keys, die von Presets gespeichert/geladen werden.
|
||||
PRESET_KEYS = (
|
||||
"base_shape", "subsurf_levels", "texture_type", "noise_scale",
|
||||
@@ -52,6 +60,7 @@ PRESET_KEYS = (
|
||||
"shading_mode", "autosmooth_angle",
|
||||
"randomize_shape", "noise_scale_max", "displace_strength_max", "nonuniform_scale",
|
||||
"flatten_bottom", "flatten_ratio", "set_origin_bottom", "keep_size",
|
||||
"use_class_size", "base_size",
|
||||
)
|
||||
|
||||
# Eingebaute Presets (nicht ueberschreibbar/loeschbar).
|
||||
@@ -407,7 +416,18 @@ def _apply_hardsurface(context, obj, settings):
|
||||
|
||||
def _apply_random_transform(context, obj, settings, rng):
|
||||
if not settings.randomize_transform:
|
||||
# Klassengroesse gilt trotzdem - sie ist keine Zufalls-Streuung.
|
||||
base = (CLASS_SIZES.get(settings.name_class, 1.0)
|
||||
if settings.use_class_size else settings.base_size)
|
||||
if abs(base - 1.0) > 1e-6:
|
||||
obj.scale = (base, base, base)
|
||||
_deselect_all(context)
|
||||
context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
|
||||
return
|
||||
base = (CLASS_SIZES.get(settings.name_class, 1.0)
|
||||
if settings.use_class_size else settings.base_size)
|
||||
lo, hi = sorted((settings.scale_min, settings.scale_max))
|
||||
if settings.nonuniform_scale:
|
||||
sx, sy, sz = (rng.uniform(lo, hi), rng.uniform(lo, hi), rng.uniform(lo, hi))
|
||||
@@ -418,10 +438,10 @@ def _apply_random_transform(context, obj, settings, rng):
|
||||
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)
|
||||
obj.scale = (sx * base, sy * base, sz * base)
|
||||
else:
|
||||
s = 1.0 if settings.keep_size else rng.uniform(lo, hi)
|
||||
obj.scale = (s, s, s)
|
||||
obj.scale = (s * base, s * base, s * base)
|
||||
obj.rotation_euler = (
|
||||
rng.uniform(0.0, TWO_PI),
|
||||
rng.uniform(0.0, TWO_PI),
|
||||
@@ -781,6 +801,17 @@ class RockGenSettings(PropertyGroup):
|
||||
"macht die Engine ueber ScaleMin/Max - eine eigene Klasse nur, "
|
||||
"wenn sich die Silhouette aendert"),
|
||||
)
|
||||
use_class_size: BoolProperty(
|
||||
name="Groesse aus Klasse", default=True,
|
||||
description=("Leitet die Basisgroesse aus der Klasse ab "
|
||||
"(S=0.45, M=1.0, L=1.9, XL=3.2). Ausschalten, um "
|
||||
"'Basis-Groesse' frei zu setzen"),
|
||||
)
|
||||
base_size: FloatProperty(
|
||||
name="Basis-Groesse", default=1.0, min=0.05, max=20.0,
|
||||
description=("Absolute Groesse des Steins (Faktor). Getrennt von der "
|
||||
"Zufalls-Streuung, die nur die Form variiert"),
|
||||
)
|
||||
name_numbered: BoolProperty(
|
||||
name="Bei Batch durchnummerieren", default=True,
|
||||
description="Haengt bei mehreren Rocks _01, _02 ... an (sonst Blender-Suffix)",
|
||||
@@ -1316,11 +1347,15 @@ class ROCKGEN_OT_randomize(Operator):
|
||||
a = s.random_amount
|
||||
|
||||
def jitter(prop, lo, hi):
|
||||
"""Wert um +-a in Richtung der Grenzen ziehen, dann clampen."""
|
||||
"""Wert um +-a in Richtung der Grenzen ziehen, dann clampen.
|
||||
|
||||
Int-Properties brauchen einen int - ein float wirft sonst
|
||||
TypeError und der ganze Operator bricht ab.
|
||||
"""
|
||||
cur = getattr(s, prop)
|
||||
span = (hi - lo) * a
|
||||
val = cur + rng.uniform(-span, span)
|
||||
setattr(s, prop, max(lo, min(hi, val)))
|
||||
val = max(lo, min(hi, cur + rng.uniform(-span, span)))
|
||||
setattr(s, prop, int(round(val)) if isinstance(cur, int) else val)
|
||||
|
||||
s.seed = rng.randint(0, 9999)
|
||||
jitter("noise_scale", 0.4, 3.0)
|
||||
@@ -1329,7 +1364,6 @@ class ROCKGEN_OT_randomize(Operator):
|
||||
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)
|
||||
@@ -1444,6 +1478,15 @@ class VIEW3D_PT_rock_generator(Panel):
|
||||
row.prop(settings, "name_form", text="")
|
||||
row.prop(settings, "name_class", text="")
|
||||
box.prop(settings, "name_numbered")
|
||||
row = box.row(align=True)
|
||||
row.prop(settings, "use_class_size")
|
||||
sub = row.row()
|
||||
sub.enabled = not settings.use_class_size
|
||||
sub.prop(settings, "base_size", text="")
|
||||
if settings.use_class_size:
|
||||
box.label(text="Basis-Groesse aus Klasse %s: x%.2f"
|
||||
% (settings.name_class, CLASS_SIZES.get(settings.name_class, 1.0)),
|
||||
icon='FIXED_SIZE')
|
||||
box.label(text="Ergebnis: %s" % _asset_name(settings, 0, settings.batch_count),
|
||||
icon='INFO')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user