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-09-28 07:19:52 +0400
committerCampbell Barton <ideasman42@gmail.com>2009-09-28 07:19:52 +0400
commit2d797f35d804fd86ef079b30f2c83a81a8e38cd5 (patch)
treef3354beea96c83a39c5d89d291d4429c8a69ec22 /release/scripts/uvcalc_smart_project.py
parent0cbc87b42816711469cb63825ee826a837cc364a (diff)
- removed 2.4x release/scripts
- moved release/io and release/ui into release/scripts/io, ui - updated scons, cmake, make When porting 2.4x scripts back, use a command like this so as not to loose the commit history... svn cp https://svn.blender.org/svnroot/bf-blender/branches/blender2.4/release/scripts/raw_import.py release/scripts/io/import_raw.py
Diffstat (limited to 'release/scripts/uvcalc_smart_project.py')
-rw-r--r--release/scripts/uvcalc_smart_project.py1132
1 files changed, 0 insertions, 1132 deletions
diff --git a/release/scripts/uvcalc_smart_project.py b/release/scripts/uvcalc_smart_project.py
deleted file mode 100644
index 9d9bd2aaefd..00000000000
--- a/release/scripts/uvcalc_smart_project.py
+++ /dev/null
@@ -1,1132 +0,0 @@
-#!BPY
-
-""" Registration info for Blender menus: <- these words are ignored
-Name: 'Unwrap (smart projections)'
-Blender: 240
-Group: 'UVCalculation'
-Tooltip: 'UV Unwrap mesh faces for all select mesh objects'
-"""
-
-
-__author__ = "Campbell Barton"
-__url__ = ("blender", "blenderartists.org")
-__version__ = "1.1 12/18/05"
-
-__bpydoc__ = """\
-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.
-"""
-
-# --------------------------------------------------------------------------
-# Smart Projection UV Projection Unwrapper v1.1 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
-from Blender.Mathutils import Matrix, Vector, RotationMatrix
-import bpy
-from math import cos
-
-DEG_TO_RAD = 0.017453292519943295 # pi/180.0
-SMALL_NUM = 0.000000001
-BIG_NUM = 1e15
-
-global USER_FILL_HOLES
-global USER_FILL_HOLES_QUALITY
-USER_FILL_HOLES = None
-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
-
-
-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
-
-"""
-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
-"""
-
-# Turns the islands into a list of unpordered edges (Non internal)
-# Onlt for UV's
-# 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.iteritems() 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.itervalues()]
-
-# ========================= NOT WORKING????
-# Find if a points inside an edge loop, un-orderd.
-# pt is and x/y
-# edges are a non ordered loop of edges.
-# #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
-"""
-
-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
-
-
-# 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
-
-
-
-
-# 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
-
-# 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( 90, 2)
-ROTMAT_2D_POS_45D = RotationMatrix( 45, 2)
-
-RotMatStepRotation = []
-rot_angle = 22.5 #45.0/2
-while rot_angle > 0.1:
- RotMatStepRotation.append([\
- RotationMatrix( rot_angle, 2),\
- RotationMatrix( -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(xrange(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[:]
-
- try: decoratedIslandListAreaSort.sort(key = lambda A: A[3])
- except: decoratedIslandListAreaSort.sort(lambda A, B: cmp(A[3], B[3]))
-
-
- # sort by efficiency, Least Efficient first.
- decoratedIslandListEfficSort = decoratedIslandList[:]
- # decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], A[2]))
-
- try: decoratedIslandListEfficSort.sort(key = lambda A: -A[2])
- except: decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], 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
- 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.
-
-# 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
- SEAM = Mesh.EdgeFlags.SEAM
- for ed in me.edges:
- if ed.flag & SEAM:
- edge_seams[ed.key] = None # dummy var- use sets!
- # Done finding seams
-
-
- islandList = []
-
- 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 edge_seams.has_key(ed_key): # 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 xrange(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 xrange(len(faces)):
- if face_modes[i] == 0:
- newIsland = []
- newIsland.append(faces[i])
-
- face_modes[i] = ok = 1
- break
- # if not ok will stop looping
-
- Window.DrawProgressBar(0.1, 'Optimizing Rotation for %i UV Islands' % len(islandList))
-
- for island in islandList:
- optiRotateUvIsland(island)
-
- return islandList
-
-
-def packIslands(islandList):
- if USER_FILL_HOLES:
- 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...'
- Window.DrawProgressBar(0.7, 'Packing %i UV Islands...' % len(packBoxes) )
-
- time1 = sys.time()
- packWidth, packHeight = Geometry.BoxPack2D(packBoxes)
-
- # print 'Box Packing Time:', sys.time() - time1
-
- #if len(pa ckedLs) != len(islandList):
- # raise "Error packed boxes differes from original length"
-
- #print '\tWriting Packed Data to faces'
- 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
-
-
-
-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]])
-
-
-
-class thickface(object):
- __slost__= 'v', 'uv', 'no', 'area', 'edge_keys'
- def __init__(self, face):
- self.v = face.v
- self.uv = face.uv
- self.no = face.no
- self.area = face.area
- self.edge_keys = face.edge_keys
-
-global ob
-ob = None
-def main():
- global USER_FILL_HOLES
- global USER_FILL_HOLES_QUALITY
- global USER_STRETCH_ASPECT
- global USER_ISLAND_MARGIN
-
- objects= bpy.data.scenes.active.objects
-
- # we can will tag them later.
- obList = [ob for ob in objects.context if ob.type == 'Mesh']
-
- # Face select object may not be selected.
- ob = objects.active
- if ob and ob.sel == 0 and ob.type == 'Mesh':
- # Add to the list
- obList =[ob]
- del objects
-
- if not obList:
- Draw.PupMenu('error, no selected mesh objects')
- return
-
- # Create the variables.
- USER_PROJECTION_LIMIT = Draw.Create(66)
- USER_ONLY_SELECTED_FACES = Draw.Create(1)
- USER_SHARE_SPACE = Draw.Create(1) # Only for hole filling.
- USER_STRETCH_ASPECT = Draw.Create(1) # Only for hole filling.
- USER_ISLAND_MARGIN = Draw.Create(0.0) # Only for hole filling.
- USER_FILL_HOLES = Draw.Create(0)
- USER_FILL_HOLES_QUALITY = Draw.Create(50) # Only for hole filling.
- USER_VIEW_INIT = Draw.Create(0) # Only for hole filling.
- USER_AREA_WEIGHT = Draw.Create(1) # Only for hole filling.
-
-
- pup_block = [\
- 'Projection',\
- ('Angle Limit:', USER_PROJECTION_LIMIT, 1, 89, 'lower for more projection groups, higher for less distortion.'),\
- ('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, 'Margin to reduce bleed from adjacent islands.'),\
- '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)'),\
- ]
-
- # 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:
- sys.sleep(10)
- '''
-
- if not Draw.PupBlock(ob % len(obList), pup_block):
- return
- del ob
-
- # Convert from being button types
- USER_PROJECTION_LIMIT = USER_PROJECTION_LIMIT.val
- USER_ONLY_SELECTED_FACES = USER_ONLY_SELECTED_FACES.val
- USER_SHARE_SPACE = USER_SHARE_SPACE.val
- USER_STRETCH_ASPECT = USER_STRETCH_ASPECT.val
- USER_ISLAND_MARGIN = USER_ISLAND_MARGIN.val
- USER_FILL_HOLES = USER_FILL_HOLES.val
- USER_FILL_HOLES_QUALITY = USER_FILL_HOLES_QUALITY.val
- USER_VIEW_INIT = USER_VIEW_INIT.val
- USER_AREA_WEIGHT = USER_AREA_WEIGHT.val
-
- 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 = Window.EditMode()
- if is_editmode:
- Window.EditMode(0)
- # 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
- try: obList.sort(key = lambda ob: ob.getData(name_only=1))
- except: obList.sort(lambda ob1, ob2: cmp( ob1.getData(name_only=1), ob2.getData(name_only=1) ))
-
- collected_islandList= []
-
- Window.WaitCursor(1)
-
- time1 = sys.time()
-
- # Tag as False se we dont operate on teh same mesh twice.
- bpy.data.meshes.tag = False
-
- for ob in obList:
- me = ob.getData(mesh=1)
-
- if me.tag or me.lib:
- continue
-
- # Tag as used
- me.tag = True
-
- if not me.faceUV: # Mesh has no UV Coords, dont bother.
- me.faceUV= True
-
- if USER_ONLY_SELECTED_FACES:
- meshFaces = [thickface(f) for f in me.faces if f.sel]
- else:
- meshFaces = map(thickface, me.faces)
-
- if not meshFaces:
- continue
-
- 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.
- try: meshFaces.sort( key = lambda a: -a.area )
- except: meshFaces.sort( lambda a, b: cmp(b.area , 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 xrange(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 xrange(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 xrange(len(projectVecs)) ]
-
- # MAP and Arrange # We know there are 3 or 4 faces here
-
- for fIdx in xrange(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 xrange(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):
- f_uv[j][:] = (MatProj * v.co)[: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:
- Window.DrawProgressBar(0.9, "Box Packing for all objects...")
- packIslands(collected_islandList)
-
- print "Smart Projection time: %.2f" % (sys.time() - time1)
- # Window.DrawProgressBar(0.9, "Smart Projections done, time: %.2f sec." % (sys.time() - time1))
-
- if is_editmode:
- Window.EditMode(1)
-
- Window.DrawProgressBar(1.0, "")
- Window.WaitCursor(0)
- Window.RedrawAll()
-
-if __name__ == '__main__':
- main()