Magic UV: release v4.5

This commit is contained in:
nutti 2017-11-19 15:35:57 +09:00
parent c21b887882
commit 85a2b50e0e
19 changed files with 635 additions and 174 deletions

View File

@ -20,15 +20,15 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
bl_info = {
"name": "Magic UV",
"author": "Nutti, Mifth, Jace Priester, kgeogeo, mem, "
"Keith (Wahooney) Boshoff, McBuff, MaxRobinot",
"version": (4, 4, 0),
"Keith (Wahooney) Boshoff, McBuff, MaxRobinot, Alexander Milovsky",
"version": (4, 5, 0),
"blender": (2, 79, 0),
"location": "See Add-ons Preferences",
"description": "UV Manipulator Tools. See Add-ons Preferences for details",
@ -59,6 +59,7 @@ if "bpy" in locals():
importlib.reload(muv_wsuv_ops)
importlib.reload(muv_unwrapconst_ops)
importlib.reload(muv_preserve_uv_aspect)
importlib.reload(muv_uvw_ops)
else:
from . import muv_preferences
from . import muv_menu
@ -77,6 +78,7 @@ else:
from . import muv_wsuv_ops
from . import muv_unwrapconst_ops
from . import muv_preserve_uv_aspect
from . import muv_uvw_ops
import bpy
@ -84,7 +86,8 @@ import bpy
def view3d_uvmap_menu_fn(self, context):
self.layout.separator()
self.layout.menu(muv_menu.MUV_CPUVMenu.bl_idname, icon="IMAGE_COL")
self.layout.operator(muv_fliprot_ops.MUV_FlipRot.bl_idname, icon="IMAGE_COL")
self.layout.operator(
muv_fliprot_ops.MUV_FlipRot.bl_idname, icon="IMAGE_COL")
self.layout.menu(muv_menu.MUV_TransUVMenu.bl_idname, icon="IMAGE_COL")
self.layout.operator(muv_mvuv_ops.MUV_MVUV.bl_idname, icon="IMAGE_COL")
self.layout.menu(muv_menu.MUV_TexLockMenu.bl_idname, icon="IMAGE_COL")
@ -96,6 +99,7 @@ def view3d_uvmap_menu_fn(self, context):
self.layout.menu(
muv_preserve_uv_aspect.MUV_PreserveUVAspectMenu.bl_idname,
icon='IMAGE_COL')
self.layout.menu(muv_menu.MUV_UVWMenu.bl_idname, icon="IMAGE_COL")
def image_uvs_menu_fn(self, context):

View File

@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
import bpy
from . import muv_props
@ -45,11 +45,9 @@ def check_version(major, minor, _):
return 0
if bpy.app.version[0] > major:
return 1
else:
if bpy.app.version[1] > minor:
return 1
else:
return -1
if bpy.app.version[1] > minor:
return 1
return -1
def redraw_all_areas():

View File

@ -20,17 +20,17 @@
__author__ = "Nutti <nutti.metro@gmail.com>, Jace Priester"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
import bpy
import bmesh
from bpy.props import (
StringProperty,
BoolProperty,
IntProperty,
EnumProperty,
)
StringProperty,
BoolProperty,
IntProperty,
EnumProperty,
)
from . import muv_common
@ -85,11 +85,11 @@ class MUV_CPUVCopyUV(bpy.types.Operator):
if face.select:
uvs = [l[uv_layer].uv.copy() for l in face.loops]
pin_uvs = [l[uv_layer].pin_uv for l in face.loops]
seams = [l.edge.seam for l in face.loops]
seams = [l.edge.seam for l in face.loops]
props.src_uvs.append(uvs)
props.src_pin_uvs.append(pin_uvs)
props.src_seams.append(seams)
if len(props.src_uvs) == 0 or len(props.src_pin_uvs) == 0:
if not props.src_uvs or not props.src_pin_uvs:
self.report({'WARNING'}, "No faces are selected")
return {'CANCELLED'}
self.report({'INFO'}, "%d face(s) are selected" % len(props.src_uvs))
@ -164,7 +164,7 @@ class MUV_CPUVPasteUV(bpy.types.Operator):
def execute(self, context):
props = context.scene.muv_props.cpuv
if len(props.src_uvs) == 0 or len(props.src_pin_uvs) == 0:
if not props.src_uvs or not props.src_pin_uvs:
self.report({'WARNING'}, "Need copy UV at first")
return {'CANCELLED'}
if self.uv_map == "":
@ -201,7 +201,7 @@ class MUV_CPUVPasteUV(bpy.types.Operator):
dest_uvs.append(uvs)
dest_pin_uvs.append(pin_uvs)
dest_seams.append(seams)
if len(dest_uvs) == 0 or len(dest_pin_uvs) == 0:
if not dest_uvs or not dest_pin_uvs:
self.report({'WARNING'}, "No faces are selected")
return {'CANCELLED'}
if self.strategy == 'N_N' and len(props.src_uvs) != len(dest_uvs):
@ -248,7 +248,8 @@ class MUV_CPUVPasteUV(bpy.types.Operator):
spuvs_fr.insert(0, pin_uv)
ss_fr.insert(0, s)
# paste UVs
for l, suv, spuv, ss in zip(bm.faces[idx].loops, suvs_fr, spuvs_fr, ss_fr):
for l, suv, spuv, ss in zip(bm.faces[idx].loops, suvs_fr,
spuvs_fr, ss_fr):
l[uv_layer].uv = suv
l[uv_layer].pin_uv = spuv
if self.copy_seams is True:
@ -331,7 +332,7 @@ class MUV_CPUVObjCopyUV(bpy.types.Operator):
for face in bm.faces:
uvs = [l[uv_layer].uv.copy() for l in face.loops]
pin_uvs = [l[uv_layer].pin_uv for l in face.loops]
seams = [l.edge.seam for l in face.loops]
seams = [l.edge.seam for l in face.loops]
props.src_uvs.append(uvs)
props.src_pin_uvs.append(pin_uvs)
props.src_seams.append(seams)
@ -383,7 +384,7 @@ class MUV_CPUVObjPasteUV(bpy.types.Operator):
@memorize_view_3d_mode
def execute(self, context):
props = context.scene.muv_props.cpuv_obj
if len(props.src_uvs) == 0 or len(props.src_pin_uvs) == 0:
if not props.src_uvs or not props.src_pin_uvs:
self.report({'WARNING'}, "Need copy UV at first")
return {'CANCELLED'}

