############################################################# # # # Blender Knife Tool # # # # (C) December 2002 Stefano Selleri # # Released under the Blender Artistic Licence (BAL) # # See www.blender.org # # # ############################################################# # History # # V: 0.0.0 - 08-12-02 - The script starts to take shape, an # # history is now deserved :) # # 0.0.1 - 09-12-02 - The faces are correctly selected and# # assigned to the relevant objects # # now the hard (splitting) part... # # 0.0.2 - 14-12-02 - Still hacking on the splitting... # # It works, but I have to de-globalize# # the intersection coordinates # # 0.0.3 - 15-12-02 - First Alpha version # # 0.0.4 - 17-12-02 - Upgraded accordingly to eeshlo tips # # Use Matrices for coordinate transf. # # Add a GUI # # Make it Run on 2.23 # # 0.0.5 - 17-12-02 - Eeshlo solved some problems.... # # Theeth too adviced me # # 0.0.6 - 18-12-02 - Better error messages # ############################################################# import Blender from Blender import * from math import * Epsilon = 0.00001 msg = '' BL_VERSION = Blender.Get('version') if (BL_VERSION<=223): import Blender210 ############################################################# # SUBS from LOCAL to GLOBAL coordinates # ############################################################# def GlobalPosition(P, Obj): # # Prende un punto nelle coordinate locali (Vertice di Mesh) # E lo trasporta in coordinate globali # if (BL_VERSION<=223): m = Obj.matrix else: m = Obj.getMatrix() PX = P[0]*m[0][0] + P[1]*m[1][0] + P[2]*m[2][0] + m[3][0] PY = P[0]*m[0][1] + P[1]*m[1][1] + P[2]*m[2][1] + m[3][1] PZ = P[0]*m[0][2] + P[1]*m[1][2] + P[2]*m[2][2] + m[3][2] return (PX, PY, PZ) def LocalPosition(P, Obj): # # Prende un punto nelle coordinate globali # E lo trasporta in coordinate locali a un ggetto (Vertice di Mesh) # if (BL_VERSION<=223): m = Blender210.getObject(Obj.name).inverseMatrix else: m = Obj.getInverseMatrix() PX = P[0]*m[0][0] + P[1]*m[1][0] + P[2]*m[2][0] + m[3][0] PY = P[0]*m[0][1] + P[1]*m[1][1] + P[2]*m[2][1] + m[3][1] PZ = P[0]*m[0][2] + P[1]*m[1][2] + P[2]*m[2][2] + m[3][2] return (PX, PY, PZ) ############################################################# # SUBS Extracts Cutting Plane Data GetSel # ############################################################# def CutData(Plane): # # Traduce il secondo oggetto in un punto (Il suo centro) # ed in una direzione (la normale alla faccia 0) # if (BL_VERSION<=223): PlaneCenter = Plane.loc else: PlaneCenter = [Plane.LocX,Plane.LocY,Plane.LocZ]#Plane.getLocation() PlaneMesh = NMesh.GetRawFromObject(Plane.name) if (len(PlaneMesh.faces)>1): msg = "ERROR: Active object must be a single face plane" return ((0,0,0),(0,0,0)) else: if (len(PlaneMesh.verts)<3): msg = "ERROR: 3 vertices needed to define a plane" return ((0,0,0),(0,0,0)) else: print "# OK: Active object is a single face plane -> Getting Cut Data" v0 = GlobalPosition(PlaneMesh.faces[0].v[0].co, Plane) v1 = GlobalPosition(PlaneMesh.faces[0].v[1].co, Plane) v2 = GlobalPosition(PlaneMesh.faces[0].v[2].co, Plane) NormX = (v1[1]-v0[1])*(v2[2]-v0[2]) - (v1[2]-v0[2])*(v2[1]-v0[1]) NormY = (v1[2]-v0[2])*(v2[0]-v0[0]) - (v1[0]-v0[0])*(v2[2]-v0[2]) NormZ = (v1[0]-v0[0])*(v2[1]-v0[1]) - (v1[1]-v0[1])*(v2[0]-v0[0]) NormLen = sqrt(NormX*NormX+NormY*NormY+NormZ*NormZ) NormX /= NormLen NormY /= NormLen NormZ /= NormLen return PlaneCenter, (NormX, NormY, NormZ) ############################################################# # SUBS giving position with respect to Cut Plane # ############################################################# def Distance(P, N, d0): # # Ok, quanto dista un punto dal piano? # return P[0]*N[0] + P[1]*N[1] + P[2]*N[2]-d0 def FacePosition(f, Obj, N, d0): # # Valuta se una faccia è tutta da una parte, da un'altra, o è intersecata # np, nn, nz = 0, 0, 0 for v in f.v: V = GlobalPosition(v.co, Obj) d = Distance(V, N, d0) if (d>0): np += 1 elif (d<0): nn += 1 else: nz += 1 if (np==0): return -1 if (nn==0): return 1 return 0 ############################################################# # SUBS Appending existing faces or creating new faces for # # cutted objects # ############################################################# def FaceAppend(me, f): # # Aggiunge una faccia senza nessuna elaborazione # idx = len(me.verts) for v in f.v: me.verts.append(NMesh.Vert(v.co[0], v.co[1], v.co[2])) nf = NMesh.Face() for i in range(len(f.v)): nf.v.append(me.verts[idx+i]) me.faces.append(nf) def FaceMake(me, vl): # # Crea una o due nuove facce da un elenco di vertici # idx = len(me.verts) for v in vl: me.verts.append(NMesh.Vert(v[0], v[1], v[2])) if (len(vl)<=4): nf = NMesh.Face() for i in range(len(vl)): nf.v.append(me.verts[idx+i]) me.faces.append(nf) else: nf = NMesh.Face() nf.v.append(me.verts[idx]) nf.v.append(me.verts[idx+1]) nf.v.append(me.verts[idx+2]) nf.v.append(me.verts[idx+3]) me.faces.append(nf) nf = NMesh.Face() nf.v.append(me.verts[idx+3]) nf.v.append(me.verts[idx+4]) nf.v.append(me.verts[idx]) me.faces.append(nf) ############################################################# # SUBS Generating vertex lists for splitting faces # ############################################################# def Split(Obj, v, N, d0): # # Genera una lista di vertici da una parte e dall'altra e relativi punti intermedi # i = 0 V = [] d = [] vp = [] vn = [] for vl in v: V.append(GlobalPosition(vl.co, Obj)) d.append(Distance(V[i], N, d0)) i += 1 for i in range(0,len(d)): if (i==0): dim1 = d[len(d)-1] Vim1 = V[len(V)-1] else: dim1 = d[i-1] Vim1 = V[i-1] if (abs(d[i])0): vp.append(tuple(v[i].co)) else: vn.append(tuple(v[i].co)) else: if (d[i]*dim1>0): # Nessun cambio netto, nessuna intersezione if (d[i]>0): vp.append(tuple(v[i].co)) else: vn.append(tuple(v[i].co)) else: # INTERSEZIONE!!! Den = (Vim1[1]-V[i][1])*N[1] + (Vim1[0]-V[i][0])*N[0] + (Vim1[2]-V[i][2])*N[2] Vi = [] Vi.append (- ((Vim1[0]*V[i][1]-Vim1[1]*V[i][0])*N[1]+(Vim1[0]*V[i][2]-Vim1[2]*V[i][0])*N[2]+(V[i][0]-Vim1[0])*d0)/Den) Vi.append (- ((Vim1[1]*V[i][0]-Vim1[0]*V[i][1])*N[0]+(Vim1[1]*V[i][2]-Vim1[2]*V[i][1])*N[2]+(V[i][1]-Vim1[1])*d0)/Den) Vi.append (- ((Vim1[2]*V[i][0]-Vim1[0]*V[i][2])*N[0]+(Vim1[2]*V[i][1]-Vim1[1]*V[i][2])*N[1]+(V[i][2]-Vim1[2])*d0)/Den) ViL = LocalPosition(Vi, Obj) vp.append(ViL) vn.append(ViL) # E il punto stesso... if (d[i]>0): vp.append(tuple(v[i].co)) else: vn.append(tuple(v[i].co)) return (vp,vn) ############################################################# # SUBS Splitting a face intersected by the Cut Plane # ############################################################# def FaceSplit(Obj, mp, mn, f, N, d0): # # Divide la faccia in due pezi e crea le facce necessarie # # Crea le liste dei vertici (vlp, vln) = Split(Obj, f.v, N, d0) # Crea nuove facce coi nuovi vertici FaceMake(mp, vlp) FaceMake(mn, vln) ############################################################# # SUBS Appending to two new meshes all the faces of the # # original one, splitting the intersected ones # ############################################################# def Cut(Obj, Normal, d0, MeshPos, MeshNeg): # # Vabbuò, scandiamoci tutte le faccine... # if BL_VERSION<=223: ObjMesh = Obj.data else: ObjMesh = Obj.getData() for oface in ObjMesh.faces: fp = FacePosition(oface, Obj, Normal, d0) if (fp>0): # Tutta nel positivo FaceAppend(MeshPos, oface) if (fp<0): # Tutta nel negativo FaceAppend(MeshNeg, oface) if (fp==0): # Un po' qua un po' là FaceSplit(Obj, MeshPos, MeshNeg, oface, Normal, d0) ############################################################# # MAIN # ############################################################# def DoTheCut(): global msg print "KNIFE TOOL - (C) Dec. 2002 Stefano Selleri" print "Vers. 0.0.3 - Released under Blender Artistic License (www.blender.org)" if (BL_VERSION<=223): selected_obs = Object.GetSelected() else: selected_obs = Object.GetSelected() if (len(selected_obs)!=2): msg = "ERROR: You must select exactly two objects" else : Pln = selected_obs[0] # Piano di taglio Obj = selected_obs[1] # Oggetto da dividere (CC,CN) = CutData(Pln) d0 = CC[0]*CN[0]+CC[1]*CN[1]+CC[2]*CN[2] print "Cut data: Normal = ",CN," Offset = ",d0 if (CN!=(0,0,0)): # Crea i due nuovi oggetti MeshPos = NMesh.GetRaw() MeshNeg = NMesh.GetRaw() # Li popola di vertici e facce Cut(Obj, CN, d0, MeshPos, MeshNeg) NMesh.PutRaw(MeshPos) ObPos = Object.GetSelected()[0] if (BL_VERSION<=223): # remove doubles using 210 module me = Blender210.getMesh(ObPos.data.name) me.enterEditMode() me.removeDoubles() me.leaveEditMode() NMesh.PutRaw(MeshNeg) ObNeg = Object.GetSelected()[0] if (BL_VERSION<=223): # remove doubles using 210 module me = Blender210.getMesh(ObNeg.data.name) me.enterEditMode() me.removeDoubles() me.leaveEditMode() # Li posiziona dove devono stare ObPos.LocX, ObPos.LocY, ObPos.LocZ = Obj.LocX,Obj.LocY,Obj.LocZ ObPos.RotX,ObPos.RotY,ObPos.RotZ = Obj.RotX,Obj.RotY,Obj.RotZ ObPos.SizeX,ObPos.SizeY,ObPos.SizeZ = Obj.SizeX,Obj.SizeY,Obj.SizeZ ObNeg.LocX, ObNeg.LocY,ObNeg.LocZ= Obj.LocX,Obj.LocY,Obj.LocZ ObNeg.RotX, ObNeg.RotY, ObNeg.RotZ= Obj.RotX,Obj.RotY,Obj.RotZ ObNeg.SizeX,ObNeg.SizeY,ObNeg.SizeZ = Obj.SizeX,Obj.SizeY,Obj.SizeZ else: msg = "ERROR: active object has no face" ############################################################# # Graphics # ############################################################# def Warn(): BGL.glRasterPos2d(115, 23) Blender.Window.Redraw(Blender.Window.Const.TEXT) def draw(): BGL.glClearColor(0.5, 0.5, 0.5, 1) BGL.glColor3f(1.,1.,1.) BGL.glClear(BGL.GL_COLOR_BUFFER_BIT) #Titolone BGL.glColor3f(1, 1, 1) BGL.glRasterPos2d(8, 153) Draw.Text("Blender Knife Tool") BGL.glRasterPos2d(8, 133) Draw.Text("(C) Dec. 2002 Stefano Selleri ") BGL.glRasterPos2d(8, 103) Draw.Text("1 - Draw a Mesh Plane defining the Cut Plane") BGL.glRasterPos2d(8, 83) Draw.Text("2 - Select the Mesh to be Cut and the Cut Plane") BGL.glRasterPos2d(8, 63) Draw.Text(" (This means Mesh Dark Purple, Plane Light Purple)") BGL.glRasterPos2d(8, 43) Draw.Text("3 - Push The CUT button!!!") #Create Button Draw.Button("CUT", 3, 10, 10, 100, 25) #Create Button Draw.Button("Exit", 1, 120, 147, 40, 18) BGL.glRasterPos2d(115, 23) BGL.glColor3f(1,0,0) Draw.Text(msg) def event(evt, val): if (evt== Draw.QKEY and not val): Draw.Exit() def bevent(evt): if (evt== 1): Draw.Exit() elif (evt== 3): DoTheCut() Redraw() Draw.Register(draw, event, bevent)