#!BPY
"""
Name: 'Arnaud SkyBox'
Blender: 248
Group: 'Render'
Tooltip: 'SkyBox Rendering script'
"""

__author__ = "Arnaud Couturier (piiichan)"
__url__ = [""]
__version__ = "1.0"

__bpydoc__ = """\
   	This script will make your life easier by automatically creating
	
a skybox representing your Blender scene. The rendered textures

will be saved as png files in the Blender output folder..
"""


# Skybox creation script
# Alt-p to execute
#
# author: Arnaud Couturier (piiichan)
# location: New Caledonia (Pacific island)
# date: August 2008 (Blender 2.47)
# version: 1.0
# license: free for any use
# rationale:
#	This script will make your life easier by automatically creating
#	a skybox representing your Blender scene. The rendered textures
#	will be saved as png files in the Blender output folder.

# ***** BEGIN GPL LICENSE BLOCK *****
#
# Script copyright (c) Arnaud Couturier (piiichan)
#
# 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, maximum  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****

import math
import Blender.Scene
import Blender.Mesh
import Blender.Scene.Render
import Blender.Image
import Blender.Draw
import Blender.BGL
import Blender.Material


# TODO
# let user choose image file format
# add target platform Ogre3D


# GUI elements
gTextTitle = 0
gTextTitle2 = 0

gButtonTexSize = Blender.Draw.Create(128)
gButtonTexSizeNb = 1 # must be unique among GUI elements

gButtonBoxSize = Blender.Draw.Create(1)
gButtonBoxSizeNb = 2 # must be unique among GUI elements

gButtonBoxPos = Blender.Draw.Create("")
gButtonBoxPosNb = 3 # must be unique among GUI elements

gButtonPlatform = Blender.Draw.Create(1)
gButtonPlatformNb = 4 # must be unique among GUI elements

gButtonCreate = Blender.Draw.Create("Create skybox")
gButtonCreateNb = 5 # must be unique among GUI elements

# draws the GUI
def drawgui():
	global gTextTitle
	global gTextTitle2
	
	global gButtonCreate
	global gButtonCreateNb
	
	global gButtonTexSize
	global gButtonTexSizeNb
	
	global gButtonBoxSize
	global gButtonBoxSizeNb
	
	global gButtonPlatform
	global gButtonPlatformNb
	
	global gButtonBoxPos
	global gButtonBoxPosNb
	
	
	
	
	Blender.BGL.glClearColor(0.5,0.5,0.5,1.0)
	Blender.BGL.glClear(Blender.BGL.GL_COLOR_BUFFER_BIT)
	
	# title
	Blender.BGL.glColor3f(0,0,0) # this must be before glRasterPos
	Blender.BGL.glRasterPos2d(10,140)
	gTextTitle = Blender.Draw.Text("Arnaud's skybox creation script 1.0")
	Blender.BGL.glRasterPos2d(10,120)
	gTextTitle2 = Blender.Draw.Text("for Blender 2.47, Q to quit", "tiny")
	
	# button texture size
	gButtonTexSize = Blender.Draw.Number("Texture size:", gButtonTexSizeNb, 10, 90, 200, 19, gButtonTexSize.val, 32, 4096,
		"Size (width and height) of the texture (in pixels) for each face of the skybox")
	
	# button box size
	gButtonBoxSize = Blender.Draw.Number("Skybox size:", gButtonBoxSizeNb, 10, 70, 200, 19, gButtonBoxSize.val, 1, 10000,
		"Size of the box edges in Blender units")
	
	# button box origin
	gButtonBoxPos = Blender.Draw.String("View from object: ", gButtonBoxPosNb, 10, 50, 200, 19, gButtonBoxPos.val, 10,
		"Object in the scene. The skybox and point of view will be centered at that object's location. If the specified object doesn't exist, the origin (0,0,0) will be considered instead")
	
	# button target platform
	gButtonPlatform = Blender.Draw.Menu("Target platform%t|For Blender%x1|For Irrlicht%x2", gButtonPlatformNb, 10, 30, 200, 19, gButtonPlatform.val, "Is this skybox to be used in Blender, Irrlicht (Ogre3d to come)?")
	
	# button create
	gButtonCreate = Blender.Draw.PushButton("Create skybox", gButtonCreateNb, 10, 10, 200, 19,
		"Create the skybox: creates the mesh, the object and renders/saves the 6 faces in the Blender output folder as png files")

# mouse and key events
def event(evt, val):
	if evt == Blender.Draw.QKEY and val == 0: # 0 means key released
		Blender.Draw.Exit()

# GUI buttons events
def bevent(evt):
	global gButtonCreateNb
	global gButtonBoxPos
	global gButtonBoxSize
	global gButtonTexSize
	global gButtonPlatform
	
	if evt == gButtonCreateNb:
		# try to get the specified object for the skybox position
		sc = Blender.Scene.GetCurrent()
		pos = [0.0, 0.0, 0.0]
		posFound = False
		if gButtonBoxPos.val != "":
			for o in sc.objects:
				if o.name == gButtonBoxPos.val:
					pos = o.loc
					posFound = True
					break
			if not posFound:
				print "Specified object not found in current scene, default to (0,0,0)"
		
		# define target platform
		if gButtonPlatform.val == 1:
			targetPlatform = "blender"
		elif gButtonPlatform.val == 2:
			targetPlatform = "irrlicht"
		else:
			raise NameError, "Specified target platform unknown"
			
		# now create the skybox
		createSkybox(pos, gButtonBoxSize.val, gButtonTexSize.val, targetPlatform)