View File

@ -20,17 +20,17 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
import bpy
import bmesh
from bpy.props import (
StringProperty,
BoolProperty,
IntProperty,
EnumProperty,
)
StringProperty,
BoolProperty,
IntProperty,
EnumProperty,
)
from . import muv_common
@ -78,11 +78,11 @@ class MUV_CPUVSelSeqCopyUV(bpy.types.Operator):
if isinstance(hist, bmesh.types.BMFace) and hist.select:
uvs = [l[uv_layer].uv.copy() for l in hist.loops]
pin_uvs = [l[uv_layer].pin_uv for l in hist.loops]
seams = [l.edge.seam for l in hist.loops]
seams = [l.edge.seam for l in hist.loops]
props.src_uvs.append(uvs)
props.src_pin_uvs.append(pin_uvs)
props.src_seams.append(seams)
if len(props.src_uvs) == 0 or len(props.src_pin_uvs) == 0:
if not props.src_uvs or not props.src_pin_uvs:
self.report({'WARNING'}, "No faces are selected")
return {'CANCELLED'}
self.report({'INFO'}, "%d face(s) are selected" % len(props.src_uvs))
@ -152,7 +152,7 @@ class MUV_CPUVSelSeqPasteUV(bpy.types.Operator):
def execute(self, context):
props = context.scene.muv_props.cpuv_selseq
if len(props.src_uvs) == 0 or len(props.src_pin_uvs) == 0:
if not props.src_uvs or not props.src_pin_uvs:
self.report({'WARNING'}, "Need copy UV at first")
return {'CANCELLED'}
if self.uv_map == "":
@ -192,7 +192,7 @@ class MUV_CPUVSelSeqPasteUV(bpy.types.Operator):
dest_uvs.append(uvs)
dest_pin_uvs.append(pin_uvs)
dest_seams.append(seams)
if len(dest_uvs) == 0 or len(dest_pin_uvs) == 0:
if not dest_uvs or not dest_pin_uvs:
self.report({'WARNING'}, "No faces are selected")
return {'CANCELLED'}
if self.strategy == 'N_N' and len(props.src_uvs) != len(dest_uvs):
@ -239,7 +239,8 @@ class MUV_CPUVSelSeqPasteUV(bpy.types.Operator):
spuvs_fr.insert(0, pin_uv)
ss_fr.insert(0, s)
# paste UVs
for l, suv, spuv, ss in zip(bm.faces[idx].loops, suvs_fr, spuvs_fr, ss_fr):
for l, suv, spuv, ss in zip(bm.faces[idx].loops, suvs_fr,
spuvs_fr, ss_fr):
l[uv_layer].uv = suv
l[uv_layer].pin_uv = spuv
if self.copy_seams is True:

