From f44af5d0c297abc483b7fa4325998cf4b79b5568 Mon Sep 17 00:00:00 2001 From: SavMartin Date: Mon, 13 Jan 2020 21:43:18 +0000 Subject: [PATCH 01/17] Fix get_set_name (in Organize) https://github.com/SavMartin/TexTools-Blender/issues/9 Thx to CAPA14 --- op_bake_organize_names.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op_bake_organize_names.py b/op_bake_organize_names.py index 0faf688..cad63e3 100644 --- a/op_bake_organize_names.py +++ b/op_bake_organize_names.py @@ -97,7 +97,7 @@ def sort_objects(self): for obj_A in pairs_low_high: obj_B = pairs_low_high[obj_A] 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_B.select_set( state = True, view_layer = None) From f94b531f6c36a829105a4c266f36527e94b1bb11 Mon Sep 17 00:00:00 2001 From: franMarz <58062362+franMarz@users.noreply.github.com> Date: Sun, 17 May 2020 02:18:27 +0200 Subject: [PATCH 02/17] Add files via upload --- op_island_align_world.py | 219 +++++++++++---------------------------- 1 file changed, 61 insertions(+), 158 deletions(-) diff --git a/op_island_align_world.py b/op_island_align_world.py index a552d4d..463af23 100644 --- a/op_island_align_world.py +++ b/op_island_align_world.py @@ -57,7 +57,6 @@ class op(bpy.types.Operator): return {'FINISHED'} - def main(context): print("\n________________________\nis_global") @@ -76,9 +75,7 @@ def main(context): uv_layers = bm.loops.layers.uv.verify(); islands = utilities_uv.getSelectionIslands() - - for faces in islands: # Get average viewport normal of UV island avg_normal = Vector((0,0,0)) @@ -94,29 +91,23 @@ def main(context): z = 2 max_size = max(abs(avg_normal.x), abs(avg_normal.y), abs(avg_normal.z)) - # Use multiple steps - for i in range(3): - if(abs(avg_normal.x) == max_size): - print("x normal") - align_island(obj, bm, uv_layers, faces, y, z, avg_normal.x < 0, False) - - elif(abs(avg_normal.y) == max_size): - print("y normal") - align_island(obj, bm, uv_layers, faces, x, z, avg_normal.y > 0, False) - - elif(abs(avg_normal.z) == max_size): - print("z normal") - align_island(obj, bm, uv_layers, faces, x, y, False, avg_normal.z < 0) + #for i in range(3): # Use multiple steps #REMOVED: maybe should be reimplemented as a custom property + if(abs(avg_normal.x) == max_size): + print("x normal") + align_island(obj, bm, uv_layers, faces, y, z, avg_normal.x < 0, False) + elif(abs(avg_normal.y) == max_size): + print("y normal") + align_island(obj, bm, uv_layers, faces, x, z, avg_normal.y > 0, False) + elif(abs(avg_normal.z) == max_size): + print("z normal") + align_island(obj, bm, uv_layers, faces, x, y, False, avg_normal.z < 0) print("align island: faces {}x n:{}, max:{}".format(len(faces), avg_normal, max_size)) - - #Restore selection utilities_uv.selection_restore() - def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False): # Find lowest and highest verts @@ -126,67 +117,69 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False axis_names = ['x', 'y', 'z'] print("Align shell {}x at {},{} flip {},{}".format(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] )) - - - + # Collect UV to Vert - vert_to_uv = utilities_uv.get_vert_to_uv(bm, uv_layers) - uv_to_vert = utilities_uv.get_uv_to_vert(bm, uv_layers) - + vert_to_uv = {} + 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 = [] - edges = [] + n_edges = 0 + avg_angle = 0 for face in faces: for edge in face.edges: if edge not in processed_edges: processed_edges.append(edge) delta = edge.verts[0].co -edge.verts[1].co max_side = max(abs(delta.x), abs(delta.y), abs(delta.z)) - # Check edges dominant in active axis if( abs(delta[x]) == max_side or abs(delta[y]) == max_side): - # if( abs(delta[y]) == max_side): - edges.append(edge) - - print("Edges {}x".format(len(edges))) - - avg_angle = 0 - for edge in edges: - 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_verts.y = edge.verts[0].co[y] - edge.verts[1].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)) - # 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 - + n_edges += 1 + 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 + )) - avg_angle/=len(edges) # - math.pi/2 + 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 + + print("Edges {}x".format(n_edges)) print("Turn {:.1f}".format(avg_angle * 180/math.pi)) bpy.ops.uv.select_all(action='DESELECT') @@ -194,98 +187,8 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False 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') - # 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 + bpy.ops.transform.rotate(value=-avg_angle, orient_axis='Z') # minus angle; Blender uses unconventional rotation notation (positive for clockwise) - # return - ''' bpy.utils.register_class(op) From c579ca9202d57c4b8c63fefc05429041c5793711 Mon Sep 17 00:00:00 2001 From: franMarz <58062362+franMarz@users.noreply.github.com> Date: Mon, 18 May 2020 00:13:53 +0200 Subject: [PATCH 03/17] Align World fix 2 Added options for speeding up the script. --- op_island_align_world.py | 117 ++++++++++++++++++++++++++++++++------- 1 file changed, 97 insertions(+), 20 deletions(-) diff --git a/op_island_align_world.py b/op_island_align_world.py index 463af23..007fb0c 100644 --- a/op_island_align_world.py +++ b/op_island_align_world.py @@ -19,6 +19,10 @@ class op(bpy.types.Operator): bl_description = "Align selected UV islands to world / gravity directions" 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( # name = "Global Axis", # description = "Global or local object axis alignment", @@ -51,13 +55,16 @@ class op(bpy.types.Operator): return True - def execute(self, context): - main(self) + main(self, context) 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") #Store selection @@ -74,16 +81,20 @@ def main(context): bm = bmesh.from_edit_mesh(bpy.context.active_object.data); 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: - # Get average viewport normal of UV island avg_normal = Vector((0,0,0)) - for face in faces: - avg_normal+=face.normal - avg_normal/=len(faces) - - # avg_normal = (obj.matrix_world*avg_normal).normalized() + if self.bool_face: + avg_normal = faces[0].normal + else: + # Get average viewport normal of UV island + for face in faces: + avg_normal+=face.normal + avg_normal/=len(faces) # Which Side x = 0 @@ -91,16 +102,25 @@ def main(context): z = 2 max_size = max(abs(avg_normal.x), abs(avg_normal.y), abs(avg_normal.z)) - #for i in range(3): # Use multiple steps #REMOVED: maybe should be reimplemented as a custom property - if(abs(avg_normal.x) == max_size): - print("x normal") - align_island(obj, bm, uv_layers, faces, y, z, avg_normal.x < 0, False) - elif(abs(avg_normal.y) == max_size): - print("y normal") - align_island(obj, bm, uv_layers, faces, x, z, avg_normal.y > 0, False) - elif(abs(avg_normal.z) == max_size): - print("z normal") - align_island(obj, bm, uv_layers, faces, x, y, False, avg_normal.z < 0) + for i in range(self.steps): # Use multiple steps + if(abs(avg_normal.x) == max_size): + print("x normal") + if self.bool_simple: + 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): + print("y normal") + if self.bool_simple: + 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): + print("z normal") + if self.bool_simple: + align_island_simple(obj, bm, uv_layers, faces, x, z, avg_normal.y > 0, False) + else: + align_island(obj, bm, uv_layers, faces, x, y, False, avg_normal.z < 0) print("align island: faces {}x n:{}, max:{}".format(len(faces), avg_normal, max_size)) @@ -191,4 +211,61 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False bpy.ops.transform.rotate(value=-avg_angle, orient_axis='Z') # minus angle; Blender uses unconventional rotation notation (positive for clockwise) +def align_island_simple(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False): + + # Find lowest and highest verts + 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)) + + # 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(( + 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) + a1 = math.atan2(delta_uvs.y, delta_uvs.x) + + a_delta = math.atan2(math.sin(a0-a1), math.cos(a0-a1)) + + print("Turn {:.1f}".format(a_delta * 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=-a_delta, orient_axis='Z') # minus angle; Blender uses unconventional rotation notation (positive for clockwise) + + bpy.utils.register_class(op) From 00efc2c759201af2f27a2ac6e2ec92f7170db7ed Mon Sep 17 00:00:00 2001 From: SavMartin Date: Sun, 24 May 2020 05:42:43 +0100 Subject: [PATCH 04/17] fix --- op_island_align_world.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/op_island_align_world.py b/op_island_align_world.py index 007fb0c..5ad4dd8 100644 --- a/op_island_align_world.py +++ b/op_island_align_world.py @@ -19,9 +19,9 @@ class op(bpy.types.Operator): bl_description = "Align selected UV islands to world / gravity directions" 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.") + 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( # name = "Global Axis", From b227c34d70a23bb9760188a63b503c339d55789b Mon Sep 17 00:00:00 2001 From: SavMartin Date: Wed, 27 May 2020 21:47:15 +0100 Subject: [PATCH 05/17] Fix op_disable_uv_sync Thx to franMarz --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 4d89e03..eddc933 100644 --- a/__init__.py +++ b/__init__.py @@ -240,7 +240,7 @@ class UV_OT_op_debug(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_description = "Disable UV sync mode" From bcb546cafc341fbda07c33f071d29a73fc6fb150 Mon Sep 17 00:00:00 2001 From: Antti Tikka Date: Thu, 18 Jun 2020 16:42:29 +0300 Subject: [PATCH 06/17] Fix Align Island by Edge giving wrong result --- op_island_align_edge.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/op_island_align_edge.py b/op_island_align_edge.py index 13eeadc..b15eee7 100644 --- a/op_island_align_edge.py +++ b/op_island_align_edge.py @@ -126,17 +126,15 @@ def align_island(uv_vert0, uv_vert1, faces): loop[uv_layers].select = True diff = uv_vert1 - uv_vert0 - angle = math.atan2(diff.y, diff.x)%(math.pi/2) + current_angle = math.atan2(diff.y, diff.x) + angle_to_rotate = round(current_angle / (math.pi/2)) * (math.pi/2) - current_angle bpy.ops.uv.select_linked() bpy.context.tool_settings.transform_pivot_point = 'CURSOR' bpy.ops.uv.cursor_set(location=uv_vert0 + diff/2) - if angle >= (math.pi/4): - 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.ops.transform.rotate(value=angle_to_rotate, orient_axis='Z', constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False) bpy.utils.register_class(op) From 97fe5f56f29a6f0196780e7d14fb8dda38a76b51 Mon Sep 17 00:00:00 2001 From: franMarz <58062362+franMarz@users.noreply.github.com> Date: Mon, 22 Jun 2020 22:05:39 +0200 Subject: [PATCH 07/17] Add files via upload --- op_island_align_world.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/op_island_align_world.py b/op_island_align_world.py index 5ad4dd8..b9047f6 100644 --- a/op_island_align_world.py +++ b/op_island_align_world.py @@ -118,7 +118,7 @@ def main(self, context): elif(abs(avg_normal.z) == max_size): print("z normal") if self.bool_simple: - align_island_simple(obj, bm, uv_layers, faces, x, z, avg_normal.y > 0, False) + align_island_simple(obj, bm, uv_layers, faces, x, y, False, avg_normal.z < 0) else: align_island(obj, bm, uv_layers, faces, x, y, False, avg_normal.z < 0) @@ -232,6 +232,7 @@ def align_island_simple(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_ 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 : From 65a35c8e4b21fd9ffad63dbd9dd3d206edffc919 Mon Sep 17 00:00:00 2001 From: nagadomi Date: Wed, 1 Jul 2020 11:12:06 +0900 Subject: [PATCH 08/17] Fix time.clock() error in Python 3.8 --- op_rectify.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/op_rectify.py b/op_rectify.py index e5fc90a..d1b3348 100644 --- a/op_rectify.py +++ b/op_rectify.py @@ -4,6 +4,7 @@ import operator from mathutils import Vector from collections import defaultdict from math import pi +import sys import time from math import radians, hypot @@ -38,7 +39,11 @@ class op(bpy.types.Operator): rectify(self, context) return {'FINISHED'} - +def time_clock(): + if sys.version_info >= (3, 3): + return time.process_time() + else: + return time.clock() precision = 3 @@ -61,7 +66,7 @@ def rectify(self, context): def main(square = False, snapToClosest = False): - startTime = time.clock() + startTime = time_clock() obj = bpy.context.active_object me = obj.data bm = bmesh.from_edit_mesh(me) @@ -310,7 +315,7 @@ def SuccessFinished(me, startTime): #use for backtrack of steps #bpy.ops.ed.undo_push() 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.") return @@ -634,4 +639,4 @@ def hypotVert(v1, v2): hyp = hypot(v1.x - v2.x, v1.y - v2.y) return hyp -bpy.utils.register_class(op) \ No newline at end of file +bpy.utils.register_class(op) From c9b684ae0b94c369b282e961b84170c1809fb83c Mon Sep 17 00:00:00 2001 From: franMarz <58062362+franMarz@users.noreply.github.com> Date: Wed, 15 Jul 2020 16:09:27 +0200 Subject: [PATCH 09/17] popup update --- utilities_ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities_ui.py b/utilities_ui.py index 690274b..087abae 100644 --- a/utilities_ui.py +++ b/utilities_ui.py @@ -107,7 +107,7 @@ class op_popup(bpy.types.Operator): def invoke(self, context, event): wm = context.window_manager - return wm.invoke_popup(self, width=200, height=200) + return wm.invoke_popup(self, width=200) def draw(self, context): self.layout.label(text=self.message) From 9ab565dc1a3ad48837ae69b3ba136de619019a44 Mon Sep 17 00:00:00 2001 From: franMarz <58062362+franMarz@users.noreply.github.com> Date: Sun, 9 Aug 2020 18:28:50 +0200 Subject: [PATCH 10/17] previews fix --- __init__.py | 15 +++++++-------- utilities_ui.py | 5 +++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/__init__.py b/__init__.py index eddc933..9a4517b 100644 --- a/__init__.py +++ b/__init__.py @@ -1279,7 +1279,7 @@ def icon_get(name): -def menu_IMAGE_uvs(self, context): +'''def menu_IMAGE_uvs(self, context): layout = self.layout layout.separator() layout.operator(op_uv_resize.op.bl_idname, text="Resize", icon_value = icon_get("op_extend_canvas_open")) @@ -1295,7 +1295,7 @@ def menu_IMAGE_uvs(self, context): layout.operator(op_island_align_edge.op.bl_idname, text="Align Edge", icon_value = icon_get("op_island_align_edge")) layout.operator(op_island_align_world.op.bl_idname, text="Align World", icon_value = icon_get("op_island_align_world")) - layout.menu(VIEW3D_MT_submenu_align) + layout.menu(VIEW3D_MT_submenu_align)''' class VIEW3D_MT_submenu_align(bpy.types.Menu): bl_label="Align" @@ -1435,7 +1435,7 @@ def register(): for icon in icons: utilities_ui.icon_register(icon) - bpy.types.IMAGE_MT_uvs.append(menu_IMAGE_uvs) + #bpy.types.IMAGE_MT_uvs.append(menu_IMAGE_uvs) bpy.types.IMAGE_MT_select.append(menu_IMAGE_select) bpy.types.IMAGE_MT_image.append(menu_IMAGE_MT_image) bpy.types.VIEW3D_MT_object.append(menu_VIEW3D_MT_object) @@ -1447,14 +1447,10 @@ def register(): def unregister(): - #GUI Utilities - # utilities_ui.unregister() - from bpy.utils import unregister_class for cls in reversed(classes): unregister_class(cls) - #Unregister Settings del bpy.types.Scene.texToolsSettings @@ -1463,7 +1459,10 @@ def unregister(): km.keymap_items.remove(kmi) keymaps.clear() - bpy.types.IMAGE_MT_uvs.remove(menu_IMAGE_uvs) + #GUI Utilities + utilities_ui.unregister() + + #bpy.types.IMAGE_MT_uvs.remove(menu_IMAGE_uvs) bpy.types.IMAGE_MT_select.remove(menu_IMAGE_select) bpy.types.IMAGE_MT_image.remove(menu_IMAGE_MT_image) bpy.types.VIEW3D_MT_object.remove(menu_VIEW3D_MT_object) diff --git a/utilities_ui.py b/utilities_ui.py index 087abae..02e2f37 100644 --- a/utilities_ui.py +++ b/utilities_ui.py @@ -155,12 +155,13 @@ def unregister(): from bpy.types import WindowManager for preview_collection in preview_collections.values(): bpy.utils.previews.remove(preview_collection) - preview_collections.clear() + preview_collection.clear() # Unregister 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 From f614e3bea74319df8371974af27c7289d6491134 Mon Sep 17 00:00:00 2001 From: franMarz <58062362+franMarz@users.noreply.github.com> Date: Sun, 9 Aug 2020 18:32:37 +0200 Subject: [PATCH 11/17] Add files via upload --- __init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/__init__.py b/__init__.py index 9a4517b..80f88dc 100644 --- a/__init__.py +++ b/__init__.py @@ -1279,7 +1279,7 @@ def icon_get(name): -'''def menu_IMAGE_uvs(self, context): +def menu_IMAGE_uvs(self, context): layout = self.layout layout.separator() layout.operator(op_uv_resize.op.bl_idname, text="Resize", icon_value = icon_get("op_extend_canvas_open")) @@ -1295,7 +1295,7 @@ def icon_get(name): layout.operator(op_island_align_edge.op.bl_idname, text="Align Edge", icon_value = icon_get("op_island_align_edge")) layout.operator(op_island_align_world.op.bl_idname, text="Align World", icon_value = icon_get("op_island_align_world")) - layout.menu(VIEW3D_MT_submenu_align)''' + layout.menu(VIEW3D_MT_submenu_align) class VIEW3D_MT_submenu_align(bpy.types.Menu): bl_label="Align" @@ -1435,7 +1435,7 @@ def register(): for icon in icons: utilities_ui.icon_register(icon) - #bpy.types.IMAGE_MT_uvs.append(menu_IMAGE_uvs) + bpy.types.IMAGE_MT_uvs.append(menu_IMAGE_uvs) bpy.types.IMAGE_MT_select.append(menu_IMAGE_select) bpy.types.IMAGE_MT_image.append(menu_IMAGE_MT_image) bpy.types.VIEW3D_MT_object.append(menu_VIEW3D_MT_object) From fa7317b751c0b3c29b20312a22a435d54f7485ab Mon Sep 17 00:00:00 2001 From: franMarz <58062362+franMarz@users.noreply.github.com> Date: Sun, 9 Aug 2020 18:33:26 +0200 Subject: [PATCH 12/17] Add files via upload --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 80f88dc..92104a1 100644 --- a/__init__.py +++ b/__init__.py @@ -1462,7 +1462,7 @@ def unregister(): #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_image.remove(menu_IMAGE_MT_image) bpy.types.VIEW3D_MT_object.remove(menu_VIEW3D_MT_object) From 0dfef2cf4b968c89d8b9f3edba96b75f805ea792 Mon Sep 17 00:00:00 2001 From: franMarz <58062362+franMarz@users.noreply.github.com> Date: Fri, 14 Aug 2020 10:16:04 +0200 Subject: [PATCH 13/17] Bounds fixes --- op_select_islands_outline.py | 58 +++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/op_select_islands_outline.py b/op_select_islands_outline.py index 750445c..96a18dd 100644 --- a/op_select_islands_outline.py +++ b/op_select_islands_outline.py @@ -10,7 +10,7 @@ from . import utilities_ui class op(bpy.types.Operator): bl_idname = "uv.textools_select_islands_outline" - bl_label = "Select Overlap" + bl_label = "Select Island outline" bl_description = "Select island edge bounds" bl_options = {'REGISTER', 'UNDO'} @@ -24,7 +24,11 @@ class op(bpy.types.Operator): #Requires UV map 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 @@ -40,12 +44,23 @@ def select_outline(context): if bpy.context.active_object.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); 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_all(action='DESELECT') @@ -60,19 +75,40 @@ def select_outline(context): # Create seams from islands 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 - for edge in edges_islands: + for edge in edges_seams_from_islands: edge.seam = False - # Select island edges - bpy.ops.mesh.select_all(action='DESELECT') - for edge in edges_islands: - edge.select = True + if pre_sync: + # Select seams from islands edges and edge boundaries + for edge in edges_islands: + 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 for edge in edges_seam: edge.seam = True + bpy.context.scene.tool_settings.use_uv_select_sync = pre_sync + bpy.utils.register_class(op) \ No newline at end of file From 31b24ecb765fcbb7269399df75f66ccee76617df Mon Sep 17 00:00:00 2001 From: ShalokShalom Date: Thu, 3 Sep 2020 08:02:47 +0200 Subject: [PATCH 14/17] correct typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b8a863d..f20a217 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 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 ## 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) From e1403bd7cac76b59f5bb8089e872711a4865f2d3 Mon Sep 17 00:00:00 2001 From: Antti Tikka Date: Sat, 5 Sep 2020 19:22:02 +0300 Subject: [PATCH 15/17] Fix Align Island by Edge only working in Blender 2.83 --- op_island_align_edge.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/op_island_align_edge.py b/op_island_align_edge.py index b15eee7..1e6c7ad 100644 --- a/op_island_align_edge.py +++ b/op_island_align_edge.py @@ -126,9 +126,14 @@ def align_island(uv_vert0, uv_vert1, faces): loop[uv_layers].select = True diff = uv_vert1 - uv_vert0 - current_angle = math.atan2(diff.y, diff.x) + 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.context.tool_settings.transform_pivot_point = 'CURSOR' From 1302a22e18641cf2fc19125ffbb4db0c3f6304a0 Mon Sep 17 00:00:00 2001 From: franMarz <58062362+franMarz@users.noreply.github.com> Date: Mon, 7 Sep 2020 01:41:46 +0200 Subject: [PATCH 16/17] Align World versions fix --- op_island_align_world.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/op_island_align_world.py b/op_island_align_world.py index b9047f6..639ed55 100644 --- a/op_island_align_world.py +++ b/op_island_align_world.py @@ -198,6 +198,10 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False 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)) @@ -208,7 +212,7 @@ def align_island(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False loop[uv_layers].select = True bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT' - bpy.ops.transform.rotate(value=-avg_angle, orient_axis='Z') # minus angle; Blender uses unconventional rotation notation (positive for clockwise) + bpy.ops.transform.rotate(value=-avg_angle, orient_axis='Z', constraint_axis=(False, False, False), orient_type='GLOBAL', mirror=False, use_proportional_edit=False) def align_island_simple(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_y=False): @@ -266,7 +270,7 @@ def align_island_simple(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_ loop[uv_layers].select = True bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT' - bpy.ops.transform.rotate(value=-a_delta, orient_axis='Z') # minus angle; Blender uses unconventional rotation notation (positive for clockwise) + bpy.ops.transform.rotate(value=a_delta, orient_axis='Z') bpy.utils.register_class(op) From 240f02f6eb99497548f997c57f96fa95c136ce5b Mon Sep 17 00:00:00 2001 From: franMarz <58062362+franMarz@users.noreply.github.com> Date: Mon, 7 Sep 2020 01:48:20 +0200 Subject: [PATCH 17/17] Align World versions fix 2 --- op_island_align_world.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/op_island_align_world.py b/op_island_align_world.py index 639ed55..d98744f 100644 --- a/op_island_align_world.py +++ b/op_island_align_world.py @@ -261,6 +261,10 @@ def align_island_simple(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_ a1 = math.atan2(delta_uvs.y, delta_uvs.x) a_delta = math.atan2(math.sin(a0-a1), math.cos(a0-a1)) + + # 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: + a_delta = -a_delta print("Turn {:.1f}".format(a_delta * 180/math.pi)) @@ -270,7 +274,7 @@ def align_island_simple(obj, bm, uv_layers, faces, x=0, y=1, flip_x=False, flip_ loop[uv_layers].select = True bpy.context.tool_settings.transform_pivot_point = 'MEDIAN_POINT' - bpy.ops.transform.rotate(value=a_delta, 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.utils.register_class(op)