#!BPY

"""
Name: 'OBJ'
Blender: 233
Group: 'Export'
Tip: 'Export to OBJ file format. (.obj)'
"""

######################################################
# OBJ Exporter
# By:  Bob Holcomb
# Date: 19 Jan 04
# Ver: 0.6
######################################################
# This script imports a text OBJ file and the materials
#  into blender for editing.  Hopefully
# this will make it into a future version as an import
# feature of the menu.  Loader is based on OBJ loader
# from www.gametutorials.com-Thanks DigiBen!  The export
# portion is based on blenderobjex.py by Christoph Frick
# <rid@zefix.tv>
######################################################

######################################################
# Importing modules
######################################################

import Blender
from Blender import NMesh, Scene, Object, Draw, Image, Material
from Blender.BGL import *
from Blender.Draw import *
from Blender.Window import *
from Blender.Image import *
from Blender.Material import *

import sys, struct, string
from types import *

import os
from os import path

def point_by_matrix(p, m):
	return [p[0] * m[0][0] + p[1] * m[1][0] + p[2] * m[2][0] + m[3][0],
	p[0] * m[0][1] + p[1] * m[1][1] + p[2] * m[2][1] + m[3][1],
	p[0] * m[0][2] + p[1] * m[1][2] + p[2] * m[2][2] + m[3][2]]

# HACK -- it seems that some Blender versions don't define sys.argv,
# which may crash Python if a warning occurs.
if not hasattr(sys, "argv"): sys.argv = ["???"]

# Import globals
g_obj_filename=Create("model")
g_material_filename=Create("material")
#search buttons
g_filename_search=Create("model")
g_material_search=Create("material")

#Globals
g_scale=Create(1.0)


# Events
EVENT_NOEVENT=1
EVENT_CHOOSE_FILENAME=3
EVENT_CHOOSE_MATERIAL=4
EVENT_EXPORT_OBJ=5
EVENT_EXIT=100

######################################################
# Callbacks for Window functions
######################################################
def filename_callback(input_filename):
	global g_obj_filename
	g_obj_filename.val=input_filename

def material_callback(input_material):
	global g_material_filename
	g_material_filename.val=input_material

######################################################
# GUI Loader
######################################################
def draw_gui():
	global g_scale
	global g_obj_filename
	global g_material_filename
	global g_scale_slider
	global EVENT_NOEVENT,EVENT_EXPORT_OBJ,EVENT_CHOOSE_FILENAME,EVENT_CHOOSE_MATERIAL,EVENT_EXIT

	########## Titles
	glClear(GL_COLOR_BUFFER_BIT)
	glRasterPos2d(8, 103)
	Text("OBJ exporter")

	######### Parameters GUI Buttons
	g_obj_filename = String("OBJ file to export: ", EVENT_NOEVENT, 10, 55, 210, 18,
			g_obj_filename.val, 255, "OBJ file to load")
	########## obj File Search Button
	Button("Search",EVENT_CHOOSE_FILENAME,220,55,80,18)


	########## Scale slider-default is 1
	g_scale= Slider("Scale Factor: ", EVENT_NOEVENT, 10, 75, 210, 18,
		1.0, 0.001, 10.0, 1, "Scale factor for obj Model");

	######### Draw and Exit Buttons
	Button("Export",EVENT_EXPORT_OBJ, 10,10,80,18)
	Button("Exit",EVENT_EXIT , 170, 10, 80, 18)

def event(evt, val):
	if (evt == QKEY and not val):
		Exit()

def bevent(evt):
	global g_obj_filename
	global g_material_filename
	global EVENT_NOEVENT,EVENT_EXPORT_OBJ,EVENT_EXIT

	######### Manages GUI events
	if (evt==EVENT_EXIT):
		Exit()
	elif (evt==EVENT_CHOOSE_FILENAME):
		FileSelector(filename_callback, "OBJ File Selection")
	elif (evt==EVENT_CHOOSE_MATERIAL):
		FileSelector(material_callback, "Material Selection")
	
	#export the object and materials
	elif (evt==EVENT_EXPORT_OBJ):
		#check if the user was lazy and didn't pick a file
		if(g_obj_filename.val=="model"):
			result=Blender.Draw.PupMenu("No filename entered.  Use Scene Name?%t|Yes|No")
			if(result==1):
				export_obj(g_obj_filename.val)
				Exit()
		#are we overwriting a file?
		if (os.path.isfile(g_obj_filename.val)):
			temp=os.path.basename(g_obj_filename.val)
			result=Blender.Draw.PupMenu("Overwrite file: "+temp+"?%t|Yes|No")
			if(result==1):
				#strip ".obj" off the filename of the overwriten file.  It's added later
				if(g_obj_filename.val[:-4]==".obj"):
					temp=g_obj_filename.val[:-4]
				export_obj(temp)
				Exit()
		else:
			if(g_obj_filename.val[:-4]==".obj"):
				temp=g_obj_filename.val[:-4]
			export_obj(temp)
			Exit()

Register(draw_gui, event, bevent)

