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 /magic_uv/op | |
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
Diffstat (limited to 'magic_uv/op')
-rw-r--r-- | magic_uv/op/__init__.py | 4 | ||||
-rw-r--r-- | magic_uv/op/align_uv.py | 4 | ||||
-rw-r--r-- | magic_uv/op/align_uv_cursor.py | 4 | ||||
-rw-r--r-- | magic_uv/op/clip_uv.py | 4 | ||||
-rw-r--r-- | magic_uv/op/copy_paste_uv.py | 4 | ||||
-rw-r--r-- | magic_uv/op/copy_paste_uv_object.py | 4 | ||||
-rw-r--r-- | magic_uv/op/copy_paste_uv_uvedit.py | 4 | ||||
-rw-r--r-- | magic_uv/op/flip_rotate_uv.py | 4 | ||||
-rw-r--r-- | magic_uv/op/mirror_uv.py | 96 | ||||
-rw-r--r-- | magic_uv/op/move_uv.py | 4 | ||||
-rw-r--r-- | magic_uv/op/pack_uv.py | 15 | ||||
-rw-r--r-- | magic_uv/op/preserve_uv_aspect.py | 4 | ||||
-rw-r--r-- | magic_uv/op/select_uv.py | 203 | ||||
-rw-r--r-- | magic_uv/op/smooth_uv.py | 4 | ||||
-rw-r--r-- | magic_uv/op/texture_lock.py | 4 | ||||
-rw-r--r-- | magic_uv/op/texture_projection.py | 136 | ||||
-rw-r--r-- | magic_uv/op/texture_wrap.py | 4 | ||||
-rw-r--r-- | magic_uv/op/transfer_uv.py | 4 | ||||
-rw-r--r-- | magic_uv/op/unwrap_constraint.py | 4 | ||||
-rw-r--r-- | magic_uv/op/uv_bounding_box.py | 4 | ||||
-rw-r--r-- | magic_uv/op/uv_inspection.py | 110 | ||||
-rw-r--r-- | magic_uv/op/uv_sculpt.py | 4 | ||||
-rw-r--r-- | magic_uv/op/uvw.py | 232 | ||||
-rw-r--r-- | magic_uv/op/world_scale_uv.py | 49 |
24 files changed, 763 insertions, 146 deletions
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: |