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 | 314 |
1 files changed, 179 insertions, 135 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..d9f42447 100644 --- a/uv_magic_uv/op/copy_paste_uv_object.py +++ b/uv_magic_uv/op/copy_paste_uv_object.py @@ -20,17 +20,72 @@ __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 +import bpy from bpy.props import ( StringProperty, BoolProperty, ) +from ..impl import copy_paste_uv_impl as impl from .. import common +from ..utils.bl_class_registry import BlClassRegistry +from ..utils.property_class_registry import PropertyClassRegistry + +__all__ = [ + 'Properties', + 'MUV_OT_CopyPasteUVObject_CopyUV', + 'MUV_MT_CopyPasteUVObject_CopyUV', + 'MUV_OT_CopyPasteUVObject_PasteUV', + 'MUV_MT_CopyPasteUVObject_PasteUV', +] + + +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 + + +@PropertyClassRegistry() +class Properties: + idname = "copy_paste_uv_object" + + @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 def memorize_view_3d_mode(fn): @@ -42,197 +97,173 @@ def memorize_view_3d_mode(fn): return __memorize_view_3d_mode -class MUV_CPUVObjCopyUV(bpy.types.Operator): +@BlClassRegistry() +class MUV_OT_CopyPasteUVObject_CopyUV(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 = impl.get_copy_uv_layers(self, bm, self.uv_map) + 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)) + src_info = impl.get_src_face_info(self, bm, uv_layers) + if src_info is None: + return {'CANCELLED'} + props.src_info = src_info + + self.report({'INFO'}, + "{}'s UV coordinates are copied".format(obj.name)) return {'FINISHED'} -class MUV_CPUVObjCopyUVMenu(bpy.types.Menu): +@BlClassRegistry() +class MUV_MT_CopyPasteUVObject_CopyUV(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 = "" + uv_maps = bpy.context.active_object.data.uv_layers.keys() + + ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname, + text="[Default]") + ops.uv_map = "__default" + + ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.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(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname, + text=m) + ops.uv_map = m -class MUV_CPUVObjPasteUV(bpy.types.Operator): +@BlClassRegistry() +class MUV_OT_CopyPasteUVObject_PasteUV(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'}) - copy_seams = BoolProperty( - name="Copy Seams", + uv_map: StringProperty(default="__default", options={'HIDDEN'}) + copy_seams: BoolProperty( + 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'} for o in bpy.data.objects: - if not hasattr(o.data, "uv_textures") or not o.select: + if not hasattr(o.data, "uv_layers") or not o.select_get(): continue bpy.ops.object.mode_set(mode='OBJECT') - bpy.context.scene.objects.active = o + bpy.context.view_layer.objects.active = o 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 = impl.get_paste_uv_layers(self, obj, bm, props.src_info, + self.uv_map) + 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)) - ) + dest_info = impl.get_dest_face_info(self, bm, uv_layers, + props.src_info, 'N_N') + if dest_info is None: return {'CANCELLED'} # 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 = impl.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): +@BlClassRegistry() +class MUV_MT_CopyPasteUVObject_PasteUV(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 @@ -240,13 +271,26 @@ class MUV_CPUVObjPasteUVMenu(bpy.types.Menu): # create sub menu uv_maps = [] 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 + if hasattr(obj.data, "uv_layers") and obj.select_get(): + uv_maps.extend(obj.data.uv_layers.keys()) + + ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname, + text="[Default]") + ops.uv_map = "__default" + ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams + + ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname, + text="[New]") + ops.uv_map = "__new" + ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams + + ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.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(MUV_OT_CopyPasteUVObject_PasteUV.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 |