View File

@ -20,15 +20,15 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
import bpy
import bmesh
from bpy.props import (
BoolProperty,
IntProperty,
)
BoolProperty,
IntProperty,
)
from . import muv_common
@ -86,13 +86,14 @@ class MUV_FlipRot(bpy.types.Operator):
dest_uvs.append(uvs)
dest_pin_uvs.append(pin_uvs)
dest_seams.append(seams)
if len(dest_uvs) == 0 or len(dest_pin_uvs) == 0:
if not dest_uvs or not dest_pin_uvs:
self.report({'WARNING'}, "No faces are selected")
return {'CANCELLED'}
self.report({'INFO'}, "%d face(s) are selected" % len(dest_uvs))
# paste
for idx, duvs, dpuvs, dss in zip(dest_face_indices, dest_uvs, dest_pin_uvs, dest_seams):
for idx, duvs, dpuvs, dss in zip(dest_face_indices, dest_uvs,
dest_pin_uvs, dest_seams):
duvs_fr = [uv for uv in duvs]
dpuvs_fr = [pin_uv for pin_uv in dpuvs]
dss_fr = [s for s in dss]

View File

@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
import bpy
from . import muv_cpuv_ops
@ -29,6 +29,7 @@ from . import muv_cpuv_selseq_ops
from . import muv_transuv_ops
from . import muv_texlock_ops
from . import muv_wsuv_ops
from . import muv_uvw_ops
class MUV_CPUVMenu(bpy.types.Menu):
@ -119,3 +120,19 @@ class MUV_WSUVMenu(bpy.types.Menu):
muv_wsuv_ops.MUV_WSUVMeasure.bl_idname, icon="IMAGE_COL")
self.layout.operator(
muv_wsuv_ops.MUV_WSUVApply.bl_idname, icon="IMAGE_COL")
class MUV_UVWMenu(bpy.types.Menu):
"""
Menu class: Master menu of UVW
"""
bl_idname = "uv.muv_uvw_menu"
bl_label = "UVW"
bl_description = ""
def draw(self, _):
self.layout.operator(
muv_uvw_ops.MUV_UVWBoxMap.bl_idname, icon="IMAGE_COL")
self.layout.operator(
muv_uvw_ops.MUV_UVWBestPlanerMap.bl_idname, icon="IMAGE_COL")

View File

@ -20,14 +20,14 @@
__author__ = "Keith (Wahooney) Boshoff, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
import bpy
from bpy.props import (
EnumProperty,
FloatProperty,
)
EnumProperty,
FloatProperty,
)
import bmesh
from mathutils import Vector
from . import muv_common

View File

@ -20,8 +20,8 @@
__author__ = "kgeogeo, mem, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
import bpy
import bmesh

View File

@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
from math import fabs
from collections import defaultdict
@ -30,10 +30,10 @@ import bpy
import bmesh
import mathutils
from bpy.props import (
FloatProperty,
FloatVectorProperty,
BoolProperty,
)
FloatProperty,
FloatVectorProperty,
BoolProperty,
)
from mathutils import Vector
from . import muv_common
@ -279,7 +279,7 @@ class MUV_PackUV(bpy.types.Operator):
uv_island_lists = []
faces_left = set(self.__face_to_verts.keys())
while len(faces_left) > 0:
while faces_left:
current_island = []
face_idx = list(faces_left)[0]
self.__parse_island(bm, face_idx, faces_left, current_island)

