Fuer handgebaute Assets, die nicht durch einen Generator laufen (Pilze, Baumstumpf, Erzfels, Props). Panel in fuenf einklappbaren Unter-Panels statt einer langen Liste: 1 Vorbereiten - Rotation/Scale anwenden, Ursprung unten-mittig, Normalen 2 Vertex-Farben - R=Cavity / G=Teil-oder-Moos-Maske / B=Hoehe / A=1 3 Benennung - <Basis>_<NN>, Mesh-Daten ziehen mit 4 Pruefen - Asset-Check auf alles, was in UE Aerger macht 5 Export - FBX je Mesh mit colors_type='SRGB' Zwei Bugs beim Bauen gefunden und gefixt: - v.link_faces gibt es nur in bmesh, nicht auf Mesh.vertices -> lose Vertices ueber die tatsaechlich benutzten Indizes zaehlen. - bpy.ops.paint.vertex_color_set braucht den Vertex-Paint-Modus und scheitert im Object-Mode mit "poll() failed". Zusammen mit vertex_color_dirt im selben try wurde dadurch der GANZE Bake stillschweigend uebersprungen (R/G/B blieben 1.0). Initialisierung laeuft jetzt direkt ueber Python. Verifiziert am nachgebauten Pilz: Check meldet vorher Scale/Rotation/Ursprung, nach "Vorbereiten" Scale 1/1/1, Rot 0/0/0, min_z 0.0000 und "ok". Teil-Maske G=1 im Hut, 0 im Stiel. Nach FBX-Roundtrip G-Werte [0.0, 1.0] erhalten. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
546 lines
21 KiB
Python
546 lines
21 KiB
Python
bl_info = {
|
|
"name": "Stylized Asset Utils",
|
|
"author": "D4rkst3r",
|
|
"version": (1, 0, 0),
|
|
"blender": (4, 2, 0),
|
|
"location": "View3D > Sidebar > Asset Utils",
|
|
"description": (
|
|
"Werkzeuge fuer HANDGEBAUTE Assets: Transform/Ursprung, Vertex-Farben, "
|
|
"Benennung, Asset-Check und FBX-Export nach EcoGame-Konvention."
|
|
),
|
|
"category": "Object",
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Wofuer dieses Addon da ist
|
|
#
|
|
# Rock-, Tree- und Grass-Generator erzeugen Assets nach Konvention. Was von HAND
|
|
# gebaut wird (Pilze, Baumstumpf, Erzfels, Props) laeuft daran vorbei. Dieses
|
|
# Addon bringt beliebige Meshes auf denselben Stand:
|
|
# - Transform anwenden + Ursprung unten-mittig
|
|
# - Vertex-Farben R=Cavity / G=Teil-oder-Moos / B=Hoehe / A=1
|
|
# - Benennung nach Schema
|
|
# - Asset-Check: was wuerde in UE Aerger machen?
|
|
# - FBX-Export mit den richtigen Schaltern (Vertex-Farben!)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
import bpy
|
|
import os
|
|
import math
|
|
import random
|
|
|
|
from bpy.props import (
|
|
IntProperty, FloatProperty, EnumProperty, BoolProperty,
|
|
StringProperty, PointerProperty,
|
|
)
|
|
from bpy.types import Operator, Panel, PropertyGroup
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helfer
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def _deselect_all(context):
|
|
for o in list(context.view_layer.objects):
|
|
try:
|
|
o.select_set(False)
|
|
except (ReferenceError, AttributeError, RuntimeError):
|
|
pass
|
|
|
|
|
|
def _ensure_object_mode(context):
|
|
if context.mode != 'OBJECT':
|
|
try:
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
except RuntimeError:
|
|
pass
|
|
|
|
|
|
def _activate(context, ob):
|
|
_deselect_all(context)
|
|
context.view_layer.objects.active = ob
|
|
ob.select_set(True)
|
|
|
|
|
|
def _tris(me):
|
|
return sum(len(p.vertices) - 2 for p in me.polygons)
|
|
|
|
|
|
def _mesh_shells(me):
|
|
"""Zusammenhaengende Teile als Mengen von Polygon-Indizes.
|
|
|
|
Assets, die aus mehreren Objekten mit Strg+J verbunden wurden (Pilz =
|
|
Hut + Stiel), bleiben geometrisch getrennt - so laesst sich die Teil-Maske
|
|
EXAKT bilden statt sie ueber Normalen zu schaetzen.
|
|
"""
|
|
adj = {}
|
|
for poly in me.polygons:
|
|
for ek in poly.edge_keys:
|
|
adj.setdefault(ek, []).append(poly.index)
|
|
seen, shells = set(), []
|
|
for poly in me.polygons:
|
|
if poly.index in seen:
|
|
continue
|
|
stack, comp = [poly.index], set()
|
|
while stack:
|
|
pi = stack.pop()
|
|
if pi in comp:
|
|
continue
|
|
comp.add(pi)
|
|
for ek in me.polygons[pi].edge_keys:
|
|
for nb in adj.get(ek, ()):
|
|
if nb not in comp:
|
|
stack.append(nb)
|
|
seen |= comp
|
|
shells.append(comp)
|
|
return shells
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Operatoren: Vorbereiten
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ASSETUTILS_OT_prepare(Operator):
|
|
bl_idname = "assetutils.prepare"
|
|
bl_label = "Transform anwenden + Ursprung unten"
|
|
bl_description = ("Wendet Rotation und Skalierung an und setzt den Ursprung "
|
|
"unten-mittig - so steht das Asset in UE bei Scale 1 auf dem Boden")
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
s = context.scene.asset_utils_settings
|
|
targets = [o for o in context.selected_objects if o.type == 'MESH']
|
|
if not targets:
|
|
self.report({'ERROR'}, "Kein Mesh ausgewaehlt.")
|
|
return {'CANCELLED'}
|
|
_ensure_object_mode(context)
|
|
scene = context.scene
|
|
prev_cursor = scene.cursor.location.copy()
|
|
for ob in targets:
|
|
_activate(context, ob)
|
|
if s.apply_transforms:
|
|
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
|
|
if s.origin_bottom:
|
|
mw = ob.matrix_world
|
|
co = [mw @ v.co for v in ob.data.vertices]
|
|
if co:
|
|
cx = sum(c.x for c in co) / len(co)
|
|
cy = sum(c.y for c in co) / len(co)
|
|
zmin = min(c.z for c in co)
|
|
scene.cursor.location = (cx, cy, zmin)
|
|
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
|
|
if s.recalc_normals:
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
bpy.ops.mesh.normals_make_consistent(inside=False)
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
scene.cursor.location = prev_cursor
|
|
self.report({'INFO'}, "%d Asset(s) vorbereitet." % len(targets))
|
|
return {'FINISHED'}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Operatoren: Vertex-Farben
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ASSETUTILS_OT_bake_vcol(Operator):
|
|
bl_idname = "assetutils.bake_vcol"
|
|
bl_label = "Vertex-Farben backen"
|
|
bl_description = ("Backt R=Cavity, G=Teil-/Moos-Maske, B=Hoehe, A=1 in das "
|
|
"Farbattribut 'Cavity'")
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
s = context.scene.asset_utils_settings
|
|
targets = [o for o in context.selected_objects if o.type == 'MESH']
|
|
if not targets:
|
|
self.report({'ERROR'}, "Kein Mesh ausgewaehlt.")
|
|
return {'CANCELLED'}
|
|
_ensure_object_mode(context)
|
|
notes = []
|
|
for ob in targets:
|
|
me = ob.data
|
|
if not me.polygons:
|
|
continue
|
|
layer = me.color_attributes.get("Cavity")
|
|
if layer is None:
|
|
layer = me.color_attributes.new(name="Cavity", type='BYTE_COLOR',
|
|
domain='CORNER')
|
|
me.color_attributes.active_color = layer
|
|
idx = me.color_attributes.find("Cavity")
|
|
if idx >= 0:
|
|
me.color_attributes.render_color_index = idx
|
|
|
|
_activate(context, ob)
|
|
# FALLE: bpy.ops.paint.vertex_color_set braucht den VERTEX-PAINT-Modus
|
|
# und schlaegt im Object-Mode mit "poll() failed" fehl. Steht es mit
|
|
# vertex_color_dirt im selben try-Block, wird der ganze Bake
|
|
# uebersprungen. Deshalb hier direkt in Python auf Weiss setzen -
|
|
# das geht immer und ist schneller.
|
|
for i in range(len(layer.data)):
|
|
layer.data[i].color = (1.0, 1.0, 1.0, 1.0)
|
|
try:
|
|
bpy.ops.paint.vertex_color_dirt(
|
|
blur_strength=1.0, blur_iterations=s.cavity_blur,
|
|
clean_angle=math.pi, dirt_angle=0.0,
|
|
dirt_only=False, normalize=True,
|
|
)
|
|
except RuntimeError as exc:
|
|
self.report({'WARNING'}, "%s: Cavity uebersprungen (%s)" % (ob.name, exc))
|
|
|
|
data = layer.data
|
|
cav_raw = [data[i].color[0] for i in range(len(data))]
|
|
zs = [v.co.z for v in me.vertices]
|
|
zmin, zmax = min(zs), max(zs)
|
|
span = max(zmax - zmin, 1e-6)
|
|
|
|
part = {}
|
|
if s.g_mode == 'SHELLS':
|
|
shells = _mesh_shells(me)
|
|
if len(shells) >= 2:
|
|
infos = []
|
|
for sh in shells:
|
|
vs = {vi for pi in sh for vi in me.polygons[pi].vertices}
|
|
zt = max(me.vertices[v].co.z for v in vs)
|
|
xs = [me.vertices[v].co.x for v in vs]
|
|
ys = [me.vertices[v].co.y for v in vs]
|
|
infos.append((sh, zt, max(max(xs) - min(xs), max(ys) - min(ys))))
|
|
top = max(infos, key=lambda t: (t[1], t[2]))
|
|
for sh, _, _ in infos:
|
|
for pi in sh:
|
|
part[pi] = 1.0 if sh is top[0] else 0.0
|
|
notes.append("%s: %d Teile" % (ob.name, len(shells)))
|
|
else:
|
|
# Nur ein Teil -> nichts markieren, statt etwas zu behaupten
|
|
part = {p.index: 0.0 for p in me.polygons}
|
|
notes.append("%s: 1 Teil (G=0)" % ob.name)
|
|
|
|
rng = random.Random(s.moss_seed)
|
|
vnoise = [rng.random() for _ in range(len(me.vertices))]
|
|
|
|
for poly in me.polygons:
|
|
nz = poly.normal.z
|
|
for li in poly.loop_indices:
|
|
vi = me.loops[li].vertex_index
|
|
c_raw = cav_raw[li]
|
|
r = max(0.0, min(1.0, 1.0 - (1.0 - c_raw) * s.cavity_strength))
|
|
b = max(0.0, min(1.0, (me.vertices[vi].co.z - zmin) / span))
|
|
if s.g_mode == 'SHELLS':
|
|
g = part.get(poly.index, 0.0)
|
|
else:
|
|
up = max(0.0, nz)
|
|
crev = 1.0 - c_raw
|
|
n = 1.0 - s.moss_noise + s.moss_noise * vnoise[vi]
|
|
g = max(0.0, min(1.0, (up * 0.7 + crev * 0.5) * n * s.moss_amount * 1.6))
|
|
data[li].color = (r, g, b, 1.0)
|
|
|
|
msg = "Vertex-Farben auf %d Mesh(es)." % len(targets)
|
|
if notes:
|
|
msg += " " + ", ".join(notes[:4])
|
|
self.report({'INFO'}, msg)
|
|
return {'FINISHED'}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Operatoren: Benennung
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ASSETUTILS_OT_rename(Operator):
|
|
bl_idname = "assetutils.rename"
|
|
bl_label = "Umbenennen"
|
|
bl_description = "Benennt die Auswahl nach <Basis>_<NN> und zieht die Mesh-Daten mit"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
s = context.scene.asset_utils_settings
|
|
base = s.rename_base.strip()
|
|
if not base:
|
|
self.report({'ERROR'}, "Bitte einen Basisnamen eingeben.")
|
|
return {'CANCELLED'}
|
|
targets = sorted([o for o in context.selected_objects if o.type == 'MESH'],
|
|
key=lambda o: o.name)
|
|
if not targets:
|
|
self.report({'ERROR'}, "Kein Mesh ausgewaehlt.")
|
|
return {'CANCELLED'}
|
|
for i, ob in enumerate(targets):
|
|
name = base if (len(targets) == 1 and not s.rename_numbered) \
|
|
else "%s_%02d" % (base, s.rename_start + i)
|
|
ob.name = name
|
|
ob.data.name = name
|
|
self.report({'INFO'}, "%d Objekt(e) umbenannt." % len(targets))
|
|
return {'FINISHED'}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Operatoren: Asset-Check
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ASSETUTILS_OT_check(Operator):
|
|
bl_idname = "assetutils.check"
|
|
bl_label = "Asset-Check"
|
|
bl_description = ("Prueft die Auswahl auf die Dinge, die in UE Aerger machen: "
|
|
"Transform, Ursprung, Tri-Budget, UV, Vertex-Farben, Normalen")
|
|
bl_options = {'REGISTER'}
|
|
|
|
def execute(self, context):
|
|
s = context.scene.asset_utils_settings
|
|
targets = [o for o in context.selected_objects if o.type == 'MESH']
|
|
if not targets:
|
|
self.report({'ERROR'}, "Kein Mesh ausgewaehlt.")
|
|
return {'CANCELLED'}
|
|
problems = 0
|
|
print("\n=== Asset-Check ===")
|
|
for ob in targets:
|
|
me = ob.data
|
|
issues = []
|
|
if any(abs(v - 1.0) > 1e-4 for v in ob.scale):
|
|
issues.append("Scale nicht angewendet %s"
|
|
% [round(v, 3) for v in ob.scale])
|
|
if any(abs(v) > 1e-4 for v in ob.rotation_euler):
|
|
issues.append("Rotation nicht angewendet")
|
|
zs = [(ob.matrix_world @ v.co).z - ob.location.z for v in me.vertices]
|
|
if zs and abs(min(zs)) > s.origin_tol:
|
|
issues.append("Ursprung nicht unten (min_z %.3f)" % min(zs))
|
|
t = _tris(me)
|
|
if s.tri_budget and t > s.tri_budget:
|
|
issues.append("%d Tris > Budget %d" % (t, s.tri_budget))
|
|
if not me.uv_layers:
|
|
issues.append("kein UV")
|
|
if not me.color_attributes:
|
|
issues.append("keine Vertex-Farben")
|
|
# link_faces gibt es nur in bmesh - auf einem Mesh ueber die
|
|
# tatsaechlich benutzten Vertex-Indizes gehen.
|
|
used = {vi for poly in me.polygons for vi in poly.vertices}
|
|
n_loose = len(me.vertices) - len(used)
|
|
if n_loose:
|
|
issues.append("%d lose Vertices" % n_loose)
|
|
if me.has_custom_normals:
|
|
issues.append("Custom Split Normals (UE ggf. 'Import Normals' noetig)")
|
|
shells = len(_mesh_shells(me))
|
|
info = "%-24s %5d Tris %d Teil(e)" % (ob.name, t, shells)
|
|
if issues:
|
|
problems += 1
|
|
print(" %s\n -> %s" % (info, "; ".join(issues)))
|
|
else:
|
|
print(" %s ok" % info)
|
|
print("=== %d von %d Asset(s) mit Hinweisen ===\n" % (problems, len(targets)))
|
|
if problems:
|
|
self.report({'WARNING'},
|
|
"%d von %d Asset(s) mit Hinweisen - Details in der System-Konsole."
|
|
% (problems, len(targets)))
|
|
else:
|
|
self.report({'INFO'}, "Alle %d Asset(s) in Ordnung." % len(targets))
|
|
return {'FINISHED'}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Operatoren: Export
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ASSETUTILS_OT_export(Operator):
|
|
bl_idname = "assetutils.export"
|
|
bl_label = "FBX exportieren"
|
|
bl_description = "Exportiert jedes ausgewaehlte Mesh als eigene FBX (EcoGame-Settings)"
|
|
bl_options = {'REGISTER'}
|
|
|
|
def execute(self, context):
|
|
s = context.scene.asset_utils_settings
|
|
folder = bpy.path.abspath(s.export_dir) if s.export_dir else ""
|
|
if not folder or not os.path.isdir(folder):
|
|
self.report({'ERROR'}, "Bitte einen gueltigen Export-Ordner waehlen.")
|
|
return {'CANCELLED'}
|
|
targets = [o for o in context.selected_objects if o.type == 'MESH']
|
|
if not targets:
|
|
self.report({'ERROR'}, "Kein Mesh ausgewaehlt.")
|
|
return {'CANCELLED'}
|
|
_ensure_object_mode(context)
|
|
done = 0
|
|
for ob in targets:
|
|
_activate(context, ob)
|
|
fp = os.path.join(folder, ob.name + ".fbx")
|
|
try:
|
|
bpy.ops.export_scene.fbx(
|
|
filepath=fp, use_selection=True, object_types={'MESH'},
|
|
use_mesh_modifiers=True,
|
|
mesh_smooth_type='FACE' if s.smooth_type == 'FACE' else 'EDGE',
|
|
use_triangles=True,
|
|
colors_type='SRGB', # Vertex-Farben mitnehmen
|
|
add_leaf_bones=False, bake_anim=False, path_mode='AUTO',
|
|
)
|
|
done += 1
|
|
except RuntimeError as exc:
|
|
self.report({'WARNING'}, "%s: %s" % (ob.name, exc))
|
|
self.report({'INFO'}, "%d FBX nach %s" % (done, folder))
|
|
return {'FINISHED'}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Properties
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class AssetUtilsSettings(PropertyGroup):
|
|
apply_transforms: BoolProperty(name="Rotation + Scale anwenden", default=True)
|
|
origin_bottom: BoolProperty(name="Ursprung unten-mittig", default=True)
|
|
recalc_normals: BoolProperty(name="Normalen neu berechnen", default=False)
|
|
|
|
g_mode: EnumProperty(
|
|
name="G-Kanal",
|
|
items=[
|
|
('SHELLS', "Teil-Maske", "Oberster getrennter Teil = 1 (z.B. Pilzhut)"),
|
|
('MOSS', "Moos/Schmutz", "Oberseite + Ritzen + Rauschen"),
|
|
],
|
|
default='SHELLS',
|
|
)
|
|
cavity_strength: FloatProperty(name="Cavity-Staerke", default=0.7, min=0.0, max=1.0)
|
|
cavity_blur: IntProperty(name="Weichzeichnen", default=1, min=0, max=5)
|
|
moss_amount: FloatProperty(name="Moos-Menge", default=0.55, min=0.0, max=1.0)
|
|
moss_noise: FloatProperty(name="Moos-Aufbruch", default=0.55, min=0.0, max=1.0)
|
|
moss_seed: IntProperty(name="Moos-Seed", default=0, min=0, max=9999)
|
|
|
|
rename_base: StringProperty(name="Basisname", default="")
|
|
rename_start: IntProperty(name="Startnummer", default=1, min=0, max=99)
|
|
rename_numbered: BoolProperty(name="Immer nummerieren", default=False)
|
|
|
|
tri_budget: IntProperty(
|
|
name="Tri-Budget", default=2500, min=0, max=100000,
|
|
description="0 = nicht pruefen. ASSETS.md: Gras 400, Busch 800, Baum 2500",
|
|
)
|
|
origin_tol: FloatProperty(name="Ursprung-Toleranz (m)", default=0.01,
|
|
min=0.0, max=0.5)
|
|
|
|
export_dir: StringProperty(name="Export-Ordner", default="", subtype='DIR_PATH')
|
|
smooth_type: EnumProperty(
|
|
name="Smoothing",
|
|
items=[('FACE', "Faceted", "Harte Facetten"),
|
|
('EDGE', "Edge", "Weiche Kanten mit Sharp-Edges")],
|
|
default='FACE',
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Panels (Haupt-Panel + einklappbare Unter-Panels)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class VIEW3D_PT_asset_utils(Panel):
|
|
bl_label = "Asset Utils"
|
|
bl_idname = "VIEW3D_PT_asset_utils"
|
|
bl_space_type = 'VIEW_3D'
|
|
bl_region_type = 'UI'
|
|
bl_category = "Asset Utils"
|
|
|
|
def draw(self, context):
|
|
n = len([o for o in context.selected_objects if o.type == 'MESH'])
|
|
self.layout.label(text="%d Mesh(es) ausgewaehlt" % n,
|
|
icon='OUTLINER_OB_MESH' if n else 'INFO')
|
|
|
|
|
|
class _Sub(Panel):
|
|
bl_space_type = 'VIEW_3D'
|
|
bl_region_type = 'UI'
|
|
bl_category = "Asset Utils"
|
|
bl_parent_id = "VIEW3D_PT_asset_utils"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
|
|
|
|
class VIEW3D_PT_au_prepare(_Sub):
|
|
bl_label = "1 - Vorbereiten"
|
|
bl_options = set() # offen lassen
|
|
|
|
def draw(self, context):
|
|
s = context.scene.asset_utils_settings
|
|
col = self.layout.column(align=True)
|
|
col.prop(s, "apply_transforms")
|
|
col.prop(s, "origin_bottom")
|
|
col.prop(s, "recalc_normals")
|
|
self.layout.operator("assetutils.prepare", icon='OBJECT_ORIGIN')
|
|
|
|
|
|
class VIEW3D_PT_au_vcol(_Sub):
|
|
bl_label = "2 - Vertex-Farben"
|
|
bl_options = set()
|
|
|
|
def draw(self, context):
|
|
s = context.scene.asset_utils_settings
|
|
lay = self.layout
|
|
lay.prop(s, "g_mode", expand=True)
|
|
col = lay.column(align=True)
|
|
col.prop(s, "cavity_strength")
|
|
col.prop(s, "cavity_blur")
|
|
if s.g_mode == 'MOSS':
|
|
col.separator()
|
|
col.prop(s, "moss_amount")
|
|
col.prop(s, "moss_noise")
|
|
col.prop(s, "moss_seed")
|
|
lay.label(text="R=Cavity G=%s B=Hoehe"
|
|
% ("Teil" if s.g_mode == 'SHELLS' else "Moos"), icon='INFO')
|
|
lay.operator("assetutils.bake_vcol", icon='BRUSH_DATA')
|
|
|
|
|
|
class VIEW3D_PT_au_name(_Sub):
|
|
bl_label = "3 - Benennung"
|
|
|
|
def draw(self, context):
|
|
s = context.scene.asset_utils_settings
|
|
col = self.layout.column(align=True)
|
|
col.prop(s, "rename_base")
|
|
row = col.row(align=True)
|
|
row.prop(s, "rename_start")
|
|
row.prop(s, "rename_numbered", text="", icon='LINENUMBERS_ON')
|
|
self.layout.operator("assetutils.rename", icon='SORTALPHA')
|
|
|
|
|
|
class VIEW3D_PT_au_check(_Sub):
|
|
bl_label = "4 - Pruefen"
|
|
bl_options = set()
|
|
|
|
def draw(self, context):
|
|
s = context.scene.asset_utils_settings
|
|
col = self.layout.column(align=True)
|
|
col.prop(s, "tri_budget")
|
|
col.prop(s, "origin_tol")
|
|
self.layout.operator("assetutils.check", icon='CHECKMARK')
|
|
|
|
|
|
class VIEW3D_PT_au_export(_Sub):
|
|
bl_label = "5 - Export"
|
|
|
|
def draw(self, context):
|
|
s = context.scene.asset_utils_settings
|
|
col = self.layout.column(align=True)
|
|
col.prop(s, "export_dir")
|
|
col.prop(s, "smooth_type", expand=True)
|
|
self.layout.operator("assetutils.export", icon='EXPORT')
|
|
self.layout.label(text="UE: Vertex Color = Replace", icon='INFO')
|
|
|
|
|
|
classes = (
|
|
AssetUtilsSettings,
|
|
ASSETUTILS_OT_prepare,
|
|
ASSETUTILS_OT_bake_vcol,
|
|
ASSETUTILS_OT_rename,
|
|
ASSETUTILS_OT_check,
|
|
ASSETUTILS_OT_export,
|
|
VIEW3D_PT_asset_utils,
|
|
VIEW3D_PT_au_prepare,
|
|
VIEW3D_PT_au_vcol,
|
|
VIEW3D_PT_au_name,
|
|
VIEW3D_PT_au_check,
|
|
VIEW3D_PT_au_export,
|
|
)
|
|
|
|
|
|
def register():
|
|
for cls in classes:
|
|
bpy.utils.register_class(cls)
|
|
bpy.types.Scene.asset_utils_settings = PointerProperty(type=AssetUtilsSettings)
|
|
|
|
|
|
def unregister():
|
|
del bpy.types.Scene.asset_utils_settings
|
|
for cls in reversed(classes):
|
|
bpy.utils.unregister_class(cls)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
register()
|