v2.1.0: FBX-Export, Shading-Toggle, Pro-Rock-Varianz, Boden abflachen + Origin

- FBX-Export-Button (UE-ready): je Rock/LOD-Gruppe eine FBX, use_custom_props fuer LodGroup, Triangulate, EDGE/FACE-Smoothing je nach Shading
- Shading-Modus: Faceted / Smooth / Auto-Smooth-nach-Winkel
- Pro-Rock-Varianz: Noise/Displace Min-Max + nicht-uniforme Skalierung
- Boden abflachen (Bisect) + Origin unten-mittig
- Live gegen Blender 5.1.2 getestet, Headless-Test gruen

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 07:01:10 +02:00
co-authored by Claude Opus 4.8
parent 32317c446c
commit 46fc05eec4
6 changed files with 273 additions and 13 deletions
+11
View File
@@ -113,6 +113,17 @@ aussen (signed volume > 0), UV0 + UV1 vorhanden, und gibt eine Zusammenfassung
--- ---
## Changelog v2.1.0
- **FBX-Export-Button (UE-ready):** exportiert jeden Rock / jede LOD-Gruppe als
eigene FBX mit `use_custom_props=True` (fuer `fbx_type=LodGroup`), Triangulate
und passendem Smoothing (`EDGE` bei Auto-Smooth, sonst `FACE`).
*UE-Import-Tipp:* Normal Import Method auf „Import Normals" stellen, „Import LODs" an.
- **Shading-Toggle:** Faceted / Smooth / Auto-Smooth-nach-Winkel.
- **Pro-Rock-Varianz:** Noise-Scale & Displace-Staerke zwischen Min/Max plus
optionale nicht-uniforme Skalierung → ein Batch wirkt variantenreich.
- **Boden abflachen (Bisect) + Origin unten-mittig** → Rocks stehen in UE plan auf.
## Changelog v2.0.0 ## Changelog v2.0.0
Gehaertet & erweitert gegenueber v1.0.0: Gehaertet & erweitert gegenueber v1.0.0:
+1 -1
View File
@@ -1,7 +1,7 @@
schema_version = "1.0.0" schema_version = "1.0.0"
id = "stylized_rock_generator" id = "stylized_rock_generator"
version = "2.0.0" version = "2.1.0"
name = "Stylized Rock Generator" name = "Stylized Rock Generator"
tagline = "Batch-Generator fuer stylized Rocks mit UE5-Export" tagline = "Batch-Generator fuer stylized Rocks mit UE5-Export"
maintainer = "D4rkst3r" maintainer = "D4rkst3r"
+4 -4
View File
@@ -7,7 +7,7 @@
"id": "stylized_rock_generator", "id": "stylized_rock_generator",
"name": "Stylized Rock Generator", "name": "Stylized Rock Generator",
"tagline": "Batch-Generator fuer stylized Rocks mit UE5-Export", "tagline": "Batch-Generator fuer stylized Rocks mit UE5-Export",
"version": "2.0.0", "version": "2.1.0",
"type": "add-on", "type": "add-on",
"maintainer": "D4rkst3r", "maintainer": "D4rkst3r",
"license": [ "license": [
@@ -23,9 +23,9 @@
"Mesh", "Mesh",
"Modeling" "Modeling"
], ],
"archive_url": "./stylized_rock_generator-2.0.0.zip", "archive_url": "./stylized_rock_generator-2.1.0.zip",
"archive_size": 9183, "archive_size": 11477,
"archive_hash": "sha256:7bcf0d9cc56015157dfa17264d9ea51e624e8c0013f84b59069bcd3386815ad6" "archive_hash": "sha256:76911905d9e283ce2cbe85ab341194e3fcfcb431eb7cf3816b96e6d2358214ae"
} }
] ]
} }
Binary file not shown.
Binary file not shown.
+257 -8
View File
@@ -1,7 +1,7 @@
bl_info = { bl_info = {
"name": "Stylized Rock Generator", "name": "Stylized Rock Generator",
"author": "D4rkst3r", "author": "D4rkst3r",
"version": (2, 0, 0), "version": (2, 1, 0),
"blender": (4, 2, 0), "blender": (4, 2, 0),
"location": "View3D > Sidebar > Rock Gen", "location": "View3D > Sidebar > Rock Gen",
"description": ( "description": (
@@ -49,6 +49,9 @@ PRESET_KEYS = (
"add_bevel", "bevel_width", "bevel_segments", "add_weighted_normal", "add_bevel", "bevel_width", "bevel_segments", "add_weighted_normal",
"assign_material", "generate_lods", "lod_count", "lod_ratio_step", "assign_material", "generate_lods", "lod_count", "lod_ratio_step",
"batch_count", "spacing", "batch_count", "spacing",
"shading_mode", "autosmooth_angle",
"randomize_shape", "noise_scale_max", "displace_strength_max", "nonuniform_scale",
"flatten_bottom", "flatten_ratio", "set_origin_bottom",
) )
# Eingebaute Presets (nicht ueberschreibbar/loeschbar). # Eingebaute Presets (nicht ueberschreibbar/loeschbar).
@@ -285,6 +288,15 @@ def _build_rock_mesh(context, settings, index, location, rng):
obj = context.active_object obj = context.active_object
obj.name = "Rock_%03d" % index obj.name = "Rock_%03d" % index
# Effektive Form-Parameter (optional pro Rock variiert)
eff_noise = settings.noise_scale
eff_displace = settings.displace_strength
if settings.randomize_shape:
lo_n, hi_n = sorted((settings.noise_scale, settings.noise_scale_max))
lo_d, hi_d = sorted((settings.displace_strength, settings.displace_strength_max))
eff_noise = rng.uniform(lo_n, hi_n)
eff_displace = rng.uniform(lo_d, hi_d)
# 2. Subdivision Surface -> Apply # 2. Subdivision Surface -> Apply
subsurf = obj.modifiers.new(name="Subsurf", type='SUBSURF') subsurf = obj.modifiers.new(name="Subsurf", type='SUBSURF')
subsurf.levels = settings.subsurf_levels subsurf.levels = settings.subsurf_levels
@@ -294,18 +306,18 @@ def _build_rock_mesh(context, settings, index, location, rng):
# 3. Displace + Noise-Textur -> Apply # 3. Displace + Noise-Textur -> Apply
tex = bpy.data.textures.new(name="RockTex_%03d" % index, type=settings.texture_type) tex = bpy.data.textures.new(name="RockTex_%03d" % index, type=settings.texture_type)
if settings.texture_type == 'VORONOI': if settings.texture_type == 'VORONOI':
tex.noise_scale = settings.noise_scale tex.noise_scale = eff_noise
elif settings.texture_type == 'CLOUDS': elif settings.texture_type == 'CLOUDS':
tex.noise_scale = settings.noise_scale tex.noise_scale = eff_noise
tex.noise_depth = 2 tex.noise_depth = 2
elif settings.texture_type == 'DISTORTED_NOISE': elif settings.texture_type == 'DISTORTED_NOISE':
tex.noise_scale = settings.noise_scale tex.noise_scale = eff_noise
tex.distortion = 1.0 tex.distortion = 1.0
displace = obj.modifiers.new(name="Displace", type='DISPLACE') displace = obj.modifiers.new(name="Displace", type='DISPLACE')
displace.texture = tex displace.texture = tex
displace.texture_coords = 'GLOBAL' displace.texture_coords = 'GLOBAL'
displace.strength = settings.displace_strength displace.strength = eff_displace
displace.mid_level = 0.5 displace.mid_level = 0.5
# Variation pro Instanz: Objekt vor dem Apply an eine zufaellige (moderate!) # Variation pro Instanz: Objekt vor dem Apply an eine zufaellige (moderate!)
@@ -349,6 +361,10 @@ def _build_rock_mesh(context, settings, index, location, rng):
if _face_count(obj) == 0: if _face_count(obj) == 0:
raise RockGenError("Nach Limited Dissolve keine Faces mehr uebrig.") raise RockGenError("Nach Limited Dissolve keine Faces mehr uebrig.")
# 5c. Boden abflachen (optional) - vor den UVs, damit die Unterseite UVs bekommt
if settings.flatten_bottom:
_flatten_bottom(context, obj, settings)
# 6. UVs # 6. UVs
if settings.generate_uv0: if settings.generate_uv0:
_make_uv0(context, obj) _make_uv0(context, obj)
@@ -371,7 +387,6 @@ def _apply_hardsurface(context, obj, settings):
_deselect_all(context) _deselect_all(context)
context.view_layer.objects.active = obj context.view_layer.objects.active = obj
obj.select_set(True) obj.select_set(True)
bpy.ops.object.shade_smooth()
wn = obj.modifiers.new(name="WeightedNormal", type='WEIGHTED_NORMAL') wn = obj.modifiers.new(name="WeightedNormal", type='WEIGHTED_NORMAL')
wn.keep_sharp = True wn.keep_sharp = True
_apply_modifier(context, obj, wn.name) _apply_modifier(context, obj, wn.name)
@@ -380,8 +395,12 @@ def _apply_hardsurface(context, obj, settings):
def _apply_random_transform(context, obj, settings, rng): def _apply_random_transform(context, obj, settings, rng):
if not settings.randomize_transform: if not settings.randomize_transform:
return return
scale_factor = rng.uniform(settings.scale_min, settings.scale_max) lo, hi = sorted((settings.scale_min, settings.scale_max))
obj.scale = (scale_factor, scale_factor, scale_factor) if settings.nonuniform_scale:
obj.scale = (rng.uniform(lo, hi), rng.uniform(lo, hi), rng.uniform(lo, hi))
else:
s = rng.uniform(lo, hi)
obj.scale = (s, s, s)
obj.rotation_euler = ( obj.rotation_euler = (
rng.uniform(0.0, TWO_PI), rng.uniform(0.0, TWO_PI),
rng.uniform(0.0, TWO_PI), rng.uniform(0.0, TWO_PI),
@@ -393,6 +412,77 @@ def _apply_random_transform(context, obj, settings, rng):
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True) bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
def _apply_shading(context, obj, settings):
"""Setzt den Shading-Look: FLAT (facettiert), SMOOTH oder AUTO (nach Winkel)."""
_deselect_all(context)
context.view_layer.objects.active = obj
obj.select_set(True)
mode = settings.shading_mode
if mode == 'FLAT':
bpy.ops.object.shade_flat()
elif mode == 'SMOOTH':
bpy.ops.object.shade_smooth()
else: # AUTO
try:
bpy.ops.object.shade_auto_smooth(angle=math.radians(settings.autosmooth_angle))
except (RuntimeError, TypeError):
bpy.ops.object.shade_smooth()
# Scharfe Kanten nach Winkel markieren -> FBX-EDGE-Export traegt sie mit.
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.mark_sharp(clear=True)
bpy.ops.mesh.edges_select_sharp(sharpness=math.radians(settings.autosmooth_angle))
bpy.ops.mesh.mark_sharp()
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.object.mode_set(mode='OBJECT')
def _flatten_bottom(context, obj, settings):
"""Schneidet eine flache Unterseite (Bisect), damit der Rock plan aufsteht."""
zs = [v.co.z for v in obj.data.vertices]
if not zs:
return
zmin, zmax = min(zs), max(zs)
cut_z = zmin + (zmax - zmin) * settings.flatten_ratio
context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
try:
bpy.ops.mesh.bisect(
plane_co=(0.0, 0.0, cut_z),
plane_no=(0.0, 0.0, 1.0),
clear_inner=True,
use_fill=True,
)
except RuntimeError as exc:
bpy.ops.object.mode_set(mode='OBJECT')
raise RockGenError("Boden abflachen (bisect) fehlgeschlagen: %s" % exc)
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.normals_make_consistent(inside=False)
bpy.ops.object.mode_set(mode='OBJECT')
if _face_count(obj) == 0:
raise RockGenError("Nach Boden-Abflachen keine Faces mehr uebrig.")
def _set_origin_bottom(context, obj):
"""Setzt den Origin auf die Unterseiten-Mitte (fuer sauberes Aufstellen in UE)."""
mw = obj.matrix_world
coords = [mw @ v.co for v in obj.data.vertices]
if not coords:
return
cx = sum(c.x for c in coords) / len(coords)
cy = sum(c.y for c in coords) / len(coords)
zmin = min(c.z for c in coords)
scene = context.scene
prev_cursor = scene.cursor.location.copy()
scene.cursor.location = (cx, cy, zmin)
_deselect_all(context)
context.view_layer.objects.active = obj
obj.select_set(True)
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
scene.cursor.location = prev_cursor
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# LOD-Gruppen (fbx_type = LodGroup -> von Unreal beim FBX-Import erkannt) # LOD-Gruppen (fbx_type = LodGroup -> von Unreal beim FBX-Import erkannt)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -443,6 +533,10 @@ def _build_lod_group(context, settings, index, location, rng):
_make_lightmap_uv(context, lod) _make_lightmap_uv(context, lod)
created.append(lod) created.append(lod)
# Shading pro LOD (nach dem Decimate, damit scharfe Kanten zur Topologie passen)
for lod in created:
_apply_shading(context, lod, settings)
# Empty als LOD-Group-Parent # Empty als LOD-Group-Parent
empty = bpy.data.objects.new(base_name + "_LODGroup", None) empty = bpy.data.objects.new(base_name + "_LODGroup", None)
empty.empty_display_type = 'PLAIN_AXES' empty.empty_display_type = 'PLAIN_AXES'
@@ -470,6 +564,9 @@ def make_one_rock(context, settings, index):
obj = _build_rock_mesh(context, settings, index, location, rng) obj = _build_rock_mesh(context, settings, index, location, rng)
_apply_hardsurface(context, obj, settings) _apply_hardsurface(context, obj, settings)
_apply_random_transform(context, obj, settings, rng) _apply_random_transform(context, obj, settings, rng)
_apply_shading(context, obj, settings)
if settings.set_origin_bottom:
_set_origin_bottom(context, obj)
if settings.assign_material: if settings.assign_material:
_assign_material(obj, index) _assign_material(obj, index)
return [obj] return [obj]
@@ -524,6 +621,12 @@ class RockGenSettings(PropertyGroup):
noise_scale: FloatProperty(name="Noise Scale", default=1.0, min=0.01, max=10.0) noise_scale: FloatProperty(name="Noise Scale", default=1.0, min=0.01, max=10.0)
displace_strength: FloatProperty(name="Displace Staerke", default=0.3, min=0.0, max=2.0) displace_strength: FloatProperty(name="Displace Staerke", default=0.3, min=0.0, max=2.0)
seed: IntProperty(name="Seed", default=0, min=0) seed: IntProperty(name="Seed", default=0, min=0)
randomize_shape: BoolProperty(
name="Form pro Rock variieren", default=False,
description="Noise-Scale und Displace-Staerke pro Rock zufaellig zwischen Min und Max",
)
noise_scale_max: FloatProperty(name="Noise Scale Max", default=2.0, min=0.01, max=10.0)
displace_strength_max: FloatProperty(name="Displace Max", default=0.5, min=0.0, max=2.0)
# --- Decimate / Cleanup --- # --- Decimate / Cleanup ---
decimate_ratio_1: FloatProperty(name="Decimate 1", default=0.2, min=0.01, max=1.0) decimate_ratio_1: FloatProperty(name="Decimate 1", default=0.2, min=0.01, max=1.0)
@@ -555,6 +658,35 @@ class RockGenSettings(PropertyGroup):
description="Weighted-Normal-Modifier fuer sauberere Shading-Uebergaenge", description="Weighted-Normal-Modifier fuer sauberere Shading-Uebergaenge",
) )
# --- Shading ---
shading_mode: EnumProperty(
name="Shading",
items=[
('FLAT', "Faceted", "Harte Flaechen - klassischer stylized Low-Poly-Look"),
('SMOOTH', "Smooth", "Voll geglaettet"),
('AUTO', "Auto-Smooth", "Glatt, aber scharfe Kanten oberhalb des Winkels"),
],
default='AUTO',
)
autosmooth_angle: FloatProperty(
name="Auto-Smooth Winkel (Grad)", default=30.0, min=0.0, max=180.0,
description="Kanten steiler als dieser Winkel bleiben hart",
)
# --- Form / Boden ---
flatten_bottom: BoolProperty(
name="Boden abflachen", default=False,
description="Schneidet eine flache Unterseite, damit der Rock plan aufsteht",
)
flatten_ratio: FloatProperty(
name="Schnitthoehe", default=0.1, min=0.0, max=0.45,
description="Wie weit von unten abgeschnitten wird (Anteil der Hoehe)",
)
set_origin_bottom: BoolProperty(
name="Origin unten-mittig", default=False,
description="Setzt den Origin auf die Unterseiten-Mitte (sauberes Aufstellen in UE)",
)
# --- Material --- # --- Material ---
assign_material: BoolProperty( assign_material: BoolProperty(
name="Material zuweisen", default=False, name="Material zuweisen", default=False,
@@ -579,6 +711,10 @@ class RockGenSettings(PropertyGroup):
randomize_transform: BoolProperty(name="Skalierung/Rotation randomisieren", default=True) randomize_transform: BoolProperty(name="Skalierung/Rotation randomisieren", default=True)
scale_min: FloatProperty(name="Scale Min", default=0.8, min=0.1, max=5.0) 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) scale_max: FloatProperty(name="Scale Max", default=1.2, min=0.1, max=5.0)
nonuniform_scale: BoolProperty(
name="Nicht-uniforme Skalierung", default=False,
description="Skaliert jede Achse einzeln (flache Platten vs. runde Brocken)",
)
# --- Batch --- # --- Batch ---
batch_count: IntProperty(name="Anzahl Rocks", default=5, min=1, max=500) batch_count: IntProperty(name="Anzahl Rocks", default=5, min=1, max=500)
@@ -592,6 +728,16 @@ class RockGenSettings(PropertyGroup):
description="Ab dieser Rock-Anzahl wird der modale (nicht-blockierende) Modus genutzt", description="Ab dieser Rock-Anzahl wird der modale (nicht-blockierende) Modus genutzt",
) )
# --- FBX-Export ---
export_dir: StringProperty(
name="Export-Ordner", default="", subtype='DIR_PATH',
description="Zielordner fuer die FBX-Dateien (je Rock/LOD-Gruppe eine Datei)",
)
export_selected_only: BoolProperty(
name="Nur Auswahl exportieren", default=False,
description="Exportiert nur die aktuell selektierten Rocks statt aller",
)
# --- Presets --- # --- Presets ---
active_preset: EnumProperty(name="Preset", items=_preset_items) active_preset: EnumProperty(name="Preset", items=_preset_items)
new_preset_name: StringProperty(name="Preset-Name", default="") new_preset_name: StringProperty(name="Preset-Name", default="")
@@ -687,6 +833,84 @@ class OBJECT_OT_generate_rocks(Operator):
context.workspace.status_text_set(None) context.workspace.status_text_set(None)
# ---------------------------------------------------------------------------
# FBX-Export (UE-ready)
# ---------------------------------------------------------------------------
def _collect_export_targets(context, settings):
"""Top-Level-Rocks: LOD-Group-Empties + freistehende Rock-Meshes ohne Parent."""
tops = []
for o in context.scene.objects:
is_group = (o.type == 'EMPTY' and o.get("fbx_type") == "LodGroup")
is_standalone = (o.type == 'MESH' and o.name.startswith("Rock_") and o.parent is None)
if is_group or is_standalone:
tops.append(o)
if settings.export_selected_only:
sel = set(context.selected_objects)
tops = [t for t in tops
if t in sel or any(c in sel for c in t.children_recursive)]
return tops
def _safe_filename(name):
keep = "-_.() "
return "".join(c for c in name if c.isalnum() or c in keep).strip() or "Rock"
class OBJECT_OT_export_rocks_fbx(Operator):
bl_idname = "object.export_rocks_fbx"
bl_label = "FBX exportieren"
bl_description = "Exportiert jeden Rock / jede LOD-Gruppe als eigene FBX (UE-ready)"
def execute(self, context):
settings = context.scene.rock_gen_settings
directory = bpy.path.abspath(settings.export_dir) if settings.export_dir else ""
if not directory or not os.path.isdir(directory):
self.report({'ERROR'}, "Bitte einen gueltigen Export-Ordner waehlen.")
return {'CANCELLED'}
targets = _collect_export_targets(context, settings)
if not targets:
self.report({'WARNING'}, "Keine Rocks zum Exportieren gefunden.")
return {'CANCELLED'}
# AUTO -> Edge-Smoothing (traegt scharfe Kanten), sonst Face-Smoothing.
smooth_type = 'EDGE' if settings.shading_mode == 'AUTO' else 'FACE'
_ensure_object_mode(context)
exported, failed = 0, 0
for top in targets:
objs = list(top.children_recursive) + [top]
_deselect_all(context)
for o in objs:
o.select_set(True)
context.view_layer.objects.active = top
filepath = os.path.join(directory, _safe_filename(top.name) + ".fbx")
try:
bpy.ops.export_scene.fbx(
filepath=filepath,
use_selection=True,
object_types={'MESH', 'EMPTY'},
use_mesh_modifiers=True,
mesh_smooth_type=smooth_type,
use_triangles=True,
use_custom_props=True, # noetig fuer fbx_type=LodGroup
add_leaf_bones=False,
bake_anim=False,
path_mode='AUTO',
)
exported += 1
except RuntimeError as exc:
failed += 1
print("[RockGen] FBX-Export '%s' fehlgeschlagen: %s" % (top.name, exc))
if failed:
self.report({'WARNING'}, "%d FBX exportiert, %d fehlgeschlagen (Konsole)." % (exported, failed))
else:
self.report({'INFO'}, "%d FBX-Datei(en) nach %s exportiert." % (exported, directory))
return {'FINISHED'}
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Operatoren: Presets # Operatoren: Presets
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -799,6 +1023,11 @@ class VIEW3D_PT_rock_generator(Panel):
box.prop(settings, "noise_scale") box.prop(settings, "noise_scale")
box.prop(settings, "displace_strength") box.prop(settings, "displace_strength")
box.prop(settings, "seed") box.prop(settings, "seed")
box.prop(settings, "randomize_shape")
sub = box.column(align=True)
sub.enabled = settings.randomize_shape
sub.prop(settings, "noise_scale_max")
sub.prop(settings, "displace_strength_max")
box = layout.box() box = layout.box()
box.label(text="Decimate / Cleanup") box.label(text="Decimate / Cleanup")
@@ -815,6 +1044,18 @@ class VIEW3D_PT_rock_generator(Panel):
sub.prop(settings, "bevel_segments") sub.prop(settings, "bevel_segments")
box.prop(settings, "add_weighted_normal") box.prop(settings, "add_weighted_normal")
box = layout.box()
box.label(text="Shading / Form")
box.prop(settings, "shading_mode")
row = box.row()
row.enabled = settings.shading_mode == 'AUTO'
row.prop(settings, "autosmooth_angle")
box.prop(settings, "flatten_bottom")
row = box.row()
row.enabled = settings.flatten_bottom
row.prop(settings, "flatten_ratio")
box.prop(settings, "set_origin_bottom")
box = layout.box() box = layout.box()
box.label(text="UV (Unreal Export)") box.label(text="UV (Unreal Export)")
box.prop(settings, "generate_uv0") box.prop(settings, "generate_uv0")
@@ -838,6 +1079,7 @@ class VIEW3D_PT_rock_generator(Panel):
row = box.row(align=True) row = box.row(align=True)
row.prop(settings, "scale_min") row.prop(settings, "scale_min")
row.prop(settings, "scale_max") row.prop(settings, "scale_max")
box.prop(settings, "nonuniform_scale")
box = layout.box() box = layout.box()
box.label(text="Batch") box.label(text="Batch")
@@ -851,6 +1093,12 @@ class VIEW3D_PT_rock_generator(Panel):
layout.separator() layout.separator()
layout.operator("object.generate_rocks_batch", icon='MESH_ICOSPHERE') layout.operator("object.generate_rocks_batch", icon='MESH_ICOSPHERE')
box = layout.box()
box.label(text="FBX-Export (Unreal)")
box.prop(settings, "export_dir")
box.prop(settings, "export_selected_only")
box.operator("object.export_rocks_fbx", icon='EXPORT')
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Registration # Registration
@@ -859,6 +1107,7 @@ class VIEW3D_PT_rock_generator(Panel):
classes = ( classes = (
RockGenSettings, RockGenSettings,
OBJECT_OT_generate_rocks, OBJECT_OT_generate_rocks,
OBJECT_OT_export_rocks_fbx,
ROCKGEN_OT_preset_save, ROCKGEN_OT_preset_save,
ROCKGEN_OT_preset_load, ROCKGEN_OT_preset_load,
ROCKGEN_OT_preset_delete, ROCKGEN_OT_preset_delete,