Commit e383550d authored by frankie's avatar frankie 💬

basis for addon to straighten softskin meshes

parent 0c945e9b
'''
softskin related
this script will group co-planar vertices and link the opposite vertices
in each plane
SLICE_AXIS: axis perpendicular to group planes;
'x', 'y' or 'z' (by default)
SLICE_TOLERANCE: defines minimum distance between 2 positions to be considered
as different
LINK_STEP: defines the step to apply while looping in a group;
must be >= 1
LINK_NEXT_STEP: defines distance to next vertex in slice;
must be != 0
LINK_METHOD:
- 'radial', link opposite vertices based on group barycenter
- 'axial', link opposite vertices based on LINK_AXIS
- 'next', links to the next vertice, jumps depends on LINK_NEXT_STEP
LINK_AXIS: defines wich axis to use to link vertices;
'x', 'xy', 'xyz', 'xz', etc. are accepted
note that SLICE_AXIS will be ignored if present
author: frankie zafe, http://polymorph.cool
'''
import bpy
import math
import mathutils
SLICE_AXIS = 'z'
SLICE_TOLERANCE = 1e-5
LINK_STEP = 1
LINK_NEXT_STEP = 5
#LINK_METHOD = 'radial'
#LINK_METHOD = 'axial'
LINK_METHOD = 'next'
#LINK_METHOD = None
LINK_AXIS = 'xyz'
#bpy.ops.object.mode_set(mode='OBJECT',toggle=False)
obj = bpy.context.active_object
mesh = obj.data
slices = []
def clone_v3(v):
return mathutils.Vector((v.x,v.y,v.z))
def flatten_v3(v):
if SLICE_AXIS == 'x':
return mathutils.Vector((0,v.y,v.z))
elif SLICE_AXIS == 'y':
return mathutils.Vector((v.x,0,v.z))
else:
return mathutils.Vector((v.x,v.y,0))
def get_axis( vec ):
if SLICE_AXIS == 'x':
return mathutils.Vector((vec.x,0,0))
elif SLICE_AXIS == 'y':
return mathutils.Vector((0,vec.y,0))
else:
return mathutils.Vector((0,0,vec.z))
def add_slice( v ):
global slices
slice = {
'axis': get_axis(v.co),
'center': mathutils.Vector((0,0,0)),
'vids': [],
'local_pos': []
}
lid = len(slices)
slices.append( slice )
return slices[ lid ]
def get_slice( v ):
global slices
axis_pos = get_axis(v.co)
for s in slices:
delta = (s['axis'] - axis_pos).length
if delta < SLICE_TOLERANCE:
return s
return add_slice( v )
def get_edge( vlocal, vforeign ):
for e in mesh.edges:
if e.vertices[0] == vlocal and e.vertices[1] == vforeign:
return e
elif e.vertices[0] == vforeign and e.vertices[1] == vlocal:
return e
return None
def prepare_slice( slice ):
# barycenter of the slice
slice['center'] = mathutils.Vector((0,0,0))
for vid in slice['vids']:
slice['center'] += mesh.vertices[vid].co
slice['center'] /= len(slice['vids'])
for vid in slice['vids']:
v = clone_v3(mesh.vertices[vid].co) - slice['center']
slice['local_pos'].append( flatten_v3(v) )
def sort_slice( slice ):
vnum = len(slice['vids'])
if len(slice['vids']) != len(slice['local_pos']):
prepare_slice( slice )
if vnum < 2:
return
radians = []
for i in range(len(slice['local_pos'])):
lp = slice['local_pos'][i]
n = mathutils.Vector((lp.x,lp.y,lp.z))
n.normalize()
r = 0
if SLICE_AXIS == 'x':
r = math.atan2( n.z, n.y )
elif SLICE_AXIS == 'y':
r = math.atan2( n.z, n.x )
else:
r = math.atan2( n.y, n.x )
radians.append({'vid':slice['vids'][i],'loc':slice['local_pos'][i],'angle':r})
radians.sort(key=lambda item: item.get("angle"))
vids = []
locs = []
for r in radians:
vids.append( r['vid'] )
locs.append( r['loc'] )
slice['vids'] = vids
slice['local_pos'] = locs
def link_vertices( vid0, vid1 ):
if get_edge(vid0, vid1) == None:
mesh.vertices[vid0].select = True
mesh.vertices[vid1].select = True
bpy.ops.object.mode_set(mode='EDIT',toggle=False)
bpy.ops.mesh.edge_face_add()
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT',toggle=False)
mesh.vertices[vid0].select = False
mesh.vertices[vid1].select = False
def link_radial( slice ):
global SLICE_TOLERANCE
global LINK_STEP
vnum = len(slice['vids'])
for vi in range(0,vnum,LINK_STEP):
vlocal = slice['vids'][vi]
otherside = slice['local_pos'][vi]
otherside.negate()
closest = 0
vforeign = -1
for vj in range(0,vnum):
if vj == vi:
continue
delta = (slice['local_pos'][vj] - otherside).length
if (delta < SLICE_TOLERANCE and vforeign == -1) or closest > delta:
vforeign = slice['vids'][vj]
closest = delta
if vforeign == -1:
continue
link_vertices(vlocal, vforeign)
def link_axial( slice ):
global SLICE_AXIS
global SLICE_TOLERANCE
global LINK_STEP
global LINK_AXIS
vnum = len(slice['vids'])
for vi in range(0,vnum,LINK_STEP):
vlocal = slice['vids'][vi]
for a in range(len(LINK_AXIS)):
axis = LINK_AXIS[a:a+1]
lp = slice['local_pos'][vi]
otherside = None
if axis == SLICE_AXIS:
continue
if axis == 'x':
otherside = mathutils.Vector((lp.x*-1,lp.y,lp.z))
elif axis == 'y':
otherside = mathutils.Vector((lp.x,lp.y*-1,lp.z))
else:
otherside = mathutils.Vector((lp.x,lp.y,lp.z*-1))
closest = 0
vforeign = -1
for vj in range(0,vnum):
if vj == vi:
continue
delta = (slice['local_pos'][vj] - otherside).length
if (delta < SLICE_TOLERANCE and vforeign == -1) or closest > delta:
vforeign = slice['vids'][vj]
closest = delta
if vforeign == -1:
continue
link_vertices(vlocal, vforeign)
def link_next( slice ):
global LINK_STEP
global LINK_NEXT_STEP
vnum = len(slice['vids'])
for vi in range(0,vnum,LINK_STEP):
vlocal = slice['vids'][vi]
nexti = vi + LINK_NEXT_STEP
while( nexti >= vnum ):
nexti -= vnum
while( nexti < 0 ):
nexti += vnum
vforeign = slice['vids'][nexti]
link_vertices(vlocal, vforeign)
# going to object mode to retrieve all selected vertices
bpy.ops.object.mode_set(mode='OBJECT',toggle=False)
# create slices with selected vertices
for vid in range(len(mesh.vertices)):
v = mesh.vertices[vid]
print(v)
if not v.select:
continue
slice = get_slice( v )
slice['vids'].append( vid )
# deselecting all vertices
bpy.ops.object.mode_set(mode='EDIT',toggle=False)
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT',toggle=False)
# preparing slices for opposite linking
for s in slices:
sort_slice(s)
print( 'detected slices: ', len(slices) )
# generating edges in each slice
for s in slices:
if LINK_METHOD == 'radial':
link_radial( s )
elif LINK_METHOD == 'axial':
link_axial( s )
elif LINK_METHOD == 'next':
link_next( s )
else:
print( 'Unknown link method ', LINK_METHOD )
# selecting again all linked vertices
for s in slices:
for vid in s['vids']:
mesh.vertices[vid].select = True
pass
# back to EDIT mde
bpy.ops.object.mode_set(mode='EDIT',toggle=False)
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment