home *** CD-ROM | disk | FTP | other *** search
/ Windows News 2005 November / WNnov2005.iso / Windows / Equipement / Blender / blender-2.37a-windows.exe / $_5_ / .blender / scripts / skin.py < prev    next >
Text File  |  2005-06-13  |  18KB  |  587 lines

  1. #!BPY
  2.  
  3. """
  4. Name: 'Bridge/Skin/Loft'
  5. Blender: 234
  6. Group: 'Mesh'
  7. Tooltip: 'Select 2 or more vert loops, then run this script'
  8. """
  9.  
  10. __author__ = "Campbell Barton AKA Ideasman"
  11. __url__ = ["http://members.iinet.net.au/~cpbarton/ideasman/", "blender", "elysiun"]
  12. __version__ = "1.1 2005/06/13"
  13.  
  14. __bpydoc__ = """\
  15. With this script vertex loops can be skinned: faces are created to connect the
  16. selected loops of vertices.
  17.  
  18. Usage:
  19.  
  20. In mesh Edit mode select the vertices of the loops (closed paths / curves of
  21. vertices: circles, for example) that should be skinned, then run this script.
  22. A pop-up will provide further options.
  23.  
  24. Notes:
  25.  
  26. If the results of a method chosen from the pop-up are not adequate, undo and try one of the others.
  27. """
  28.  
  29.  
  30. # $Id: skin.py,v 1.3 2005/06/13 17:21:30 ianwill Exp $
  31. #
  32. # -------------------------------------------------------------------------- 
  33. # Skin Selected edges 1.0 By Campbell Barton (AKA Ideasman)
  34. # -------------------------------------------------------------------------- 
  35. # ***** BEGIN GPL LICENSE BLOCK ***** 
  36. # This program is free software; you can redistribute it and/or 
  37. # modify it under the terms of the GNU General Public License 
  38. # as published by the Free Software Foundation; either version 2 
  39. # of the License, or (at your option) any later version. 
  40. # This program is distributed in the hope that it will be useful, 
  41. # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  42. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  43. # GNU General Public License for more details. 
  44. # You should have received a copy of the GNU General Public License 
  45. # along with this program; if not, write to the Free Software Foundation, 
  46. # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
  47. # ***** END GPL LICENCE BLOCK ***** 
  48. # -------------------------------------------------------------------------- 
  49.  
  50.  
  51.  
  52. # Made by Ideasman/Campbell 2004/04/25 - ideasman@linuxmail.org
  53.  
  54. import Blender
  55. from Blender import *
  56. import math
  57. from math import *
  58.  
  59.  
  60. choice = Draw.PupMenu(\
  61. 'Loft-loop - shortest edge method|\
  62. Loft-loop - even method|\
  63. Loft-segment - shortest edge|\
  64. Loft-segment - even method')
  65.  
  66. if choice == 1:
  67.     arg='A1'
  68. elif choice == 2:
  69.     arg='A2'
  70. elif choice == 3:
  71.     arg='B1'
  72. elif choice == 4:
  73.     arg='B2'
  74.  
  75.  
  76.  
  77. #================#
  78. # Math functions #
  79. #================#
  80.  
  81. # Measure 2 points
  82. def measure(v1, v2):
  83.   return Mathutils.Vector([v1[0]-v2[0], v1[1] - v2[1], v1[2] - v2[2]]).length
  84.   
  85. # Clamp
  86. def clamp(max, number):
  87.     while number >= max:
  88.         number = number - max
  89.     return number
  90.  
  91. #=============================================================#
  92. # List func that takes the last item and adds it to the front #
  93. #=============================================================#
  94. def listRotate(ls):
  95.     ls.append(ls.pop(0))
  96.  
  97. #=================================================================#
  98. # Recieve a list of locs: [x,y,z] and return the average location #
  99. #=================================================================#
  100. def averageLocation(locList):
  101.     avLoc = [0,0,0]
  102.     
  103.     # Loop through x/y/z
  104.     for coordIdx in [0,1,2]:
  105.         
  106.         # Add all the values from 1 of the 3 coords at the avLoc.
  107.         for loc in locList:
  108.             avLoc[coordIdx] += loc[coordIdx]
  109.         
  110.         avLoc[coordIdx] = avLoc[coordIdx] / len(locList)    
  111.     return avLoc
  112.  
  113.  
  114.  
  115. #=============================#
  116. # Blender functions/shortcuts #
  117. #=============================#
  118. def error(str):
  119.     Draw.PupMenu('ERROR%t|'+str)
  120.  
  121. # Returns a new face that has the same properties as the origional face
  122. # With no verts though
  123. def copyFace(face):
  124.   newFace = NMesh.Face()
  125.   # Copy some generic properties
  126.   newFace.mode = face.mode
  127.   if face.image != None:
  128.     newFace.image = face.image
  129.   newFace.flag = face.flag
  130.   newFace.mat = face.mat
  131.   newFace.smooth = face.smooth
  132.   return newFace
  133.  
  134. #=============================================#
  135. # Find a selected vert that 2 faces share.    #
  136. #=============================================#
  137. def selVertBetween2Faces(face1, face2):
  138.     for v1 in face1.v:
  139.         if v1.sel:
  140.             for v2 in face2.v:
  141.                 if v1 == v2:
  142.                     return v1
  143.     
  144.     
  145. #=======================================================#
  146. # Measure the total distance between all the edges in   #
  147. # 2 vertex loops                                        #
  148. #=======================================================#
  149. def measureVloop(mesh, v1loop, v2loop, surplusFaces, bestSoFar):
  150.     totalDist = 0
  151.     
  152.     # Rotate the vertloops to cycle through each pair.
  153.     # of faces to compate the distance between the 2 poins
  154.     for ii in range(len(v1loop)):
  155.         if ii not in surplusFaces:
  156.             # Clamp
  157.             v2clampii = ii
  158.             while v2clampii >= len(v2loop):
  159.                 v2clampii -= len(v2loop)
  160.             print v2clampii
  161.             
  162.             V1 = selVertBetween2Faces(mesh.faces[v1loop[ii-1]], mesh.faces[v1loop[ii]])
  163.             V2 = selVertBetween2Faces(mesh.faces[v2loop[v2clampii-1]], mesh.faces[v2loop[v2clampii]])
  164.             
  165.             totalDist += measure(V1, V2)
  166.             # Bail out early if not an improvement on previously measured.
  167.             if bestSoFar != None and totalDist > bestSoFar:
  168.                 return totalDist
  169.     
  170.     #selVertBetween2Faces(mesh.faces[v2loop[0]], mesh.faces[v2loop[1]])
  171.     return totalDist
  172.  
  173. # Remove the shortest edge from a vert loop
  174. def removeSmallestFace(mesh, vloop):
  175.     bestDistSoFar = None
  176.     bestFIdxSoFar = None
  177.     for fIdx in vloop:
  178.         vSelLs = []
  179.         for v in mesh.faces[fIdx].v:
  180.             if v.sel:
  181.                 vSelLs.append(v)
  182.         
  183.         dist = measure(vSelLs[0].co, vSelLs[1].co)
  184.         
  185.         if bestDistSoFar == None:
  186.             bestDistSoFar = dist
  187.             bestFIdxSoFar = fIdx 
  188.         elif dist < bestDistSoFar:
  189.             bestDistSoFar = dist
  190.             bestFIdxSoFar = fIdx
  191.     
  192.     # Return the smallest face index of the vloop that was sent
  193.     return bestFIdxSoFar
  194.  
  195.  
  196. #=============================================#
  197. # Take 2 vert loops and skin them             #
  198. #=============================================#
  199. def skinVertLoops(mesh, v1loop, v2loop):
  200.     
  201.     
  202.     #=============================================#
  203.     # Handle uneven vert loops, this is tricky    #
  204.     #=============================================#
  205.     # Reorder so v1loop is always the biggest
  206.     if len(v1loop) < len(v2loop):
  207.         v1loop, v2loop = v2loop, v1loop
  208.     
  209.     # Work out if the vert loops are equel or not, if not remove the extra faces from the larger
  210.     surplusFaces = []
  211.     tempv1loop = v1loop[:]    # strip faces off this one, use it to keep track of which we have taken faces from.
  212.     if len(v1loop) > len(v2loop):
  213.         
  214.         # Even face method.
  215.         if arg[1] == '2':
  216.             remIdx = 0
  217.             faceStepping = len(    v1loop) / len(v2loop)
  218.             while len(v1loop) - len(surplusFaces) > len(v2loop):
  219.                 remIdx += faceStepping
  220.                 surplusFaces.append(tempv1loop[ clamp(len(tempv1loop),remIdx) ]) 
  221.                 tempv1loop.remove(surplusFaces[-1])
  222.         
  223.         # Shortest face
  224.         elif arg[1] == '1':
  225.             while len(v1loop) - len(surplusFaces) > len(v2loop):
  226.                 surplusFaces.append(removeSmallestFace(mesh, tempv1loop)) 
  227.                 tempv1loop.remove(surplusFaces[-1])
  228.             
  229.     
  230.     tempv1loop = None
  231.     
  232.     v2loop = optimizeLoopOrdedShortEdge(mesh, v1loop, v2loop, surplusFaces)
  233.     
  234.     # make Faces from 
  235.     lenVloop = len(v1loop)
  236.     lenSupFaces = len(surplusFaces)
  237.     fIdx = 0
  238.     offset = 0
  239.     while fIdx < lenVloop:
  240.         
  241.         face = copyFace( mesh.faces[v1loop[clamp(lenVloop, fIdx+1)]] )
  242.         
  243.         if v1loop[fIdx] in surplusFaces:
  244.             # Draw a try, this face does not catch with an edge.
  245.             # So we must draw a tri and wedge it in.
  246.             
  247.             # Copy old faces properties
  248.             
  249.             face.v.append( selVertBetween2Faces(\
  250.                 mesh.faces[v1loop[clamp(lenVloop, fIdx)]],\
  251.                 mesh.faces[v1loop[clamp(lenVloop, fIdx+1)]]) )
  252.             
  253.             face.v.append( selVertBetween2Faces(\
  254.                 mesh.faces[v1loop[clamp(lenVloop, fIdx+1)]],\
  255.                 mesh.faces[v1loop[clamp(lenVloop, fIdx+2)]]) )
  256.             
  257.             #face.v.append( selVertBetween2Faces(\
  258.             #mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset +1 ))]],\
  259.             #mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset + 2))]]) )
  260.             
  261.             face.v.append( selVertBetween2Faces(\
  262.                 mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset))]],\
  263.                 mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, fIdx - offset + 1)]]) )
  264.             
  265.             mesh.faces.append(face)            
  266.             
  267.             # We need offset to work out how much smaller v2loop is at this current index.
  268.             offset+=1        
  269.             
  270.  
  271.         else:    
  272.             # Draw a normal quad between the 2 edges/faces
  273.             
  274.             face.v.append( selVertBetween2Faces(\
  275.                 mesh.faces[v1loop[clamp(lenVloop, fIdx)]],\
  276.                 mesh.faces[v1loop[clamp(lenVloop, fIdx+1)]]) )
  277.             
  278.             face.v.append( selVertBetween2Faces(\
  279.                 mesh.faces[v1loop[clamp(lenVloop, fIdx+1)]],\
  280.                 mesh.faces[v1loop[clamp(lenVloop, fIdx+2)]]) )
  281.             
  282.             face.v.append( selVertBetween2Faces(\
  283.                 mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset +1 ))]],\
  284.                 mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset + 2))]]) )
  285.             
  286.             face.v.append( selVertBetween2Faces(\
  287.                 mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, (fIdx - offset))]],\
  288.                 mesh.faces[v2loop[clamp(lenVloop - lenSupFaces, fIdx - offset + 1)]]) )
  289.             
  290.             mesh.faces.append(face)
  291.             
  292.         fIdx +=1
  293.         
  294.     return mesh
  295.  
  296.  
  297.  
  298. #=======================================================#
  299. # Takes a face and returns the number of selected verts #
  300. #=======================================================#
  301. def faceVSel(face):
  302.     vSel = 0
  303.     for v in face.v:
  304.         if v.sel:
  305.             vSel +=1
  306.     return vSel
  307.  
  308.  
  309.  
  310.  
  311. #================================================================#
  312. # This function takes a face and returns its selected vert loop  #
  313. # it returns a list of face indicies
  314. #================================================================#
  315. def vertLoop(mesh, startFaceIdx, fIgLs): # fIgLs is a list of faces to ignore.
  316.     # Here we store the faces indicies that
  317.     # are a part of the first vertex loop
  318.     vertLoopLs = [startFaceIdx]
  319.  
  320.     restart = 0
  321.     while restart == 0:
  322.         # this keeps the face loop going until its told to stop,
  323.         # If the face loop does not find an adjacent face then the vert loop has been compleated
  324.         restart = 1 
  325.         
  326.         # Get my selected verts for the active face/edge.
  327.         selVerts = []
  328.         for v in mesh.faces[vertLoopLs[-1]].v:
  329.             selVerts.append(v)
  330.         
  331.         fIdx = 0
  332.         while fIdx < len(mesh.faces) and restart:
  333.             # Not already added to the vert list
  334.             if fIdx not in fIgLs + vertLoopLs:
  335.                 # Has 2 verts selected
  336.                 if faceVSel(mesh.faces[fIdx]) > 1:
  337.                     # Now we need to find if any of the selected verts
  338.                     # are shared with our active face. (are we next to ActiveFace)
  339.                     for v in mesh.faces[fIdx].v:
  340.                         if v in selVerts:
  341.                             vertLoopLs.append(fIdx)
  342.                             restart = 0 # restart the face loop.
  343.                             break
  344.                     
  345.             fIdx +=1
  346.             
  347.     return vertLoopLs
  348.  
  349.  
  350.  
  351.  
  352. #================================================================#
  353. # Now we work out the optimum order to 'skin' the 2 vert loops   #
  354. # by measuring the total distance of all edges created,          #
  355. # test this for every possible series of joins                   # 
  356. # and find the shortest, Once this is done the                   #
  357. # shortest dist can be skinned.                                  #
  358. # returns only the 2nd-reordered vert loop                       #
  359. #================================================================#
  360. def optimizeLoopOrded(mesh, v1loop, v2loop):
  361.     bestSoFar = None
  362.     
  363.     # Measure the dist, ii is just a counter
  364.     for ii in range(len(v1loop)):
  365.         
  366.         # Loop twice , Once for the forward test, and another for the revearsed
  367.         for iii in [None, None]:
  368.             dist = measureVloop(mesh, v1loop, v2loop, bestSoFar)
  369.             # Initialize the Best distance recorded
  370.             if bestSoFar == None or dist < bestSoFar:
  371.                 bestSoFar = dist
  372.                 bestv2Loop = v2loop[:]
  373.             
  374.             # We might have got the vert loop backwards, try the other way
  375.             v2loop.reverse()
  376.         listRotate(v2loop)
  377.     return bestv2Loop
  378.     
  379.     
  380. #================================================================#
  381. # Now we work out the optimum order to 'skin' the 2 vert loops   #
  382. # by measuring the total distance of all edges created,          #
  383. # test this for every possible series of joins                   # 
  384. # and find the shortest, Once this is done the                   #
  385. # shortest dist can be skinned.                                  #
  386. # returns only the 2nd-reordered vert loop                       #
  387. #================================================================#
  388. def optimizeLoopOrdedShortEdge(mesh, v1loop, v2loop, surplusFaces):
  389.     bestSoFar = None
  390.     
  391.     # Measure the dist, ii is just a counter
  392.     for ii in range(len(v2loop)):
  393.         
  394.         # Loop twice , Once for the forward test, and another for the revearsed
  395.         for iii in [None, None]:
  396.             dist = measureVloop(mesh, v1loop, v2loop, surplusFaces, bestSoFar)
  397.             print 'dist', dist 
  398.             # Initialize the Best distance recorded
  399.             if bestSoFar == None or dist < bestSoFar:
  400.                 bestSoFar = dist
  401.                 bestv2Loop = v2loop[:]
  402.                 
  403.             
  404.             # We might have got the vert loop backwards, try the other way
  405.             v2loop.reverse()
  406.         #v2loop = listRotate(v2loop)
  407.         listRotate(v2loop)
  408.     print 'best so far ', bestSoFar
  409.     return bestv2Loop    
  410.     
  411.  
  412. #==============================#
  413. #  Find our     vert loop list #
  414. #==============================#
  415. # Find a face with 2 verts selected,
  416. #this will be the first face in out vert loop
  417. def findVertLoop(mesh, fIgLs): # fIgLs is a list of faces to ignore.
  418.     
  419.     startFaceIdx = None
  420.     
  421.     fIdx = 0
  422.     while fIdx < len(mesh.faces):    
  423.         if fIdx not in fIgLs:
  424.             # Do we have an edge?
  425.             if faceVSel(mesh.faces[fIdx]) > 1:
  426.                 # THIS IS THE STARTING FACE.
  427.                 startFaceIdx = fIdx
  428.                 break
  429.         fIdx+=1
  430.     
  431.     # Here we access the function that generates the real vert loop
  432.     if startFaceIdx != None:
  433.         return vertLoop(mesh, startFaceIdx, fIgLs)
  434.     else:
  435.         # We are out'a vert loops, return a None,
  436.         return None
  437.  
  438. #===================================#
  439. # Get the average loc of a vertloop #
  440. # This is used when working out the #
  441. # order to loft an object           #
  442. #===================================#
  443. def vLoopAverageLoc(mesh, vertLoop):
  444.     locList = [] # List of vert locations
  445.         
  446.     fIdx = 0
  447.     while fIdx < len(mesh.faces):    
  448.         if fIdx in vertLoop:
  449.             for v in mesh.faces[fIdx].v:
  450.                 if v.sel:
  451.                     locList.append(v.co)
  452.         fIdx+=1
  453.     
  454.     return averageLocation(locList)
  455.  
  456.  
  457.  
  458. #=================================================#
  459. # Vert loop group functions
  460.  
  461. def getAllVertLoops(mesh):
  462.     # Make a chain of vert loops.
  463.     fIgLs = [] # List of faces to ignore 
  464.     allVLoops = [findVertLoop(mesh, fIgLs)]
  465.     while allVLoops[-1] != None:
  466.         
  467.         # In future ignore all faces in this vert loop
  468.         fIgLs += allVLoops[-1]        
  469.         
  470.         # Add the new vert loop to the list
  471.         allVLoops.append( findVertLoop(mesh, fIgLs) )
  472.     
  473.     return allVLoops[:-1] # Remove the last Value- None.
  474.     
  475.     
  476. def reorderCircularVLoops(mesh, allVLoops):
  477.     # Now get a location for each vert loop.
  478.     allVertLoopLocs = []
  479.     for vLoop in allVLoops:
  480.         allVertLoopLocs.append( vLoopAverageLoc(mesh, vLoop) )
  481.  
  482.     # We need to find the longest distance between 2 vert loops so we can 
  483.     reorderedVLoopLocs = []
  484.  
  485.     # Start with this one, then find the next closest.
  486.     # in doing this make a new list called reorderedVloop
  487.     currentVLoop = 0
  488.     reorderedVloopIdx = [currentVLoop]
  489.     newOrderVLoops = [allVLoops[0]] # This is a re-ordered allVLoops
  490.     while len(reorderedVloopIdx) != len(allVLoops):
  491.         bestSoFar = None
  492.         bestVIdxSoFar = None
  493.         for vLoopIdx in range(len(allVLoops)):
  494.             if vLoopIdx not in reorderedVloopIdx + [currentVLoop]:
  495.                 if bestSoFar == None:
  496.                     bestSoFar = measure( allVertLoopLocs[vLoopIdx], allVertLoopLocs[currentVLoop] )
  497.                     bestVIdxSoFar = vLoopIdx
  498.                 else:
  499.                     newDist = measure( allVertLoopLocs[vLoopIdx], allVertLoopLocs[currentVLoop] )
  500.                     if newDist < bestSoFar:
  501.                         bestSoFar = newDist
  502.                         bestVIdxSoFar = vLoopIdx
  503.         
  504.         reorderedVloopIdx.append(bestVIdxSoFar)
  505.         reorderedVLoopLocs.append(allVertLoopLocs[bestVIdxSoFar])
  506.         newOrderVLoops.append( allVLoops[bestVIdxSoFar] ) 
  507.         
  508.         # Start looking for the next best fit
  509.         currentVLoop = bestVIdxSoFar
  510.     
  511.     # This is not the locicle place to put this but its convieneint.
  512.     # Here we find the 2 vert loops that are most far apart
  513.     # We use this to work out which 2 vert loops not to skin when making an open loft.
  514.     vLoopIdx = 0
  515.     # Longest measured so far - 0 dummy.
  516.     bestSoFar = 0
  517.     while vLoopIdx < len(reorderedVLoopLocs):
  518.         
  519.         # Skin back to the start if needs be, becuase this is a crcular loft
  520.         toSkin2 = vLoopIdx + 1
  521.         if toSkin2 == len(reorderedVLoopLocs):
  522.             toSkin2 = 0
  523.         
  524.         newDist  = measure( reorderedVLoopLocs[vLoopIdx], reorderedVLoopLocs[toSkin2] )
  525.         
  526.         if newDist >= bestSoFar:
  527.             bestSoFar = newDist
  528.             vLoopIdxNotToSkin = vLoopIdx + 1    
  529.                 
  530.         vLoopIdx +=1 
  531.     
  532.     return newOrderVLoops, vLoopIdxNotToSkin
  533.  
  534.  
  535. is_editmode = Window.EditMode()
  536. if is_editmode: Window.EditMode(0)
  537.  
  538. # Get a mesh and raise errors if we cant
  539. mesh = None
  540. if choice == -1:
  541.     pass
  542. elif len(Object.GetSelected()) > 0:
  543.   if Object.GetSelected()[0].getType() == 'Mesh':
  544.     mesh = Object.GetSelected()[0].getData()
  545.   else:
  546.     error('please select a mesh')
  547. else:
  548.   error('no mesh object selected')
  549.  
  550. time1 = sys.time()
  551. if mesh != None:
  552.   Window.WaitCursor(1)
  553.   allVLoops = getAllVertLoops(mesh)
  554.   
  555.   # Re order the vert loops
  556.   allVLoops, vLoopIdxNotToSkin = reorderCircularVLoops(mesh, allVLoops)    
  557.   
  558.   vloopIdx = 0
  559.   while vloopIdx < len(allVLoops):
  560.     #print range(len(allVLoops) )
  561.     #print vloopIdx
  562.     #print allVLoops[vloopIdx]
  563.     
  564.     # Skin back to the start if needs be, becuase this is a crcular loft
  565.     toSkin2 = vloopIdx + 1
  566.     if toSkin2 == len(allVLoops):
  567.       toSkin2 = 0
  568.  
  569.     # Circular loft or not?
  570.     if arg[0] == 'B': # B for open
  571.       if vloopIdx != vLoopIdxNotToSkin:
  572.         mesh = skinVertLoops(mesh, allVLoops[vloopIdx], allVLoops[toSkin2])
  573.     elif arg[0] == 'A': # A for closed
  574.       mesh = skinVertLoops(mesh, allVLoops[vloopIdx], allVLoops[toSkin2])
  575.     
  576.     vloopIdx +=1    
  577.   
  578.   mesh.update(1,(mesh.edges != []),0)
  579.  
  580. if is_editmode: Window.EditMode(1)
  581. Window.WaitCursor(0)
  582. print "skinning time: %.2f" % (sys.time() - time1)
  583.