Rock Gen 2.18.0: Material-Slots, Praefix Rock, Warnung bei zu grobem Erz-Mesh
Drei Rueckmeldungen von der Spielseite abgearbeitet. 1) Fehlende Material-Slots "Material zuweisen" stand auf Standard AUS, deshalb kamen alle Proben mit mats: [] an - Unreal legt dann nur einen Default-Slot an und die eingefrorene Konvention M_Rock_<Sorte> greift nicht. Jetzt Standard AN. Normale Steine rotieren durch M_Rock_Granite/Basalt/Sandstone/Mossy, das Erzfels-Set bekommt einen NEUTRALEN Slot M_Rock_Ore - ein Set fuer alle Erzsorten, die Sorte macht die Material-Instanz. 2) Namens-Drift Fels_ statt Rock_ War ein Versehen, keine Absicht: der Standard-Praefix stammte noch aus der ersten Fassung, bevor die Konvention Rock_<Set>_<Groesse>_<NN> stand. Der Erz-Set-Knopf setzte "Rock" schon selbst, der allgemeine Standard nicht. Standard ist jetzt "Rock" - keine Asset-Zwillinge, keine Migration noetig. 3) Erzfelsen mit ~870 statt ~2700 Tris Nachgestellt: aus den Werkseinstellungen liefert der Set-Knopf 2590-2690 Tris, die Automatik greift also. ~870 entstehen genau dann, wenn man den Erz-Haken setzt und den NORMALEN Erzeugen-Knopf nimmt - Werkseinstellung ist Decimate 0.20/0.50 statt 0.40/0.80. Das ist eine Bedienluecke, kein Fehler in der Automatik: die Dichte wird bewusst nur ueber den Set-Knopf gesetzt, damit niemandem seine Einstellungen unterm Hintern weggezogen werden. Deshalb warnt der Operator jetzt, wenn mit Erz-Haken ein Mesh unter 800 Verts entsteht, und nennt den Knopf "Smooth & dicht" beim Namen. Sonst faellt das erst in UE auf und sieht dort nach einem Material-Fehler aus. Tests halten den Vertrag jetzt fest: Auslieferungszustand (assign_material an, Praefix Rock), neutraler Erz-Slot, Sorten-Slots bei normalen Steinen. 17 Tests bestehen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+54
-12
@@ -1,7 +1,7 @@
|
||||
bl_info = {
|
||||
"name": "Stylized Rock Generator",
|
||||
"author": "D4rkst3r",
|
||||
"version": (2, 17, 0),
|
||||
"version": (2, 18, 0),
|
||||
"blender": (4, 2, 0),
|
||||
"location": "View3D > Sidebar > Rock Gen",
|
||||
"description": (
|
||||
@@ -35,6 +35,15 @@ from mathutils import Matrix, Vector
|
||||
TWO_PI = math.pi * 2.0
|
||||
|
||||
# Platzhalter-Materialien: (Name, Base-Color RGBA). Werden pro Rock rotiert.
|
||||
# Neutraler Slot fuer das Erzfels-Set: EIN Set fuer alle Erzsorten, welche
|
||||
# Sorte es ist (Kupfer/Eisen/Gold) macht die Material-Instanz in UE.
|
||||
ORE_MATERIAL = ("M_Rock_Ore", (0.30, 0.28, 0.26, 1.0))
|
||||
|
||||
# Unterhalb dieser Vertex-Zahl verschmieren die Erz-Nuggets zu Schlieren - die
|
||||
# Maske haengt an der Vertex-Dichte. Gemessen: bei ~150 Verts ist jedes dritte
|
||||
# ein Nugget-Zentrum, bei ~1300 liest es sich als einzelne Einschluesse.
|
||||
ORE_MIN_VERTS = 800
|
||||
|
||||
MATERIAL_PALETTE = (
|
||||
("M_Rock_Granite", (0.32, 0.30, 0.28, 1.0)),
|
||||
("M_Rock_Basalt", (0.11, 0.11, 0.13, 1.0)),
|
||||
@@ -251,8 +260,14 @@ def _get_or_create_material(name, color):
|
||||
return mat
|
||||
|
||||
|
||||
def _assign_material(obj, index):
|
||||
name, color = MATERIAL_PALETTE[index % len(MATERIAL_PALETTE)]
|
||||
def _assign_material(obj, index, fixed=None):
|
||||
"""Setzt den Material-Slot. Die UE-Skripte mappen ueber den SLOT-NAMEN,
|
||||
deshalb ist die Benennung Teil des Vertrags und kein Kosmetik-Thema.
|
||||
|
||||
`fixed` erzwingt einen bestimmten Slot - das Erzfels-Set nutzt das fuer
|
||||
seinen neutralen `M_Rock_Ore`, statt durch die Sorten zu rotieren.
|
||||
"""
|
||||
name, color = fixed if fixed is not None else MATERIAL_PALETTE[index % len(MATERIAL_PALETTE)]
|
||||
mat = _get_or_create_material(name, color)
|
||||
obj.data.materials.clear()
|
||||
obj.data.materials.append(mat)
|
||||
@@ -781,7 +796,8 @@ def _build_lod_group(context, settings, index, location, rng, base_name=None):
|
||||
_bake_vertex_cavity(context, lod0, settings)
|
||||
|
||||
if settings.assign_material:
|
||||
_assign_material(lod0, index)
|
||||
_assign_material(lod0, index,
|
||||
fixed=ORE_MATERIAL if settings.bake_ore else None)
|
||||
lod0.name = base_name + "_LOD0"
|
||||
lod0.data.name = base_name + "_LOD0"
|
||||
|
||||
@@ -844,7 +860,8 @@ def make_one_rock(context, settings, index, batch_count=1):
|
||||
if settings.set_origin_bottom:
|
||||
_set_origin_bottom(context, obj)
|
||||
if settings.assign_material:
|
||||
_assign_material(obj, index)
|
||||
_assign_material(obj, index,
|
||||
fixed=ORE_MATERIAL if settings.bake_ore else None)
|
||||
return [obj]
|
||||
|
||||
|
||||
@@ -858,8 +875,26 @@ def _cleanup_partial(context, names_before):
|
||||
bpy.data.objects.remove(obj, do_unlink=True)
|
||||
|
||||
|
||||
def _report_result(op, created, failed, cancelled=False):
|
||||
def _report_result(op, created, failed, cancelled=False, settings=None):
|
||||
n_mesh = sum(1 for o in created if getattr(o, "type", None) == 'MESH')
|
||||
|
||||
# Erz-Haken an, aber das Mesh ist zu grob? Dann verschmieren die Nuggets.
|
||||
# Das faellt sonst erst in UE auf - und dort sieht es nach einem
|
||||
# Material-Fehler aus, obwohl es an der Vertex-Dichte liegt.
|
||||
if settings is not None and settings.bake_ore and not failed:
|
||||
duenn = [o for o in created
|
||||
if getattr(o, "type", None) == 'MESH'
|
||||
and len(o.data.vertices) < ORE_MIN_VERTS]
|
||||
if duenn:
|
||||
op.report(
|
||||
{'WARNING'},
|
||||
"%d Rock(s) erzeugt - aber %d davon haben unter %d Verts. Die "
|
||||
"Erz-Nuggets verschmieren dabei zu Schlieren. Knopf "
|
||||
"'Smooth & dicht' druecken und neu erzeugen."
|
||||
% (n_mesh, len(duenn), ORE_MIN_VERTS),
|
||||
)
|
||||
return
|
||||
|
||||
if failed:
|
||||
op.report(
|
||||
{'WARNING'},
|
||||
@@ -1060,8 +1095,11 @@ class RockGenSettings(PropertyGroup):
|
||||
|
||||
# --- Material ---
|
||||
assign_material: BoolProperty(
|
||||
name="Material zuweisen", default=False,
|
||||
description="Weist pro Rock ein Platzhalter-Material zu (rotiert durch eine Liste)",
|
||||
name="Material zuweisen", default=True,
|
||||
description=("Legt den Material-Slot an (M_Rock_<Sorte>, beim Erz-Set "
|
||||
"neutral M_Rock_Ore). Die UE-Skripte mappen ueber den "
|
||||
"Slot-NAMEN - ohne Slot erzeugt Unreal nur einen Default "
|
||||
"und die Konvention greift nicht mehr"),
|
||||
)
|
||||
|
||||
# --- LOD ---
|
||||
@@ -1113,7 +1151,7 @@ class RockGenSettings(PropertyGroup):
|
||||
)
|
||||
# --- Benennung (EcoGame-Schema: Fels_<Form>_<Klasse>) ---
|
||||
name_prefix: StringProperty(
|
||||
name="Praefix", default="Fels",
|
||||
name="Praefix", default="Rock",
|
||||
description="Basisname, z.B. 'Fels' oder 'Felsblock'",
|
||||
)
|
||||
name_form: StringProperty(
|
||||
@@ -1268,7 +1306,7 @@ class OBJECT_OT_generate_rocks(Operator):
|
||||
print("[RockGen] Rock %d fehlgeschlagen: %s" % (i, exc))
|
||||
traceback.print_exc()
|
||||
_cleanup_partial(context, names_before)
|
||||
_report_result(self, created, failed)
|
||||
_report_result(self, created, failed, settings=settings)
|
||||
return {'FINISHED'}
|
||||
|
||||
# --- Einstiegspunkt: entscheidet synchron vs. modal ---
|
||||
@@ -1294,13 +1332,15 @@ class OBJECT_OT_generate_rocks(Operator):
|
||||
|
||||
if event.type == 'ESC':
|
||||
self._finish(context)
|
||||
_report_result(self, self._created, self._failed, cancelled=True)
|
||||
_report_result(self, self._created, self._failed, cancelled=True,
|
||||
settings=context.scene.rock_gen_settings)
|
||||
return {'CANCELLED'}
|
||||
|
||||
if event.type == 'TIMER':
|
||||
if self._index >= settings.batch_count:
|
||||
self._finish(context)
|
||||
_report_result(self, self._created, self._failed)
|
||||
_report_result(self, self._created, self._failed,
|
||||
settings=context.scene.rock_gen_settings)
|
||||
return {'FINISHED'}
|
||||
|
||||
names_before = set(bpy.data.objects.keys())
|
||||
@@ -1800,6 +1840,7 @@ class ROCKGEN_OT_ore_set(Operator):
|
||||
"name_class": s.name_class, "name_numbered": s.name_numbered,
|
||||
"bake_ore": s.bake_ore, "bake_vertex_cavity": s.bake_vertex_cavity,
|
||||
"use_class_size": s.use_class_size, "seed": s.seed,
|
||||
"assign_material": s.assign_material,
|
||||
}
|
||||
vorher.update({k: getattr(s, k) for k in DENSE_PRESET})
|
||||
# Erz MUSS dicht: die Maske haengt an der Vertex-Dichte. Bei ~150 Verts
|
||||
@@ -1813,6 +1854,7 @@ class ROCKGEN_OT_ore_set(Operator):
|
||||
s.name_numbered = True
|
||||
s.bake_ore = True
|
||||
s.bake_vertex_cavity = True # A haengt am selben Attribut
|
||||
s.assign_material = True # Slot M_Rock_Ore - die UE-Skripte mappen darueber
|
||||
s.use_class_size = True # S und M sollen sich in der Groesse unterscheiden
|
||||
|
||||
gesamt = 0
|
||||
|
||||
Reference in New Issue
Block a user