#!BPY # """ # Name: 'Pack selected objects on a square' # Blender: 245 # Group: 'UV' # Tooltip: 'Pack UV maps of all selected objects onto an empty square texture' # """ __author__ = "Melchior FRANZ < mfranz # aon : at >" __url__ = "http://members.aon.at/mfranz/flightgear/" __version__ = "0.1" __bpydoc__ = """\ Script for mapping multiple objects onto one square texture. Usage: (1) create new square texture in the UV editor (2) map all objects individually, choosing the most appropriate technique (3) scale each of the mappings to the appropriate size, relative to the other object mappings (4) select all objects and switch to edit mode (consider to use Select->Linked->Material or similar methods) (5) start this script with UVs->Scripts->Pack objects on a square [now the texture image will first be erased, then colored rectangles will appear for each object] (6) rescale and/or remap objects that you aren't happy with (the relative size of a mapping will be kept) [continue with (5) until you like the result] (7) export UV layout to SVG (UVs->Scripts->Save UV Face Layout) """ MARGIN = 10 # px GAP = 10 # px import Blender from random import randint as rand class Abort(Exception): def __init__(self, msg): self.msg = msg def pack(): image = Blender.Image.GetCurrent() if not image: raise Abort('No texture image selected') imgwidth, imgheight = image.getSize() if imgwidth != imgheight: Blender.Draw.PupMenu("Warning%t|Image isn't a square!") gap = (float(GAP) / imgwidth, float(GAP) / imgheight) margin = (float(MARGIN) / imgwidth - gap[0] * 0.5, float(MARGIN) / imgheight - gap[1] * 0.5) def drawrect(x0, y0, x1, y1, color = (255, 255, 255, 255)): x0 *= imgwidth x1 *= imgwidth y0 *= imgheight y1 *= imgheight for u in range(int(x0 + 0.5), int(x1 - 0.5)): for v in range(int(y0 + 0.5), int(y1 - 0.5)): image.setPixelI(u, v, color) boxes = [] unique_meshes = {} BIG = 1<<30 Blender.Window.DrawProgressBar(0.0, "packing") for o in Blender.Scene.GetCurrent().objects.selected: if o.type != "Mesh": continue mesh = o.getData(mesh = 1) if not mesh.faceUV: continue if mesh.name in unique_meshes: #print "dropping duplicate mesh", mesh.name, "of object", o.name continue unique_meshes[mesh.name] = True print "\tobject '%s'" % o.name xmin = ymin = BIG xmax = ymax = -BIG for f in mesh.faces: for p in f.uv: xmin = min(xmin, p[0]) xmax = max(xmax, p[0]) ymin = min(ymin, p[1]) ymax = max(ymax, p[1]) width = xmax - xmin height = ymax - ymin boxes.append([0, 0, width + gap[0], height + gap[1], xmin, ymin, mesh]) if not boxes: raise Abort('No mesh objects selected') boxwidth, boxheight = Blender.Geometry.BoxPack2D(boxes) boxmax = max(boxwidth, boxheight) xscale = (1.0 - 2.0 * margin[0]) / boxmax yscale = (1.0 - 2.0 * margin[1]) / boxmax Blender.Window.DrawProgressBar(0.2, "Erasing texture") image.reload() #drawrect(0, 0, 1, 1) # erase texture for i, box in enumerate(boxes): Blender.Window.DrawProgressBar(i * 0.8 / len(boxes), "Drawing") xmin = ymin = BIG xmax = ymax = -BIG for f in box[6].faces: for p in f.uv: p[0] = (p[0] - box[4] + box[0] + gap[0] * 0.5 + margin[0]) * xscale p[1] = (p[1] - box[5] + box[1] + gap[1] * 0.5 + margin[1]) * yscale xmin = min(xmin, p[0]) xmax = max(xmax, p[0]) ymin = min(ymin, p[1]) ymax = max(ymax, p[1]) drawrect(xmin, ymin, xmax, ymax, (rand(128, 255), rand(128, 255), rand(128, 255), 255)) box[6].update() Blender.Window.RedrawAll() Blender.Window.DrawProgressBar(1.0, "Finished") editmode = Blender.Window.EditMode() if editmode: Blender.Window.EditMode(0) Blender.Window.WaitCursor(1) try: print "box packing ..." pack() print "done\n" except Abort, e: print "Error:", e.msg, " -> aborting ...\n" Blender.Draw.PupMenu("Error%t|" + e.msg) Blender.Window.WaitCursor(0) if editmode: Blender.Window.EditMode(1)