# 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