#!BPY """ Name: 'MegaBool r2' Blender: 234 Group: 'Object' Tooltip: 'Perform boolean operations on meshes and get nice results' """ ################################################################### # # # Experimental New Mesh Boolean Operations # # # # (C) 2004 Theodore K Schundler (tschundler)(@)(scu)(edu) # # FOR TESTING ONLY, not for redistribution # # Lastest version available for from: # # http://astro.scu.edu/~ted/oss/blender/boolean # # # # USAGE: # # -By Menu- # # * Put this file in ~/.blender/scripts/ # # * Select two objects, then choose "MegaBool" from the # # scripts menu. # # -By Running the script- # # * Load this file into blender's internal text editor # # * Select two objects, then press Alt+P in the test editor # # # # OPERATIONS: # # On execution of the script, there are four operations # # * Intersect - Intersection of the two meshes (logical AND) # # * Union - Union of the two meshes (logical OR) # # * Difference - First selected mesh - Last selected mesh # # * Cookie Cutter - First mesh's faces are subdivided where # # they intersect with the second mesh # # After the script executes, all "inside" vertices are selected # # # # IMPORTANT NOTES: # # This script uses Blender's internal scan fill to fill faces. # # It does not have an official python interface *yet*, so I # # quickly hacked one together. A patch to the bf-blender cvs code # # base is available at the URL above. Patch your blender source # # and recompile. Then this script will run. Without the patched # # blender this script will fail on "fillVertLoops". # # Like any boolean algorith, normals on the source meshes must # # be all facing outside. To fix normals, select a mesh, enter # # edit mode, select all verticies, and press Ctrl+N. Also, all # # intersections must create closed loops. (Basically the meshes # # should be closed.) If you find a set of meshes that do not work # # send me the .blend file and an explaination of the problem. # # # # TO DO: # # * After the operation, should probably translate results, so # # the mesh isn't centered around <0,0,0>, use one of the base # # meshes instead # # * Set materials, UVs, etc., on new mesh # # * Use correct normals on new mesh # ################################################################### # # # Date: Updates: # # 16-Aug-2004 Initial Revision - working(?) & ready to go # # 17-Aug-2004 Fixed some bugs based on incorrect assumptions. # # that means even more computation. Also made more # # use of the traversalCookies to speed up a some # # sections # # # ################################################################### import Blender from Blender import NMesh from Blender import Object from Blender import Draw from Blender import Mathutils from Blender.Mathutils import * traversalCookie=0 #Mathutils doesn't like NMesh data, this makes it happy def TransList(o): l=[] for a in o: l.append(a) return l class CCVert: def __init__(self,nmVert,ccObject): global traversalCookie self.parentObject=ccObject if nmVert: self.counterPart=nmVert v=TransList(nmVert.co) vec=Mathutils.Vector(v) vec.resize4D() vec.w=1 self.tco=Mathutils.VecMultMat(vec,ccObject.matrix) #self.tco=Mathutils.MatMultVex(vec,ccObject.matrix) self.tco.resize3D() #print vec,"->",self.tco else: self.tco=[0,0,0] self.inside=0 self.outside=0 self.neither=0 self.traversalCookie=traversalCookie self.cutBy=[] self.cutFrom=[] self.edgeWith=[] #self.newIndex=0 self.newCounterpart=0 self.edgePos=0 self.used=0 #print "\tAdded Vertex: ", self.tco def DetermineSide(self,traversalCookie): return class CCEdge: def __init__(self,v1,v2,ccObject): global traversalCookie self.parentObject=ccObject ccObject.edges.append(self) self.index=len(ccObject.edges)-1 self.v=[v1,v2] #print "\t\tNew Edge[%d]: (v%d -> v%d)"%(self.index,v1.counterPart.index,v2.counterPart.index) v1.edgeWith.append([v2,self]) v2.edgeWith.append([v1,self]) self.faces=[] self.bb=[[v1.tco[0],v1.tco[1],v1.tco[2]],[v2.tco[0],v2.tco[1],v2.tco[2]]] for i in range(3): if self.bb[0][i]>self.bb[1][i]: t=self.bb[0][i] self.bb[0][i]=self.bb[1][i] self.bb[1][i]=t self.inside=0 self.outside=0 self.neither=0 self.traversalCookie=traversalCookie self.real=[] def realize(self,newEdges,sweep): global traversalCookie v=self.v if len(v)==2: if sweep==1: return ne=CCEdge(v[0],v[1],self.parentObject) traversalCookie=traversalCookie+1 FindVertexSide(v[0],traversalCookie) FindVertexSide(v[1],traversalCookie) if (v[0].outside!=v[0].inside): ne.outside=v[0].outside ne.inside=v[0].inside ne.neither=v[0].neither else: ne.outside=v[1].outside ne.inside=v[1].inside ne.neither=v[1].neither #v[0].edgeWith.append([v[1],self]) #v[1].edgeWith.append([v[0],self]) newEdges.append(ne) else: if sweep==2: return #if len(self.real)>1: # print "teh leet" # for e in self.real: # newEdges.append(e) # return vec=v[1].tco-v[0].tco nv=[] ep=[] for vtx in v: #vtx.edgePos=DotVecs(vtx.tco,vec) vtx.edgePos=(vtx.tco-v[0].tco).length ep.append(vtx.edgePos) nv.append(vtx) ep.sort() for vtx in v: nv[ep.index(vtx.edgePos)]=vtx #print "sort edges",nv[0].outside,nv[0].inside outside=nv[0].outside nv[0].inside=1-outside for n in range(1,len(nv)): ne=CCEdge(nv[n-1],nv[n],self.parentObject) #print self,"--",outside,"--",ne ne.outside=outside ne.inside=1-ne.outside nv[n-1].outside=nv[n-1].outside|outside nv[n-1].inside=nv[n-1].inside|ne.inside nv[n].outside=outside nv[n].inside=1-outside newEdges.append(ne) self.real.append(ne) outside=1-outside #add a vertex, and figure out if any others on the edge need to ne redefined as inside or outside def AddVert(self,v,normal): insideVec=[] insideL=(self.v[1].tco-self.v[0].tco).length outsideVec=[] outsideL=(self.v[1].tco-self.v[0].tco).length for vx in self.v: vec=vx.tco-v.tco if DotVecs(vec,normal)>0: if vec.lengthself.bb[1][i]: self.bb[1][i]=v[i] #Add Edges self.addEdge(nmFace.v[0],nmFace.v[1]) self.addEdge(nmFace.v[1],nmFace.v[2]) if len(nmFace.v)==3: self.addEdge(nmFace.v[2],nmFace.v[0]) else: self.addEdge(nmFace.v[2],nmFace.v[3]) self.addEdge(nmFace.v[3],nmFace.v[0]) def GetVertexSet(self): vs=[] for e in self.edges: for v in e.v: try: vs.index(v) except: vs.append(v) for v in self.extraVerts: try: vs.index(v) except: #try: #v.cutBy.index(self) #except: #print self,v vs.append(v) return vs class CCObject: def __init__(self,blenderObject): self.counterPart=blenderObject print "Adding Object %s"%blenderObject.getName() #setup variables self.verts=[] self.faces=[] self.edges=[] #store some matrices for future use omtx=blenderObject.getMatrix() #print "OMTX:",omtx self.matrix=Mathutils.Matrix(TransList(omtx[0]),TransList(omtx[1]),TransList(omtx[2]),TransList(omtx[3])) #self.matrix.transpose() #print "SELF:",self print "\tAdding verts" #build up vertex list m=NMesh.GetRawFromObject(blenderObject.getName()) self.nmesh=m for v in m.verts: self.verts.append([]) #print "vtx: ",len(self.verts) for v in m.verts: self.verts[v.index]=CCVert(v,self) print "\tAdding Faces & Edges" #build up faces & edges for f in m.faces: if len(f.v)>2: self.faces.append(CCFace(f,self)) def TestPointInFaceInEdge(v,f,ed): ev=ed.v[1].tco-ed.v[0].tco vA=v-ed.v[0].tco d=vA.length d=DotVecs(vA,ev) if d<0 or vA.length>ev.length: return 0 for e in f.edges: cp=CrossVecs(f.normal,e.v[1].tco-e.v[0].tco) for ew in e.v[0].edgeWith: if ew[0]!=e.v[1]: try: f.edges.index(ew[1]) v2=ew[0].tco except: 1 d=DotVecs(cp,v2-e.v[0].tco) cp=cp*d cp.normalize() s=DotVecs((v-e.v[0].tco),cp) if s<0: return 0 return 1 def EdgeLoopVxLoop(el): global traversalCookie traversalCookie=traversalCookie+1 vxl=[] for e in el: for v in e.v: if v.traversalCookie10: print "APEX!!! -- ", len(v.edgeWith) for ew in v.edgeWith: if ew[0].traversalCookie10: print "Apex Outside" v.outside=1 return sv elif sv==0: if len(v.edgeWith)>10: print "Apex Inside" v.inside=1 return sv #else: # print "NEITHER@!!!!!!!!!!!!!!!!!!!!!!!" # v.neither=1 #if loop failed, this must be floating out in space somewhere #v.neither=1 if len(v.edgeWith)>10: print "APEX died" else: print "died...",tc return -1 def FindEdgeSide(e,tc): if e.inside==0 and e.outside==0 and e.neither==0: v1=FindVertexSide(e.v[0],tc) v2=FindVertexSide(e.v[1],tc) if v1<0 or v2<0: e.outside=1 return 1 #print "findlike ",v1,"--",v2 if v1==v2: if v1==1: e.outside=1 elif v1==0: e.inside=1 else: e.neither=1 else: e.neither=1 if e.neither==1: return 0 elif e.inside==1 and e.outside==1: return 3 elif e.inside==1: return 2 else: return 1 def FindVertex(vtx,vxlist): for v in vxlist: m=0 for n in range(3): if v.tco[n]==vtx[n]: m=m+1 if m==3: return v print "Failed to find vertex ",vtx return [] def SortVertexLoop(vs,es): global traversalCookie #traversalCookie=traversalCookie+1 vx=[vs[0]] print "Sort ",len(vs)," verts" for n in range(0,len(vs)-1): #if n==len(vx): # print "SCRIPT FAILED!!!!" # return [] print "try ",n,":",vx[n].tco,", options: ",len(vx[n].edgeWith) for ew in vx[n].edgeWith: #print "\t",ew[0].tco try: vx.index(ew[0]) except: try: es.index(ew[1]) vs.index(ew[0]) vx.append(ew[0]) #ew[0].traversalCookie=traversalCookie break except: 1 return vx def MakeEdgeLoop(loop,edge,inside,outside,tc): for v in edge.v: for ew in v.edgeWith: if ew[1].inside>=inside and ew[1].outside>=outside: if ew[1].traversalCookie3 or 1: el=[] if (simple==0): #add edges of original face for e in f.edges: e.realize(el,1) for e in f.edges: e.realize(el,2) f.edges=el if (len(vs)==4 or len(vs)==3) and simple==1: if inside!=outside: for v in vs: if (v.outside3: MakeFace(self.nm,f,vs,inside,outside,1) print "Running Megabool!" objsel=Object.GetSelected() def TestBounds(b1,b2): #return 1 b0=[[0,0,0],[0,0,0]] for i in range(3): #Lower bound? b0[0][i]=b1[0][i] if b0[0][i]b2[1][i]: b0[1][i]=b2[1][i] if b0[1][i]