[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [35421] trunk/blender/release/scripts/op/ uvcalc_lightmap.py: copy from 2.4x, update to 2.5x next.

Campbell Barton ideasman42 at gmail.com
Wed Mar 9 05:01:40 CET 2011


Revision: 35421
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=35421
Author:   campbellbarton
Date:     2011-03-09 04:01:40 +0000 (Wed, 09 Mar 2011)
Log Message:
-----------
copy from 2.4x, update to 2.5x next.
svn cp https://svn.blender.org/svnroot/bf-blender/branches/blender2.4/release/scripts/uvcalc_lightmap.py release/scripts/op/uvcalc_lightmap.py

Added Paths:
-----------
    trunk/blender/release/scripts/op/uvcalc_lightmap.py

Copied: trunk/blender/release/scripts/op/uvcalc_lightmap.py (from rev 35420, branches/blender2.4/release/scripts/uvcalc_lightmap.py)
===================================================================
--- trunk/blender/release/scripts/op/uvcalc_lightmap.py	                        (rev 0)
+++ trunk/blender/release/scripts/op/uvcalc_lightmap.py	2011-03-09 04:01:40 UTC (rev 35421)
@@ -0,0 +1,599 @@
+#!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)

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list