diff options
Diffstat (limited to 'uv_magic_uv/op/copy_paste_uv_object.py')
-rw-r--r-- | uv_magic_uv/op/copy_paste_uv_object.py | 312 |
1 files changed, 185 insertions, 127 deletions
diff --git a/uv_magic_uv/op/copy_paste_uv_object.py b/uv_magic_uv/op/copy_paste_uv_object.py index d80ee415..eb6d87c9 100644 --- a/uv_magic_uv/op/copy_paste_uv_object.py +++ b/uv_magic_uv/op/copy_paste_uv_object.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "5.1" -__date__ = "24 Feb 2018" +__version__ = "5.2" +__date__ = "17 Nov 2018" import bpy import bmesh @@ -31,6 +31,41 @@ from bpy.props import ( ) from .. import common +from .copy_paste_uv import ( + get_copy_uv_layers, + get_paste_uv_layers, + paste_uv +) + + +__all__ = [ + 'Properties', + 'OperatorCopyUV', + 'MenuCopyUV', + 'OperatorPasteUV', + 'MenuPasteUV', +] + + +def is_valid_context(context): + obj = context.object + + # only object mode is allowed to execute + if obj is None: + return False + if obj.type != 'MESH': + return False + if context.object.mode != 'OBJECT': + return False + + # only 'VIEW_3D' space is allowed to execute + for space in context.area.spaces: + if space.type == 'VIEW_3D': + break + else: + return False + + return True def memorize_view_3d_mode(fn): @@ -42,101 +77,138 @@ def memorize_view_3d_mode(fn): return __memorize_view_3d_mode -class MUV_CPUVObjCopyUV(bpy.types.Operator): +class Properties: + @classmethod + def init_props(cls, scene): + class Props(): + src_info = None + + scene.muv_props.copy_paste_uv_object = Props() + + scene.muv_copy_paste_uv_object_copy_seams = BoolProperty( + name="Seams", + description="Copy Seams", + default=True + ) + + @classmethod + def del_props(cls, scene): + del scene.muv_props.copy_paste_uv_object + del scene.muv_copy_paste_uv_object_copy_seams + + +class OperatorCopyUV(bpy.types.Operator): """ - Operation class: Copy UV coordinate per object + Operation class: Copy UV coordinate among objects """ - bl_idname = "object.muv_cpuv_obj_copy_uv" - bl_label = "Copy UV" - bl_description = "Copy UV coordinate" + bl_idname = "object.muv_copy_paste_uv_object_operator_copy_uv" + bl_label = "Copy UV (Among Objects)" + bl_description = "Copy UV coordinate (Among Objects)" bl_options = {'REGISTER', 'UNDO'} - uv_map = StringProperty(options={'HIDDEN'}) + uv_map = StringProperty(default="__default", options={'HIDDEN'}) + + @classmethod + def poll(cls, context): + # we can not get area/space/region from console + if common.is_console_mode(): + return True + return is_valid_context(context) @memorize_view_3d_mode def execute(self, context): - props = context.scene.muv_props.cpuv_obj - if self.uv_map == "": - self.report({'INFO'}, "Copy UV coordinate per object") - else: - self.report( - {'INFO'}, - "Copy UV coordinate per object (UV map:%s)" % (self.uv_map)) + props = context.scene.muv_props.copy_paste_uv_object bpy.ops.object.mode_set(mode='EDIT') - obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.faces.ensure_lookup_table() + bm = common.create_bmesh(obj) # get UV layer - if self.uv_map == "": - if not bm.loops.layers.uv: - self.report( - {'WARNING'}, "Object must have more than one UV map") - return {'CANCELLED'} - uv_layer = bm.loops.layers.uv.verify() - else: - uv_layer = bm.loops.layers.uv[self.uv_map] + uv_layers = get_copy_uv_layers(self, bm) + if not uv_layers: + return {'CANCELLED'} # get selected face - props.src_uvs = [] - props.src_pin_uvs = [] - props.src_seams = [] - for face in bm.faces: - uvs = [l[uv_layer].uv.copy() for l in face.loops] - pin_uvs = [l[uv_layer].pin_uv for l in face.loops] - seams = [l.edge.seam for l in face.loops] - props.src_uvs.append(uvs) - props.src_pin_uvs.append(pin_uvs) - props.src_seams.append(seams) - - self.report({'INFO'}, "%s's UV coordinates are copied" % (obj.name)) + props.src_info = {} + for layer in uv_layers: + face_info = [] + for face in bm.faces: + if face.select: + info = { + "uvs": [l[layer].uv.copy() for l in face.loops], + "pin_uvs": [l[layer].pin_uv for l in face.loops], + "seams": [l.edge.seam for l in face.loops], + } + face_info.append(info) + props.src_info[layer.name] = face_info + + self.report({'INFO'}, + "{}'s UV coordinates are copied".format(obj.name)) return {'FINISHED'} -class MUV_CPUVObjCopyUVMenu(bpy.types.Menu): +class MenuCopyUV(bpy.types.Menu): """ - Menu class: Copy UV coordinate per object + Menu class: Copy UV coordinate among objects """ - bl_idname = "object.muv_cpuv_obj_copy_uv_menu" - bl_label = "Copy UV" - bl_description = "Copy UV coordinate per object" + bl_idname = "object.muv_copy_paste_uv_object_menu_copy_uv" + bl_label = "Copy UV (Among Objects) (Menu)" + bl_description = "Menu of Copy UV coordinate (Among Objects)" + + @classmethod + def poll(cls, context): + return is_valid_context(context) def draw(self, _): layout = self.layout # create sub menu uv_maps = bpy.context.active_object.data.uv_textures.keys() - layout.operator(MUV_CPUVObjCopyUV.bl_idname, text="[Default]")\ - .uv_map = "" + + ops = layout.operator(OperatorCopyUV.bl_idname, text="[Default]") + ops.uv_map = "__default" + + ops = layout.operator(OperatorCopyUV.bl_idname, text="[All]") + ops.uv_map = "__all" + for m in uv_maps: - layout.operator(MUV_CPUVObjCopyUV.bl_idname, text=m).uv_map = m + ops = layout.operator(OperatorCopyUV.bl_idname, text=m) + ops.uv_map = m -class MUV_CPUVObjPasteUV(bpy.types.Operator): +class OperatorPasteUV(bpy.types.Operator): """ - Operation class: Paste UV coordinate per object + Operation class: Paste UV coordinate among objects """ - bl_idname = "object.muv_cpuv_obj_paste_uv" - bl_label = "Paste UV" - bl_description = "Paste UV coordinate" + bl_idname = "object.muv_copy_paste_uv_object_operator_paste_uv" + bl_label = "Paste UV (Among Objects)" + bl_description = "Paste UV coordinate (Among Objects)" bl_options = {'REGISTER', 'UNDO'} - uv_map = StringProperty(options={'HIDDEN'}) + uv_map = StringProperty(default="__default", options={'HIDDEN'}) copy_seams = BoolProperty( - name="Copy Seams", + name="Seams", description="Copy Seams", default=True ) + @classmethod + def poll(cls, context): + # we can not get area/space/region from console + if common.is_console_mode(): + return True + sc = context.scene + props = sc.muv_props.copy_paste_uv_object + if not props.src_info: + return False + return is_valid_context(context) + @memorize_view_3d_mode def execute(self, context): - props = context.scene.muv_props.cpuv_obj - if not props.src_uvs or not props.src_pin_uvs: + props = context.scene.muv_props.copy_paste_uv_object + if not props.src_info: self.report({'WARNING'}, "Need copy UV at first") return {'CANCELLED'} @@ -149,90 +221,67 @@ class MUV_CPUVObjPasteUV(bpy.types.Operator): bpy.ops.object.mode_set(mode='EDIT') obj = context.active_object - bm = bmesh.from_edit_mesh(obj.data) - if common.check_version(2, 73, 0) >= 0: - bm.faces.ensure_lookup_table() - - if (self.uv_map == "" or - self.uv_map not in bm.loops.layers.uv.keys()): - self.report({'INFO'}, "Paste UV coordinate per object") - else: - self.report( - {'INFO'}, - "Paste UV coordinate per object (UV map: %s)" - % (self.uv_map)) + bm = common.create_bmesh(obj) # get UV layer - if (self.uv_map == "" or - self.uv_map not in bm.loops.layers.uv.keys()): - if not bm.loops.layers.uv: - self.report( - {'WARNING'}, "Object must have more than one UV map") - return {'CANCELLED'} - uv_layer = bm.loops.layers.uv.verify() - else: - uv_layer = bm.loops.layers.uv[self.uv_map] + uv_layers = get_paste_uv_layers(self, obj, bm, props.src_info) + if not uv_layers: + return {'CANCELLED'} # get selected face - dest_uvs = [] - dest_pin_uvs = [] - dest_seams = [] - dest_face_indices = [] - for face in bm.faces: - dest_face_indices.append(face.index) - uvs = [l[uv_layer].uv.copy() for l in face.loops] - pin_uvs = [l[uv_layer].pin_uv for l in face.loops] - seams = [l.edge.seam for l in face.loops] - dest_uvs.append(uvs) - dest_pin_uvs.append(pin_uvs) - dest_seams.append(seams) - if len(props.src_uvs) != len(dest_uvs): - self.report( - {'WARNING'}, - "Number of faces is different from copied " + - "(src:%d, dest:%d)" - % (len(props.src_uvs), len(dest_uvs)) - ) - return {'CANCELLED'} + dest_info = {} + for layer in uv_layers: + face_info = [] + for face in bm.faces: + if face.select: + info = { + "uvs": [l[layer].uv.copy() for l in face.loops], + } + face_info.append(info) + key = list(props.src_info.keys())[0] + src_face_count = len(props.src_info[key]) + dest_face_count = len(face_info) + if src_face_count != dest_face_count: + self.report( + {'WARNING'}, + "Number of selected faces is different from copied" + + "(src:{}, dest:{})" + .format(src_face_count, dest_face_count)) + return {'CANCELLED'} + dest_info[layer.name] = face_info # paste - for i, idx in enumerate(dest_face_indices): - suv = props.src_uvs[i] - spuv = props.src_pin_uvs[i] - ss = props.src_seams[i] - duv = dest_uvs[i] - if len(suv) != len(duv): - self.report({'WARNING'}, "Some faces are different size") - return {'CANCELLED'} - suvs_fr = [uv for uv in suv] - spuvs_fr = [pin_uv for pin_uv in spuv] - ss_fr = [s for s in ss] - # paste UVs - for l, suv, spuv, ss in zip( - bm.faces[idx].loops, suvs_fr, spuvs_fr, ss_fr): - l[uv_layer].uv = suv - l[uv_layer].pin_uv = spuv - if self.copy_seams is True: - l.edge.seam = ss + ret = paste_uv(self, bm, props.src_info, dest_info, uv_layers, + 'N_N', 0, 0, self.copy_seams) + if ret: + return {'CANCELLED'} bmesh.update_edit_mesh(obj.data) if self.copy_seams is True: obj.data.show_edge_seams = True self.report( - {'INFO'}, "%s's UV coordinates are pasted" % (obj.name)) + {'INFO'}, "{}'s UV coordinates are pasted".format(obj.name)) return {'FINISHED'} -class MUV_CPUVObjPasteUVMenu(bpy.types.Menu): +class MenuPasteUV(bpy.types.Menu): """ - Menu class: Paste UV coordinate per object + Menu class: Paste UV coordinate among objects """ - bl_idname = "object.muv_cpuv_obj_paste_uv_menu" - bl_label = "Paste UV" - bl_description = "Paste UV coordinate per object" + bl_idname = "object.muv_copy_paste_uv_object_menu_paste_uv" + bl_label = "Paste UV (Among Objects) (Menu)" + bl_description = "Menu of Paste UV coordinate (Among Objects)" + + @classmethod + def poll(cls, context): + sc = context.scene + props = sc.muv_props.copy_paste_uv_object + if not props.src_info: + return False + return is_valid_context(context) def draw(self, context): sc = context.scene @@ -242,11 +291,20 @@ class MUV_CPUVObjPasteUVMenu(bpy.types.Menu): for obj in bpy.data.objects: if hasattr(obj.data, "uv_textures") and obj.select: uv_maps.extend(obj.data.uv_textures.keys()) - uv_maps = list(set(uv_maps)) - ops = layout.operator(MUV_CPUVObjPasteUV.bl_idname, text="[Default]") - ops.uv_map = "" - ops.copy_seams = sc.muv_cpuv_copy_seams + + ops = layout.operator(OperatorPasteUV.bl_idname, text="[Default]") + ops.uv_map = "__default" + ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams + + ops = layout.operator(OperatorPasteUV.bl_idname, text="[New]") + ops.uv_map = "__new" + ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams + + ops = layout.operator(OperatorPasteUV.bl_idname, text="[All]") + ops.uv_map = "__all" + ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams + for m in uv_maps: - ops = layout.operator(MUV_CPUVObjPasteUV.bl_idname, text=m) + ops = layout.operator(OperatorPasteUV.bl_idname, text=m) ops.uv_map = m - ops.copy_seams = sc.muv_cpuv_copy_seams + ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams |