~siretart/ubuntu/utopic/blender/libav10

« back to all changes in this revision

Viewing changes to release/scripts/op/uvcalc_smart_project.py

  • Committer: Package Import Robot
  • Author(s): Matteo F. Vescovi
  • Date: 2012-07-23 08:54:18 UTC
  • mfrom: (14.2.16 sid)
  • mto: (14.2.19 sid)
  • mto: This revision was merged to the branch mainline in revision 42.
  • Revision ID: package-import@ubuntu.com-20120723085418-9foz30v6afaf5ffs
Tags: 2.63a-2
* debian/: Cycles support added (Closes: #658075)
  For now, this top feature has been enabled only
  on [any-amd64 any-i386] architectures because
  of OpenImageIO failing on all others
* debian/: scripts installation path changed
  from /usr/lib to /usr/share:
  + debian/patches/: patchset re-worked for path changing
  + debian/control: "Breaks" field added on yafaray-exporter

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# --------------------------------------------------------------------------
2
 
# Smart Projection UV Projection Unwrapper v1.2 by Campbell Barton (AKA Ideasman)
3
 
# --------------------------------------------------------------------------
4
 
# ***** BEGIN GPL LICENSE BLOCK *****
5
 
#
6
 
# This program is free software; you can redistribute it and/or
7
 
# modify it under the terms of the GNU General Public License
8
 
# as published by the Free Software Foundation; either version 2
9
 
# of the License, or (at your option) any later version.
10
 
#
11
 
# This program is distributed in the hope that it will be useful,
12
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 
# GNU General Public License for more details.
15
 
#
16
 
# You should have received a copy of the GNU General Public License
17
 
# along with this program; if not, write to the Free Software Foundation,
18
 
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
 
#
20
 
# ***** END GPL LICENCE BLOCK *****
21
 
# --------------------------------------------------------------------------
22
 
 
23
 
# <pep8 compliant>
24
 
 
25
 
from mathutils import Matrix, Vector, RotationMatrix
26
 
import time
27
 
import geometry
28
 
import bpy
29
 
from math import cos, radians
30
 
 
31
 
DEG_TO_RAD = 0.017453292519943295 # pi/180.0
32
 
SMALL_NUM = 0.000000001
33
 
BIG_NUM = 1e15
34
 
 
35
 
global USER_FILL_HOLES
36
 
global USER_FILL_HOLES_QUALITY
37
 
USER_FILL_HOLES = None
38
 
USER_FILL_HOLES_QUALITY = None
39
 
 
40
 
dict_matrix = {}
41
 
 
42
 
def pointInTri2D(v, v1, v2, v3):
43
 
    global dict_matrix
44
 
 
45
 
    key = v1.x, v1.y, v2.x, v2.y, v3.x, v3.y
46
 
 
47
 
    # Commented because its slower to do teh bounds check, we should realy cache the bounds info for each face.
48
 
    '''
49
 
    # BOUNDS CHECK
50
 
    xmin= 1000000
51
 
    ymin= 1000000
52
 
 
53
 
    xmax= -1000000
54
 
    ymax= -1000000
55
 
 
56
 
    for i in (0,2,4):
57
 
        x= key[i]
58
 
        y= key[i+1]
59
 
 
60
 
        if xmax<x:      xmax= x
61
 
        if ymax<y:      ymax= y
62
 
        if xmin>x:      xmin= x
63
 
        if ymin>y:      ymin= y
64
 
 
65
 
    x= v.x
66
 
    y= v.y
67
 
 
68
 
    if x<xmin or x>xmax or y < ymin or y > ymax:
69
 
        return False
70
 
    # Done with bounds check
71
 
    '''
72
 
    try:
73
 
        mtx = dict_matrix[key]
74
 
        if not mtx:
75
 
            return False
76
 
    except:
77
 
        side1 = v2 - v1
78
 
        side2 = v3 - v1
79
 
 
80
 
        nor = side1.cross(side2)
81
 
 
82
 
        l1 = [side1[0], side1[1], side1[2]]
83
 
        l2 = [side2[0], side2[1], side2[2]]
84
 
        l3 = [nor[0], nor[1], nor[2]]
85
 
 
86
 
        mtx = Matrix(l1, l2, l3)
87
 
 
88
 
        # Zero area 2d tri, even tho we throw away zerop area faces
89
 
        # the projection UV can result in a zero area UV.
90
 
        if not mtx.determinant():
91
 
            dict_matrix[key] = None
92
 
            return False
93
 
 
94
 
        mtx.invert()
95
 
 
96
 
        dict_matrix[key] = mtx
97
 
 
98
 
    uvw = (v - v1) * mtx
99
 
    return 0 <= uvw[0] and 0 <= uvw[1] and uvw[0] + uvw[1] <= 1
100
 
 
101
 
 
102
 
def boundsIsland(faces):
103
 
    minx = maxx = faces[0].uv[0][0] # Set initial bounds.
104
 
    miny = maxy = faces[0].uv[0][1]
105
 
    # print len(faces), minx, maxx, miny , maxy
106
 
    for f in faces:
107
 
        for uv in f.uv:
108
 
            x= uv.x
109
 
            y= uv.y
110
 
            if x<minx: minx= x
111
 
            if y<miny: miny= y
112
 
            if x>maxx: maxx= x
113
 
            if y>maxy: maxy= y
114
 
 
115
 
    return minx, miny, maxx, maxy
116
 
 
117
 
"""
118
 
def boundsEdgeLoop(edges):
119
 
    minx = maxx = edges[0][0] # Set initial bounds.
120
 
    miny = maxy = edges[0][1]
121
 
    # print len(faces), minx, maxx, miny , maxy
122
 
    for ed in edges:
123
 
        for pt in ed:
124
 
            print 'ass'
125
 
            x= pt[0]
126
 
            y= pt[1]
127
 
            if x<minx: x= minx
128
 
            if y<miny: y= miny
129
 
            if x>maxx: x= maxx
130
 
            if y>maxy: y= maxy
131
 
 
132
 
    return minx, miny, maxx, maxy
133
 
"""
134
 
 
135
 
# Turns the islands into a list of unpordered edges (Non internal)
136
 
# Onlt for UV's
137
 
# only returns outline edges for intersection tests. and unique points.
138
 
 
139
 
def island2Edge(island):
140
 
 
141
 
    # Vert index edges
142
 
    edges = {}
143
 
 
144
 
    unique_points= {}
145
 
 
146
 
    for f in island:
147
 
        f_uvkey= map(tuple, f.uv)
148
 
 
149
 
 
150
 
        for vIdx, edkey in enumerate(f.edge_keys):
151
 
            unique_points[f_uvkey[vIdx]] = f.uv[vIdx]
152
 
 
153
 
            if f.v[vIdx].index > f.v[vIdx-1].index:
154
 
                i1= vIdx-1;     i2= vIdx
155
 
            else:
156
 
                i1= vIdx;       i2= vIdx-1
157
 
 
158
 
            try:        edges[ f_uvkey[i1], f_uvkey[i2] ] *= 0 # sets eny edge with more then 1 user to 0 are not returned.
159
 
            except:     edges[ f_uvkey[i1], f_uvkey[i2] ] = (f.uv[i1] - f.uv[i2]).length,
160
 
 
161
 
    # If 2 are the same then they will be together, but full [a,b] order is not correct.
162
 
 
163
 
    # Sort by length
164
 
 
165
 
 
166
 
    length_sorted_edges = [(Vector(key[0]), Vector(key[1]), value) for key, value in edges.items() if value != 0]
167
 
 
168
 
    try:        length_sorted_edges.sort(key = lambda A: -A[2]) # largest first
169
 
    except:     length_sorted_edges.sort(lambda A, B: cmp(B[2], A[2]))
170
 
 
171
 
    # Its okay to leave the length in there.
172
 
    #for e in length_sorted_edges:
173
 
    #   e.pop(2)
174
 
 
175
 
    # return edges and unique points
176
 
    return length_sorted_edges, [v.__copy__().resize3D() for v in unique_points.values()]
177
 
 
178
 
# ========================= NOT WORKING????
179
 
# Find if a points inside an edge loop, un-orderd.
180
 
# pt is and x/y
181
 
# edges are a non ordered loop of edges.
182
 
# #offsets are the edge x and y offset.
183
 
"""
184
 
def pointInEdges(pt, edges):
185
 
    #
186
 
    x1 = pt[0]
187
 
    y1 = pt[1]
188
 
 
189
 
    # Point to the left of this line.
190
 
    x2 = -100000
191
 
    y2 = -10000
192
 
    intersectCount = 0
193
 
    for ed in edges:
194
 
        xi, yi = lineIntersection2D(x1,y1, x2,y2, ed[0][0], ed[0][1], ed[1][0], ed[1][1])
195
 
        if xi != None: # Is there an intersection.
196
 
            intersectCount+=1
197
 
 
198
 
    return intersectCount % 2
199
 
"""
200
 
 
201
 
def pointInIsland(pt, island):
202
 
    vec1, vec2, vec3 = Vector(), Vector(), Vector()
203
 
    for f in island:
204
 
        vec1.x, vec1.y = f.uv[0]
205
 
        vec2.x, vec2.y = f.uv[1]
206
 
        vec3.x, vec3.y = f.uv[2]
207
 
 
208
 
        if pointInTri2D(pt, vec1, vec2, vec3):
209
 
            return True
210
 
 
211
 
        if len(f.v) == 4:
212
 
            vec1.x, vec1.y = f.uv[0]
213
 
            vec2.x, vec2.y = f.uv[2]
214
 
            vec3.x, vec3.y = f.uv[3]
215
 
            if pointInTri2D(pt, vec1, vec2, vec3):
216
 
                return True
217
 
    return False
218
 
 
219
 
 
220
 
# box is (left,bottom, right, top)
221
 
def islandIntersectUvIsland(source, target, SourceOffset):
222
 
    # Is 1 point in the box, inside the vertLoops
223
 
    edgeLoopsSource = source[6] # Pretend this is offset
224
 
    edgeLoopsTarget = target[6]
225
 
 
226
 
    # Edge intersect test
227
 
    for ed in edgeLoopsSource:
228
 
        for seg in edgeLoopsTarget:
229
 
            i = geometry.LineIntersect2D(\
230
 
            seg[0], seg[1], SourceOffset+ed[0], SourceOffset+ed[1])
231
 
            if i:
232
 
                return 1 # LINE INTERSECTION
233
 
 
234
 
    # 1 test for source being totally inside target
235
 
    SourceOffset.resize3D()
236
 
    for pv in source[7]:
237
 
        if pointInIsland(pv+SourceOffset, target[0]):
238
 
            return 2 # SOURCE INSIDE TARGET
239
 
 
240
 
    # 2 test for a part of the target being totaly inside the source.
241
 
    for pv in target[7]:
242
 
        if pointInIsland(pv-SourceOffset, source[0]):
243
 
            return 3 # PART OF TARGET INSIDE SOURCE.
244
 
 
245
 
    return 0 # NO INTERSECTION
246
 
 
247
 
 
248
 
 
249
 
 
250
 
# Returns the X/y Bounds of a list of vectors.
251
 
def testNewVecLs2DRotIsBetter(vecs, mat=-1, bestAreaSoFar = -1):
252
 
 
253
 
    # UV's will never extend this far.
254
 
    minx = miny = BIG_NUM
255
 
    maxx = maxy = -BIG_NUM
256
 
 
257
 
    for i, v in enumerate(vecs):
258
 
 
259
 
        # Do this allong the way
260
 
        if mat != -1:
261
 
            v = vecs[i] = v*mat
262
 
            x= v.x
263
 
            y= v.y
264
 
            if x<minx: minx= x
265
 
            if y<miny: miny= y
266
 
            if x>maxx: maxx= x
267
 
            if y>maxy: maxy= y
268
 
 
269
 
        # Spesific to this algo, bail out if we get bigger then the current area
270
 
        if bestAreaSoFar != -1 and (maxx-minx) * (maxy-miny) > bestAreaSoFar:
271
 
            return (BIG_NUM, None), None
272
 
    w = maxx-minx
273
 
    h = maxy-miny
274
 
    return (w*h, w,h), vecs # Area, vecs
275
 
 
276
 
# Takes a list of faces that make up a UV island and rotate
277
 
# until they optimally fit inside a square.
278
 
ROTMAT_2D_POS_90D = RotationMatrix( radians(90.0), 2)
279
 
ROTMAT_2D_POS_45D = RotationMatrix( radians(45.0), 2)
280
 
 
281
 
RotMatStepRotation = []
282
 
rot_angle = 22.5 #45.0/2
283
 
while rot_angle > 0.1:
284
 
    RotMatStepRotation.append([\
285
 
     RotationMatrix( radians(rot_angle), 2),\
286
 
     RotationMatrix( radians(-rot_angle), 2)])
287
 
 
288
 
    rot_angle = rot_angle/2.0
289
 
 
290
 
 
291
 
def optiRotateUvIsland(faces):
292
 
    global currentArea
293
 
 
294
 
    # Bestfit Rotation
295
 
    def best2dRotation(uvVecs, MAT1, MAT2):
296
 
        global currentArea
297
 
 
298
 
        newAreaPos, newfaceProjectionGroupListPos =\
299
 
        testNewVecLs2DRotIsBetter(uvVecs[:], MAT1, currentArea[0])
300
 
 
301
 
 
302
 
        # Why do I use newpos here? May as well give the best area to date for an early bailout
303
 
        # some slight speed increase in this.
304
 
        # If the new rotation is smaller then the existing, we can
305
 
        # avoid copying a list and overwrite the old, crappy one.
306
 
 
307
 
        if newAreaPos[0] < currentArea[0]:
308
 
            newAreaNeg, newfaceProjectionGroupListNeg =\
309
 
            testNewVecLs2DRotIsBetter(uvVecs, MAT2, newAreaPos[0])  # Reuse the old bigger list.
310
 
        else:
311
 
            newAreaNeg, newfaceProjectionGroupListNeg =\
312
 
            testNewVecLs2DRotIsBetter(uvVecs[:], MAT2, currentArea[0])  # Cant reuse, make a copy.
313
 
 
314
 
 
315
 
        # Now from the 3 options we need to discover which to use
316
 
        # we have cerrentArea/newAreaPos/newAreaNeg
317
 
        bestArea = min(currentArea[0], newAreaPos[0], newAreaNeg[0])
318
 
 
319
 
        if currentArea[0] == bestArea:
320
 
            return uvVecs
321
 
        elif newAreaPos[0] == bestArea:
322
 
            uvVecs = newfaceProjectionGroupListPos
323
 
            currentArea = newAreaPos
324
 
        elif newAreaNeg[0] == bestArea:
325
 
            uvVecs = newfaceProjectionGroupListNeg
326
 
            currentArea = newAreaNeg
327
 
 
328
 
        return uvVecs
329
 
 
330
 
 
331
 
    # Serialized UV coords to Vectors
332
 
    uvVecs = [uv for f in faces  for uv in f.uv]
333
 
 
334
 
    # Theres a small enough number of these to hard code it
335
 
    # rather then a loop.
336
 
 
337
 
    # Will not modify anything
338
 
    currentArea, dummy =\
339
 
    testNewVecLs2DRotIsBetter(uvVecs)
340
 
 
341
 
 
342
 
    # Try a 45d rotation
343
 
    newAreaPos, newfaceProjectionGroupListPos = testNewVecLs2DRotIsBetter(uvVecs[:], ROTMAT_2D_POS_45D, currentArea[0])
344
 
 
345
 
    if newAreaPos[0] < currentArea[0]:
346
 
        uvVecs = newfaceProjectionGroupListPos
347
 
        currentArea = newAreaPos
348
 
    # 45d done
349
 
 
350
 
    # Testcase different rotations and find the onfe that best fits in a square
351
 
    for ROTMAT in RotMatStepRotation:
352
 
        uvVecs = best2dRotation(uvVecs, ROTMAT[0], ROTMAT[1])
353
 
 
354
 
    # Only if you want it, make faces verticle!
355
 
    if currentArea[1] > currentArea[2]:
356
 
        # Rotate 90d
357
 
        # Work directly on the list, no need to return a value.
358
 
        testNewVecLs2DRotIsBetter(uvVecs, ROTMAT_2D_POS_90D)
359
 
 
360
 
 
361
 
    # Now write the vectors back to the face UV's
362
 
    i = 0 # count the serialized uv/vectors
363
 
    for f in faces:
364
 
        #f.uv = [uv for uv in uvVecs[i:len(f)+i] ]
365
 
        for j, k in enumerate(range(i, len(f.v)+i)):
366
 
            f.uv[j][:] = uvVecs[k]
367
 
        i += len(f.v)
368
 
 
369
 
 
370
 
# Takes an island list and tries to find concave, hollow areas to pack smaller islands into.
371
 
def mergeUvIslands(islandList):
372
 
    global USER_FILL_HOLES
373
 
    global USER_FILL_HOLES_QUALITY
374
 
 
375
 
 
376
 
    # Pack islands to bottom LHS
377
 
    # Sync with island
378
 
 
379
 
    #islandTotFaceArea = [] # A list of floats, each island area
380
 
    #islandArea = [] # a list of tuples ( area, w,h)
381
 
 
382
 
 
383
 
    decoratedIslandList = []
384
 
 
385
 
    islandIdx = len(islandList)
386
 
    while islandIdx:
387
 
        islandIdx-=1
388
 
        minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
389
 
        w, h = maxx-minx, maxy-miny
390
 
 
391
 
        totFaceArea = 0
392
 
        offset= Vector((minx, miny))
393
 
        for f in islandList[islandIdx]:
394
 
            for uv in f.uv:
395
 
                uv -= offset
396
 
 
397
 
            totFaceArea += f.area
398
 
 
399
 
        islandBoundsArea = w*h
400
 
        efficiency = abs(islandBoundsArea - totFaceArea)
401
 
 
402
 
        # UV Edge list used for intersections as well as unique points.
403
 
        edges, uniqueEdgePoints = island2Edge(islandList[islandIdx])
404
 
 
405
 
        decoratedIslandList.append([islandList[islandIdx], totFaceArea, efficiency, islandBoundsArea, w,h, edges, uniqueEdgePoints])
406
 
 
407
 
 
408
 
    # Sort by island bounding box area, smallest face area first.
409
 
    # no.. chance that to most simple edge loop first.
410
 
    decoratedIslandListAreaSort =decoratedIslandList[:]
411
 
 
412
 
    decoratedIslandListAreaSort.sort(key = lambda A: A[3])
413
 
 
414
 
    # sort by efficiency, Least Efficient first.
415
 
    decoratedIslandListEfficSort = decoratedIslandList[:]
416
 
    # decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], A[2]))
417
 
 
418
 
    decoratedIslandListEfficSort.sort(key = lambda A: -A[2])
419
 
 
420
 
    # ================================================== THESE CAN BE TWEAKED.
421
 
    # This is a quality value for the number of tests.
422
 
    # from 1 to 4, generic quality value is from 1 to 100
423
 
    USER_STEP_QUALITY =   ((USER_FILL_HOLES_QUALITY - 1) / 25.0) + 1
424
 
 
425
 
    # If 100 will test as long as there is enough free space.
426
 
    # this is rarely enough, and testing takes a while, so lower quality speeds this up.
427
 
 
428
 
    # 1 means they have the same quality
429
 
    USER_FREE_SPACE_TO_TEST_QUALITY = 1 + (((100 - USER_FILL_HOLES_QUALITY)/100.0) *5)
430
 
 
431
 
    #print 'USER_STEP_QUALITY', USER_STEP_QUALITY
432
 
    #print 'USER_FREE_SPACE_TO_TEST_QUALITY', USER_FREE_SPACE_TO_TEST_QUALITY
433
 
 
434
 
    removedCount = 0
435
 
 
436
 
    areaIslandIdx = 0
437
 
    ctrl = Window.Qual.CTRL
438
 
    BREAK= False
439
 
    while areaIslandIdx < len(decoratedIslandListAreaSort) and not BREAK:
440
 
        sourceIsland = decoratedIslandListAreaSort[areaIslandIdx]
441
 
        # Alredy packed?
442
 
        if not sourceIsland[0]:
443
 
            areaIslandIdx+=1
444
 
        else:
445
 
            efficIslandIdx = 0
446
 
            while efficIslandIdx < len(decoratedIslandListEfficSort) and not BREAK:
447
 
 
448
 
                if Window.GetKeyQualifiers() & ctrl:
449
 
                    BREAK= True
450
 
                    break
451
 
 
452
 
                # Now we have 2 islands, is the efficience of the islands lowers theres an
453
 
                # increasing likely hood that we can fit merge into the bigger UV island.
454
 
                # this ensures a tight fit.
455
 
 
456
 
                # Just use figures we have about user/unused area to see if they might fit.
457
 
 
458
 
                targetIsland = decoratedIslandListEfficSort[efficIslandIdx]
459
 
 
460
 
 
461
 
                if sourceIsland[0] == targetIsland[0] or\
462
 
                not targetIsland[0] or\
463
 
                not sourceIsland[0]:
464
 
                    pass
465
 
                else:
466
 
 
467
 
                    # ([island, totFaceArea, efficiency, islandArea, w,h])
468
 
                    # Waisted space on target is greater then UV bounding island area.
469
 
 
470
 
 
471
 
                    # if targetIsland[3] > (sourceIsland[2]) and\ #
472
 
                    # print USER_FREE_SPACE_TO_TEST_QUALITY, 'ass'
473
 
                    if targetIsland[2] > (sourceIsland[1] * USER_FREE_SPACE_TO_TEST_QUALITY) and\
474
 
                    targetIsland[4] > sourceIsland[4] and\
475
 
                    targetIsland[5] > sourceIsland[5]:
476
 
 
477
 
                        # DEBUG # print '%.10f  %.10f' % (targetIsland[3], sourceIsland[1])
478
 
 
479
 
                        # These enough spare space lets move the box until it fits
480
 
 
481
 
                        # How many times does the source fit into the target x/y
482
 
                        blockTestXUnit = targetIsland[4]/sourceIsland[4]
483
 
                        blockTestYUnit = targetIsland[5]/sourceIsland[5]
484
 
 
485
 
                        boxLeft = 0
486
 
 
487
 
 
488
 
                        # Distllllance we can move between whilst staying inside the targets bounds.
489
 
                        testWidth = targetIsland[4] - sourceIsland[4]
490
 
                        testHeight = targetIsland[5] - sourceIsland[5]
491
 
 
492
 
                        # Increment we move each test. x/y
493
 
                        xIncrement = (testWidth / (blockTestXUnit * ((USER_STEP_QUALITY/50)+0.1)))
494
 
                        yIncrement = (testHeight / (blockTestYUnit * ((USER_STEP_QUALITY/50)+0.1)))
495
 
 
496
 
                        # Make sure were not moving less then a 3rg of our width/height
497
 
                        if xIncrement<sourceIsland[4]/3:
498
 
                            xIncrement= sourceIsland[4]
499
 
                        if yIncrement<sourceIsland[5]/3:
500
 
                            yIncrement= sourceIsland[5]
501
 
 
502
 
 
503
 
                        boxLeft = 0 # Start 1 back so we can jump into the loop.
504
 
                        boxBottom= 0 #-yIncrement
505
 
 
506
 
                        ##testcount= 0
507
 
 
508
 
                        while boxBottom <= testHeight:
509
 
                            # Should we use this? - not needed for now.
510
 
                            #if Window.GetKeyQualifiers() & ctrl:
511
 
                            #   BREAK= True
512
 
                            #   break
513
 
 
514
 
                            ##testcount+=1
515
 
                            #print 'Testing intersect'
516
 
                            Intersect = islandIntersectUvIsland(sourceIsland, targetIsland, Vector((boxLeft, boxBottom)))
517
 
                            #print 'Done', Intersect
518
 
                            if Intersect == 1:  # Line intersect, dont bother with this any more
519
 
                                pass
520
 
 
521
 
                            if Intersect == 2:  # Source inside target
522
 
                                '''
523
 
                                We have an intersection, if we are inside the target
524
 
                                then move us 1 whole width accross,
525
 
                                Its possible this is a bad idea since 2 skinny Angular faces
526
 
                                could join without 1 whole move, but its a lot more optimal to speed this up
527
 
                                since we have already tested for it.
528
 
 
529
 
                                It gives about 10% speedup with minimal errors.
530
 
                                '''
531
 
                                #print 'ass'
532
 
                                # Move the test allong its width + SMALL_NUM
533
 
                                #boxLeft += sourceIsland[4] + SMALL_NUM
534
 
                                boxLeft += sourceIsland[4]
535
 
                            elif Intersect == 0: # No intersection?? Place it.
536
 
                                # Progress
537
 
                                removedCount +=1
538
 
#XXX                                                            Window.DrawProgressBar(0.0, 'Merged: %i islands, Ctrl to finish early.' % removedCount)
539
 
 
540
 
                                # Move faces into new island and offset
541
 
                                targetIsland[0].extend(sourceIsland[0])
542
 
                                offset= Vector((boxLeft, boxBottom))
543
 
 
544
 
                                for f in sourceIsland[0]:
545
 
                                    for uv in f.uv:
546
 
                                        uv+= offset
547
 
 
548
 
                                sourceIsland[0][:] = [] # Empty
549
 
 
550
 
 
551
 
                                # Move edge loop into new and offset.
552
 
                                # targetIsland[6].extend(sourceIsland[6])
553
 
                                #while sourceIsland[6]:
554
 
                                targetIsland[6].extend( [ (\
555
 
                                     (e[0]+offset, e[1]+offset, e[2])\
556
 
                                ) for e in sourceIsland[6] ] )
557
 
 
558
 
                                sourceIsland[6][:] = [] # Empty
559
 
 
560
 
                                # Sort by edge length, reverse so biggest are first.
561
 
 
562
 
                                try:     targetIsland[6].sort(key = lambda A: A[2])
563
 
                                except: targetIsland[6].sort(lambda B,A: cmp(A[2], B[2] ))
564
 
 
565
 
 
566
 
                                targetIsland[7].extend(sourceIsland[7])
567
 
                                offset= Vector((boxLeft, boxBottom, 0.0))
568
 
                                for p in sourceIsland[7]:
569
 
                                    p+= offset
570
 
 
571
 
                                sourceIsland[7][:] = []
572
 
 
573
 
 
574
 
                                # Decrement the efficiency
575
 
                                targetIsland[1]+=sourceIsland[1] # Increment totFaceArea
576
 
                                targetIsland[2]-=sourceIsland[1] # Decrement efficiency
577
 
                                # IF we ever used these again, should set to 0, eg
578
 
                                sourceIsland[2] = 0 # No area if anyone wants to know
579
 
 
580
 
                                break
581
 
 
582
 
 
583
 
                            # INCREMENR NEXT LOCATION
584
 
                            if boxLeft > testWidth:
585
 
                                boxBottom += yIncrement
586
 
                                boxLeft = 0.0
587
 
                            else:
588
 
                                boxLeft += xIncrement
589
 
                        ##print testcount
590
 
 
591
 
                efficIslandIdx+=1
592
 
        areaIslandIdx+=1
593
 
 
594
 
    # Remove empty islands
595
 
    i = len(islandList)
596
 
    while i:
597
 
        i-=1
598
 
        if not islandList[i]:
599
 
            del islandList[i] # Can increment islands removed here.
600
 
 
601
 
# Takes groups of faces. assumes face groups are UV groups.
602
 
def getUvIslands(faceGroups, me):
603
 
 
604
 
    # Get seams so we dont cross over seams
605
 
    edge_seams = {} # shoudl be a set
606
 
    for ed in me.edges:
607
 
        if ed.seam:
608
 
            edge_seams[ed.key] = None # dummy var- use sets!
609
 
    # Done finding seams
610
 
 
611
 
 
612
 
    islandList = []
613
 
 
614
 
#XXX    Window.DrawProgressBar(0.0, 'Splitting %d projection groups into UV islands:' % len(faceGroups))
615
 
    #print '\tSplitting %d projection groups into UV islands:' % len(faceGroups),
616
 
    # Find grouped faces
617
 
 
618
 
    faceGroupIdx = len(faceGroups)
619
 
 
620
 
    while faceGroupIdx:
621
 
        faceGroupIdx-=1
622
 
        faces = faceGroups[faceGroupIdx]
623
 
 
624
 
        if not faces:
625
 
            continue
626
 
 
627
 
        # Build edge dict
628
 
        edge_users = {}
629
 
 
630
 
        for i, f in enumerate(faces):
631
 
            for ed_key in f.edge_keys:
632
 
                if ed_key in edge_seams: # DELIMIT SEAMS! ;)
633
 
                    edge_users[ed_key] = [] # so as not to raise an error
634
 
                else:
635
 
                    try:                edge_users[ed_key].append(i)
636
 
                    except:             edge_users[ed_key] = [i]
637
 
 
638
 
        # Modes
639
 
        # 0 - face not yet touched.
640
 
        # 1 - added to island list, and need to search
641
 
        # 2 - touched and searched - dont touch again.
642
 
        face_modes = [0] * len(faces) # initialize zero - untested.
643
 
 
644
 
        face_modes[0] = 1 # start the search with face 1
645
 
 
646
 
        newIsland = []
647
 
 
648
 
        newIsland.append(faces[0])
649
 
 
650
 
 
651
 
        ok = True
652
 
        while ok:
653
 
 
654
 
            ok = True
655
 
            while ok:
656
 
                ok= False
657
 
                for i in range(len(faces)):
658
 
                    if face_modes[i] == 1: # search
659
 
                        for ed_key in faces[i].edge_keys:
660
 
                            for ii in edge_users[ed_key]:
661
 
                                if i != ii and face_modes[ii] == 0:
662
 
                                    face_modes[ii] = ok = 1 # mark as searched
663
 
                                    newIsland.append(faces[ii])
664
 
 
665
 
                        # mark as searched, dont look again.
666
 
                        face_modes[i] = 2
667
 
 
668
 
            islandList.append(newIsland)
669
 
 
670
 
            ok = False
671
 
            for i in range(len(faces)):
672
 
                if face_modes[i] == 0:
673
 
                    newIsland = []
674
 
                    newIsland.append(faces[i])
675
 
 
676
 
                    face_modes[i] = ok = 1
677
 
                    break
678
 
            # if not ok will stop looping
679
 
 
680
 
#XXX    Window.DrawProgressBar(0.1, 'Optimizing Rotation for %i UV Islands' % len(islandList))
681
 
 
682
 
    for island in islandList:
683
 
        optiRotateUvIsland(island)
684
 
 
685
 
    return islandList
686
 
 
687
 
 
688
 
def packIslands(islandList):
689
 
    if USER_FILL_HOLES:
690
 
#XXX            Window.DrawProgressBar(0.1, 'Merging Islands (Ctrl: skip merge)...')
691
 
        mergeUvIslands(islandList) # Modify in place
692
 
 
693
 
 
694
 
    # Now we have UV islands, we need to pack them.
695
 
 
696
 
    # Make a synchronised list with the islands
697
 
    # so we can box pak the islands.
698
 
    packBoxes = []
699
 
 
700
 
    # Keep a list of X/Y offset so we can save time by writing the
701
 
    # uv's and packed data in one pass.
702
 
    islandOffsetList = []
703
 
 
704
 
    islandIdx = 0
705
 
 
706
 
    while islandIdx < len(islandList):
707
 
        minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
708
 
 
709
 
        w, h = maxx-minx, maxy-miny
710
 
 
711
 
        if USER_ISLAND_MARGIN:
712
 
            minx -= USER_ISLAND_MARGIN# *w
713
 
            miny -= USER_ISLAND_MARGIN# *h
714
 
            maxx += USER_ISLAND_MARGIN# *w
715
 
            maxy += USER_ISLAND_MARGIN# *h
716
 
 
717
 
            # recalc width and height
718
 
            w, h = maxx-minx, maxy-miny
719
 
 
720
 
        if w < 0.00001 or h < 0.00001:
721
 
            del islandList[islandIdx]
722
 
            islandIdx -=1
723
 
            continue
724
 
 
725
 
        '''Save the offset to be applied later,
726
 
        we could apply to the UVs now and allign them to the bottom left hand area
727
 
        of the UV coords like the box packer imagines they are
728
 
        but, its quicker just to remember their offset and
729
 
        apply the packing and offset in 1 pass '''
730
 
        islandOffsetList.append((minx, miny))
731
 
 
732
 
        # Add to boxList. use the island idx for the BOX id.
733
 
        packBoxes.append([0, 0, w, h])
734
 
        islandIdx+=1
735
 
 
736
 
    # Now we have a list of boxes to pack that syncs
737
 
    # with the islands.
738
 
 
739
 
    #print '\tPacking UV Islands...'
740
 
#XXX    Window.DrawProgressBar(0.7, 'Packing %i UV Islands...' % len(packBoxes) )
741
 
 
742
 
    time1 = time.time()
743
 
    packWidth, packHeight = geometry.BoxPack2D(packBoxes)
744
 
 
745
 
    # print 'Box Packing Time:', time.time() - time1
746
 
 
747
 
    #if len(pa  ckedLs) != len(islandList):
748
 
    #   raise "Error packed boxes differes from original length"
749
 
 
750
 
    #print '\tWriting Packed Data to faces'
751
 
#XXX    Window.DrawProgressBar(0.8, 'Writing Packed Data to faces')
752
 
 
753
 
    # Sort by ID, so there in sync again
754
 
    islandIdx = len(islandList)
755
 
    # Having these here avoids devide by 0
756
 
    if islandIdx:
757
 
 
758
 
        if USER_STRETCH_ASPECT:
759
 
            # Maximize to uv area?? Will write a normalize function.
760
 
            xfactor = 1.0 / packWidth
761
 
            yfactor = 1.0 / packHeight
762
 
        else:
763
 
            # Keep proportions.
764
 
            xfactor = yfactor = 1.0 / max(packWidth, packHeight)
765
 
 
766
 
    while islandIdx:
767
 
        islandIdx -=1
768
 
        # Write the packed values to the UV's
769
 
 
770
 
        xoffset = packBoxes[islandIdx][0] - islandOffsetList[islandIdx][0]
771
 
        yoffset = packBoxes[islandIdx][1] - islandOffsetList[islandIdx][1]
772
 
 
773
 
        for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box
774
 
            for uv in f.uv:
775
 
                uv.x= (uv.x+xoffset) * xfactor
776
 
                uv.y= (uv.y+yoffset) * yfactor
777
 
 
778
 
 
779
 
 
780
 
def VectoMat(vec):
781
 
    a3 = vec.__copy__().normalize()
782
 
 
783
 
    up = Vector((0.0, 0.0, 1.0))
784
 
    if abs(a3.dot(up)) == 1.0:
785
 
        up = Vector((0.0, 1.0, 0.0))
786
 
 
787
 
    a1 = a3.cross(up).normalize()
788
 
    a2 = a3.cross(a1)
789
 
    return Matrix([a1[0], a1[1], a1[2]], [a2[0], a2[1], a2[2]], [a3[0], a3[1], a3[2]])
790
 
 
791
 
 
792
 
class thickface(object):
793
 
    __slost__= 'v', 'uv', 'no', 'area', 'edge_keys'
794
 
    def __init__(self, face, uvface, mesh_verts):
795
 
        self.v = [mesh_verts[i] for i in face.verts]
796
 
        if len(self.v)==4:
797
 
            self.uv = uvface.uv1, uvface.uv2, uvface.uv3, uvface.uv4
798
 
        else:
799
 
            self.uv = uvface.uv1, uvface.uv2, uvface.uv3
800
 
 
801
 
        self.no = face.normal
802
 
        self.area = face.area
803
 
        self.edge_keys = face.edge_keys
804
 
 
805
 
global ob
806
 
ob = None
807
 
def main(context, island_margin, projection_limit):
808
 
    global USER_FILL_HOLES
809
 
    global USER_FILL_HOLES_QUALITY
810
 
    global USER_STRETCH_ASPECT
811
 
    global USER_ISLAND_MARGIN
812
 
 
813
 
#XXX objects= bpy.data.scenes.active.objects
814
 
    objects = context.selected_editable_objects
815
 
 
816
 
 
817
 
    # we can will tag them later.
818
 
    obList =  [ob for ob in objects if ob.type == 'MESH']
819
 
 
820
 
    # Face select object may not be selected.
821
 
#XXX    ob = objects.active
822
 
    ob= objects[0]
823
 
 
824
 
    if ob and (not ob.select) and ob.type == 'MESH':
825
 
        # Add to the list
826
 
        obList =[ob]
827
 
    del objects
828
 
 
829
 
    if not obList:
830
 
        raise('error, no selected mesh objects')
831
 
 
832
 
    # Create the variables.
833
 
    USER_PROJECTION_LIMIT = projection_limit
834
 
    USER_ONLY_SELECTED_FACES = (1)
835
 
    USER_SHARE_SPACE = (1) # Only for hole filling.
836
 
    USER_STRETCH_ASPECT = (1) # Only for hole filling.
837
 
    USER_ISLAND_MARGIN = island_margin # Only for hole filling.
838
 
    USER_FILL_HOLES = (0)
839
 
    USER_FILL_HOLES_QUALITY = (50) # Only for hole filling.
840
 
    USER_VIEW_INIT = (0) # Only for hole filling.
841
 
    USER_AREA_WEIGHT = (1) # Only for hole filling.
842
 
 
843
 
    # Reuse variable
844
 
    if len(obList) == 1:
845
 
        ob = "Unwrap %i Selected Mesh"
846
 
    else:
847
 
        ob = "Unwrap %i Selected Meshes"
848
 
 
849
 
    # HACK, loop until mouse is lifted.
850
 
    '''
851
 
    while Window.GetMouseButtons() != 0:
852
 
        time.sleep(10)
853
 
    '''
854
 
 
855
 
#XXX    if not Draw.PupBlock(ob % len(obList), pup_block):
856
 
#XXX            return
857
 
#XXX    del ob
858
 
 
859
 
    # Convert from being button types
860
 
 
861
 
    USER_PROJECTION_LIMIT_CONVERTED = cos(USER_PROJECTION_LIMIT * DEG_TO_RAD)
862
 
    USER_PROJECTION_LIMIT_HALF_CONVERTED = cos((USER_PROJECTION_LIMIT/2) * DEG_TO_RAD)
863
 
 
864
 
 
865
 
    # Toggle Edit mode
866
 
    is_editmode = (context.active_object.mode == 'EDIT')
867
 
    if is_editmode:
868
 
        bpy.ops.object.mode_set(mode='OBJECT')
869
 
    # Assume face select mode! an annoying hack to toggle face select mode because Mesh dosent like faceSelectMode.
870
 
 
871
 
    if USER_SHARE_SPACE:
872
 
        # Sort by data name so we get consistant results
873
 
        obList.sort(key = lambda ob: ob.data.name)
874
 
        collected_islandList= []
875
 
 
876
 
#XXX    Window.WaitCursor(1)
877
 
 
878
 
    time1 = time.time()
879
 
 
880
 
    # Tag as False se we dont operate on teh same mesh twice.
881
 
#XXX    bpy.data.meshes.tag = False
882
 
    for me in bpy.data.meshes:
883
 
        me.tag = False
884
 
 
885
 
 
886
 
    for ob in obList:
887
 
        me = ob.data
888
 
 
889
 
        if me.tag or me.library:
890
 
            continue
891
 
 
892
 
        # Tag as used
893
 
        me.tag = True
894
 
 
895
 
        if len(me.uv_textures)==0: # Mesh has no UV Coords, dont bother.
896
 
            me.add_uv_texture()
897
 
 
898
 
        uv_layer = me.active_uv_texture.data
899
 
        me_verts = list(me.verts)
900
 
 
901
 
        if USER_ONLY_SELECTED_FACES:
902
 
            meshFaces = [thickface(f, uv_layer[i], me_verts) for i, f in enumerate(me.faces) if f.select]
903
 
        #else:
904
 
        #       meshFaces = map(thickface, me.faces)
905
 
 
906
 
        if not meshFaces:
907
 
            continue
908
 
 
909
 
#XXX            Window.DrawProgressBar(0.1, 'SmartProj UV Unwrapper, mapping "%s", %i faces.' % (me.name, len(meshFaces)))
910
 
 
911
 
        # =======
912
 
        # Generate a projection list from face normals, this is ment to be smart :)
913
 
 
914
 
        # make a list of face props that are in sync with meshFaces
915
 
        # Make a Face List that is sorted by area.
916
 
        # meshFaces = []
917
 
 
918
 
        # meshFaces.sort( lambda a, b: cmp(b.area , a.area) ) # Biggest first.
919
 
        meshFaces.sort( key = lambda a: -a.area )
920
 
 
921
 
        # remove all zero area faces
922
 
        while meshFaces and meshFaces[-1].area <= SMALL_NUM:
923
 
            # Set their UV's to 0,0
924
 
            for uv in meshFaces[-1].uv:
925
 
                uv.zero()
926
 
            meshFaces.pop()
927
 
 
928
 
        # Smallest first is slightly more efficient, but if the user cancels early then its better we work on the larger data.
929
 
 
930
 
        # Generate Projection Vecs
931
 
        # 0d is   1.0
932
 
        # 180 IS -0.59846
933
 
 
934
 
 
935
 
        # Initialize projectVecs
936
 
        if USER_VIEW_INIT:
937
 
            # Generate Projection
938
 
            projectVecs = [Vector(Window.GetViewVector()) * ob.matrix_world.copy().invert().rotation_part()] # We add to this allong the way
939
 
        else:
940
 
            projectVecs = []
941
 
 
942
 
        newProjectVec = meshFaces[0].no
943
 
        newProjectMeshFaces = []        # Popping stuffs it up.
944
 
 
945
 
 
946
 
        # Predent that the most unique angke is ages away to start the loop off
947
 
        mostUniqueAngle = -1.0
948
 
 
949
 
        # This is popped
950
 
        tempMeshFaces = meshFaces[:]
951
 
 
952
 
 
953
 
 
954
 
        # This while only gathers projection vecs, faces are assigned later on.
955
 
        while 1:
956
 
            # If theres none there then start with the largest face
957
 
 
958
 
            # add all the faces that are close.
959
 
            for fIdx in range(len(tempMeshFaces)-1, -1, -1):
960
 
                # Use half the angle limit so we dont overweight faces towards this
961
 
                # normal and hog all the faces.
962
 
                if newProjectVec.dot(tempMeshFaces[fIdx].no) > USER_PROJECTION_LIMIT_HALF_CONVERTED:
963
 
                    newProjectMeshFaces.append(tempMeshFaces.pop(fIdx))
964
 
 
965
 
            # Add the average of all these faces normals as a projectionVec
966
 
            averageVec = Vector((0.0, 0.0, 0.0))
967
 
            if USER_AREA_WEIGHT:
968
 
                for fprop in newProjectMeshFaces:
969
 
                    averageVec += (fprop.no * fprop.area)
970
 
            else:
971
 
                for fprop in newProjectMeshFaces:
972
 
                    averageVec += fprop.no
973
 
 
974
 
            if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0: # Avoid NAN
975
 
                projectVecs.append(averageVec.normalize())
976
 
 
977
 
 
978
 
            # Get the next vec!
979
 
            # Pick the face thats most different to all existing angles :)
980
 
            mostUniqueAngle = 1.0 # 1.0 is 0d. no difference.
981
 
            mostUniqueIndex = 0 # dummy
982
 
 
983
 
            for fIdx in range(len(tempMeshFaces)-1, -1, -1):
984
 
                angleDifference = -1.0 # 180d difference.
985
 
 
986
 
                # Get the closest vec angle we are to.
987
 
                for p in projectVecs:
988
 
                    temp_angle_diff= p.dot(tempMeshFaces[fIdx].no)
989
 
 
990
 
                    if angleDifference < temp_angle_diff:
991
 
                        angleDifference= temp_angle_diff
992
 
 
993
 
                if angleDifference < mostUniqueAngle:
994
 
                    # We have a new most different angle
995
 
                    mostUniqueIndex = fIdx
996
 
                    mostUniqueAngle = angleDifference
997
 
 
998
 
            if mostUniqueAngle < USER_PROJECTION_LIMIT_CONVERTED:
999
 
                #print 'adding', mostUniqueAngle, USER_PROJECTION_LIMIT, len(newProjectMeshFaces)
1000
 
                # Now weight the vector to all its faces, will give a more direct projection
1001
 
                # if the face its self was not representive of the normal from surrounding faces.
1002
 
 
1003
 
                newProjectVec = tempMeshFaces[mostUniqueIndex].no
1004
 
                newProjectMeshFaces = [tempMeshFaces.pop(mostUniqueIndex)]
1005
 
 
1006
 
 
1007
 
            else:
1008
 
                if len(projectVecs) >= 1: # Must have at least 2 projections
1009
 
                    break
1010
 
 
1011
 
 
1012
 
        # If there are only zero area faces then its possible
1013
 
        # there are no projectionVecs
1014
 
        if not len(projectVecs):
1015
 
            Draw.PupMenu('error, no projection vecs where generated, 0 area faces can cause this.')
1016
 
            return
1017
 
 
1018
 
        faceProjectionGroupList =[[] for i in range(len(projectVecs)) ]
1019
 
 
1020
 
        # MAP and Arrange # We know there are 3 or 4 faces here
1021
 
 
1022
 
        for fIdx in range(len(meshFaces)-1, -1, -1):
1023
 
            fvec = meshFaces[fIdx].no
1024
 
            i = len(projectVecs)
1025
 
 
1026
 
            # Initialize first
1027
 
            bestAng = fvec.dot(projectVecs[0])
1028
 
            bestAngIdx = 0
1029
 
 
1030
 
            # Cycle through the remaining, first already done
1031
 
            while i-1:
1032
 
                i-=1
1033
 
 
1034
 
                newAng = fvec.dot(projectVecs[i])
1035
 
                if newAng > bestAng: # Reverse logic for dotvecs
1036
 
                    bestAng = newAng
1037
 
                    bestAngIdx = i
1038
 
 
1039
 
            # Store the area for later use.
1040
 
            faceProjectionGroupList[bestAngIdx].append(meshFaces[fIdx])
1041
 
 
1042
 
        # Cull faceProjectionGroupList,
1043
 
 
1044
 
 
1045
 
        # Now faceProjectionGroupList is full of faces that face match the project Vecs list
1046
 
        for i in range(len(projectVecs)):
1047
 
            # Account for projectVecs having no faces.
1048
 
            if not faceProjectionGroupList[i]:
1049
 
                continue
1050
 
 
1051
 
            # Make a projection matrix from a unit length vector.
1052
 
            MatProj = VectoMat(projectVecs[i])
1053
 
 
1054
 
            # Get the faces UV's from the projected vertex.
1055
 
            for f in faceProjectionGroupList[i]:
1056
 
                f_uv = f.uv
1057
 
                for j, v in enumerate(f.v):
1058
 
                    # XXX - note, between mathutils in 2.4 and 2.5 the order changed.
1059
 
                    f_uv[j][:] = (v.co * MatProj)[:2]
1060
 
 
1061
 
 
1062
 
        if USER_SHARE_SPACE:
1063
 
            # Should we collect and pack later?
1064
 
            islandList = getUvIslands(faceProjectionGroupList, me)
1065
 
            collected_islandList.extend(islandList)
1066
 
 
1067
 
        else:
1068
 
            # Should we pack the islands for this 1 object?
1069
 
            islandList = getUvIslands(faceProjectionGroupList, me)
1070
 
            packIslands(islandList)
1071
 
 
1072
 
 
1073
 
        # update the mesh here if we need to.
1074
 
 
1075
 
    # We want to pack all in 1 go, so pack now
1076
 
    if USER_SHARE_SPACE:
1077
 
#XXX            Window.DrawProgressBar(0.9, "Box Packing for all objects...")
1078
 
        packIslands(collected_islandList)
1079
 
 
1080
 
    print("Smart Projection time: %.2f" % (time.time() - time1))
1081
 
    # Window.DrawProgressBar(0.9, "Smart Projections done, time: %.2f sec." % (time.time() - time1))
1082
 
 
1083
 
    if is_editmode:
1084
 
        bpy.ops.object.mode_set(mode='EDIT')
1085
 
 
1086
 
#XXX    Window.DrawProgressBar(1.0, "")
1087
 
#XXX    Window.WaitCursor(0)
1088
 
#XXX    Window.RedrawAll()
1089
 
 
1090
 
"""
1091
 
    pup_block = [\
1092
 
    'Projection',\
1093
 
*       ('Angle Limit:', USER_PROJECTION_LIMIT, 1, 89, ''),\
1094
 
    ('Selected Faces Only', USER_ONLY_SELECTED_FACES, 'Use only selected faces from all selected meshes.'),\
1095
 
    ('Init from view', USER_VIEW_INIT, 'The first projection will be from the view vector.'),\
1096
 
    ('Area Weight', USER_AREA_WEIGHT, 'Weight projections vector by face area.'),\
1097
 
    '',\
1098
 
    '',\
1099
 
    '',\
1100
 
    'UV Layout',\
1101
 
    ('Share Tex Space', USER_SHARE_SPACE, 'Objects Share texture space, map all objects into 1 uvmap.'),\
1102
 
    ('Stretch to bounds', USER_STRETCH_ASPECT, 'Stretch the final output to texture bounds.'),\
1103
 
*       ('Island Margin:', USER_ISLAND_MARGIN, 0.0, 0.5, ''),\
1104
 
    'Fill in empty areas',\
1105
 
    ('Fill Holes', USER_FILL_HOLES, 'Fill in empty areas reduced texture waistage (slow).'),\
1106
 
    ('Fill Quality:', USER_FILL_HOLES_QUALITY, 1, 100, 'Depends on fill holes, how tightly to fill UV holes, (higher is slower)'),\
1107
 
    ]
1108
 
"""
1109
 
 
1110
 
from bpy.props import *
1111
 
 
1112
 
 
1113
 
class SmartProject(bpy.types.Operator):
1114
 
    '''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.'''
1115
 
    bl_idname = "uv.smart_project"
1116
 
    bl_label = "Smart UV Project"
1117
 
    bl_options = {'REGISTER', 'UNDO'}
1118
 
 
1119
 
    angle_limit = FloatProperty(name="Angle Limit",
1120
 
            description="lower for more projection groups, higher for less distortion.",
1121
 
            default=66.0, min=1.0, max=89.0)
1122
 
 
1123
 
    island_margin = FloatProperty(name="Island Margin",
1124
 
            description="Margin to reduce bleed from adjacent islands.",
1125
 
            default=0.0, min=0.0, max=1.0)
1126
 
 
1127
 
    def poll(self, context):
1128
 
        return context.active_object != None
1129
 
 
1130
 
    def execute(self, context):
1131
 
        main(context, self.properties.island_margin, self.properties.angle_limit)
1132
 
        return {'FINISHED'}
1133
 
 
1134
 
 
1135
 
# Add to a menu
1136
 
menu_func = (lambda self, context: self.layout.operator(SmartProject.bl_idname,
1137
 
                                        text="Smart Project"))
1138
 
 
1139
 
 
1140
 
def register():
1141
 
    bpy.types.register(SmartProject)
1142
 
    bpy.types.VIEW3D_MT_uv_map.append(menu_func)
1143
 
 
1144
 
 
1145
 
def unregister():
1146
 
    bpy.types.unregister(SmartProject)
1147
 
    bpy.types.VIEW3D_MT_uv_map.remove(menu_func)
1148
 
 
1149
 
if __name__ == "__main__":
1150
 
    register()