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
This commit is contained in:
nutti 2021-03-06 17:54:33 +09:00
parent c875244331
commit 117faa96af
47 changed files with 975 additions and 231 deletions

View File

@ -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",

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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
)

View File

@ -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

View File

@ -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'}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 (

View File

@ -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

View File

@ -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 (

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
aspect = tex_aspect
# 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)
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)
)

View File

@ -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:

View File

@ -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(

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)")

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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",

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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__', {})

View File

@ -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