Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2006-05-19 19:56:14 +0400
committerCampbell Barton <ideasman42@gmail.com>2006-05-19 19:56:14 +0400
commit4475c4211ae27c9a360091d39563f0d91940b457 (patch)
tree995a7445aeec5347cc05513ff2ef43ebbf5412f3 /release/scripts
parent1022107236f75fae4a8d31f2f8ae5832db13deab (diff)
From __bpydoc__
This script makes a new image from the used areas of all the images mapped to the selected mesh objects. Image are packed into 1 new image that is assigned to the original faces. This is usefull for game models where 1 image is faster then many, and saves the labour of manual texture layout in an image editor. - Realy needs an image example to see why this is usefull.
Diffstat (limited to 'release/scripts')
-rw-r--r--release/scripts/uv_auto_layout_tex.py352
1 files changed, 352 insertions, 0 deletions
diff --git a/release/scripts/uv_auto_layout_tex.py b/release/scripts/uv_auto_layout_tex.py
new file mode 100644
index 00000000000..15e990c8b80
--- /dev/null
+++ b/release/scripts/uv_auto_layout_tex.py
@@ -0,0 +1,352 @@
+#!BPY
+
+"""
+Name: 'Auto Image Layout'
+Blender: 241
+Group: 'UV'
+Tooltip: 'Pack all texture images into 1 image and remap faces.'
+"""
+
+__author__ = "Campbell Barton"
+__url__ = ("blender", "blenderartists.org")
+__version__ = "1.0 2005/05/20"
+
+__bpydoc__ = """\
+This script makes a new image from the used areas of all the images mapped to the selected mesh objects.
+Image are packed into 1 new image that is assigned to the original faces.
+This is usefull for game models where 1 image is faster then many, and saves the labour of manual texture layout in an image editor.
+
+"""
+# --------------------------------------------------------------------------
+# Auto Texture Layout v1.0 by Campbell Barton (AKA Ideasman)
+# --------------------------------------------------------------------------
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# 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 *****
+# --------------------------------------------------------------------------
+
+
+# Function to find all the images we use
+import Blender as B
+import boxpack2d
+from Blender.Mathutils import Vector
+from Blender.Scene import Render
+
+BIGNUM= 1<<30
+class faceGroup(object):
+ __slots__= 'xmax', 'ymax', 'xmin', 'ymin',\
+ 'image', 'faces', 'box_pack', 'size'\
+
+ def __init__(self, mesh_list, image, size, PREF_IMAGE_MARGIN):
+ self.image= image
+ self.size= size
+ # Add to our face group and set bounds.
+ xmin=ymin= BIGNUM
+ xmax=ymax= -BIGNUM
+ self.faces= []
+ for me in mesh_list:
+ for f in me.faces:
+ if f.image==image:
+ self.faces.append(f)
+ for uv in f.uv:
+ xmax= max(xmax, uv.x)
+ xmin= min(xmin, uv.x)
+ ymax= max(ymax, uv.y)
+ ymin= min(ymin, uv.y)
+
+ # The box pack list is to be passed to the external function "boxpack2d"
+ # format is ID, w,h
+
+ # Store the bounds, impliment the margin.
+ self.xmax= xmax + (PREF_IMAGE_MARGIN/size[0])
+ self.xmin= xmin - (PREF_IMAGE_MARGIN/size[0])
+ self.ymax= ymax + (PREF_IMAGE_MARGIN/size[1])
+ self.ymin= ymin - (PREF_IMAGE_MARGIN/size[1])
+
+ self.box_pack=[\
+ image.name,\
+ size[0]*(self.xmax - self.xmin),\
+ size[1]*(self.ymax - self.ymin)]
+
+
+ '''
+ # default.
+ self.scale= 1.0
+
+ def set_worldspace_scale(self):
+ scale_uv= 0.0
+ scale_3d= 0.0
+ for f in self.faces:
+ for i in xrange(len(f.v)):
+ scale_uv+= (f.uv[i]-f.uv[i-1]).length * 0.1
+ scale_3d+= (f.v[i].co-f.v[i-1].co).length * 0.1
+ self.scale= scale_3d/scale_uv
+ '''
+
+
+
+ def move2packed(self, width, height):
+ '''
+ Moves the UV coords to their packed location
+ using self.box_pack as the offset, scaler.
+ box_pack must be set to its packed location.
+ width and weight are the w/h of the overall packed area's bounds.
+ '''
+ # packedLs is a list of [(anyUniqueID, left, bottom, width, height)...]
+ # Width and height in float pixel space.
+
+ # X Is flipped :/
+ #offset_x= (1-(self.box_pack[1]/d)) - (((self.xmax-self.xmin) * self.image.size[0])/d)
+ offset_x= self.box_pack[1]/width
+ offset_y= self.box_pack[2]/height
+
+ for f in self.faces:
+ for uv in f.uv:
+ uv.x= offset_x+ (((uv.x-self.xmin) * self.size[0])/width)
+ uv.y= offset_y+ (((uv.y-self.ymin) * self.size[1])/height)
+
+def auto_layout_tex(mesh_list, scn, PREF_IMAGE_PATH, PREF_IMAGE_SIZE, PREF_KEEP_ASPECT, PREF_IMAGE_MARGIN): #, PREF_SIZE_FROM_UV=True):
+
+ # Get all images used by the mesh
+ face_groups= {}
+
+ for me in mesh_list:
+ for f in me.faces:
+ if f.image:
+ try:
+ face_groups[f.image.name] # will fail if teh groups not added.
+ except:
+ image= f.image
+ try:
+ size= image.size
+ except:
+ B.Draw.PupMenu('Aborting: Image cold not be loaded|' + image.name)
+ return
+
+ face_groups[f.image.name]= faceGroup(mesh_list, f.image, size, PREF_IMAGE_MARGIN)
+
+ if not face_groups:
+ B.Draw.PupMenu('No Images found in mesh. aborting.')
+ return
+
+ if len(face_groups)==1:
+ B.Draw.PupMenu('Only 1 image found|use meshes using 2 or more images.')
+ return
+
+ '''
+ if PREF_SIZE_FROM_UV:
+ for fg in face_groups.itervalues():
+ fg.set_worldspace_scale()
+ '''
+
+ # RENDER THE FACES.
+ render_scn= B.Scene.New()
+ render_scn.makeCurrent()
+
+
+ # Set the render context
+ render_context= render_scn.getRenderingContext()
+ render_context.currentFrame(1)
+ PREF_IMAGE_PATH_EXPAND= B.sys.expandpath(PREF_IMAGE_PATH + '#') + '.png'
+
+ # TEST THE FILE WRITING.
+ try:
+ # Can we write to this file???
+ f= open(PREF_IMAGE_PATH_EXPAND, 'w')
+ f.write('\n')
+ f.close()
+ except:
+ B.Draw.PupMenu('Error: Could not write to path|' + PREF_IMAGE_PATH_EXPAND)
+ scn.makeCurrent()
+ B.Scene.Unlink(render_scn)
+ return
+
+ render_context.setRenderPath(PREF_IMAGE_PATH)
+
+ render_context.imageSizeX(PREF_IMAGE_SIZE)
+ render_context.imageSizeY(PREF_IMAGE_SIZE)
+ render_context.startFrame(1)
+ render_context.endFrame(1)
+ render_context.enableOversampling(True)
+ render_context.setOversamplingLevel(16)
+ render_context.setRenderWinSize(100)
+ render_context.setImageType(Render.PNG)
+ render_context.enableExtensions(True)
+ render_context.enableSky() # No alpha needed.
+ render_context.enableRGBColor()
+
+ Render.EnableDispView() # Broken??
+
+ # New Mesh and Object
+ render_mat= B.Material.New()
+ render_mat.mode |= B.Material.Modes.SHADELESS
+ render_mat.mode |= B.Material.Modes.TEXFACE
+
+
+ render_me= B.Mesh.New()
+ render_me.verts.extend([Vector(0,0,0)]) # Stupid, dummy vert, preverts errors.
+ render_ob= B.Object.New('Mesh')
+ render_ob.link(render_me)
+ render_scn.link(render_ob)
+ render_me.materials= [render_mat]
+
+
+ # New camera and object
+ render_cam_data= B.Camera.New('ortho')
+ render_cam_ob= B.Object.New('Camera')
+ render_cam_ob.link(render_cam_data)
+ render_scn.link(render_cam_ob)
+ render_scn.setCurrentCamera(render_cam_ob)
+
+ render_cam_data.type= 1 # ortho
+ render_cam_data.scale= 1.0
+
+
+ # Position the camera
+ render_cam_ob.LocZ= 1.0 # set back 1
+ render_cam_ob.LocX= 0.5 # set back 1
+ render_cam_ob.LocY= 0.5 # set back 1
+ #render_cam_ob.RotY= 180 * 0.017453292519943295 # pi/180.0
+
+ # List to send to to boxpack function.
+ boxes2Pack= [ fg.box_pack for fg in face_groups.itervalues()]
+
+ packWidth, packHeight, packedLs = boxpack2d.boxPackIter(boxes2Pack)
+
+ if PREF_KEEP_ASPECT:
+ packWidth= packHeight= max(packWidth, packHeight)
+
+
+ # packedLs is a list of [(anyUniqueID, left, bottom, width, height)...]
+ # Re assign the face groups boxes to the face_group.
+ for box in packedLs:
+ face_groups[ box[0] ].box_pack= box # box[0] is the ID (image name)
+
+
+ # Add geometry to the mesh
+ for fg in face_groups.itervalues():
+ # Add verts clockwise from the bottom left.
+ _x= fg.box_pack[1] / packWidth
+ _y= fg.box_pack[2] / packHeight
+ _w= fg.box_pack[3] / packWidth
+ _h= fg.box_pack[4] / packHeight
+
+ render_me.verts.extend([\
+ Vector(_x, _y, 0),\
+ Vector(_x, _y +_h, 0),\
+ Vector(_x + _w, _y +_h, 0),\
+ Vector(_x + _w, _y, 0),\
+ ])
+
+ render_me.faces.extend([\
+ render_me.verts[-1],\
+ render_me.verts[-2],\
+ render_me.verts[-3],\
+ render_me.verts[-4],\
+ ])
+
+ target_face= render_me.faces[-1]
+ target_face.image= fg.image
+ target_face.mode |= B.Mesh.FaceModes.TEX
+
+ # Set the UV's, we need to flip them HOZ?
+ target_face.uv[0].x= target_face.uv[1].x= fg.xmax
+ target_face.uv[2].x= target_face.uv[3].x= fg.xmin
+
+ target_face.uv[0].y= target_face.uv[3].y= fg.ymin
+ target_face.uv[1].y= target_face.uv[2].y= fg.ymax
+
+ # VCOLS
+ # Set them white.
+ for c in target_face.col:
+ c.r= c.g= c.b= 255
+
+ #render_context.render()
+ #render_context.saveRenderedImage('/test.png') # BROKEN.
+ render_context.renderAnim()
+
+ #if not B.sys.exists(PREF_IMAGE_PATH):
+ # raise 'Error!!!'
+ Render.CloseRenderWindow()
+
+ # NOW APPLY THE SAVED IMAGE TO THE FACES!
+ print PREF_IMAGE_PATH_EXPAND
+ target_image= B.Image.Load(PREF_IMAGE_PATH_EXPAND)
+
+ # Set to the 1 image.
+ for me in mesh_list:
+ for f in me.faces:
+ if f.image:
+ f.image= target_image
+
+ for fg in face_groups.itervalues():
+ fg.move2packed(packWidth, packHeight)
+
+ scn.makeCurrent()
+ B.Scene.Unlink(render_scn)
+
+
+def main():
+ scn= B.Scene.GetCurrent()
+ ob= scn.getActiveObject()
+
+ if not ob or ob.getType() != 'Mesh':
+ B.Draw.PupMenu('Error, no active mesh object, aborting.')
+ return
+
+ # Create the variables.
+ # Filename without path or extension.
+ newpath= B.Get('filename').split('/')[-1].split('\\')[-1].replace('.blend', '')
+
+ PREF_IMAGE_PATH = B.Draw.Create('//%s_grp' % newpath)
+ PREF_IMAGE_SIZE = B.Draw.Create(512)
+ PREF_IMAGE_MARGIN = B.Draw.Create(6)
+ PREF_KEEP_ASPECT = B.Draw.Create(1)
+ PREF_ALL_SEL_OBS = B.Draw.Create(0)
+
+ pup_block = [\
+ 'image path: no ext',\
+ ('', PREF_IMAGE_PATH, 3, 100, 'Path to new Image. "//" for curent blend dir.'),\
+ 'Image Options',
+ ('Pixel Size:', PREF_IMAGE_SIZE, 64, 4096, 'Image Width and Height.'),\
+ ('Pixel Margin:', PREF_IMAGE_MARGIN, 0, 64, 'Image Width and Height.'),\
+ ('Keep Image Aspect', PREF_KEEP_ASPECT, 'If disabled, will stretch the images to the bounds of the texture'),\
+ 'Texture Source',\
+ ('All Sel Objects', PREF_ALL_SEL_OBS, 'Combine and replace textures from all objects into 1 texture.'),\
+ ]
+
+ if not B.Draw.PupBlock('Auto Texture Layout', pup_block):
+ return
+
+ PREF_IMAGE_PATH= PREF_IMAGE_PATH.val
+ PREF_IMAGE_SIZE= PREF_IMAGE_SIZE.val
+ PREF_IMAGE_MARGIN= PREF_IMAGE_MARGIN.val
+ PREF_KEEP_ASPECT= PREF_KEEP_ASPECT.val
+ PREF_ALL_SEL_OBS= PREF_ALL_SEL_OBS.val
+
+ if PREF_ALL_SEL_OBS:
+ mesh_list= [ob.getData(mesh=1) for ob in B.Object.GetSelected() if ob.getType()=='Mesh']
+ # Make sure we have no doubles- dict by name, then get the values back.
+ mesh_list= dict([(me.name, me) for me in mesh_list])
+ mesh_list= mesh_list.values()
+ else:
+ mesh_list= [ob.getData(mesh=1)]
+
+ auto_layout_tex(mesh_list, scn, PREF_IMAGE_PATH, PREF_IMAGE_SIZE, PREF_KEEP_ASPECT, PREF_IMAGE_MARGIN)
+
+if __name__=='__main__':
+ main()