diff --git a/LancerEditUtility.py b/LancerEditUtility.py index 915e6ad..607ae1f 100644 --- a/LancerEditUtility.py +++ b/LancerEditUtility.py @@ -3,7 +3,195 @@ bl_info = { "author": "Darklighter", "version": (0, 0, 1), "blender": (4, 5, 3), - "location": "3D-Viewport > Sidebar > FLCT", + "location": "3D-Viewport > Sidebar > FLLancerEdit Utility", "description": "Non-offical LancerEdit Model Utility Plugin for PC-Game Freelancer", "category": "Development", -} \ No newline at end of file +} + +import bpy +import re + +class RenameObjectsAndMeshesOperator(bpy.types.Operator): + bl_idname = "object.rename_objects_and_meshes" + bl_label = "Rename Objects and Mesh Data" + bl_description = "Rename specified objects and their mesh data blocks, and their children" + + object_names: bpy.props.StringProperty( + name="Object Names", + description="Comma-separated list of object names to rename", + default="" + ) + new_name: bpy.props.StringProperty( + name="New Name", + description="New name for objects and their mesh data blocks", + default="" + ) + + def execute(self, context): + names_input = self.object_names.strip() + new_name = self.new_name.strip() + + if not names_input or not new_name: + self.report({'WARNING'}, "Please enter object names and a new name") + return {'CANCELLED'} + + names_list = [name.strip() for name in names_input.split(",") if name.strip()] + + for obj_name in names_list: + obj = bpy.data.objects.get(obj_name) + if obj: + # Rename the object + old_mesh_name = obj.data.name if obj.data else None + obj.name = new_name + + # Rename the mesh data block if it exists + if obj.data: + obj.data.name = new_name + + # Rename children objects starting with 'parent_name.' + for child in bpy.data.objects: + if child.name.startswith(f"{obj_name}."): + suffix = child.name[len(f"{obj_name}."):] + child.name = f"{new_name}.{suffix}" + # Also rename their mesh data if applicable + if child.data: + child.data.name = f"{new_name}.{suffix}" + + else: + self.report({'WARNING'}, f"Object '{obj_name}' not found.") + + self.report({'INFO'}, "Renaming of objects and mesh data completed.") + return {'FINISHED'} + + +class RenameChildObjectOperator(bpy.types.Operator): + bl_idname = "object.rename_selected_child" + bl_label = "Rename Selected Child (Hulls/Sur) Objects" + bl_description = "Rename the mesh data of the selected child object and assign a new suffix, filling gaps" + + def execute(self, context): + scene = context.scene + base_name = scene.child_object_new_name.strip() + + if not base_name: + self.report({'WARNING'}, "New name cannot be empty") + return {'CANCELLED'} + + selected_children = [obj for obj in context.selected_objects if obj.parent] + + if not selected_children: + self.report({'WARNING'}, "No selected child objects found") + return {'CANCELLED'} + + # Collect existing suffixes + pattern = re.compile(rf"{re.escape(base_name)}\.(\d+)\$hull") + used_suffixes = [] + + for o in bpy.data.objects: + match = pattern.match(o.name) + if match: + used_suffixes.append(int(match.group(1))) + + used_suffixes = sorted(set(used_suffixes)) + suffixes_to_use = [] + + # Fill in gaps starting from 1 + expected_suffix = 0 + for suffix in used_suffixes: + while expected_suffix < suffix: + suffixes_to_use.append(expected_suffix) + expected_suffix += 1 + expected_suffix = suffix + 1 + + # Now, assign suffixes: + suffix_index = 0 + + for obj in selected_children: + # Check if object already has a suffix + match = pattern.match(obj.name) + if match: + current_suffix = int(match.group(1)) + if current_suffix in suffixes_to_use: + # Already assigned, skip + continue + else: + # Object already named properly, skip + continue + else: + # Assign next available gap suffix or new suffix + if suffix_index < len(suffixes_to_use): + suffix_num = suffixes_to_use[suffix_index] + suffix_index += 1 + else: + suffix_num = expected_suffix + expected_suffix += 1 + + new_name = f"{base_name}.{suffix_num}$hull" + if obj.type == 'MESH' and obj.data: + obj.name = new_name + obj.data.name = new_name + else: + self.report({'WARNING'}, f"Object {obj.name} does not have mesh data, skipped.") + + self.report({'INFO'}, "Selected child objects renamed successfully") + return {'FINISHED'} + + +class LancerEditHelpersPanel(bpy.types.Panel): + bl_label = "LancerEdit Model Utility Plugin" + bl_idname = "OBJECT_PT_rename_objects_meshes" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = 'LancerEdit Utility' + + def draw(self, context): + layout = self.layout + scene = context.scene + + # Existing renaming section + layout.label(text="Rename Parent Object:") + layout.prop(scene, "object_names") + layout.prop(scene, "new_name") + op = layout.operator("object.rename_objects_and_meshes") + op.object_names = scene.object_names + op.new_name = scene.new_name + + # Section for renaming selected child object + layout.separator() + layout.label(text="Rename Selected Child (Hulls/Sur) Objects") + layout.prop(scene, "child_object_new_name") + layout.operator("object.rename_selected_child") + + +def register(): + bpy.utils.register_class(LancerEditHelpersPanel) + bpy.utils.register_class(RenameObjectsAndMeshesOperator) + bpy.utils.register_class(RenameChildObjectOperator) + + bpy.types.Scene.object_names = bpy.props.StringProperty( + name="Parent Name", + description="Comma-separated list of object names", + default="" + ) + bpy.types.Scene.new_name = bpy.props.StringProperty( + name="Change Name", + description="New name for objects and their mesh data", + default="" + ) + bpy.types.Scene.child_object_new_name = bpy.props.StringProperty( + name="Change Name", + description="New name for the selected child object", + default="" + ) + +def unregister(): + bpy.utils.unregister_class(LancerEditHelpersPanel) + bpy.utils.unregister_class(RenameObjectsAndMeshesOperator) + bpy.utils.unregister_class(RenameChildObjectOperator) + del bpy.types.Scene.object_names + del bpy.types.Scene.new_name + del bpy.types.Scene.child_object_new_name + + +if __name__ == "__main__": + register() \ No newline at end of file