######################################################
# EXPORT
######################################################
def export_obj(filename):
	#open the current scene
	scene = Blender.Scene.GetCurrent()
	#if the user was lazy and didn't add a filename,
	#just name the files after the scene name
	#hope they weren't lazy there too.
	if filename=="model":
		filename=scene.getName()
		#basefilename and /path/filename are the same
		bfilename=filename
	else:
		#extract the actual filename from the
		#entire path of the /path/filename
		bfilename = os.path.basename(filename)

	#open the files up for writing
	objfile = open( filename+".obj", "w" )
	mtlfile = open( filename+".mtl", "w" )
	mapfile = open( filename+".map", "w" )

	#header in the object file
	objfile.write( "# \n" );
	objfile.write( "#Extracted from Blender \n")
	objfile.write( "#OBJ import/export script ver. 0.8 \n")
	objfile.write( "mtllib "+bfilename+".mtl\n")
	objfile.write( "maplib "+bfilename+".map\n")

	#initialize some variables
	mtllib = {}
	vs = {}
	vns = {}
	vts = {}
	mats = {}
	maps = {}
	s_count = 1
	t_count = 1
	n_count = 1

	current_material = ""

	#get a list of the objects in the scene
	obj_list=scene.getChildren()

	#loop through the objects in the scene
	for current_obj in obj_list:
		if current_obj.getType()=="Mesh":
			objfile.write("#Mesh name: "+current_obj.name+"\n")
			#write our first object
			objfile.write("o "+current_obj.name+"\n")
			#get the mesh data
			mesh = current_obj.getData()
			matrix = current_obj.getMatrix()
			for vertex in mesh.verts:
				co = point_by_matrix(vertex.co, matrix)
				v = "v %f %f %f\n" % (co[0]*g_scale.val,co[1]*g_scale.val,co[2]*g_scale.val)
				if not vs.has_key(v):
					vs[v] = s_count
					s_count = s_count + 1
					objfile.write( v)
			#output number of verts
			objfile.write("# number of vertices: "+str(s_count-1)+"\n")


			#loop through again for the normals
			for vertex in mesh.verts:
				vn = "vn %f %f %f\n" % (vertex.no[1],vertex.no[2],vertex.no[0])
				if not vns.has_key(vn):
					vns[vn] = n_count
					n_count = n_count +1
					objfile.write( vn)
			objfile.write("# number of normals: "+str(n_count-1)+"\n")

			#loop through again for the texture coords
			#if the model has "sticky" UV coords (stored in the vertex info)
			if mesh.hasVertexUV():
				for vertex in mesh.verts:
					vt = "vt %f %f\n" % (vertex.uvco[0],vertex.uvco[1])
					if not vts.has_key(vt):
						vts[vt] = t_count
						t_count = t_count +1
						objfile.write( vt )
				objfile.write("# number of texture coords: "+str(t_count-1)+"\n")

			#otherwise the UV info is stored in the face, only pick one UV for a vertex
			elif mesh.hasFaceUV():
				for face in mesh.faces:
					for uv_coord in face.uv:
						vt="vt %f %f\n" % (uv_coord[0], uv_coord[1])
						if not vts.has_key(vt):
							vts[vt]=t_count
							t_count=t_count+1
							objfile.write(vt)
				objfile.write("# number of texture coords: "+str(t_count-1)+"\n")


			#what materials (if any) does this mesh have
			for material in mesh.materials:
				if not mats.has_key( material.name ):
					print "Material name: ", material.name
					mtlfile.write( "newmtl %s\n" % (material.name) )
					mtlfile.write( "Ka %f %f %f\n" % (material.mirCol[0],material.mirCol[1],material.mirCol[2]))
					mtlfile.write( "Kd %f %f %f\n" % (material.rgbCol[0],material.rgbCol[1],material.rgbCol[2]))
					mtlfile.write( "Ks %f %f %f\n" % (material.specCol[0],material.specCol[1],material.specCol[2]))
					mtlfile.write( "Ns %f\n" % (material.spec))
					mtlfile.write( "\n" );
					mats[ material.name ] = 1;

			"""#what textures does this mesh use
			for this_face in mesh.faces:
				mesh_image=this_face.image.getName()
				if mesh_image!="":
					if not maps.has_key(mesh_image):
						maps[mesh_image]=1
						mapfile.write("newmap %s\nKa %s\n\n" % (mesh_image, mesh_image))"""


			current_mesh_image="first_time"
			#write the face info to the file
			for current_face in mesh.faces:
				#put what texture map this face uses
				#don't want to repeat if we use the same texture map again
				if (current_face.image != None):
					mesh_image=current_face.image.getName()
					if(mesh_image!=current_mesh_image):
						if(mesh_image!=""):
							objfile.write("usemap "+mesh_image+"\n")
							current_mesh_image=mesh_image
						else:
							objfile.write("usemap (null) \n")
							current_mesh_image=mesh_image

				if (current_face.materialIndex < len(mesh.materials)):
					material_name=mesh.materials[current_face.materialIndex].name
					if (material_name != current_material):
						objfile.write("usemtl "+material_name+"\n")
						current_material=material_name

				objfile.write( "f " )
				#I'm only supporting tri's right now
				for i in range(0, len(current_face.v)):
					face_line = ""
					#add a vertex index to the face line
					co = point_by_matrix(current_face.v[i], matrix)
					v = "v %f %f %f\n" % (co[0]*g_scale.val, co[1]*g_scale.val, co[2]*g_scale.val)
					face_line = face_line + "%i" % (vs[v])

					#Is it a vertex UV or a face UV
					#add a texture index to the face line
					hasUV=0
					if mesh.hasVertexUV():
						vt="vt %f %f\n" %(current_face.v[i].uvco[0], current_face.v[i].uvco[1])
						hasUV=1
					elif mesh.hasFaceUV():
						uv_coord=current_face.uv[i]
						vt="vt %f %f\n" % (uv_coord[0], uv_coord[1])
						hasUV=1

					if (hasUV):
						face_line=face_line+ "/%i" % (vts[vt])
					else:
						face_line=face_line+ "/"

					#add a normal index to the face info
					vn = "vn %f %f %f\n" % (current_face.v[i].no[1], current_face.v[i].no[2], current_face.v[i].no[0]);
					face_line = face_line + "/%i"%(vns[vn]);

					face_line = face_line + " ";
					objfile.write(face_line);
				objfile.write( "\n" );



	objfile.close()
	mtlfile.close()
	mapfile.close()