mirror of
https://github.com/drewcassidy/TexTools-Blender
synced 2024-09-01 14:54:44 +00:00
277 lines
9.2 KiB
Python
277 lines
9.2 KiB
Python
import bpy
|
|
import os
|
|
import bmesh
|
|
import math
|
|
import operator
|
|
|
|
from mathutils import Vector
|
|
from collections import defaultdict
|
|
from itertools import chain # 'flattens' collection of iterables
|
|
|
|
from . import utilities_uv
|
|
|
|
|
|
class op(bpy.types.Operator):
|
|
bl_idname = "uv.textools_island_align_world"
|
|
bl_label = "Align World"
|
|
bl_description = "Align selected UV islands to world / gravity directions"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
# is_global = bpy.props.BoolProperty(
|
|
# name = "Global Axis",
|
|
# description = "Global or local object axis alignment",
|
|
# default = False
|
|
# )
|
|
|
|
# def draw(self, context):
|
|
# layout = self.layout
|
|
# layout.prop(self, "is_global")
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
if not bpy.context.active_object:
|
|
return False
|
|
|
|
# Only in Edit mode
|
|
if bpy.context.active_object.mode != 'EDIT':
|
|
return False
|
|
|
|
# Requires UV map
|
|
if not bpy.context.object.data.uv_layers:
|
|
return False
|
|
|
|
if bpy.context.scene.tool_settings.use_uv_select_sync:
|
|
return False
|
|
|
|
# Only in UV editor mode
|
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
|
return False
|
|
|
|
return True
|
|
|
|
def execute(self, context):
|
|
main(self)
|
|
return {'FINISHED'}
|
|
|
|
|
|
def main(context):
|
|
print("\n________________________\nis_global")
|
|
|
|
# Store selection
|
|
utilities_uv.selection_store()
|
|
|
|
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
|
uv_layers = bm.loops.layers.uv.verify()
|
|
|
|
# Only in Face or Island mode
|
|
if bpy.context.scene.tool_settings.uv_select_mode is not 'FACE' or 'ISLAND':
|
|
bpy.context.scene.tool_settings.uv_select_mode = 'FACE'
|
|
|
|
obj = bpy.context.object
|
|
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
|
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))
|
|
for face in faces:
|
|
avg_normal += face.normal
|
|
avg_normal /= len(faces)
|
|
|
|
# avg_normal = (obj.matrix_world*avg_normal).normalized()
|
|
|
|
# Which Side
|
|
x = 0
|
|
y = 1
|
|
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)
|
|
|
|
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
|
|
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))
|
|
|
|
# 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)
|
|
|
|
processed_edges = []
|
|
edges = []
|
|
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
|
|
|
|
avg_angle /= len(edges) # - math.pi/2
|
|
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')
|
|
# 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)
|