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:
-rw-r--r--release/scripts/op/uvcalc_lightmap.py1091
1 files changed, 539 insertions, 552 deletions
diff --git a/release/scripts/op/uvcalc_lightmap.py b/release/scripts/op/uvcalc_lightmap.py
index 1433ccbd13a..09cd141442c 100644
--- a/release/scripts/op/uvcalc_lightmap.py
+++ b/release/scripts/op/uvcalc_lightmap.py
@@ -34,566 +34,553 @@ __bpydoc__ = """\
# --------------------------------------------------------------------------
-from Blender import *
+# from Blender import *
import bpy
-import BPyMesh
-# reload(BPyMesh)
from math import sqrt
-def AngleBetweenVecs(a1,a2):
- try:
- return Mathutils.AngleBetweenVecs(a1,a2)
- except:
- return 180.0
-# python 2.3 has no reversed() iterator. this will only work on lists and tuples
-try:
- reversed
-except:
- def reversed(l): return l[::-1]
+def AngleBetweenVecs(a1, a2):
+ try:
+ return Mathutils.AngleBetweenVecs(a1, a2)
+ except:
+ return 180.0
+
class prettyface(object):
- __slots__ = 'uv', 'width', 'height', 'children', 'xoff', 'yoff', 'has_parent', 'rot'
- def __init__(self, data):
-
- self.has_parent = False
- self.rot = False # only used for triables
- self.xoff = 0
- self.yoff = 0
-
- if type(data) == list: # list of data
- self.uv = None
-
- # join the data
- if len(data) == 2:
- # 2 vertical blocks
- data[1].xoff = data[0].width
- self.width = data[0].width * 2
- self.height = data[0].height
-
- elif len(data) == 4:
- # 4 blocks all the same size
- d = data[0].width # dimension x/y are the same
-
- data[1].xoff += d
- data[2].yoff += d
-
- data[3].xoff += d
- data[3].yoff += d
-
- self.width = self.height = d*2
-
- #else:
- # print len(data), data
- # raise "Error"
-
- for pf in data:
- pf.has_parent = True
-
-
- self.children = data
-
- elif type(data) == tuple:
- # 2 blender faces
- # f, (len_min, len_mid, len_max)
- self.uv = data
-
- f1, lens1, lens1ord = data[0]
- if data[1]:
- f2, lens2, lens2ord = data[1]
- self.width = (lens1[lens1ord[0]] + lens2[lens2ord[0]])/2
- self.height = (lens1[lens1ord[1]] + lens2[lens2ord[1]])/2
- else: # 1 tri :/
- self.width = lens1[0]
- self.height = lens1[1]
-
- self.children = []
-
-
- else: # blender face
- self.uv = data.uv
-
- cos = [v.co for v in data]
- self.width = ((cos[0]-cos[1]).length + (cos[2]-cos[3]).length)/2
- self.height = ((cos[1]-cos[2]).length + (cos[0]-cos[3]).length)/2
-
- self.children = []
-
-
- def spin(self):
- if self.uv and len(self.uv) == 4:
- self.uv = self.uv[1], self.uv[2], self.uv[3], self.uv[0]
-
- self.width, self.height = self.height, self.width
- self.xoff, self.yoff = self.yoff, self.xoff # not needed?
- self.rot = not self.rot # only for tri pairs.
- # print 'spinning'
- for pf in self.children:
- pf.spin()
-
-
- def place(self, xoff, yoff, xfac, yfac, margin_w, margin_h):
-
- xoff += self.xoff
- yoff += self.yoff
-
- for pf in self.children:
- pf.place(xoff, yoff, xfac, yfac, margin_w, margin_h)
-
- uv = self.uv
- if not uv:
- return
-
- x1 = xoff
- y1 = yoff
- x2 = xoff + self.width
- y2 = yoff + self.height
-
- # Scale the values
- x1 = x1/xfac + margin_w
- x2 = x2/xfac - margin_w
- y1 = y1/yfac + margin_h
- y2 = y2/yfac - margin_h
-
- # 2 Tri pairs
- if len(uv) == 2:
- # match the order of angle sizes of the 3d verts with the UV angles and rotate.
- def get_tri_angles(v1,v2,v3):
- a1= AngleBetweenVecs(v2-v1,v3-v1)
- a2= AngleBetweenVecs(v1-v2,v3-v2)
- a3 = 180 - (a1+a2) #a3= AngleBetweenVecs(v2-v3,v1-v3)
-
-
- return [(a1,0),(a2,1),(a3,2)]
-
- def set_uv(f, p1, p2, p3):
-
- # cos =
- #v1 = cos[0]-cos[1]
- #v2 = cos[1]-cos[2]
- #v3 = cos[2]-cos[0]
- angles_co = get_tri_angles(*[v.co for v in f])
- angles_co.sort()
- I = [i for a,i in angles_co]
-
- fuv = f.uv
- if self.rot:
- fuv[I[2]][:] = p1
- fuv[I[1]][:] = p2
- fuv[I[0]][:] = p3
- else:
- fuv[I[2]][:] = p1
- fuv[I[0]][:] = p2
- fuv[I[1]][:] = p3
-
- f, lens, lensord = uv[0]
-
- set_uv(f, (x1,y1), (x1, y2-margin_h), (x2-margin_w, y1))
-
- if uv[1]:
- f, lens, lensord = uv[1]
- set_uv(f, (x2,y2), (x2, y1+margin_h), (x1+margin_w, y2))
-
- else: # 1 QUAD
- uv[1][:] = x1,y1
- uv[2][:] = x1,y2
- uv[3][:] = x2,y2
- uv[0][:] = x2,y1
-
- def __hash__(self):
- # None unique hash
- return self.width, self.height
-
-
-def lightmap_uvpack( meshes,\
-PREF_SEL_ONLY= True,\
-PREF_NEW_UVLAYER= False,\
-PREF_PACK_IN_ONE= False,\
-PREF_APPLY_IMAGE= False,\
-PREF_IMG_PX_SIZE= 512,\
-PREF_BOX_DIV= 8,\
-PREF_MARGIN_DIV= 512):
- '''
- BOX_DIV if the maximum division of the UV map that
- a box may be consolidated into.
- Basicly, a lower value will be slower but waist less space
- and a higher value will have more clumpy boxes but more waisted space
- '''
-
- if not meshes:
- return
-
- t = sys.time()
-
- if PREF_PACK_IN_ONE:
- if PREF_APPLY_IMAGE:
- image = Image.New('lightmap', PREF_IMG_PX_SIZE, PREF_IMG_PX_SIZE, 24)
- face_groups = [[]]
- else:
- face_groups = []
-
- for me in meshes:
- # Add face UV if it does not exist.
- # All new faces are selected.
- me.faceUV = True
-
- if PREF_SEL_ONLY:
- faces = [f for f in me.faces if f.sel]
- else:
- faces = list(me.faces)
-
- if PREF_PACK_IN_ONE:
- face_groups[0].extend(faces)
- else:
- face_groups.append(faces)
-
- if PREF_NEW_UVLAYER:
- uvname_org = uvname = 'lightmap'
- uvnames = me.getUVLayerNames()
- i = 1
- while uvname in uvnames:
- uvname = '%s.%03d' % (uvname_org, i)
- i+=1
-
- me.addUVLayer(uvname)
- me.activeUVLayer = uvname
-
- del uvnames, uvname_org, uvname
-
- for face_sel in face_groups:
- print "\nStarting unwrap"
-
- if len(face_sel) <4:
- print '\tWarning, less then 4 faces, skipping'
- continue
-
- pretty_faces = [prettyface(f) for f in face_sel if len(f) == 4]
-
-
- # Do we have any tri's
- if len(pretty_faces) != len(face_sel):
-
- # Now add tri's, not so simple because we need to pair them up.
- def trylens(f):
- # f must be a tri
- cos = [v.co for v in f]
- lens = [(cos[0] - cos[1]).length, (cos[1] - cos[2]).length, (cos[2] - cos[0]).length]
-
- lens_min = lens.index(min(lens))
- lens_max = lens.index(max(lens))
- for i in xrange(3):
- if i != lens_min and i!= lens_max:
- lens_mid = i
- break
- lens_order = lens_min, lens_mid, lens_max
-
- return f, lens, lens_order
-
- tri_lengths = [trylens(f) for f in face_sel if len(f) == 3]
- del trylens
-
- def trilensdiff(t1,t2):
- return\
- abs(t1[1][t1[2][0]]-t2[1][t2[2][0]])+\
- abs(t1[1][t1[2][1]]-t2[1][t2[2][1]])+\
- abs(t1[1][t1[2][2]]-t2[1][t2[2][2]])
-
- while tri_lengths:
- tri1 = tri_lengths.pop()
-
- if not tri_lengths:
- pretty_faces.append(prettyface((tri1, None)))
- break
-
- best_tri_index = -1
- best_tri_diff = 100000000.0
-
- for i, tri2 in enumerate(tri_lengths):
- diff = trilensdiff(tri1, tri2)
- if diff < best_tri_diff:
- best_tri_index = i
- best_tri_diff = diff
-
- pretty_faces.append(prettyface((tri1, tri_lengths.pop(best_tri_index))))
-
-
- # Get the min, max and total areas
- max_area = 0.0
- min_area = 100000000.0
- tot_area = 0
- for f in face_sel:
- area = f.area
- if area > max_area: max_area = area
- if area < min_area: min_area = area
- tot_area += area
-
- max_len = sqrt(max_area)
- min_len = sqrt(min_area)
- side_len = sqrt(tot_area)
-
- # Build widths
-
- curr_len = max_len
-
- print '\tGenerating lengths...',
-
- lengths = []
- while curr_len > min_len:
- lengths.append(curr_len)
- curr_len = curr_len/2
-
- # Dont allow boxes smaller then the margin
- # since we contract on the margin, boxes that are smaller will create errors
- # print curr_len, side_len/MARGIN_DIV
- if curr_len/4 < side_len/PREF_MARGIN_DIV:
- break
-
- if not lengths:
- lengths.append(curr_len)
-
- # convert into ints
- lengths_to_ints = {}
-
- l_int = 1
- for l in reversed(lengths):
- lengths_to_ints[l] = l_int
- l_int*=2
-
- lengths_to_ints = lengths_to_ints.items()
- lengths_to_ints.sort()
- print 'done'
-
- # apply quantized values.
-
- for pf in pretty_faces:
- w = pf.width
- h = pf.height
- bestw_diff = 1000000000.0
- besth_diff = 1000000000.0
- new_w = 0.0
- new_h = 0.0
- for l, i in lengths_to_ints:
- d = abs(l - w)
- if d < bestw_diff:
- bestw_diff = d
- new_w = i # assign the int version
-
- d = abs(l - h)
- if d < besth_diff:
- besth_diff = d
- new_h = i # ditto
-
- pf.width = new_w
- pf.height = new_h
-
- if new_w > new_h:
- pf.spin()
-
- print '...done'
-
-
- # Since the boxes are sized in powers of 2, we can neatly group them into bigger squares
- # this is done hierarchily, so that we may avoid running the pack function
- # on many thousands of boxes, (under 1k is best) because it would get slow.
- # Using an off and even dict us usefull because they are packed differently
- # where w/h are the same, their packed in groups of 4
- # where they are different they are packed in pairs
- #
- # After this is done an external pack func is done that packs the whole group.
-
- print '\tConsolidating Boxes...',
- even_dict = {} # w/h are the same, the key is an int (w)
- odd_dict = {} # w/h are different, the key is the (w,h)
-
- for pf in pretty_faces:
- w,h = pf.width, pf.height
- if w==h: even_dict.setdefault(w, []).append( pf )
- else: odd_dict.setdefault((w,h), []).append( pf )
-
- # Count the number of boxes consolidated, only used for stats.
- c = 0
-
- # This is tricky. the total area of all packed boxes, then squt that to get an estimated size
- # this is used then converted into out INT space so we can compare it with
- # the ints assigned to the boxes size
- # and divided by BOX_DIV, basicly if BOX_DIV is 8
- # ...then the maximum box consolidataion (recursive grouping) will have a max width & height
- # ...1/8th of the UV size.
- # ...limiting this is needed or you end up with bug unused texture spaces
- # ...however if its too high, boxpacking is way too slow for high poly meshes.
- float_to_int_factor = lengths_to_ints[0][0]
- if float_to_int_factor > 0:
- max_int_dimension = int(((side_len / float_to_int_factor)) / PREF_BOX_DIV)
- ok = True
- else:
- max_int_dimension = 0.0 # wont be used
- ok = False
-
- # RECURSIVE prettyface grouping
- while ok:
- ok = False
-
- # Tall boxes in groups of 2
- for d, boxes in odd_dict.items():
- if d[1] < max_int_dimension:
- #\boxes.sort(key = lambda a: len(a.children))
- while len(boxes) >= 2:
- # print "foo", len(boxes)
- ok = True
- c += 1
- pf_parent = prettyface([boxes.pop(), boxes.pop()])
- pretty_faces.append(pf_parent)
-
- w,h = pf_parent.width, pf_parent.height
-
- if w>h: raise "error"
-
- if w==h:
- even_dict.setdefault(w, []).append(pf_parent)
- else:
- odd_dict.setdefault((w,h), []).append(pf_parent)
-
- # Even boxes in groups of 4
- for d, boxes in even_dict.items():
- if d < max_int_dimension:
- # py 2.3 compat
- try: boxes.sort(key = lambda a: len(a.children))
- except: boxes.sort(lambda a, b: cmp(len(a.children), len(b.children)))
-
- while len(boxes) >= 4:
- # print "bar", len(boxes)
- ok = True
- c += 1
-
- pf_parent = prettyface([boxes.pop(), boxes.pop(), boxes.pop(), boxes.pop()])
- pretty_faces.append(pf_parent)
- w = pf_parent.width # width and weight are the same
- even_dict.setdefault(w, []).append(pf_parent)
-
- del even_dict
- del odd_dict
-
- orig = len(pretty_faces)
-
- pretty_faces = [pf for pf in pretty_faces if not pf.has_parent]
-
- # spin every second prettyface
- # if there all vertical you get less efficiently used texture space
- i = len(pretty_faces)
- d = 0
- while i:
- i -=1
- pf = pretty_faces[i]
- if pf.width != pf.height:
- d += 1
- if d % 2: # only pack every second
- pf.spin()
- # pass
-
- print 'Consolidated', c, 'boxes, done'
- # print 'done', orig, len(pretty_faces)
-
-
- # boxes2Pack.append([islandIdx, w,h])
- print '\tPacking Boxes', len(pretty_faces), '...',
- boxes2Pack = [ [0.0, 0.0, pf.width, pf.height, i] for i, pf in enumerate(pretty_faces)]
- packWidth, packHeight = Geometry.BoxPack2D(boxes2Pack)
-
- # print packWidth, packHeight
-
- packWidth = float(packWidth)
- packHeight = float(packHeight)
-
- margin_w = ((packWidth) / PREF_MARGIN_DIV)/ packWidth
- margin_h = ((packHeight) / PREF_MARGIN_DIV) / packHeight
-
- # print margin_w, margin_h
- print 'done'
-
- # Apply the boxes back to the UV coords.
- print '\twriting back UVs',
- for i, box in enumerate(boxes2Pack):
- pretty_faces[i].place(box[0], box[1], packWidth, packHeight, margin_w, margin_h)
- # pf.place(box[1][1], box[1][2], packWidth, packHeight, margin_w, margin_h)
- print 'done'
-
-
- if PREF_APPLY_IMAGE:
- if not PREF_PACK_IN_ONE:
- image = Image.New('lightmap', PREF_IMG_PX_SIZE, PREF_IMG_PX_SIZE, 24)
-
- for f in face_sel:
- f.image = image
-
- for me in meshes:
- me.update()
-
- print 'finished all %.2f ' % (sys.time() - t)
-
- Window.RedrawAll()
+ __slots__ = "uv", "width", "height", "children", "xoff", "yoff", "has_parent", "rot"
+
+ def __init__(self, data):
+ self.has_parent = False
+ self.rot = False # only used for triables
+ self.xoff = 0
+ self.yoff = 0
+
+ if type(data) == list: # list of data
+ self.uv = None
+
+ # join the data
+ if len(data) == 2:
+ # 2 vertical blocks
+ data[1].xoff = data[0].width
+ self.width = data[0].width * 2
+ self.height = data[0].height
+
+ elif len(data) == 4:
+ # 4 blocks all the same size
+ d = data[0].width # dimension x/y are the same
+
+ data[1].xoff += d
+ data[2].yoff += d
+
+ data[3].xoff += d
+ data[3].yoff += d
+
+ self.width = self.height = d * 2
+
+ #else:
+ # print(len(data), data)
+ # raise "Error"
+
+ for pf in data:
+ pf.has_parent = True
+
+ self.children = data
+
+ elif type(data) == tuple:
+ # 2 blender faces
+ # f, (len_min, len_mid, len_max)
+ self.uv = data
+
+ f1, lens1, lens1ord = data[0]
+ if data[1]:
+ f2, lens2, lens2ord = data[1]
+ self.width = (lens1[lens1ord[0]] + lens2[lens2ord[0]]) / 2.0
+ self.height = (lens1[lens1ord[1]] + lens2[lens2ord[1]]) / 2.0
+ else: # 1 tri :/
+ self.width = lens1[0]
+ self.height = lens1[1]
+
+ self.children = []
+
+ else: # blender face
+ self.uv = data.uv
+
+ cos = [v.co for v in data]
+ self.width = ((cos[0] - cos[1]).length + (cos[2] - cos[3]).length) / 2.0
+ self.height = ((cos[1] - cos[2]).length + (cos[0] - cos[3]).length) / 2.0
+
+ self.children = []
+
+ def spin(self):
+ if self.uv and len(self.uv) == 4:
+ self.uv = self.uv[1], self.uv[2], self.uv[3], self.uv[0]
+
+ self.width, self.height = self.height, self.width
+ self.xoff, self.yoff = self.yoff, self.xoff # not needed?
+ self.rot = not self.rot # only for tri pairs.
+ # print("spinning")
+ for pf in self.children:
+ pf.spin()
+
+ def place(self, xoff, yoff, xfac, yfac, margin_w, margin_h):
+
+ xoff += self.xoff
+ yoff += self.yoff
+
+ for pf in self.children:
+ pf.place(xoff, yoff, xfac, yfac, margin_w, margin_h)
+
+ uv = self.uv
+ if not uv:
+ return
+
+ x1 = xoff
+ y1 = yoff
+ x2 = xoff + self.width
+ y2 = yoff + self.height
+
+ # Scale the values
+ x1 = x1 / xfac + margin_w
+ x2 = x2 / xfac - margin_w
+ y1 = y1 / yfac + margin_h
+ y2 = y2 / yfac - margin_h
+
+ # 2 Tri pairs
+ if len(uv) == 2:
+ # match the order of angle sizes of the 3d verts with the UV angles and rotate.
+ def get_tri_angles(v1, v2, v3):
+ a1 = AngleBetweenVecs(v2 - v1, v3 - v1)
+ a2 = AngleBetweenVecs(v1 - v2, v3 - v2)
+ a3 = 180.0 - (a1 + a2) # a3= AngleBetweenVecs(v2-v3,v1-v3)
+
+ return [(a1, 0), (a2, 1), (a3, 2)]
+
+ def set_uv(f, p1, p2, p3):
+
+ # cos =
+ #v1 = cos[0]-cos[1]
+ #v2 = cos[1]-cos[2]
+ #v3 = cos[2]-cos[0]
+ angles_co = get_tri_angles(*[v.co for v in f])
+ angles_co.sort()
+ I = [i for a, i in angles_co]
+
+ fuv = f.uv
+ if self.rot:
+ fuv[I[2]][:] = p1
+ fuv[I[1]][:] = p2
+ fuv[I[0]][:] = p3
+ else:
+ fuv[I[2]][:] = p1
+ fuv[I[0]][:] = p2
+ fuv[I[1]][:] = p3
+
+ f, lens, lensord = uv[0]
+
+ set_uv(f, (x1, y1), (x1, y2 - margin_h), (x2 - margin_w, y1))
+
+ if uv[1]:
+ f, lens, lensord = uv[1]
+ set_uv(f, (x2, y2), (x2, y1 + margin_h), (x1 + margin_w, y2))
+
+ else: # 1 QUAD
+ uv[1][:] = x1, y1
+ uv[2][:] = x1, y2
+ uv[3][:] = x2, y2
+ uv[0][:] = x2, y1
+
+ def __hash__(self):
+ # None unique hash
+ return self.width, self.height
+
+
+def lightmap_uvpack(meshes,
+ PREF_SEL_ONLY=True,
+ PREF_NEW_UVLAYER=False,
+ PREF_PACK_IN_ONE=False,
+ PREF_APPLY_IMAGE=False,
+ PREF_IMG_PX_SIZE=512,
+ PREF_BOX_DIV=8,
+ PREF_MARGIN_DIV=512
+ ):
+ '''
+ BOX_DIV if the maximum division of the UV map that
+ a box may be consolidated into.
+ Basicly, a lower value will be slower but waist less space
+ and a higher value will have more clumpy boxes but more waisted space
+ '''
+
+ if not meshes:
+ return
+
+ t = sys.time()
+
+ if PREF_PACK_IN_ONE:
+ if PREF_APPLY_IMAGE:
+ image = Image.New("lightmap", PREF_IMG_PX_SIZE, PREF_IMG_PX_SIZE, 24)
+ face_groups = [[]]
+ else:
+ face_groups = []
+
+ for me in meshes:
+ # Add face UV if it does not exist.
+ # All new faces are selected.
+ me.faceUV = True
+
+ if PREF_SEL_ONLY:
+ faces = [f for f in me.faces if f.sel]
+ else:
+ faces = list(me.faces)
+
+ if PREF_PACK_IN_ONE:
+ face_groups[0].extend(faces)
+ else:
+ face_groups.append(faces)
+
+ if PREF_NEW_UVLAYER:
+ uvname_org = uvname = "lightmap"
+ uvnames = me.getUVLayerNames()
+ i = 1
+ while uvname in uvnames:
+ uvname = "%s.%03d" % (uvname_org, i)
+ i += 1
+
+ me.addUVLayer(uvname)
+ me.activeUVLayer = uvname
+
+ del uvnames, uvname_org, uvname
+
+ for face_sel in face_groups:
+ print("\nStarting unwrap")
+
+ if len(face_sel) < 4:
+ print("\tWarning, less then 4 faces, skipping")
+ continue
+
+ pretty_faces = [prettyface(f) for f in face_sel if len(f) == 4]
+
+ # Do we have any tri's
+ if len(pretty_faces) != len(face_sel):
+
+ # Now add tri's, not so simple because we need to pair them up.
+ def trylens(f):
+ # f must be a tri
+ cos = [v.co for v in f]
+ lens = [(cos[0] - cos[1]).length, (cos[1] - cos[2]).length, (cos[2] - cos[0]).length]
+
+ lens_min = lens.index(min(lens))
+ lens_max = lens.index(max(lens))
+ for i in xrange(3):
+ if i != lens_min and i != lens_max:
+ lens_mid = i
+ break
+ lens_order = lens_min, lens_mid, lens_max
+
+ return f, lens, lens_order
+
+ tri_lengths = [trylens(f) for f in face_sel if len(f) == 3]
+ del trylens
+
+ def trilensdiff(t1, t2):
+ return\
+ abs(t1[1][t1[2][0]] - t2[1][t2[2][0]]) + \
+ abs(t1[1][t1[2][1]] - t2[1][t2[2][1]]) + \
+ abs(t1[1][t1[2][2]] - t2[1][t2[2][2]])
+
+ while tri_lengths:
+ tri1 = tri_lengths.pop()
+
+ if not tri_lengths:
+ pretty_faces.append(prettyface((tri1, None)))
+ break
+
+ best_tri_index = -1
+ best_tri_diff = 100000000.0
+
+ for i, tri2 in enumerate(tri_lengths):
+ diff = trilensdiff(tri1, tri2)
+ if diff < best_tri_diff:
+ best_tri_index = i
+ best_tri_diff = diff
+
+ pretty_faces.append(prettyface((tri1, tri_lengths.pop(best_tri_index))))
+
+ # Get the min, max and total areas
+ max_area = 0.0
+ min_area = 100000000.0
+ tot_area = 0
+ for f in face_sel:
+ area = f.area
+ if area > max_area:
+ max_area = area
+ if area < min_area:
+ min_area = area
+ tot_area += area
+
+ max_len = sqrt(max_area)
+ min_len = sqrt(min_area)
+ side_len = sqrt(tot_area)
+
+ # Build widths
+
+ curr_len = max_len
+
+ print("\tGenerating lengths...", end="")
+
+ lengths = []
+ while curr_len > min_len:
+ lengths.append(curr_len)
+ curr_len = curr_len / 2.0
+
+ # Dont allow boxes smaller then the margin
+ # since we contract on the margin, boxes that are smaller will create errors
+ # print(curr_len, side_len/MARGIN_DIV)
+ if curr_len / 4.0 < side_len / PREF_MARGIN_DIV:
+ break
+
+ if not lengths:
+ lengths.append(curr_len)
+
+ # convert into ints
+ lengths_to_ints = {}
+
+ l_int = 1
+ for l in reversed(lengths):
+ lengths_to_ints[l] = l_int
+ l_int *= 2
+
+ lengths_to_ints = lengths_to_ints.items()
+ lengths_to_ints.sort()
+ print("done")
+
+ # apply quantized values.
+
+ for pf in pretty_faces:
+ w = pf.width
+ h = pf.height
+ bestw_diff = 1000000000.0
+ besth_diff = 1000000000.0
+ new_w = 0.0
+ new_h = 0.0
+ for l, i in lengths_to_ints:
+ d = abs(l - w)
+ if d < bestw_diff:
+ bestw_diff = d
+ new_w = i # assign the int version
+
+ d = abs(l - h)
+ if d < besth_diff:
+ besth_diff = d
+ new_h = i # ditto
+
+ pf.width = new_w
+ pf.height = new_h
+
+ if new_w > new_h:
+ pf.spin()
+
+ print("...done")
+
+ # Since the boxes are sized in powers of 2, we can neatly group them into bigger squares
+ # this is done hierarchily, so that we may avoid running the pack function
+ # on many thousands of boxes, (under 1k is best) because it would get slow.
+ # Using an off and even dict us usefull because they are packed differently
+ # where w/h are the same, their packed in groups of 4
+ # where they are different they are packed in pairs
+ #
+ # After this is done an external pack func is done that packs the whole group.
+
+ print("\tConsolidating Boxes...", end="")
+ even_dict = {} # w/h are the same, the key is an int (w)
+ odd_dict = {} # w/h are different, the key is the (w,h)
+
+ for pf in pretty_faces:
+ w, h = pf.width, pf.height
+ if w == h:
+ even_dict.setdefault(w, []).append(pf)
+ else:
+ odd_dict.setdefault((w, h), []).append(pf)
+
+ # Count the number of boxes consolidated, only used for stats.
+ c = 0
+
+ # This is tricky. the total area of all packed boxes, then squt that to get an estimated size
+ # this is used then converted into out INT space so we can compare it with
+ # the ints assigned to the boxes size
+ # and divided by BOX_DIV, basicly if BOX_DIV is 8
+ # ...then the maximum box consolidataion (recursive grouping) will have a max width & height
+ # ...1/8th of the UV size.
+ # ...limiting this is needed or you end up with bug unused texture spaces
+ # ...however if its too high, boxpacking is way too slow for high poly meshes.
+ float_to_int_factor = lengths_to_ints[0][0]
+ if float_to_int_factor > 0:
+ max_int_dimension = int(((side_len / float_to_int_factor)) / PREF_BOX_DIV)
+ ok = True
+ else:
+ max_int_dimension = 0.0 # wont be used
+ ok = False
+
+ # RECURSIVE prettyface grouping
+ while ok:
+ ok = False
+
+ # Tall boxes in groups of 2
+ for d, boxes in odd_dict.items():
+ if d[1] < max_int_dimension:
+ #\boxes.sort(key = lambda a: len(a.children))
+ while len(boxes) >= 2:
+ # print("foo", len(boxes))
+ ok = True
+ c += 1
+ pf_parent = prettyface([boxes.pop(), boxes.pop()])
+ pretty_faces.append(pf_parent)
+
+ w, h = pf_parent.width, pf_parent.height
+
+ if w > h:
+ raise "error"
+
+ if w == h:
+ even_dict.setdefault(w, []).append(pf_parent)
+ else:
+ odd_dict.setdefault((w, h), []).append(pf_parent)
+
+ # Even boxes in groups of 4
+ for d, boxes in even_dict.items():
+ if d < max_int_dimension:
+ boxes.sort(key=lambda a: len(a.children))
+
+ while len(boxes) >= 4:
+ # print("bar", len(boxes))
+ ok = True
+ c += 1
+
+ pf_parent = prettyface([boxes.pop(), boxes.pop(), boxes.pop(), boxes.pop()])
+ pretty_faces.append(pf_parent)
+ w = pf_parent.width # width and weight are the same
+ even_dict.setdefault(w, []).append(pf_parent)
+
+ del even_dict
+ del odd_dict
+
+ orig = len(pretty_faces)
+
+ pretty_faces = [pf for pf in pretty_faces if not pf.has_parent]
+
+ # spin every second prettyface
+ # if there all vertical you get less efficiently used texture space
+ i = len(pretty_faces)
+ d = 0
+ while i:
+ i -= 1
+ pf = pretty_faces[i]
+ if pf.width != pf.height:
+ d += 1
+ if d % 2: # only pack every second
+ pf.spin()
+ # pass
+
+ print("Consolidated", c, "boxes, done")
+ # print("done", orig, len(pretty_faces))
+
+ # boxes2Pack.append([islandIdx, w,h])
+ print("\tPacking Boxes", len(pretty_faces), end="...")
+ boxes2Pack = [[0.0, 0.0, pf.width, pf.height, i] for i, pf in enumerate(pretty_faces)]
+ packWidth, packHeight = Geometry.BoxPack2D(boxes2Pack)
+
+ # print(packWidth, packHeight)
+
+ packWidth = float(packWidth)
+ packHeight = float(packHeight)
+
+ margin_w = ((packWidth) / PREF_MARGIN_DIV) / packWidth
+ margin_h = ((packHeight) / PREF_MARGIN_DIV) / packHeight
+
+ # print(margin_w, margin_h)
+ print("done")
+
+ # Apply the boxes back to the UV coords.
+ print("\twriting back UVs", end="")
+ for i, box in enumerate(boxes2Pack):
+ pretty_faces[i].place(box[0], box[1], packWidth, packHeight, margin_w, margin_h)
+ # pf.place(box[1][1], box[1][2], packWidth, packHeight, margin_w, margin_h)
+ print("done")
+
+ if PREF_APPLY_IMAGE:
+ if not PREF_PACK_IN_ONE:
+ image = Image.New("lightmap", PREF_IMG_PX_SIZE, PREF_IMG_PX_SIZE, 24)
+
+ for f in face_sel:
+ f.image = image
+
+ for me in meshes:
+ me.update()
+
+ print("finished all %.2f " % (sys.time() - t))
+
+ Window.RedrawAll()
+
def main():
- scn = bpy.data.scenes.active
-
- PREF_ACT_ONLY = Draw.Create(1)
- PREF_SEL_ONLY = Draw.Create(1)
- PREF_NEW_UVLAYER = Draw.Create(0)
- PREF_PACK_IN_ONE = Draw.Create(0)
- PREF_APPLY_IMAGE = Draw.Create(0)
- PREF_IMG_PX_SIZE = Draw.Create(512)
- PREF_BOX_DIV = Draw.Create(12)
- PREF_MARGIN_DIV = Draw.Create(0.1)
-
- if not Draw.PupBlock('Lightmap Pack', [\
- 'Context...',
- ('Active Object', PREF_ACT_ONLY, 'If disabled, include other selected objects for packing the lightmap.'),\
- ('Selected Faces', PREF_SEL_ONLY, 'Use only selected faces from all selected meshes.'),\
- 'Image & UVs...',
- ('Share Tex Space', PREF_PACK_IN_ONE, 'Objects Share texture space, map all objects into 1 uvmap'),\
- ('New UV Layer', PREF_NEW_UVLAYER, 'Create a new UV layer for every mesh packed'),\
- ('New Image', PREF_APPLY_IMAGE, 'Assign new images for every mesh (only one if shared tex space enabled)'),\
- ('Image Size', PREF_IMG_PX_SIZE, 64, 5000, 'Width and Height for the new image'),\
- 'UV Packing...',
- ('Pack Quality: ', PREF_BOX_DIV, 1, 48, 'Pre Packing before the complex boxpack'),\
- ('Margin: ', PREF_MARGIN_DIV, 0.001, 1.0, 'Size of the margin as a division of the UV')\
- ]):
- return
-
-
- if PREF_ACT_ONLY.val:
- ob = scn.objects.active
- if ob == None or ob.type != 'Mesh':
- Draw.PupMenu('Error%t|No mesh object.')
- return
- meshes = [ ob.getData(mesh=1) ]
- else:
- meshes = dict([ (me.name, me) for ob in scn.objects.context if ob.type == 'Mesh' for me in (ob.getData(mesh=1),) if not me.lib if len(me.faces)])
- meshes = meshes.values()
- if not meshes:
- Draw.PupMenu('Error%t|No mesh objects selected.')
- return
-
- # Toggle Edit mode
- is_editmode = Window.EditMode()
- if is_editmode:
- Window.EditMode(0)
-
-
- Window.WaitCursor(1)
- lightmap_uvpack(meshes,\
- PREF_SEL_ONLY.val,\
- PREF_NEW_UVLAYER.val,\
- PREF_PACK_IN_ONE.val,\
- PREF_APPLY_IMAGE.val,\
- PREF_IMG_PX_SIZE.val,\
- PREF_BOX_DIV.val,\
- int(1/(PREF_MARGIN_DIV.val/100)))
-
- if is_editmode:
- Window.EditMode(1)
-
- Window.WaitCursor(0)
+ scn = bpy.data.scenes.active
+
+ PREF_ACT_ONLY = Draw.Create(1)
+ PREF_SEL_ONLY = Draw.Create(1)
+ PREF_NEW_UVLAYER = Draw.Create(0)
+ PREF_PACK_IN_ONE = Draw.Create(0)
+ PREF_APPLY_IMAGE = Draw.Create(0)
+ PREF_IMG_PX_SIZE = Draw.Create(512)
+ PREF_BOX_DIV = Draw.Create(12)
+ PREF_MARGIN_DIV = Draw.Create(0.1)
+
+ if not Draw.PupBlock("Lightmap Pack", [\
+ "Context...",
+ ('Active Object', PREF_ACT_ONLY, 'If disabled, include other selected objects for packing the lightmap.'),\
+ ('Selected Faces', PREF_SEL_ONLY, 'Use only selected faces from all selected meshes.'),\
+ 'Image & UVs...',
+ ('Share Tex Space', PREF_PACK_IN_ONE, 'Objects Share texture space, map all objects into 1 uvmap'),\
+ ('New UV Layer', PREF_NEW_UVLAYER, 'Create a new UV layer for every mesh packed'),\
+ ('New Image', PREF_APPLY_IMAGE, 'Assign new images for every mesh (only one if shared tex space enabled)'),\
+ ('Image Size', PREF_IMG_PX_SIZE, 64, 5000, 'Width and Height for the new image'),\
+ 'UV Packing...',
+ ('Pack Quality: ', PREF_BOX_DIV, 1, 48, 'Pre Packing before the complex boxpack'),\
+ ('Margin: ', PREF_MARGIN_DIV, 0.001, 1.0, 'Size of the margin as a division of the UV')\
+ ]):
+ return
+
+ if PREF_ACT_ONLY.val:
+ ob = scn.objects.active
+ if ob == None or ob.type != 'Mesh':
+ Draw.PupMenu('Error%t|No mesh object.')
+ return
+ meshes = [ob.getData(mesh=1)]
+ else:
+ meshes = dict([(me.name, me) for ob in scn.objects.context if ob.type == 'Mesh' for me in (ob.getData(mesh=1),) if not me.lib if len(me.faces)])
+ meshes = meshes.values()
+ if not meshes:
+ Draw.PupMenu('Error%t|No mesh objects selected.')
+ return
+
+ # Toggle Edit mode
+ is_editmode = Window.EditMode()
+ if is_editmode:
+ Window.EditMode(0)
+
+ Window.WaitCursor(1)
+ lightmap_uvpack(meshes,
+ PREF_SEL_ONLY.val,
+ PREF_NEW_UVLAYER.val,
+ PREF_PACK_IN_ONE.val,
+ PREF_APPLY_IMAGE.val,
+ PREF_IMG_PX_SIZE.val,
+ PREF_BOX_DIV.val,\
+ int(1.0 / (PREF_MARGIN_DIV.val / 100.0)))
+
+ if is_editmode:
+ Window.EditMode(1)
+
+ Window.WaitCursor(0)
if __name__ == '__main__':
- main()
+ main()