NEW: - auto gen. Emptys at mesh origin - fix naming convention from point to underscore - auto gen. Collections for Unreal export FIX: - "Fix Material Names" completely redone (pls test)
170 lines
7.5 KiB
Python
170 lines
7.5 KiB
Python
import bpy
|
||
import re
|
||
|
||
### FIX MATERIALS ###
|
||
|
||
class MESH_OT_fix_material_names(bpy.types.Operator):
|
||
"""Fixes the material naming, if duplicated are present e.g. Material.001, Material.002 ..."""
|
||
|
||
bl_idname = "mesh.fix_material_names"
|
||
bl_label = "Fixes the material naming, if duplicated are present"
|
||
bl_options = {"REGISTER", "UNDO"}
|
||
|
||
def execute(self, context):
|
||
#Remove all duplicated materials
|
||
self.merge_duplicated_materials()
|
||
|
||
#Merge material slots for every mesh in the scene
|
||
#for obj in bpy.context.scene.objects:
|
||
#if obj.type == "MESH":
|
||
#self.merge_material_slots(obj)
|
||
|
||
self.report({'INFO'}, 'All duplicated Materials fixed')
|
||
return {"FINISHED"}
|
||
|
||
def merge_duplicated_materials(self):
|
||
for material in bpy.data.materials:
|
||
if material.name[-3].isnumeric():
|
||
opti_matName = material.name[:-4]
|
||
if bpy.data.materials.get(opti_matName): #check if og_mat exists
|
||
material.user_remap(bpy.data.materials.get(opti_matName))
|
||
print("Removed Material: " + material.name)
|
||
bpy.data.materials.remove(material)
|
||
else:
|
||
material.name = opti_matName
|
||
|
||
def merge_material_slots(self, obj):
|
||
duplicated_material_list = []
|
||
|
||
#create list with indexes of material slots with the same name
|
||
for og_slot in obj.material_slots:
|
||
for slot in obj.material_slots:
|
||
if slot.name == og_slot.name:
|
||
if slot.slot_index == og_slot.slot_index:
|
||
continue
|
||
if og_slot.slot_index in duplicated_material_list:
|
||
continue
|
||
duplicated_material_list.append(int(slot.slot_index))
|
||
|
||
#delete all material slots within list
|
||
for slot_index in sorted(duplicated_material_list, reverse=True):
|
||
obj.data.materials.pop(index = slot_index)
|
||
|
||
|
||
### GROUP OBJECTS TO COLLECTIONS ###
|
||
|
||
class MESH_OT_group_objects_in_collections(bpy.types.Operator):
|
||
"""Groups objects by name into seperate collections, usefull for Unreal Decogon import"""
|
||
|
||
bl_idname = "mesh.group_objects_in_collections"
|
||
bl_label = "Groups objects by name into seperate collections, usefull for Unreal Decogon import"
|
||
bl_options = {"REGISTER", "UNDO"}
|
||
|
||
def execute(self, context):
|
||
# Erstellen einer leeren Dictionary zur Speicherung der Collections nach Basisnamen
|
||
collection_dict = {}
|
||
|
||
# Durchlaufen aller Objekte in der Szene
|
||
for obj in bpy.context.scene.objects:
|
||
# Überprüfen, ob das Objekt ein leeres Elternobjekt ist
|
||
if obj.type == 'EMPTY':
|
||
# Anwenden der Regex auf den Objektnamen
|
||
match = re.match(r'^(.*?)_(.*?)_(\d+)([a-zA-Z]+)$', obj.name)
|
||
if match:
|
||
prefix = match.group(1)
|
||
name = match.group(2)
|
||
number = match.group(3)
|
||
letter = match.group(4)
|
||
|
||
# Extrahieren des Basisnamens für die Collection
|
||
base_name = f"L1960_{name}"
|
||
|
||
# Hinzufügen des Objekts zur entsprechenden Liste in der Dictionary
|
||
if base_name not in collection_dict:
|
||
collection_dict[base_name] = {}
|
||
if number not in collection_dict[base_name]:
|
||
collection_dict[base_name][number] = {}
|
||
if letter not in collection_dict[base_name][number]:
|
||
collection_dict[base_name][number][letter] = {'empty': obj, 'children': []}
|
||
|
||
# Durchlaufen der Dictionary und Erstellen der Collections
|
||
for base_name, number_dict in collection_dict.items():
|
||
# Erstellen einer neuen Collection für den Basisnamen
|
||
base_collection = bpy.data.collections.new(base_name)
|
||
bpy.context.scene.collection.children.link(base_collection)
|
||
|
||
# Durchlaufen der sortierten Liste der Objekte und Verschieben in die Collections
|
||
for number, letter_dict in number_dict.items():
|
||
# Erstellen einer Collection für die Nummer und Verschieben des leeren Elternobjekts dorthin
|
||
number_collection = bpy.data.collections.new(f"{base_name}_{number}")
|
||
base_collection.children.link(number_collection)
|
||
|
||
# Durchlaufen der Buchstaben und Erstellen der Buchstaben-Collection unter der Nummer
|
||
for letter, obj_data in letter_dict.items():
|
||
empty = obj_data['empty']
|
||
children = empty.children
|
||
|
||
letter_collection = bpy.data.collections.new(f"{base_name}_{number}{letter}")
|
||
number_collection.children.link(letter_collection)
|
||
|
||
# Verschieben des leeren Elternobjekts in die entsprechende Collection
|
||
letter_collection.objects.link(empty)
|
||
|
||
# Verschieben der Kinder des leeren Elternobjekts in die entsprechende Collection
|
||
for child in children:
|
||
letter_collection.objects.link(child)
|
||
|
||
# Entfernen des leeren Elternobjekts und seiner Kinder aus der Szene
|
||
bpy.context.collection.objects.unlink(empty)
|
||
for child in children:
|
||
bpy.context.collection.objects.unlink(child)
|
||
|
||
self.report({'INFO'}, 'All objects sorted')
|
||
return {"FINISHED"}
|
||
|
||
### FIX NAMING CONVENTIONS ###
|
||
|
||
class MESH_OT_fix_naming_conventions(bpy.types.Operator):
|
||
"""Changes . to _ to solve naming issues with workbench"""
|
||
|
||
bl_idname = "mesh.fix_naming_convention"
|
||
bl_label = "Changes . to _ to solve naming issues with workbench"
|
||
bl_options = {"REGISTER", "UNDO"}
|
||
|
||
def execute(self, context):
|
||
for obj in bpy.data.objects:
|
||
obj.name = obj.name.replace(".","_")
|
||
|
||
self.report({'INFO'}, 'Fixed Naming')
|
||
return {"FINISHED"}
|
||
|
||
class MESH_OT_generate_empty_for_mesh(bpy.types.Operator):
|
||
"""Generates a Empty with the objects name at it´s position"""
|
||
|
||
bl_idname = "mesh.generate_empty_for_mesh"
|
||
bl_label = "Generates a Empty with the objects name at it´s position"
|
||
bl_options = {"REGISTER", "UNDO"}
|
||
|
||
def execute(self, context):
|
||
scene = bpy.context.scene
|
||
Selection = bpy.context.selected_objects
|
||
FilteredSelection = [obj for obj in Selection if obj.type != 'EMPTY']
|
||
ActiveObj = bpy.context.active_object
|
||
|
||
for obj in FilteredSelection:
|
||
oldEmpty = scene.objects.get("Socket_" + obj.name)
|
||
if oldEmpty:
|
||
oldEmpty.location = obj.location
|
||
oldEmpty.rotation_euler = obj.rotation_euler
|
||
else:
|
||
obj.select_set(True)
|
||
bpy.ops.object.empty_add(type='PLAIN_AXES', align='WORLD', location=obj.location, scale=(1, 1, 1), rotation=obj.rotation_euler)
|
||
empty = bpy.context.active_object
|
||
empty.name = "Socket_" + obj.name
|
||
|
||
#Change back to selection and select old active
|
||
bpy.ops.object.select_all(action='DESELECT')
|
||
|
||
self.report({'INFO'}, 'Generated Emptys for selectes Meshes in Scene')
|
||
return {"FINISHED"}
|