Optimized merging uv islands when packing by testing only intersections of boundry uv edges.

updated to use more recent changes to the bpython api and some other cleanups.
This commit is contained in:
Campbell Barton 2006-09-25 16:24:07 +00:00
parent 7e4f1425b7
commit a3e3025663

@ -136,8 +136,33 @@ dict_matrix = {}
def pointInTri2D(v, v1, v2, v3): def pointInTri2D(v, v1, v2, v3):
global dict_matrix global dict_matrix
key = (v1.x, v1.y, v2.x, v2.y, v3.x, v3.y) 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: try:
mtx = dict_matrix[key] mtx = dict_matrix[key]
if not mtx: if not mtx:
@ -203,32 +228,49 @@ def boundsEdgeLoop(edges):
# Turns the islands into a list of unpordered edges (Non internal) # Turns the islands into a list of unpordered edges (Non internal)
# Onlt for UV's # Onlt for UV's
# only returns outline edges for intersection tests. and unique points.
def island2Edge(island): def island2Edge(island):
# Vert index edges # Vert index edges
edges = {} edges = {}
unique_points= {}
for f in island: for f in island:
for vIdx in xrange(len(f)): f_v= f.v
if f.v[vIdx].index > f.v[vIdx-1].index: f_uv= f.uv
edges[((f.uv[vIdx-1][0], f.uv[vIdx-1][1]), (f.uv[vIdx][0], f.uv[vIdx][1]))] =\ f_uvkey= map(tuple, f_uv)
(Vector([f.uv[vIdx-1][0], f.uv[vIdx-1][1]]) - Vector([f.uv[vIdx][0], f.uv[vIdx][1]])).length
else: # 3
edges[((f.uv[vIdx][0], f.uv[vIdx][1]), (f.uv[vIdx-1][0], f.uv[vIdx-1][1]) )] =\ for vIdx in xrange(len(f_v)):
(Vector([f.uv[vIdx-1][0], f.uv[vIdx-1][1]]) - Vector([f.uv[vIdx][0], f.uv[vIdx][1]])).length
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. # If 2 are the same then they will be together, but full [a,b] order is not correct.
# Sort by length # Sort by length
length_sorted_edges = [] length_sorted_edges = [(key[0], key[1], value) for key, value in edges.iteritems() if value != 0]
for key in edges.keys():
length_sorted_edges.append([key[0], key[1], edges[key]])
length_sorted_edges.sort(lambda A, B: cmp(B[2], A[2])) 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: #for e in length_sorted_edges:
# e.pop(2) # e.pop(2)
return length_sorted_edges # return edges and unique points
return length_sorted_edges, [v.__copy__().resize3D() for v in unique_points.itervalues()]
# ========================= NOT WORKING???? # ========================= NOT WORKING????
# Find if a points inside an edge loop, un-orderd. # Find if a points inside an edge loop, un-orderd.
@ -253,17 +295,6 @@ def pointInEdges(pt, edges):
return intersectCount % 2 return intersectCount % 2
""" """
def uniqueEdgePairPoints(edges):
points = {}
pointsVec = []
for e in edges:
points[e[0]] = points[e[1]] = None
for p in points.keys():
pointsVec.append( Vector([p[0], p[1], 0]) )
return pointsVec
def pointInIsland(pt, island): def pointInIsland(pt, island):
vec1 = Vector(); vec2 = Vector(); vec3 = Vector() vec1 = Vector(); vec2 = Vector(); vec3 = Vector()
for f in island: for f in island:
@ -300,7 +331,7 @@ def islandIntersectUvIsland(source, target, xSourceOffset, ySourceOffset):
# 1 test for source being totally inside target # 1 test for source being totally inside target
for pv in source[7]: for pv in source[7]:
p = Vector(pv) p = pv.__copy__()
p.x += xSourceOffset p.x += xSourceOffset
p.y += ySourceOffset p.y += ySourceOffset
@ -309,7 +340,7 @@ def islandIntersectUvIsland(source, target, xSourceOffset, ySourceOffset):
# 2 test for a part of the target being totaly inside the source. # 2 test for a part of the target being totaly inside the source.
for pv in target[7]: for pv in target[7]:
p = Vector(pv) p = pv.__copy__()
p.x -= xSourceOffset p.x -= xSourceOffset
p.y -= ySourceOffset p.y -= ySourceOffset
@ -403,7 +434,7 @@ def optiRotateUvIsland(faces):
# Serialized UV coords to Vectors # Serialized UV coords to Vectors
uvVecs = [Vector(uv) for f in faces for uv in f.uv] uvVecs = [uv for f in faces for uv in f.uv]
# Theres a small enough number of these to hard code it # Theres a small enough number of these to hard code it
# rather then a loop. # rather then a loop.
@ -463,17 +494,17 @@ def mergeUvIslands(islandList, islandListArea):
totFaceArea = 0 totFaceArea = 0
for fIdx, f in enumerate(islandList[islandIdx]): for fIdx, f in enumerate(islandList[islandIdx]):
f.uv = [Vector(uv[0]-minx, uv[1]-miny) for uv in f.uv] for uv in f.uv:
uv.x -= minx
uv.y -= miny
totFaceArea += islandListArea[islandIdx][fIdx] # Use Cached area. dont recalculate. totFaceArea += islandListArea[islandIdx][fIdx] # Use Cached area. dont recalculate.
islandBoundsArea = w*h islandBoundsArea = w*h
efficiency = abs(islandBoundsArea - totFaceArea) efficiency = abs(islandBoundsArea - totFaceArea)
# UV Edge list used for intersections # UV Edge list used for intersections as well as unique points.
edges = island2Edge(islandList[islandIdx]) edges, uniqueEdgePoints = island2Edge(islandList[islandIdx])
uniqueEdgePoints = uniqueEdgePairPoints(edges)
decoratedIslandList.append([islandList[islandIdx], totFaceArea, efficiency, islandBoundsArea, w,h, edges, uniqueEdgePoints]) decoratedIslandList.append([islandList[islandIdx], totFaceArea, efficiency, islandBoundsArea, w,h, edges, uniqueEdgePoints])
@ -613,7 +644,10 @@ def mergeUvIslands(islandList, islandListArea):
targetIsland[0].extend(sourceIsland[0]) targetIsland[0].extend(sourceIsland[0])
for f in sourceIsland[0]: for f in sourceIsland[0]:
f.uv = [Vector(uv[0]+boxLeft, uv[1]+boxBottom) for uv in f.uv] for uv in f.uv:
uv.x += boxLeft
uv.y += boxBottom
sourceIsland[0][:] = [] # Empty sourceIsland[0][:] = [] # Empty
@ -663,7 +697,7 @@ def mergeUvIslands(islandList, islandListArea):
while i: while i:
i-=1 i-=1
if not islandList[i]: if not islandList[i]:
islandList.pop(i) # Can increment islands removed here. del islandList[i] # Can increment islands removed here.
# Takes groups of faces. assumes face groups are UV groups. # Takes groups of faces. assumes face groups are UV groups.
@ -687,10 +721,9 @@ def getUvIslands(faceGroups, faceGroupsArea, me):
faceUsersArea = [[] for i in xrange(len(me.verts)) ] faceUsersArea = [[] for i in xrange(len(me.verts)) ]
# Do the first face # Do the first face
fIdx = len(faces) fIdx = len(faces)
while fIdx: for fIdx, f in enumerate(faces):
fIdx-=1 for v in f:
for v in faces[fIdx].v: faceUsers[v.index].append(f)
faceUsers[v.index].append(faces[fIdx])
faceUsersArea[v.index].append(facesArea[fIdx]) faceUsersArea[v.index].append(facesArea[fIdx])
@ -708,7 +741,7 @@ def getUvIslands(faceGroups, faceGroupsArea, me):
hasBeenUsed = 1 # Assume its been used. hasBeenUsed = 1 # Assume its been used.
if searchFaceIndex >= len(faces): if searchFaceIndex >= len(faces):
break break
for v in faces[searchFaceIndex].v: for v in faces[searchFaceIndex]:
if faces[searchFaceIndex] in faceUsers[v.index]: if faces[searchFaceIndex] in faceUsers[v.index]:
# This has not yet been used, it still being used by a vert # This has not yet been used, it still being used by a vert
hasBeenUsed = 0 hasBeenUsed = 0
@ -724,34 +757,36 @@ def getUvIslands(faceGroups, faceGroupsArea, me):
# Before we start remove the first, search face from being used. # Before we start remove the first, search face from being used.
for v in newIsland[0].v: for v in newIsland[0]:
vIdx= v.index
popoffset = 0 popoffset = 0
for fIdx in xrange(len(faceUsers[v.index])): for fIdx in xrange(len(faceUsers[vIdx])):
if faceUsers[v.index][fIdx - popoffset] is newIsland[0]: if faceUsers[vIdx][fIdx - popoffset] is newIsland[0]:
faceUsers[v.index].pop(fIdx - popoffset) del faceUsers[vIdx][fIdx - popoffset]
faceUsersArea[v.index].pop(fIdx - popoffset) del faceUsersArea[vIdx][fIdx - popoffset]
popoffset += 1 popoffset += 1
searchFaceIndex = 0 searchFaceIndex = 0
while searchFaceIndex != len(newIsland): while searchFaceIndex != len(newIsland):
for v in newIsland[searchFaceIndex].v: for v in newIsland[searchFaceIndex]:
vIdx= v.index
# Loop through all faces that use this vert # Loop through all faces that use this vert
while faceUsers[v.index]: while faceUsers[vIdx]:
sharedFace = faceUsers[v.index][-1] sharedFace = faceUsers[vIdx][-1]
sharedFaceArea = faceUsersArea[v.index][-1] sharedFaceArea = faceUsersArea[vIdx][-1]
newIsland.append(sharedFace) newIsland.append(sharedFace)
newIslandArea.append(sharedFaceArea) newIslandArea.append(sharedFaceArea)
# Before we start remove the first, search face from being used. # Before we start remove the first, search face from being used.
for vv in sharedFace.v: for vv in sharedFace:
#faceUsers = [f for f in faceUsers[vv.index] if f != sharedFace] #faceUsers = [f for f in faceUsers[vv.index] if f != sharedFace]
vvIdx= vv.index
fIdx = 0 fIdx = 0
for fIdx in xrange(len(faceUsers[vv.index])): for fIdx in xrange(len(faceUsers[vvIdx])):
if faceUsers[vv.index][fIdx] is sharedFace: if faceUsers[vvIdx][fIdx] is sharedFace:
faceUsers[vv.index].pop(fIdx) del faceUsers[vvIdx][fIdx]
faceUsersArea[vv.index].pop(fIdx) del faceUsersArea[vvIdx][fIdx]
break # Can only be used once. break # Can only be used once.
searchFaceIndex += 1 searchFaceIndex += 1
@ -852,16 +887,20 @@ def packIslands(islandList, islandListArea):
if USER_MARGIN: if USER_MARGIN:
USER_MARGIN_SCALE = 1-(USER_MARGIN*2) USER_MARGIN_SCALE = 1-(USER_MARGIN*2)
for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box, margin for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box, margin
f.uv = [Vector((((uv[0]+xoffset)*xfactor)*USER_MARGIN_SCALE)+USER_MARGIN, (((uv[1]+yoffset)*yfactor)*USER_MARGIN_SCALE)+USER_MARGIN) for uv in f.uv] for uv in f.uv:
uv.x= (((uv.x+xoffset) * xfactor ) * USER_MARGIN_SCALE ) * USER_MARGIN
uv.y= (((uv.y+yoffset) * yfactor ) * USER_MARGIN_SCALE ) * USER_MARGIN
else: else:
for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box
f.uv = [Vector(((uv[0]+xoffset)*xfactor), ((uv[1]+yoffset)*yfactor)) for uv in f.uv] for uv in f.uv:
uv.x= (uv.x+xoffset) * xfactor
uv.y= (uv.y+yoffset) * yfactor
def VectoMat(vec): def VectoMat(vec):
a3 = Vector(vec) # copy the vector a3 = vec.__copy__().normalize()
a3.normalize()
up = Vector(0,0,1) up = Vector(0,0,1)
if abs(DotVecs(a3, up)) == 1.0: if abs(DotVecs(a3, up)) == 1.0:
@ -881,17 +920,18 @@ def main():
global USER_STRETCH_ASPECT global USER_STRETCH_ASPECT
global USER_MARGIN global USER_MARGIN
objects= Scene.GetCurrent().objects
# Use datanames as kesy so as not to unwrap a mesh more then once. # Use datanames as kesy so as not to unwrap a mesh more then once.
obList = dict([(ob.getData(name_only=1), ob) for ob in Object.GetSelected() if ob.getType() == 'Mesh']) obList = dict([(ob.getData(name_only=1), ob) for ob in objects.context if ob.type == 'Mesh'])
# Face select object may not be selected. # Face select object may not be selected.
scn = Scene.GetCurrent() ob = objects.active
ob = scn.getActiveObject() if ob and ob.sel == 0 and ob.type == 'Mesh':
if ob and ob.sel == 0 and ob.getType() == 'Mesh':
# Add to the list # Add to the list
obList[ob.getData(name_only=1)] = ob obList[ob.getData(name_only=1)] = ob
del scn # Sone use the scene again. del objects
obList = obList.values() # turn from a dict to a list. obList = obList.values() # turn from a dict to a list.
@ -978,7 +1018,7 @@ def main():
if USER_ONLY_SELECTED_FACES: if USER_ONLY_SELECTED_FACES:
meshFaces = [f for f in me.faces if f.flag & SELECT_FLAG] meshFaces = [f for f in me.faces if f.flag & SELECT_FLAG]
else: else:
meshFaces = [f for f in me.faces] meshFaces = me.faces
if not meshFaces: if not meshFaces:
continue continue
@ -1000,12 +1040,13 @@ def main():
area = f.area area = f.area
if area <= SMALL_NUM: if area <= SMALL_NUM:
for uv in f.uv: # Assign Dummy UVs for uv in f.uv: # Assign Dummy UVs
uv.x= uv.y= 0.0 uv.zero()
print 'found zero area face, removing.' print 'found zero area face, removing.'
else: else:
# Store all here # Store all here
faceListProps.append( [f, area, f.no] ) faceListProps.append( (f, area, f.no) )
del meshFaces del meshFaces
@ -1102,7 +1143,7 @@ def main():
fIdx = len(faceListProps) fIdx = len(faceListProps)
while fIdx: while fIdx:
fIdx-=1 fIdx-=1
fvec = Vector(faceListProps[fIdx][2]) fvec = faceListProps[fIdx][2]
i = len(projectVecs) i = len(projectVecs)
# Initialize first # Initialize first
@ -1140,7 +1181,7 @@ def main():
# Get the faces UV's from the projected vertex. # Get the faces UV's from the projected vertex.
for f in faceProjectionGroupList[i]: for f in faceProjectionGroupList[i]:
f.uv = [MatProj * v.co for v in f.v] f.uv = [MatProj * v.co for v in f]
if USER_SHARE_SPACE: if USER_SHARE_SPACE: