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