View File

@ -20,14 +20,14 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
from bpy.props import (
BoolProperty,
FloatProperty,
FloatVectorProperty,
)
BoolProperty,
FloatProperty,
FloatVectorProperty,
)
from bpy.types import AddonPreferences

View File

@ -20,12 +20,12 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
import bpy
import bmesh
from bpy.props import StringProperty
from bpy.props import StringProperty, EnumProperty
from mathutils import Vector
from . import muv_common
@ -41,6 +41,23 @@ class MUV_PreserveUVAspect(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO'}
dest_img_name = StringProperty(options={'HIDDEN'})
origin = EnumProperty(
name="Origin",
description="Aspect Origin",
items=[
('CENTER', 'Center', 'Center'),
('LEFT_TOP', 'Left Top', 'Left Bottom'),
('LEFT_CENTER', 'Left Center', 'Left Center'),
('LEFT_BOTTOM', 'Left Bottom', 'Left Bottom'),
('CENTER_TOP', 'Center Top', 'Center Top'),
('CENTER_BOTTOM', 'Center Bottom', 'Center Bottom'),
('RIGHT_TOP', 'Right Top', 'Right Top'),
('RIGHT_CENTER', 'Right Center', 'Right Center'),
('RIGHT_BOTTOM', 'Right Bottom', 'Right Bottom')
],
default="CENTER"
)
@classmethod
def poll(cls, context):
@ -83,12 +100,85 @@ class MUV_PreserveUVAspect(bpy.types.Operator):
ratio = Vector((
dest_img.size[0] / src_img.size[0],
dest_img.size[1] / src_img.size[1]))
origin = Vector((100000.0, 100000.0))
for f in info[img]['faces']:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = min(uv.x, origin.x)
origin.y = min(uv.y, origin.y)
if self.origin == 'CENTER':
origin = Vector((0.0, 0.0))
num = 0
for f in info[img]['faces']:
for l in f.loops:
uv = l[uv_layer].uv
origin = origin + uv
num = num + 1
origin = origin / num
elif self.origin == 'LEFT_TOP':
origin = Vector((100000.0, -100000.0))
for f in info[img]['faces']:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = min(origin.x, uv.x)
origin.y = max(origin.y, uv.y)
elif self.origin == 'LEFT_CENTER':
origin = Vector((100000.0, 0.0))
num = 0
for f in info[img]['faces']:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = min(origin.x, uv.x)
origin.y = origin.y + uv.y
num = num + 1
origin.y = origin.y / num
elif self.origin == 'LEFT_BOTTOM':
origin = Vector((100000.0, 100000.0))
for f in info[img]['faces']:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = min(origin.x, uv.x)
origin.y = min(origin.y, uv.y)
elif self.origin == 'CENTER_TOP':
origin = Vector((0.0, -100000.0))
num = 0
for f in info[img]['faces']:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = origin.x + uv.x
origin.y = max(origin.y, uv.y)
num = num + 1
origin.x = origin.x / num
elif self.origin == 'CENTER_BOTTOM':
origin = Vector((0.0, 100000.0))
num = 0
for f in info[img]['faces']:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = origin.x + uv.x
origin.y = min(origin.y, uv.y)
num = num + 1
origin.x = origin.x / num
elif self.origin == 'RIGHT_TOP':
origin = Vector((-100000.0, -100000.0))
for f in info[img]['faces']:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = max(origin.x, uv.x)
origin.y = max(origin.y, uv.y)
elif self.origin == 'RIGHT_CENTER':
origin = Vector((-100000.0, 0.0))
num = 0
for f in info[img]['faces']:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = max(origin.x, uv.x)
origin.y = origin.y + uv.y
num = num + 1
origin.y = origin.y / num
elif self.origin == 'RIGHT_BOTTOM':
origin = Vector((-100000.0, 100000.0))
for f in info[img]['faces']:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = max(origin.x, uv.x)
origin.y = min(origin.y, uv.y)
info[img]['ratio'] = ratio
info[img]['origin'] = origin
@ -100,11 +190,14 @@ class MUV_PreserveUVAspect(bpy.types.Operator):
f[tex_layer].image = dest_img
for l in f.loops:
uv = l[uv_layer].uv
diff = uv - info[img]['origin']
diff.x = diff.x / info[img]['ratio'].x
diff.y = diff.y / info[img]['ratio'].y
origin = info[img]['origin']
ratio = info[img]['ratio']
diff = uv - origin
diff.x = diff.x / ratio.x
diff.y = diff.y / ratio.y
uv.x = origin.x + diff.x
uv.y = origin.y + diff.y
l[uv_layer].uv = uv
bmesh.update_edit_mesh(obj.data)

View File

@ -20,15 +20,15 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
import bpy
from bpy.props import (
FloatProperty,
EnumProperty,
BoolProperty,
)
FloatProperty,
EnumProperty,
BoolProperty,
)
DEBUG = False

