#!BPY

"""
Name: 'OBJ'
Blender: 233
Group: 'Import'
Tip: 'Import from OBJ file format. (.obj)'
"""

######################################################
# OBJ Importer
# 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, Texture, sys
from Blender.BGL import *
from Blender.Draw import *
from Blender.Window import *
from Blender.Image import *
from Blender.Material import *
from Blender.Texture import *
from Blender.sys import *


import sys, struct, string
from types import *

import os
from os import path

# 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")

#Globals
g_scale=Create(1.0)


# Events
EVENT_NOEVENT=1
EVENT_LOAD_OBJ=2
EVENT_CHOOSE_FILENAME=3
EVENT_EXIT=100

######################################################
# Callbacks for Window functions
######################################################
def filename_callback(input_filename):
	global g_obj_filename
	g_obj_filename.val=input_filename

######################################################
# GUI Loader
######################################################
def draw_gui():
	global g_scale
	global g_obj_filename
	global g_scale_slider
	global EVENT_NOEVENT,EVENT_LOAD_OBJ,EVENT_CHOOSE_FILENAME,EVENT_EXIT

	########## Titles
	glClear(GL_COLOR_BUFFER_BIT)
	glRasterPos2d(8, 103)
	Text("OBJ loader")

	######### Parameters GUI Buttons
	g_obj_filename = String("OBJ file to load: ", 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("Load",EVENT_LOAD_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 EVENT_NOEVENT,EVENT_LOAD_OBJ,EVENT_EXIT

	######### Manages GUI events
	if (evt==EVENT_EXIT):
		Exit()
	elif (evt==EVENT_CHOOSE_FILENAME):
		FileSelector(filename_callback, "OBJ File Selection")
	
	#load the object
	elif (evt==EVENT_LOAD_OBJ):
		if (g_obj_filename.val == "model"):
			Exit()
		else:
			load_obj(g_obj_filename.val)
			Blender.Redraw()
			Exit()

Register(draw_gui, event, bevent)

######################################################
# IMPORT
######################################################

def load_materials(filename, possible_path):

	full_filename=""

	if (os.path.isfile(filename)):
		full_filename = filename
	elif (os.path.isfile(possible_path+sep+filename)):
		full_filename = possible_path+sep+filename

	if(len(full_filename) > 0):
		file=open(full_filename,"r")

		lines=file.readlines()

		for counter in xrange(0,len(lines)):
			current_line=lines[counter]

			if current_line[0]=="#":
				#found a comment
				pass

			elif current_line[0]=="n":
				data=current_line.split()
				if data[0]=="newmtl":
					#create a new material from the name
					mat=Material.New(data[1])
					#remove leading whitespace
					current_line=string.lstrip(lines[counter])
					#look for the values and fill them in
					while counter+1 < len(lines) and string.find(lines[counter+1], "newmtl") == -1:
						counter=counter+1
						current_line=string.lstrip(lines[counter])
						data=current_line.split()
						if (len(data) == 0):
							continue
						if data[0]=="Ka":
							mat.setMirCol(float(data[1]), float(data[2]), float(data[3]))
						elif data[0]=="Kd":
							mat.setRGBCol(float(data[1]), float(data[2]), float(data[3]))
						elif data[0]=="Ks":
							mat.setSpecCol(float(data[1]), float(data[2]), float(data[3]))
						elif data[0]=="Ns":
							mat.setSpec(float(data[1])/100)
						elif data[0]=="illum":
							#do something with illum?  mat.Add or mat.Emit???
							pass
						elif data[0]=="map_Kd":
							texture_name = data[-1]
							texture_path=""
							if (os.path.isfile(texture_name)):
								texture_path=texture_name
							elif (os.path.isfile(possible_path+sep+texture_name)):
								texture_path=possible_path+sep+texture_name
							else:
								print "Couldn't load texture: ", texture_name

							if len(texture_path) > 0:
								texture = Texture.New(texture_name)
								image = Image.Load(texture_path)
								texture.setType("Image")
								texture.setImage(image)

								mat.clearTexture(0)
								mat.setTexture(0, texture, TexCo.UV)
	else:
		print "Non-Fatal Error:  Can't find the material file: ",filename

def load_maps(filename):
	map_filename=filename[:-4]
	temp=map_filename+".map"
	if (os.path.isfile(temp)):
		file=open(temp,"r")

		lines=file.readlines()
		for counter in xrange(0,len(lines)):
			current_line=lines[counter]

			if (current_line[0]=="#" or current_line[0]=="\n"):
				#found a comment
				pass
			else:
				data=current_line.split()
				if data[0]=="newmap":
					#find out the filename
					next_data=lines[counter+1].split()
					if next_data[0]=="Ka":
						#create the new map from the filename
						new_image=Image.Load(next_data[1])
						#add the maps name to the image struct, usually the filename
						new_image.name=data[1]
						counter=counter+1
				else:
					pass


def load_obj (obj_filename):
	load_maps(obj_filename)
	load_materials((splitext(obj_filename))[0]+".mtl", dirname(obj_filename))
	file=open(obj_filename,"r")

	just_found_group=0
	mesh = NMesh.New()
	mesh_material_index=-1

	current_material=""

	uv_list=[]
	uv_coord=[]

	no_list=[]
	no_coord=[]

	v_count=0
	vt_count=0
	vn_count=0

	vert_offset=0


	lines=file.readlines()
	for counter in xrange(0,len(lines)):
		current_line=lines[counter]

		if (current_line[0]=="#" or current_line[0]=="\n"):
			#found a comment
			pass

		elif current_line[0]=="m":
			data=current_line.split()
			if (data[0] == "mtllib"):
				for i in range(1, len(data)):
					# if material-filename is equal to obj-filename don't
					# load as it's already loaded in the beginning
					if (data[i] != ((splitext(obj_filename))[0]+".mtl")):
						load_materials(data[i], dirname(obj_filename))

		elif current_line[0]=="v":
			if current_line[1]==" ":
				#found a vertex
				data=current_line.split()
				v=NMesh.Vert(float(data[1])*g_scale.val, float(data[2])*g_scale.val, float(data[3])*g_scale.val)
				mesh.verts.append(v)
				v_count=v_count+1

			elif current_line[1]=="t":
				#found a texture coordinate
				data=current_line.split()
				u=(float(data[1]))
				v=(float(data[2]))
				uv_coord=(u,v)
				uv_list.append(uv_coord)
				vt_count=vt_count+1

			elif current_line[1]=="n":
				#found a vertex normal
				data=current_line.split()
				x=(float(data[1]))
				y=(float(data[2]))
				z=(float(data[3]))
				no_coord=(x,y,z)
				no_list.append(no_coord)
				vn_count=vn_count+1

		elif current_line[0]=="f":
			data=current_line.split()

			#how many idexes are there ie. is this a tri or a quad
			face_counter=len(data)
			#create a new blank face
			face = NMesh.Face()

			#for each index group build the face
			for i in xrange(1,face_counter):
				vert = NMesh.Vert()
				index=data[i].split("/")

				#vertex indexes only
				if len(index)==1:
					#check for relative vertices
					if (int(index[0])<0):
						vertex_index=v_count+int(index[0])-vert_offset #add because it's negative
					else:
						vert_index=int(index[0])-vert_offset
					face.v.append(mesh.verts[(vertex_index-1)])

				#vertex and texture indexes
				if len(index)==2:
					#check for relative vertices
					if (int(index[0])<0):
						vertex_index=v_count+int(index[0])-vert_offset #add because it's negative
					else:
						vertex_index=int(index[0])-vert_offset
					face.v.append(mesh.verts[(vertex_index-1)])
					#check for relative uv_verts
					if (int(index[1])<0):
						uv_index=vt_count+int(index[1]) #ditto on the add thing
					else:
						uv_index=int(index[1])
					face.uv.append(uv_list[(uv_index-1)])

				#vertex, texture and normal indexes
				if len(index)==3:
					#check for relative vertices
					if (int(index[0])<0):
						vertex_index=v_count+int(index[0])-vert_offset #add because it's negative
					else:
						vertex_index=int(index[0])-vert_offset
					face.v.append(mesh.verts[(vertex_index-1)])
					if index[1]!="":
						#check for relative uv_verts
						if (int(index[1])<0):
							uv_index=vt_count+int(index[1]) #ditto on the add thing
						else:
							uv_index=int(index[1])
						face.uv.append(uv_list[(uv_index-1)])
					if index[2]!="":
						#don't worry about vertex normals yet, blender will recalculate them anyways (i think)
						pass

			#are we using a material
			if mesh_material_index!=-1:
				face.mat=mesh_material_index
			elif len(current_material) > 0:
				mat_list=Material.Get()
				#find the material we want to use in the list
				for this_mat in mat_list:
					if current_material==this_mat.name:
						#found it
						#try to add the material to this mesh if it's not already there
						found_material=0
						mesh_material_index=-1
						i=0
						for mat in mesh.materials:
							if current_material==mat.name:
								found_material=1
								mesh_material_index=i
								break
							i += 1

						if not found_material:
							mesh.materials.append(this_mat)
							mesh_material_index=len(mesh.materials)-1
				if mesh_material_index!=-1:
					face.mat=mesh_material_index

			#mesh.hasFaceUV(1)
			mesh.faces.append(face)

		# Disabled until the issues with it are fixed
		elif 0 == 1: #current_line[0]=="g":
			#found group name
			if just_found_group==1:
				#got another group, put previous one in blender
				mesh_obj = NMesh.PutRaw(mesh)
				#cursor_pos=Blender.Window.GetCursorPos()
				#mesh_obj.setLocation(float(cursor_pos[0]),float(cursor_pos[1]),float(cursor_pos[2]))
			# Creates a new mesh
			mesh = NMesh.New()
			data=current_line.split()
			if (len(data) >= 2):
				mesh.name=data[1]
			just_found_group=1
			vert_offset=v_count
			mesh_material_index=-1
			#adjust vert offset

			print "got group: ", mesh.name

		#treat objects just like groups
		elif current_line[0]=="o":
			#found an object
			if just_found_group==1:
				#got another group, put previous one in blender
				mesh_obj = NMesh.PutRaw(mesh)
			mesh = NMesh.New()
			data=current_line.split()
			mesh.name=data[1]
			just_found_group=1
			vert_offset=v_count
			mesh_material_index=-1

		#check for the first letter of usemtl
		elif current_line[0]=="u":
			#supposed to draw with this material, is it in the list
			data=current_line.split()
			#be sure this is the right command
			if data[0]=="usemtl":
				current_material=data[1]
				mesh_material_index=-1

		else:
			#don't know what we got
			pass

	#########Puts the last mesh into blender and close file
	mesh_obj = NMesh.PutRaw(mesh)
	file.close()

	#put the object at the cursor position
	cursor_pos=Blender.Window.GetCursorPos()
	mesh_obj.setLocation(float(cursor_pos[0]),float(cursor_pos[1]),float(cursor_pos[2]))
