forked from bartvdbraak/blender
Added utility functions to BPyMesh.py
- dictWeightFlipGroups, return a vert weight with flipped group names. - dictWeightMerge, takes a list of dictWeights and merges them mesh_mirror_tool.py Added vertex weight support for the mirror tool, its able to mirror arbitary meshes weights with optional name flipping and creation of name flipped groups. This also uses the mode for l<>r, l>r, r>l so you can copy from left to right only for eg.
This commit is contained in:
parent
94ebee891c
commit
11586835da
@ -10,7 +10,7 @@ def meshWeight2Dict(me):
|
||||
|
||||
# Clear the vert group.
|
||||
groupNames= me.getVertGroupNames()
|
||||
|
||||
|
||||
for group in groupNames:
|
||||
for index, weight in me.getVertsFromGroup(group, 1): # (i,w) tuples.
|
||||
vWeightDict[index][group]= weight
|
||||
@ -56,8 +56,84 @@ def dict2MeshWeight(me, groupNames, vWeightDict):
|
||||
|
||||
me.update()
|
||||
|
||||
def dictWeightMerge(dict_weights):
|
||||
'''
|
||||
Takes dict weight list and merges into 1 weight dict item and returns it
|
||||
'''
|
||||
|
||||
if not dict_weights:
|
||||
return {}
|
||||
|
||||
keys= []
|
||||
for weight in dict_weights:
|
||||
keys.extend([ (k, 0.0) for k in weight.iterkeys() ])
|
||||
|
||||
new_wdict = dict(keys)
|
||||
|
||||
len_dict_weights= len(dict_weights)
|
||||
|
||||
for weight in dict_weights:
|
||||
for group, value in weight.iteritems():
|
||||
new_wdict[group] += value/len_dict_weights
|
||||
|
||||
return new_wdict
|
||||
|
||||
|
||||
FLIPNAMES=[\
|
||||
('Left','Right'),\
|
||||
('_L','_R'),\
|
||||
('-L','-R'),\
|
||||
('.L','.R'),\
|
||||
]
|
||||
|
||||
def dictWeightFlipGroups(dict_weight, groupNames, createNewGroups):
|
||||
'''
|
||||
Returns a weight with flip names
|
||||
dict_weight - 1 vert weight.
|
||||
groupNames - because we may need to add new group names.
|
||||
dict_weight - Weather to make new groups where needed.
|
||||
'''
|
||||
|
||||
def flipName(name):
|
||||
for n1,n2 in FLIPNAMES:
|
||||
for nA, nB in ( (n1,n2), (n1.lower(),n2.lower()), (n1.upper(),n2.upper()) ):
|
||||
if createNewGroups:
|
||||
newName= name.replace(nA,nB)
|
||||
if newName!=name:
|
||||
if newName not in groupNames:
|
||||
groupNames.append(newName)
|
||||
return newName
|
||||
|
||||
newName= name.replace(nB,nA)
|
||||
if newName!=name:
|
||||
if newName not in groupNames:
|
||||
groupNames.append(newName)
|
||||
return newName
|
||||
|
||||
else:
|
||||
newName= name.replace(nA,nB)
|
||||
if newName!=name and newName in groupNames:
|
||||
return newName
|
||||
|
||||
newName= name.replace(nB,nA)
|
||||
if newName!=name and newName in groupNames:
|
||||
return newName
|
||||
|
||||
return name
|
||||
|
||||
if not dict_weight:
|
||||
return dict_weight, groupNames
|
||||
|
||||
|
||||
new_wdict = {}
|
||||
for group, weight in dict_weight.iteritems():
|
||||
flipname= flipName(group)
|
||||
print flipname, group
|
||||
new_wdict[flipname]= weight
|
||||
|
||||
return new_wdict, groupNames
|
||||
|
||||
|
||||
def getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=True, scn=None):
|
||||
'''
|
||||
ob - the object that you want to get the mesh from
|
||||
@ -239,6 +315,75 @@ me.faces.extend([[me.verts[ii] for ii in i] for i in indices])
|
||||
|
||||
|
||||
|
||||
from Blender import *
|
||||
|
||||
def pointInsideMesh(ob, pt):
|
||||
Intersect = Mathutils.Intersect # 2 less dict lookups.
|
||||
Vector = Mathutils.Vector
|
||||
|
||||
def ptInFaceXYBounds(f, pt):
|
||||
|
||||
co= f.v[0].co
|
||||
xmax= xmin= co.x
|
||||
ymax= ymin= co.y
|
||||
|
||||
co= f.v[1].co
|
||||
xmax= max(xmax, co.x)
|
||||
xmin= min(xmin, co.x)
|
||||
ymax= max(ymax, co.y)
|
||||
ymin= min(ymin, co.y)
|
||||
|
||||
co= f.v[2].co
|
||||
xmax= max(xmax, co.x)
|
||||
xmin= min(xmin, co.x)
|
||||
ymax= max(ymax, co.y)
|
||||
ymin= min(ymin, co.y)
|
||||
|
||||
if len(f.v)==4:
|
||||
co= f.v[3].co
|
||||
xmax= max(xmax, co.x)
|
||||
xmin= min(xmin, co.x)
|
||||
ymax= max(ymax, co.y)
|
||||
ymin= min(ymin, co.y)
|
||||
|
||||
# Now we have the bounds, see if the point is in it.
|
||||
if\
|
||||
pt.x < xmin or\
|
||||
pt.y < ymin or\
|
||||
pt.x > xmax or\
|
||||
pt.y > ymax:
|
||||
return False # point is outside face bounds
|
||||
else:
|
||||
return True # point inside.
|
||||
#return xmax, ymax, xmin, ymin
|
||||
|
||||
def faceIntersect(f):
|
||||
isect = Intersect(f.v[0].co, f.v[1].co, f.v[2].co, ray, obSpacePt, 1) # Clipped.
|
||||
if not isect and len(f.v) == 4:
|
||||
isect = Intersect(f.v[0].co, f.v[2].co, f.v[3].co, ray, obSpacePt, 1) # Clipped.
|
||||
|
||||
if isect and isect.z > obSpacePt.z: # This is so the ray only counts if its above the point.
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
obImvMat = Mathutils.Matrix(ob.matrixWorld)
|
||||
obImvMat.invert()
|
||||
pt.resize4D()
|
||||
obSpacePt = pt* obImvMat
|
||||
pt.resize3D()
|
||||
obSpacePt.resize3D()
|
||||
ray = Vector(0,0,-1)
|
||||
me= ob.getData(mesh=1)
|
||||
|
||||
# Here we find the number on intersecting faces, return true if an odd number (inside), false (outside) if its true.
|
||||
return len([None for f in me.faces if ptInFaceXYBounds(f, obSpacePt) if faceIntersect(f)]) % 2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,22 +1,23 @@
|
||||
#!BPY
|
||||
"""
|
||||
Name: 'Mirror Snap Verts'
|
||||
Name: 'Mirror Verts Loc & Weight'
|
||||
Blender: 241
|
||||
Group: 'Object'
|
||||
Group: 'Mesh'
|
||||
Tooltip: 'Move verts so they snap to their mirrored locations.'
|
||||
"""
|
||||
|
||||
from Blender import Draw, Window, Scene, Mesh, Mathutils, sys
|
||||
import BPyMesh
|
||||
reload(BPyMesh)
|
||||
|
||||
|
||||
def mesh_mirror(me, PREF_MAX_DIST, PREF_MODE, PREF_NOR_WEIGHT, PREF_SEL_ONLY, PREF_EDGE_USERS):
|
||||
def mesh_mirror(me, PREF_MIRROR_LOCATION, PREF_MAX_DIST, PREF_MODE, PREF_NOR_WEIGHT, PREF_SEL_ONLY, PREF_EDGE_USERS, PREF_MIRROR_WEIGHTS, PREF_FLIP_NAMES, PREF_CREATE_FLIP_NAMES):
|
||||
'''
|
||||
PREF_MAX_DIST, Maximum distance to test snapping verts.
|
||||
PREF_MODE, 0:middle, 1: Left. 2:Right.
|
||||
PREF_NOR_WEIGHT, use normal angle difference to weight distances.
|
||||
PREF_SEL_ONLY, only snap the selection
|
||||
PREF_EDGE_USERS, match only verts with the same number of edge users.
|
||||
PREF_MIRROR_LOCATION,
|
||||
'''
|
||||
|
||||
is_editmode = Window.EditMode() # Exit Editmode.
|
||||
@ -29,6 +30,7 @@ def mesh_mirror(me, PREF_MAX_DIST, PREF_MODE, PREF_NOR_WEIGHT, PREF_SEL_ONLY, PR
|
||||
for v in me.verts:
|
||||
v.sel=1
|
||||
|
||||
|
||||
if PREF_EDGE_USERS:
|
||||
edge_users= [0]*len(me.verts)
|
||||
for ed in me.edges:
|
||||
@ -39,68 +41,135 @@ def mesh_mirror(me, PREF_MAX_DIST, PREF_MODE, PREF_NOR_WEIGHT, PREF_SEL_ONLY, PR
|
||||
neg_vts = [v for v in me.verts if v.sel and v.co.x > 0.000001]
|
||||
pos_vts = [v for v in me.verts if v.sel and v.co.x < -0.000001]
|
||||
|
||||
mirror_pos= True
|
||||
mirror_weights=True
|
||||
|
||||
|
||||
groupNames, vWeightDict= BPyMesh.meshWeight2Dict(me)
|
||||
|
||||
mirror_pairs= []
|
||||
# allign the negative with the positive.
|
||||
flipvec= Mathutils.Vector()
|
||||
len_neg_vts= float(len(neg_vts))
|
||||
for i1, nv in enumerate(neg_vts):
|
||||
nv_co= nv.co
|
||||
for i2, pv in enumerate(pos_vts):
|
||||
# Enforce edge users.
|
||||
if not PREF_EDGE_USERS or edge_users[i1]==edge_users[i2]:
|
||||
flipvec[:]= pv.co
|
||||
flipvec.x= -flipvec.x
|
||||
l= (nv_co-flipvec).length
|
||||
|
||||
# Record a match.
|
||||
if l<=PREF_MAX_DIST:
|
||||
|
||||
# We can adjust the length by the normal, now we know the length is under the limit.
|
||||
if PREF_NOR_WEIGHT>0:
|
||||
# Get the normal and flipm reuse flipvec
|
||||
flipvec[:]= pv.no
|
||||
flipvec.x= -flipvec.x
|
||||
try:
|
||||
ang= Mathutils.AngleBetweenVecs(nv.no, flipvec)/180.0
|
||||
except: # on rare occasions angle between vecs will fail.- zero length vec.
|
||||
ang= 0
|
||||
|
||||
l=l*(1+(ang*PREF_NOR_WEIGHT))
|
||||
|
||||
mirror_pairs.append((l, nv, pv))
|
||||
|
||||
# Update every 20 loops
|
||||
if i1 % 10 == 0:
|
||||
Window.DrawProgressBar(0.8 * (i1/len_neg_vts), 'Mirror verts %i of %i' % (i1, len_neg_vts))
|
||||
|
||||
|
||||
Window.DrawProgressBar(0.9, 'Mirror verts: Updating locations')
|
||||
# Now we have a list of the pairs we might use, lets find the best and do them first.
|
||||
# de-selecting as we go. so we can makke sure not to mess it up.
|
||||
mirror_pairs.sort(lambda a,b: cmp(a[0], b[0]))
|
||||
|
||||
for dist, v1,v2 in mirror_pairs: # dist, neg, pos
|
||||
if v1.sel and v2.sel:
|
||||
if PREF_MODE==0: # Middle
|
||||
flipvec[:]= v2.co # positive
|
||||
flipvec.x= -flipvec.x # negatve
|
||||
v2.co[:]= v1.co[:]= (flipvec+v1.co)*0.5 # midway
|
||||
v2.co.x= -v2.co.x
|
||||
elif PREF_MODE==2: # Left
|
||||
v2.co[:]= v1.co
|
||||
v2.co.x= -v2.co.x
|
||||
elif PREF_MODE==1: # Right
|
||||
v1.co[:]= v2.co
|
||||
v1.co.x= -v1.co.x
|
||||
v1.sel= 0
|
||||
v2.sel= 0
|
||||
#*Mirror Location*********************************************************#
|
||||
if PREF_MIRROR_LOCATION:
|
||||
mirror_pairs= []
|
||||
# allign the negative with the positive.
|
||||
flipvec= Mathutils.Vector()
|
||||
len_neg_vts= float(len(neg_vts))
|
||||
for i1, nv in enumerate(neg_vts):
|
||||
nv_co= nv.co
|
||||
for i2, pv in enumerate(pos_vts):
|
||||
# Enforce edge users.
|
||||
if not PREF_EDGE_USERS or edge_users[i1]==edge_users[i2]:
|
||||
flipvec[:]= pv.co
|
||||
flipvec.x= -flipvec.x
|
||||
l= (nv_co-flipvec).length
|
||||
|
||||
# Record a match.
|
||||
if l<=PREF_MAX_DIST:
|
||||
|
||||
# We can adjust the length by the normal, now we know the length is under the limit.
|
||||
if PREF_NOR_WEIGHT>0:
|
||||
# Get the normal and flipm reuse flipvec
|
||||
flipvec[:]= pv.no
|
||||
flipvec.x= -flipvec.x
|
||||
try:
|
||||
ang= Mathutils.AngleBetweenVecs(nv.no, flipvec)/180.0
|
||||
except: # on rare occasions angle between vecs will fail.- zero length vec.
|
||||
ang= 0
|
||||
|
||||
l=l*(1+(ang*PREF_NOR_WEIGHT))
|
||||
|
||||
mirror_pairs.append((l, nv, pv))
|
||||
|
||||
# Update every 20 loops
|
||||
if i1 % 10 == 0:
|
||||
Window.DrawProgressBar(0.8 * (i1/len_neg_vts), 'Mirror verts %i of %i' % (i1, len_neg_vts))
|
||||
|
||||
Window.DrawProgressBar(0.9, 'Mirror verts: Updating locations')
|
||||
# Now we have a list of the pairs we might use, lets find the best and do them first.
|
||||
# de-selecting as we go. so we can makke sure not to mess it up.
|
||||
mirror_pairs.sort(lambda a,b: cmp(a[0], b[0]))
|
||||
|
||||
for dist, v1,v2 in mirror_pairs: # dist, neg, pos
|
||||
if v1.sel and v2.sel:
|
||||
if PREF_MODE==0: # Middle
|
||||
flipvec[:]= v2.co # positive
|
||||
flipvec.x= -flipvec.x # negatve
|
||||
v2.co[:]= v1.co[:]= (flipvec+v1.co)*0.5 # midway
|
||||
v2.co.x= -v2.co.x
|
||||
elif PREF_MODE==2: # Left
|
||||
v2.co[:]= v1.co
|
||||
v2.co.x= -v2.co.x
|
||||
elif PREF_MODE==1: # Right
|
||||
v1.co[:]= v2.co
|
||||
v1.co.x= -v1.co.x
|
||||
v1.sel= 0
|
||||
v2.sel= 0
|
||||
|
||||
#*Mirror Weights**********************************************************#
|
||||
if PREF_MIRROR_WEIGHTS:
|
||||
|
||||
groupNames, vWeightDict= BPyMesh.meshWeight2Dict(me)
|
||||
mirror_pairs_l2r= [] # Stor a list of matches for these verts.
|
||||
mirror_pairs_r2l= [] # Stor a list of matches for these verts.
|
||||
|
||||
# allign the negative with the positive.
|
||||
flipvec= Mathutils.Vector()
|
||||
len_neg_vts= float(len(neg_vts))
|
||||
|
||||
if PREF_MODE==0: # Middle
|
||||
find_set= ((neg_vts, pos_vts, mirror_pairs_l2r), (pos_vts, neg_vts, mirror_pairs_r2l))
|
||||
elif PREF_MODE==1: # Left
|
||||
find_set= ((neg_vts, pos_vts, mirror_pairs_l2r), )
|
||||
elif PREF_MODE==2: # Right
|
||||
find_set= ((pos_vts, neg_vts, mirror_pairs_r2l), )
|
||||
|
||||
for vtls_A, vtls_B, pair_ls in ((neg_vts, pos_vts, mirror_pairs_l2r), (pos_vts, neg_vts, mirror_pairs_r2l)):
|
||||
for i1, vA in enumerate(vtls_A):
|
||||
best_len=1<<30
|
||||
best_idx=-1
|
||||
|
||||
# Find the BEST match.
|
||||
vA_co= vA.co
|
||||
for i2, vB in enumerate(vtls_B):
|
||||
# Enforce edge users.
|
||||
if not PREF_EDGE_USERS or edge_users[i1]==edge_users[i2]:
|
||||
flipvec[:]= vB.co
|
||||
flipvec.x= -flipvec.x
|
||||
l= (vA_co-flipvec).length
|
||||
|
||||
if l<best_len:
|
||||
best_len=l
|
||||
best_idx=i2
|
||||
|
||||
if best_idx != -1:
|
||||
pair_ls.append((vtls_A[i1].index, vtls_B[best_idx].index)) # neg, pos.
|
||||
|
||||
# Now we can merge the weights
|
||||
if PREF_MODE==0: # Middle
|
||||
newVWeightDict= [{} for i in xrange(len(me.verts))] # Have empty dicts just incase
|
||||
for pair_ls in (mirror_pairs_l2r, mirror_pairs_r2l):
|
||||
if PREF_FLIP_NAMES:
|
||||
for i1, i2 in pair_ls:
|
||||
flipWeight, groupNames= BPyMesh.dictWeightFlipGroups( vWeightDict[i2], groupNames, PREF_CREATE_FLIP_NAMES )
|
||||
newVWeightDict[i1]= BPyMesh.dictWeightMerge([vWeightDict[i1], flipWeight] )
|
||||
else:
|
||||
for i1, i2 in pair_ls:
|
||||
newVWeightDict[i1]= BPyMesh.dictWeightMerge([vWeightDict[i1], vWeightDict[i2]])
|
||||
|
||||
vWeightDict= newVWeightDict
|
||||
|
||||
elif PREF_MODE==1: # Left
|
||||
if PREF_FLIP_NAMES:
|
||||
for i1, i2 in mirror_pairs_l2r:
|
||||
vWeightDict[i2], groupNames= BPyMesh.dictWeightFlipGroups(vWeightDict[i1], groupNames, PREF_CREATE_FLIP_NAMES)
|
||||
else:
|
||||
for i1, i2 in mirror_pairs_l2r:
|
||||
vWeightDict[i2]= vWeightDict[i1] # Warning Multiple instances of the same data
|
||||
|
||||
elif PREF_MODE==2: # Right
|
||||
if PREF_FLIP_NAMES:
|
||||
for i1, i2 in mirror_pairs_r2l:
|
||||
vWeightDict[i2], groupNames= BPyMesh.dictWeightFlipGroups(vWeightDict[i1], groupNames, PREF_CREATE_FLIP_NAMES)
|
||||
else:
|
||||
for i1, i2 in mirror_pairs_r2l:
|
||||
vWeightDict[i2]= vWeightDict[i1]
|
||||
|
||||
BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict)
|
||||
|
||||
me.update()
|
||||
|
||||
if is_editmode: Window.EditMode(1)
|
||||
@ -116,30 +185,48 @@ def main():
|
||||
except:
|
||||
Draw.PupMenu('Error, select a mesh as your active object')
|
||||
|
||||
# Defaults
|
||||
PREF_MIRROR_LOCATION= Draw.Create(1)
|
||||
PREF_MAX_DIST= Draw.Create(0.2)
|
||||
PREF_MODE= Draw.Create(0)
|
||||
PREF_NOR_WEIGHT= Draw.Create(0.0)
|
||||
PREF_SEL_ONLY= Draw.Create(1)
|
||||
PREF_EDGE_USERS= Draw.Create(0)
|
||||
# Weights
|
||||
PREF_MIRROR_WEIGHTS= Draw.Create(0)
|
||||
PREF_FLIP_NAMES= Draw.Create(1)
|
||||
PREF_CREATE_FLIP_NAMES= Draw.Create(1)
|
||||
|
||||
pup_block = [\
|
||||
('MaxDist:', PREF_MAX_DIST, 0.0, 1.0, 'Generate interpolated verts so closer vert weights can be copied.'),\
|
||||
('Mode:', PREF_MODE, 0, 2, 'New Location (0:AverageL/R, 1:Left>Right 2:Right>Left)'),\
|
||||
('Mode:', PREF_MODE, 0, 2, 'New Location/Weight (0:AverageL/R, 1:Left>Right 2:Right>Left)'),\
|
||||
('NorWeight:', PREF_NOR_WEIGHT, 0.0, 1.0, 'Generate interpolated verts so closer vert weights can be copied.'),\
|
||||
('Sel Only', PREF_SEL_ONLY, 'Only mirror selected verts. Else try and mirror all'),\
|
||||
('Edge Users', PREF_EDGE_USERS, 'Only match up verts that have the same number of edge users.'),\
|
||||
'Locations',\
|
||||
('Mirror Location', PREF_MIRROR_LOCATION, 'Mirror vertex locations.'),\
|
||||
'Weights',\
|
||||
('Mirror Weights', PREF_MIRROR_WEIGHTS, 'Mirror vertex locations.'),\
|
||||
('Flip Groups', PREF_FLIP_NAMES, 'Mirror flip names.'),\
|
||||
('New Flip Groups', PREF_CREATE_FLIP_NAMES, 'Make new groups for flipped names.'),\
|
||||
]
|
||||
|
||||
if not Draw.PupBlock("Mirror mesh tool", pup_block):
|
||||
return
|
||||
|
||||
PREF_MIRROR_LOCATION= PREF_MIRROR_LOCATION.val
|
||||
PREF_MAX_DIST= PREF_MAX_DIST.val
|
||||
PREF_MODE= PREF_MODE.val
|
||||
PREF_NOR_WEIGHT= PREF_NOR_WEIGHT.val
|
||||
PREF_SEL_ONLY= PREF_SEL_ONLY.val
|
||||
PREF_EDGE_USERS= PREF_EDGE_USERS.val
|
||||
# weights
|
||||
PREF_MIRROR_WEIGHTS= PREF_MIRROR_WEIGHTS.val
|
||||
PREF_FLIP_NAMES= PREF_FLIP_NAMES.val
|
||||
PREF_CREATE_FLIP_NAMES= PREF_CREATE_FLIP_NAMES.val
|
||||
|
||||
t= sys.time()
|
||||
mesh_mirror(me, PREF_MAX_DIST, PREF_MODE, PREF_NOR_WEIGHT, PREF_SEL_ONLY, PREF_EDGE_USERS)
|
||||
mesh_mirror(me, PREF_MIRROR_LOCATION, PREF_MAX_DIST, PREF_MODE, PREF_NOR_WEIGHT, PREF_SEL_ONLY, PREF_EDGE_USERS, PREF_MIRROR_WEIGHTS, PREF_FLIP_NAMES, PREF_CREATE_FLIP_NAMES)
|
||||
print 'Mirror done in %.6f sec.' % (sys.time()-t)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
Loading…
Reference in New Issue
Block a user