View File

@ -20,14 +20,14 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
import math
from math import (
atan2, cos,
sqrt, sin, fabs,
)
atan2, cos,
sqrt, sin, fabs,
)
import bpy
import bmesh

View File

@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
from collections import namedtuple

View File

@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>, Mifth, MaxRobinot"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
from collections import OrderedDict
@ -134,46 +134,48 @@ class MUV_TransUVPaste(bpy.types.Operator):
# parse selection history
for i, _ in enumerate(all_sel_faces):
if i > 0 and i % 2 != 0:
sel_faces = [all_sel_faces[i - 1], all_sel_faces[i]]
active_face = all_sel_faces[i]
if (i == 0) or (i % 2 == 0):
continue
sel_faces = [all_sel_faces[i - 1], all_sel_faces[i]]
active_face = all_sel_faces[i]
# parse all faces according to selection history
active_face_nor = active_face.normal.copy()
if self.invert_normals:
active_face_nor.negate()
all_sorted_faces = main_parse(
self, uv_layer, sel_faces, active_face,
active_face_nor)
# parse all faces according to selection history
active_face_nor = active_face.normal.copy()
if self.invert_normals:
active_face_nor.negate()
all_sorted_faces = main_parse(
self, uv_layer, sel_faces, active_face,
active_face_nor)
if all_sorted_faces:
# check amount of copied/pasted faces
if len(all_sorted_faces) != len(props.topology_copied):
if all_sorted_faces:
# check amount of copied/pasted faces
if len(all_sorted_faces) != len(props.topology_copied):
self.report(
{'WARNING'},
"Mesh has different amount of faces"
)
return {'FINISHED'}
for j, face_data in enumerate(all_sorted_faces.values()):
copied_data = props.topology_copied[j]
# check amount of copied/pasted verts
if len(copied_data[0]) != len(face_data[2]):
bpy.ops.mesh.select_all(action='DESELECT')
# select problematic face
list(all_sorted_faces.keys())[j].select = True
self.report(
{'WARNING'},
"Mesh has different amount of faces"
"Face have different amount of vertices"
)
return {'FINISHED'}
for i, face_data in enumerate(all_sorted_faces.values()):
copied_data = props.topology_copied[i]
# check amount of copied/pasted verts
if len(copied_data[0]) != len(face_data[2]):
bpy.ops.mesh.select_all(action='DESELECT')
# select problematic face
list(all_sorted_faces.keys())[i].select = True
self.report(
{'WARNING'},
"Face have different amount of vertices"
)
return {'FINISHED'}
for j, (edge, uvloop) in enumerate(zip(face_data[1], face_data[2])):
uvloop.uv = copied_data[0][j]
uvloop.pin_uv = copied_data[1][j]
if self.copy_seams:
edge.seam = copied_data[2][j]
for k, (edge, uvloop) in enumerate(zip(face_data[1],
face_data[2])):
uvloop.uv = copied_data[0][k]
uvloop.pin_uv = copied_data[1][k]
if self.copy_seams:
edge.seam = copied_data[2][k]
bmesh.update_edit_mesh(active_obj.data)
if self.copy_seams:

