From 5c02f0fe2bff2f8fce7d413e635e8040d1814b9f Mon Sep 17 00:00:00 2001 From: "jonatan.bijl" Date: Wed, 10 Jun 2015 10:08:33 +0200 Subject: [PATCH 1/2] The script didn't work for me; it gave me a "convertViewVec: called in an invalid context " when joining the objects. I replaced the operator call with direct data handling (which should also be faster). --- multi_object_uv_edit.py | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/multi_object_uv_edit.py b/multi_object_uv_edit.py index 2008aaf..c49d694 100644 --- a/multi_object_uv_edit.py +++ b/multi_object_uv_edit.py @@ -114,38 +114,31 @@ def select_vertex_group(self,ob,group_name): bpy.ops.object.mode_set(mode='EDIT') def merge_selected_objects(self,context): - objects = [] + objects = list(context.selected_objects) dupli_objects = [] - active_object = None ### deselect objects - active_object = context.scene.objects.active - for ob in context.selected_objects: - objects.append(ob) + for ob in objects: ob.select = False - - for i,ob in enumerate(objects): - ob.select = True - context.scene.objects.active = ob - - bpy.ops.object.duplicate_move() - dupli_ob = context.active_object + #no need to use modifier for copying, + #also no need to duplicate the meshes. We will duplicate only for the object we'll be joining into, + #and that we do later on. + dupli_ob = ob.copy() + context.scene.objects.link(dupli_ob) dupli_objects.append(dupli_ob) for group in dupli_ob.vertex_groups: dupli_ob.vertex_groups.remove(group) - - v_group = dupli_ob.vertex_groups.new(name=ob.name) v_group.add(range(len(dupli_ob.data.vertices)),1,"REPLACE") - dupli_ob.select = False - context.scene.objects.active = None + #select all the new objects, and make the first one active, so we can do a join for ob in dupli_objects: ob.select = True - context.scene.objects.active = context.selected_objects[0] + self.multi_object = context.scene.objects.active = dupli_objects[0] + #copy the mesh, because we will join into that mesh + self.multi_object.data = self.multi_object.data.copy() bpy.ops.object.join() - self.multi_object = context.active_object self.multi_object.name = "Multi_UV_Object" def modal(self, context, event): From 731cff7d4fc4a3b8629abb244f82cbcc81f1fb20 Mon Sep 17 00:00:00 2001 From: "jonatan.bijl" Date: Wed, 10 Jun 2015 17:59:41 +0200 Subject: [PATCH 2/2] - renamed "object" to "obj" to avoid confusion with python's object class - Fixed bug where it would crash if one of the objects had 0 verts - Fixed bug where it would only copy the active layer, even if multiple new uv layers were added - Rely more on self.active_object and self.initial_objects, instead of context.active_object and context.selected_objects --- multi_object_uv_edit.py | 145 ++++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 74 deletions(-) diff --git a/multi_object_uv_edit.py b/multi_object_uv_edit.py index bf2722b..466b1e9 100644 --- a/multi_object_uv_edit.py +++ b/multi_object_uv_edit.py @@ -32,8 +32,14 @@ import bpy from bpy.props import IntProperty, FloatProperty +def get_selected_mesh_objects(context): + return [obj for obj in context.selected_objects if obj.type=='MESH'] - +def deselect_all(context): + for obj in context.selected_objects: + obj.select = False + + class MultiObjectUVEdit(bpy.types.Operator): """This operator gives you the ability to edit the uv of multiple objects at once.""" bl_idname = "object.multi_object_uv_edit" @@ -45,60 +51,56 @@ class MultiObjectUVEdit(bpy.types.Operator): active_object = None def leave_editing_mode(self,context): - selected_objects = [] - active_object = None + deselect_all(context) + mesh_select_mode = list(context.tool_settings.mesh_select_mode) context.tool_settings.mesh_select_mode = (True,False,False) - ### create array of tmp selected objects - for object in context.selected_objects: - selected_objects.append(object) - active_object = context.scene.objects.active - ### copy uvs based on the vertex groups to its final object - for v_group in self.multi_object.vertex_groups: - + for v_group in self.multi_object.vertex_groups: ### select object vertex group and separate mesh into its own object - self.select_vertex_group(self.multi_object,v_group.name) - bpy.ops.mesh.separate(type="SELECTED") - tmp_obj = context.selected_objects[0] - tmp_obj.name = v_group.name+"_tmp" - - - ### go into object mode select newely created object and transfer the uv's to its final object - bpy.ops.object.mode_set(mode='OBJECT') - - for object in context.selected_objects: - object.select = False + num_verts = self.select_vertex_group(self.multi_object,v_group.name) + if num_verts > 0: + bpy.ops.mesh.separate(type="SELECTED") + tmp_obj = context.selected_objects[0] #we had nothing selected before, so this should be the separated object + tmp_obj.name = v_group.name+"_tmp" - tmp_obj.select = True - context.scene.objects.active = tmp_obj - bpy.data.objects[v_group.name].hide = False - bpy.data.objects[v_group.name].select = True - if len(tmp_obj.data.uv_textures) > 0: - if tmp_obj.data.uv_textures.active.name not in bpy.data.objects[v_group.name].data.uv_textures: - new_uv_layer = bpy.data.objects[v_group.name].data.uv_textures.new(tmp_obj.data.uv_textures.active.name) - bpy.data.objects[v_group.name].data.uv_textures.active = new_uv_layer - else: - bpy.data.objects[v_group.name].data.uv_textures.active = bpy.data.objects[v_group.name].data.uv_textures[self.multi_object.data.uv_textures.active.name] - bpy.ops.object.join_uvs() + ### go into object mode select newely created object and transfer the uv's to its final object + bpy.ops.object.mode_set(mode='OBJECT') - ### delete the tmp object - bpy.data.objects[v_group.name].select = False - tmp_obj.select = False - context.scene.objects.active = active_object - bpy.context.scene.objects.unlink(tmp_obj) - bpy.data.objects.remove(tmp_obj) - bpy.ops.object.mode_set(mode='EDIT') + deselect_all(context) + + #find original object and unhide it, otherwise we can't use an operator on it + original_obj = bpy.data.objects[v_group.name] + original_obj.select = True + original_obj.hide = False + tmp_obj.select = True + context.scene.objects.active = tmp_obj + for tmp_tex in tmp_obj.data.uv_textures: + if tmp_tex.name not in original_obj.data.uv_textures: + new_uv_layer = original_obj.data.uv_textures.new(tmp_tex.name) + original_obj.data.uv_textures.active = new_uv_layer + else: + original_obj.data.uv_textures.active = original_obj.data.uv_textures[tmp_tex.name] + tmp_obj.data.uv_textures.active = tmp_tex + bpy.ops.object.join_uvs() + + ### delete the tmp object and return to editing our multi_object + original_obj.select = False + tmp_obj.select = False + context.scene.objects.active = self.multi_object + bpy.context.scene.objects.unlink(tmp_obj) + bpy.data.objects.remove(tmp_obj) + bpy.ops.object.mode_set(mode='EDIT') ### restore everything context.tool_settings.mesh_select_mode = mesh_select_mode bpy.ops.object.mode_set(mode="OBJECT") bpy.context.scene.objects.unlink(self.multi_object) bpy.data.objects.remove(self.multi_object) - for i,object in enumerate(self.initial_objects): - object.select = True - object.hide_render = self.initial_objects_hide_render[i] + for i, obj in enumerate(self.initial_objects): + obj.select = True + obj.hide_render = self.initial_objects_hide_render[i] context.scene.objects.active = self.active_object @@ -108,22 +110,28 @@ def select_vertex_group(self,ob,group_name): bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.object.mode_set(mode='OBJECT') + num_selected_verts = 0 for i,vert in enumerate(ob.data.vertices): try: ob.vertex_groups[group_name].weight(i) vert.select = True + num_selected_verts += 1 except: pass bpy.ops.object.mode_set(mode='EDIT') + return num_selected_verts def merge_selected_objects(self,context): - objects = list(context.selected_objects) dupli_objects = [] + ### deselect objects - for ob in objects: + for ob in self.initial_objects: ob.select = False - for i,ob in enumerate(objects): + for ob in self.initial_objects: + #no need to use modifier for copying, + #also no need to duplicate the meshes. We will duplicate only for the object we'll be joining into, + #and that we do later on. dupli_ob = ob.copy() context.scene.objects.link(dupli_ob) dupli_objects.append(dupli_ob) @@ -140,10 +148,8 @@ def merge_selected_objects(self,context): self.multi_object.data = self.multi_object.data.copy() bpy.ops.object.join() self.multi_object.name = "Multi_UV_Object" - - def modal(self, context, event): - + def modal(self, context, event): if event.type in ['TAB'] or context.active_object.mode == "OBJECT": self.report({'INFO'}, "Multi Object UV Editing done.") self.leave_editing_mode(context) @@ -152,55 +158,46 @@ def modal(self, context, event): def invoke(self, context, event): ### reset variables - self.multi_object = None - self.initial_objects = [] - self.initial_objects_hide_render = [] - self.active_object = None - - for object in context.selected_objects: - if object.type != "MESH": - self.report({'WARNING'}, "Please select Mesh Objects only.") - return {'CANCELLED'} + self.multi_object = None context.window_manager.modal_handler_add(self) ### store active and selected objects - for object in context.selected_objects: - self.initial_objects.append(object) - self.initial_objects_hide_render.append(object.hide_render) - object.hide_render = True + self.initial_objects = get_selected_mesh_objects(context) + self.initial_objects_hide_render = [obj.hide_render for obj in self.initial_objects] self.active_object = context.scene.objects.active - - ### go into editing mode with all selected objects merged + + ### make merged copy of all selected objects, that we can edit self.merge_selected_objects(context) self.multi_object.hide_render = False self.multi_object.hide = False - for object in self.initial_objects: - object.hide = True + #hide the initial objects + for obj in self.initial_objects: + obj.hide = True + obj.hide_render = True + #switch to edit mode bpy.ops.object.mode_set(mode="EDIT") bpy.ops.mesh.select_all(action='SELECT') - + return {'RUNNING_MODAL'} -def add_object_tools(self,context): - if len(bpy.context.selected_objects) > 1: - for object in bpy.context.selected_objects: - if object.type != "MESH": - return +def add_button_to_panel_if_allowed(self,context): + if len(context.selected_objects) > 1 and len(get_selected_mesh_objects(context)) > 1: + # we only handle selected mesh-objects, but other objects can be selected as well self.layout.operator_context = "INVOKE_DEFAULT" self.layout.separator() self.layout.label("UV Tools:") - op = self.layout.operator("object.multi_object_uv_edit",text="Multi Object UV Editing",icon="IMAGE_RGB") + self.layout.operator("object.multi_object_uv_edit",text="Multi Object UV Editing",icon="IMAGE_RGB") def register(): - bpy.types.VIEW3D_PT_tools_object.append(add_object_tools) + bpy.types.VIEW3D_PT_tools_object.append(add_button_to_panel_if_allowed) bpy.utils.register_class(MultiObjectUVEdit) def unregister(): - bpy.types.VIEW3D_PT_tools_object.remove(add_object_tools) + bpy.types.VIEW3D_PT_tools_object.remove(add_button_to_panel_if_allowed) bpy.utils.unregister_class(MultiObjectUVEdit)