Merge remote-tracking branch 'SavMartin/master'

master
Andrew Cassidy 4 years ago
commit 07e6fe4556
No known key found for this signature in database
GPG Key ID: 963017B38FD477A1

@ -1,6 +1,6 @@
# TexTools for Blender # # TexTools for Blender #
TexTools is a Free addon for Blender 3D with a set of professional UV and Texture toolsBack in 2009 I released the [Original TexTools](http://renderhjs.net/textools/) for 3dsMax. Current features include: Easy Texture Baking, UV Align and Selection tools and Texel Density tools. TexTools is a Free addon for Blender 3D with a set of professional UV and Texture tools. Back in 2009 I released the [Original TexTools](http://renderhjs.net/textools/) for 3dsMax. Current features include: Easy Texture Baking, UV Align and Selection tools and Texel Density tools.
## Download & Documentation ## ## Download & Documentation ##
Visit the [Official Website & Documentation](http://renderhjs.net/textools/blender/) for in depth overview of all the tools. Alternatively visit this [release log](http://renderhjs.net/textools/blender/log.html) Visit the [Official Website & Documentation](http://renderhjs.net/textools/blender/) for in depth overview of all the tools. Alternatively visit this [release log](http://renderhjs.net/textools/blender/log.html)

@ -240,7 +240,7 @@ class UV_OT_op_debug(bpy.types.Operator):
class UV_OT_op_disable_uv_sync(bpy.types.Operator): class UV_OT_op_disable_uv_sync(bpy.types.Operator):
bl_idname = "uv.op_disable_sync" bl_idname = "uv.op_disable_uv_sync"
bl_label = "Disable Sync" bl_label = "Disable Sync"
bl_description = "Disable UV sync mode" bl_description = "Disable UV sync mode"
@ -1458,11 +1458,6 @@ def register():
def unregister():
#GUI Utilities
# utilities_ui.unregister()
from bpy.utils import unregister_class from bpy.utils import unregister_class
for cls in reversed(classes): for cls in reversed(classes):
unregister_class(cls) unregister_class(cls)
@ -1470,7 +1465,6 @@ def unregister():
#Unregister Icons #Unregister Icons
utilities_ui.icon_unregister() utilities_ui.icon_unregister()
#Unregister Settings #Unregister Settings
del bpy.types.Scene.texToolsSettings del bpy.types.Scene.texToolsSettings
@ -1479,6 +1473,9 @@ def unregister():
km.keymap_items.remove(kmi) km.keymap_items.remove(kmi)
keymaps.clear() keymaps.clear()
#GUI Utilities
utilities_ui.unregister()
bpy.types.IMAGE_MT_uvs.remove(menu_IMAGE_uvs) bpy.types.IMAGE_MT_uvs.remove(menu_IMAGE_uvs)
bpy.types.IMAGE_MT_select.remove(menu_IMAGE_select) bpy.types.IMAGE_MT_select.remove(menu_IMAGE_select)
bpy.types.IMAGE_MT_image.remove(menu_IMAGE_MT_image) bpy.types.IMAGE_MT_image.remove(menu_IMAGE_MT_image)
@ -1487,7 +1484,6 @@ def unregister():
bpy.types.VIEW3D_MT_uv_map.remove(menu_VIEW3D_MT_uv_map) bpy.types.VIEW3D_MT_uv_map.remove(menu_VIEW3D_MT_uv_map)
bpy.types.VIEW3D_MT_object_context_menu.remove(menu_VIEW3D_MT_object_context_menu) bpy.types.VIEW3D_MT_object_context_menu.remove(menu_VIEW3D_MT_object_context_menu)
if __name__ == "__main__": if __name__ == "__main__":
register() register()

@ -97,7 +97,7 @@ def sort_objects(self):
for obj_A in pairs_low_high: for obj_A in pairs_low_high:
obj_B = pairs_low_high[obj_A] obj_B = pairs_low_high[obj_A]
try: try:
obj_B.name = utilities_bake.get_bake_name(obj_A)+" high" obj_B.name = utilities_bake.get_set_name(obj_A)+" high"
obj_A.select_set( state = True, view_layer = None) obj_A.select_set( state = True, view_layer = None)
obj_B.select_set( state = True, view_layer = None) obj_B.select_set( state = True, view_layer = None)

@ -126,18 +126,20 @@ def align_island(uv_vert0, uv_vert1, faces):
loop[uv_layers].select = True loop[uv_layers].select = True
diff = uv_vert1 - uv_vert0 diff = uv_vert1 - uv_vert0
angle = math.atan2(diff.y, diff.x) % (math.pi/2) current_angle = math.atan2(diff.x, diff.y)
angle_to_rotate = round(current_angle / (math.pi/2)) * (math.pi/2) - current_angle
# For some reason bpy.ops.transform.rotate rotates in the opposite
# direction in Blender 2.83 than in other versions.
if float(bpy.app.version_string[0:4]) == 2.83:
angle_to_rotate = -angle_to_rotate
bpy.ops.uv.select_linked() bpy.ops.uv.select_linked()
bpy.context.tool_settings.transform_pivot_point = 'CURSOR' bpy.context.tool_settings.transform_pivot_point = 'CURSOR'
bpy.ops.uv.cursor_set(location=uv_vert0 + diff/2) bpy.ops.uv.cursor_set(location=uv_vert0 + diff/2)
if angle >= (math.pi/4): bpy.ops.transform.rotate(value=angle_to_rotate, orient_axis='Z', constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False)
angle = angle - (math.pi/2)
bpy.ops.transform.rotate(value=angle, orient_axis='Z', constraint_axis=(
False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False)
bpy.utils.register_class(op) bpy.utils.register_class(op)

@ -6,17 +6,23 @@ import operator
from mathutils import Vector from mathutils import Vector
from collections import defaultdict from collections import defaultdict
from itertools import chain # 'flattens' collection of iterables from itertools import chain # 'flattens' collection of iterables
from . import utilities_uv from . import utilities_uv
class op(bpy.types.Operator): class op(bpy.types.Operator):
bl_idname = "uv.textools_island_align_world" bl_idname = "uv.textools_island_align_world"
bl_label = "Align World" bl_label = "Align World"
bl_description = "Align selected UV islands to world / gravity directions" bl_description = "Align selected UV islands to world / gravity directions"
bl_options = {'REGISTER', 'UNDO'} bl_options = {'REGISTER', 'UNDO'}
bool_face : bpy.props.BoolProperty(name="Per face", default=False, description="Use if every face is an island in uv space; this speeds up the script dramatically.")
bool_simple : bpy.props.BoolProperty(name="Simple align", default=False, description="Only process one edge per island, enough for nearly undistorted uvs.")
steps : bpy.props.IntProperty(name="Iterations", min=1, max=100, soft_min=1, soft_max=5, default=1, description="Using multiple steps (up to 5, usually 2 or 3) is useful in certain cases, especially uv hulls with high localized distortion.")
# is_global = bpy.props.BoolProperty( # is_global = bpy.props.BoolProperty(
# name = "Global Axis", # name = "Global Axis",
# description = "Global or local object axis alignment", # description = "Global or local object axis alignment",
@ -32,245 +38,243 @@ class op(bpy.types.Operator):
if not bpy.context.active_object: if not bpy.context.active_object:
return False return False
# Only in Edit mode #Only in Edit mode
if bpy.context.active_object.mode != 'EDIT': if bpy.context.active_object.mode != 'EDIT':
return False return False
# Requires UV map #Requires UV map
if not bpy.context.object.data.uv_layers: if not bpy.context.object.data.uv_layers:
return False return False
if bpy.context.scene.tool_settings.use_uv_select_sync: if bpy.context.scene.tool_settings.use_uv_select_sync:
return False return False
# Only in UV editor mode #Only in UV editor mode
if bpy.context.area.type != 'IMAGE_EDITOR': if bpy.context.area.type != 'IMAGE_EDITOR':
return False return False
return True return True
def execute(self, context): def execute(self, context):
main(self) main(self, context)
return {'FINISHED'} return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def main(context):
def main(self, context):
print("\n________________________\nis_global") print("\n________________________\nis_global")
# Store selection #Store selection
utilities_uv.selection_store() utilities_uv.selection_store()
bm = bmesh.from_edit_mesh(bpy.context.active_object.data) bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
uv_layers = bm.loops.layers.uv.verify() uv_layers = bm.loops.layers.uv.verify()
# Only in Face or Island mode #Only in Face or Island mode
if bpy.context.scene.tool_settings.uv_select_mode is not 'FACE' or 'ISLAND': if bpy.context.scene.tool_settings.uv_select_mode is not 'FACE' or 'ISLAND':
bpy.context.scene.tool_settings.uv_select_mode = 'FACE' bpy.context.scene.tool_settings.uv_select_mode = 'FACE'
obj = bpy.context.object obj = bpy.context.object
bm = bmesh.from_edit_mesh(bpy.context.active_object.data) bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
uv_layers = bm.loops.layers.uv.verify() uv_layers = bm.loops.layers.uv.verify();
islands = utilities_uv.getSelectionIslands() if self.bool_face:
islands = [[f] for f in bm.faces if f.select and f.loops[0][uv_layers].select]
else:
islands = utilities_uv.getSelectionIslands()
for faces in islands: for faces in islands:
# Get average viewport normal of UV island avg_normal = Vector((0,0,0))
avg_normal = Vector((0, 0, 0)) if self.bool_face:
for face in faces: avg_normal = faces[0].normal
avg_normal += face.normal else:
avg_normal /= len(faces) # Get average viewport normal of UV island
for face in faces:
# avg_normal = (obj.matrix_world*avg_normal).normalized() avg_normal+=face.normal
avg_normal/=len(faces)
# Which Side # Which Side
x = 0 x = 0
y = 1 y = 1
z = 2 z = 2
max_size = max(abs(avg_normal.x), abs(avg_normal.y), abs(avg_normal.z)) max_size = max(abs(avg_normal.x), abs(avg_normal.y), abs(avg_normal.z))
# Use multiple steps for i in range(self.steps): # Use multiple steps
for i in range(3):
if(abs(avg_normal.x) == max_size): if(abs(avg_normal.x) == max_size):
print("x normal") print("x normal")
align_island(obj, bm, uv_layers, faces, y, if self.bool_simple:
z, avg_normal.x < 0, False) align_island_simple(obj, bm, uv_layers, faces, y, z, avg_normal.x < 0, False)
else:
align_island(obj, bm, uv_layers, faces, y, z, avg_normal.x < 0, False)
elif(abs(avg_normal.y) == max_size): elif(abs(avg_normal.y) == max_size):
print("y normal") print("y normal")
align_island(obj, bm, uv_layers, faces, x, if self.bool_simple:
z, avg_normal.y > 0, False) align_island_simple(obj, bm, uv_layers, faces, x, z, avg_normal.y > 0, False)
else:
align_island(obj, bm, uv_layers, faces, x, z, avg_normal.y > 0, False)
elif(abs(avg_normal.z) == max_size): elif(abs(avg_normal.z) == max_size):
print("z normal") print("z normal")
align_island(obj, bm, uv_layers, faces, x, if self.bool_simple:
y, False, avg_normal.z < 0) align_island_simple(obj, bm, uv_layers, faces, x, y, False, avg_normal.z < 0)
else:
print("align island: faces {}x n:{}, max:{}".format( align_island(obj, bm, uv_layers, faces, x, y, False, avg_normal.z < 0)
len(faces), avg_normal, max_size))
print("align island: faces {}x n:{}, max:{}".format(len(faces), avg_normal, max_size))
# Restore selection
#Restore selection
utilities_uv.selection_restore() utilities_uv.selection_restore()
def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False): def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False):
# Find lowest and highest verts # Find lowest and highest verts
minmax_val = [0, 0] minmax_val = [0,0]
minmax_vert = [None, None] minmax_vert = [None, None]
axis_names = ['x', 'y', 'z'] axis_names = ['x', 'y', 'z']
print("Align shell {}x at {},{} flip {},{}".format( print("Align shell {}x at {},{} flip {},{}".format(len(faces), axis_names[x], axis_names[y], flip_x, flip_y))
len(faces), axis_names[x], axis_names[y], flip_x, flip_y))
# print(" Min #{} , Max #{} along '{}'".format(minmax_vert[0].index, minmax_vert[1].index, axis_names[y] ))
# print(" A1 {:.1f} , A2 {:.1f} along ".format(minmax_val[0], minmax_val[1] ))
# print(" Min #{} , Max #{} along '{}'".format(minmax_vert[0].index, minmax_vert[1].index, axis_names[y] ))
# print(" A1 {:.1f} , A2 {:.1f} along ".format(minmax_val[0], minmax_val[1] ))
# Collect UV to Vert # Collect UV to Vert
vert_to_uv = utilities_uv.get_vert_to_uv(bm, uv_layers) vert_to_uv = {}
uv_to_vert = utilities_uv.get_uv_to_vert(bm, uv_layers) for face in faces:
for loop in face.loops:
vert = loop.vert
uv = loop[uv_layers]
if vert not in vert_to_uv:
vert_to_uv[vert] = [uv];
else:
vert_to_uv[vert].append(uv)
#uv_to_vert = utilities_uv.get_uv_to_vert(bm, uv_layers)
processed_edges = [] processed_edges = []
edges = [] n_edges = 0
avg_angle = 0
for face in faces: for face in faces:
for edge in face.edges: for edge in face.edges:
if edge not in processed_edges: if edge not in processed_edges:
processed_edges.append(edge) processed_edges.append(edge)
delta = edge.verts[0].co - edge.verts[1].co delta = edge.verts[0].co -edge.verts[1].co
max_side = max(abs(delta.x), abs(delta.y), abs(delta.z)) max_side = max(abs(delta.x), abs(delta.y), abs(delta.z))
# Check edges dominant in active axis # Check edges dominant in active axis
if(abs(delta[x]) == max_side or abs(delta[y]) == max_side): if( abs(delta[x]) == max_side or abs(delta[y]) == max_side):
# if( abs(delta[y]) == max_side): n_edges += 1
edges.append(edge) uv0 = vert_to_uv[ edge.verts[0] ][0]
uv1 = vert_to_uv[ edge.verts[1] ][0]
delta_verts = Vector((
edge.verts[1].co[x] - edge.verts[0].co[x],
edge.verts[1].co[y] - edge.verts[0].co[y]
))
if flip_x:
delta_verts.x = -edge.verts[1].co[x] + edge.verts[0].co[x]
if flip_y:
delta_verts.y = -edge.verts[1].co[y] + edge.verts[0].co[y]
delta_uvs = Vector((
uv1.uv.x - uv0.uv.x,
uv1.uv.y - uv0.uv.y
))
a0 = math.atan2(delta_verts.y, delta_verts.x) #- math.pi/2
a1 = math.atan2(delta_uvs.y, delta_uvs.x) #- math.pi/2
a_delta = math.atan2(math.sin(a0-a1), math.cos(a0-a1))
# Consolidation (math.atan2 gives the lower angle between -Pi and Pi, this triggers errors when using the average avg_angle /= n_edges for rotation angles close to Pi)
if n_edges > 1:
if abs((avg_angle / (n_edges-1)) - a_delta) > 2.8:
if a_delta > 0:
avg_angle+=(a_delta-math.pi*2)
else:
avg_angle+=(a_delta+math.pi*2)
else:
avg_angle+=a_delta
else:
avg_angle+=a_delta
avg_angle /= n_edges
# For some reason, bpy.ops.transform.rotate rotates in the opposite direction in Blender 2.83 compared to other versions.
if float(bpy.app.version_string[0:4]) == 2.83:
avg_angle = -avg_angle
print("Edges {}x".format(n_edges))
print("Turn {:.1f}".format(avg_angle * 180/math.pi))
bpy.ops.uv.select_all(action='DESELECT')
for face in faces:
for loop in face.loops:
loop[uv_layers].select = True
bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT'
bpy.ops.transform.rotate(value=-avg_angle, orient_axis='Z', constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False)
print("Edges {}x".format(len(edges)))
avg_angle = 0 def align_island_simple(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False):
for edge in edges:
uv0 = vert_to_uv[edge.verts[0]][0] # Find lowest and highest verts
uv1 = vert_to_uv[edge.verts[1]][0] minmax_val = [0,0]
minmax_vert = [None, None]
axis_names = ['x', 'y', 'z']
print("Align shell {}x at {},{} flip {},{}".format(len(faces), axis_names[x], axis_names[y], flip_x, flip_y))
# Collect UV to Vert
vert_to_uv = {}
face = faces[0]
for loop in face.loops:
vert = loop.vert
uv = loop[uv_layers]
vert_to_uv[vert] = [uv]
uv.select = True
edge = faces[0].edges[0]
delta = edge.verts[0].co -edge.verts[1].co
max_side = max(abs(delta.x), abs(delta.y), abs(delta.z))
a_delta = 0
# Check edges dominant in active axis
if abs(delta[x]) == max_side or abs(delta[y]) == max_side :
uv0 = vert_to_uv[ edge.verts[0] ][0]
uv1 = vert_to_uv[ edge.verts[1] ][0]
delta_verts = Vector(( delta_verts = Vector((
edge.verts[1].co[x] - edge.verts[0].co[x], edge.verts[1].co[x] - edge.verts[0].co[x],
edge.verts[1].co[y] - edge.verts[0].co[y] edge.verts[1].co[y] - edge.verts[0].co[y]
)) ))
if flip_x: if flip_x:
delta_verts.x = -edge.verts[1].co[x] + edge.verts[0].co[x] delta_verts.x = -edge.verts[1].co[x] + edge.verts[0].co[x]
if flip_y: if flip_y:
delta_verts.y = -edge.verts[1].co[y] + edge.verts[0].co[y] delta_verts.y = -edge.verts[1].co[y] + edge.verts[0].co[y]
# delta_verts.y = edge.verts[0].co[y] - edge.verts[1].co[y]
delta_uvs = Vector(( delta_uvs = Vector((
uv1.uv.x - uv0.uv.x, uv1.uv.x - uv0.uv.x,
uv1.uv.y - uv0.uv.y uv1.uv.y - uv0.uv.y
)) ))
a0 = math.atan2(delta_verts.y, delta_verts.x) - math.pi/2
a1 = math.atan2(delta_uvs.y, delta_uvs.x) - math.pi/2
a0 = math.atan2(delta_verts.y, delta_verts.x)
a1 = math.atan2(delta_uvs.y, delta_uvs.x)
a_delta = math.atan2(math.sin(a0-a1), math.cos(a0-a1)) a_delta = math.atan2(math.sin(a0-a1), math.cos(a0-a1))
# edge.verts[0].index, edge.verts[1].index
# print(" turn {:.1f} .. {:.1f} , {:.1f}".format(a_delta*180/math.pi, a0*180/math.pi,a1*180/math.pi))
avg_angle += a_delta
avg_angle /= len(edges) # - math.pi/2 # For some reason, bpy.ops.transform.rotate rotates in the opposite direction in Blender 2.83 compared to other versions.
print("Turn {:.1f}".format(avg_angle * 180/math.pi)) if float(bpy.app.version_string[0:4]) == 2.83:
a_delta = -a_delta
print("Turn {:.1f}".format(a_delta * 180/math.pi))
bpy.ops.uv.select_all(action='DESELECT') bpy.ops.uv.select_all(action='DESELECT')
for face in faces: for face in faces:
for loop in face.loops: for loop in face.loops:
loop[uv_layers].select = True loop[uv_layers].select = True
bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT' bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT'
bpy.ops.transform.rotate(value=avg_angle, orient_axis='Z') bpy.ops.transform.rotate(value=-a_delta, orient_axis='Z', constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False)
# bpy.ops.transform.rotate(value=0.58191, axis=(-0, -0, -1), constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False, proportional_edit_falloff='SPHERE', proportional_size=0.0267348)
# processed = []
'''
bpy.ops.uv.select_all(action='DESELECT')
for face in faces:
# Collect UV to Vert
for loop in face.loops:
loop[uv_layers].select = True
vert = loop.vert
uv = loop[uv_layers]
# vert_to_uv
if vert not in vert_to_uv:
vert_to_uv[vert] = [uv];
else:
vert_to_uv[vert].append(uv)
# uv_to_vert
if uv not in uv_to_vert:
uv_to_vert[ uv ] = vert;
for vert in face.verts:
if vert not in processed:
processed.append(vert)
vert_y = (vert.co)[y] #obj.matrix_world *
print("idx {} = {}".format(vert.index, vert_y))
if not minmax_vert[0] or not minmax_vert[1]:
minmax_vert[0] = vert
minmax_vert[1] = vert
minmax_val[0] = vert_y
minmax_val[1] = vert_y
continue
if vert_y < minmax_val[0]:
# Not yet defined or smaller
minmax_vert[0] = vert
minmax_val[0] = vert_y
elif vert_y > minmax_val[1]:
minmax_vert[1] = vert
minmax_val[1] = vert_y
if minmax_vert[0] and minmax_vert[1]:
axis_names = ['x', 'y', 'z']
print(" Min #{} , Max #{} along '{}'".format(minmax_vert[0].index, minmax_vert[1].index, axis_names[y] ))
# print(" A1 {:.1f} , A2 {:.1f} along ".format(minmax_val[0], minmax_val[1] ))
vert_A = minmax_vert[0]
vert_B = minmax_vert[1]
uv_A = vert_to_uv[vert_A][0]
uv_B = vert_to_uv[vert_B][0]
delta_verts = Vector((
vert_B.co[x] - vert_A.co[x],
vert_B.co[y] - vert_A.co[y]
))
delta_uvs = Vector((
uv_B.uv.x - uv_A.uv.x,
uv_B.uv.y - uv_A.uv.y,
))
# Get angles
angle_vert = math.atan2(delta_verts.y, delta_verts.x) - math.pi/2
angle_uv = math.atan2(delta_uvs.y, delta_uvs.x) - math.pi/2
angle_delta = math.atan2(math.sin(angle_vert-angle_uv), math.cos(angle_vert-angle_uv))
print(" Angles {:.2f} | {:.2f}".format(angle_vert*180/math.pi, angle_uv*180/math.pi))
print(" Angle Diff {:.2f}".format(angle_delta*180/math.pi))
bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT'
bpy.ops.transform.rotate(value=angle_delta, axis='Z')
# bpy.ops.transform.rotate(value=0.58191, axis=(-0, -0, -1), constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False, proportional_edit_falloff='SPHERE', proportional_size=0.0267348)
# bpy.ops.mesh.select_all(action='DESELECT')
# vert_A.select = True
# vert_B.select = True
# return
'''
bpy.utils.register_class(op) bpy.utils.register_class(op)

@ -4,6 +4,7 @@ import operator
from mathutils import Vector from mathutils import Vector
from collections import defaultdict from collections import defaultdict
from math import pi from math import pi
import sys
import time import time
from math import radians, hypot from math import radians, hypot
@ -37,6 +38,11 @@ class op(bpy.types.Operator):
rectify(self, context) rectify(self, context)
return {'FINISHED'} return {'FINISHED'}
def time_clock():
if sys.version_info >= (3, 3):
return time.process_time()
else:
return time.clock()
precision = 3 precision = 3
@ -58,7 +64,7 @@ def rectify(self, context):
def main(square=False, snapToClosest=False): def main(square=False, snapToClosest=False):
startTime = time.clock() startTime = time_clock()
obj = bpy.context.active_object obj = bpy.context.active_object
me = obj.data me = obj.data
bm = bmesh.from_edit_mesh(me) bm = bmesh.from_edit_mesh(me)
@ -303,7 +309,7 @@ def SuccessFinished(me, startTime):
# use for backtrack of steps # use for backtrack of steps
# bpy.ops.ed.undo_push() # bpy.ops.ed.undo_push()
bmesh.update_edit_mesh(me) bmesh.update_edit_mesh(me)
#elapsed = round(time.clock()-startTime, 2) #elapsed = round(time_clock()-startTime, 2)
#if (elapsed >= 0.05): operator.report({'INFO'}, "UvSquares finished, elapsed:", elapsed, "s.") #if (elapsed >= 0.05): operator.report({'INFO'}, "UvSquares finished, elapsed:", elapsed, "s.")
return return

@ -10,7 +10,7 @@ from . import utilities_ui
class op(bpy.types.Operator): class op(bpy.types.Operator):
bl_idname = "uv.textools_select_islands_outline" bl_idname = "uv.textools_select_islands_outline"
bl_label = "Select Overlap" bl_label = "Select Island outline"
bl_description = "Select island edge bounds" bl_description = "Select island edge bounds"
bl_options = {'REGISTER', 'UNDO'} bl_options = {'REGISTER', 'UNDO'}
@ -24,7 +24,11 @@ class op(bpy.types.Operator):
#Requires UV map #Requires UV map
if not bpy.context.object.data.uv_layers: if not bpy.context.object.data.uv_layers:
return False return False
# #requires UV_sync
# if not bpy.context.scene.tool_settings.use_uv_select_sync:
# return False
return True return True
@ -40,12 +44,23 @@ def select_outline(context):
if bpy.context.active_object.mode != 'EDIT': if bpy.context.active_object.mode != 'EDIT':
bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='EDIT')
bpy.context.scene.tool_settings.use_uv_select_sync = False
bm = bmesh.from_edit_mesh(bpy.context.active_object.data); bm = bmesh.from_edit_mesh(bpy.context.active_object.data);
uv_layers = bm.loops.layers.uv.verify(); uv_layers = bm.loops.layers.uv.verify();
pre_sync = bpy.context.scene.tool_settings.use_uv_select_sync
if bpy.context.scene.tool_settings.use_uv_select_sync:
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
bpy.ops.uv.select_linked()
bpy.context.scene.tool_settings.use_uv_select_sync = False
bpy.ops.uv.select_all(action='SELECT')
else:
current_edit = tuple(bpy.context.tool_settings.mesh_select_mode)
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE')
current_select = [f for f in bm.faces if f.select]
islands = utilities_uv.getSelectionIslands()
faces_islands = [face for island in islands for face in island]
edges_islands = [edge for island in islands for face in island for edge in face.edges]
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE') bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.mesh.select_all(action='DESELECT')
@ -60,19 +75,40 @@ def select_outline(context):
# Create seams from islands # Create seams from islands
bpy.ops.uv.seams_from_islands(contextViewUV) bpy.ops.uv.seams_from_islands(contextViewUV)
edges_islands = [edge for edge in bm.edges if edge.seam] edges_seams_from_islands = [edge for edge in bm.edges if edge.seam]
# Clear seams # Clear seams
for edge in edges_islands: for edge in edges_seams_from_islands:
edge.seam = False edge.seam = False
# Select island edges if pre_sync:
bpy.ops.mesh.select_all(action='DESELECT') # Select seams from islands edges and edge boundaries
for edge in edges_islands: for edge in edges_islands:
edge.select = True if edge.is_boundary or edge in edges_seams_from_islands:
edge.select = True
else:
for face in current_select:
face.select = True
bpy.context.tool_settings.mesh_select_mode = current_edit
bpy.ops.uv.select_all(action='DESELECT')
edges = []
for edge in edges_islands:
if edge.is_boundary or edge in edges_seams_from_islands:
edges.extend([e for e in edge.verts[0].link_loops])
edges.extend([e for e in edge.verts[1].link_loops])
#edges.append(edge)
bpy.context.scene.tool_settings.uv_select_mode = 'EDGE'
for face in faces_islands:
for loop in face.loops:
if loop in edges:
loop[uv_layers].select = True
# Restore seam selection # Restore seam selection
for edge in edges_seam: for edge in edges_seam:
edge.seam = True edge.seam = True
bpy.context.scene.tool_settings.use_uv_select_sync = pre_sync
bpy.utils.register_class(op) bpy.utils.register_class(op)

@ -110,8 +110,8 @@ class op_popup(bpy.types.Operator):
def invoke(self, context, event): def invoke(self, context, event):
wm = context.window_manager wm = context.window_manager
return wm.invoke_popup(self, width=200, height=200) return wm.invoke_popup(self, width=200)
def draw(self, context): def draw(self, context):
self.layout.label(text=self.message) self.layout.label(text=self.message)
@ -155,15 +155,17 @@ def unregister():
from bpy.types import WindowManager from bpy.types import WindowManager
for preview_collection in preview_collections.values(): for preview_collection in preview_collections.values():
bpy.utils.previews.remove(preview_collection) bpy.utils.previews.remove(preview_collection)
preview_collections.clear() preview_collection.clear()
# Unregister icons # Unregister icons
# global preview_icons # global preview_icons
bpy.utils.previews.remove(preview_icons) # bpy.utils.previews.remove(preview_icons)
preview_icons.clear()
del bpy.types.Scene.TT_bake_mode
del bpy.types.Scene.TT_bake_mode
if __name__ == "__main__": if __name__ == "__main__":
register() register()
bpy.utils.register_class(op_popup) bpy.utils.register_class(op_popup)

Loading…
Cancel
Save