diff --git a/release/scripts/bpymodules/BPyMesh.py b/release/scripts/bpymodules/BPyMesh.py index 140613ad873..b1a5765bf22 100644 --- a/release/scripts/bpymodules/BPyMesh.py +++ b/release/scripts/bpymodules/BPyMesh.py @@ -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 + + + + + diff --git a/release/scripts/mesh_mirror_tool.py b/release/scripts/mesh_mirror_tool.py index b86e429f10c..23c30324041 100644 --- a/release/scripts/mesh_mirror_tool.py +++ b/release/scripts/mesh_mirror_tool.py @@ -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 lRight 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__':