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
@ -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__':
|
||||||
|
Loading…
Reference in New Issue
Block a user