#!BPY """ Name: 'Extrude Along Normal' Blender: 228 Group: 'Misc' Tooltip: 'In FaceSelectMode (FKEY), extrude selected faces along their normal' """ ##################################################################### # # # Extrude Along Normal # # # # (C) September 2003 Wim Van Hoydonck # # email: tuinbels@hotmail.com # # # # Released under the Blender Artistic Licence (BAL) # # see www.blender.org # # # # Works in Blender 2.28 and higher # # # # this script can be found online at: # # http://users.telenet.be/tuinbels/scripts/extrudealongnormal.py # # # ##################################################################### # # # History # # V: 0.1 - 22-08-03 - First working version # # 0.2 - 23-09-03 - Got rid of a bug thanks to Theeth # # 0.3 - 30-09-03 - Modified script a little to create less # # double vertices # # 0.4 - 10-10-03 - Added a togglebutton to append the faces # # that were not selected to the new mesh # # (default = 1) # # - Removed a bug that got in with version 0.3 # # 0.4.1 - 12-10-03 - Speed improvement by removing the # # coordinate transformations # # 0.5 - 13-10-03 - Extrude multiple (1 or more) connected # # segments # # 0.6 - 22-12-03 - Finally figured out howto create a new mesh # # without doubles :-) (code cleanup) # # - The togglebutton is removed # # - Added keyboard shortcut to extrude (E-key) # # 0.6.1 - 21-01-04 - Some minor changes: added extra button to # # control the maximum value of extrudeslider, # # added toggle to remove original object from # # scene (so it seems that the operation is # # done on original object). # # 0.6.2 - 26-01-04 - Modified script to really extrude faces of # # the original mesh, without the need to # # creating a new one, and mesh stays in # # faceselectmode (as suggested by Niket, many # # thanks for the feedback :D ) # # You can still create a new mesh by pressing # # the toggle button. # # # ##################################################################### # # # -> How it works: # # # # - Go out of edit mode # # - Enter FaceSelect Mode (FKEY) # # - Now all faces are selected (so if you press extrude, every face # # should be extruded) # # - To deselect all faces, press AKEY # # - Now select the face(s) that you want to extrude # # - Press the EXTRUDE button or press EKEY with the mouse in the # # script window. # # - If toggle "new ob" is pressed, a new mesh is created with the # # extruded faces. # # - If not, the original mesh is used. Since UNDO in editmode was # # added to blender, you can UNDO the extrusion of the faces if # # you have already edited the mesh. # # # ##################################################################### import Blender from Blender import * from math import * from Blender.Draw import * from Blender.BGL import * # the length of the extrusion height = Create(1.0) # number of segments to make segment = Create(1) # max value of extrudeslider max = Create(5) # make new mesh newmesh = Create(0) ############################################################# # vector and matrix manipulations # ############################################################# # vector substration def vecsub(a, b): return [a[0] - b[0], a[1] - b[1], a[2] - b[2]] # vector addition def vecadd(a, b): return [a[0] + b[0], a[1] + b[1], a[2] + b[2]] # vector crossproduct def veccross(x, y): v = [0, 0, 0] v[0] = x[1]*y[2] - x[2]*y[1] v[1] = x[2]*y[0] - x[0]*y[2] v[2] = x[0]*y[1] - x[1]*y[0] return v # vector length def length(v): return sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]) # vector multiplied by constant s def vecmul(a, s): return[a[0]*s, a[1]*s, a[2]*s] # vector divided by constant s def vecdiv(a, s): if s!=0.0: s = 1.0/s return vecmul(a, s) # Normalization of a vector def Normalize(a): lengte = length(a) return vecdiv(a, lengte) # calculate normal from 3 verts def Normal(v0, v1, v2): return veccross(vecsub(v0, v1),vecsub(v0, v2)) # calculate normal from 4 verts # found in the blender sources in arithb.c # (bl-blender\blender\source\blender\blenlib\intern\arithb.c) def Normal4(v0, v1, v2, v3): return veccross(vecsub(v0, v2),vecsub(v1, v3)) ############################################################# # get meshdata # ############################################################# def Meshdata(m, idx, ob, indx): lenface = [] Vertglob = [] normal = [] vertidx = [] vertidx1 = [] for i in idx: fvertidx = [] fa = m.faces[i] # facelength lenface.append(len(fa)) Ve = [] for v in fa.v: # next line is for a bug in the first Blender release with the new API V = v.co[0:3] # Ve is vector with coords of selected faces Ve.append(V) # index of the verts fvertidx.append(v.index) # calculate normal for every face vertidx.append(fvertidx) Vertglob.append(Ve) if len(fa) == 4: normal.append(Normalize(Normal4(Ve[0], Ve[1], Ve[2], Ve[3]))) else: normal.append(Normalize(Normal(Ve[0], Ve[1], Ve[2]))) for i in indx: fvertidx1 = [] fa = m.faces[i] for v in fa.v: fvertidx1.append(v.index) vertidx1.append(fvertidx1) return [lenface, Vertglob, normal, vertidx, vertidx1] ############################################################# # main vertex and face adding loop # ############################################################# def addVertsandFaces(idx, m, lenfa, Vg, nor, vidx): for i in range(0, len(idx)): # make vertices print i for j in range(1, segment.val+1): for k in range(0, lenfa[i]): vl1 = vecadd(Vg[i][k], vecmul(vecmul(nor[i], (height.val/segment.val)), j)) v1 = NMesh.Vert(vl1[0], vl1[1], vl1[2]) m.verts.append(v1) vpf = j*lenfa[i] # make topface from last four verts f = NMesh.Face() for l in range(0, lenfa[i]): l -= lenfa[i] f.v.append(m.verts[l]) m.faces.append(f) # make sidefaces for sf in range(0, segment.val): for n in range(0, lenfa[i]): # first segment next to mesh requires special treatment if sf == 0: f = NMesh.Face() f.v.append(m.verts[vidx[i][n]]) f.v.append(m.verts[-(segment.val*lenfa[i])+n]) f.v.append(m.verts[-(segment.val*lenfa[i])+int(fmod(n+1,lenfa[i]))]) f.v.append(m.verts[vidx[i][int(fmod(n+1, len(vidx[i])))]]) f.v.reverse() # reverse vertex list to point normals outside m.faces.append(f) # other segments else: f = NMesh.Face() f.v.append(m.verts[-(segment.val+1-sf)*lenfa[i]+n]) f.v.append(m.verts[-(segment.val-sf)*lenfa[i]+n]) f.v.append(m.verts[-(segment.val-sf)*lenfa[i]+int(fmod(n+1, lenfa[i]))]) f.v.append(m.verts[-(segment.val+1-sf)*lenfa[i]+int(fmod(n+1, lenfa[i]))]) f.v.reverse() # reverse vertex list to point normals outside m.faces.append(f) ############################################################# # main # ############################################################# def main(): obs = Object.GetSelected() ob = obs[0] ObN = [[0,0,0],[0,0,0],[0,0,0]] ObN[0][0], ObN[0][1], ObN[0][2] = ob.LocX, ob.LocY, ob.LocZ ObN[1][0], ObN[1][1], ObN[1][2] = ob.RotX, ob.RotY, ob.RotZ ObN[2][0], ObN[2][1], ObN[2][2] = ob.SizeX, ob.SizeY, ob.SizeZ m = ob.getData() # get indices of selected faces idx = m.getSelectedFaces(1) # get indices of faces that are not selected indx = [] for i in range(0, len(m.faces)): if i not in idx: indx.append(i) # get some facedata (length of faces, (global) coords of verts and normals of faces) facesdata = Meshdata(m, idx, ob, indx) lenfa, Vg, nor, vidx, vindx = facesdata[0], facesdata[1], facesdata[2], facesdata[3], facesdata[4] #===================================# # make new mesh for extruding faces # #===================================# if newmesh.val == 1: MeshNew = NMesh.GetRaw() # append the verts of original mesh to new mesh for v in m.verts: MeshNew.verts.append(v) # append the faces not selected to the new mesh for fi in vindx: f = NMesh.Face() for vi in fi: f.v.append(MeshNew.verts[vi]) MeshNew.faces.append(f) # main vertex- and faces-adding loop addVertsandFaces(idx, MeshNew, lenfa, Vg, nor, vidx) MeshNew.update() ObNew = NMesh.PutRaw(MeshNew) ObNew.LocX, ObNew.LocY, ObNew.LocZ = ObN[0][0], ObN[0][1], ObN[0][2] ObNew.RotX, ObNew.RotY, ObNew.RotZ = ObN[1][0], ObN[1][1], ObN[1][2] ObNew.SizeX, ObNew.SizeY, ObNew.SizeZ = ObN[2][0], ObN[2][1], ObN[2][2] #==================================# # use old mesh for extruding faces # #==================================# elif newmesh.val == 0: idx.sort() idx.reverse() # remove the selected faces for i in idx: fa = m.faces[i] m.faces.remove(fa) # main vertex- and faces-adding loop addVertsandFaces(idx, m, lenfa, Vg, nor, vidx) # update the mesh m.update() ############################################################# # Graphics # ############################################################# def draw(): global height, segment, max, newmesh BGL.glClearColor(0.5, 0.5, 0.5, 1) BGL.glColor3f(1.,1.,1.) BGL.glClear(BGL.GL_COLOR_BUFFER_BIT) BGL.glColor3f(1, 1, 1) BGL.glRasterPos2d(10, 80) Text("Extrude along normal") BGL.glRasterPos2d(10, 62) Text("(C) Sept. 2003 Wim Van Hoydonck") #Create Buttons & sliders Button("Extrude", 3, 10, 4, 80, 22, "extrude selected faces, or press EKEY with mouse in script window") Button("Exit", 1, 250, 4, 45, 22, "get out, QKEY with mouse in script window") height = Slider("extrusionlength: ", 2, 10, 31, 250, 22, height.val, 0.0, max.val, 0, "height of extrusion") segment = Number("segments: ", 2, 95, 4, 95, 22, segment.val, 1, 50, "number of segments to make") max = Number(" ", 4, 260, 31, 35, 22, max.val, 1, 50, "max value of extrudeslider") newmesh = Toggle("new ob", 2, 195, 4, 50, 22, newmesh.val, "make a new object") def event(evt, val): if (evt== QKEY and not val): Exit() # keyboard shortcut to start extrude if (evt== EKEY and not val): main() Redraw() def bevent(evt): if (evt== 1): Exit() elif (evt== 3): main() Redraw() elif (evt==4): Redraw() Register(draw, event, bevent)