diff options
author | nutti <nutti.metro@gmail.com> | 2021-03-06 11:54:33 +0300 |
---|---|---|
committer | nutti <nutti.metro@gmail.com> | 2021-03-06 11:54:33 +0300 |
commit | 117faa96af35685d72e5e01f9a386d163d874133 (patch) | |
tree | b4378f8ca2e91c93ab035ab37b05e018f601ff54 | |
parent | c8752443311b133f8783a6b9b2152eb7c92d06a7 (diff) |
Magic UV: Release v6.5
Updated Features
* Texture Projection
* Add option "Scaling", "Rotation", "Translation"
* Select UV
* Add Zoom Selected UV feature
* Add option "Same Polygon Threshold"
* Add option "Selection Method"
* Add option "Sync Mesh Selection"
* UV Inspection
* Add option "Same Polygon Threshold"
* Add option "Display View3D"
* Mirror UV
* Add option "Origin"
* UVW
* Add option "Force Axis"
Other Updates
* Fix bugs
47 files changed, 976 insertions, 232 deletions
diff --git a/magic_uv/__init__.py b/magic_uv/__init__.py index e9c95f24..d0b8ae45 100644 --- a/magic_uv/__init__.py +++ b/magic_uv/__init__.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" bl_info = { @@ -29,7 +29,7 @@ bl_info = { "author": "Nutti, Mifth, Jace Priester, kgeogeo, mem, imdjs" "Keith (Wahooney) Boshoff, McBuff, MaxRobinot, " "Alexander Milovsky, Dusan Stevanovic, MatthiasThDs", - "version": (6, 4, 0), + "version": (6, 5, 0), "blender": (2, 80, 0), "location": "See Add-ons Preferences", "description": "UV Toolset. See Add-ons Preferences for details", diff --git a/magic_uv/common.py b/magic_uv/common.py index 3817486c..1d9d55cf 100644 --- a/magic_uv/common.py +++ b/magic_uv/common.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" from collections import defaultdict from pprint import pprint @@ -333,7 +333,7 @@ def get_uvimg_editor_board_size(area): return (255.0, 255.0) -def calc_polygon_2d_area(points): +def calc_tris_2d_area(points): area = 0.0 for i, p1 in enumerate(points): p2 = points[(i + 1) % len(points)] @@ -345,7 +345,7 @@ def calc_polygon_2d_area(points): return fabs(0.5 * area) -def calc_polygon_3d_area(points): +def calc_tris_3d_area(points): area = 0.0 for i, p1 in enumerate(points): p2 = points[(i + 1) % len(points)] @@ -395,6 +395,23 @@ def get_faces_list(bm, method, only_selected): return faces_list +def measure_all_faces_mesh_area(bm): + if compat.check_version(2, 80, 0) >= 0: + triangle_loops = bm.calc_loop_triangles() + else: + triangle_loops = bm.calc_tessface() + + areas = {face: 0.0 for face in bm.faces} + + for loops in triangle_loops: + face = loops[0].face + area = areas[face] + area += calc_tris_3d_area([l.vert.co for l in loops]) + areas[face] = area + + return areas + + def measure_mesh_area(obj, calc_method, only_selected): bm = bmesh.from_edit_mesh(obj.data) if check_version(2, 73, 0) >= 0: @@ -406,17 +423,18 @@ def measure_mesh_area(obj, calc_method, only_selected): areas = [] for faces in faces_list: - areas.append(measure_mesh_area_from_faces(faces)) + areas.append(measure_mesh_area_from_faces(bm, faces)) return areas -def measure_mesh_area_from_faces(faces): +def measure_mesh_area_from_faces(bm, faces): + face_areas = measure_all_faces_mesh_area(bm) + mesh_area = 0.0 for f in faces: - verts = [l.vert.co for l in f.loops] - f_mesh_area = calc_polygon_3d_area(verts) - mesh_area = mesh_area + f_mesh_area + if f in face_areas: + mesh_area += face_areas[f] return mesh_area @@ -486,12 +504,34 @@ def find_images(obj, face=None, tex_layer=None): return images -def measure_uv_area_from_faces(obj, faces, uv_layer, tex_layer, +def measure_all_faces_uv_area(bm, uv_layer): + if compat.check_version(2, 80, 0) >= 0: + triangle_loops = bm.calc_loop_triangles() + else: + triangle_loops = bm.calc_tessface() + + areas = {face: 0.0 for face in bm.faces} + + for loops in triangle_loops: + face = loops[0].face + area = areas[face] + area += calc_tris_2d_area([l[uv_layer].uv for l in loops]) + areas[face] = area + + return areas + + +def measure_uv_area_from_faces(obj, bm, faces, uv_layer, tex_layer, tex_selection_method, tex_size): + + face_areas = measure_all_faces_uv_area(bm, uv_layer) + uv_area = 0.0 for f in faces: - uvs = [l[uv_layer].uv for l in f.loops] - f_uv_area = calc_polygon_2d_area(uvs) + if f not in face_areas: + continue + + f_uv_area = face_areas[f] # user specified if tex_selection_method == 'USER_SPECIFIED' and tex_size is not None: @@ -547,8 +587,8 @@ def measure_uv_area_from_faces(obj, faces, uv_layer, tex_layer, return uv_area -def measure_uv_area(obj, calc_method, tex_selection_method, tex_size, - only_selected): +def measure_uv_area(obj, calc_method, tex_selection_method, + tex_size, only_selected): bm = bmesh.from_edit_mesh(obj.data) if check_version(2, 73, 0) >= 0: bm.verts.ensure_lookup_table() @@ -565,7 +605,8 @@ def measure_uv_area(obj, calc_method, tex_selection_method, tex_size, uv_areas = [] for faces in faces_list: uv_area = measure_uv_area_from_faces( - obj, faces, uv_layer, tex_layer, tex_selection_method, tex_size) + obj, bm, faces, uv_layer, tex_layer, + tex_selection_method, tex_size) if uv_area is None: return None uv_areas.append(uv_area) @@ -946,7 +987,8 @@ class RingBuffer: # clip: reference polygon # subject: tested polygon -def __do_weiler_atherton_cliping(clip_uvs, subject_uvs, mode): +def __do_weiler_atherton_cliping(clip_uvs, subject_uvs, mode, + same_polygon_threshold): clip_uvs = RingBuffer(clip_uvs) if __is_polygon_flipped(clip_uvs): @@ -961,7 +1003,7 @@ def __do_weiler_atherton_cliping(clip_uvs, subject_uvs, mode): debug_print(subject_uvs) # check if clip and subject is overlapped completely - if __is_polygon_same(clip_uvs, subject_uvs): + if __is_polygon_same(clip_uvs, subject_uvs, same_polygon_threshold): polygons = [subject_uvs.as_list()] debug_print("===== Polygons Overlapped Completely =====") debug_print(polygons) @@ -1193,26 +1235,31 @@ def get_uv_editable_objects(context): return objs -def get_overlapped_uv_info(bm_list, faces_list, uv_layer_list, mode): +def get_overlapped_uv_info(bm_list, faces_list, uv_layer_list, + mode, same_polygon_threshold=0.0000001): # at first, check island overlapped isl = [] for bm, uv_layer, faces in zip(bm_list, uv_layer_list, faces_list): info = get_island_info_from_faces(bm, faces, uv_layer) - isl.extend([(i, uv_layer) for i in info]) + isl.extend([(i, uv_layer, bm) for i in info]) overlapped_isl_pairs = [] overlapped_uv_layer_pairs = [] - for i, (i1, uv_layer_1) in enumerate(isl): - for i2, uv_layer_2 in isl[i + 1:]: + overlapped_bm_paris = [] + for i, (i1, uv_layer_1, bm_1) in enumerate(isl): + for i2, uv_layer_2, bm_2 in isl[i + 1:]: if (i1["max"].x < i2["min"].x) or (i2["max"].x < i1["min"].x) or \ (i1["max"].y < i2["min"].y) or (i2["max"].y < i1["min"].y): continue overlapped_isl_pairs.append([i1, i2]) overlapped_uv_layer_pairs.append([uv_layer_1, uv_layer_2]) + overlapped_bm_paris.append([bm_1, bm_2]) # next, check polygon overlapped overlapped_uvs = [] - for oip, uvlp in zip(overlapped_isl_pairs, overlapped_uv_layer_pairs): + for oip, uvlp, bmp in zip(overlapped_isl_pairs, + overlapped_uv_layer_pairs, + overlapped_bm_paris): for clip in oip[0]["faces"]: f_clip = clip["face"] clip_uvs = [l[uvlp[0]].uv.copy() for l in f_clip.loops] @@ -1228,11 +1275,13 @@ def get_overlapped_uv_info(bm_list, faces_list, uv_layer_list, mode): subject_uvs = [l[uvlp[1]].uv.copy() for l in f_subject.loops] # slow operation, apply Weiler-Atherton cliping algorithm - result, polygons = __do_weiler_atherton_cliping(clip_uvs, - subject_uvs, - mode) + result, polygons = \ + __do_weiler_atherton_cliping(clip_uvs, subject_uvs, + mode, same_polygon_threshold) if result: - overlapped_uvs.append({"clip_face": f_clip, + overlapped_uvs.append({"clip_bmesh": bmp[0], + "subject_bmesh": bmp[1], + "clip_face": f_clip, "subject_face": f_subject, "clip_uv_layer": uvlp[0], "subject_uv_layer": uvlp[1], @@ -1242,14 +1291,15 @@ def get_overlapped_uv_info(bm_list, faces_list, uv_layer_list, mode): return overlapped_uvs -def get_flipped_uv_info(faces_list, uv_layer_list): +def get_flipped_uv_info(bm_list, faces_list, uv_layer_list): flipped_uvs = [] - for faces, uv_layer in zip(faces_list, uv_layer_list): + for bm, faces, uv_layer in zip(bm_list, faces_list, uv_layer_list): for f in faces: polygon = RingBuffer([l[uv_layer].uv.copy() for l in f.loops]) if __is_polygon_flipped(polygon): uvs = [l[uv_layer].uv.copy() for l in f.loops] - flipped_uvs.append({"face": f, + flipped_uvs.append({"bmesh": bm, + "face": f, "uv_layer": uv_layer, "uvs": uvs, "polygons": [polygon.as_list()]}) @@ -1257,7 +1307,7 @@ def get_flipped_uv_info(faces_list, uv_layer_list): return flipped_uvs -def __is_polygon_same(points1, points2): +def __is_polygon_same(points1, points2, threshold): if len(points1) != len(points2): return False @@ -1267,7 +1317,7 @@ def __is_polygon_same(points1, points2): for p1 in pts1: for p2 in pts2: diff = p2 - p1 - if diff.length < 0.0000001: + if diff.length < threshold: pts2.remove(p2) break else: diff --git a/magic_uv/lib/__init__.py b/magic_uv/lib/__init__.py index 8bed4656..68d1843a 100644 --- a/magic_uv/lib/__init__.py +++ b/magic_uv/lib/__init__.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" if "bpy" in locals(): import importlib diff --git a/magic_uv/lib/bglx.py b/magic_uv/lib/bglx.py index 6bc885c6..e72cd734 100644 --- a/magic_uv/lib/bglx.py +++ b/magic_uv/lib/bglx.py @@ -159,6 +159,11 @@ def glEnd(): #shader = gpu.shader.from_builtin('2D_IMAGE') vert_shader, frag_shader = _get_transparency_shader() shader = gpu.types.GPUShader(vert_shader, frag_shader) + elif inst.get_dims() == 3: + if len(tex_coords) == 0: + shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR') + else: + raise NotImplemented("Texture is not supported in get_dims() == 3") else: raise NotImplemented("get_dims() != 2") @@ -223,6 +228,12 @@ def glVertex2f(x, y): inst.set_dims(2) +def glVertex3f(x, y, z): + inst = InternalData.get_instance() + inst.add_vert([x, y, z]) + inst.set_dims(3) + + def glTexCoord2f(u, v): inst = InternalData.get_instance() inst.add_tex_coord([u, v]) @@ -234,6 +245,7 @@ GL_INT = bgl.GL_INT GL_SCISSOR_BOX = bgl.GL_SCISSOR_BOX GL_TEXTURE_2D = bgl.GL_TEXTURE_2D GL_TEXTURE0 = bgl.GL_TEXTURE0 +GL_DEPTH_TEST = bgl.GL_DEPTH_TEST GL_TEXTURE_MIN_FILTER = 0 GL_TEXTURE_MAG_FILTER = 0 diff --git a/magic_uv/op/__init__.py b/magic_uv/op/__init__.py index 459a37e0..702ee452 100644 --- a/magic_uv/op/__init__.py +++ b/magic_uv/op/__init__.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" if "bpy" in locals(): import importlib diff --git a/magic_uv/op/align_uv.py b/magic_uv/op/align_uv.py index cb68bf25..0cc3bcda 100644 --- a/magic_uv/op/align_uv.py +++ b/magic_uv/op/align_uv.py @@ -20,8 +20,8 @@ __author__ = "imdjs, Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import math from math import atan2, tan, sin, cos diff --git a/magic_uv/op/align_uv_cursor.py b/magic_uv/op/align_uv_cursor.py index 08c90db7..10f19a71 100644 --- a/magic_uv/op/align_uv_cursor.py +++ b/magic_uv/op/align_uv_cursor.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy from mathutils import Vector diff --git a/magic_uv/op/clip_uv.py b/magic_uv/op/clip_uv.py index 990d35a6..b1532a36 100644 --- a/magic_uv/op/clip_uv.py +++ b/magic_uv/op/clip_uv.py @@ -20,8 +20,8 @@ __author__ = "Dusan Stevanovic, Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import math diff --git a/magic_uv/op/copy_paste_uv.py b/magic_uv/op/copy_paste_uv.py index 0410ee8d..761aa36b 100644 --- a/magic_uv/op/copy_paste_uv.py +++ b/magic_uv/op/copy_paste_uv.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>, Jace Priester" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bmesh import bpy.utils diff --git a/magic_uv/op/copy_paste_uv_object.py b/magic_uv/op/copy_paste_uv_object.py index 2130ea38..37706c37 100644 --- a/magic_uv/op/copy_paste_uv_object.py +++ b/magic_uv/op/copy_paste_uv_object.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bmesh import bpy diff --git a/magic_uv/op/copy_paste_uv_uvedit.py b/magic_uv/op/copy_paste_uv_uvedit.py index abfb69a8..5ee7d62c 100644 --- a/magic_uv/op/copy_paste_uv_uvedit.py +++ b/magic_uv/op/copy_paste_uv_uvedit.py @@ -20,8 +20,8 @@ __author__ = "imdjs, Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import math from math import atan2, sin, cos diff --git a/magic_uv/op/flip_rotate_uv.py b/magic_uv/op/flip_rotate_uv.py index 7e6dbdf9..246e5fbf 100644 --- a/magic_uv/op/flip_rotate_uv.py +++ b/magic_uv/op/flip_rotate_uv.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy import bmesh diff --git a/magic_uv/op/mirror_uv.py b/magic_uv/op/mirror_uv.py index a893ab83..969c26dc 100644 --- a/magic_uv/op/mirror_uv.py +++ b/magic_uv/op/mirror_uv.py @@ -20,8 +20,8 @@ __author__ = "Keith (Wahooney) Boshoff, Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy from bpy.props import ( @@ -30,7 +30,7 @@ from bpy.props import ( BoolProperty, ) import bmesh -from mathutils import Vector +from mathutils import Vector, Euler from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry @@ -65,15 +65,15 @@ def _is_vector_similar(v1, v2, error): return within_err_x and within_err_y and within_err_z -def _mirror_uvs(uv_layer, src, dst, axis, error): +def _mirror_uvs(uv_layer, src, dst, axis, error, transformed): """ Copy UV coordinates from one UV face to another """ for sl in src.loops: suv = sl[uv_layer].uv.copy() - svco = sl.vert.co.copy() + svco = transformed[sl.vert].copy() for dl in dst.loops: - dvco = dl.vert.co.copy() + dvco = transformed[dl.vert].copy() if axis == 'X': dvco.x = -dvco.x elif axis == 'Y': @@ -85,13 +85,14 @@ def _mirror_uvs(uv_layer, src, dst, axis, error): dl[uv_layer].uv = suv.copy() -def _get_face_center(face): +def _get_face_center(face, transformed): """ Get center coordinate of the face """ center = Vector((0.0, 0.0, 0.0)) for v in face.verts: - center = center + v.co + tv = transformed[v] + center = center + tv return center / len(face.verts) @@ -117,11 +118,22 @@ class _Properties: description="Mirror Axis", default='X' ) + scene.muv_mirror_uv_origin = EnumProperty( + items=( + ('WORLD', "World", "World"), + ("GLOBAL", "Global", "Global"), + ('LOCAL', "Local", "Local"), + ), + name="Origin", + description="Origin of the mirror operation", + default='LOCAL' + ) @classmethod def del_props(cls, scene): del scene.muv_mirror_uv_enabled del scene.muv_mirror_uv_axis + del scene.muv_mirror_uv_origin @BlClassRegistry() @@ -154,6 +166,16 @@ class MUV_OT_MirrorUV(bpy.types.Operator): soft_min=0.0, soft_max=1.0 ) + origin = EnumProperty( + items=( + ('WORLD', "World", "World"), + ("GLOBAL", "Global", "Global"), + ('LOCAL', "Local", "Local"), + ), + name="Origin", + description="Origin of the mirror operation", + default='LOCAL' + ) @classmethod def poll(cls, context): @@ -162,6 +184,51 @@ class MUV_OT_MirrorUV(bpy.types.Operator): return True return _is_valid_context(context) + def _get_world_vertices(self, obj, bm): + # Get world orientation matrix. + world_orientation_mat = obj.matrix_world + + # Move to local to world. + transformed = {} + for v in bm.verts: + transformed[v] = compat.matmul(world_orientation_mat, v.co) + + return transformed + + def _get_global_vertices(self, obj, bm): + # Get world rotation matrix. + eular = Euler(obj.rotation_euler) + rotation_mat = eular.to_matrix() + + # Get center location of all verticies. + center_location = Vector((0.0, 0.0, 0.0)) + for v in bm.verts: + center_location += v.co + center_location /= len(bm.verts) + + # Move to local to global. + transformed = {} + for v in bm.verts: + transformed[v] = compat.matmul(rotation_mat, v.co) + transformed[v] -= center_location + + return transformed + + def _get_local_vertices(self, _, bm): + transformed = {} + + # Get center location of all verticies. + center_location = Vector((0.0, 0.0, 0.0)) + for v in bm.verts: + center_location += v.co + center_location /= len(bm.verts) + + for v in bm.verts: + transformed[v] = v.co.copy() + transformed[v] -= center_location + + return transformed + def execute(self, context): objs = common.get_uv_editable_objects(context) @@ -180,6 +247,13 @@ class MUV_OT_MirrorUV(bpy.types.Operator): return {'CANCELLED'} uv_layer = bm.loops.layers.uv.verify() + if self.origin == 'WORLD': + transformed_verts = self._get_world_vertices(obj, bm) + elif self.origin == 'GLOBAL': + transformed_verts = self._get_global_vertices(obj, bm) + elif self.origin == 'LOCAL': + transformed_verts = self._get_local_vertices(obj, bm) + faces = [f for f in bm.faces if f.select] for f_dst in faces: count = len(f_dst.verts) @@ -191,8 +265,8 @@ class MUV_OT_MirrorUV(bpy.types.Operator): continue # test if the vertices x values are the same sign - dst = _get_face_center(f_dst) - src = _get_face_center(f_src) + dst = _get_face_center(f_dst, transformed_verts) + src = _get_face_center(f_src, transformed_verts) if (dst.x > 0 and src.x > 0) or (dst.x < 0 and src.x < 0): continue @@ -207,7 +281,7 @@ class MUV_OT_MirrorUV(bpy.types.Operator): # do mirror UV if _is_vector_similar(dst, src, error): _mirror_uvs(uv_layer, f_src, f_dst, - self.axis, self.error) + self.axis, self.error, transformed_verts) bmesh.update_edit_mesh(obj.data) diff --git a/magic_uv/op/move_uv.py b/magic_uv/op/move_uv.py index fb7c287d..210169ab 100644 --- a/magic_uv/op/move_uv.py +++ b/magic_uv/op/move_uv.py @@ -20,8 +20,8 @@ __author__ = "kgeogeo, mem, Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy from bpy.props import BoolProperty diff --git a/magic_uv/op/pack_uv.py b/magic_uv/op/pack_uv.py index 75fc760c..dd4a74d3 100644 --- a/magic_uv/op/pack_uv.py +++ b/magic_uv/op/pack_uv.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" from math import fabs @@ -151,7 +151,7 @@ class _Properties: name="Allowable Center Deviation", description="Allowable center deviation to judge same UV island", min=0.000001, - max=0.1, + max=10.0, default=(0.001, 0.001), size=2 ) @@ -159,7 +159,7 @@ class _Properties: name="Allowable Size Deviation", description="Allowable sizse deviation to judge same UV island", min=0.000001, - max=0.1, + max=10.0, default=(0.001, 0.001), size=2 ) @@ -196,12 +196,13 @@ class MUV_OT_PackUV(bpy.types.Operator): description="Margin used by default pack UV function", min=0, max=1, - default=0.001) + default=0.001 + ) allowable_center_deviation = FloatVectorProperty( name="Allowable Center Deviation", description="Allowable center deviation to judge same UV island", min=0.000001, - max=0.1, + max=10.0, default=(0.001, 0.001), size=2 ) @@ -209,7 +210,7 @@ class MUV_OT_PackUV(bpy.types.Operator): name="Allowable Size Deviation", description="Allowable sizse deviation to judge same UV island", min=0.000001, - max=0.1, + max=10.0, default=(0.001, 0.001), size=2 ) diff --git a/magic_uv/op/preserve_uv_aspect.py b/magic_uv/op/preserve_uv_aspect.py index 270bc7ec..f1404b10 100644 --- a/magic_uv/op/preserve_uv_aspect.py +++ b/magic_uv/op/preserve_uv_aspect.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy from bpy.props import StringProperty, EnumProperty, BoolProperty diff --git a/magic_uv/op/select_uv.py b/magic_uv/op/select_uv.py index a405e66d..cf8195f2 100644 --- a/magic_uv/op/select_uv.py +++ b/magic_uv/op/select_uv.py @@ -20,16 +20,17 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy -from bpy.props import BoolProperty +from bpy.props import BoolProperty, FloatProperty, EnumProperty import bmesh from .. import common from ..utils.bl_class_registry import BlClassRegistry from ..utils.property_class_registry import PropertyClassRegistry +from ..utils import compatibility as compat def _is_valid_context(context): @@ -61,13 +62,38 @@ class _Properties: description="Select UV is enabled", default=False ) + scene.muv_select_uv_same_polygon_threshold = FloatProperty( + name="Same Polygon Threshold", + description="Threshold to distinguish same polygons", + default=0.000001, + min=0.000001, + max=0.01, + step=0.00001 + ) + scene.muv_select_uv_selection_method = EnumProperty( + name="Selection Method", + description="How to select faces which have overlapped UVs", + items=[ + ('EXTEND', "Extend", + "Select faces without unselecting selected faces"), + ('RESET', "Reset", "Select faces and unselect selected faces"), + ], + default='RESET' + ) + scene.muv_select_uv_sync_mesh_selection = BoolProperty( + name="Sync Mesh Selection", + description="Select the mesh's faces as well as UV's faces", + default=False + ) @classmethod def del_props(cls, scene): del scene.muv_select_uv_enabled + del scene.muv_select_uv_same_polygon_threshold @BlClassRegistry() +@compat.make_annotations class MUV_OT_SelectUV_SelectOverlapped(bpy.types.Operator): """ Operation class: Select faces which have overlapped UVs @@ -78,6 +104,30 @@ class MUV_OT_SelectUV_SelectOverlapped(bpy.types.Operator): bl_description = "Select faces which have overlapped UVs" bl_options = {'REGISTER', 'UNDO'} + same_polygon_threshold = FloatProperty( + name="Same Polygon Threshold", + description="Threshold to distinguish same polygons", + default=0.000001, + min=0.000001, + max=0.01, + step=0.00001 + ) + selection_method = EnumProperty( + name="Selection Method", + description="How to select faces which have overlapped UVs", + items=[ + ('EXTEND', "Extend", + "Select faces without unselecting selected faces"), + ('RESET', "Reset", "Select faces and unselect selected faces"), + ], + default='RESET' + ) + sync_mesh_selection = BoolProperty( + name="Sync Mesh Selection", + description="Select mesh's faces as well as UV's faces", + default=False + ) + @classmethod def poll(cls, context): # we can not get area/space/region from console @@ -85,6 +135,12 @@ class MUV_OT_SelectUV_SelectOverlapped(bpy.types.Operator): return True return _is_valid_context(context) + @staticmethod + def setup_argument(ops, scene): + ops.same_polygon_threshold = scene.muv_select_uv_same_polygon_threshold + ops.selection_method = scene.muv_select_uv_selection_method + ops.sync_mesh_selection = scene.muv_select_uv_sync_mesh_selection + def execute(self, context): objs = common.get_uv_editable_objects(context) @@ -105,13 +161,29 @@ class MUV_OT_SelectUV_SelectOverlapped(bpy.types.Operator): uv_layer_list.append(uv_layer) faces_list.append(sel_faces) - overlapped_info = common.get_overlapped_uv_info(bm_list, faces_list, - uv_layer_list, 'FACE') + overlapped_info = common.get_overlapped_uv_info( + bm_list, faces_list, uv_layer_list, 'FACE', + self.same_polygon_threshold) + + if self.selection_method == 'RESET': + if context.tool_settings.use_uv_select_sync: + for faces in faces_list: + for f in faces: + f.select = False + else: + for uv_layer, faces in zip(uv_layer_list, faces_list): + for f in faces: + if self.sync_mesh_selection: + f.select = False + for l in f.loops: + l[uv_layer].select = False for info in overlapped_info: if context.tool_settings.use_uv_select_sync: info["subject_face"].select = True else: + if self.sync_mesh_selection: + info["subject_face"].select = True for l in info["subject_face"].loops: l[info["subject_uv_layer"]].select = True @@ -122,6 +194,7 @@ class MUV_OT_SelectUV_SelectOverlapped(bpy.types.Operator): @BlClassRegistry() +@compat.make_annotations class MUV_OT_SelectUV_SelectFlipped(bpy.types.Operator): """ Operation class: Select faces which have flipped UVs @@ -132,6 +205,22 @@ class MUV_OT_SelectUV_SelectFlipped(bpy.types.Operator): bl_description = "Select faces which have flipped UVs" bl_options = {'REGISTER', 'UNDO'} + selection_method = EnumProperty( + name="Selection Method", + description="How to select faces which have overlapped UVs", + items=[ + ('EXTEND', "Extend", + "Select faces without unselecting selected faces"), + ('RESET', "Reset", "Select faces and unselect selected faces"), + ], + default='RESET' + ) + sync_mesh_selection = BoolProperty( + name="Sync Mesh Selection", + description="Select mesh's faces as well as UV's faces", + default=False + ) + @classmethod def poll(cls, context): # we can not get area/space/region from console @@ -139,6 +228,11 @@ class MUV_OT_SelectUV_SelectFlipped(bpy.types.Operator): return True return _is_valid_context(context) + @staticmethod + def setup_argument(ops, scene): + ops.selection_method = scene.muv_select_uv_selection_method + ops.sync_mesh_selection = scene.muv_select_uv_sync_mesh_selection + def execute(self, context): objs = common.get_uv_editable_objects(context) @@ -159,12 +253,28 @@ class MUV_OT_SelectUV_SelectFlipped(bpy.types.Operator): uv_layer_list.append(uv_layer) faces_list.append(sel_faces) - flipped_info = common.get_flipped_uv_info(faces_list, uv_layer_list) + flipped_info = common.get_flipped_uv_info( + bm_list, faces_list, uv_layer_list) + + if self.selection_method == 'RESET': + if context.tool_settings.use_uv_select_sync: + for faces in faces_list: + for f in faces: + f.select = False + else: + for uv_layer, faces in zip(uv_layer_list, faces_list): + for f in faces: + if self.sync_mesh_selection: + f.select = False + for l in f.loops: + l[uv_layer].select = False for info in flipped_info: if context.tool_settings.use_uv_select_sync: info["face"].select = True else: + if self.sync_mesh_selection: + info["face"].select = True for l in info["face"].loops: l[info["uv_layer"]].select = True @@ -172,3 +282,84 @@ class MUV_OT_SelectUV_SelectFlipped(bpy.types.Operator): bmesh.update_edit_mesh(obj.data) return {'FINISHED'} + + +@BlClassRegistry() +class MUV_OT_SelectUV_ZoomSelectedUV(bpy.types.Operator): + """ + Operation class: Zoom selected UV in View3D space + """ + + bl_idname = "uv.muv_select_uv_zoom_selected_uv" + bl_label = "Zoom Selected UV" + bl_description = "Zoom selected UV in View3D space" + bl_options = {'REGISTER', 'UNDO'} + + @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) + + def _get_override_context(self, context): + for window in context.window_manager.windows: + screen = window.screen + for area in screen.areas: + if area.type == 'VIEW_3D': + for region in area.regions: + if region.type == 'WINDOW': + return {'window': window, 'screen': screen, + 'area': area, 'region': region} + return None + + def execute(self, context): + objs = common.get_uv_editable_objects(context) + + bm_list = [] + uv_layer_list = [] + verts_list = [] + for obj in objs: + bm = bmesh.from_edit_mesh(obj.data) + if common.check_version(2, 73, 0) >= 0: + bm.verts.ensure_lookup_table() + uv_layer = bm.loops.layers.uv.verify() + + sel_verts = [v for v in bm.verts if v.select] + bm_list.append(bm) + uv_layer_list.append(uv_layer) + verts_list.append(sel_verts) + + # Get all selected UV vertices in UV Editor. + sel_uv_verts = [] + for vlist, uv_layer in zip(verts_list, uv_layer_list): + for v in vlist: + for l in v.link_loops: + if l[uv_layer].select or \ + context.tool_settings.use_uv_select_sync: + sel_uv_verts.append(v) + break + + # Select vertices only selected in UV Editor. + for bm in bm_list: + for v in bm.verts: + v.select = False + for v in sel_uv_verts: + v.select = True + for obj in objs: + bmesh.update_edit_mesh(obj.data) + + # Zoom. + override_context = self._get_override_context(context) + if override_context is None: + self.report({'WARNING'}, "More than one 'VIEW_3D' area must exist") + return {'CANCELLED'} + bpy.ops.view3d.view_selected(override_context, use_all_regions=False) + + # Revert selection of verticies. + for v in sel_verts: + v.select = True + for obj in objs: + bmesh.update_edit_mesh(obj.data) + + return {'FINISHED'} diff --git a/magic_uv/op/smooth_uv.py b/magic_uv/op/smooth_uv.py index 9b721615..232d6ccc 100644 --- a/magic_uv/op/smooth_uv.py +++ b/magic_uv/op/smooth_uv.py @@ -20,8 +20,8 @@ __author__ = "imdjs, Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy from bpy.props import BoolProperty, FloatProperty diff --git a/magic_uv/op/texture_lock.py b/magic_uv/op/texture_lock.py index fb9ac4c7..6873cc4a 100644 --- a/magic_uv/op/texture_lock.py +++ b/magic_uv/op/texture_lock.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import math from math import atan2, cos, sqrt, sin, fabs diff --git a/magic_uv/op/texture_projection.py b/magic_uv/op/texture_projection.py index 694fac0d..9ce3cdc4 100644 --- a/magic_uv/op/texture_projection.py +++ b/magic_uv/op/texture_projection.py @@ -20,10 +20,11 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" from collections import namedtuple +from math import sin, cos import bpy import bmesh @@ -32,6 +33,7 @@ from bpy.props import ( BoolProperty, EnumProperty, FloatProperty, + FloatVectorProperty, ) import mathutils @@ -56,7 +58,7 @@ def _get_loaded_texture_name(_, __): return items -def _get_canvas(context, magnitude): +def _get_canvas(context): """ Get canvas to be renderred texture """ @@ -88,11 +90,11 @@ def _get_canvas(context, magnitude): len_y = canvas_h else: if sc.muv_texture_projection_apply_tex_aspect: - len_x = tex_w * magnitude - len_y = tex_h * magnitude + len_x = tex_w + len_y = tex_h else: - len_x = region_w * magnitude - len_y = region_h * magnitude + len_x = region_w + len_y = region_h x0 = int(center_x - len_x * 0.5) y0 = int(center_y - len_y * 0.5) @@ -123,6 +125,35 @@ def _region_to_canvas(rg_vec, canvas): return cv_vec +def _create_affine_matrix(identity, scale, rotate, translate): + if identity: + return mathutils.Matrix.Identity(3) + + sx = scale[0] + sy = scale[1] + theta = rotate + tx = translate[0] + ty = translate[1] + + mat_scale = mathutils.Matrix(( + (sx, 0.0, 0.0), + (0.0, sy, 0.0), + (0.0, 0.0, 1.0) + )) + mat_rotate = mathutils.Matrix(( + (cos(theta), sin(theta), 0.0), + (-sin(theta), cos(theta), 0.0), + (0.0, 0.0, 1.0) + )) + mat_translate = mathutils.Matrix(( + (1.0, 0.0, tx), + (0.0, 1.0, ty), + (0.0, 0.0, 1.0) + )) + + return compat.matmul(compat.matmul(mat_translate, mat_rotate), mat_scale) + + def _is_valid_context(context): objs = common.get_uv_editable_objects(context) if not objs: @@ -167,12 +198,31 @@ class _Properties: set=set_func, update=update_func ) - scene.muv_texture_projection_tex_magnitude = FloatProperty( - name="Magnitude", - description="Texture Magnitude", - default=0.5, - min=0.0, - max=100.0 + scene.muv_texture_projection_tex_scaling = FloatVectorProperty( + name="Scaling", + description="Texture Scale", + default=(0.5, 0.5), + min=-100.0, + max=100.0, + size=2, + subtype='XYZ' + ) + scene.muv_texture_projection_tex_rotation = FloatProperty( + name="Rotation", + description="Texture Rotate", + default=0.0, + min=-360.0, + max=360.0, + subtype='ANGLE' + ) + scene.muv_texture_projection_tex_translation = FloatVectorProperty( + name="Translation", + description="Texture Translate", + default=(0.0, 0.0), + min=-2000.0, + max=2000.0, + size=2, + subtype='XYZ' ) scene.muv_texture_projection_tex_image = EnumProperty( name="Image", @@ -188,7 +238,7 @@ class _Properties: ) scene.muv_texture_projection_adjust_window = BoolProperty( name="Adjust Window", - description="Size of renderered texture is fitted to window", + description="Scale of renderered texture is fitted to window", default=True ) scene.muv_texture_projection_apply_tex_aspect = BoolProperty( @@ -205,7 +255,9 @@ class _Properties: @classmethod def del_props(cls, scene): del scene.muv_texture_projection_enabled - del scene.muv_texture_projection_tex_magnitude + del scene.muv_texture_projection_tex_scaling + del scene.muv_texture_projection_tex_rotation + del scene.muv_texture_projection_tex_translation del scene.muv_texture_projection_tex_image del scene.muv_texture_projection_tex_transparency del scene.muv_texture_projection_adjust_window @@ -264,12 +316,33 @@ class MUV_OT_TextureProjection(bpy.types.Operator): img = bpy.data.images[sc.muv_texture_projection_tex_image] # setup rendering region - rect = _get_canvas(context, sc.muv_texture_projection_tex_magnitude) + rect = _get_canvas(context) + + # Apply affine transformation. + center = mathutils.Vector(( + (rect.x1 + rect.x0) / 2.0, + (rect.y1 + rect.y0) / 2.0, + 0.0, + )) + p1 = mathutils.Vector((rect.x0 - center.x, rect.y0 - center.y, 1.0)) + p2 = mathutils.Vector((rect.x0 - center.x, rect.y1 - center.y, 1.0)) + p3 = mathutils.Vector((rect.x1 - center.x, rect.y1 - center.y, 1.0)) + p4 = mathutils.Vector((rect.x1 - center.x, rect.y0 - center.y, 1.0)) + mat_affine = _create_affine_matrix( + sc.muv_texture_projection_adjust_window, + sc.muv_texture_projection_tex_scaling, + sc.muv_texture_projection_tex_rotation, + sc.muv_texture_projection_tex_translation) + p1 = compat.matmul(mat_affine, p1) + center + p2 = compat.matmul(mat_affine, p2) + center + p3 = compat.matmul(mat_affine, p3) + center + p4 = compat.matmul(mat_affine, p4) + center + positions = [ - [rect.x0, rect.y0], - [rect.x0, rect.y1], - [rect.x1, rect.y1], - [rect.x1, rect.y0] + [p1.x, p1.y], + [p2.x, p2.y], + [p3.x, p3.y], + [p4.x, p4.y] ] tex_coords = [ [0.0, 0.0], @@ -384,13 +457,30 @@ class MUV_OT_TextureProjection_Project(bpy.types.Operator): for f in sel_faces for l in f.loops ] + # Apply affine transformation. + rect = _get_canvas(bpy.context) + center = mathutils.Vector(( + (rect.x1 + rect.x0) / 2.0, + (rect.y1 + rect.y0) / 2.0, + 0.0, + )) + v_screen_transformed = [] + for v in v_screen: + p1 = mathutils.Vector((v.x - center.x, v.y - center.y, 1.0)) + mat_affine = _create_affine_matrix( + sc.muv_texture_projection_adjust_window, + sc.muv_texture_projection_tex_scaling, + sc.muv_texture_projection_tex_rotation, + sc.muv_texture_projection_tex_translation) + p1 = compat.matmul(mat_affine.inverted(), p1) + center + v_screen_transformed.append(p1) + # transform screen region to canvas v_canvas = [ _region_to_canvas( v, - _get_canvas(bpy.context, - sc.muv_texture_projection_tex_magnitude) - ) for v in v_screen + rect + ) for v in v_screen_transformed ] # assign image diff --git a/magic_uv/op/texture_wrap.py b/magic_uv/op/texture_wrap.py index 5fd0cfe6..9a706fd7 100644 --- a/magic_uv/op/texture_wrap.py +++ b/magic_uv/op/texture_wrap.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy from bpy.props import ( diff --git a/magic_uv/op/transfer_uv.py b/magic_uv/op/transfer_uv.py index bcf9fab9..da308816 100644 --- a/magic_uv/op/transfer_uv.py +++ b/magic_uv/op/transfer_uv.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>, Mifth, MaxRobinot" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" from collections import OrderedDict diff --git a/magic_uv/op/unwrap_constraint.py b/magic_uv/op/unwrap_constraint.py index dcaa79b4..489c3b69 100644 --- a/magic_uv/op/unwrap_constraint.py +++ b/magic_uv/op/unwrap_constraint.py @@ -18,8 +18,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy from bpy.props import ( diff --git a/magic_uv/op/uv_bounding_box.py b/magic_uv/op/uv_bounding_box.py index fc3455b4..d7b9badb 100644 --- a/magic_uv/op/uv_bounding_box.py +++ b/magic_uv/op/uv_bounding_box.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" from enum import IntEnum import math diff --git a/magic_uv/op/uv_inspection.py b/magic_uv/op/uv_inspection.py index 49525b98..7ee1633d 100644 --- a/magic_uv/op/uv_inspection.py +++ b/magic_uv/op/uv_inspection.py @@ -20,14 +20,14 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import random from math import fabs import bpy -from bpy.props import BoolProperty, EnumProperty +from bpy.props import BoolProperty, EnumProperty, FloatProperty import bmesh from .. import common @@ -64,6 +64,7 @@ def _update_uvinsp_info(context): props = sc.muv_props.uv_inspection objs = common.get_uv_editable_objects(context) + bm_to_obj = {} # { Object: BMesh } bm_list = [] uv_layer_list = [] faces_list = [] @@ -77,13 +78,35 @@ def _update_uvinsp_info(context): sel_faces = [f for f in bm.faces] else: sel_faces = [f for f in bm.faces if f.select] + bm_to_obj[bm] = obj bm_list.append(bm) uv_layer_list.append(uv_layer) faces_list.append(sel_faces) props.overlapped_info = common.get_overlapped_uv_info( - bm_list, faces_list, uv_layer_list, sc.muv_uv_inspection_show_mode) - props.flipped_info = common.get_flipped_uv_info(faces_list, uv_layer_list) + bm_list, faces_list, uv_layer_list, sc.muv_uv_inspection_show_mode, + sc.muv_uv_inspection_same_polygon_threshold) + props.flipped_info = common.get_flipped_uv_info( + bm_list, faces_list, uv_layer_list) + + if sc.muv_uv_inspection_display_in_v3d: + props.overlapped_info_for_v3d = {} + for info in props.overlapped_info: + bm = info["subject_bmesh"] + face = info["subject_face"] + obj = bm_to_obj[bm] + if obj not in props.overlapped_info_for_v3d: + props.overlapped_info_for_v3d[obj] = [] + props.overlapped_info_for_v3d[obj].append(face.index) + + props.filpped_info_for_v3d = {} + for info in props.flipped_info: + bm = info["bmesh"] + face = info["face"] + obj = bm_to_obj[bm] + if obj not in props.filpped_info_for_v3d: + props.filpped_info_for_v3d[obj] = [] + props.filpped_info_for_v3d[obj].append(face.index) @PropertyClassRegistry() @@ -95,6 +118,8 @@ class _Properties: class Props(): overlapped_info = [] flipped_info = [] + overlapped_info_for_v3d = {} # { Object: [face_indices] } + filpped_info_for_v3d = {} # { Object: [face_indices] } scene.muv_props.uv_inspection = Props() @@ -130,6 +155,11 @@ class _Properties: description="Show flipped UVs", default=False ) + scene.muv_uv_inspection_display_in_v3d = BoolProperty( + name="Display View3D", + description="Display overlapped/flipped faces on View3D", + default=True + ) scene.muv_uv_inspection_show_mode = EnumProperty( name="Mode", description="Show mode", @@ -139,6 +169,14 @@ class _Properties: ], default='PART' ) + scene.muv_uv_inspection_same_polygon_threshold = FloatProperty( + name="Same Polygon Threshold", + description="Threshold to distinguish same polygons", + default=0.000001, + min=0.000001, + max=0.01, + step=0.00001 + ) @classmethod def del_props(cls, scene): @@ -147,7 +185,9 @@ class _Properties: del scene.muv_uv_inspection_show del scene.muv_uv_inspection_show_overlapped del scene.muv_uv_inspection_show_flipped + del scene.muv_uv_inspection_display_in_v3d del scene.muv_uv_inspection_show_mode + del scene.muv_uv_inspection_same_polygon_threshold @BlClassRegistry() @@ -162,6 +202,7 @@ class MUV_OT_UVInspection_Render(bpy.types.Operator): bl_label = "Overlapped/Flipped UV renderer" __handle = None + __handle_v3d = None @classmethod def poll(cls, context): @@ -181,6 +222,11 @@ class MUV_OT_UVInspection_Render(bpy.types.Operator): MUV_OT_UVInspection_Render.draw, (obj, context), 'WINDOW', 'POST_PIXEL') + sv3d = bpy.types.SpaceView3D + cls.__handle_v3d = sv3d.draw_handler_add( + MUV_OT_UVInspection_Render.draw_v3d, (obj, context), + 'WINDOW', 'POST_VIEW') + @classmethod def handle_remove(cls): if cls.__handle is not None: @@ -188,6 +234,60 @@ class MUV_OT_UVInspection_Render(bpy.types.Operator): cls.__handle, 'WINDOW') cls.__handle = None + if cls.__handle_v3d is not None: + bpy.types.SpaceView3D.draw_handler_remove( + cls.__handle_v3d, 'WINDOW') + + @staticmethod + def draw_v3d(_, context): + sc = context.scene + props = sc.muv_props.uv_inspection + user_prefs = compat.get_user_preferences(context) + prefs = user_prefs.addons["magic_uv"].preferences + + if not MUV_OT_UVInspection_Render.is_running(context): + return + + if not sc.muv_uv_inspection_display_in_v3d: + return + + # OpenGL configuration. + bgl.glEnable(bgl.GL_BLEND) + bgl.glEnable(bgl.GL_DEPTH_TEST) + + # Render faces whose UV is overlapped. + if sc.muv_uv_inspection_show_overlapped: + color = prefs.uv_inspection_overlapped_color_for_v3d + for obj, findices in props.overlapped_info_for_v3d.items(): + world_mat = obj.matrix_world + bm = bmesh.from_edit_mesh(obj.data) + + for fidx in findices: + bgl.glBegin(bgl.GL_TRIANGLE_FAN) + bgl.glColor4f(color[0], color[1], color[2], color[3]) + for l in bm.faces[fidx].loops: + co = compat.matmul(world_mat, l.vert.co) + bgl.glVertex3f(co[0], co[1], co[2]) + bgl.glEnd() + + # Render faces whose UV is flipped. + if sc.muv_uv_inspection_show_flipped: + color = prefs.uv_inspection_flipped_color_for_v3d + for obj, findices in props.filpped_info_for_v3d.items(): + world_mat = obj.matrix_world + bm = bmesh.from_edit_mesh(obj.data) + + for fidx in findices: + bgl.glBegin(bgl.GL_TRIANGLE_FAN) + bgl.glColor4f(color[0], color[1], color[2], color[3]) + for l in bm.faces[fidx].loops: + co = compat.matmul(world_mat, l.vert.co) + bgl.glVertex3f(co[0], co[1], co[2]) + bgl.glEnd() + + bgl.glDisable(bgl.GL_DEPTH_TEST) + bgl.glDisable(bgl.GL_BLEND) + @staticmethod def draw(_, context): sc = context.scene diff --git a/magic_uv/op/uv_sculpt.py b/magic_uv/op/uv_sculpt.py index c9ed4f34..0821f0bc 100644 --- a/magic_uv/op/uv_sculpt.py +++ b/magic_uv/op/uv_sculpt.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" from math import pi, cos, tan, sin diff --git a/magic_uv/op/uvw.py b/magic_uv/op/uvw.py index 42918dd5..3b155b89 100644 --- a/magic_uv/op/uvw.py +++ b/magic_uv/op/uvw.py @@ -20,8 +20,8 @@ __author__ = "Alexander Milovsky, Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" from math import sin, cos, pi @@ -30,7 +30,8 @@ import bmesh from bpy.props import ( FloatProperty, FloatVectorProperty, - BoolProperty + BoolProperty, + EnumProperty ) from mathutils import Vector @@ -70,7 +71,9 @@ def _get_uv_layer(ops_obj, bm, assign_uvmap): return uv_layer -def _apply_box_map(bm, uv_layer, size, offset, rotation, tex_aspect): +def _apply_box_map(bm, uv_layer, size, offset, rotation, + tex_aspect, force_axis, force_axis_tex_aspect_correction, + force_axis_rotation): scale = 1.0 / size sx = 1.0 * scale @@ -82,7 +85,10 @@ def _apply_box_map(bm, uv_layer, size, offset, rotation, tex_aspect): rx = rotation[0] * pi / 180.0 ry = rotation[1] * pi / 180.0 rz = rotation[2] * pi / 180.0 - aspect = tex_aspect + + farx = force_axis_rotation[0] * pi / 180.0 + fary = force_axis_rotation[1] * pi / 180.0 + farz = force_axis_rotation[2] * pi / 180.0 sel_faces = [f for f in bm.faces if f.select] @@ -94,37 +100,116 @@ def _apply_box_map(bm, uv_layer, size, offset, rotation, tex_aspect): x = co.x * sx y = co.y * sy z = co.z * sz - - # X-plane - if abs(n[0]) >= abs(n[1]) and abs(n[0]) >= abs(n[2]): - if n[0] >= 0.0: - u = (y - ofy) * cos(rx) + (z - ofz) * sin(rx) - v = -(y * aspect - ofy) * sin(rx) + \ - (z * aspect - ofz) * cos(rx) - else: - u = -(y - ofy) * cos(rx) + (z - ofz) * sin(rx) - v = (y * aspect - ofy) * sin(rx) + \ - (z * aspect - ofz) * cos(rx) - # Y-plane - elif abs(n[1]) >= abs(n[0]) and abs(n[1]) >= abs(n[2]): - if n[1] >= 0.0: - u = -(x - ofx) * cos(ry) + (z - ofz) * sin(ry) - v = (x * aspect - ofx) * sin(ry) + \ - (z * aspect - ofz) * cos(ry) - else: - u = (x - ofx) * cos(ry) + (z - ofz) * sin(ry) - v = -(x * aspect - ofx) * sin(ry) + \ - (z * aspect - ofz) * cos(ry) - # Z-plane - else: - if n[2] >= 0.0: - u = (x - ofx) * cos(rz) + (y - ofy) * sin(rz) - v = -(x * aspect - ofx) * sin(rz) + \ - (y * aspect - ofy) * cos(rz) - else: - u = -(x - ofx) * cos(rz) - (y + ofy) * sin(rz) - v = -(x * aspect + ofx) * sin(rz) + \ - (y * aspect - ofy) * cos(rz) + aspect = tex_aspect + + transformed = False + if force_axis == 'X': + # Use Y-plane + if abs(n[1]) < abs(n[0]) and abs(n[1]) >= abs(n[2]): + aspect *= force_axis_tex_aspect_correction + if n[1] >= 0.0: + u = -(x - ofx) * cos(fary) + (z - ofz) * sin(fary) + v = (x * aspect - ofx) * sin(fary) + \ + (z * aspect - ofz) * cos(fary) + else: + u = (x - ofx) * cos(fary) + (z - ofz) * sin(fary) + v = -(x * aspect - ofx) * sin(fary) + \ + (z * aspect - ofz) * cos(fary) + transformed = True + # Use Z-plane + elif abs(n[2]) < abs(n[0]) and abs(n[2]) >= abs(n[1]): + aspect *= force_axis_tex_aspect_correction + if n[2] >= 0.0: + u = (x - ofx) * cos(farz) + (y - ofy) * sin(farz) + v = -(x * aspect - ofx) * sin(farz) + \ + (y * aspect - ofy) * cos(farz) + else: + u = -(x - ofx) * cos(farz) - (y + ofy) * sin(farz) + v = -(x * aspect + ofx) * sin(farz) + \ + (y * aspect - ofy) * cos(farz) + transformed = True + elif force_axis == 'Y': + # Use X-plane + if abs(n[0]) < abs(n[1]) and abs(n[0]) >= abs(n[2]): + aspect *= force_axis_tex_aspect_correction + if n[0] >= 0.0: + u = (y - ofy) * cos(farx) + (z - ofz) * sin(farx) + v = -(y * aspect - ofy) * sin(farx) + \ + (z * aspect - ofz) * cos(farx) + else: + u = -(y - ofy) * cos(farx) + (z - ofz) * sin(farx) + v = (y * aspect - ofy) * sin(farx) + \ + (z * aspect - ofz) * cos(farx) + transformed = True + # Use Z-plane + elif abs(n[2]) >= abs(n[0]) and abs(n[2]) < abs(n[1]): + aspect *= force_axis_tex_aspect_correction + if n[2] >= 0.0: + u = (x - ofx) * cos(farz) + (y - ofy) * sin(farz) + v = -(x * aspect - ofx) * sin(farz) + \ + (y * aspect - ofy) * cos(farz) + else: + u = -(x - ofx) * cos(farz) - (y + ofy) * sin(farz) + v = -(x * aspect + ofx) * sin(farz) + \ + (y * aspect - ofy) * cos(farz) + transformed = True + elif force_axis == 'Z': + # Use X-plane + if abs(n[0]) >= abs(n[1]) and abs(n[0]) < abs(n[2]): + aspect *= force_axis_tex_aspect_correction + if n[0] >= 0.0: + u = (y - ofy) * cos(farx) + (z - ofz) * sin(farx) + v = -(y * aspect - ofy) * sin(farx) + \ + (z * aspect - ofz) * cos(farx) + else: + u = -(y - ofy) * cos(farx) + (z - ofz) * sin(farx) + v = (y * aspect - ofy) * sin(farx) + \ + (z * aspect - ofz) * cos(farx) + transformed = True + # Use Y-plane + elif abs(n[1]) >= abs(n[0]) and abs(n[1]) < abs(n[2]): + aspect *= force_axis_tex_aspect_correction + if n[1] >= 0.0: + u = -(x - ofx) * cos(fary) + (z - ofz) * sin(fary) + v = (x * aspect - ofx) * sin(fary) + \ + (z * aspect - ofz) * cos(fary) + else: + u = (x - ofx) * cos(fary) + (z - ofz) * sin(fary) + v = -(x * aspect - ofx) * sin(fary) + \ + (z * aspect - ofz) * cos(fary) + transformed = True + + if not transformed: + # X-plane + if abs(n[0]) >= abs(n[1]) and abs(n[0]) >= abs(n[2]): + if n[0] >= 0.0: + u = (y - ofy) * cos(rx) + (z - ofz) * sin(rx) + v = -(y * aspect - ofy) * sin(rx) + \ + (z * aspect - ofz) * cos(rx) + else: + u = -(y - ofy) * cos(rx) + (z - ofz) * sin(rx) + v = (y * aspect - ofy) * sin(rx) + \ + (z * aspect - ofz) * cos(rx) + # Y-plane + elif abs(n[1]) >= abs(n[0]) and abs(n[1]) >= abs(n[2]): + if n[1] >= 0.0: + u = -(x - ofx) * cos(ry) + (z - ofz) * sin(ry) + v = (x * aspect - ofx) * sin(ry) + \ + (z * aspect - ofz) * cos(ry) + else: + u = (x - ofx) * cos(ry) + (z - ofz) * sin(ry) + v = -(x * aspect - ofx) * sin(ry) + \ + (z * aspect - ofz) * cos(ry) + # Z-plane + elif abs(n[2]) >= abs(n[0]) and abs(n[2]) >= abs(n[1]): + if n[2] >= 0.0: + u = (x - ofx) * cos(rz) + (y - ofy) * sin(rz) + v = -(x * aspect - ofx) * sin(rz) + \ + (y * aspect - ofy) * cos(rz) + else: + u = -(x - ofx) * cos(rz) - (y + ofy) * sin(rz) + v = -(x * aspect + ofx) * sin(rz) + \ + (y * aspect - ofy) * cos(rz) l[uv_layer].uv = Vector((u, v)) @@ -196,14 +281,16 @@ class MUV_OT_UVW_BoxMap(bpy.types.Operator): precision=4 ) rotation = FloatVectorProperty( - name="XYZ Rotation", + name="Rotation", size=3, - default=(0.0, 0.0, 0.0) + default=(0.0, 0.0, 0.0), + subtype='XYZ' ) offset = FloatVectorProperty( - name="XYZ Offset", + name="Offset", size=3, - default=(0.0, 0.0, 0.0) + default=(0.0, 0.0, 0.0), + subtype='XYZ' ) tex_aspect = FloatProperty( name="Texture Aspect", @@ -215,6 +302,30 @@ class MUV_OT_UVW_BoxMap(bpy.types.Operator): description="Assign UVMap when no UVmaps are available", default=True ) + force_axis = EnumProperty( + name="Force Axis", + description="Axis to force the mapping", + items=[ + ('NONE', "None", "None"), + ('X', "X", "Axis X"), + ('Y', "Y", "Axis Y"), + ('Z', "Z", "Axis Z") + ], + default='NONE' + ) + force_axis_tex_aspect_correction = FloatProperty( + name="Texture Aspect Correction (Force Axis)", + description="Texture Aspect correction for the faces mapped forcibly", + default=3.14, + precision=4 + ) + force_axis_rotation = FloatVectorProperty( + name="Rotation (Force Axis)", + description="Rotation for the faces mapped forcibly", + size=3, + default=(0.0, 0.0, 0.0), + subtype='XYZ' + ) @classmethod def poll(cls, context): @@ -223,6 +334,39 @@ class MUV_OT_UVW_BoxMap(bpy.types.Operator): return True return _is_valid_context(context) + def draw(self, _): + layout = self.layout + + col = layout.column() + row = col.row() + row.label(text="Size:") + row.prop(self, "size", text="") + + layout.label(text="Rotation:") + layout.row().prop(self, "rotation", text="") + + layout.label(text="Offset:") + layout.row().prop(self, "offset", text="") + + col = layout.column() + row = col.row() + row.label(text="Texture Aspect:") + row.prop(self, "tex_aspect", text="") + + layout.prop(self, "assign_uvmap") + + layout.separator(factor=2.0) + + layout.prop(self, "force_axis") + if self.force_axis != 'NONE': + col = layout.column() + row = col.row() + row.label(text="Texture Aspect Correction (Force Axis)") + row.prop(self, "force_axis_tex_aspect_correction", text="") + + layout.label(text="Rotation (Force Axis)") + layout.row().prop(self, "force_axis_rotation", text="") + def execute(self, context): objs = common.get_uv_editable_objects(context) @@ -237,7 +381,9 @@ class MUV_OT_UVW_BoxMap(bpy.types.Operator): return {'CANCELLED'} _apply_box_map(bm, uv_layer, self.size, self.offset, self.rotation, - self.tex_aspect) + self.tex_aspect, self.force_axis, + self.force_axis_tex_aspect_correction, + self.force_axis_rotation) bmesh.update_edit_mesh(obj.data) return {'FINISHED'} @@ -256,11 +402,11 @@ class MUV_OT_UVW_BestPlanerMap(bpy.types.Operator): precision=4 ) rotation = FloatProperty( - name="XY Rotation", + name="Rotation", default=0.0 ) offset = FloatVectorProperty( - name="XY Offset", + name="Offset", size=2, default=(0.0, 0.0) ) diff --git a/magic_uv/op/world_scale_uv.py b/magic_uv/op/world_scale_uv.py index a2806db5..dd42ed3d 100644 --- a/magic_uv/op/world_scale_uv.py +++ b/magic_uv/op/world_scale_uv.py @@ -20,8 +20,8 @@ __author__ = "McBuff, Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" from math import sqrt @@ -97,11 +97,11 @@ def _measure_wsuv_info(obj, calc_method='MESH', return uv_areas, mesh_areas, densities -def _measure_wsuv_info_from_faces(obj, faces, uv_layer, tex_layer, +def _measure_wsuv_info_from_faces(obj, bm, faces, uv_layer, tex_layer, tex_selection_method='FIRST', tex_size=None): - mesh_area = common.measure_mesh_area_from_faces(faces) + mesh_area = common.measure_mesh_area_from_faces(bm, faces) uv_area = common.measure_uv_area_from_faces( - obj, faces, uv_layer, tex_layer, tex_selection_method, tex_size) + obj, bm, faces, uv_layer, tex_layer, tex_selection_method, tex_size) if not uv_area: return None, mesh_area, None @@ -376,7 +376,12 @@ class MUV_OT_WorldScaleUV_Measure(bpy.types.Operator): @staticmethod def setup_argument(ops, scene): - ops.tgt_texture = scene.muv_world_scale_uv_measure_tgt_texture + try: + ops.tgt_texture = scene.muv_world_scale_uv_measure_tgt_texture + except TypeError: + # Workaround for the error raised when the items of EnumProperty + # are deleted. + ops.tgt_texture = "[Average]" ops.only_selected = scene.muv_world_scale_uv_measure_only_selected def execute(self, context): @@ -524,7 +529,7 @@ class MUV_OT_WorldScaleUV_ApplyManual(bpy.types.Operator): factors = [] for faces in faces_list: uv_area, _, density = _measure_wsuv_info_from_faces( - obj, faces, uv_layer, tex_layer, + obj, bm, faces, uv_layer, tex_layer, tex_selection_method='USER_SPECIFIED', tex_size=tex_size) if not uv_area: @@ -659,7 +664,12 @@ class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator): ops.src_density = scene.muv_world_scale_uv_src_density ops.same_density = False ops.show_dialog = False - ops.tgt_texture = scene.muv_world_scale_uv_apply_tgt_texture + try: + ops.tgt_texture = scene.muv_world_scale_uv_apply_tgt_texture + except TypeError: + # Workaround for the error raised when the items of EnumProperty + # are deleted. + ops.tgt_texture = "[Average]" ops.tgt_area_calc_method = \ scene.muv_world_scale_uv_tgt_area_calc_method ops.only_selected = scene.muv_world_scale_uv_apply_only_selected @@ -688,20 +698,20 @@ class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator): for faces in faces_list: if self.tgt_texture == "[Average]": uv_area, _, density = _measure_wsuv_info_from_faces( - obj, faces, uv_layer, tex_layer, + obj, bm, faces, uv_layer, tex_layer, tex_selection_method='AVERAGE') elif self.tgt_texture == "[Max]": uv_area, _, density = _measure_wsuv_info_from_faces( - obj, faces, uv_layer, tex_layer, + obj, bm, faces, uv_layer, tex_layer, tex_selection_method='MAX') elif self.tgt_texture == "[Min]": uv_area, _, density = _measure_wsuv_info_from_faces( - obj, faces, uv_layer, tex_layer, + obj, bm, faces, uv_layer, tex_layer, tex_selection_method='MIN') else: tgt_texture = bpy.data.images[self.tgt_texture] uv_area, _, density = _measure_wsuv_info_from_faces( - obj, faces, uv_layer, tex_layer, + obj, bm, faces, uv_layer, tex_layer, tex_selection_method='USER_SPECIFIED', tex_size=tgt_texture.size) @@ -859,7 +869,12 @@ class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator): ops.src_uv_area = scene.muv_world_scale_uv_src_uv_area ops.src_mesh_area = scene.muv_world_scale_uv_src_mesh_area ops.show_dialog = False - ops.tgt_texture = scene.muv_world_scale_uv_apply_tgt_texture + try: + ops.tgt_texture = scene.muv_world_scale_uv_apply_tgt_texture + except TypeError: + # Workaround for the error raised when the items of EnumProperty + # are deleted. + ops.tgt_texture = "[Average]" ops.tgt_area_calc_method = \ scene.muv_world_scale_uv_tgt_area_calc_method ops.only_selected = scene.muv_world_scale_uv_apply_only_selected @@ -889,23 +904,23 @@ class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator): if self.tgt_texture == "[Average]": uv_area, mesh_area, density = \ _measure_wsuv_info_from_faces( - obj, faces, uv_layer, tex_layer, + obj, bm, faces, uv_layer, tex_layer, tex_selection_method='AVERAGE') elif self.tgt_texture == "[Max]": uv_area, mesh_area, density = \ _measure_wsuv_info_from_faces( - obj, faces, uv_layer, tex_layer, + obj, bm, faces, uv_layer, tex_layer, tex_selection_method='MAX') elif self.tgt_texture == "[Min]": uv_area, mesh_area, density = \ _measure_wsuv_info_from_faces( - obj, faces, uv_layer, tex_layer, + obj, bm, faces, uv_layer, tex_layer, tex_selection_method='MIN') else: tgt_texture = bpy.data.images[self.tgt_texture] uv_area, mesh_area, density = \ _measure_wsuv_info_from_faces( - obj, faces, uv_layer, tex_layer, + obj, bm, faces, uv_layer, tex_layer, tex_selection_method='USER_SPECIFIED', tex_size=tgt_texture.size) if not uv_area: diff --git a/magic_uv/preferences.py b/magic_uv/preferences.py index ea8e7434..21f70508 100644 --- a/magic_uv/preferences.py +++ b/magic_uv/preferences.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy from bpy.props import ( @@ -241,6 +241,15 @@ class MUV_Preferences(AddonPreferences): size=4, subtype='COLOR' ) + uv_inspection_overlapped_color_for_v3d = FloatVectorProperty( + name="Color (View3D)", + description="Color in View3D", + default=(0.0, 0.0, 1.0, 0.5), + min=0.0, + max=1.0, + size=4, + subtype='COLOR' + ) # for Flipped UV uv_inspection_flipped_color = FloatVectorProperty( @@ -252,6 +261,15 @@ class MUV_Preferences(AddonPreferences): size=4, subtype='COLOR' ) + uv_inspection_flipped_color_for_v3d = FloatVectorProperty( + name="Color (View3D)", + description="Color in View3D", + default=(1.0, 0.0, 0.0, 0.5), + min=0.0, + max=1.0, + size=4, + subtype='COLOR' + ) # for Texture Projection texture_projection_canvas_padding = FloatVectorProperty( @@ -458,6 +476,20 @@ class MUV_Preferences(AddonPreferences): col = sp.column() col.label(text="Flipped UV Color:") col.prop(self, "uv_inspection_flipped_color", text="") + + sp = compat.layout_split(layout, 0.05) + col = sp.column() # spacer + sp = compat.layout_split(sp, 0.3) + col = sp.column() + col.label(text="Overlapped UV Color (View3D):") + col.prop(self, "uv_inspection_overlapped_color_for_v3d", + text="") + sp = compat.layout_split(sp, 0.45) + col = sp.column() + col.label(text="Flipped UV Color (View3D):") + col.prop(self, "uv_inspection_flipped_color_for_v3d", + text="") + layout.separator() layout.prop( diff --git a/magic_uv/properites.py b/magic_uv/properites.py index 28b9216d..a301d64f 100644 --- a/magic_uv/properites.py +++ b/magic_uv/properites.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" from .utils.property_class_registry import PropertyClassRegistry diff --git a/magic_uv/ui/IMAGE_MT_uvs.py b/magic_uv/ui/IMAGE_MT_uvs.py index 3984c20f..79199cfd 100644 --- a/magic_uv/ui/IMAGE_MT_uvs.py +++ b/magic_uv/ui/IMAGE_MT_uvs.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy @@ -123,13 +123,16 @@ class MUV_MT_SelectUV(bpy.types.Menu): bl_label = "Select UV" bl_description = "Select UV" - def draw(self, _): + def draw(self, context): + sc = context.scene layout = self.layout - layout.operator(MUV_OT_SelectUV_SelectOverlapped.bl_idname, - text="Overlapped") - layout.operator(MUV_OT_SelectUV_SelectFlipped.bl_idname, - text="Flipped") + ops = layout.operator(MUV_OT_SelectUV_SelectOverlapped.bl_idname, + text="Overlapped") + MUV_OT_SelectUV_SelectOverlapped.setup_argument(ops, sc) + ops = layout.operator(MUV_OT_SelectUV_SelectFlipped.bl_idname, + text="Flipped") + MUV_OT_SelectUV_SelectFlipped.setup_argument(ops, sc) @BlClassRegistry() diff --git a/magic_uv/ui/VIEW3D_MT_object.py b/magic_uv/ui/VIEW3D_MT_object.py index c8980592..e4255c3a 100644 --- a/magic_uv/ui/VIEW3D_MT_object.py +++ b/magic_uv/ui/VIEW3D_MT_object.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy diff --git a/magic_uv/ui/VIEW3D_MT_uv_map.py b/magic_uv/ui/VIEW3D_MT_uv_map.py index e6574f4d..a795ad48 100644 --- a/magic_uv/ui/VIEW3D_MT_uv_map.py +++ b/magic_uv/ui/VIEW3D_MT_uv_map.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy.utils diff --git a/magic_uv/ui/__init__.py b/magic_uv/ui/__init__.py index 083590a6..ebf11949 100644 --- a/magic_uv/ui/__init__.py +++ b/magic_uv/ui/__init__.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" if "bpy" in locals(): import importlib diff --git a/magic_uv/ui/uvedit_copy_paste_uv.py b/magic_uv/ui/uvedit_copy_paste_uv.py index 5f029f6f..2f652460 100644 --- a/magic_uv/ui/uvedit_copy_paste_uv.py +++ b/magic_uv/ui/uvedit_copy_paste_uv.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy diff --git a/magic_uv/ui/uvedit_editor_enhancement.py b/magic_uv/ui/uvedit_editor_enhancement.py index a0eba3a9..3d1e9ba3 100644 --- a/magic_uv/ui/uvedit_editor_enhancement.py +++ b/magic_uv/ui/uvedit_editor_enhancement.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy @@ -143,6 +143,10 @@ class MUV_PT_UVEdit_EditorEnhancement(bpy.types.Panel): row.prop(sc, "muv_uv_inspection_show_overlapped") row.prop(sc, "muv_uv_inspection_show_flipped") row = box.row() + row.prop(sc, "muv_uv_inspection_display_in_v3d", text="3D") row.prop(sc, "muv_uv_inspection_show_mode") + if sc.muv_uv_inspection_show_overlapped: + row = box.row() + row.prop(sc, "muv_uv_inspection_same_polygon_threshold") box.separator() box.operator(MUV_OT_UVInspection_PaintUVIsland.bl_idname) diff --git a/magic_uv/ui/uvedit_uv_manipulation.py b/magic_uv/ui/uvedit_uv_manipulation.py index 1b05cb00..4a4358da 100644 --- a/magic_uv/ui/uvedit_uv_manipulation.py +++ b/magic_uv/ui/uvedit_uv_manipulation.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy @@ -41,6 +41,7 @@ from ..op.smooth_uv import ( from ..op.select_uv import ( MUV_OT_SelectUV_SelectOverlapped, MUV_OT_SelectUV_SelectFlipped, + MUV_OT_SelectUV_ZoomSelectedUV, ) from ..op.pack_uv import MUV_OT_PackUV from ..op.clip_uv import MUV_OT_ClipUV @@ -176,8 +177,20 @@ class MUV_PT_UVEdit_UVManipulation(bpy.types.Panel): box.prop(sc, "muv_select_uv_enabled", text="Select UV") if sc.muv_select_uv_enabled: row = box.row(align=True) - row.operator(MUV_OT_SelectUV_SelectOverlapped.bl_idname) - row.operator(MUV_OT_SelectUV_SelectFlipped.bl_idname) + ops = row.operator(MUV_OT_SelectUV_SelectOverlapped.bl_idname) + MUV_OT_SelectUV_SelectOverlapped.setup_argument(ops, sc) + ops = row.operator(MUV_OT_SelectUV_SelectFlipped.bl_idname) + MUV_OT_SelectUV_SelectFlipped.setup_argument(ops, sc) + + col = box.column() + col.label(text="Same Polygon Threshold:") + col.prop(sc, "muv_select_uv_same_polygon_threshold", text="") + col.prop(sc, "muv_select_uv_selection_method") + col.prop(sc, "muv_select_uv_sync_mesh_selection") + + box.separator() + + box.operator(MUV_OT_SelectUV_ZoomSelectedUV.bl_idname) box = layout.box() box.prop(sc, "muv_pack_uv_enabled", text="Pack UV (Extension)") diff --git a/magic_uv/ui/view3d_copy_paste_uv_editmode.py b/magic_uv/ui/view3d_copy_paste_uv_editmode.py index 762fd9d9..6a458aff 100644 --- a/magic_uv/ui/view3d_copy_paste_uv_editmode.py +++ b/magic_uv/ui/view3d_copy_paste_uv_editmode.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy diff --git a/magic_uv/ui/view3d_copy_paste_uv_objectmode.py b/magic_uv/ui/view3d_copy_paste_uv_objectmode.py index 71d30755..b4fb3934 100644 --- a/magic_uv/ui/view3d_copy_paste_uv_objectmode.py +++ b/magic_uv/ui/view3d_copy_paste_uv_objectmode.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy diff --git a/magic_uv/ui/view3d_uv_manipulation.py b/magic_uv/ui/view3d_uv_manipulation.py index 6d0fce6d..dde58266 100644 --- a/magic_uv/ui/view3d_uv_manipulation.py +++ b/magic_uv/ui/view3d_uv_manipulation.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy @@ -94,7 +94,9 @@ class MUV_PT_View3D_UVManipulation(bpy.types.Panel): row = box.row() ops = row.operator(MUV_OT_MirrorUV.bl_idname, text="Mirror") ops.axis = sc.muv_mirror_uv_axis + ops.origin = sc.muv_mirror_uv_origin row.prop(sc, "muv_mirror_uv_axis", text="") + box.prop(sc, "muv_mirror_uv_origin") box = layout.box() box.prop(sc, "muv_move_uv_enabled", text="Move UV") diff --git a/magic_uv/ui/view3d_uv_mapping.py b/magic_uv/ui/view3d_uv_mapping.py index 6a4217c0..22d20b4f 100644 --- a/magic_uv/ui/view3d_uv_mapping.py +++ b/magic_uv/ui/view3d_uv_mapping.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy @@ -92,8 +92,18 @@ class MUV_PT_View3D_UVMapping(bpy.types.Panel): row.prop(sc, "muv_texture_projection_adjust_window", text="Adjust Window") if not sc.muv_texture_projection_adjust_window: - row.prop(sc, "muv_texture_projection_tex_magnitude", - text="Magnitude") + sp = compat.layout_split(col, factor=0.5) + sub = sp.column() + sub.prop(sc, "muv_texture_projection_tex_scaling", + text="Scaling") + sp = compat.layout_split(sp, factor=1.0) + sub = sp.column() + sub.prop(sc, "muv_texture_projection_tex_translation", + text="Translation") + row = col.row() + row.label(text="Rotation:") + row.prop(sc, "muv_texture_projection_tex_rotation", text="") + col.separator() col.prop(sc, "muv_texture_projection_apply_tex_aspect", text="Texture Aspect Ratio") col.prop(sc, "muv_texture_projection_assign_uvmap", diff --git a/magic_uv/updater.py b/magic_uv/updater.py index a9e09bfa..72d85766 100644 --- a/magic_uv/updater.py +++ b/magic_uv/updater.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import os @@ -131,7 +131,7 @@ def register_updater(bl_info): config.owner = "nutti" config.repository = "Magic-UV" config.current_addon_path = os.path.dirname(os.path.realpath(__file__)) - config.branches = ["master", "develop"] + config.branches = ["master"] config.addon_directory = \ config.current_addon_path[ :config.current_addon_path.rfind(get_separator())] @@ -139,7 +139,6 @@ def register_updater(bl_info): config.default_target_addon_path = "magic_uv" config.target_addon_path = { "master": "src{}magic_uv".format(get_separator()), - "develop": "src{}magic_uv".format(get_separator()), } updater = AddonUpdaterManager.get_instance() updater.init(bl_info, config) diff --git a/magic_uv/utils/__init__.py b/magic_uv/utils/__init__.py index 918bc207..776c7564 100644 --- a/magic_uv/utils/__init__.py +++ b/magic_uv/utils/__init__.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" if "bpy" in locals(): import importlib diff --git a/magic_uv/utils/addon_updater.py b/magic_uv/utils/addon_updater.py index 813813eb..8c1601b8 100644 --- a/magic_uv/utils/addon_updater.py +++ b/magic_uv/utils/addon_updater.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" from threading import Lock import urllib diff --git a/magic_uv/utils/bl_class_registry.py b/magic_uv/utils/bl_class_registry.py index 080a1a45..93f29244 100644 --- a/magic_uv/utils/bl_class_registry.py +++ b/magic_uv/utils/bl_class_registry.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy diff --git a/magic_uv/utils/compatibility.py b/magic_uv/utils/compatibility.py index 517c33af..d17f2c66 100644 --- a/magic_uv/utils/compatibility.py +++ b/magic_uv/utils/compatibility.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" import bpy import bgl @@ -47,7 +47,9 @@ def make_annotations(cls): return cls # make annotation from attributes - props = {k: v for k, v in cls.__dict__.items() if isinstance(v, tuple)} + props = {k: v + for k, v in cls.__dict__.items() + if isinstance(v, getattr(bpy.props, '_PropertyDeferred', tuple))} if props: if '__annotations__' not in cls.__dict__: setattr(cls, '__annotations__', {}) diff --git a/magic_uv/utils/property_class_registry.py b/magic_uv/utils/property_class_registry.py index f107aed3..ef4f5872 100644 --- a/magic_uv/utils/property_class_registry.py +++ b/magic_uv/utils/property_class_registry.py @@ -20,8 +20,8 @@ __author__ = "Nutti <nutti.metro@gmail.com>" __status__ = "production" -__version__ = "6.4" -__date__ = "23 Oct 2020" +__version__ = "6.5" +__date__ = "6 Mar 2021" from .. import common |