Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2006-07-10 13:22:07 +0400
committerCampbell Barton <ideasman42@gmail.com>2006-07-10 13:22:07 +0400
commit309fb2ec26ece92b7c30e2354237344d46bc4068 (patch)
tree78cb78e80fe0e0a938ea6de466b5fd8b357478c4 /release/scripts/mesh_skin.py
parent06095778c19a6e5191d0a27908a90d55d9b5837c (diff)
removed a script I wrote and added it under a different name.
Moved from NMesh to Mesh and optimized.
Diffstat (limited to 'release/scripts/mesh_skin.py')
-rw-r--r--release/scripts/mesh_skin.py502
1 files changed, 502 insertions, 0 deletions
diff --git a/release/scripts/mesh_skin.py b/release/scripts/mesh_skin.py
new file mode 100644
index 00000000000..4e2626e5e77
--- /dev/null
+++ b/release/scripts/mesh_skin.py
@@ -0,0 +1,502 @@
+#!BPY
+
+"""
+Name: 'Bridge Faces/Edge-Loops'
+Blender: 237
+Group: 'Mesh'
+Tooltip: 'Select 2 vert loops, then run this script.'
+"""
+
+__author__ = "Campbell Barton AKA Ideasman"
+__url__ = ["http://members.iinet.net.au/~cpbarton/ideasman/", "blender", "elysiun"]
+__version__ = "1.0 2004/04/25"
+
+__bpydoc__ = """\
+With this script vertex loops can be skinned: faces are created to connect the
+selected loops of vertices.
+
+Usage:
+
+In mesh Edit mode select the vertices of the loops (closed paths / curves of
+vertices: circles, for example) that should be skinned, then run this script.
+A pop-up will provide further options, if the results of a method are not adequate try one of the others.
+"""
+
+
+# $Id$
+#
+# --------------------------------------------------------------------------
+# Skin Selected edges 1.0 By Campbell Barton (AKA Ideasman)
+# --------------------------------------------------------------------------
+# ***** 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 *****
+# --------------------------------------------------------------------------
+
+# Made by Ideasman/Campbell 2005/06/15 - ideasman@linuxmail.org
+
+import Blender
+from Blender import *
+
+BIG_NUM = 1<<30
+
+global CULL_METHOD
+CULL_METHOD = 0
+
+class edge:
+ def __init__(self, v1,v2):
+ self.v1 = v1
+ self.v2 = v2
+
+ # uv1 uv2 vcol1 vcol2 # Add later
+ self.length = (v1.co - v2.co).length
+
+ self.removed = 0 # Have we been culled from the eloop
+ self.match = None # The other edge were making a face with
+
+
+class edgeLoop:
+ def __init__(self, loop): # Vert loop
+ # Use next and prev, nextDist, prevDist
+
+ # Get Loops centre.
+ self.centre = Mathutils.Vector()
+ f = 1.0/len(loop)
+ for v in loop:
+ self.centre += v.co * f
+
+
+
+
+ # Convert Vert loop to Edges.
+ self.edges = []
+ vIdx = 0
+ while vIdx < len(loop):
+ self.edges.append( edge(loop[vIdx-1], loop[vIdx]) )
+ vIdx += 1
+
+ # Assign linked list
+ for eIdx in xrange(len(self.edges)-1):
+ self.edges[eIdx].next = self.edges[eIdx+1]
+ self.edges[eIdx].prev = self.edges[eIdx-1]
+ # Now last
+ self.edges[-1].next = self.edges[0]
+ self.edges[-1].prev = self.edges[-2]
+
+
+
+ # GENERATE AN AVERAGE NORMAL FOR THE WHOLE LOOP.
+ self.normal = Mathutils.Vector()
+ for e in self.edges:
+ n = Mathutils.CrossVecs(self.centre-e.v1.co, self.centre-e.v2.co)
+ # Do we realy need tot normalize?
+ n.normalize()
+ self.normal += n
+ self.normal.normalize()
+
+
+ # Generate a normal for each edge.
+ for e in self.edges:
+
+ n1 = e.v1.co
+ n2 = e.v2.co
+ n3 = e.prev.v1.co
+
+ a = n1-n2
+ b = n1-n3
+ normal1 = Mathutils.CrossVecs(a,b)
+ normal1.normalize()
+
+ n1 = e.v2.co
+ n3 = e.next.v2.co
+ n2 = e.v1.co
+
+ a = n1-n2
+ b = n1-n3
+
+ normal2 = Mathutils.CrossVecs(a,b)
+ normal2.normalize()
+
+ # Reuse normal1 var
+ normal1 += normal1 + normal2
+ normal1.normalize()
+
+ e.normal = normal1
+ #print e.normal
+
+
+
+
+ def backup(self):
+ # Keep a backup of the edges
+ self.backupEdges = self.edges[:]
+
+ def restore(self):
+ self.edges = self.backupEdges[:]
+ for e in self.edges:
+ e.removed = 0
+
+ def reverse(self):
+ self.edges.reverse()
+ for e in self.edges:
+ e.normal = -e.normal
+ e.v1, e.v2 = e.v2, e.v1
+ self.normal = -self.normal
+
+ def removeSmallest(self, cullNum, otherLoopLen):
+ '''
+ Removes N Smallest edges and backs up the loop,
+ this is so we can loop between 2 loops as if they are the same length,
+ backing up and restoring incase the loop needs to be skinned with another loop of a different length.
+ '''
+ global CULL_METHOD
+ if CULL_METHOD == 0: # Shortest edge
+
+ eloopCopy = self.edges[:]
+ eloopCopy.sort(lambda e1, e2: cmp(e1.length, e2.length )) # Length sort, smallest first
+ eloopCopy = eloopCopy[:cullNum]
+ for e in eloopCopy:
+ e.removed = 1
+ self.edges.remove( e ) # Remove from own list, still in linked list.
+
+ else: # CULL METHOD is even
+
+ culled = 0
+
+ step = int(otherLoopLen / float(cullNum))
+
+ currentEdge = self.edges[0]
+ while culled < cullNum:
+
+ # Get the shortest face in the next STEP
+ while currentEdge.removed == 1:
+ # Bug here!
+ currentEdge = currentEdge.next
+ smallestEdge = currentEdge
+
+ for i in xrange(step):
+ currentEdge = currentEdge.next
+ while currentEdge.removed == 1:
+ currentEdge = currentEdge.next
+ if smallestEdge.length > currentEdge.length:
+ smallestEdge = currentEdge
+
+ # In that stepping length we have the smallest edge.remove it
+ smallestEdge.removed = 1
+ self.edges.remove(smallestEdge)
+
+ culled+=1
+
+
+# Returns face edges.
+# face must have edge data.
+
+def getSelectedEdges(me, ob):
+ MESH_MODE= Blender.Mesh.Mode()
+
+ if MESH_MODE==Blender.Mesh.SelectModes.EDGE or MESH_MODE==Blender.Mesh.SelectModes.VERTEX:
+ Blender.Mesh.Mode(Blender.Mesh.SelectModes.EDGE)
+ edges= [ ed for ed in me.edges if ed.sel ]
+ Blender.Mesh.Mode(MESH_MODE)
+ return edges
+
+ elif MESH_MODE==Blender.Mesh.SelectModes.FACE:
+ Blender.Mesh.Mode(Blender.Mesh.SelectModes.EDGE)
+
+ def ed_key(ed):
+ i1= ed.v1.index
+ i2= ed.v2.index
+ if i1 > i2:
+ return i2, i1
+ else:
+ return i1, i2
+
+ # value is [edge, face_sel_user_in]
+ edge_dict= dict((ed_key(ed), [ed, 0]) for ed in me.edges)
+
+ for f in me.faces:
+ if f.sel:
+ fidx= [v.index for v in f.v]
+ for i in xrange(len(fidx)):
+ i1= fidx[i]
+ i2= fidx[i-1]
+
+ if i1>i2:
+ i1,i2= i2,i1
+
+ # ed_data is a list of 2 ed and face user
+ ed_data= edge_dict[i1,i2]
+ ed_data[1]+=1
+
+
+ Blender.Mesh.Mode(MESH_MODE)
+ return [ ed_data[0] for ed_data in edge_dict.itervalues() if ed_data[1] == 1 ]
+
+
+
+# Like vert loops
+def getVertLoops(selEdges):
+
+ mainVertLoops = []
+ while selEdges:
+ e = selEdges.pop()
+ contextVertLoop= [e.v1, e.v2] # start the vert loop
+
+ eIdx = 1 # Get us into the loop. dummy var.
+
+ # if eIdx == 0 then it means we searched and found no matches...
+ # time for a new vert loop,
+ while eIdx:
+ eIdx = len(selEdges)
+ while eIdx:
+ eIdx-=1
+
+ # Check for edge attached at the head of the loop.
+ if contextVertLoop[0] == selEdges[eIdx].v1:
+ contextVertLoop.insert(0, selEdges.pop(eIdx).v2)
+ elif contextVertLoop[0] == selEdges[eIdx].v2:
+ contextVertLoop.insert(0, selEdges.pop(eIdx).v1)
+
+ # Chech for edge vert at the tail.
+ elif contextVertLoop[-1] == selEdges[eIdx].v1:
+ contextVertLoop.append(selEdges.pop(eIdx).v2)
+ elif contextVertLoop[-1] == selEdges[eIdx].v2:
+ contextVertLoop.append(selEdges.pop(eIdx).v1)
+ else:
+ # None found? Keep looking
+ continue
+
+ # Once found we.
+ break
+
+ # Is this a loop? if so then its forst and last vert must be teh same.
+ if contextVertLoop[0].index == contextVertLoop[-1].index:
+ contextVertLoop.pop() # remove double vert
+ mainVertLoops.append(contextVertLoop)
+
+ # Build context vert loops
+ return mainVertLoops
+
+
+def skin2EdgeLoops(eloop1, eloop2, me, ob, MODE):
+
+ new_faces= [] #
+
+ # Make sure e1 loops is bigger then e2
+ if len(eloop1.edges) != len(eloop2.edges):
+ if len(eloop1.edges) < len(eloop2.edges):
+ eloop1, eloop2 = eloop2, eloop1
+
+ eloop1.backup() # were about to cull faces
+ CULL_FACES = len(eloop1.edges) - len(eloop2.edges)
+ eloop1.removeSmallest(CULL_FACES, len(eloop1.edges))
+ else:
+ CULL_FACES = 0
+ # First make sure poly vert loops are in sync with eachother.
+
+ # The vector allong which we are skinning.
+ skinVector = eloop1.centre - eloop2.centre
+
+ loopDist = skinVector.length
+
+
+ # IS THE LOOP FLIPPED, IF SO FLIP BACK.
+ angleBetweenLoopNormals = Mathutils.AngleBetweenVecs(eloop1.normal, eloop2.normal)
+
+ if angleBetweenLoopNormals > 90:
+ eloop2.reverse()
+
+
+ bestEloopDist = BIG_NUM
+ bestOffset = 0
+ # Loop rotation offset to test.1
+ eLoopIdxs = range(len(eloop1.edges))
+ for offset in xrange(len(eloop1.edges)):
+ totEloopDist = 0 # Measure this total distance for thsi loop.
+
+ offsetIndexLs = eLoopIdxs[offset:] + eLoopIdxs[:offset] # Make offset index list
+
+ # e1Idx is always from 0 to N, e2Idx is offset.
+ for e1Idx, e2Idx in enumerate(offsetIndexLs):
+ # Measure the vloop distance ===============
+ totEloopDist += ((eloop1.edges[e1Idx].v1.co - eloop2.edges[e2Idx].v1.co).length / loopDist) #/ nangle1
+ totEloopDist += ((eloop1.edges[e1Idx].v2.co - eloop2.edges[e2Idx].v2.co).length / loopDist) #/ nangle1
+
+ # Premeture break if where no better off
+ if totEloopDist > bestEloopDist:
+ break
+
+ if totEloopDist < bestEloopDist:
+ bestOffset = offset
+ bestEloopDist = totEloopDist
+
+ # Modify V2 LS for Best offset
+ eloop2.edges = eloop2.edges[bestOffset:] + eloop2.edges[:bestOffset]
+
+
+
+ for loopIdx in xrange(len(eloop2.edges)):
+ e1 = eloop1.edges[loopIdx]
+ e2 = eloop2.edges[loopIdx]
+
+ # Remember the pairs for fan filling culled edges.
+ e1.match = e2; e2.match = e1
+ new_faces.append([e1.v1, e1.v2, e2.v2, e2.v1])
+
+ # FAN FILL MISSING FACES.
+ if CULL_FACES:
+ # Culled edges will be in eloop1.
+ FAN_FILLED_FACES = 0
+
+ contextEdge = eloop1.edges[0] # The larger of teh 2
+ while FAN_FILLED_FACES < CULL_FACES:
+ while contextEdge.next.removed == 0:
+ contextEdge = contextEdge.next
+
+ vertFanPivot = contextEdge.match.v2
+
+ while contextEdge.next.removed == 1:
+ new_faces.append([contextEdge.next.v1, contextEdge.next.v2, vertFanPivot])
+
+ # Should we use another var?, this will work for now.
+ contextEdge.next.removed = 1
+
+ contextEdge = contextEdge.next
+ FAN_FILLED_FACES += 1
+
+ eloop1.restore() # Add culled back into the list.
+
+ me.faces.extend(new_faces)
+
+ #if angleBetweenLoopNormals > 90:
+ # eloop2.reverse()
+
+
+def main():
+ global CULL_METHOD
+
+ is_editmode = Window.EditMode()
+ if is_editmode: Window.EditMode(0)
+ ob = Scene.GetCurrent().getActiveObject()
+ if ob == None or ob.getType() != 'Mesh':
+ return
+
+ me = ob.getData(mesh=1)
+ '''
+ if not me.edges:
+ Draw.PupMenu('Error, add edge data first')
+ if is_editmode: Window.EditMode(1)
+ return
+ '''
+
+ # BAD BLENDER PYTHON API, NEED TO ENTER EXIT EDIT MODE FOR ADDING EDGE DATA.
+ # ADD EDGE DATA HERE, Python API CANT DO IT YET, LOOSES SELECTION
+
+ selEdges = getSelectedEdges(me, ob)
+ vertLoops = getVertLoops(selEdges) # list of lists of edges.
+
+ if len(vertLoops) > 2:
+ choice = Draw.PupMenu('Loft '+str(len(vertLoops))+' edge loops%t|loop|segment')
+ if choice == -1:
+ if is_editmode: Window.EditMode(1)
+ return
+ elif len(vertLoops) < 2:
+ Draw.PupMenu('Error, No Vertloops found%t|if you have a valid selection, go in and out of face edit mode to update the selection state.')
+ if is_editmode: Window.EditMode(1)
+ return
+ else:
+ choice = 2
+
+
+ # The line below checks if any of the vert loops are differenyt in length.
+ if False in [len(v) == len(vertLoops[0]) for v in vertLoops]:
+ CULL_METHOD = Draw.PupMenu('Small to large edge loop distrobution method%t|remove edges evenly|remove smallest edges edges')
+ if CULL_METHOD == -1:
+ if is_editmode: Window.EditMode(1)
+ return
+
+ if CULL_METHOD ==1: # RESET CULL_METHOD
+ CULL_METHOD = 0 # shortest
+ else:
+ CULL_METHOD = 1 # even
+
+
+ time1 = sys.time()
+ # Convert to special edge data.
+ edgeLoops = []
+ for vloop in vertLoops:
+ edgeLoops.append(edgeLoop(vloop))
+
+
+ # VERT LOOP ORDERING CODE
+ # "Build a worm" list - grow from Both ends
+ edgeOrderedList = [edgeLoops.pop()]
+
+ # Find the closest.
+ bestSoFar = BIG_NUM
+ bestIdxSoFar = None
+ for edLoopIdx, edLoop in enumerate(edgeLoops):
+ l =(edgeOrderedList[-1].centre - edLoop.centre).length
+ if l < bestSoFar:
+ bestIdxSoFar = edLoopIdx
+ bestSoFar = l
+
+ edgeOrderedList.append( edgeLoops.pop(bestIdxSoFar) )
+
+ # Now we have the 2 closest, append to either end-
+ # Find the closest.
+ while edgeLoops:
+ bestSoFar = BIG_NUM
+ bestIdxSoFar = None
+ first_or_last = 0 # Zero is first
+ for edLoopIdx, edLoop in enumerate(edgeLoops):
+ l1 =(edgeOrderedList[-1].centre - edLoop.centre).length
+
+ if l1 < bestSoFar:
+ bestIdxSoFar = edLoopIdx
+ bestSoFar = l1
+ first_or_last = 1 # last
+
+ l2 =(edgeOrderedList[0].centre - edLoop.centre).length
+ if l2 < bestSoFar:
+ bestIdxSoFar = edLoopIdx
+ bestSoFar = l2
+ first_or_last = 0 # last
+
+ if first_or_last: # add closest Last
+ edgeOrderedList.append( edgeLoops.pop(bestIdxSoFar) )
+ else: # Add closest First
+ edgeOrderedList.insert(0, edgeLoops.pop(bestIdxSoFar) ) # First
+
+ for i in xrange(len(edgeOrderedList)-1):
+ skin2EdgeLoops(edgeOrderedList[i], edgeOrderedList[i+1], me, ob, 0)
+ if choice == 1 and len(edgeOrderedList) > 2: # Loop
+ skin2EdgeLoops(edgeOrderedList[0], edgeOrderedList[-1], me, ob, 0)
+
+ print '\nSkin done in %.4f sec.' % (sys.time()-time1)
+
+ # REMOVE SELECTED FACES.
+ faces= [ f for f in me.faces if f.sel ]
+ print faces
+ if faces:
+ print faces
+ me.faces.delete(1, faces)
+
+ if is_editmode: Window.EditMode(1)
+
+if __name__ == '__main__':
+ main()