# ***** BEGIN GPL LICENSE BLOCK ***** # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- import Blender import BPyMesh_redux # seperated because of its size. # reload(BPyMesh_redux) redux= BPyMesh_redux.redux # python 2.3 has no reversed() iterator. this will only work on lists and tuples try: reversed except: def reversed(l): return l[::-1] # If python version is less than 2.4, try to get set stuff from module try: set except: try: from sets import Set as set except: set= None def meshWeight2List(me): ''' Takes a mesh and return its group names and a list of lists, one list per vertex. aligning the each vert list with the group names, each list contains float value for the weight. These 2 lists can be modified and then used with list2MeshWeight to apply the changes. ''' # Clear the vert group. groupNames= me.getVertGroupNames() len_groupNames= len(groupNames) if not len_groupNames: # no verts? return a vert aligned empty list return [[] for i in xrange(len(me.verts))] else: vWeightList= [[0.0]*len_groupNames for i in xrange(len(me.verts))] for group_index, group in enumerate(groupNames): for vert_index, weight in me.getVertsFromGroup(group, 1): # (i,w) tuples. vWeightList[vert_index][group_index]= weight # removed this because me may be copying teh vertex groups. #for group in groupNames: # me.removeVertGroup(group) return groupNames, vWeightList def list2MeshWeight(me, groupNames, vWeightList): ''' Takes a list of groups and a list of vertex Weight lists as created by meshWeight2List and applys it to the mesh.''' if len(vWeightList) != len(me.verts): raise 'Error, Lists Differ in size, do not modify your mesh.verts before updating the weights' # Clear the vert group. currentGroupNames= me.getVertGroupNames() for group in currentGroupNames: me.removeVertGroup(group) # messes up the active group. # Add clean unused vert groupNames back currentGroupNames= me.getVertGroupNames() for group in groupNames: me.addVertGroup(group) add_ = Blender.Mesh.AssignModes.ADD vertList= [None] for i, v in enumerate(me.verts): vertList[0]= i for group_index, weight in enumerate(vWeightList[i]): if weight: try: me.assignVertsToGroup(groupNames[group_index], vertList, min(1, max(0, weight)), add_) except: pass # vert group is not used anymore. me.update() def meshWeight2Dict(me): ''' Takes a mesh and return its group names and a list of dicts, one dict per vertex. using the group as a key and a float value for the weight. These 2 lists can be modified and then used with dict2MeshWeight to apply the changes. ''' vWeightDict= [dict() for i in xrange(len(me.verts))] # Sync with vertlist. # Clear the vert group. groupNames= me.getVertGroupNames() for group in groupNames: for vert_index, weight in me.getVertsFromGroup(group, 1): # (i,w) tuples. vWeightDict[vert_index][group]= weight # removed this because me may be copying teh vertex groups. #for group in groupNames: # me.removeVertGroup(group) return groupNames, vWeightDict def dict2MeshWeight(me, groupNames, vWeightDict): ''' Takes a list of groups and a list of vertex Weight dicts as created by meshWeight2Dict and applys it to the mesh.''' if len(vWeightDict) != len(me.verts): raise 'Error, Lists Differ in size, do not modify your mesh.verts before updating the weights' # Clear the vert group. currentGroupNames= me.getVertGroupNames() for group in currentGroupNames: if group not in groupNames: me.removeVertGroup(group) # messes up the active group. else: me.removeVertsFromGroup(group) # Add clean unused vert groupNames back currentGroupNames= me.getVertGroupNames() for group in groupNames: if group not in currentGroupNames: me.addVertGroup(group) add_ = Blender.Mesh.AssignModes.ADD vertList= [None] for i, v in enumerate(me.verts): vertList[0]= i for group, weight in vWeightDict[i].iteritems(): try: me.assignVertsToGroup(group, vertList, min(1, max(0, weight)), add_) except: pass # vert group is not used anymore. 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) new_wdict[flipname]= weight return new_wdict, groupNames def mesh2linkedFaces(me): ''' Splits the mesh into connected parts, these parts are returned as lists of faces. used for seperating cubes from other mesh elements in the 1 mesh ''' # Build vert face connectivity vert_faces= [[] for i in xrange(len(me.verts))] for f in me.faces: for v in f: vert_faces[v.index].append(f) # sort faces into connectivity groups face_groups= [[f] for f in me.faces] face_mapping = range(len(me.faces)) # map old, new face location # Now clump faces iterativly ok= True while ok: ok= False for i, f in enumerate(me.faces): mapped_index= face_mapping[f.index] mapped_group= face_groups[mapped_index] for v in f: for nxt_f in vert_faces[v.index]: if nxt_f != f: nxt_mapped_index= face_mapping[nxt_f.index] # We are not a part of the same group if mapped_index != nxt_mapped_index: ok= True # Assign mapping to this group so they all map to this group for grp_f in face_groups[nxt_mapped_index]: face_mapping[grp_f.index] = mapped_index # Move faces into this group mapped_group.extend(face_groups[nxt_mapped_index]) # remove reference to the list face_groups[nxt_mapped_index]= None # return all face groups that are not null # this is all the faces that are connected in their own lists. return [fg for fg in face_groups if fg] def getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=True, scn=None): ''' ob - the object that you want to get the mesh from container_mesh - a Blender.Mesh type mesh that is reused to avoid a new datablock per call to getMeshFromObject apply_modifiers - if enabled, subsurf bones etc. will be applied to the returned mesh. disable to get a copy of the mesh. vgroup - For mesh objects only, apply the vgroup to the the copied mesh. (slower) scn - Scene type. avoids getting the current scene each time getMeshFromObject is called. Returns Mesh or None ''' if not scn: scn= Blender.Scene.GetCurrent() if not container_mesh: mesh = Blender.Mesh.New() else: mesh= container_mesh mesh.verts= None type = ob.getType() dataname = ob.getData(1) tempob= None if apply_modifiers or type != 'Mesh': try: mesh.getFromObject(ob) except: return None else: ''' Dont apply modifiers, copy the mesh. So we can transform the data. its easiest just to get a copy of the mesh. ''' tempob= Blender.Object.New('Mesh') tempob.shareFrom(ob) scn.link(tempob) mesh.getFromObject(tempob) scn.unlink(tempob) if type == 'Mesh': if vgroups: if tempob==None: tempob= Blender.Object.New('Mesh') tempob.link(mesh) try: # Copy the influences if possible. groupNames, vWeightDict= meshWeight2Dict(ob.getData(mesh=1)) dict2MeshWeight(mesh, groupNames, vWeightDict) except: # if the modifier changes the vert count then it messes it up for us. pass return mesh def faceRayIntersect(f, orig, dir): ''' Returns face, side Side is the side of a quad we intersect. side 0 == 0,1,2 side 1 == 0,2,3 ''' f_v= f.v isect= Blender.Mathutils.Intersect(f_v[0].co, f_v[1].co, f_v[2].co, dir, orig, 1) # 1==clip if isect: return isect, 0 if len(f_v)==4: isect= Blender.Mathutils.Intersect(f_v[0].co, f_v[2].co, f_v[3].co, dir, orig, 1) # 1==clip if isect: return isect, 1 return False, 0 def pickMeshRayFace(me, orig, dir): best_dist= 1000000 best_isect= best_side= best_face= None for f in me.faces: isect, side= faceRayIntersect(f, orig, dir) if isect: dist= (isect-orig).length if disti2: i1,i2= i2,i1 return i1, i2 def edge_face_users(me): ''' Takes a mesh and returns a list aligned with the meshes edges. Each item is a list of the faces that use the edge would be the equiv for having ed.face_users as a property ''' face_edges_dict= dict([(sorted_edge_indicies(ed), (ed.index, [])) for ed in me.edges]) for f in me.faces: fvi= [v.index for v in f.v]# face vert idx's for i in xrange(len(f)): i1= fvi[i] i2= fvi[i-1] if i1>i2: i1,i2= i2,i1 face_edges_dict[i1,i2][1].append(f) face_edges= [None] * len(me.edges) for ed_index, ed_faces in face_edges_dict.itervalues(): face_edges[ed_index]= ed_faces return face_edges def face_edges(me): ''' Returns a list alligned to the meshes faces. each item is a list of lists: that is face_edges -> face indicies face_edges[i] -> list referencs local faces v indicies 1,2,3 &| 4 face_edges[i][j] -> list of faces that this edge uses. crap this is tricky to explain :/ ''' face_edges= [ [None] * len(f) for f in me.faces ] face_edges_dict= dict([(sorted_edge_indicies(ed), []) for ed in me.edges]) for fidx, f in enumerate(me.faces): fvi= [v.index for v in f.v]# face vert idx's for i in xrange(len(f)): i1= fvi[i] i2= fvi[i-1] if i1>i2: i1,i2= i2,i1 edge_face_users= face_edges_dict[i1,i2] edge_face_users.append(f) face_edges[fidx][i]= edge_face_users return face_edges def facesPlanerIslands(me): DotVecs= Blender.Mathutils.DotVecs def roundvec(v): return round(v[0], 4), round(v[1], 4), round(v[2], 4) face_props= [(cent, no, roundvec(no), DotVecs(cent, no)) for f in me.faces for no, cent in ((f.no, f.cent),)] face_edge_users= face_edges(me) islands= [] used_faces= [0] * len(me.faces) while True: new_island= False for i, used_val in enumerate(used_faces): if used_val==0: island= [i] new_island= True used_faces[i]= 1 break if not new_island: break island_growing= True while island_growing: island_growing= False for fidx1 in island[:]: if used_faces[fidx1]==1: used_faces[fidx1]= 2 face_prop1= face_props[fidx1] for ed in face_edge_users[fidx1]: for f2 in ed: fidx2= f2.index if fidx1 != fidx2 and used_faces[fidx2]==0: island_growing= True face_prop2= face_props[fidx2] # normals are the same? if face_prop1[2]==face_prop2[2]: if abs(face_prop1[3] - DotVecs(face_prop1[1], face_prop2[0])) < 0.000001: used_faces[fidx2]= 1 island.append(fidx2) islands.append([me.faces[i] for i in island]) return islands def facesUvIslands(me, PREF_IMAGE_DELIMIT=True): DotVecs= Blender.Mathutils.DotVecs def roundvec(v): return round(v[0], 4), round(v[1], 4) if not me.faceUV: return [ list(me.faces), ] # make a list of uv dicts face_uvs= [ [roundvec(uv) for uv in f.uv] for f in me.faces] # key - face uv || value - list of face idxs uv_connect_dict= dict([ (uv, [] ) for f_uvs in face_uvs for uv in f_uvs]) for i, f_uvs in enumerate(face_uvs): for uv in f_uvs: # loops through rounded uv values uv_connect_dict[uv].append(i) islands= [] used_faces= [0] * len(me.faces) while True: new_island= False for i, used_val in enumerate(used_faces): if used_val==0: island= [i] new_island= True used_faces[i]= 1 break if not new_island: break island_growing= True while island_growing: island_growing= False for fidx1 in island[:]: if used_faces[fidx1]==1: used_faces[fidx1]= 2 for uv in face_uvs[fidx1]: for fidx2 in uv_connect_dict[uv]: if fidx1 != fidx2 and used_faces[fidx2]==0: if not PREF_IMAGE_DELIMIT or me.faces[fidx1].image==me.faces[fidx2].image: island_growing= True used_faces[fidx2]= 1 island.append(fidx2) islands.append([me.faces[i] for i in island]) return islands #def faceUvBounds(me, faces= None): def facesUvRotate(me, deg, faces= None, pivot= (0,0)): ''' Faces can be None an all faces will be used pivot is just the x/y well rotated about positive deg value for clockwise rotation ''' if faces==None: faces= me.faces pivot= Blender.Mathutils.Vector(pivot) rotmat= Blender.Mathutils.RotationMatrix(-deg, 2) for f in faces: f.uv= [((uv-pivot)*rotmat)+pivot for uv in f.uv] def facesUvScale(me, sca, faces= None, pivot= (0,0)): ''' Faces can be None an all faces will be used pivot is just the x/y well rotated about sca can be wither an int/float or a vector if you want to scale x/y seperately. a sca or (1.0, 1.0) will do nothing. ''' def vecmulti(v1,v2): '''V2 is unchanged''' v1[:]= (v1.x*v2.x, v1.y*v2.y) return v1 sca= Blender.Mathutils.Vector(sca) if faces==None: faces= me.faces pivot= Blender.Mathutils.Vector(pivot) for f in faces: f.uv= [vecmulti(uv-pivot, sca)+pivot for uv in f.uv] def facesUvTranslate(me, tra, faces= None, pivot= (0,0)): ''' Faces can be None an all faces will be used pivot is just the x/y well rotated about ''' if faces==None: faces= me.faces tra= Blender.Mathutils.Vector(tra) for f in faces: f.uv= [uv+tra for uv in f.uv] def edgeFaceUserCount(me, faces= None): ''' Return an edge aligned list with the count for all the faces that use that edge. - can spesify a subset of the faces, so only those will be counted. ''' if faces==None: faces= me.faces max_vert= len(me.verts) else: # find the lighest vert index pass edge_users= [0] * len(me.edges) edges_idx_dict= dict([(sorted_edge_indicies(ed), ed.index) for ed in me.edges]) for f in faces: fvi= [v.index for v in f.v]# face vert idx's for i in xrange(len(f)): i1= fvi[i] i2= fvi[i-1] if i1>i2: i1,i2= i2,i1 edge_users[edges_idx_dict[i1,i2]] += 1 return edge_users #============================================================================# # Takes a face, and a pixel x/y on the image and returns a worldspace x/y/z # # will return none if the pixel is not inside the faces UV # #============================================================================# def getUvPixelLoc(face, pxLoc, img_size = None, uvArea = None): TriangleArea= Blender.Mathutils.TriangleArea Vector= Blender.Mathutils.Vector if not img_size: w,h = face.image.size else: w,h= img_size scaled_uvs= [Vector(uv.x*w, uv.y*h) for uv in f.uv] if len(scaled_uvs)==3: indicies= ((0,1,2),) else: indicies= ((0,1,2), (0,2,3)) for fidxs in indicies: for i1,i2,i3 in fidxs: # IS a point inside our triangle? # UVArea could be cached? uv_area = TriangleArea(scaled_uvs[i1], scaled_uvs[i2], scaled_uvs[i3]) area0 = TriangleArea(pxLoc, scaled_uvs[i2], scaled_uvs[i3]) area1 = TriangleArea(pxLoc, scaled_uvs[i1], scaled_uvs[i3]) area2 = TriangleArea(pxLoc, scaled_uvs[i1], scaled_uvs[i2]) if area0 + area1 + area2 > uv_area + 1: # 1 px bleed/error margin. pass # if were a quad the other side may contain the pixel so keep looking. else: # We know the point is in the tri area0 /= uv_area area1 /= uv_area area2 /= uv_area # New location return Vector(\ face.v[i1].co[0]*area0 + face.v[i2].co[0]*area1 + face.v[i3].co[0]*area2,\ face.v[i1].co[1]*area0 + face.v[i2].co[1]*area1 + face.v[i3].co[1]*area2,\ face.v[i1].co[2]*area0 + face.v[i2].co[2]*area1 + face.v[i3].co[2]*area2\ ) return None type_tuple= type( (0,) ) type_list= type( [] ) # Used for debugging ngon """ def draw_loops(loops): me= Blender.Mesh.New() for l in loops: #~ me= Blender.Mesh.New() i= len(me.verts) me.verts.extend([v[0] for v in l]) try: me.verts[0].sel= 1 except: pass me.edges.extend([ (j-1, j) for j in xrange(i+1, len(me.verts)) ]) # Close the edge? me.edges.extend((i, len(me.verts)-1)) #~ ob= Blender.Object.New('Mesh') #~ ob.link(me) #~ scn= Blender.Scene.GetCurrent() #~ scn.link(ob) #~ ob.Layers= scn.Layers #~ ob.sel= 1 # Fill #fill= Blender.Mathutils.PolyFill(loops) #me.faces.extend(fill) ob= Blender.Object.New('Mesh') ob.link(me) scn= Blender.Scene.GetCurrent() scn.link(ob) ob.Layers= scn.Layers ob.sel= 1 Blender.Window.RedrawAll() """ def ngon(from_data, indices, PREF_FIX_LOOPS= True): ''' Takes a polyline of indices (fgon) and returns a list of face indicie lists. Designed to be used for importers that need indices for an fgon to create from existing verts. from_data: either a mesh, or a list/tuple of vectors. indices: a list of indicies to use this list is the ordered closed polyline to fill, and can be a subset of the data given. PREF_FIX_LOOPS: If this is enabled polylines that use loops to make multiple polylines are delt with correctly. ''' if not set: # Need sets for this, otherwise do a normal fill. PREF_FIX_LOOPS= False Vector= Blender.Mathutils.Vector if not indices: return [] # return [] def rvec(co): return round(co.x, 6), round(co.y, 6), round(co.z, 6) def mlen(co): return abs(co[0])+abs(co[1])+abs(co[2]) # manhatten length of a vector, faster then length def vert_treplet(v, i): return v, rvec(v), i, mlen(v) def ed_key_mlen(v1, v2): if v1[3] > v2[3]: return v2[1], v1[1] else: return v1[1], v2[1] if not PREF_FIX_LOOPS: ''' Normal single concave loop filling ''' if type(from_data) in (type_tuple, type_list): verts= [Vector(from_data[i]) for ii, i in enumerate(indices)] else: verts= [from_data.verts[i].co for ii, i in enumerate(indices)] for i in xrange(len(verts)-1, 0, -1): # same as reversed(xrange(1, len(verts))): if verts[i][1]==verts[i-1][0]: verts.pop(i-1) fill= Blender.Geometry.PolyFill([verts]) else: ''' Seperate this loop into multiple loops be finding edges that are used twice This is used by lightwave LWO files a lot ''' if type(from_data) in (type_tuple, type_list): verts= [vert_treplet(Vector(from_data[i]), ii) for ii, i in enumerate(indices)] else: verts= [vert_treplet(from_data.verts[i].co, ii) for ii, i in enumerate(indices)] edges= [(i, i-1) for i in xrange(len(verts))] if edges: edges[0]= (0,len(verts)-1) if not verts: return [] edges_used= set() edges_doubles= set() # We need to check if any edges are used twice location based. for ed in edges: edkey= ed_key_mlen(verts[ed[0]], verts[ed[1]]) if edkey in edges_used: edges_doubles.add(edkey) else: edges_used.add(edkey) # Store a list of unconnected loop segments split by double edges. # will join later loop_segments= [] v_prev= verts[0] context_loop= [v_prev] loop_segments= [context_loop] for v in verts: if v!=v_prev: # Are we crossing an edge we removed? if ed_key_mlen(v, v_prev) in edges_doubles: context_loop= [v] loop_segments.append(context_loop) else: if context_loop and context_loop[-1][1]==v[1]: #raise "as" pass else: context_loop.append(v) v_prev= v # Now join loop segments def join_seg(s1,s2): if s2[-1][1]==s1[0][1]: # s1,s2= s2,s1 elif s1[-1][1]==s2[0][1]: pass else: return False # If were stuill here s1 and s2 are 2 segments in the same polyline s1.pop() # remove the last vert from s1 s1.extend(s2) # add segment 2 to segment 1 if s1[0][1]==s1[-1][1]: # remove endpoints double s1.pop() s2[:]= [] # Empty this segment s2 so we dont use it again. return True joining_segments= True while joining_segments: joining_segments= False segcount= len(loop_segments) for j in xrange(segcount-1, -1, -1): #reversed(xrange(segcount)): seg_j= loop_segments[j] if seg_j: for k in xrange(j-1, -1, -1): # reversed(xrange(j)): if not seg_j: break seg_k= loop_segments[k] if seg_k and join_seg(seg_j, seg_k): joining_segments= True loop_list= loop_segments for verts in loop_list: while verts and verts[0][1]==verts[-1][1]: verts.pop() loop_list= [verts for verts in loop_list if len(verts)>2] # DONE DEALING WITH LOOP FIXING # vert mapping vert_map= [None]*len(indices) ii=0 for verts in loop_list: if len(verts)>2: for i, vert in enumerate(verts): vert_map[i+ii]= vert[2] ii+=len(verts) fill= Blender.Geometry.PolyFill([ [v[0] for v in loop] for loop in loop_list ]) #draw_loops(loop_list) #raise 'done loop' # map to original indicies fill= [[vert_map[i] for i in reversed(f)] for f in fill] if not fill: print 'Warning Cannot scanfill, fallback on a triangle fan.' fill= [ [0, i-1, i] for i in xrange(2, len(indices)) ] else: # Use real scanfill. # See if its flipped the wrong way. flip= None for fi in fill: if flip != None: break for i, vi in enumerate(fi): if vi==0 and fi[i-1]==1: flip= False break elif vi==1 and fi[i-1]==0: flip= True break if not flip: for i, fi in enumerate(fill): fill[i]= tuple([ii for ii in reversed(fi)]) return fill # EG ''' scn= Scene.GetCurrent() me = scn.getActiveObject().getData(mesh=1) ind= [v.index for v in me.verts if v.sel] # Get indices indices = ngon(me, ind) # fill the ngon. # Extand the faces to show what the scanfill looked like. print len(indices) me.faces.extend([[me.verts[ii] for ii in i] for i in indices]) ''' def meshCalcNormals(me, vertNormals=None): ''' takes a mesh and returns very high quality normals 1 normal per vertex. The normals should be correct, indipendant of topology vertNormals - a list of vectors at least as long as the number of verts in the mesh ''' Ang= Blender.Mathutils.AngleBetweenVecs Vector= Blender.Mathutils.Vector SMALL_NUM=0.000001 # Weight the edge normals by total angle difference # EDGE METHOD if not vertNormals: vertNormals= [ Vector() for v in xrange(len(me.verts)) ] else: for v in vertNormals: v.zero() edges={} for f in me.faces: for i in xrange(len(f)): i1, i2= f.v[i].index, f.v[i-1].index if i11: totAngDiff=0 for j in xrange(len_fnos-1, -1, -1): # same as reversed(xrange(...)) for k in xrange(j-1, -1, -1): # same as reversed(xrange(...)) #print j,k try: totAngDiff+= (Ang(fnos[j], fnos[k])) # /180 isnt needed, just to keeop the vert small. except: pass # Zero length face # print totAngDiff if totAngDiff > SMALL_NUM: ''' average_no= Vector() for no in fnos: average_no+=no ''' average_no= reduce(lambda a,b: a+b, fnos, Vector()) fnos.append(average_no*totAngDiff) # average no * total angle diff #else: # fnos[0] else: fnos.append(fnos[0]) for ed, v in edges.iteritems(): vertNormals[ed[0]]+= v[-1] vertNormals[ed[1]]+= v[-1] for i, v in enumerate(me.verts): v.no= vertNormals[i] def pointInsideMesh(ob, pt): Intersect = Blender.Mathutils.Intersect # 2 less dict lookups. Vector = Blender.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)==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) == 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 obSpacePt = pt*ob.matrixWorld.copy().invert() 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 # NMesh wrapper Vector= Blender.Mathutils.Vector class NMesh(object): __slots__= 'verts', 'faces', 'edges', 'faceUV', 'materials', 'realmesh' def __init__(self, mesh): ''' This is an NMesh wrapper that mesh is an Mesh as returned by Blender.Mesh.New() This class wraps NMesh like access into Mesh Running NMesh.update() - with this wrapper, Will update the realmesh. ''' self.verts= [] self.faces= [] self.edges= [] self.faceUV= False self.materials= [] self.realmesh= mesh def addFace(self, nmf): self.faces.append(nmf) def Face(self, v=[]): return NMFace(v) def Vert(self, x,y,z): return NMVert(x,y,z) def hasFaceUV(self, flag): if flag: self.faceUV= True else: self.faceUV= False def addMaterial(self, mat): self.materials.append(mat) def update(self, recalc_normals=False): # recalc_normals is dummy mesh= self.realmesh mesh.verts= None # Clears the # Add in any verts from faces we may have not added. for nmf in self.faces: for nmv in nmf.v: if nmv.index==-1: nmv.index= len(self.verts) self.verts.append(nmv) mesh.verts.extend([nmv.co for nmv in self.verts]) for i, nmv in enumerate(self.verts): nmv.index= i mv= mesh.verts[i] mv.sel= nmv.sel good_faces= [nmf for nmf in self.faces if len(nmf.v) in (3,4)] #print len(good_faces), 'AAA' #mesh.faces.extend([nmf.v for nmf in self.faces]) mesh.faces.extend([[mesh.verts[nmv.index] for nmv in nmf.v] for nmf in good_faces]) if len(mesh.faces): if self.faceUV: mesh.faceUV= 1 #for i, nmf in enumerate(self.faces): for i, nmf in enumerate(good_faces): mf= mesh.faces[i] if self.faceUV: if len(nmf.uv) == len(mf.v): mf.uv= [Vector(uv[0], uv[1]) for uv in nmf.uv] if len(nmf.col) == len(mf.v): for c, i in enumerate(mf.col): c.r, c.g, c.b= nmf.col[i].r, nmf.col[i].g, nmf.col[i].b if nmf.image: mf.image= nmf.image mesh.materials= self.materials[:16] class NMVert(object): __slots__= 'co', 'index', 'no', 'sel', 'uvco' def __init__(self, x,y,z): self.co= Vector(x,y,z) self.index= None # set on appending. self.no= Vector(0,0,1) # dummy self.sel= 0 self.uvco= None class NMFace(object): __slots__= 'col', 'flag', 'hide', 'image', 'mat', 'materialIndex', 'mode', 'normal',\ 'sel', 'smooth', 'transp', 'uv', 'v' def __init__(self, v=[]): self.col= [] self.flag= 0 self.hide= 0 self.image= None self.mat= 0 # materialIndex needs support too. self.mode= 0 self.normal= Vector(0,0,1) self.uv= [] self.sel= 0 self.smooth= 0 self.transp= 0 self.uv= [] self.v= [] # a list of nmverts. class NMCol(object): __slots__ = 'r', 'g', 'b', 'a' def __init__(self): self.r= 255 self.g= 255 self.b= 255 self.a= 255 ''' # verts_split= [dict() for i in xrange(len(me.verts))] tot_verts= 0 for f in me.faces: f_uv= f.uv for i, v in enumerate(f.v): vert_index= v.index # mesh index vert_dict= verts_split[vert_index] # get the dict for this vert uv= f_uv[i] # now we have the vert and the face uv well make a unique dict. vert_key= v.x, v.y, v.x, uv.x, uv.y # ADD IMAGE NAME HETR IF YOU WANT TO SPLIT BY THAT TOO value= vert_index, tot_verts # ADD WEIGHT HERE IF YOU NEED. try: vert_dict[vert_key] # if this is missing it will fail. except: # this stores a mapping between the split and orig vert indicies vert_dict[vert_key]= value tot_verts+= 1 # a flat list of split verts - can add custom weight data here too if you need split_verts= [None]*tot_verts for vert_split_dict in verts_split: for key, value in vert_split_dict.iteritems(): local_index, split_index= value split_verts[split_index]= key # split_verts - Now you have a list of verts split by their UV. '''