328 lines
12 KiB
Python
328 lines
12 KiB
Python
bl_info = {
|
|
"name": "LancerEdit Model Utility Plugin",
|
|
"author": "Darklighter",
|
|
"version": (0, 0, 2),
|
|
"blender": (4, 5, 3),
|
|
"location": "3D-Viewport > Sidebar > FLLancerEdit Utility",
|
|
"description": "Non-offical LancerEdit Model Utility Plugin for PC-Game Freelancer",
|
|
"category": "Development",
|
|
}
|
|
|
|
import bmesh
|
|
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 = "Convert to Hulls/Sur Objects"
|
|
bl_description = "Select Child Objects and assign to hull suffix"
|
|
|
|
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 0
|
|
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
|
|
|
|
# Prepare the special material
|
|
material_name = "material_0x00000000"
|
|
material = bpy.data.materials.get(material_name)
|
|
if material is None:
|
|
material = bpy.data.materials.new(name=material_name)
|
|
# Set viewport display color
|
|
material.diffuse_color = (1.0, 0.0, 0.0, 0.5) # RGBA
|
|
# For newer Blender versions, ensure the viewport display color is set
|
|
if hasattr(material, 'viewport_color'):
|
|
material.viewport_color = (1.0, 0.0, 0.0)
|
|
# Set material display options
|
|
material.blend_method = 'BLEND' # ensure transparency is visible
|
|
# For Eevee, ensure transparency is enabled
|
|
# (optional, depending on your render engine)
|
|
|
|
# Assign the material to each selected child object
|
|
for obj in selected_children:
|
|
# Assign the material
|
|
if obj.type == 'MESH':
|
|
if material.name not in [mat.name for mat in obj.data.materials]:
|
|
obj.data.materials.append(material)
|
|
|
|
# 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 OBJECT_OT_remove_hull_material(bpy.types.Operator):
|
|
bl_idname = "object.remove_hull_material"
|
|
bl_label = "Remove Hull Material"
|
|
bl_description = "Remove materials from selected objects"
|
|
|
|
def execute(self, context):
|
|
selected_objects = context.selected_objects
|
|
|
|
for obj in selected_objects:
|
|
if obj.type == 'MESH' and obj.data.materials:
|
|
obj.data.materials.clear()
|
|
|
|
self.report({'INFO'}, "Materials removed from selected objects")
|
|
return {'FINISHED'}
|
|
|
|
class OBJECT_OT_create_convex(bpy.types.Operator):
|
|
bl_idname = "object.create_convex"
|
|
bl_label = "Create Convex"
|
|
bl_description = "Delete faces and edges, then create a convex hull of the active object"
|
|
|
|
def execute(self, context):
|
|
obj = context.active_object
|
|
if not obj or obj.type != 'MESH':
|
|
self.report({'WARNING'}, "No active mesh object selected")
|
|
return {'CANCELLED'}
|
|
|
|
# Make object active and selected
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
obj.select_set(True)
|
|
context.view_layer.objects.active = obj
|
|
|
|
# Enter edit mode
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
mesh = bmesh.from_edit_mesh(obj.data)
|
|
|
|
# Delete all faces and edges
|
|
bmesh.ops.delete(mesh, geom=[f for f in mesh.faces] + [e for e in mesh.edges], context='EDGES_FACES')
|
|
bmesh.update_edit_mesh(obj.data)
|
|
|
|
# Exit edit mode
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
# Now, perform the convex hull operation on the vertices
|
|
# Create a new bmesh from the current mesh
|
|
bm = bmesh.new()
|
|
bm.from_mesh(obj.data)
|
|
|
|
# Perform convex hull
|
|
bmesh.ops.convex_hull(bm, input=bm.verts)
|
|
|
|
# Write back to the mesh
|
|
bm.to_mesh(obj.data)
|
|
bm.free()
|
|
|
|
self.report({'INFO'}, "Convex hull created")
|
|
return {'FINISHED'}
|
|
|
|
# Alternatively, directly run the convex hull operator:
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
obj.select_set(True)
|
|
context.view_layer.objects.active = obj
|
|
bpy.ops.object.convert(target='MESH')
|
|
bpy.ops.mesh.convex_hull()
|
|
|
|
self.report({'INFO'}, "Convex hull created")
|
|
return {'FINISHED'}
|
|
|
|
class OBJECT_OT_add_parent_component(bpy.types.Operator):
|
|
bl_idname = "object.add_parent_component"
|
|
bl_label = "Add Parent Component"
|
|
bl_description = "Set the parent of the selected object to specified object"
|
|
|
|
parent_object_name: bpy.props.StringProperty(
|
|
name="Parent Object Name",
|
|
description="Name of the object to set as parent",
|
|
default=""
|
|
)
|
|
|
|
def execute(self, context):
|
|
parent_obj = bpy.data.objects.get(self.parent_object_name)
|
|
if not parent_obj:
|
|
self.report({'WARNING'}, f"Object '{self.parent_object_name}' not found.")
|
|
return {'CANCELLED'}
|
|
for obj in context.selected_objects:
|
|
obj.parent = parent_obj
|
|
self.report({'INFO'}, f"Parent set to '{self.parent_object_name}' for selected objects.")
|
|
return {'FINISHED'}
|
|
|
|
def invoke(self, context, event):
|
|
return context.window_manager.invoke_props_dialog(self)
|
|
|
|
|
|
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="Surface Creation Kit:")
|
|
# Button for creating Convex Hull Object
|
|
layout.operator("object.create_convex")
|
|
layout.separator()
|
|
# Input Dialogue for Sur Name
|
|
layout.prop(scene, "child_object_new_name")
|
|
# Button for Convert to Hull
|
|
layout.operator("object.rename_selected_child")
|
|
layout.separator()
|
|
layout.operator("object.remove_hull_material")
|
|
layout.separator()
|
|
layout.operator("object.add_parent_component")
|
|
|
|
|
|
def register():
|
|
bpy.utils.register_class(LancerEditHelpersPanel)
|
|
bpy.utils.register_class(RenameObjectsAndMeshesOperator)
|
|
bpy.utils.register_class(RenameChildObjectOperator)
|
|
bpy.utils.register_class(OBJECT_OT_create_convex)
|
|
bpy.utils.register_class(OBJECT_OT_remove_hull_material)
|
|
bpy.utils.register_class(OBJECT_OT_add_parent_component)
|
|
|
|
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="Sur 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)
|
|
bpy.utils.unregister_class(OBJECT_OT_create_convex)
|
|
bpy.utils.unregister_class(OBJECT_OT_remove_hull_material)
|
|
bpy.utils.register_class(OBJECT_OT_add_parent_component)
|
|
del bpy.types.Scene.object_names
|
|
del bpy.types.Scene.new_name
|
|
del bpy.types.Scene.child_object_new_name
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
register() |