diff options
Diffstat (limited to 'release/scripts/uvcalc_lightmap.py')
-rw-r--r-- | release/scripts/uvcalc_lightmap.py | 599 |
1 files changed, 0 insertions, 599 deletions
diff --git a/release/scripts/uvcalc_lightmap.py b/release/scripts/uvcalc_lightmap.py deleted file mode 100644 index 1433ccbd13a..00000000000 --- a/release/scripts/uvcalc_lightmap.py +++ /dev/null @@ -1,599 +0,0 @@ -#!BPY -""" -Name: 'Lightmap UVPack' -Blender: 242 -Group: 'UVCalculation' -Tooltip: 'Give each face non overlapping space on a texture.' -""" -__author__ = "Campbell Barton aka ideasman42" -__url__ = ("blender", "blenderartists.org") -__version__ = "1.0 2006/02/07" - -__bpydoc__ = """\ -""" - -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# Script copyright (C) Campbell Barton -# -# 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 * -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] - -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() - -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) - -if __name__ == '__main__': - main() |