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>2009-11-29 02:37:56 +0300
committerCampbell Barton <ideasman42@gmail.com>2009-11-29 02:37:56 +0300
commit8b897879cd3925f701708e30710534aa5d0ae616 (patch)
tree69dc22e5be7f03d5196568b3ac810de27814670b /release/scripts/op
parent98a7a11e556e020265c4ca8d54e9609e9e86631c (diff)
pep8 cleanup in ui and op dirs, added popup to select pattern
Diffstat (limited to 'release/scripts/op')
-rw-r--r--release/scripts/op/mesh_skin.py1176
-rw-r--r--release/scripts/op/object.py44
-rw-r--r--release/scripts/op/presets.py16
-rw-r--r--release/scripts/op/uvcalc_smart_project.py2048
-rw-r--r--release/scripts/op/wm.py2
5 files changed, 1661 insertions, 1625 deletions
diff --git a/release/scripts/op/mesh_skin.py b/release/scripts/op/mesh_skin.py
index 63b5a44e901..5cf526cc23e 100644
--- a/release/scripts/op/mesh_skin.py
+++ b/release/scripts/op/mesh_skin.py
@@ -4,12 +4,12 @@
# 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.
@@ -29,620 +29,620 @@ from Mathutils import AngleBetweenVecs as _AngleBetweenVecs_
BIG_NUM = 1<<30
global CULL_METHOD
-CULL_METHOD = 0
+CULL_METHOD = 0
def AngleBetweenVecs(a1,a2):
- import math
- try:
- return math.degrees(_AngleBetweenVecs_(a1,a2))
- except:
- return 180.0
+ import math
+ try:
+ return math.degrees(_AngleBetweenVecs_(a1,a2))
+ except:
+ return 180.0
class edge(object):
- __slots__ = 'v1', 'v2', 'co1', 'co2', 'length', 'removed', 'match', 'cent', 'angle', 'next', 'prev', 'normal', 'fake'
- def __init__(self, v1,v2):
- self.v1 = v1
- self.v2 = v2
- co1, co2= v1.co, v2.co
- self.co1= co1
- self.co2= co2
-
- # uv1 uv2 vcol1 vcol2 # Add later
- self.length = (co1 - co2).length
- self.removed = 0 # Have we been culled from the eloop
- self.match = None # The other edge were making a face with
-
- self.cent= MidpointVecs(co1, co2)
- self.angle= 0.0
- self.fake= False
+ __slots__ = 'v1', 'v2', 'co1', 'co2', 'length', 'removed', 'match', 'cent', 'angle', 'next', 'prev', 'normal', 'fake'
+ def __init__(self, v1,v2):
+ self.v1 = v1
+ self.v2 = v2
+ co1, co2= v1.co, v2.co
+ self.co1= co1
+ self.co2= co2
+
+ # uv1 uv2 vcol1 vcol2 # Add later
+ self.length = (co1 - co2).length
+ self.removed = 0 # Have we been culled from the eloop
+ self.match = None # The other edge were making a face with
+
+ self.cent= MidpointVecs(co1, co2)
+ self.angle= 0.0
+ self.fake= False
class edgeLoop(object):
- __slots__ = 'centre', 'edges', 'normal', 'closed', 'backup_edges'
- def __init__(self, loop, me, closed): # Vert loop
- # Use next and prev, nextDist, prevDist
-
- # Get Loops centre.
- fac= len(loop)
- verts = me.verts
- self.centre= functools.reduce(lambda a,b: a+verts[b].co/fac, loop, Vector())
-
- # Convert Vert loop to Edges.
- self.edges = [edge(verts[loop[vIdx-1]], verts[loop[vIdx]]) for vIdx in range(len(loop))]
-
- if not closed:
- self.edges[0].fake = True # fake edge option
-
- self.closed = closed
-
-
- # Assign linked list
- for eIdx in range(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 = Vector()
- for e in self.edges:
- n = (self.centre-e.co1).cross(self.centre-e.co2)
- # Do we realy need tot normalize?
- n.normalize()
- self.normal += n
-
- # Generate the angle
- va= e.cent - e.prev.cent
- vb= e.next.cent - e.cent
-
- e.angle= AngleBetweenVecs(va, vb)
-
- # Blur the angles
- #for e in self.edges:
- # e.angle= (e.angle+e.next.angle)/2
-
- # Blur the angles
- #for e in self.edges:
- # e.angle= (e.angle+e.prev.angle)/2
-
- self.normal.normalize()
-
- # Generate a normal for each edge.
- for e in self.edges:
-
- n1 = e.co1
- n2 = e.co2
- n3 = e.prev.co1
-
- a = n1-n2
- b = n1-n3
- normal1 = a.cross(b)
- normal1.normalize()
-
- n1 = e.co2
- n3 = e.next.co2
- n2 = e.co1
-
- a = n1-n2
- b = n1-n3
-
- normal2 = a.cross(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.backup_edges = self.edges[:]
-
- def restore(self):
- self.edges = self.backup_edges[:]
- for e in self.edges:
- e.removed = 0
-
- def reverse(self):
- self.edges.reverse()
- self.normal.negate()
-
- for e in self.edges:
- e.normal.negate()
- e.v1, e.v2 = e.v2, e.v1
- e.co1, e.co2 = e.co2, e.co1
- e.next, e.prev = e.prev, e.next
-
-
- 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 == 1: # Shortest edge
- eloopCopy = self.edges[:]
-
- # Length sort, smallest first
- try: eloopCopy.sort(key = lambda e1: e1.length)
- except: eloopCopy.sort(lambda e1, e2: cmp(e1.length, e2.length ))
-
- # Dont use atm
- #eloopCopy.sort(lambda e1, e2: cmp(e1.angle*e1.length, e2.angle*e2.length)) # Length sort, smallest first
- #eloopCopy.sort(lambda e1, e2: cmp(e1.angle, e2.angle)) # Length sort, smallest first
-
- remNum = 0
- for i, e in enumerate(eloopCopy):
- if not e.fake:
- e.removed = 1
- self.edges.remove( e ) # Remove from own list, still in linked list.
- remNum += 1
-
- if not remNum < cullNum:
- break
-
- else: # CULL METHOD is even
-
- culled = 0
-
- step = int(otherLoopLen / float(cullNum)) * 2
-
- currentEdge = self.edges[0]
- while culled < cullNum:
-
- # Get the shortest face in the next STEP
- step_count= 0
- bestAng= 360.0
- smallestEdge= None
- while step_count<=step or smallestEdge==None:
- step_count+=1
- if not currentEdge.removed: # 0 or -1 will not be accepted
- if currentEdge.angle<bestAng and not currentEdge.fake:
- smallestEdge= currentEdge
- bestAng= currentEdge.angle
-
- currentEdge = currentEdge.next
-
- # In that stepping length we have the smallest edge.remove it
- smallestEdge.removed = 1
- self.edges.remove(smallestEdge)
-
- # Start scanning from the edge we found? - result is over fanning- no good.
- #currentEdge= smallestEdge.next
-
- culled+=1
-
+ __slots__ = 'centre', 'edges', 'normal', 'closed', 'backup_edges'
+ def __init__(self, loop, me, closed): # Vert loop
+ # Use next and prev, nextDist, prevDist
+
+ # Get Loops centre.
+ fac= len(loop)
+ verts = me.verts
+ self.centre= functools.reduce(lambda a,b: a+verts[b].co/fac, loop, Vector())
+
+ # Convert Vert loop to Edges.
+ self.edges = [edge(verts[loop[vIdx-1]], verts[loop[vIdx]]) for vIdx in range(len(loop))]
+
+ if not closed:
+ self.edges[0].fake = True # fake edge option
+
+ self.closed = closed
+
+
+ # Assign linked list
+ for eIdx in range(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 = Vector()
+ for e in self.edges:
+ n = (self.centre-e.co1).cross(self.centre-e.co2)
+ # Do we realy need tot normalize?
+ n.normalize()
+ self.normal += n
+
+ # Generate the angle
+ va= e.cent - e.prev.cent
+ vb= e.next.cent - e.cent
+
+ e.angle= AngleBetweenVecs(va, vb)
+
+ # Blur the angles
+ #for e in self.edges:
+ # e.angle= (e.angle+e.next.angle)/2
+
+ # Blur the angles
+ #for e in self.edges:
+ # e.angle= (e.angle+e.prev.angle)/2
+
+ self.normal.normalize()
+
+ # Generate a normal for each edge.
+ for e in self.edges:
+
+ n1 = e.co1
+ n2 = e.co2
+ n3 = e.prev.co1
+
+ a = n1-n2
+ b = n1-n3
+ normal1 = a.cross(b)
+ normal1.normalize()
+
+ n1 = e.co2
+ n3 = e.next.co2
+ n2 = e.co1
+
+ a = n1-n2
+ b = n1-n3
+
+ normal2 = a.cross(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.backup_edges = self.edges[:]
+
+ def restore(self):
+ self.edges = self.backup_edges[:]
+ for e in self.edges:
+ e.removed = 0
+
+ def reverse(self):
+ self.edges.reverse()
+ self.normal.negate()
+
+ for e in self.edges:
+ e.normal.negate()
+ e.v1, e.v2 = e.v2, e.v1
+ e.co1, e.co2 = e.co2, e.co1
+ e.next, e.prev = e.prev, e.next
+
+
+ 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 == 1: # Shortest edge
+ eloopCopy = self.edges[:]
+
+ # Length sort, smallest first
+ try: eloopCopy.sort(key = lambda e1: e1.length)
+ except: eloopCopy.sort(lambda e1, e2: cmp(e1.length, e2.length ))
+
+ # Dont use atm
+ #eloopCopy.sort(lambda e1, e2: cmp(e1.angle*e1.length, e2.angle*e2.length)) # Length sort, smallest first
+ #eloopCopy.sort(lambda e1, e2: cmp(e1.angle, e2.angle)) # Length sort, smallest first
+
+ remNum = 0
+ for i, e in enumerate(eloopCopy):
+ if not e.fake:
+ e.removed = 1
+ self.edges.remove( e ) # Remove from own list, still in linked list.
+ remNum += 1
+
+ if not remNum < cullNum:
+ break
+
+ else: # CULL METHOD is even
+
+ culled = 0
+
+ step = int(otherLoopLen / float(cullNum)) * 2
+
+ currentEdge = self.edges[0]
+ while culled < cullNum:
+
+ # Get the shortest face in the next STEP
+ step_count= 0
+ bestAng= 360.0
+ smallestEdge= None
+ while step_count<=step or smallestEdge==None:
+ step_count+=1
+ if not currentEdge.removed: # 0 or -1 will not be accepted
+ if currentEdge.angle<bestAng and not currentEdge.fake:
+ smallestEdge= currentEdge
+ bestAng= currentEdge.angle
+
+ currentEdge = currentEdge.next
+
+ # In that stepping length we have the smallest edge.remove it
+ smallestEdge.removed = 1
+ self.edges.remove(smallestEdge)
+
+ # Start scanning from the edge we found? - result is over fanning- no good.
+ #currentEdge= smallestEdge.next
+
+ culled+=1
+
# Returns face edges.
# face must have edge data.
-
+
def mesh_faces_extend(me, faces, mat_idx = 0):
- orig_facetot = len(me.faces)
- new_facetot = len(faces)
- me.add_geometry(0, 0, new_facetot)
- tot = orig_facetot+new_facetot
- me_faces = me.faces
- i= 0
- while i < new_facetot:
-
- f = [v.index for v in faces[i]]
- if len(f)==4:
- if f[3]==0:
- f = f[1], f[2], f[3], f[0]
- else:
- f = f[0], f[1], f[2], 0
-
- mf = me_faces[orig_facetot+i]
- mf.verts_raw = f
- mf.material_index = mat_idx
- i+=1
+ orig_facetot = len(me.faces)
+ new_facetot = len(faces)
+ me.add_geometry(0, 0, new_facetot)
+ tot = orig_facetot+new_facetot
+ me_faces = me.faces
+ i= 0
+ while i < new_facetot:
+
+ f = [v.index for v in faces[i]]
+ if len(f)==4:
+ if f[3]==0:
+ f = f[1], f[2], f[3], f[0]
+ else:
+ f = f[0], f[1], f[2], 0
+
+ mf = me_faces[orig_facetot+i]
+ mf.verts_raw = f
+ mf.material_index = mat_idx
+ i+=1
# end utils
def getSelectedEdges(context, me, ob):
- MESH_MODE= context.scene.tool_settings.mesh_selection_mode
-
- if MESH_MODE in ('EDGE', 'VERTEX'):
- context.scene.tool_settings.mesh_selection_mode = 'EDGE'
- edges= [ ed for ed in me.edges if ed.selected ]
- # print len(edges), len(me.edges)
- context.scene.tool_settings.mesh_selection_mode = MESH_MODE
- return edges
-
- if MESH_MODE == 'FACE':
- context.scene.tool_settings.mesh_selection_mode = 'EDGE'
- # value is [edge, face_sel_user_in]
- edge_dict= dict((ed.key, [ed, 0]) for ed in me.edges)
-
- for f in me.faces:
- if f.selected:
- for edkey in f.edge_keys:
- edge_dict[edkey][1] += 1
-
- context.scene.tool_settings.mesh_selection_mode = MESH_MODE
- return [ ed_data[0] for ed_data in edge_dict.values() if ed_data[1] == 1 ]
-
-
+ MESH_MODE= context.scene.tool_settings.mesh_selection_mode
+
+ if MESH_MODE in ('EDGE', 'VERTEX'):
+ context.scene.tool_settings.mesh_selection_mode = 'EDGE'
+ edges= [ ed for ed in me.edges if ed.selected ]
+ # print len(edges), len(me.edges)
+ context.scene.tool_settings.mesh_selection_mode = MESH_MODE
+ return edges
+
+ if MESH_MODE == 'FACE':
+ context.scene.tool_settings.mesh_selection_mode = 'EDGE'
+ # value is [edge, face_sel_user_in]
+ edge_dict= dict((ed.key, [ed, 0]) for ed in me.edges)
+
+ for f in me.faces:
+ if f.selected:
+ for edkey in f.edge_keys:
+ edge_dict[edkey][1] += 1
+
+ context.scene.tool_settings.mesh_selection_mode = MESH_MODE
+ return [ ed_data[0] for ed_data in edge_dict.values() if ed_data[1] == 1 ]
+
+
def getVertLoops(selEdges, me):
- '''
- return a list of vert loops, closed and open [(loop, closed)...]
- '''
-
- mainVertLoops = []
- # second method
- tot = len(me.verts)
- vert_siblings = [[] for i in range(tot)]
- vert_used = [False] * tot
-
- for ed in selEdges:
- i1, i2 = ed.key
- vert_siblings[i1].append(i2)
- vert_siblings[i2].append(i1)
-
- # find the first used vert and keep looping.
- for i in range(tot):
- if vert_siblings[i] and not vert_used[i]:
- sbl = vert_siblings[i] # siblings
-
- if len(sbl) > 2:
- return None
-
- vert_used[i] = True
-
- # do an edgeloop seek
- if len(sbl) == 2:
- contextVertLoop= [sbl[0], i, sbl[1]] # start the vert loop
- vert_used[contextVertLoop[ 0]] = True
- vert_used[contextVertLoop[-1]] = True
- else:
- contextVertLoop= [i, sbl[0]]
- vert_used[contextVertLoop[ 1]] = True
-
- # Always seek up
- ok = True
- while ok:
- ok = False
- closed = False
- sbl = vert_siblings[contextVertLoop[-1]]
- if len(sbl) == 2:
- next = sbl[not sbl.index( contextVertLoop[-2] )]
- if vert_used[next]:
- closed = True
- # break
- else:
- contextVertLoop.append( next ) # get the vert that isnt the second last
- vert_used[next] = True
- ok = True
-
- # Seek down as long as the starting vert was not at the edge.
- if not closed and len(vert_siblings[i]) == 2:
-
- ok = True
- while ok:
- ok = False
- sbl = vert_siblings[contextVertLoop[0]]
- if len(sbl) == 2:
- next = sbl[not sbl.index( contextVertLoop[1] )]
- if vert_used[next]:
- closed = True
- else:
- contextVertLoop.insert(0, next) # get the vert that isnt the second last
- vert_used[next] = True
- ok = True
-
- mainVertLoops.append((contextVertLoop, closed))
-
-
- verts = me.verts
- # convert from indicies to verts
- # mainVertLoops = [([verts[i] for i in contextVertLoop], closed) for contextVertLoop, closed in mainVertLoops]
- # print len(mainVertLoops)
- return mainVertLoops
-
+ '''
+ return a list of vert loops, closed and open [(loop, closed)...]
+ '''
+
+ mainVertLoops = []
+ # second method
+ tot = len(me.verts)
+ vert_siblings = [[] for i in range(tot)]
+ vert_used = [False] * tot
+
+ for ed in selEdges:
+ i1, i2 = ed.key
+ vert_siblings[i1].append(i2)
+ vert_siblings[i2].append(i1)
+
+ # find the first used vert and keep looping.
+ for i in range(tot):
+ if vert_siblings[i] and not vert_used[i]:
+ sbl = vert_siblings[i] # siblings
+
+ if len(sbl) > 2:
+ return None
+
+ vert_used[i] = True
+
+ # do an edgeloop seek
+ if len(sbl) == 2:
+ contextVertLoop= [sbl[0], i, sbl[1]] # start the vert loop
+ vert_used[contextVertLoop[ 0]] = True
+ vert_used[contextVertLoop[-1]] = True
+ else:
+ contextVertLoop= [i, sbl[0]]
+ vert_used[contextVertLoop[ 1]] = True
+
+ # Always seek up
+ ok = True
+ while ok:
+ ok = False
+ closed = False
+ sbl = vert_siblings[contextVertLoop[-1]]
+ if len(sbl) == 2:
+ next = sbl[not sbl.index( contextVertLoop[-2] )]
+ if vert_used[next]:
+ closed = True
+ # break
+ else:
+ contextVertLoop.append( next ) # get the vert that isnt the second last
+ vert_used[next] = True
+ ok = True
+
+ # Seek down as long as the starting vert was not at the edge.
+ if not closed and len(vert_siblings[i]) == 2:
+
+ ok = True
+ while ok:
+ ok = False
+ sbl = vert_siblings[contextVertLoop[0]]
+ if len(sbl) == 2:
+ next = sbl[not sbl.index( contextVertLoop[1] )]
+ if vert_used[next]:
+ closed = True
+ else:
+ contextVertLoop.insert(0, next) # get the vert that isnt the second last
+ vert_used[next] = True
+ ok = True
+
+ mainVertLoops.append((contextVertLoop, closed))
+
+
+ verts = me.verts
+ # convert from indicies to verts
+ # mainVertLoops = [([verts[i] for i in contextVertLoop], closed) for contextVertLoop, closed in mainVertLoops]
+ # print len(mainVertLoops)
+ 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. we keep it flipped, its ok,
- if eloop1.closed or eloop2.closed:
- angleBetweenLoopNormals = AngleBetweenVecs(eloop1.normal, eloop2.normal)
- if angleBetweenLoopNormals > 90:
- eloop2.reverse()
-
-
- DIR= eloop1.centre - eloop2.centre
-
- # if eloop2.closed:
- bestEloopDist = BIG_NUM
- bestOffset = 0
- # Loop rotation offset to test.1
- eLoopIdxs = list(range(len(eloop1.edges)))
- for offset in range(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 0uu to N, e2Idx is offset.
- for e1Idx, e2Idx in enumerate(offsetIndexLs):
- e1= eloop1.edges[e1Idx]
- e2= eloop2.edges[e2Idx]
-
-
- # Include fan connections in the measurement.
- OK= True
- while OK or e1.removed:
- OK= False
-
- # Measure the vloop distance ===============
- diff= ((e1.cent - e2.cent).length) #/ nangle1
-
- ed_dir= e1.cent-e2.cent
- a_diff= AngleBetweenVecs(DIR, ed_dir)/18 # 0 t0 18
-
- totEloopDist += (diff * (1+a_diff)) / (1+loopDist)
-
- # Premeture break if where no better off
- if totEloopDist > bestEloopDist:
- break
-
- e1=e1.next
-
- if totEloopDist < bestEloopDist:
- bestOffset = offset
- bestEloopDist = totEloopDist
-
- # Modify V2 LS for Best offset
- eloop2.edges = eloop2.edges[bestOffset:] + eloop2.edges[:bestOffset]
-
- else:
- # Both are open loops, easier to calculate.
-
-
- # Make sure the fake edges are at the start.
- for i, edloop in enumerate((eloop1, eloop2)):
- # print "LOOPO"
- if edloop.edges[0].fake:
- # alredy at the start
- #print "A"
- pass
- elif edloop.edges[-1].fake:
- # put the end at the start
- edloop.edges.insert(0, edloop.edges.pop())
- #print "B"
-
- else:
- for j, ed in enumerate(edloop.edges):
- if ed.fake:
- #print "C"
- edloop.edges = edloop.edges = edloop.edges[j:] + edloop.edges[:j]
- break
- # print "DONE"
- ed1, ed2 = eloop1.edges[0], eloop2.edges[0]
-
- if not ed1.fake or not ed2.fake:
- raise "Error"
-
- # Find the join that isnt flipped (juts like detecting a bow-tie face)
- a1 = (ed1.co1 - ed2.co1).length + (ed1.co2 - ed2.co2).length
- a2 = (ed1.co1 - ed2.co2).length + (ed1.co2 - ed2.co1).length
-
- if a1 > a2:
- eloop2.reverse()
- # make the first edge the start edge still
- eloop2.edges.insert(0, eloop2.edges.pop())
-
-
-
-
- for loopIdx in range(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
-
- if not (e1.fake or e2.fake):
- 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:
- #if not contextEdge.next.fake:
- 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
-
- # may need to fan fill backwards 1 for non closed loops.
-
- eloop1.restore() # Add culled back into the list.
-
- return new_faces
+
+ 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. we keep it flipped, its ok,
+ if eloop1.closed or eloop2.closed:
+ angleBetweenLoopNormals = AngleBetweenVecs(eloop1.normal, eloop2.normal)
+ if angleBetweenLoopNormals > 90:
+ eloop2.reverse()
+
+
+ DIR= eloop1.centre - eloop2.centre
+
+ # if eloop2.closed:
+ bestEloopDist = BIG_NUM
+ bestOffset = 0
+ # Loop rotation offset to test.1
+ eLoopIdxs = list(range(len(eloop1.edges)))
+ for offset in range(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 0uu to N, e2Idx is offset.
+ for e1Idx, e2Idx in enumerate(offsetIndexLs):
+ e1= eloop1.edges[e1Idx]
+ e2= eloop2.edges[e2Idx]
+
+
+ # Include fan connections in the measurement.
+ OK= True
+ while OK or e1.removed:
+ OK= False
+
+ # Measure the vloop distance ===============
+ diff= ((e1.cent - e2.cent).length) #/ nangle1
+
+ ed_dir= e1.cent-e2.cent
+ a_diff= AngleBetweenVecs(DIR, ed_dir)/18 # 0 t0 18
+
+ totEloopDist += (diff * (1+a_diff)) / (1+loopDist)
+
+ # Premeture break if where no better off
+ if totEloopDist > bestEloopDist:
+ break
+
+ e1=e1.next
+
+ if totEloopDist < bestEloopDist:
+ bestOffset = offset
+ bestEloopDist = totEloopDist
+
+ # Modify V2 LS for Best offset
+ eloop2.edges = eloop2.edges[bestOffset:] + eloop2.edges[:bestOffset]
+
+ else:
+ # Both are open loops, easier to calculate.
+
+
+ # Make sure the fake edges are at the start.
+ for i, edloop in enumerate((eloop1, eloop2)):
+ # print "LOOPO"
+ if edloop.edges[0].fake:
+ # alredy at the start
+ #print "A"
+ pass
+ elif edloop.edges[-1].fake:
+ # put the end at the start
+ edloop.edges.insert(0, edloop.edges.pop())
+ #print "B"
+
+ else:
+ for j, ed in enumerate(edloop.edges):
+ if ed.fake:
+ #print "C"
+ edloop.edges = edloop.edges = edloop.edges[j:] + edloop.edges[:j]
+ break
+ # print "DONE"
+ ed1, ed2 = eloop1.edges[0], eloop2.edges[0]
+
+ if not ed1.fake or not ed2.fake:
+ raise "Error"
+
+ # Find the join that isnt flipped (juts like detecting a bow-tie face)
+ a1 = (ed1.co1 - ed2.co1).length + (ed1.co2 - ed2.co2).length
+ a2 = (ed1.co1 - ed2.co2).length + (ed1.co2 - ed2.co1).length
+
+ if a1 > a2:
+ eloop2.reverse()
+ # make the first edge the start edge still
+ eloop2.edges.insert(0, eloop2.edges.pop())
+
+
+
+
+ for loopIdx in range(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
+
+ if not (e1.fake or e2.fake):
+ 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:
+ #if not contextEdge.next.fake:
+ 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
+
+ # may need to fan fill backwards 1 for non closed loops.
+
+ eloop1.restore() # Add culled back into the list.
+
+ return new_faces
def main(context):
- global CULL_METHOD
-
- ob = context.object
-
- is_editmode = (ob.mode=='EDIT')
- if is_editmode: bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
- if ob == None or ob.type != 'MESH':
- raise Exception("BPyMessages.Error_NoMeshActive()")
- return
-
- me = ob.data
-
- time1 = time.time()
- selEdges = getSelectedEdges(context, me, ob)
- vertLoops = getVertLoops(selEdges, me) # list of lists of edges.
- if vertLoops == None:
- raise Exception('Error%t|Selection includes verts that are a part of more then 1 loop')
- if is_editmode: bpy.ops.object.mode_set(mode='EDIT', toggle=False)
- return
- # print len(vertLoops)
-
-
- if len(vertLoops) > 2:
- choice = PupMenu('Loft '+str(len(vertLoops))+' edge loops%t|loop|segment')
- if choice == -1:
- if is_editmode: bpy.ops.object.mode_set(mode='EDIT', toggle=False)
- return
-
- elif len(vertLoops) < 2:
- raise Exception('Error%t|No Vertloops found!')
- if is_editmode: bpy.ops.object.mode_set(mode='EDIT', toggle=False)
- return
- else:
- choice = 2
-
-
- # The line below checks if any of the vert loops are differenyt in length.
- if False in [len(v[0]) == len(vertLoops[0][0]) for v in vertLoops]:
+ global CULL_METHOD
+
+ ob = context.object
+
+ is_editmode = (ob.mode=='EDIT')
+ if is_editmode: bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
+ if ob == None or ob.type != 'MESH':
+ raise Exception("BPyMessages.Error_NoMeshActive()")
+ return
+
+ me = ob.data
+
+ time1 = time.time()
+ selEdges = getSelectedEdges(context, me, ob)
+ vertLoops = getVertLoops(selEdges, me) # list of lists of edges.
+ if vertLoops == None:
+ raise Exception('Error%t|Selection includes verts that are a part of more then 1 loop')
+ if is_editmode: bpy.ops.object.mode_set(mode='EDIT', toggle=False)
+ return
+ # print len(vertLoops)
+
+
+ if len(vertLoops) > 2:
+ choice = PupMenu('Loft '+str(len(vertLoops))+' edge loops%t|loop|segment')
+ if choice == -1:
+ if is_editmode: bpy.ops.object.mode_set(mode='EDIT', toggle=False)
+ return
+
+ elif len(vertLoops) < 2:
+ raise Exception('Error%t|No Vertloops found!')
+ if is_editmode: bpy.ops.object.mode_set(mode='EDIT', toggle=False)
+ return
+ else:
+ choice = 2
+
+
+ # The line below checks if any of the vert loops are differenyt in length.
+ if False in [len(v[0]) == len(vertLoops[0][0]) for v in vertLoops]:
#XXX CULL_METHOD = PupMenu('Small to large edge loop distrobution method%t|remove edges evenly|remove smallest edges')
#XXX if CULL_METHOD == -1:
#XXX if is_editmode: Window.EditMode(1)
#XXX return
- CULL_METHOD = 1 # XXX FIXME
-
-
-
-
- if CULL_METHOD ==1: # RESET CULL_METHOD
- CULL_METHOD = 0 # shortest
- else:
- CULL_METHOD = 1 # even
-
-
- time1 = time.time()
- # Convert to special edge data.
- edgeLoops = []
- for vloop, closed in vertLoops:
- edgeLoops.append(edgeLoop(vloop, me, closed))
-
-
- # 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
-
- faces = []
-
- for i in range(len(edgeOrderedList)-1):
- faces.extend( skin2EdgeLoops(edgeOrderedList[i], edgeOrderedList[i+1], me, ob, 0) )
- if choice == 1 and len(edgeOrderedList) > 2: # Loop
- faces.extend( skin2EdgeLoops(edgeOrderedList[0], edgeOrderedList[-1], me, ob, 0) )
-
- # REMOVE SELECTED FACES.
- MESH_MODE= ob.mode
- if MESH_MODE == 'EDGE' or MESH_MODE == 'VERTEX': pass
- elif MESH_MODE == 'FACE':
- try: me.faces.delete(1, [ f for f in me.faces if f.sel ])
- except: pass
-
- if 1: # 2.5
- mesh_faces_extend(me, faces, ob.active_material_index)
- me.update(calc_edges=True)
- else:
- me.faces.extend(faces, smooth = True)
-
- print('\nSkin done in %.4f sec.' % (time.time()-time1))
-
- if is_editmode: bpy.ops.object.mode_set(mode='EDIT', toggle=False)
+ CULL_METHOD = 1 # XXX FIXME
+
+
+
+
+ if CULL_METHOD ==1: # RESET CULL_METHOD
+ CULL_METHOD = 0 # shortest
+ else:
+ CULL_METHOD = 1 # even
+
+
+ time1 = time.time()
+ # Convert to special edge data.
+ edgeLoops = []
+ for vloop, closed in vertLoops:
+ edgeLoops.append(edgeLoop(vloop, me, closed))
+
+
+ # 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
+
+ faces = []
+
+ for i in range(len(edgeOrderedList)-1):
+ faces.extend( skin2EdgeLoops(edgeOrderedList[i], edgeOrderedList[i+1], me, ob, 0) )
+ if choice == 1 and len(edgeOrderedList) > 2: # Loop
+ faces.extend( skin2EdgeLoops(edgeOrderedList[0], edgeOrderedList[-1], me, ob, 0) )
+
+ # REMOVE SELECTED FACES.
+ MESH_MODE= ob.mode
+ if MESH_MODE == 'EDGE' or MESH_MODE == 'VERTEX': pass
+ elif MESH_MODE == 'FACE':
+ try: me.faces.delete(1, [ f for f in me.faces if f.sel ])
+ except: pass
+
+ if 1: # 2.5
+ mesh_faces_extend(me, faces, ob.active_material_index)
+ me.update(calc_edges=True)
+ else:
+ me.faces.extend(faces, smooth = True)
+
+ print('\nSkin done in %.4f sec.' % (time.time()-time1))
+
+ if is_editmode: bpy.ops.object.mode_set(mode='EDIT', toggle=False)
class MESH_OT_skin(bpy.types.Operator):
- '''Bridge face loops.'''
-
- bl_idname = "mesh.skin"
- bl_label = "Add Torus"
- bl_register = True
- bl_undo = True
-
- '''
- loft_method = EnumProperty(attr="loft_method", items=[(), ()], description="", default= True)
-
- '''
-
- def execute(self, context):
- main(context)
- return ('FINISHED',)
+ '''Bridge face loops.'''
+
+ bl_idname = "mesh.skin"
+ bl_label = "Add Torus"
+ bl_register = True
+ bl_undo = True
+
+ '''
+ loft_method = EnumProperty(attr="loft_method", items=[(), ()], description="", default= True)
+
+ '''
+
+ def execute(self, context):
+ main(context)
+ return ('FINISHED',)
# Register the operator
@@ -653,4 +653,4 @@ import dynamic_menu
menu_item = dynamic_menu.add(bpy.types.VIEW3D_MT_edit_mesh_faces, (lambda self, context: self.layout.operator("mesh.skin", text="Bridge Faces")) )
if __name__ == "__main__":
- bpy.ops.mesh.skin()
+ bpy.ops.mesh.skin()
diff --git a/release/scripts/op/object.py b/release/scripts/op/object.py
index 4d3d8288833..50bbbf8158a 100644
--- a/release/scripts/op/object.py
+++ b/release/scripts/op/object.py
@@ -17,6 +17,42 @@
# ##### END GPL LICENSE BLOCK #####
import bpy
+from bpy.props import *
+
+
+class SelectPattern(bpy.types.Operator):
+ '''Select object matching a naming pattern.'''
+ bl_idname = "object.select_pattern"
+ bl_label = "Select Pattern"
+ bl_register = True
+ bl_undo = True
+
+ pattern = StringProperty(name="Pattern", description="Name filter using '*' and '?' wildcard chars", maxlen=32, default="*")
+ case_sensitive = BoolProperty(name="Case Sensitive", description="Do a case sensitive compare", default=False)
+ extend = BoolProperty(name="Extend", description="Extend the existing selection", default=True)
+
+ def execute(self, context):
+
+ import fnmatch
+
+ if self.properties.case_sensitive:
+ pattern_match = fnmatch.fnmatchcase
+ else:
+ pattern_match = lambda a, b: fnmatch.fnmatchcase(a.upper(), b.upper())
+
+ for ob in context.visible_objects:
+ if pattern_match(ob.name, self.properties.pattern):
+ ob.selected = True
+ elif not self.properties.extend:
+ ob.selected = False
+
+ return ('FINISHED',)
+
+ def invoke(self, context, event):
+ wm = context.manager
+ wm.invoke_props_popup(self, event)
+ return ('RUNNING_MODAL',)
+
class SubsurfSet(bpy.types.Operator):
'''Sets a Subdivision Surface Level (1-5)'''
@@ -25,8 +61,8 @@ class SubsurfSet(bpy.types.Operator):
bl_label = "Subsurf Set"
bl_register = True
bl_undo = True
-
- level = bpy.props.IntProperty(name="Level",
+
+ level = IntProperty(name="Level",
default=1, min=0, max=6)
def poll(self, context):
@@ -41,12 +77,12 @@ class SubsurfSet(bpy.types.Operator):
if mod.levels != level:
mod.levels = level
return ('FINISHED',)
-
+
# adda new modifier
mod = ob.modifiers.new("Subsurf", 'SUBSURF')
mod.levels = level
return ('FINISHED',)
-# Register the operator
+bpy.ops.add(SelectPattern)
bpy.ops.add(SubsurfSet)
diff --git a/release/scripts/op/presets.py b/release/scripts/op/presets.py
index 0b2e959ff0c..84a60765fa4 100644
--- a/release/scripts/op/presets.py
+++ b/release/scripts/op/presets.py
@@ -4,12 +4,12 @@
# 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.
@@ -21,14 +21,14 @@ import os
class AddPresetBase(bpy.types.Operator):
'''Base preset class, only for subclassing
- subclasses must define
+ subclasses must define
- preset_values
- preset_subdir '''
bl_idname = "render.preset_add"
bl_label = "Add Render Preset"
name = bpy.props.StringProperty(name="Name", description="Name of the preset, used to make the path name", maxlen= 64, default= "")
-
+
def _as_filename(self, name): # could reuse for other presets
for char in " !@#$%^&*(){}:\";'[]<>,./?":
name = name.replace('.', '_')
@@ -44,7 +44,7 @@ class AddPresetBase(bpy.types.Operator):
target_path = bpy.utils.preset_paths(self.preset_subdir)[0] # we need some way to tell the user and system preset path
file_preset = open(os.path.join(target_path, filename), 'w')
-
+
for rna_path in self.preset_values:
file_preset.write("%s = %s\n" % (rna_path, eval(rna_path)))
@@ -100,13 +100,13 @@ class AddPresetSSS(AddPresetBase):
]
preset_subdir = "sss"
-
+
class AddPresetCloth(AddPresetBase):
'''Add a Cloth Preset.'''
bl_idname = "cloth.preset_add"
bl_label = "Add Cloth Preset"
name = AddPresetBase.name
-
+
preset_values = [
"bpy.context.cloth.settings.quality",
"bpy.context.cloth.settings.mass",
@@ -115,7 +115,7 @@ class AddPresetCloth(AddPresetBase):
"bpy.context.cloth.settings.spring_damping",
"bpy.context.cloth.settings.air_damping",
]
-
+
preset_subdir = "cloth"
bpy.ops.add(AddPresetRender)
diff --git a/release/scripts/op/uvcalc_smart_project.py b/release/scripts/op/uvcalc_smart_project.py
index cc8aa5fe63a..2da7174ed99 100644
--- a/release/scripts/op/uvcalc_smart_project.py
+++ b/release/scripts/op/uvcalc_smart_project.py
@@ -1,24 +1,24 @@
-# --------------------------------------------------------------------------
-# Smart Projection UV Projection Unwrapper v1.2 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 *****
-# --------------------------------------------------------------------------
+# --------------------------------------------------------------------------
+# Smart Projection UV Projection Unwrapper v1.2 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 *****
+# --------------------------------------------------------------------------
#from Blender import Object, Draw, Window, sys, Mesh, Geometry
@@ -40,96 +40,96 @@ USER_FILL_HOLES_QUALITY = None
dict_matrix = {}
def pointInTri2D(v, v1, v2, v3):
- global dict_matrix
-
- key = v1.x, v1.y, v2.x, v2.y, v3.x, v3.y
-
- # Commented because its slower to do teh bounds check, we should realy cache the bounds info for each face.
- '''
- # BOUNDS CHECK
- xmin= 1000000
- ymin= 1000000
-
- xmax= -1000000
- ymax= -1000000
-
- for i in (0,2,4):
- x= key[i]
- y= key[i+1]
-
- if xmax<x: xmax= x
- if ymax<y: ymax= y
- if xmin>x: xmin= x
- if ymin>y: ymin= y
-
- x= v.x
- y= v.y
-
- if x<xmin or x>xmax or y < ymin or y > ymax:
- return False
- # Done with bounds check
- '''
- try:
- mtx = dict_matrix[key]
- if not mtx:
- return False
- except:
- side1 = v2 - v1
- side2 = v3 - v1
-
- nor = side1.cross(side2)
-
- l1 = [side1[0], side1[1], side1[2]]
- l2 = [side2[0], side2[1], side2[2]]
- l3 = [nor[0], nor[1], nor[2]]
-
- mtx = Matrix(l1, l2, l3)
-
- # Zero area 2d tri, even tho we throw away zerop area faces
- # the projection UV can result in a zero area UV.
- if not mtx.determinant():
- dict_matrix[key] = None
- return False
-
- mtx.invert()
-
- dict_matrix[key] = mtx
-
- uvw = (v - v1) * mtx
- return 0 <= uvw[0] and 0 <= uvw[1] and uvw[0] + uvw[1] <= 1
-
-
+ global dict_matrix
+
+ key = v1.x, v1.y, v2.x, v2.y, v3.x, v3.y
+
+ # Commented because its slower to do teh bounds check, we should realy cache the bounds info for each face.
+ '''
+ # BOUNDS CHECK
+ xmin= 1000000
+ ymin= 1000000
+
+ xmax= -1000000
+ ymax= -1000000
+
+ for i in (0,2,4):
+ x= key[i]
+ y= key[i+1]
+
+ if xmax<x: xmax= x
+ if ymax<y: ymax= y
+ if xmin>x: xmin= x
+ if ymin>y: ymin= y
+
+ x= v.x
+ y= v.y
+
+ if x<xmin or x>xmax or y < ymin or y > ymax:
+ return False
+ # Done with bounds check
+ '''
+ try:
+ mtx = dict_matrix[key]
+ if not mtx:
+ return False
+ except:
+ side1 = v2 - v1
+ side2 = v3 - v1
+
+ nor = side1.cross(side2)
+
+ l1 = [side1[0], side1[1], side1[2]]
+ l2 = [side2[0], side2[1], side2[2]]
+ l3 = [nor[0], nor[1], nor[2]]
+
+ mtx = Matrix(l1, l2, l3)
+
+ # Zero area 2d tri, even tho we throw away zerop area faces
+ # the projection UV can result in a zero area UV.
+ if not mtx.determinant():
+ dict_matrix[key] = None
+ return False
+
+ mtx.invert()
+
+ dict_matrix[key] = mtx
+
+ uvw = (v - v1) * mtx
+ return 0 <= uvw[0] and 0 <= uvw[1] and uvw[0] + uvw[1] <= 1
+
+
def boundsIsland(faces):
- minx = maxx = faces[0].uv[0][0] # Set initial bounds.
- miny = maxy = faces[0].uv[0][1]
- # print len(faces), minx, maxx, miny , maxy
- for f in faces:
- for uv in f.uv:
- x= uv.x
- y= uv.y
- if x<minx: minx= x
- if y<miny: miny= y
- if x>maxx: maxx= x
- if y>maxy: maxy= y
-
- return minx, miny, maxx, maxy
+ minx = maxx = faces[0].uv[0][0] # Set initial bounds.
+ miny = maxy = faces[0].uv[0][1]
+ # print len(faces), minx, maxx, miny , maxy
+ for f in faces:
+ for uv in f.uv:
+ x= uv.x
+ y= uv.y
+ if x<minx: minx= x
+ if y<miny: miny= y
+ if x>maxx: maxx= x
+ if y>maxy: maxy= y
+
+ return minx, miny, maxx, maxy
"""
def boundsEdgeLoop(edges):
- minx = maxx = edges[0][0] # Set initial bounds.
- miny = maxy = edges[0][1]
- # print len(faces), minx, maxx, miny , maxy
- for ed in edges:
- for pt in ed:
- print 'ass'
- x= pt[0]
- y= pt[1]
- if x<minx: x= minx
- if y<miny: y= miny
- if x>maxx: x= maxx
- if y>maxy: y= maxy
-
- return minx, miny, maxx, maxy
+ minx = maxx = edges[0][0] # Set initial bounds.
+ miny = maxy = edges[0][1]
+ # print len(faces), minx, maxx, miny , maxy
+ for ed in edges:
+ for pt in ed:
+ print 'ass'
+ x= pt[0]
+ y= pt[1]
+ if x<minx: x= minx
+ if y<miny: y= miny
+ if x>maxx: x= maxx
+ if y>maxy: y= maxy
+
+ return minx, miny, maxx, maxy
"""
# Turns the islands into a list of unpordered edges (Non internal)
@@ -137,44 +137,44 @@ def boundsEdgeLoop(edges):
# only returns outline edges for intersection tests. and unique points.
def island2Edge(island):
-
- # Vert index edges
- edges = {}
-
- unique_points= {}
-
- for f in island:
- f_uvkey= map(tuple, f.uv)
-
-
- for vIdx, edkey in enumerate(f.edge_keys):
- unique_points[f_uvkey[vIdx]] = f.uv[vIdx]
-
- if f.v[vIdx].index > f.v[vIdx-1].index:
- i1= vIdx-1; i2= vIdx
- else:
- i1= vIdx; i2= vIdx-1
-
- try: edges[ f_uvkey[i1], f_uvkey[i2] ] *= 0 # sets eny edge with more then 1 user to 0 are not returned.
- except: edges[ f_uvkey[i1], f_uvkey[i2] ] = (f.uv[i1] - f.uv[i2]).length,
-
- # If 2 are the same then they will be together, but full [a,b] order is not correct.
-
- # Sort by length
-
-
- length_sorted_edges = [(Vector(key[0]), Vector(key[1]), value) for key, value in edges.items() if value != 0]
-
- try: length_sorted_edges.sort(key = lambda A: -A[2]) # largest first
- except: length_sorted_edges.sort(lambda A, B: cmp(B[2], A[2]))
-
- # Its okay to leave the length in there.
- #for e in length_sorted_edges:
- # e.pop(2)
-
- # return edges and unique points
- return length_sorted_edges, [v.__copy__().resize3D() for v in unique_points.values()]
-
+
+ # Vert index edges
+ edges = {}
+
+ unique_points= {}
+
+ for f in island:
+ f_uvkey= map(tuple, f.uv)
+
+
+ for vIdx, edkey in enumerate(f.edge_keys):
+ unique_points[f_uvkey[vIdx]] = f.uv[vIdx]
+
+ if f.v[vIdx].index > f.v[vIdx-1].index:
+ i1= vIdx-1; i2= vIdx
+ else:
+ i1= vIdx; i2= vIdx-1
+
+ try: edges[ f_uvkey[i1], f_uvkey[i2] ] *= 0 # sets eny edge with more then 1 user to 0 are not returned.
+ except: edges[ f_uvkey[i1], f_uvkey[i2] ] = (f.uv[i1] - f.uv[i2]).length,
+
+ # If 2 are the same then they will be together, but full [a,b] order is not correct.
+
+ # Sort by length
+
+
+ length_sorted_edges = [(Vector(key[0]), Vector(key[1]), value) for key, value in edges.items() if value != 0]
+
+ try: length_sorted_edges.sort(key = lambda A: -A[2]) # largest first
+ except: length_sorted_edges.sort(lambda A, B: cmp(B[2], A[2]))
+
+ # Its okay to leave the length in there.
+ #for e in length_sorted_edges:
+ # e.pop(2)
+
+ # return edges and unique points
+ return length_sorted_edges, [v.__copy__().resize3D() for v in unique_points.values()]
+
# ========================= NOT WORKING????
# Find if a points inside an edge loop, un-orderd.
# pt is and x/y
@@ -182,97 +182,97 @@ def island2Edge(island):
# #offsets are the edge x and y offset.
"""
def pointInEdges(pt, edges):
- #
- x1 = pt[0]
- y1 = pt[1]
-
- # Point to the left of this line.
- x2 = -100000
- y2 = -10000
- intersectCount = 0
- for ed in edges:
- xi, yi = lineIntersection2D(x1,y1, x2,y2, ed[0][0], ed[0][1], ed[1][0], ed[1][1])
- if xi != None: # Is there an intersection.
- intersectCount+=1
-
- return intersectCount % 2
+ #
+ x1 = pt[0]
+ y1 = pt[1]
+
+ # Point to the left of this line.
+ x2 = -100000
+ y2 = -10000
+ intersectCount = 0
+ for ed in edges:
+ xi, yi = lineIntersection2D(x1,y1, x2,y2, ed[0][0], ed[0][1], ed[1][0], ed[1][1])
+ if xi != None: # Is there an intersection.
+ intersectCount+=1
+
+ return intersectCount % 2
"""
def pointInIsland(pt, island):
- vec1 = Vector(); vec2 = Vector(); vec3 = Vector()
- for f in island:
- vec1.x, vec1.y = f.uv[0]
- vec2.x, vec2.y = f.uv[1]
- vec3.x, vec3.y = f.uv[2]
-
- if pointInTri2D(pt, vec1, vec2, vec3):
- return True
-
- if len(f.v) == 4:
- vec1.x, vec1.y = f.uv[0]
- vec2.x, vec2.y = f.uv[2]
- vec3.x, vec3.y = f.uv[3]
- if pointInTri2D(pt, vec1, vec2, vec3):
- return True
- return False
+ vec1 = Vector(); vec2 = Vector(); vec3 = Vector()
+ for f in island:
+ vec1.x, vec1.y = f.uv[0]
+ vec2.x, vec2.y = f.uv[1]
+ vec3.x, vec3.y = f.uv[2]
+
+ if pointInTri2D(pt, vec1, vec2, vec3):
+ return True
+
+ if len(f.v) == 4:
+ vec1.x, vec1.y = f.uv[0]
+ vec2.x, vec2.y = f.uv[2]
+ vec3.x, vec3.y = f.uv[3]
+ if pointInTri2D(pt, vec1, vec2, vec3):
+ return True
+ return False
# box is (left,bottom, right, top)
def islandIntersectUvIsland(source, target, SourceOffset):
- # Is 1 point in the box, inside the vertLoops
- edgeLoopsSource = source[6] # Pretend this is offset
- edgeLoopsTarget = target[6]
-
- # Edge intersect test
- for ed in edgeLoopsSource:
- for seg in edgeLoopsTarget:
- i = Geometry.LineIntersect2D(\
- seg[0], seg[1], SourceOffset+ed[0], SourceOffset+ed[1])
- if i:
- return 1 # LINE INTERSECTION
-
- # 1 test for source being totally inside target
- SourceOffset.resize3D()
- for pv in source[7]:
- if pointInIsland(pv+SourceOffset, target[0]):
- return 2 # SOURCE INSIDE TARGET
-
- # 2 test for a part of the target being totaly inside the source.
- for pv in target[7]:
- if pointInIsland(pv-SourceOffset, source[0]):
- return 3 # PART OF TARGET INSIDE SOURCE.
-
- return 0 # NO INTERSECTION
+ # Is 1 point in the box, inside the vertLoops
+ edgeLoopsSource = source[6] # Pretend this is offset
+ edgeLoopsTarget = target[6]
+
+ # Edge intersect test
+ for ed in edgeLoopsSource:
+ for seg in edgeLoopsTarget:
+ i = Geometry.LineIntersect2D(\
+ seg[0], seg[1], SourceOffset+ed[0], SourceOffset+ed[1])
+ if i:
+ return 1 # LINE INTERSECTION
+
+ # 1 test for source being totally inside target
+ SourceOffset.resize3D()
+ for pv in source[7]:
+ if pointInIsland(pv+SourceOffset, target[0]):
+ return 2 # SOURCE INSIDE TARGET
+
+ # 2 test for a part of the target being totaly inside the source.
+ for pv in target[7]:
+ if pointInIsland(pv-SourceOffset, source[0]):
+ return 3 # PART OF TARGET INSIDE SOURCE.
+
+ return 0 # NO INTERSECTION
# Returns the X/y Bounds of a list of vectors.
def testNewVecLs2DRotIsBetter(vecs, mat=-1, bestAreaSoFar = -1):
-
- # UV's will never extend this far.
- minx = miny = BIG_NUM
- maxx = maxy = -BIG_NUM
-
- for i, v in enumerate(vecs):
-
- # Do this allong the way
- if mat != -1:
- v = vecs[i] = v*mat
- x= v.x
- y= v.y
- if x<minx: minx= x
- if y<miny: miny= y
- if x>maxx: maxx= x
- if y>maxy: maxy= y
-
- # Spesific to this algo, bail out if we get bigger then the current area
- if bestAreaSoFar != -1 and (maxx-minx) * (maxy-miny) > bestAreaSoFar:
- return (BIG_NUM, None), None
- w = maxx-minx
- h = maxy-miny
- return (w*h, w,h), vecs # Area, vecs
-
+
+ # UV's will never extend this far.
+ minx = miny = BIG_NUM
+ maxx = maxy = -BIG_NUM
+
+ for i, v in enumerate(vecs):
+
+ # Do this allong the way
+ if mat != -1:
+ v = vecs[i] = v*mat
+ x= v.x
+ y= v.y
+ if x<minx: minx= x
+ if y<miny: miny= y
+ if x>maxx: maxx= x
+ if y>maxy: maxy= y
+
+ # Spesific to this algo, bail out if we get bigger then the current area
+ if bestAreaSoFar != -1 and (maxx-minx) * (maxy-miny) > bestAreaSoFar:
+ return (BIG_NUM, None), None
+ w = maxx-minx
+ h = maxy-miny
+ return (w*h, w,h), vecs # Area, vecs
+
# Takes a list of faces that make up a UV island and rotate
# until they optimally fit inside a square.
ROTMAT_2D_POS_90D = RotationMatrix( radians(90.0), 2)
@@ -281,855 +281,855 @@ ROTMAT_2D_POS_45D = RotationMatrix( radians(45.0), 2)
RotMatStepRotation = []
rot_angle = 22.5 #45.0/2
while rot_angle > 0.1:
- RotMatStepRotation.append([\
- RotationMatrix( radians(rot_angle), 2),\
- RotationMatrix( radians(-rot_angle), 2)])
-
- rot_angle = rot_angle/2.0
-
+ RotMatStepRotation.append([\
+ RotationMatrix( radians(rot_angle), 2),\
+ RotationMatrix( radians(-rot_angle), 2)])
+
+ rot_angle = rot_angle/2.0
+
def optiRotateUvIsland(faces):
- global currentArea
-
- # Bestfit Rotation
- def best2dRotation(uvVecs, MAT1, MAT2):
- global currentArea
-
- newAreaPos, newfaceProjectionGroupListPos =\
- testNewVecLs2DRotIsBetter(uvVecs[:], MAT1, currentArea[0])
-
-
- # Why do I use newpos here? May as well give the best area to date for an early bailout
- # some slight speed increase in this.
- # If the new rotation is smaller then the existing, we can
- # avoid copying a list and overwrite the old, crappy one.
-
- if newAreaPos[0] < currentArea[0]:
- newAreaNeg, newfaceProjectionGroupListNeg =\
- testNewVecLs2DRotIsBetter(uvVecs, MAT2, newAreaPos[0]) # Reuse the old bigger list.
- else:
- newAreaNeg, newfaceProjectionGroupListNeg =\
- testNewVecLs2DRotIsBetter(uvVecs[:], MAT2, currentArea[0]) # Cant reuse, make a copy.
-
-
- # Now from the 3 options we need to discover which to use
- # we have cerrentArea/newAreaPos/newAreaNeg
- bestArea = min(currentArea[0], newAreaPos[0], newAreaNeg[0])
-
- if currentArea[0] == bestArea:
- return uvVecs
- elif newAreaPos[0] == bestArea:
- uvVecs = newfaceProjectionGroupListPos
- currentArea = newAreaPos
- elif newAreaNeg[0] == bestArea:
- uvVecs = newfaceProjectionGroupListNeg
- currentArea = newAreaNeg
-
- return uvVecs
-
-
- # Serialized UV coords to Vectors
- uvVecs = [uv for f in faces for uv in f.uv]
-
- # Theres a small enough number of these to hard code it
- # rather then a loop.
-
- # Will not modify anything
- currentArea, dummy =\
- testNewVecLs2DRotIsBetter(uvVecs)
-
-
- # Try a 45d rotation
- newAreaPos, newfaceProjectionGroupListPos = testNewVecLs2DRotIsBetter(uvVecs[:], ROTMAT_2D_POS_45D, currentArea[0])
-
- if newAreaPos[0] < currentArea[0]:
- uvVecs = newfaceProjectionGroupListPos
- currentArea = newAreaPos
- # 45d done
-
- # Testcase different rotations and find the onfe that best fits in a square
- for ROTMAT in RotMatStepRotation:
- uvVecs = best2dRotation(uvVecs, ROTMAT[0], ROTMAT[1])
-
- # Only if you want it, make faces verticle!
- if currentArea[1] > currentArea[2]:
- # Rotate 90d
- # Work directly on the list, no need to return a value.
- testNewVecLs2DRotIsBetter(uvVecs, ROTMAT_2D_POS_90D)
-
-
- # Now write the vectors back to the face UV's
- i = 0 # count the serialized uv/vectors
- for f in faces:
- #f.uv = [uv for uv in uvVecs[i:len(f)+i] ]
- for j, k in enumerate(range(i, len(f.v)+i)):
- f.uv[j][:] = uvVecs[k]
- i += len(f.v)
+ global currentArea
+
+ # Bestfit Rotation
+ def best2dRotation(uvVecs, MAT1, MAT2):
+ global currentArea
+
+ newAreaPos, newfaceProjectionGroupListPos =\
+ testNewVecLs2DRotIsBetter(uvVecs[:], MAT1, currentArea[0])
+
+
+ # Why do I use newpos here? May as well give the best area to date for an early bailout
+ # some slight speed increase in this.
+ # If the new rotation is smaller then the existing, we can
+ # avoid copying a list and overwrite the old, crappy one.
+
+ if newAreaPos[0] < currentArea[0]:
+ newAreaNeg, newfaceProjectionGroupListNeg =\
+ testNewVecLs2DRotIsBetter(uvVecs, MAT2, newAreaPos[0]) # Reuse the old bigger list.
+ else:
+ newAreaNeg, newfaceProjectionGroupListNeg =\
+ testNewVecLs2DRotIsBetter(uvVecs[:], MAT2, currentArea[0]) # Cant reuse, make a copy.
+
+
+ # Now from the 3 options we need to discover which to use
+ # we have cerrentArea/newAreaPos/newAreaNeg
+ bestArea = min(currentArea[0], newAreaPos[0], newAreaNeg[0])
+
+ if currentArea[0] == bestArea:
+ return uvVecs
+ elif newAreaPos[0] == bestArea:
+ uvVecs = newfaceProjectionGroupListPos
+ currentArea = newAreaPos
+ elif newAreaNeg[0] == bestArea:
+ uvVecs = newfaceProjectionGroupListNeg
+ currentArea = newAreaNeg
+
+ return uvVecs
+
+
+ # Serialized UV coords to Vectors
+ uvVecs = [uv for f in faces for uv in f.uv]
+
+ # Theres a small enough number of these to hard code it
+ # rather then a loop.
+
+ # Will not modify anything
+ currentArea, dummy =\
+ testNewVecLs2DRotIsBetter(uvVecs)
+
+
+ # Try a 45d rotation
+ newAreaPos, newfaceProjectionGroupListPos = testNewVecLs2DRotIsBetter(uvVecs[:], ROTMAT_2D_POS_45D, currentArea[0])
+
+ if newAreaPos[0] < currentArea[0]:
+ uvVecs = newfaceProjectionGroupListPos
+ currentArea = newAreaPos
+ # 45d done
+
+ # Testcase different rotations and find the onfe that best fits in a square
+ for ROTMAT in RotMatStepRotation:
+ uvVecs = best2dRotation(uvVecs, ROTMAT[0], ROTMAT[1])
+
+ # Only if you want it, make faces verticle!
+ if currentArea[1] > currentArea[2]:
+ # Rotate 90d
+ # Work directly on the list, no need to return a value.
+ testNewVecLs2DRotIsBetter(uvVecs, ROTMAT_2D_POS_90D)
+
+
+ # Now write the vectors back to the face UV's
+ i = 0 # count the serialized uv/vectors
+ for f in faces:
+ #f.uv = [uv for uv in uvVecs[i:len(f)+i] ]
+ for j, k in enumerate(range(i, len(f.v)+i)):
+ f.uv[j][:] = uvVecs[k]
+ i += len(f.v)
# Takes an island list and tries to find concave, hollow areas to pack smaller islands into.
def mergeUvIslands(islandList):
- global USER_FILL_HOLES
- global USER_FILL_HOLES_QUALITY
-
-
- # Pack islands to bottom LHS
- # Sync with island
-
- #islandTotFaceArea = [] # A list of floats, each island area
- #islandArea = [] # a list of tuples ( area, w,h)
-
-
- decoratedIslandList = []
-
- islandIdx = len(islandList)
- while islandIdx:
- islandIdx-=1
- minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
- w, h = maxx-minx, maxy-miny
-
- totFaceArea = 0
- offset= Vector(minx, miny)
- for f in islandList[islandIdx]:
- for uv in f.uv:
- uv -= offset
-
- totFaceArea += f.area
-
- islandBoundsArea = w*h
- efficiency = abs(islandBoundsArea - totFaceArea)
-
- # UV Edge list used for intersections as well as unique points.
- edges, uniqueEdgePoints = island2Edge(islandList[islandIdx])
-
- decoratedIslandList.append([islandList[islandIdx], totFaceArea, efficiency, islandBoundsArea, w,h, edges, uniqueEdgePoints])
-
-
- # Sort by island bounding box area, smallest face area first.
- # no.. chance that to most simple edge loop first.
- decoratedIslandListAreaSort =decoratedIslandList[:]
-
- decoratedIslandListAreaSort.sort(key = lambda A: A[3])
-
- # sort by efficiency, Least Efficient first.
- decoratedIslandListEfficSort = decoratedIslandList[:]
- # decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], A[2]))
-
- decoratedIslandListEfficSort.sort(key = lambda A: -A[2])
-
- # ================================================== THESE CAN BE TWEAKED.
- # This is a quality value for the number of tests.
- # from 1 to 4, generic quality value is from 1 to 100
- USER_STEP_QUALITY = ((USER_FILL_HOLES_QUALITY - 1) / 25.0) + 1
-
- # If 100 will test as long as there is enough free space.
- # this is rarely enough, and testing takes a while, so lower quality speeds this up.
-
- # 1 means they have the same quality
- USER_FREE_SPACE_TO_TEST_QUALITY = 1 + (((100 - USER_FILL_HOLES_QUALITY)/100.0) *5)
-
- #print 'USER_STEP_QUALITY', USER_STEP_QUALITY
- #print 'USER_FREE_SPACE_TO_TEST_QUALITY', USER_FREE_SPACE_TO_TEST_QUALITY
-
- removedCount = 0
-
- areaIslandIdx = 0
- ctrl = Window.Qual.CTRL
- BREAK= False
- while areaIslandIdx < len(decoratedIslandListAreaSort) and not BREAK:
- sourceIsland = decoratedIslandListAreaSort[areaIslandIdx]
- # Alredy packed?
- if not sourceIsland[0]:
- areaIslandIdx+=1
- else:
- efficIslandIdx = 0
- while efficIslandIdx < len(decoratedIslandListEfficSort) and not BREAK:
-
- if Window.GetKeyQualifiers() & ctrl:
- BREAK= True
- break
-
- # Now we have 2 islands, is the efficience of the islands lowers theres an
- # increasing likely hood that we can fit merge into the bigger UV island.
- # this ensures a tight fit.
-
- # Just use figures we have about user/unused area to see if they might fit.
-
- targetIsland = decoratedIslandListEfficSort[efficIslandIdx]
-
-
- if sourceIsland[0] == targetIsland[0] or\
- not targetIsland[0] or\
- not sourceIsland[0]:
- pass
- else:
-
- # ([island, totFaceArea, efficiency, islandArea, w,h])
- # Waisted space on target is greater then UV bounding island area.
-
-
- # if targetIsland[3] > (sourceIsland[2]) and\ #
- # print USER_FREE_SPACE_TO_TEST_QUALITY, 'ass'
- if targetIsland[2] > (sourceIsland[1] * USER_FREE_SPACE_TO_TEST_QUALITY) and\
- targetIsland[4] > sourceIsland[4] and\
- targetIsland[5] > sourceIsland[5]:
-
- # DEBUG # print '%.10f %.10f' % (targetIsland[3], sourceIsland[1])
-
- # These enough spare space lets move the box until it fits
-
- # How many times does the source fit into the target x/y
- blockTestXUnit = targetIsland[4]/sourceIsland[4]
- blockTestYUnit = targetIsland[5]/sourceIsland[5]
-
- boxLeft = 0
-
-
- # Distllllance we can move between whilst staying inside the targets bounds.
- testWidth = targetIsland[4] - sourceIsland[4]
- testHeight = targetIsland[5] - sourceIsland[5]
-
- # Increment we move each test. x/y
- xIncrement = (testWidth / (blockTestXUnit * ((USER_STEP_QUALITY/50)+0.1)))
- yIncrement = (testHeight / (blockTestYUnit * ((USER_STEP_QUALITY/50)+0.1)))
-
- # Make sure were not moving less then a 3rg of our width/height
- if xIncrement<sourceIsland[4]/3:
- xIncrement= sourceIsland[4]
- if yIncrement<sourceIsland[5]/3:
- yIncrement= sourceIsland[5]
-
-
- boxLeft = 0 # Start 1 back so we can jump into the loop.
- boxBottom= 0 #-yIncrement
-
- ##testcount= 0
-
- while boxBottom <= testHeight:
- # Should we use this? - not needed for now.
- #if Window.GetKeyQualifiers() & ctrl:
- # BREAK= True
- # break
-
- ##testcount+=1
- #print 'Testing intersect'
- Intersect = islandIntersectUvIsland(sourceIsland, targetIsland, Vector(boxLeft, boxBottom))
- #print 'Done', Intersect
- if Intersect == 1: # Line intersect, dont bother with this any more
- pass
-
- if Intersect == 2: # Source inside target
- '''
- We have an intersection, if we are inside the target
- then move us 1 whole width accross,
- Its possible this is a bad idea since 2 skinny Angular faces
- could join without 1 whole move, but its a lot more optimal to speed this up
- since we have alredy tested for it.
-
- It gives about 10% speedup with minimal errors.
- '''
- #print 'ass'
- # Move the test allong its width + SMALL_NUM
- #boxLeft += sourceIsland[4] + SMALL_NUM
- boxLeft += sourceIsland[4]
- elif Intersect == 0: # No intersection?? Place it.
- # Progress
- removedCount +=1
+ global USER_FILL_HOLES
+ global USER_FILL_HOLES_QUALITY
+
+
+ # Pack islands to bottom LHS
+ # Sync with island
+
+ #islandTotFaceArea = [] # A list of floats, each island area
+ #islandArea = [] # a list of tuples ( area, w,h)
+
+
+ decoratedIslandList = []
+
+ islandIdx = len(islandList)
+ while islandIdx:
+ islandIdx-=1
+ minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
+ w, h = maxx-minx, maxy-miny
+
+ totFaceArea = 0
+ offset= Vector(minx, miny)
+ for f in islandList[islandIdx]:
+ for uv in f.uv:
+ uv -= offset
+
+ totFaceArea += f.area
+
+ islandBoundsArea = w*h
+ efficiency = abs(islandBoundsArea - totFaceArea)
+
+ # UV Edge list used for intersections as well as unique points.
+ edges, uniqueEdgePoints = island2Edge(islandList[islandIdx])
+
+ decoratedIslandList.append([islandList[islandIdx], totFaceArea, efficiency, islandBoundsArea, w,h, edges, uniqueEdgePoints])
+
+
+ # Sort by island bounding box area, smallest face area first.
+ # no.. chance that to most simple edge loop first.
+ decoratedIslandListAreaSort =decoratedIslandList[:]
+
+ decoratedIslandListAreaSort.sort(key = lambda A: A[3])
+
+ # sort by efficiency, Least Efficient first.
+ decoratedIslandListEfficSort = decoratedIslandList[:]
+ # decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], A[2]))
+
+ decoratedIslandListEfficSort.sort(key = lambda A: -A[2])
+
+ # ================================================== THESE CAN BE TWEAKED.
+ # This is a quality value for the number of tests.
+ # from 1 to 4, generic quality value is from 1 to 100
+ USER_STEP_QUALITY = ((USER_FILL_HOLES_QUALITY - 1) / 25.0) + 1
+
+ # If 100 will test as long as there is enough free space.
+ # this is rarely enough, and testing takes a while, so lower quality speeds this up.
+
+ # 1 means they have the same quality
+ USER_FREE_SPACE_TO_TEST_QUALITY = 1 + (((100 - USER_FILL_HOLES_QUALITY)/100.0) *5)
+
+ #print 'USER_STEP_QUALITY', USER_STEP_QUALITY
+ #print 'USER_FREE_SPACE_TO_TEST_QUALITY', USER_FREE_SPACE_TO_TEST_QUALITY
+
+ removedCount = 0
+
+ areaIslandIdx = 0
+ ctrl = Window.Qual.CTRL
+ BREAK= False
+ while areaIslandIdx < len(decoratedIslandListAreaSort) and not BREAK:
+ sourceIsland = decoratedIslandListAreaSort[areaIslandIdx]
+ # Alredy packed?
+ if not sourceIsland[0]:
+ areaIslandIdx+=1
+ else:
+ efficIslandIdx = 0
+ while efficIslandIdx < len(decoratedIslandListEfficSort) and not BREAK:
+
+ if Window.GetKeyQualifiers() & ctrl:
+ BREAK= True
+ break
+
+ # Now we have 2 islands, is the efficience of the islands lowers theres an
+ # increasing likely hood that we can fit merge into the bigger UV island.
+ # this ensures a tight fit.
+
+ # Just use figures we have about user/unused area to see if they might fit.
+
+ targetIsland = decoratedIslandListEfficSort[efficIslandIdx]
+
+
+ if sourceIsland[0] == targetIsland[0] or\
+ not targetIsland[0] or\
+ not sourceIsland[0]:
+ pass
+ else:
+
+ # ([island, totFaceArea, efficiency, islandArea, w,h])
+ # Waisted space on target is greater then UV bounding island area.
+
+
+ # if targetIsland[3] > (sourceIsland[2]) and\ #
+ # print USER_FREE_SPACE_TO_TEST_QUALITY, 'ass'
+ if targetIsland[2] > (sourceIsland[1] * USER_FREE_SPACE_TO_TEST_QUALITY) and\
+ targetIsland[4] > sourceIsland[4] and\
+ targetIsland[5] > sourceIsland[5]:
+
+ # DEBUG # print '%.10f %.10f' % (targetIsland[3], sourceIsland[1])
+
+ # These enough spare space lets move the box until it fits
+
+ # How many times does the source fit into the target x/y
+ blockTestXUnit = targetIsland[4]/sourceIsland[4]
+ blockTestYUnit = targetIsland[5]/sourceIsland[5]
+
+ boxLeft = 0
+
+
+ # Distllllance we can move between whilst staying inside the targets bounds.
+ testWidth = targetIsland[4] - sourceIsland[4]
+ testHeight = targetIsland[5] - sourceIsland[5]
+
+ # Increment we move each test. x/y
+ xIncrement = (testWidth / (blockTestXUnit * ((USER_STEP_QUALITY/50)+0.1)))
+ yIncrement = (testHeight / (blockTestYUnit * ((USER_STEP_QUALITY/50)+0.1)))
+
+ # Make sure were not moving less then a 3rg of our width/height
+ if xIncrement<sourceIsland[4]/3:
+ xIncrement= sourceIsland[4]
+ if yIncrement<sourceIsland[5]/3:
+ yIncrement= sourceIsland[5]
+
+
+ boxLeft = 0 # Start 1 back so we can jump into the loop.
+ boxBottom= 0 #-yIncrement
+
+ ##testcount= 0
+
+ while boxBottom <= testHeight:
+ # Should we use this? - not needed for now.
+ #if Window.GetKeyQualifiers() & ctrl:
+ # BREAK= True
+ # break
+
+ ##testcount+=1
+ #print 'Testing intersect'
+ Intersect = islandIntersectUvIsland(sourceIsland, targetIsland, Vector(boxLeft, boxBottom))
+ #print 'Done', Intersect
+ if Intersect == 1: # Line intersect, dont bother with this any more
+ pass
+
+ if Intersect == 2: # Source inside target
+ '''
+ We have an intersection, if we are inside the target
+ then move us 1 whole width accross,
+ Its possible this is a bad idea since 2 skinny Angular faces
+ could join without 1 whole move, but its a lot more optimal to speed this up
+ since we have alredy tested for it.
+
+ It gives about 10% speedup with minimal errors.
+ '''
+ #print 'ass'
+ # Move the test allong its width + SMALL_NUM
+ #boxLeft += sourceIsland[4] + SMALL_NUM
+ boxLeft += sourceIsland[4]
+ elif Intersect == 0: # No intersection?? Place it.
+ # Progress
+ removedCount +=1
#XXX Window.DrawProgressBar(0.0, 'Merged: %i islands, Ctrl to finish early.' % removedCount)
-
- # Move faces into new island and offset
- targetIsland[0].extend(sourceIsland[0])
- offset= Vector(boxLeft, boxBottom)
-
- for f in sourceIsland[0]:
- for uv in f.uv:
- uv+= offset
-
- sourceIsland[0][:] = [] # Empty
-
-
- # Move edge loop into new and offset.
- # targetIsland[6].extend(sourceIsland[6])
- #while sourceIsland[6]:
- targetIsland[6].extend( [ (\
- (e[0]+offset, e[1]+offset, e[2])\
- ) for e in sourceIsland[6] ] )
-
- sourceIsland[6][:] = [] # Empty
-
- # Sort by edge length, reverse so biggest are first.
-
- try: targetIsland[6].sort(key = lambda A: A[2])
- except: targetIsland[6].sort(lambda B,A: cmp(A[2], B[2] ))
-
-
- targetIsland[7].extend(sourceIsland[7])
- offset= Vector(boxLeft, boxBottom, 0)
- for p in sourceIsland[7]:
- p+= offset
-
- sourceIsland[7][:] = []
-
-
- # Decrement the efficiency
- targetIsland[1]+=sourceIsland[1] # Increment totFaceArea
- targetIsland[2]-=sourceIsland[1] # Decrement efficiency
- # IF we ever used these again, should set to 0, eg
- sourceIsland[2] = 0 # No area if anyone wants to know
-
- break
-
-
- # INCREMENR NEXT LOCATION
- if boxLeft > testWidth:
- boxBottom += yIncrement
- boxLeft = 0.0
- else:
- boxLeft += xIncrement
- ##print testcount
-
- efficIslandIdx+=1
- areaIslandIdx+=1
-
- # Remove empty islands
- i = len(islandList)
- while i:
- i-=1
- if not islandList[i]:
- del islandList[i] # Can increment islands removed here.
+
+ # Move faces into new island and offset
+ targetIsland[0].extend(sourceIsland[0])
+ offset= Vector(boxLeft, boxBottom)
+
+ for f in sourceIsland[0]:
+ for uv in f.uv:
+ uv+= offset
+
+ sourceIsland[0][:] = [] # Empty
+
+
+ # Move edge loop into new and offset.
+ # targetIsland[6].extend(sourceIsland[6])
+ #while sourceIsland[6]:
+ targetIsland[6].extend( [ (\
+ (e[0]+offset, e[1]+offset, e[2])\
+ ) for e in sourceIsland[6] ] )
+
+ sourceIsland[6][:] = [] # Empty
+
+ # Sort by edge length, reverse so biggest are first.
+
+ try: targetIsland[6].sort(key = lambda A: A[2])
+ except: targetIsland[6].sort(lambda B,A: cmp(A[2], B[2] ))
+
+
+ targetIsland[7].extend(sourceIsland[7])
+ offset= Vector(boxLeft, boxBottom, 0)
+ for p in sourceIsland[7]:
+ p+= offset
+
+ sourceIsland[7][:] = []
+
+
+ # Decrement the efficiency
+ targetIsland[1]+=sourceIsland[1] # Increment totFaceArea
+ targetIsland[2]-=sourceIsland[1] # Decrement efficiency
+ # IF we ever used these again, should set to 0, eg
+ sourceIsland[2] = 0 # No area if anyone wants to know
+
+ break
+
+
+ # INCREMENR NEXT LOCATION
+ if boxLeft > testWidth:
+ boxBottom += yIncrement
+ boxLeft = 0.0
+ else:
+ boxLeft += xIncrement
+ ##print testcount
+
+ efficIslandIdx+=1
+ areaIslandIdx+=1
+
+ # Remove empty islands
+ i = len(islandList)
+ while i:
+ i-=1
+ if not islandList[i]:
+ del islandList[i] # Can increment islands removed here.
# Takes groups of faces. assumes face groups are UV groups.
def getUvIslands(faceGroups, me):
-
- # Get seams so we dont cross over seams
- edge_seams = {} # shoudl be a set
- for ed in me.edges:
- if ed.seam:
- edge_seams[ed.key] = None # dummy var- use sets!
- # Done finding seams
-
-
- islandList = []
-
+
+ # Get seams so we dont cross over seams
+ edge_seams = {} # shoudl be a set
+ for ed in me.edges:
+ if ed.seam:
+ edge_seams[ed.key] = None # dummy var- use sets!
+ # Done finding seams
+
+
+ islandList = []
+
#XXX Window.DrawProgressBar(0.0, 'Splitting %d projection groups into UV islands:' % len(faceGroups))
- #print '\tSplitting %d projection groups into UV islands:' % len(faceGroups),
- # Find grouped faces
-
- faceGroupIdx = len(faceGroups)
-
- while faceGroupIdx:
- faceGroupIdx-=1
- faces = faceGroups[faceGroupIdx]
-
- if not faces:
- continue
-
- # Build edge dict
- edge_users = {}
-
- for i, f in enumerate(faces):
- for ed_key in f.edge_keys:
- if ed_key in edge_seams: # DELIMIT SEAMS! ;)
- edge_users[ed_key] = [] # so as not to raise an error
- else:
- try: edge_users[ed_key].append(i)
- except: edge_users[ed_key] = [i]
-
- # Modes
- # 0 - face not yet touched.
- # 1 - added to island list, and need to search
- # 2 - touched and searched - dont touch again.
- face_modes = [0] * len(faces) # initialize zero - untested.
-
- face_modes[0] = 1 # start the search with face 1
-
- newIsland = []
-
- newIsland.append(faces[0])
-
-
- ok = True
- while ok:
-
- ok = True
- while ok:
- ok= False
- for i in range(len(faces)):
- if face_modes[i] == 1: # search
- for ed_key in faces[i].edge_keys:
- for ii in edge_users[ed_key]:
- if i != ii and face_modes[ii] == 0:
- face_modes[ii] = ok = 1 # mark as searched
- newIsland.append(faces[ii])
-
- # mark as searched, dont look again.
- face_modes[i] = 2
-
- islandList.append(newIsland)
-
- ok = False
- for i in range(len(faces)):
- if face_modes[i] == 0:
- newIsland = []
- newIsland.append(faces[i])
-
- face_modes[i] = ok = 1
- break
- # if not ok will stop looping
-
+ #print '\tSplitting %d projection groups into UV islands:' % len(faceGroups),
+ # Find grouped faces
+
+ faceGroupIdx = len(faceGroups)
+
+ while faceGroupIdx:
+ faceGroupIdx-=1
+ faces = faceGroups[faceGroupIdx]
+
+ if not faces:
+ continue
+
+ # Build edge dict
+ edge_users = {}
+
+ for i, f in enumerate(faces):
+ for ed_key in f.edge_keys:
+ if ed_key in edge_seams: # DELIMIT SEAMS! ;)
+ edge_users[ed_key] = [] # so as not to raise an error
+ else:
+ try: edge_users[ed_key].append(i)
+ except: edge_users[ed_key] = [i]
+
+ # Modes
+ # 0 - face not yet touched.
+ # 1 - added to island list, and need to search
+ # 2 - touched and searched - dont touch again.
+ face_modes = [0] * len(faces) # initialize zero - untested.
+
+ face_modes[0] = 1 # start the search with face 1
+
+ newIsland = []
+
+ newIsland.append(faces[0])
+
+
+ ok = True
+ while ok:
+
+ ok = True
+ while ok:
+ ok= False
+ for i in range(len(faces)):
+ if face_modes[i] == 1: # search
+ for ed_key in faces[i].edge_keys:
+ for ii in edge_users[ed_key]:
+ if i != ii and face_modes[ii] == 0:
+ face_modes[ii] = ok = 1 # mark as searched
+ newIsland.append(faces[ii])
+
+ # mark as searched, dont look again.
+ face_modes[i] = 2
+
+ islandList.append(newIsland)
+
+ ok = False
+ for i in range(len(faces)):
+ if face_modes[i] == 0:
+ newIsland = []
+ newIsland.append(faces[i])
+
+ face_modes[i] = ok = 1
+ break
+ # if not ok will stop looping
+
#XXX Window.DrawProgressBar(0.1, 'Optimizing Rotation for %i UV Islands' % len(islandList))
-
- for island in islandList:
- optiRotateUvIsland(island)
-
- return islandList
-
+
+ for island in islandList:
+ optiRotateUvIsland(island)
+
+ return islandList
+
def packIslands(islandList):
- if USER_FILL_HOLES:
+ if USER_FILL_HOLES:
#XXX Window.DrawProgressBar(0.1, 'Merging Islands (Ctrl: skip merge)...')
- mergeUvIslands(islandList) # Modify in place
-
-
- # Now we have UV islands, we need to pack them.
-
- # Make a synchronised list with the islands
- # so we can box pak the islands.
- packBoxes = []
-
- # Keep a list of X/Y offset so we can save time by writing the
- # uv's and packed data in one pass.
- islandOffsetList = []
-
- islandIdx = 0
-
- while islandIdx < len(islandList):
- minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
-
- w, h = maxx-minx, maxy-miny
-
- if USER_ISLAND_MARGIN:
- minx -= USER_ISLAND_MARGIN# *w
- miny -= USER_ISLAND_MARGIN# *h
- maxx += USER_ISLAND_MARGIN# *w
- maxy += USER_ISLAND_MARGIN# *h
-
- # recalc width and height
- w, h = maxx-minx, maxy-miny
-
- if w < 0.00001 or h < 0.00001:
- del islandList[islandIdx]
- islandIdx -=1
- continue
-
- '''Save the offset to be applied later,
- we could apply to the UVs now and allign them to the bottom left hand area
- of the UV coords like the box packer imagines they are
- but, its quicker just to remember their offset and
- apply the packing and offset in 1 pass '''
- islandOffsetList.append((minx, miny))
-
- # Add to boxList. use the island idx for the BOX id.
- packBoxes.append([0, 0, w, h])
- islandIdx+=1
-
- # Now we have a list of boxes to pack that syncs
- # with the islands.
-
- #print '\tPacking UV Islands...'
+ mergeUvIslands(islandList) # Modify in place
+
+
+ # Now we have UV islands, we need to pack them.
+
+ # Make a synchronised list with the islands
+ # so we can box pak the islands.
+ packBoxes = []
+
+ # Keep a list of X/Y offset so we can save time by writing the
+ # uv's and packed data in one pass.
+ islandOffsetList = []
+
+ islandIdx = 0
+
+ while islandIdx < len(islandList):
+ minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
+
+ w, h = maxx-minx, maxy-miny
+
+ if USER_ISLAND_MARGIN:
+ minx -= USER_ISLAND_MARGIN# *w
+ miny -= USER_ISLAND_MARGIN# *h
+ maxx += USER_ISLAND_MARGIN# *w
+ maxy += USER_ISLAND_MARGIN# *h
+
+ # recalc width and height
+ w, h = maxx-minx, maxy-miny
+
+ if w < 0.00001 or h < 0.00001:
+ del islandList[islandIdx]
+ islandIdx -=1
+ continue
+
+ '''Save the offset to be applied later,
+ we could apply to the UVs now and allign them to the bottom left hand area
+ of the UV coords like the box packer imagines they are
+ but, its quicker just to remember their offset and
+ apply the packing and offset in 1 pass '''
+ islandOffsetList.append((minx, miny))
+
+ # Add to boxList. use the island idx for the BOX id.
+ packBoxes.append([0, 0, w, h])
+ islandIdx+=1
+
+ # Now we have a list of boxes to pack that syncs
+ # with the islands.
+
+ #print '\tPacking UV Islands...'
#XXX Window.DrawProgressBar(0.7, 'Packing %i UV Islands...' % len(packBoxes) )
-
- time1 = time.time()
- packWidth, packHeight = Geometry.BoxPack2D(packBoxes)
-
- # print 'Box Packing Time:', time.time() - time1
-
- #if len(pa ckedLs) != len(islandList):
- # raise "Error packed boxes differes from original length"
-
- #print '\tWriting Packed Data to faces'
+
+ time1 = time.time()
+ packWidth, packHeight = Geometry.BoxPack2D(packBoxes)
+
+ # print 'Box Packing Time:', time.time() - time1
+
+ #if len(pa ckedLs) != len(islandList):
+ # raise "Error packed boxes differes from original length"
+
+ #print '\tWriting Packed Data to faces'
#XXX Window.DrawProgressBar(0.8, 'Writing Packed Data to faces')
-
- # Sort by ID, so there in sync again
- islandIdx = len(islandList)
- # Having these here avoids devide by 0
- if islandIdx:
-
- if USER_STRETCH_ASPECT:
- # Maximize to uv area?? Will write a normalize function.
- xfactor = 1.0 / packWidth
- yfactor = 1.0 / packHeight
- else:
- # Keep proportions.
- xfactor = yfactor = 1.0 / max(packWidth, packHeight)
-
- while islandIdx:
- islandIdx -=1
- # Write the packed values to the UV's
-
- xoffset = packBoxes[islandIdx][0] - islandOffsetList[islandIdx][0]
- yoffset = packBoxes[islandIdx][1] - islandOffsetList[islandIdx][1]
-
- for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box
- for uv in f.uv:
- uv.x= (uv.x+xoffset) * xfactor
- uv.y= (uv.y+yoffset) * yfactor
-
-
+
+ # Sort by ID, so there in sync again
+ islandIdx = len(islandList)
+ # Having these here avoids devide by 0
+ if islandIdx:
+
+ if USER_STRETCH_ASPECT:
+ # Maximize to uv area?? Will write a normalize function.
+ xfactor = 1.0 / packWidth
+ yfactor = 1.0 / packHeight
+ else:
+ # Keep proportions.
+ xfactor = yfactor = 1.0 / max(packWidth, packHeight)
+
+ while islandIdx:
+ islandIdx -=1
+ # Write the packed values to the UV's
+
+ xoffset = packBoxes[islandIdx][0] - islandOffsetList[islandIdx][0]
+ yoffset = packBoxes[islandIdx][1] - islandOffsetList[islandIdx][1]
+
+ for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box
+ for uv in f.uv:
+ uv.x= (uv.x+xoffset) * xfactor
+ uv.y= (uv.y+yoffset) * yfactor
+
+
def VectoMat(vec):
- a3 = vec.__copy__().normalize()
-
- up = Vector(0,0,1)
- if abs(a3.dot(up)) == 1.0:
- up = Vector(0,1,0)
-
- a1 = a3.cross(up).normalize()
- a2 = a3.cross(a1)
- return Matrix([a1[0], a1[1], a1[2]], [a2[0], a2[1], a2[2]], [a3[0], a3[1], a3[2]])
+ a3 = vec.__copy__().normalize()
+
+ up = Vector(0,0,1)
+ if abs(a3.dot(up)) == 1.0:
+ up = Vector(0,1,0)
+
+ a1 = a3.cross(up).normalize()
+ a2 = a3.cross(a1)
+ return Matrix([a1[0], a1[1], a1[2]], [a2[0], a2[1], a2[2]], [a3[0], a3[1], a3[2]])
class thickface(object):
- __slost__= 'v', 'uv', 'no', 'area', 'edge_keys'
- def __init__(self, face, uvface, mesh_verts):
- self.v = [mesh_verts[i] for i in face.verts]
- if len(self.v)==4:
- self.uv = uvface.uv1, uvface.uv2, uvface.uv3, uvface.uv4
- else:
- self.uv = uvface.uv1, uvface.uv2, uvface.uv3
-
- self.no = face.normal
- self.area = face.area
- self.edge_keys = face.edge_keys
+ __slost__= 'v', 'uv', 'no', 'area', 'edge_keys'
+ def __init__(self, face, uvface, mesh_verts):
+ self.v = [mesh_verts[i] for i in face.verts]
+ if len(self.v)==4:
+ self.uv = uvface.uv1, uvface.uv2, uvface.uv3, uvface.uv4
+ else:
+ self.uv = uvface.uv1, uvface.uv2, uvface.uv3
+
+ self.no = face.normal
+ self.area = face.area
+ self.edge_keys = face.edge_keys
global ob
ob = None
def main(context, island_margin, projection_limit):
- global USER_FILL_HOLES
- global USER_FILL_HOLES_QUALITY
- global USER_STRETCH_ASPECT
- global USER_ISLAND_MARGIN
-
+ global USER_FILL_HOLES
+ global USER_FILL_HOLES_QUALITY
+ global USER_STRETCH_ASPECT
+ global USER_ISLAND_MARGIN
+
#XXX objects= bpy.data.scenes.active.objects
- objects = context.selected_editable_objects
-
-
- # we can will tag them later.
- obList = [ob for ob in objects if ob.type == 'MESH']
-
- # Face select object may not be selected.
+ objects = context.selected_editable_objects
+
+
+ # we can will tag them later.
+ obList = [ob for ob in objects if ob.type == 'MESH']
+
+ # Face select object may not be selected.
#XXX ob = objects.active
- ob= objects[0]
-
- if ob and ob.selected == 0 and ob.type == 'MESH':
- # Add to the list
- obList =[ob]
- del objects
-
- if not obList:
- raise('error, no selected mesh objects')
-
- # Create the variables.
- USER_PROJECTION_LIMIT = projection_limit
- USER_ONLY_SELECTED_FACES = (1)
- USER_SHARE_SPACE = (1) # Only for hole filling.
- USER_STRETCH_ASPECT = (1) # Only for hole filling.
- USER_ISLAND_MARGIN = island_margin # Only for hole filling.
- USER_FILL_HOLES = (0)
- USER_FILL_HOLES_QUALITY = (50) # Only for hole filling.
- USER_VIEW_INIT = (0) # Only for hole filling.
- USER_AREA_WEIGHT = (1) # Only for hole filling.
-
- # Reuse variable
- if len(obList) == 1:
- ob = "Unwrap %i Selected Mesh"
- else:
- ob = "Unwrap %i Selected Meshes"
-
- # HACK, loop until mouse is lifted.
- '''
- while Window.GetMouseButtons() != 0:
- time.sleep(10)
- '''
-
+ ob= objects[0]
+
+ if ob and ob.selected == 0 and ob.type == 'MESH':
+ # Add to the list
+ obList =[ob]
+ del objects
+
+ if not obList:
+ raise('error, no selected mesh objects')
+
+ # Create the variables.
+ USER_PROJECTION_LIMIT = projection_limit
+ USER_ONLY_SELECTED_FACES = (1)
+ USER_SHARE_SPACE = (1) # Only for hole filling.
+ USER_STRETCH_ASPECT = (1) # Only for hole filling.
+ USER_ISLAND_MARGIN = island_margin # Only for hole filling.
+ USER_FILL_HOLES = (0)
+ USER_FILL_HOLES_QUALITY = (50) # Only for hole filling.
+ USER_VIEW_INIT = (0) # Only for hole filling.
+ USER_AREA_WEIGHT = (1) # Only for hole filling.
+
+ # Reuse variable
+ if len(obList) == 1:
+ ob = "Unwrap %i Selected Mesh"
+ else:
+ ob = "Unwrap %i Selected Meshes"
+
+ # HACK, loop until mouse is lifted.
+ '''
+ while Window.GetMouseButtons() != 0:
+ time.sleep(10)
+ '''
+
#XXX if not Draw.PupBlock(ob % len(obList), pup_block):
#XXX return
#XXX del ob
-
- # Convert from being button types
-
- USER_PROJECTION_LIMIT_CONVERTED = cos(USER_PROJECTION_LIMIT * DEG_TO_RAD)
- USER_PROJECTION_LIMIT_HALF_CONVERTED = cos((USER_PROJECTION_LIMIT/2) * DEG_TO_RAD)
-
-
- # Toggle Edit mode
- is_editmode = (context.active_object.mode == 'EDIT')
- if is_editmode:
- bpy.ops.object.mode_set(mode='OBJECT')
- # Assume face select mode! an annoying hack to toggle face select mode because Mesh dosent like faceSelectMode.
-
- if USER_SHARE_SPACE:
- # Sort by data name so we get consistant results
- obList.sort(key = lambda ob: ob.data.name)
- collected_islandList= []
-
+
+ # Convert from being button types
+
+ USER_PROJECTION_LIMIT_CONVERTED = cos(USER_PROJECTION_LIMIT * DEG_TO_RAD)
+ USER_PROJECTION_LIMIT_HALF_CONVERTED = cos((USER_PROJECTION_LIMIT/2) * DEG_TO_RAD)
+
+
+ # Toggle Edit mode
+ is_editmode = (context.active_object.mode == 'EDIT')
+ if is_editmode:
+ bpy.ops.object.mode_set(mode='OBJECT')
+ # Assume face select mode! an annoying hack to toggle face select mode because Mesh dosent like faceSelectMode.
+
+ if USER_SHARE_SPACE:
+ # Sort by data name so we get consistant results
+ obList.sort(key = lambda ob: ob.data.name)
+ collected_islandList= []
+
#XXX Window.WaitCursor(1)
-
- time1 = time.time()
-
- # Tag as False se we dont operate on teh same mesh twice.
-#XXX bpy.data.meshes.tag = False
- for me in bpy.data.meshes:
- me.tag = False
-
-
- for ob in obList:
- me = ob.data
-
- if me.tag or me.library:
- continue
-
- # Tag as used
- me.tag = True
-
- if len(me.uv_textures)==0: # Mesh has no UV Coords, dont bother.
- me.add_uv_texture()
-
- uv_layer = me.active_uv_texture.data
- me_verts = list(me.verts)
-
- if USER_ONLY_SELECTED_FACES:
- meshFaces = [thickface(f, uv_layer[i], me_verts) for i, f in enumerate(me.faces) if f.selected]
- #else:
- # meshFaces = map(thickface, me.faces)
-
- if not meshFaces:
- continue
-
+
+ time1 = time.time()
+
+ # Tag as False se we dont operate on teh same mesh twice.
+#XXX bpy.data.meshes.tag = False
+ for me in bpy.data.meshes:
+ me.tag = False
+
+
+ for ob in obList:
+ me = ob.data
+
+ if me.tag or me.library:
+ continue
+
+ # Tag as used
+ me.tag = True
+
+ if len(me.uv_textures)==0: # Mesh has no UV Coords, dont bother.
+ me.add_uv_texture()
+
+ uv_layer = me.active_uv_texture.data
+ me_verts = list(me.verts)
+
+ if USER_ONLY_SELECTED_FACES:
+ meshFaces = [thickface(f, uv_layer[i], me_verts) for i, f in enumerate(me.faces) if f.selected]
+ #else:
+ # meshFaces = map(thickface, me.faces)
+
+ if not meshFaces:
+ continue
+
#XXX Window.DrawProgressBar(0.1, 'SmartProj UV Unwrapper, mapping "%s", %i faces.' % (me.name, len(meshFaces)))
-
- # =======
- # Generate a projection list from face normals, this is ment to be smart :)
-
- # make a list of face props that are in sync with meshFaces
- # Make a Face List that is sorted by area.
- # meshFaces = []
-
- # meshFaces.sort( lambda a, b: cmp(b.area , a.area) ) # Biggest first.
- meshFaces.sort( key = lambda a: -a.area )
-
- # remove all zero area faces
- while meshFaces and meshFaces[-1].area <= SMALL_NUM:
- # Set their UV's to 0,0
- for uv in meshFaces[-1].uv:
- uv.zero()
- meshFaces.pop()
-
- # Smallest first is slightly more efficient, but if the user cancels early then its better we work on the larger data.
-
- # Generate Projection Vecs
- # 0d is 1.0
- # 180 IS -0.59846
-
-
- # Initialize projectVecs
- if USER_VIEW_INIT:
- # Generate Projection
- projectVecs = [Vector(Window.GetViewVector()) * ob.matrixWorld.copy().invert().rotationPart()] # We add to this allong the way
- else:
- projectVecs = []
-
- newProjectVec = meshFaces[0].no
- newProjectMeshFaces = [] # Popping stuffs it up.
-
-
- # Predent that the most unique angke is ages away to start the loop off
- mostUniqueAngle = -1.0
-
- # This is popped
- tempMeshFaces = meshFaces[:]
-
-
-
- # This while only gathers projection vecs, faces are assigned later on.
- while 1:
- # If theres none there then start with the largest face
-
- # add all the faces that are close.
- for fIdx in range(len(tempMeshFaces)-1, -1, -1):
- # Use half the angle limit so we dont overweight faces towards this
- # normal and hog all the faces.
- if newProjectVec.dot(tempMeshFaces[fIdx].no) > USER_PROJECTION_LIMIT_HALF_CONVERTED:
- newProjectMeshFaces.append(tempMeshFaces.pop(fIdx))
-
- # Add the average of all these faces normals as a projectionVec
- averageVec = Vector(0,0,0)
- if USER_AREA_WEIGHT:
- for fprop in newProjectMeshFaces:
- averageVec += (fprop.no * fprop.area)
- else:
- for fprop in newProjectMeshFaces:
- averageVec += fprop.no
-
- if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0: # Avoid NAN
- projectVecs.append(averageVec.normalize())
-
-
- # Get the next vec!
- # Pick the face thats most different to all existing angles :)
- mostUniqueAngle = 1.0 # 1.0 is 0d. no difference.
- mostUniqueIndex = 0 # dummy
-
- for fIdx in range(len(tempMeshFaces)-1, -1, -1):
- angleDifference = -1.0 # 180d difference.
-
- # Get the closest vec angle we are to.
- for p in projectVecs:
- temp_angle_diff= p.dot(tempMeshFaces[fIdx].no)
-
- if angleDifference < temp_angle_diff:
- angleDifference= temp_angle_diff
-
- if angleDifference < mostUniqueAngle:
- # We have a new most different angle
- mostUniqueIndex = fIdx
- mostUniqueAngle = angleDifference
-
- if mostUniqueAngle < USER_PROJECTION_LIMIT_CONVERTED:
- #print 'adding', mostUniqueAngle, USER_PROJECTION_LIMIT, len(newProjectMeshFaces)
- # Now weight the vector to all its faces, will give a more direct projection
- # if the face its self was not representive of the normal from surrounding faces.
-
- newProjectVec = tempMeshFaces[mostUniqueIndex].no
- newProjectMeshFaces = [tempMeshFaces.pop(mostUniqueIndex)]
-
-
- else:
- if len(projectVecs) >= 1: # Must have at least 2 projections
- break
-
-
- # If there are only zero area faces then its possible
- # there are no projectionVecs
- if not len(projectVecs):
- Draw.PupMenu('error, no projection vecs where generated, 0 area faces can cause this.')
- return
-
- faceProjectionGroupList =[[] for i in range(len(projectVecs)) ]
-
- # MAP and Arrange # We know there are 3 or 4 faces here
-
- for fIdx in range(len(meshFaces)-1, -1, -1):
- fvec = meshFaces[fIdx].no
- i = len(projectVecs)
-
- # Initialize first
- bestAng = fvec.dot(projectVecs[0])
- bestAngIdx = 0
-
- # Cycle through the remaining, first alredy done
- while i-1:
- i-=1
-
- newAng = fvec.dot(projectVecs[i])
- if newAng > bestAng: # Reverse logic for dotvecs
- bestAng = newAng
- bestAngIdx = i
-
- # Store the area for later use.
- faceProjectionGroupList[bestAngIdx].append(meshFaces[fIdx])
-
- # Cull faceProjectionGroupList,
-
-
- # Now faceProjectionGroupList is full of faces that face match the project Vecs list
- for i in range(len(projectVecs)):
- # Account for projectVecs having no faces.
- if not faceProjectionGroupList[i]:
- continue
-
- # Make a projection matrix from a unit length vector.
- MatProj = VectoMat(projectVecs[i])
-
- # Get the faces UV's from the projected vertex.
- for f in faceProjectionGroupList[i]:
- f_uv = f.uv
- for j, v in enumerate(f.v):
- # XXX - note, between Mathutils in 2.4 and 2.5 the order changed.
- f_uv[j][:] = (v.co * MatProj)[:2]
-
-
- if USER_SHARE_SPACE:
- # Should we collect and pack later?
- islandList = getUvIslands(faceProjectionGroupList, me)
- collected_islandList.extend(islandList)
-
- else:
- # Should we pack the islands for this 1 object?
- islandList = getUvIslands(faceProjectionGroupList, me)
- packIslands(islandList)
-
-
- # update the mesh here if we need to.
-
- # We want to pack all in 1 go, so pack now
- if USER_SHARE_SPACE:
+
+ # =======
+ # Generate a projection list from face normals, this is ment to be smart :)
+
+ # make a list of face props that are in sync with meshFaces
+ # Make a Face List that is sorted by area.
+ # meshFaces = []
+
+ # meshFaces.sort( lambda a, b: cmp(b.area , a.area) ) # Biggest first.
+ meshFaces.sort( key = lambda a: -a.area )
+
+ # remove all zero area faces
+ while meshFaces and meshFaces[-1].area <= SMALL_NUM:
+ # Set their UV's to 0,0
+ for uv in meshFaces[-1].uv:
+ uv.zero()
+ meshFaces.pop()
+
+ # Smallest first is slightly more efficient, but if the user cancels early then its better we work on the larger data.
+
+ # Generate Projection Vecs
+ # 0d is 1.0
+ # 180 IS -0.59846
+
+
+ # Initialize projectVecs
+ if USER_VIEW_INIT:
+ # Generate Projection
+ projectVecs = [Vector(Window.GetViewVector()) * ob.matrixWorld.copy().invert().rotationPart()] # We add to this allong the way
+ else:
+ projectVecs = []
+
+ newProjectVec = meshFaces[0].no
+ newProjectMeshFaces = [] # Popping stuffs it up.
+
+
+ # Predent that the most unique angke is ages away to start the loop off
+ mostUniqueAngle = -1.0
+
+ # This is popped
+ tempMeshFaces = meshFaces[:]
+
+
+
+ # This while only gathers projection vecs, faces are assigned later on.
+ while 1:
+ # If theres none there then start with the largest face
+
+ # add all the faces that are close.
+ for fIdx in range(len(tempMeshFaces)-1, -1, -1):
+ # Use half the angle limit so we dont overweight faces towards this
+ # normal and hog all the faces.
+ if newProjectVec.dot(tempMeshFaces[fIdx].no) > USER_PROJECTION_LIMIT_HALF_CONVERTED:
+ newProjectMeshFaces.append(tempMeshFaces.pop(fIdx))
+
+ # Add the average of all these faces normals as a projectionVec
+ averageVec = Vector(0,0,0)
+ if USER_AREA_WEIGHT:
+ for fprop in newProjectMeshFaces:
+ averageVec += (fprop.no * fprop.area)
+ else:
+ for fprop in newProjectMeshFaces:
+ averageVec += fprop.no
+
+ if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0: # Avoid NAN
+ projectVecs.append(averageVec.normalize())
+
+
+ # Get the next vec!
+ # Pick the face thats most different to all existing angles :)
+ mostUniqueAngle = 1.0 # 1.0 is 0d. no difference.
+ mostUniqueIndex = 0 # dummy
+
+ for fIdx in range(len(tempMeshFaces)-1, -1, -1):
+ angleDifference = -1.0 # 180d difference.
+
+ # Get the closest vec angle we are to.
+ for p in projectVecs:
+ temp_angle_diff= p.dot(tempMeshFaces[fIdx].no)
+
+ if angleDifference < temp_angle_diff:
+ angleDifference= temp_angle_diff
+
+ if angleDifference < mostUniqueAngle:
+ # We have a new most different angle
+ mostUniqueIndex = fIdx
+ mostUniqueAngle = angleDifference
+
+ if mostUniqueAngle < USER_PROJECTION_LIMIT_CONVERTED:
+ #print 'adding', mostUniqueAngle, USER_PROJECTION_LIMIT, len(newProjectMeshFaces)
+ # Now weight the vector to all its faces, will give a more direct projection
+ # if the face its self was not representive of the normal from surrounding faces.
+
+ newProjectVec = tempMeshFaces[mostUniqueIndex].no
+ newProjectMeshFaces = [tempMeshFaces.pop(mostUniqueIndex)]
+
+
+ else:
+ if len(projectVecs) >= 1: # Must have at least 2 projections
+ break
+
+
+ # If there are only zero area faces then its possible
+ # there are no projectionVecs
+ if not len(projectVecs):
+ Draw.PupMenu('error, no projection vecs where generated, 0 area faces can cause this.')
+ return
+
+ faceProjectionGroupList =[[] for i in range(len(projectVecs)) ]
+
+ # MAP and Arrange # We know there are 3 or 4 faces here
+
+ for fIdx in range(len(meshFaces)-1, -1, -1):
+ fvec = meshFaces[fIdx].no
+ i = len(projectVecs)
+
+ # Initialize first
+ bestAng = fvec.dot(projectVecs[0])
+ bestAngIdx = 0
+
+ # Cycle through the remaining, first alredy done
+ while i-1:
+ i-=1
+
+ newAng = fvec.dot(projectVecs[i])
+ if newAng > bestAng: # Reverse logic for dotvecs
+ bestAng = newAng
+ bestAngIdx = i
+
+ # Store the area for later use.
+ faceProjectionGroupList[bestAngIdx].append(meshFaces[fIdx])
+
+ # Cull faceProjectionGroupList,
+
+
+ # Now faceProjectionGroupList is full of faces that face match the project Vecs list
+ for i in range(len(projectVecs)):
+ # Account for projectVecs having no faces.
+ if not faceProjectionGroupList[i]:
+ continue
+
+ # Make a projection matrix from a unit length vector.
+ MatProj = VectoMat(projectVecs[i])
+
+ # Get the faces UV's from the projected vertex.
+ for f in faceProjectionGroupList[i]:
+ f_uv = f.uv
+ for j, v in enumerate(f.v):
+ # XXX - note, between Mathutils in 2.4 and 2.5 the order changed.
+ f_uv[j][:] = (v.co * MatProj)[:2]
+
+
+ if USER_SHARE_SPACE:
+ # Should we collect and pack later?
+ islandList = getUvIslands(faceProjectionGroupList, me)
+ collected_islandList.extend(islandList)
+
+ else:
+ # Should we pack the islands for this 1 object?
+ islandList = getUvIslands(faceProjectionGroupList, me)
+ packIslands(islandList)
+
+
+ # update the mesh here if we need to.
+
+ # We want to pack all in 1 go, so pack now
+ if USER_SHARE_SPACE:
#XXX Window.DrawProgressBar(0.9, "Box Packing for all objects...")
- packIslands(collected_islandList)
-
- print("Smart Projection time: %.2f" % (time.time() - time1))
- # Window.DrawProgressBar(0.9, "Smart Projections done, time: %.2f sec." % (time.time() - time1))
-
- if is_editmode:
- bpy.ops.object.mode_set(mode='EDIT')
-
+ packIslands(collected_islandList)
+
+ print("Smart Projection time: %.2f" % (time.time() - time1))
+ # Window.DrawProgressBar(0.9, "Smart Projections done, time: %.2f sec." % (time.time() - time1))
+
+ if is_editmode:
+ bpy.ops.object.mode_set(mode='EDIT')
+
#XXX Window.DrawProgressBar(1.0, "")
#XXX Window.WaitCursor(0)
#XXX Window.RedrawAll()
"""
- pup_block = [\
- 'Projection',\
+ pup_block = [\
+ 'Projection',\
* ('Angle Limit:', USER_PROJECTION_LIMIT, 1, 89, ''),\
- ('Selected Faces Only', USER_ONLY_SELECTED_FACES, 'Use only selected faces from all selected meshes.'),\
- ('Init from view', USER_VIEW_INIT, 'The first projection will be from the view vector.'),\
- ('Area Weight', USER_AREA_WEIGHT, 'Weight projections vector by face area.'),\
- '',\
- '',\
- '',\
- 'UV Layout',\
- ('Share Tex Space', USER_SHARE_SPACE, 'Objects Share texture space, map all objects into 1 uvmap.'),\
- ('Stretch to bounds', USER_STRETCH_ASPECT, 'Stretch the final output to texture bounds.'),\
+ ('Selected Faces Only', USER_ONLY_SELECTED_FACES, 'Use only selected faces from all selected meshes.'),\
+ ('Init from view', USER_VIEW_INIT, 'The first projection will be from the view vector.'),\
+ ('Area Weight', USER_AREA_WEIGHT, 'Weight projections vector by face area.'),\
+ '',\
+ '',\
+ '',\
+ 'UV Layout',\
+ ('Share Tex Space', USER_SHARE_SPACE, 'Objects Share texture space, map all objects into 1 uvmap.'),\
+ ('Stretch to bounds', USER_STRETCH_ASPECT, 'Stretch the final output to texture bounds.'),\
* ('Island Margin:', USER_ISLAND_MARGIN, 0.0, 0.5, ''),\
- 'Fill in empty areas',\
- ('Fill Holes', USER_FILL_HOLES, 'Fill in empty areas reduced texture waistage (slow).'),\
- ('Fill Quality:', USER_FILL_HOLES_QUALITY, 1, 100, 'Depends on fill holes, how tightly to fill UV holes, (higher is slower)'),\
- ]
+ 'Fill in empty areas',\
+ ('Fill Holes', USER_FILL_HOLES, 'Fill in empty areas reduced texture waistage (slow).'),\
+ ('Fill Quality:', USER_FILL_HOLES_QUALITY, 1, 100, 'Depends on fill holes, how tightly to fill UV holes, (higher is slower)'),\
+ ]
"""
from bpy.props import *
class SmartProject(bpy.types.Operator):
- '''This script projection unwraps the selected faces of a mesh. it operates on all selected mesh objects, and can be used unwrap selected faces, or all faces.'''
- bl_idname = "uv.smart_project"
- bl_label = "Smart UV Project"
+ '''This script projection unwraps the selected faces of a mesh. it operates on all selected mesh objects, and can be used unwrap selected faces, or all faces.'''
+ bl_idname = "uv.smart_project"
+ bl_label = "Smart UV Project"
- bl_register = True
- bl_undo = True
+ bl_register = True
+ bl_undo = True
- angle_limit = FloatProperty(name="Angle Limit",
- description="lower for more projection groups, higher for less distortion.",
- default=66.0, min=1.0, max=89.0)
+ angle_limit = FloatProperty(name="Angle Limit",
+ description="lower for more projection groups, higher for less distortion.",
+ default=66.0, min=1.0, max=89.0)
- island_margin = FloatProperty(name="Island Margin",
- description="Margin to reduce bleed from adjacent islands.",
- default=0.0, min=0.0, max=1.0)
+ island_margin = FloatProperty(name="Island Margin",
+ description="Margin to reduce bleed from adjacent islands.",
+ default=0.0, min=0.0, max=1.0)
- def poll(self, context):
- return context.active_object != None
+ def poll(self, context):
+ return context.active_object != None
- def execute(self, context):
- main(context, self.properties.island_margin, self.properties.angle_limit)
- return ('FINISHED',)
+ def execute(self, context):
+ main(context, self.properties.island_margin, self.properties.angle_limit)
+ return ('FINISHED',)
bpy.ops.add(SmartProject)
@@ -1137,10 +1137,10 @@ bpy.ops.add(SmartProject)
import dynamic_menu
menu_func = (lambda self, context: self.layout.operator(SmartProject.bl_idname,
- text="Smart Project"))
+ text="Smart Project"))
menu_item = dynamic_menu.add(bpy.types.VIEW3D_MT_uv_map, menu_func)
if __name__ == '__main__':
- bpy.ops.uv.smart_project()
+ bpy.ops.uv.smart_project()
diff --git a/release/scripts/op/wm.py b/release/scripts/op/wm.py
index f93c0d47e28..c5fc18964bf 100644
--- a/release/scripts/op/wm.py
+++ b/release/scripts/op/wm.py
@@ -33,7 +33,7 @@ class MESH_OT_delete_edgeloop(bpy.types.Operator):
bpy.ops.tfm.edge_slide(value=1.0)
bpy.ops.mesh.select_more()
bpy.ops.mesh.remove_doubles()
-
+
return ('FINISHED',)
rna_path_prop = StringProperty(name="Context Attributes",