2019-06-08 23:42:50 +00:00
|
|
|
import bpy
|
|
|
|
import os
|
|
|
|
import bmesh
|
|
|
|
import math
|
|
|
|
import operator
|
|
|
|
|
|
|
|
from mathutils import Vector
|
|
|
|
from collections import defaultdict
|
2019-12-24 19:59:46 +00:00
|
|
|
from itertools import chain # 'flattens' collection of iterables
|
2019-06-08 23:42:50 +00:00
|
|
|
|
|
|
|
from . import utilities_uv
|
|
|
|
|
|
|
|
|
|
|
|
class op(bpy.types.Operator):
|
2019-12-18 20:53:16 +00:00
|
|
|
bl_idname = "uv.textools_edge_split_bevel"
|
|
|
|
bl_label = "Split Bevel"
|
|
|
|
bl_description = "..."
|
|
|
|
bl_options = {'REGISTER', 'UNDO'}
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-24 19:59:46 +00:00
|
|
|
radius: bpy.props.FloatProperty(
|
|
|
|
name="Space",
|
|
|
|
description="Space for split bevel",
|
|
|
|
default=0.015,
|
|
|
|
min=0,
|
|
|
|
max=0.35
|
2019-12-18 20:53:16 +00:00
|
|
|
)
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
@classmethod
|
|
|
|
def poll(cls, context):
|
|
|
|
if not bpy.context.active_object:
|
|
|
|
return False
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-24 19:59:46 +00:00
|
|
|
# Only in Edit mode
|
2019-12-18 20:53:16 +00:00
|
|
|
if bpy.context.active_object.mode != 'EDIT':
|
|
|
|
return False
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-24 19:59:46 +00:00
|
|
|
# Requires UV map
|
2019-12-18 20:53:16 +00:00
|
|
|
if not bpy.context.object.data.uv_layers:
|
|
|
|
return False
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
if bpy.context.scene.tool_settings.use_uv_select_sync:
|
|
|
|
return False
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-24 19:59:46 +00:00
|
|
|
# Only in UV editor mode
|
2019-12-18 20:53:16 +00:00
|
|
|
if bpy.context.area.type != 'IMAGE_EDITOR':
|
|
|
|
return False
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
return True
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
def execute(self, context):
|
|
|
|
main(self, self.radius)
|
|
|
|
return {'FINISHED'}
|
2019-06-08 23:42:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
def main(self, radius):
|
|
|
|
|
2019-12-24 19:59:46 +00:00
|
|
|
# Store selection
|
2019-12-18 20:53:16 +00:00
|
|
|
utilities_uv.selection_store()
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
print("____________\nedge split UV sharp edges {}".format(radius))
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-24 19:59:46 +00:00
|
|
|
obj = bpy.context.object
|
|
|
|
bm = bmesh.from_edit_mesh(bpy.context.active_object.data)
|
|
|
|
uv_layers = bm.loops.layers.uv.verify()
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
islands = utilities_uv.getSelectionIslands()
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# 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)
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# Collect hard edges
|
|
|
|
edges = []
|
|
|
|
for edge in bm.edges:
|
|
|
|
if edge.select and not edge.smooth:
|
|
|
|
# edge.link_faces
|
|
|
|
# print("Hard edge: {} - {}".format(edge.verts[0].index, edge.verts[1].index))
|
|
|
|
edges.append(edge)
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-24 19:59:46 +00:00
|
|
|
# Get vert rails to slide
|
2019-12-18 20:53:16 +00:00
|
|
|
vert_rails = get_vert_edge_rails(edges)
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# Get left and right faces
|
|
|
|
edge_face_pairs = get_edge_face_pairs(edges)
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
print("Vert rails: {}x".format(len(vert_rails)))
|
|
|
|
# for vert in vert_rails:
|
|
|
|
# print(".. v.idx {} = {}x".format(vert.index, len(vert_rails[vert]) ))
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
vert_processed = []
|
|
|
|
vert_uv_pos = []
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
for edge in edges:
|
|
|
|
if len(edge_face_pairs[edge]) == 2:
|
|
|
|
v0 = edge.verts[0]
|
|
|
|
v1 = edge.verts[1]
|
2019-12-24 19:59:46 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
f0 = edge_face_pairs[edge][0]
|
|
|
|
f1 = edge_face_pairs[edge][1]
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# v0
|
|
|
|
if v0 not in vert_processed:
|
|
|
|
vert_processed.append(v0)
|
2019-12-24 19:59:46 +00:00
|
|
|
faces, origin, delta = slide_uvs(
|
|
|
|
v0, edge, f0, edges, vert_rails, vert_to_uv)
|
|
|
|
vert_uv_pos.append(
|
|
|
|
{"v": v0, "f": f0, "origin": origin, "delta": delta, "faces": faces})
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-24 19:59:46 +00:00
|
|
|
faces, origin, delta = slide_uvs(
|
|
|
|
v0, edge, f1, edges, vert_rails, vert_to_uv)
|
|
|
|
vert_uv_pos.append(
|
|
|
|
{"v": v0, "f": f1, "origin": origin, "delta": delta, "faces": faces})
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# V1
|
|
|
|
if v1 not in vert_processed:
|
|
|
|
vert_processed.append(v1)
|
2019-12-24 19:59:46 +00:00
|
|
|
faces, origin, delta = slide_uvs(
|
|
|
|
v1, edge, f0, edges, vert_rails, vert_to_uv)
|
|
|
|
vert_uv_pos.append(
|
|
|
|
{"v": v1, "f": f0, "origin": origin, "delta": delta, "faces": faces})
|
|
|
|
|
|
|
|
faces, origin, delta = slide_uvs(
|
|
|
|
v1, edge, f1, edges, vert_rails, vert_to_uv)
|
|
|
|
vert_uv_pos.append(
|
|
|
|
{"v": v1, "f": f1, "origin": origin, "delta": delta, "faces": faces})
|
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# ...
|
|
|
|
for item in vert_uv_pos:
|
|
|
|
v = item["v"]
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
for face in item["faces"]:
|
|
|
|
if v in face.verts:
|
|
|
|
for loop in face.loops:
|
|
|
|
if loop.vert == v:
|
2019-12-24 19:59:46 +00:00
|
|
|
loop[uv_layers].uv = item["origin"] + \
|
|
|
|
item["delta"] * (radius/2)
|
2019-12-18 20:53:16 +00:00
|
|
|
# for f in faces:
|
|
|
|
# for loop in f.loops:
|
|
|
|
# if loop.vert == vert:
|
|
|
|
# loop[uv_layers].uv= vert_to_uv[vert][0].uv + item["delta"] * radius/2
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# for loop in face.loops:
|
|
|
|
# if loop.vert == vert:
|
|
|
|
# loop[uv_layers].uv+= avg_uv_delta
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-24 19:59:46 +00:00
|
|
|
# Restore selection
|
2019-12-18 20:53:16 +00:00
|
|
|
utilities_uv.selection_restore()
|
2019-06-08 23:42:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
def slide_uvs(vert, edge, face, edges, vert_rails, vert_to_uv):
|
2019-12-24 19:59:46 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
def IS_DEBUG():
|
|
|
|
return vert.index == 64 and edge.verts[0].index == 64 and edge.verts[1].index == 63
|
|
|
|
|
|
|
|
A = edge.verts[0]
|
|
|
|
B = edge.verts[1]
|
|
|
|
A_links, B_links = get_edge_prev_next(edge, edges)
|
2019-12-24 19:59:46 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
verts_edges = {edge.verts[0], edge.verts[1]}
|
|
|
|
for v in A_links:
|
2019-12-24 19:59:46 +00:00
|
|
|
verts_edges.add(v)
|
2019-12-18 20:53:16 +00:00
|
|
|
for v in B_links:
|
2019-12-24 19:59:46 +00:00
|
|
|
verts_edges.add(v)
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
if IS_DEBUG():
|
|
|
|
print("\r")
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-24 19:59:46 +00:00
|
|
|
print(
|
|
|
|
"Edge {} <--> {} ({})".format(edge.verts[0].index, edge.verts[1].index, vert.index))
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# Collect faces of this side
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
'''
|
|
|
|
faces = [face]
|
|
|
|
face_edges_used = [e for e in face.edges if e in edges]
|
|
|
|
for e in face.edges:
|
|
|
|
if e not in face_edges_used:
|
|
|
|
for f in e.link_faces:
|
|
|
|
if f != face:
|
|
|
|
faces.append(f)
|
|
|
|
'''
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
faces = [face]
|
|
|
|
edges_main_used = [edge]
|
|
|
|
for i in range(2):
|
|
|
|
append = []
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
for f in faces:
|
|
|
|
for e in f.edges:
|
|
|
|
if e not in edges_main_used:
|
|
|
|
if e in edges:
|
|
|
|
edges_main_used.append(e)
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
for f_link in e.link_faces:
|
|
|
|
if f_link not in faces:
|
|
|
|
append.append(f_link)
|
|
|
|
faces.extend(append)
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
if IS_DEBUG():
|
|
|
|
print(" Faces {}x = {}".format(len(faces), [f.index for f in faces]))
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# Get all face edges that could be valid rails
|
2019-12-24 19:59:46 +00:00
|
|
|
face_edges = list(
|
|
|
|
set([e for f in faces for e in f.edges if e not in edges]))
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# The verts influencing the offset
|
2019-12-24 19:59:46 +00:00
|
|
|
verts = [A, B]
|
2019-12-18 20:53:16 +00:00
|
|
|
if vert == A:
|
|
|
|
verts.extend(B_links)
|
|
|
|
elif vert == B:
|
|
|
|
verts.extend(A_links)
|
|
|
|
# verts = [vert]
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
if IS_DEBUG():
|
|
|
|
print(" Verts: {}x = {}".format(len(verts), [v.index for v in verts]))
|
|
|
|
print(" Rails:")
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-24 19:59:46 +00:00
|
|
|
delta = Vector((0, 0))
|
2019-12-18 20:53:16 +00:00
|
|
|
count = 0.0
|
|
|
|
for v in verts:
|
|
|
|
rails = [e for e in vert_rails[v] if e in face_edges]
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
if IS_DEBUG():
|
2019-12-24 19:59:46 +00:00
|
|
|
print(" #{} rails = {}".format(v.index, [
|
|
|
|
("{} - {}".format(e.verts[0].index, e.verts[1].index)) for e in rails]))
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
for e in rails:
|
|
|
|
# determine order
|
|
|
|
v0 = None
|
|
|
|
v1 = None
|
|
|
|
if e.verts[0] in verts_edges:
|
|
|
|
v0 = e.verts[0]
|
|
|
|
v1 = e.verts[1]
|
|
|
|
elif e.verts[1] in verts_edges:
|
|
|
|
v0 = e.verts[1]
|
|
|
|
v1 = e.verts[0]
|
|
|
|
uv0 = vert_to_uv[v0][0].uv
|
|
|
|
uv1 = vert_to_uv[v1][0].uv
|
|
|
|
delta += (uv1-uv0).normalized()
|
|
|
|
count += 1.0
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-24 19:59:46 +00:00
|
|
|
delta /= count
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
if IS_DEBUG():
|
|
|
|
print("\r")
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
return faces, vert_to_uv[vert][0].uv.copy(), delta.normalized()
|
|
|
|
# print(" V{} = {}".format(v.index, avg_uv_delta))
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# for loop in face.loops:
|
|
|
|
# if loop.vert == vert:
|
|
|
|
# loop[uv_layers].uv+= avg_uv_delta
|
2019-06-08 23:42:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
def slide_face_uvs(uv_layers, edge, vert, face, radius, vert_to_uv):
|
2019-12-18 20:53:16 +00:00
|
|
|
avg_target = Vector((0,0))
|
|
|
|
avg_count = 0
|
|
|
|
|
|
|
|
for e in face.edges:
|
|
|
|
if e != edge and vert in e.verts:
|
|
|
|
vert_B = e.verts[0]
|
|
|
|
if vert == e.verts[0]:
|
|
|
|
vert_B = e.verts[1]
|
|
|
|
A = vert_to_uv[vert][0].uv
|
|
|
|
B = vert_to_uv[vert_B][0].uv
|
|
|
|
|
|
|
|
avg_target+= A +(B - A).normalized() * radius
|
|
|
|
avg_count+=1
|
|
|
|
|
|
|
|
avg_target/=avg_count
|
|
|
|
avg_target = vert_to_uv[vert][0].uv +(avg_target - vert_to_uv[vert][0].uv).normalized() * radius
|
|
|
|
|
|
|
|
for loop in face.loops:
|
|
|
|
if loop.vert == vert:
|
|
|
|
loop[uv_layers].uv = avg_target
|
2019-06-08 23:42:50 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
'''
|
2019-12-18 20:53:16 +00:00
|
|
|
# Get all rails (max 3x: current, before and after)
|
|
|
|
rails = [e for v in verts_edges for e in vert_rails[v]]
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
for x in rails:
|
|
|
|
print(" raail: {} x {}".format(x.verts[0].index, x.verts[1].index))
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# Keep only rails shared with faces
|
|
|
|
rails = [e for e in rails if e in face_edges]
|
2019-06-08 23:42:50 +00:00
|
|
|
|
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# print("...... v{} with {}x rails ".format(vert.index, len(rails)))
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# Filter rails on same side
|
2019-06-08 23:42:50 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
def get_edge_prev_next(edge, edges):
|
2019-12-18 20:53:16 +00:00
|
|
|
A = edge.verts[0]
|
|
|
|
B = edge.verts[1]
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
# print(" get_edge_prev_next {}x edges".format(len(edges)))
|
2019-12-24 19:59:46 +00:00
|
|
|
# v0_extends = []
|
2019-12-18 20:53:16 +00:00
|
|
|
# v0_extends = [v for e in edges for v in e.verts if v in edge.verts and e != edge and v != v0]
|
|
|
|
# v1_extends = [v for e in edges for v in e.verts if v in edge.verts and e != edge and v != v1]
|
|
|
|
# v0_extends = [v_nest for v in edge.verts for e in v.link_edges for v_nest in e.verts if e != edge and if e in edges]
|
2019-12-24 19:59:46 +00:00
|
|
|
|
|
|
|
A_extends = [v2 for v1 in edge.verts for e in v1.link_edges for v2 in e.verts if e !=
|
|
|
|
edge and e in edges and v2 not in edge.verts and v1 != A]
|
|
|
|
B_extends = [v2 for v1 in edge.verts for e in v1.link_edges for v2 in e.verts if e !=
|
|
|
|
edge and e in edges and v2 not in edge.verts and v1 != B]
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
return A_extends, B_extends
|
2019-06-08 23:42:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_edge_face_pairs(edges):
|
2019-12-18 20:53:16 +00:00
|
|
|
edge_faces = {}
|
|
|
|
for edge in edges:
|
|
|
|
v0 = edge.verts[0]
|
|
|
|
v1 = edge.verts[1]
|
|
|
|
faces = []
|
|
|
|
for face in edge.link_faces:
|
|
|
|
if v0 in face.verts and v1 in face.verts:
|
|
|
|
faces.append(face)
|
|
|
|
edge_faces[edge] = faces
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
return edge_faces
|
2019-06-08 23:42:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_vert_edge_rails(edges):
|
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
vert_rails = {}
|
|
|
|
for edge in edges:
|
|
|
|
v0 = edge.verts[0]
|
|
|
|
v1 = edge.verts[1]
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
faces = []
|
|
|
|
for face in edge.link_faces:
|
|
|
|
if v0 in face.verts and v1 in face.verts:
|
|
|
|
faces.append(face)
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
for face in faces:
|
|
|
|
for e in face.edges:
|
|
|
|
if e not in edges and len(e.link_faces) > 0:
|
|
|
|
if v0 not in vert_rails:
|
2019-12-24 19:59:46 +00:00
|
|
|
vert_rails[v0] = []
|
2019-12-18 20:53:16 +00:00
|
|
|
if v1 not in vert_rails:
|
2019-12-24 19:59:46 +00:00
|
|
|
vert_rails[v1] = []
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
if v0 in e.verts and e not in vert_rails[v0]:
|
|
|
|
vert_rails[v0].append(e)
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
if v1 in e.verts and e not in vert_rails[v1]:
|
|
|
|
vert_rails[v1].append(e)
|
2019-06-08 23:42:50 +00:00
|
|
|
|
2019-12-18 20:53:16 +00:00
|
|
|
return vert_rails
|
2019-06-08 23:42:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
bpy.utils.register_class(op)
|