View File

@ -18,16 +18,16 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
import bpy
import bmesh
from bpy.props import (
BoolProperty,
EnumProperty,
FloatProperty,
)
BoolProperty,
EnumProperty,
FloatProperty,
)
from . import muv_common

View File

@ -20,8 +20,8 @@
__author__ = "Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
from enum import IntEnum
import math
@ -399,26 +399,25 @@ class MUV_UVBBStateNone(MUV_UVBBStateBase):
prefs = context.user_preferences.addons["uv_magic_uv"].preferences
cp_react_size = prefs.uvbb_cp_react_size
is_uscaling = context.scene.muv_uvbb_uniform_scaling
if event.type == 'LEFTMOUSE':
if event.value == 'PRESS':
x, y = context.region.view2d.view_to_region(
mouse_view.x, mouse_view.y)
for i, p in enumerate(ctrl_points):
px, py = context.region.view2d.view_to_region(p.x, p.y)
in_cp_x = (px + cp_react_size > x and
px - cp_react_size < x)
in_cp_y = (py + cp_react_size > y and
py - cp_react_size < y)
if in_cp_x and in_cp_y:
if is_uscaling:
arr = [1, 3, 6, 8]
if i in arr:
return (
MUV_UVBBState.UNIFORM_SCALING_1 +
arr.index(i)
)
else:
return MUV_UVBBState.TRANSLATING + i
if (event.type == 'LEFTMOUSE') and (event.value == 'PRESS'):
x, y = context.region.view2d.view_to_region(
mouse_view.x, mouse_view.y)
for i, p in enumerate(ctrl_points):
px, py = context.region.view2d.view_to_region(p.x, p.y)
in_cp_x = (px + cp_react_size > x and
px - cp_react_size < x)
in_cp_y = (py + cp_react_size > y and
py - cp_react_size < y)
if in_cp_x and in_cp_y:
if is_uscaling:
arr = [1, 3, 6, 8]
if i in arr:
return (
MUV_UVBBState.UNIFORM_SCALING_1 +
arr.index(i)
)
else:
return MUV_UVBBState.TRANSLATING + i
return MUV_UVBBState.NONE
@ -615,7 +614,7 @@ class MUV_UVBBUpdater(bpy.types.Operator):
if f.select:
for i, l in enumerate(f.loops):
uv_info.append((f.index, i, l[uv_layer].uv.copy()))
if len(uv_info) == 0:
if not uv_info:
return None
return uv_info

222
uv_magic_uv/muv_uvw_ops.py Normal file
View File

