#!BPY """ Name: 'MegaBool' 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 # # # ################################################################### import Blender from Blender import NMesh from Blender import Object from Blender import Draw from Blender import Mathutils from Blender.Mathutils import * #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): 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=0 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): self.parentObject=ccObject ccObject.edges.append(self) self.index=ccObject.edges.index(self) 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=0 self.real=[] def realize(self,newEdges): v=self.v if len(v)==2: ne=CCEdge(v[0],v[1],self.parentObject) 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 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) 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 #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) #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): vxl=[] for e in el: for v in e.v: try: vxl.index(v) except: vxl.append(v) return SortVertexLoop(vxl,el) def FindVertexSide(v,tc): #print "fvs" if v.outside==1: return 1 elif v.inside==1: return 0 elif v.neither==1: print "Predetermined NEITHER!!!!!!" return -1 else: #don't know what side I'm on v.traversalCookie=tc if len(v.edgeWith)==0: print "DEAD POINT!!!!!!!!!!!!!!!" for ew in v.edgeWith: if ew[0].traversalCookie=inside and ew[1].outside>=outside: if ew[1].traversalCookie3: el=[] #add edges of original face for e in f.edges: e.realize(el) f.edges=el if len(vs)==4: #print "try Quad" if inside!=outside: for v in vs: if (v.outsideb2[1][i]: b0[1][i]=b2[1][i] if b0[1][i]