4 # Name: 'Pack selected objects on a square'
7 # Tooltip: 'Pack UV maps of all selected objects onto an empty square texture'
10 __author__ = "Melchior FRANZ < mfranz # aon : at >"
11 __url__ = "http://members.aon.at/mfranz/flightgear/"
14 Script for mapping multiple objects onto one square texture.
17 (1) create new square texture in the UV editor
18 (2) map all objects individually, choosing the most appropriate technique
19 (3) scale each of the mappings to the appropriate size, relative to the
21 (4) select all objects and switch to edit mode (consider to use
22 Select->Linked->Material or similar methods)
23 (5) start this script with UVs->Scripts->Pack objects on a square
25 [now the texture image will first be erased, then colored rectangles
26 will appear for each object]
28 (6) rescale and/or remap objects that you aren't happy with (the
29 relative size of a mapping will be kept)
31 [continue with (5) until you like the result]
33 (7) export UV layout to SVG (UVs->Scripts->Save UV Face Layout)
40 import Blender, math, random
43 class Abort(Exception):
44 def __init__(self, msg):
49 image = Blender.Image.GetCurrent()
51 raise Abort('No texture image selected')
53 imgwidth, imgheight = image.getSize()
54 if imgwidth != imgheight:
55 Blender.Draw.PupMenu("Warning%t|Image isn't a square!")
56 gap = (float(GAP) / imgwidth, float(GAP) / imgheight)
57 margin = (float(MARGIN) / imgwidth - gap[0] * 0.5, float(MARGIN) / imgheight - gap[1] * 0.5)
59 def drawrect(x0, y0, x1, y1, color = (255, 255, 255, 255)):
64 for u in range(int(x0 + 0.5), int(x1 - 0.5)):
65 for v in range(int(y0 + 0.5), int(y1 - 0.5)):
66 image.setPixelI(u, v, color)
73 Blender.Window.DrawProgressBar(0.0, "packing")
74 for o in Blender.Scene.GetCurrent().objects.selected:
78 mesh = o.getData(mesh = 1)
81 if mesh.name in meshes:
82 #print "dropping duplicate mesh", mesh.name, "of object", o.name
84 meshes[mesh.name] = True
86 print "\tobject '%s'" % o.name
91 xmin = min(xmin, p[0])
92 xmax = max(xmax, p[0])
93 ymin = min(ymin, p[1])
94 ymax = max(ymax, p[1])
98 boxes.append([0, 0, width + gap[0], height + gap[1], xmin, ymin, mesh])
101 raise Abort('No mesh objects selected')
104 boxwidth, boxheight = Blender.Geometry.BoxPack2D(boxes)
105 boxmax = max(boxwidth, boxheight)
106 xscale = (1.0 - 2.0 * margin[0]) / boxmax
107 yscale = (1.0 - 2.0 * margin[1]) / boxmax
109 Blender.Window.DrawProgressBar(0.2, "Erasing texture")
110 drawrect(0, 0, 1, 1) # erase texture
114 for f in box[6].faces:
116 p[0] = (p[0] - box[4] + box[0] + gap[0] * 0.5 + margin[0]) * xscale
117 p[1] = (p[1] - box[5] + box[1] + gap[1] * 0.5 + margin[1]) * yscale
119 xmin = min(xmin, p[0])
120 xmax = max(xmax, p[0])
121 ymin = min(ymin, p[1])
122 ymax = max(ymax, p[1])
124 drawrect(xmin, ymin, xmax, ymax, (random.randint(128, 255), random.randint(128, 255),
125 random.randint(128, 255), 255))
128 Blender.Window.RedrawAll()
129 Blender.Window.DrawProgressBar(1.0, "Finished")
133 editmode = Blender.Window.EditMode()
135 Blender.Window.EditMode(0)
136 Blender.Window.WaitCursor(1)
139 print "box packing ..."
143 print "Error:", e.msg, " -> aborting ...\n"
144 Blender.Draw.PupMenu("Error%t|" + e.msg)
146 Blender.Window.WaitCursor(0)
148 Blender.Window.EditMode(1)