@ -0,0 +1,222 @@
# <pep8-80 compliant>
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
__author__ = "Alexander Milovsky, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.5"
__date__ = "19 Nov 2017"
from math import sin, cos, pi
import bpy
import bmesh
from bpy.props import (
FloatProperty,
FloatVectorProperty
)
from mathutils import Vector
from . import muv_common
class MUV_UVWBoxMap(bpy.types.Operator):
bl_idname = "uv.muv_uvw_box_map"
bl_label = "Box Map"
bl_options = {'REGISTER', 'UNDO'}
size = FloatProperty(
name="Size",
default=1.0,
precision=4
)
rotation = FloatVectorProperty(
name="XYZ Rotation",
size=3,
default=(0.0, 0.0, 0.0)
)
offset = FloatVectorProperty(
name="XYZ Offset",
size=3,
default=(0.0, 0.0, 0.0)
)
tex_aspect = FloatProperty(
name="Texture Aspect",
default=1.0,
precision=4
)
@classmethod
def poll(cls, context):
obj = context.active_object
return obj and obj.type == 'MESH'
def execute(self, context):
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
if muv_common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
# get UV layer
if not bm.loops.layers.uv:
self.report(
{'WARNING'}, "Object must have more than one UV map")
return {'CANCELLED'}
uv_layer = bm.loops.layers.uv.verify()
scale = 1.0 / self.size
sx = 1.0 * scale
sy = 1.0 * scale
sz = 1.0 * scale
ofx = self.offset[0]
ofy = self.offset[1]
ofz = self.offset[2]
rx = self.rotation[0] * pi / 180.0
ry = self.rotation[1] * pi / 180.0
rz = self.rotation[2] * pi / 180.0
aspect = self.tex_aspect
sel_faces = [f for f in bm.faces if f.select]
# update UV coordinate
for f in sel_faces:
n = f.normal
for l in f.loops:
co = l.vert.co
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)
l[uv_layer].uv = Vector((u, v))
bmesh.update_edit_mesh(obj.data)
return {'FINISHED'}
class MUV_UVWBestPlanerMap(bpy.types.Operator):
bl_idname = "uv.muv_uvw_best_planer_map"
bl_label = "Best Planer Map"
bl_options = {'REGISTER', 'UNDO'}
size = FloatProperty(
name="Size",
default=1.0,
precision=4
)
rotation = FloatProperty(
name="XY Rotation",
default=0.0
)
offset = FloatVectorProperty(
name="XY Offset",
size=2,
default=(0.0, 0.0)
)
tex_aspect = FloatProperty(
name="Texture Aspect",
default=1.0,
precision=4
)
@classmethod
def poll(cls, context):
obj = context.active_object
return obj and obj.type == 'MESH'
def execute(self, context):
obj = context.active_object
bm = bmesh.from_edit_mesh(obj.data)
if muv_common.check_version(2, 73, 0) >= 0:
bm.faces.ensure_lookup_table()
# get UV layer
if not bm.loops.layers.uv:
self.report(
{'WARNING'}, "Object must have more than one UV map")
return {'CANCELLED'}
uv_layer = bm.loops.layers.uv.verify()
scale = 1.0 / self.size
sx = 1.0 * scale
sy = 1.0 * scale
ofx = self.offset[0]
ofy = self.offset[1]
rz = self.rotation * pi / 180.0
aspect = self.tex_aspect
sel_faces = [f for f in bm.faces if f.select]
# calculate average of normal
n_ave = Vector((0.0, 0.0, 0.0))
for f in sel_faces:
n_ave = n_ave + f.normal
q = n_ave.rotation_difference(Vector((0.0, 0.0, 1.0)))
# update UV coordinate
for f in sel_faces:
for l in f.loops:
co = q * l.vert.co
x = co.x * sx
y = co.y * sy
u = x * cos(rz) - y * sin(rz) + ofx
v = -x * aspect * sin(rz) - y * aspect * cos(rz) + ofy
l[uv_layer].uv = Vector((u, v))
bmesh.update_edit_mesh(obj.data)
return {'FINISHED'}

View File

