1

Where is there an issue with my code? I want to merge the models under the empty object into one object and save it under each empty object, but after I run the code, it merges all the models into one model

enter image description here enter image description here

import bpy
import time

class MergeTestModelsOperator(bpy.types.Operator): bl_idname = "object.merge_test_models_operator" bl_label = "Merge Test Models" bl_options = {'REGISTER', 'UNDO'}

def mergemodel(self, mergeobj):

    # Merge other models into new objects
    for obj in mergeobj.children:
        if obj.type == 'MESH' and obj:
            obj.select_set(True)
            bpy.ops.object.join()
            bpy.context.view_layer.objects.active.name = mergeobj.name.replace("_MODEL", "")
            bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)

def execute(self, context):
    # Obtain all models in the scene
    objects = bpy.data.objects

    start = time.perf_counter()

    for obj in objects:
        if obj.type == 'EMPTY' and obj.name.find("_MODEL")!=-1:
            self.mergemodel(obj)
            # self.report({'INFO'}, f"EMPTY: {obj.name}.")
            continue

    end = time.perf_counter()
    runTime = end - start
    runTime_ms = runTime * 1000

    print(f"Time: {runTime}\n Time_ms: {runTime_ms}")
    self.report({'INFO'}, f"Time: {runTime}\n Time_ms: {runTime_ms}")

    # Display successful prompt information
    self.report({'INFO'}, "Test models merged and deleted successfully.")

    return{'FINISHED'}

class MergeTestModelsPanel(bpy.types.Panel): bl_label = "Merge Test Models Panel" bl_idname = "PT_MergeTestModelsPanel" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Tools' bl_context = "objectmode"

def draw(self, context):
    layout = self.layout

    # Add a button that will call the Merge Test Models Operator operator when clicked
    layout.operator("object.merge_test_models_operator")

def register(): bpy.utils.register_class(MergeTestModelsOperator) bpy.utils.register_class(MergeTestModelsPanel)

def unregister(): bpy.utils.unregister_class(MergeTestModelsOperator) bpy.utils.unregister_class(MergeTestModelsPanel)

if name == "main": register()

Gorgious
  • 30,723
  • 2
  • 44
  • 101
MA__AM
  • 15
  • 4
  • Hi. Please use proper code tags when posting code, so the code is well format and readable for any visitor – Duarte Farrajota Ramos Dec 19 '23 at 01:31
  • 1
    Hello. if obj.name.find("_MODEL")!=-1 =>> if "_MODEL" in obj.name :) – Gorgious Dec 19 '23 at 06:59
  • I modified my code, saved these models in the dictionary, and then looped through the dictionary to merge objects. However, after merging the models under one sub object, I canceled the selection of the merged object, (bpy.context.view_layer.objects.active.select_set(False))but the program no longer merged other objects. After commenting on this code, it merged all objects again. May I ask why this is @Gorgious – MA__AM Dec 19 '23 at 07:40
  • I don't understand why you need to mess with selection state. I suggested a solution with an operator override where you don't need it at all – Gorgious Dec 19 '23 at 12:28

1 Answers1

2

I think it's caused by selection state not being reset after each iteration. You select objects but don't unselect the previous ones. So the last empty children get joined with the previous ones.

You can either play with the selection state or use a context override which is IMO more suited and will work both for object.join and object.transform_apply.

import bpy

def mergemodel(self, mergeobj, context): objects_to_merge = [o for o in mergeobj.children if o.type == "MESH"] if len(objects_to_merge) < 2: return with context.temp_override(active_object=objects_to_merge[0], selected_editable_objects=objects_to_merge): bpy.ops.object.join() objects_to_merge[0].name = mergeobj.name.replace("_MODEL", "") bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)

def execute(self, context): objects = bpy.context.scene.objects
for obj in objects: if obj.type == 'EMPTY' and "_MODEL" in obj.name: self.mergemodel(obj, context)

Gorgious
  • 30,723
  • 2
  • 44
  • 101
  • mergedir = {} for obj in objects: if obj.type == 'MESH' and obj: if obj.parent.name not in mergedir: mergedir[obj.parent.name] = [] mergedir[obj.parent.name].append(obj) else: mergedir[obj.parent.name].append(obj)

    for key in mergedir.keys(): for value in mergedir[key]: value.select_set(True)

    bpy.ops.object.join()
    bpy.context.view_layer.objects.active.name = key.replace("_MODEL", "")
    bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
    
    – MA__AM Dec 19 '23 at 07:49
  • I can merge the models according to the rules using your method, but as the number of models increases, running the blender code will crash. Is this due to an issue with the blender program? – MA__AM Dec 19 '23 at 09:01
  • @MA__AM hello, please edit the question to add the relevant code, as it stands it's hard to know what you're trying to do.It is most certainly not an issue with the program but with your script :) – Gorgious Dec 19 '23 at 12:19
  • btw mergedir = {obj.name: obj.children for obj in objects} and for parent_name, children in mergedir.items():. I suggest you take a crash course on python programming to help you write code more efficiently :) – Gorgious Dec 19 '23 at 12:30