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:
Campbell Barton 2006-04-24 11:26:04 +00:00
parent 94ebee891c
commit 11586835da
2 changed files with 299 additions and 67 deletions

@ -56,6 +56,82 @@ def dict2MeshWeight(me, groupNames, vWeightDict):
me.update() 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): def getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=True, scn=None):
@ -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 #!BPY
""" """
Name: 'Mirror Snap Verts' Name: 'Mirror Verts Loc & Weight'
Blender: 241 Blender: 241
Group: 'Object' Group: 'Mesh'
Tooltip: 'Move verts so they snap to their mirrored locations.' Tooltip: 'Move verts so they snap to their mirrored locations.'
""" """
from Blender import Draw, Window, Scene, Mesh, Mathutils, sys from Blender import Draw, Window, Scene, Mesh, Mathutils, sys
import BPyMesh import BPyMesh
reload(BPyMesh)
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):
def mesh_mirror(me, PREF_MAX_DIST, PREF_MODE, PREF_NOR_WEIGHT, PREF_SEL_ONLY, PREF_EDGE_USERS):
''' '''
PREF_MAX_DIST, Maximum distance to test snapping verts. PREF_MAX_DIST, Maximum distance to test snapping verts.
PREF_MODE, 0:middle, 1: Left. 2:Right. PREF_MODE, 0:middle, 1: Left. 2:Right.
PREF_NOR_WEIGHT, use normal angle difference to weight distances. PREF_NOR_WEIGHT, use normal angle difference to weight distances.
PREF_SEL_ONLY, only snap the selection PREF_SEL_ONLY, only snap the selection
PREF_EDGE_USERS, match only verts with the same number of edge users. PREF_EDGE_USERS, match only verts with the same number of edge users.
PREF_MIRROR_LOCATION,
''' '''
is_editmode = Window.EditMode() # Exit Editmode. 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: for v in me.verts:
v.sel=1 v.sel=1
if PREF_EDGE_USERS: if PREF_EDGE_USERS:
edge_users= [0]*len(me.verts) edge_users= [0]*len(me.verts)
for ed in me.edges: for ed in me.edges:
@ -39,12 +41,9 @@ 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] 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] 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 Location*********************************************************#
if PREF_MIRROR_LOCATION:
mirror_pairs= [] mirror_pairs= []
# allign the negative with the positive. # allign the negative with the positive.
flipvec= Mathutils.Vector() flipvec= Mathutils.Vector()
@ -79,7 +78,6 @@ def mesh_mirror(me, PREF_MAX_DIST, PREF_MODE, PREF_NOR_WEIGHT, PREF_SEL_ONLY, PR
if i1 % 10 == 0: if i1 % 10 == 0:
Window.DrawProgressBar(0.8 * (i1/len_neg_vts), 'Mirror verts %i of %i' % (i1, len_neg_vts)) 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') 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. # 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. # de-selecting as we go. so we can makke sure not to mess it up.
@ -101,6 +99,77 @@ def mesh_mirror(me, PREF_MAX_DIST, PREF_MODE, PREF_NOR_WEIGHT, PREF_SEL_ONLY, PR
v1.sel= 0 v1.sel= 0
v2.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() me.update()
if is_editmode: Window.EditMode(1) if is_editmode: Window.EditMode(1)
@ -116,30 +185,48 @@ def main():
except: except:
Draw.PupMenu('Error, select a mesh as your active object') 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_MAX_DIST= Draw.Create(0.2)
PREF_MODE= Draw.Create(0) PREF_MODE= Draw.Create(0)
PREF_NOR_WEIGHT= Draw.Create(0.0) PREF_NOR_WEIGHT= Draw.Create(0.0)
PREF_SEL_ONLY= Draw.Create(1) PREF_SEL_ONLY= Draw.Create(1)
PREF_EDGE_USERS= Draw.Create(0) 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 = [\ pup_block = [\
('MaxDist:', PREF_MAX_DIST, 0.0, 1.0, 'Generate interpolated verts so closer vert weights can be copied.'),\ ('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.'),\ ('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'),\ ('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.'),\ ('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): if not Draw.PupBlock("Mirror mesh tool", pup_block):
return return
PREF_MIRROR_LOCATION= PREF_MIRROR_LOCATION.val
PREF_MAX_DIST= PREF_MAX_DIST.val PREF_MAX_DIST= PREF_MAX_DIST.val
PREF_MODE= PREF_MODE.val PREF_MODE= PREF_MODE.val
PREF_NOR_WEIGHT= PREF_NOR_WEIGHT.val PREF_NOR_WEIGHT= PREF_NOR_WEIGHT.val
PREF_SEL_ONLY= PREF_SEL_ONLY.val PREF_SEL_ONLY= PREF_SEL_ONLY.val
PREF_EDGE_USERS= PREF_EDGE_USERS.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() 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) print 'Mirror done in %.6f sec.' % (sys.time()-t)
if __name__ == '__main__': if __name__ == '__main__':