@ -20,12 +20,18 @@
__author__ = "McBuff, Nutti <nutti.metro@gmail.com>"
__status__ = "production"
__version__ = "4.4"
__date__ = "2 Aug 2017"
__version__ = "4.5"
__date__ = "19 Nov 2017"
import bpy
import bmesh
from mathutils import Vector
from bpy.props import (
FloatProperty,
BoolProperty,
EnumProperty
)
from . import muv_common
@ -86,6 +92,9 @@ class MUV_WSUVMeasure(bpy.types.Operator):
props.ref_scale = scale / len(sel_faces)
self.report(
{'INFO'}, "Average face size: {0}".format(props.ref_scale))
return {'FINISHED'}
@ -99,6 +108,44 @@ class MUV_WSUVApply(bpy.types.Operator):
bl_description = "Apply scaled UV based on scale calculation"
bl_options = {'REGISTER', 'UNDO'}
proportional_scaling = BoolProperty(
name="Proportional Scaling",
default=True
)
scaling_factor = FloatProperty(
name="Scaling Factor",
default=1.0,
max=1000.0,
min=0.00001
)
origin = EnumProperty(
name="Origin",
description="Aspect Origin",
items=[
('CENTER', 'Center', 'Center'),
('LEFT_TOP', 'Left Top', 'Left Bottom'),
('LEFT_CENTER', 'Left Center', 'Left Center'),
('LEFT_BOTTOM', 'Left Bottom', 'Left Bottom'),
('CENTER_TOP', 'Center Top', 'Center Top'),
('CENTER_BOTTOM', 'Center Bottom', 'Center Bottom'),
('RIGHT_TOP', 'Right Top', 'Right Top'),
('RIGHT_CENTER', 'Right Center', 'Right Center'),
('RIGHT_BOTTOM', 'Right Bottom', 'Right Bottom')
],
default="CENTER"
)
def draw(self, _):
layout = self.layout
row = layout.row()
row.prop(self, "proportional_scaling")
row = layout.row()
row.prop(self, "scaling_factor")
if self.proportional_scaling:
row.enabled = False
def execute(self, context):
props = context.scene.muv_props.wsuv
obj = bpy.context.active_object
@ -122,26 +169,102 @@ class MUV_WSUVApply(bpy.types.Operator):
scale = scale + calc_face_scale(uv_layer, f)
scale = scale / len(sel_faces)
ratio = props.ref_scale / scale
self.report(
{'INFO'}, "Average face size: {0}".format(scale))
orig_area = bpy.context.area.type
bpy.context.area.type = 'IMAGE_EDITOR'
if self.proportional_scaling:
factor = props.ref_scale / scale
else:
factor = self.scaling_factor
# select all UV related to the selected faces
bpy.ops.uv.select_all(action='SELECT')
# calculate origin
if self.origin == 'CENTER':
origin = Vector((0.0, 0.0))
num = 0
for f in sel_faces:
for l in f.loops:
uv = l[uv_layer].uv
origin = origin + uv
num = num + 1
origin = origin / num
elif self.origin == 'LEFT_TOP':
origin = Vector((100000.0, -100000.0))
for f in sel_faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = min(origin.x, uv.x)
origin.y = max(origin.y, uv.y)
elif self.origin == 'LEFT_CENTER':
origin = Vector((100000.0, 0.0))
num = 0
for f in sel_faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = min(origin.x, uv.x)
origin.y = origin.y + uv.y
num = num + 1
origin.y = origin.y / num
elif self.origin == 'LEFT_BOTTOM':
origin = Vector((100000.0, 100000.0))
for f in sel_faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = min(origin.x, uv.x)
origin.y = min(origin.y, uv.y)
elif self.origin == 'CENTER_TOP':
origin = Vector((0.0, -100000.0))
num = 0
for f in sel_faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = origin.x + uv.x
origin.y = max(origin.y, uv.y)
num = num + 1
origin.x = origin.x / num
elif self.origin == 'CENTER_BOTTOM':
origin = Vector((0.0, 100000.0))
num = 0
for f in sel_faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = origin.x + uv.x
origin.y = min(origin.y, uv.y)
num = num + 1
origin.x = origin.x / num
elif self.origin == 'RIGHT_TOP':
origin = Vector((-100000.0, -100000.0))
for f in sel_faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = max(origin.x, uv.x)
origin.y = max(origin.y, uv.y)
elif self.origin == 'RIGHT_CENTER':
origin = Vector((-100000.0, 0.0))
num = 0
for f in sel_faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = max(origin.x, uv.x)
origin.y = origin.y + uv.y
num = num + 1
origin.y = origin.y / num
elif self.origin == 'RIGHT_BOTTOM':
origin = Vector((-100000.0, 100000.0))
for f in sel_faces:
for l in f.loops:
uv = l[uv_layer].uv
origin.x = max(origin.x, uv.x)
origin.y = min(origin.y, uv.y)
# apply scaled UV
bpy.ops.transform.resize(
value=(ratio, ratio, ratio),
constraint_axis=(False, False, False),
constraint_orientation='GLOBAL',
mirror=False,
proportional='DISABLED',
proportional_edit_falloff='SMOOTH',
proportional_size=1)
bpy.context.area.type = orig_area
# update UV coordinate
for f in sel_faces:
for l in f.loops:
uv = l[uv_layer].uv
diff = uv - origin
l[uv_layer].uv = origin + diff * factor
bmesh.update_edit_mesh(obj.data)
self.report({'INFO'}, "Scaling factor: {0}".format(factor))
return {'FINISHED'}