# register callbacks for windowing
Blender.Draw.Register(drawgui, event, bevent)













# this is the method that creates the skybox
def createSkybox(pos, boxSize, texSize, targetPlatform):
	# prepare some useful variables
	sc = Blender.Scene.GetCurrent()
	obName = "skybox"
	meName = "skybox"
	camDatName = "skybox_cam"
	camObName = "skybox_cam"

	# create the skybox mesh
	try:
		me = Blender.Mesh.Get(meName)
	except NameError:
		me = Blender.Mesh.New(meName)
		s = 0.5
		verts = [[-s,-s,-s], [s,-s,-s], [s,s,-s], [-s,s,-s],
			[-s,-s,s], [s,-s,s], [s,s,s], [-s,s,s]]
		# left, front, right, back, top, bottom (anticlockwise)
		faces = [[7,4,0,3], [3,2,6,7], [2,1,5,6], [1,0,4,5], [5,4,7,6], [2,3,0,1]]
		me.verts.extend(verts)
		me.faces.extend(faces)
		me.name = meName

	# create the skybox object
	boxExist = False
	for ob in sc.objects:
		if ob.name == obName:
			boxExist = True
			skybox = ob
	if not boxExist:
		skybox = sc.objects.new(me,obName)
	skybox.size = [boxSize, boxSize, boxSize]

	# put skybox very far from the world origin so it will not appear in the render
	skybox.LocX = skybox.LocY = skybox.LocZ = 10000
	
	# create the material and attach it to the skybox
	try:
		mat = Blender.Material.Get("skybox")
	except NameError:
		mat = Blender.Material.New("skybox")
	mat.mode |= Blender.Material.Modes.SHADELESS
	mat.mode |= Blender.Material.Modes.TEXFACE
	skybox.setMaterials([mat])

	# save current settings that we'll have to change during the execution
	render = sc.getRenderingContext()
	prevImageType = render.imageType
	prevAspectX = render.aspectX
	prevAspectY = render.aspectY
	prevSizeX = render.sizeX
	prevSizeY = render.sizeY
	prevCamera = sc.objects.camera

	# create the camera data
	try:
		camDat = Blender.Camera.Get(camDatName)
	except NameError:
		camDat = Blender.Camera.New()
		camDat.name = camDatName
		camDat.lens = 16.0

	# create the camera object and make it the active cam
	camExist = False
	for ob in sc.objects:
		if ob.name == camObName:
			camExist = True
			camOb = ob
	if not camExist:
		camOb = sc.objects.new(camDat, camObName)
	#sc.setCurrentCamera(camOb)
	sc.objects.camera = camOb
	sc.objects.camera.loc = pos

	# prepare to render
	render.aspectX = render.aspectY = 1
	render.sizeX = render.sizeY = texSize
	render.imageType = Blender.Scene.Render.PNG
	fileExt = ".png"
	filePref = "skybox_"

	# rendering
	# front
	camOb.rot = [math.radians(90),0,0]
	render.render()
	render.saveRenderedImage(filePref+"front")
	
	# left
	camOb.rot = [math.radians(90),0,math.radians(90)]
	render.render()
	render.saveRenderedImage(filePref+"left")
	
	# right
	camOb.rot = [math.radians(90),0,math.radians(-90)]
	render.render()
	render.saveRenderedImage(filePref+"right")
	
	# back
	camOb.rot = [math.radians(90),0,math.radians(-180)]
	render.render()
	render.saveRenderedImage(filePref+"back")
	
	# top (Irrlicht is different here)
	angleZ = 180
	if targetPlatform == "irrlicht":
		angleZ = 90
	camOb.rot = [math.radians(180),0,math.radians(angleZ)]
	render.render()
	render.saveRenderedImage(filePref+"top")
	
	# bottom (Irrlicht is different here)
	angleZ = 0
	if targetPlatform == "irrlicht":
		angleZ = 90
	camOb.rot = [0,0,math.radians(angleZ)]
	render.render()
	render.saveRenderedImage(filePref+"bottom")

	# apply textures to faces
	#first = True
	for f in me.faces:
		# front face pointing towards negative Y
		if f.no[1] <= -1.0:
			imgName = filePref+"front"+fileExt
		# left face pointing towards positive x
		elif f.no[0] >= 1.0:
			imgName = filePref+"left"+fileExt
		# right face pointing towards negative x
		elif f.no[0] <= -1.0:
			imgName = filePref+"right"+fileExt
		# back face pointing towards positive y
		elif f.no[1] >= 1.0:
			imgName = filePref+"back"+fileExt
		# top face pointing towards negative z
		elif f.no[2] <= -1.0:
			imgName = filePref+"top"+fileExt
		# last face, bottom
		else:
			imgName = filePref+"bottom"+fileExt
		#if first:
		#	first = False
		#	print imgName

		# now that we got the name of the image, apply it to the current face
		try:
			img = Blender.Image.Get(imgName)
			img.reload()
		except NameError:
			img = Blender.Image.Load(render.getRenderPath()+imgName)
		f.image = img

	# put skybox back at the origin
	skybox.loc = pos

	# restore all user settings that have been changed during execution
	render.imageType = prevImageType
	render.aspectX = prevAspectX
	render.aspectY = prevAspectY
	render.sizeX = prevSizeX
	render.sizeY = prevSizeY
	sc.objects.camera = prevCamera

	# redraw (Blender API says only use Blender.Draw.Redraw() inside windowing loop
	#Blender.Redraw()
	Blender.Draw.Redraw()