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 imagesize = image.getSize()
54 if imagesize[0] != imagesize[1]:
55 Blender.Draw.PupMenu("Warning%t|Image isn't a square!")
56 gap = (float(GAP) / imagesize[0], float(GAP) / imagesize[1])
57 margin = (float(MARGIN) / imagesize[0] - gap[0] * 0.5, float(MARGIN) / imagesize[1] - gap[1] * 0.5)
60 def drawrect(x0, y0, x1, y1, color = (255, 255, 255, 255)):
65 for u in range(int(x0 + 0.5), int(x1 - 0.5)):
66 for v in range(int(y0 + 0.5), int(y1 - 0.5)):
67 image.setPixelI(u, v, color)
72 Blender.Window.DrawProgressBar(0.0, "packing")
73 for o in Blender.Scene.GetCurrent().objects.selected:
77 mesh = o.getData(mesh = 1)
80 if mesh.name in meshes:
81 #print "dropping duplicate mesh", mesh.name, "of object", o.name
83 meshes[mesh.name] = True
85 print "\tobject '%s'" % o.name
90 xmin = min(xmin, p[0])
91 xmax = max(xmax, p[0])
92 ymin = min(ymin, p[1])
93 ymax = max(ymax, p[1])
97 boxes.append([0, 0, width + gap[0], height + gap[1], xmin, ymin, mesh, o.name])
100 raise Abort('No mesh objects selected')
103 boxsize = Blender.Geometry.BoxPack2D(boxes)
104 xscale = (1.0 - 2.0 * margin[0]) / max(boxsize[0], boxsize[1])
105 yscale = (1.0 - 2.0 * margin[1]) / max(boxsize[0], boxsize[1])
107 Blender.Window.DrawProgressBar(0.2, "Erasing texture")
108 drawrect(0, 0, 1, 1) # erase texture
111 xmax = ymax = -1000.0
112 for f in box[6].faces:
114 p[0] = (p[0] - box[4] + box[0] + gap[0] * 0.5 + margin[0]) * xscale
115 p[1] = (p[1] - box[5] + box[1] + gap[1] * 0.5 + margin[1]) * yscale
117 xmin = min(xmin, p[0])
118 xmax = max(xmax, p[0])
119 ymin = min(ymin, p[1])
120 ymax = max(ymax, p[1])
122 drawrect(xmin, ymin, xmax, ymax, (random.randint(128, 255), random.randint(128, 255),
123 random.randint(128, 255), 255))
124 Blender.Window.DrawProgressBar(1.0, "Finished")
128 editmode = Blender.Window.EditMode()
130 Blender.Window.EditMode(0)
133 print "box packing ..."
137 Blender.Draw.PupMenu("Error%t|" + e.msg)
138 print "Error:", e.msg, " -> aborting ...\n"
141 Blender.Window.EditMode(1)