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"}