# Blender UV Mapping Script # # Author: Nathaniel Meyer # Website: http://www.nutty.ca # Updated: Oct. 10, 2012 # # Built with Blender version 2.63 # # This script generates efficient UV mapping for the active (selected) object. It is # handy for UV mapping game objects and when Blender's UV unwrapping algorithms don't # provide you with the level of control you need. You can create UV planar maps, spherical # UV planar maps, and cube maps. With planar mapping, UVs are mapped such that front facing # polygons occupy the first half of the texture space and backfacing polygons occupy the # other half. Cube maps are oriented as follows # # Top # Left Front Right Back # Bottom # # You must be in object mode for this script to function (Blender limitation). import bpy import os import math from array import * #os.system(['clear','cls'][os.name == 'nt']) #print("Starting\n---\n\n"); # Enumerations used by this script class UV: Planar = 1 Spherical = 2 Cube = 3 XY = 1 YZ = 2 XZ = 3 # Specifies the top-left starting coordinates for each cube face class CubeUV: DeltaX = 1.0 / 4.0 DeltaY = 1.0 / 3.0 LeftX = 0.0 LeftY = DeltaY FrontX = LeftX + DeltaX FrontY = LeftY RightX = FrontX + DeltaX RightY = LeftY BackX = RightX + DeltaX BackY = LeftY TopX = FrontX TopY = 0.0 BottomX = FrontX BottomY = FrontY + DeltaY # # CONFIGURABLE PROPERTIES # TODO: Integrate into Blender's UI # # Specify the UV mapping coordinate system to use. If using planar or spherical mapping, # specify the plane on which the mapping will take place. uvMap = UV.Cube uvPlane = UV.XY # Start UV mapping process obj = bpy.context.active_object; if ( obj != None ): if ( obj.type == "MESH" ): mesh = obj.data; if ( len(mesh.uv_layers) > 0 ): uvLayer = mesh.uv_layers[0] polyCount = len(mesh.polygons) vertCount = len(mesh.vertices) uvCount = len(uvLayer.data) print("Processing %s. Polys = %d. Verts = %d. UVs: %d." % (obj.name, polyCount, vertCount, uvCount)) # Get the bounding volume of this object # 0 = bottom back left, 6 = forward top right min = obj.bound_box[0] max = obj.bound_box[6] extents = array('f', [max[0] - min[0], max[1] - min[1], max[2] - min[2]]); print("Bounding Box: (%f, %f, %f), (%f, %f, %f)" % (min[0], min[1], min[2], max[0], max[1], max[2])) # Iterate over indices uvIndex = 0; for polygon in mesh.polygons: # Use the face normal to detect if the polygon is backface and adjust the UV coordinates # accordingly. normal = polygon.normal absNormal = [math.fabs(normal[0]), math.fabs(normal[1]), math.fabs(normal[2])] for index in polygon.vertices: poly = mesh.vertices[index] vertex = poly.co vertexNormal = poly.normal uv = uvLayer.data[uvIndex].uv uvIndex = uvIndex + 1 if uvMap == UV.Planar: # XY if ( uvPlane == UV.XY ): uv[0] = ((vertex[0] - min[0]) / extents[0]) * 0.5 if ( normal[2] < 0 ): uv[0] = 1.0 - uv[0] uv[1] = (vertex[1] - min[1]) / extents[1] # YZ elif ( uvPlane == UV.YZ ): uv[0] = ((vertex[1] - min[1]) / extents[1]) * 0.5 if ( normal[0] < 0 ): uv[0] = 1.0 - uv[0] uv[1] = (vertex[2] - min[2]) / extents[2] # XZ elif ( uvPlane == UV.XZ ): uv[0] = ((vertex[0] - min[0]) / extents[0]) * 0.5 if ( normal[0] < 0 ): uv[0] = 1.0 - uv[0] uv[1] = (vertex[2] - min[2]) / extents[2] elif uvMap == UV.Spherical: # XY if ( uvPlane == UV.XY ): uv[0] = (vertexNormal[0] * 0.25) + 0.25 if ( normal[2] < 0 ): uv[0] = 1.0 - uv[0] uv[1] = (vertex[1] - min[1]) / extents[1] # YZ elif ( uvPlane == UV.YZ ): uv[0] = (vertexNormal[1] * 0.25) + 0.25 if ( normal[0] < 0 ): uv[0] = 1.0 - uv[0] uv[1] = (vertex[2] - min[2]) / extents[2] # XZ elif ( uvPlane == UV.XZ ): uv[0] = (vertexNormal[0] * 0.25) + 0.25 if ( normal[0] < 0 ): uv[0] = 1.0 - uv[0] uv[1] = (vertex[2] - min[2]) / extents[2] elif uvMap == UV.Cube: # Use the face normal to determine UV coordinates # Assume face normals are pointing towards the centre if (absNormal[0] > absNormal[1]) and (absNormal[0] > absNormal[2]): if normal[0] > 0.0: # Left uv[0] = CubeUV.LeftX + (1.0 - (vertex[2] - min[2]) / extents[2]) * CubeUV.DeltaX uv[1] = CubeUV.LeftY + ((vertex[1] - min[1]) / extents[1]) * CubeUV.DeltaY else: # Right uv[0] = CubeUV.RightX + ((vertex[2] - min[2]) / extents[2]) * CubeUV.DeltaX uv[1] = CubeUV.RightY + ((vertex[1] - min[1]) / extents[1]) * CubeUV.DeltaY elif (absNormal[2] > absNormal[0]) and (absNormal[2] > absNormal[1]): if normal[2] > 0.0: # Front uv[0] = CubeUV.FrontX + ((vertex[0] - min[0]) / extents[0]) * CubeUV.DeltaX uv[1] = CubeUV.FrontY + ((vertex[1] - min[1]) / extents[1]) * CubeUV.DeltaY else: # Back uv[0] = CubeUV.BackX + (1.0 - (vertex[0] - min[0]) / extents[0]) * CubeUV.DeltaX uv[1] = CubeUV.BackY + ((vertex[1] - min[1]) / extents[1]) * CubeUV.DeltaY elif (absNormal[1] > absNormal[0]) and (absNormal[1] > absNormal[2]): if normal[1] > 0.0: # Top uv[0] = CubeUV.TopX + ((vertex[0] - min[0]) / extents[0]) * CubeUV.DeltaX uv[1] = CubeUV.TopY + ((vertex[2] - min[2]) / extents[2]) * CubeUV.DeltaY else: # Bottom uv[0] = CubeUV.BottomX + ((vertex[0] - min[0]) / extents[0]) * CubeUV.DeltaX uv[1] = CubeUV.BottomY + ((vertex[2] - min[2]) / extents[2]) * CubeUV.DeltaY