#!BPY

""" Registration info for Blender menus
Name: 'VOS COD'
Blender: 232
Group: 'Export'
Submenu: 'Preview' preview
Submenu: 'Save to file' saveToFile
Tip: 'Export all objects to VOS COD (see www.interreality.org/software/vr/blender)'
"""

__author__ = "Peter Amstutz and Reed Hedges"
__url__ = ("blender", "elysiun", "Author's site, http://interreality.org")
__version__ = "1.14"

__bpydoc__ = """\
This script exports meshes as A3DL objects 
to the Virtual Object System COD file format.
"""

# $Id: blenderExportCOD.py,v 1.16 2006/04/16 00:41:12 tetron Exp $
#
# This script will export your Blender scene to a VOS COD (set the filename 
# below).  This COD can be loaded by the "vosworld" server, and other tools.
#
# This script is written for Blender version 2.28 and later. Older versions 
# will not work.
#
# To run this script in Blender 2.32, simply choose it from the File->Export
# menu. To run it in Blender 2.31 and earlier, load it into the Blender text 
# editor, (use the [-] button # in the header of the text editor window) and 
# press ALT+P. Unless you have set the OutputFilename variable below, a file 
# selector will appear. Unfortunately we have no control over the default 
# filename: be careful not to overwrite your .blend file!
#
# Note that in VOS, "ZX" is the horizontal plane, and Y is the vertical axis,
# while in Blender, "YX" is the horizontal plane ("Top" view), and Z is vertical.
# This script re-maps the axes, so that the "Top" view in Blender becomes the
# "Top" in VOS as well.
#
# If you want to help improve this script, please join us! 
# You can get in touch by joining the mailing lists or IRC channel:
#	http://interreality.org/lists
#
#######################
# Main TODO items (see README for more): 
#	* It currently saves a copy of everything. (Most notably images)
#	 It needs to only save one instance of each data block and link the
#	 objects correctly.
#   * It ought to deal with other texture mapping modes
#   * export all texture layers
#   * export faces marked two-sided (by doubling the polygon)
#   * deal with packed images
#   * parented objects
#######################


import sys
# bug on some mac platforms:
if (sys.platform == "darwin") :
	sys.path.append("/System/Library/Frameworks/Python.framework/Versions/2.3/lib/python2.3/lib-dynload/")
import struct, math, random, os, Blender, tempfile


#######################

# Configuration, Either here (pre-2.32) or from the submenus (post-2.32)

### Set to True to suppress messages other than errors and importent warnings:
Quiet = False

### Set to true to dump all kinds of debugging info out
Debug = False

### "static" geometry will have better lighting but will be slower to load, and
### cannot be moved.  To override this setting for any particular object,
### put a $ at the beginning of the object's name for static geometry, and put 
### an ! at the beginning for dynamic geometry.  This value can be True of False
OutputStaticGeom = True

### "Hard" transforms are not affected by transformations in parent 3D objects,
### and are usually effected in the client by transforming all of the mesh 
### vertices rather than placing the object in a separate transform.  Scaling
### is always "hard" though, since non-hard scaling usually screws up culling
### and collision detection.
### This value can be True or False
OutputHardTransform = False

### Set an output file. If empty (""), then you can select one
### with a file selector
OutputFilename = ""

### Used to replace small numbers with 0 sometimes:
EPSILON = 0.000001
#######################




OutputStaticGeom = False
OutputHardTransform = False



### USER CONFIG TO ADD: 
### Name of the World object to use for some sector proporties. If it doesn't
### exist, then no sector will be included in the COD.
### There is also one special hack to make the Blender and VOS "defaults" line
### up: if this value is "World", then the sector will be created with the
### name "world" (lower case). (But the Blender object should be named "World".)
WorldName = "World"
###
### World title, or from a TXT.
###
### World description or from a TXT.



# Helpful hints to help the user avoid common problems:
NoLights = True


# Vobjects already created are stored here to reuse if a Blender object
# reuses them. They are lists of (name, vobj) pairs, or in the case of 
# MeshData, (name, ( vertices prop, texels prop, normals, polygons ).
MaterialsCache = ()
MeshDataCache = ()
ImagesCache = ()


# Functions for printing out messages
# TODO: draw them in a scrollable list on the blender window.

# Fatal error:
def print_error(msg):
	print msg

# Probably a problem:
def print_warning(msg):
	print msg

# Unimportant warnings and progress updates:
def print_notify(msg):
	if(not Quiet): 
		print msg

# Debug message:
def print_debug(msg):
	if( (not Quiet) and Debug):
		print msg


class Message:
	def __init__(self):
		self._method = ""
		self._fields = []

	def setMethod(self, method):
		self._method = method

	def addField(self, key, value):
		self._fields.append([key, value])

	def writeToCOD(self, thefile):
		thefile.write(struct.pack(('>H%isH' % len(self._method)), len(self._method), self._method, len(self._fields)))
		for f in self._fields:
			thefile.write(struct.pack(('>H%isL%is' % (len(f[0]), len(f[1]))),
									  len(f[0]), f[0],
									  len(f[1]), f[1]))

Vobject_names = {}
def uniqueName(name):
	if name == "":
		name = str(random.randint(1,1000000))
	while Vobject_names.has_key(name):
		name = name + "_" + str(random.randint(1,1000000))
	return name

class Vobject:
	def __init__(self, name):
		self._name = uniqueName(name)
		Vobject_names[name] = self
		self._types = []
		self._messages = []
		self._children = []
		self._codposition = 0
		
	def getName(self):
		return self._name

	def addType(self, type):
		self._types.append(type)

	def addMessage(self, msg):
		self._messages.append(msg)

	def addChild(self, contextname, child):
		self._children.append([contextname, child])

	def getChildren(self):
		return self._children

	def setCODPosition(self, pos):
		self._codposition = pos

	def writeDescToCOD(self, thefile):
		thefile.write(struct.pack(('>H%isLH' % len(self._name)), len(self._name),
								  self._name, self._codposition, len(self._types)))
		for t in self._types:
			thefile.write(struct.pack(('>H%is' % len(t)), len(t), t))
		thefile.write(struct.pack('>H', len(self._messages)))
		for m in self._messages:
			m.writeToCOD(thefile)

	def writeChildrenToCOD(self, thefile):
		thefile.write(struct.pack(('>H%isL' % len(self._name)), len(self._name), self._name, len(self._children)))
		for c in self._children:
			thefile.write(struct.pack(('>H%isH%is' % (len(c[0]), len(c[1].getName()))),
									  len(c[0]), c[0], len(c[1].getName()), c[1].getName()))		

class COD:
	def __init__(self):
		self._vobjects = []

	def addVobject(self, v):
		self._vobjects.append(v)

	def addVobjectTree(self, v):
		self._vobjects.append(v)
		for c in v.getChildren():
			self.addVobjectTree(c[1])
		
	def writeCOD(self, filename):
		codoutput = file(filename, "wb")
		codoutput.write(struct.pack(">4sbbL","VOS", 1, 0, len(self._vobjects)))
		for v in self._vobjects:
			v.writeDescToCOD(codoutput)
		codoutput.write(struct.pack(">L", len(self._vobjects)))
		for v in self._vobjects:
			v.writeChildrenToCOD(codoutput)
		codoutput.close()

class Property(Vobject):
	def __init__(self, name):
		Vobject.__init__(self, name)
		self.addType("property:property")
		
	def setPropValue(self, val, datatype = "?"):
		m = Message()
		m.setMethod("property:replace")
		m.addField("data", val)
		m.addField("datatype", datatype)
		self.addMessage(m)

class Texture(Vobject):
	def __init__(self, name):
		Vobject.__init__(self, name)
		self.addType("a3dl:texture")
	
	def setImage(self, val, datatype = "?"):
		v = Property("image")
		v.setPropValue(val, datatype)
		self.addChild("a3dl:image", v)

	def setShaded(self, val):
		if (val == False):
			v = Property("shaded")
			v.setPropValue("false", "boolean")
			self.addChild("a3dl:shaded")

		
class Material(Vobject):
	def __init__(self, name):
		Vobject.__init__(self, name)
		self.addType("a3dl:material")
		
	def setColor(self, r, g, b):
		v = Property("color")
		v.setPropValue(("%f %f %f" % (r, g, b)), "list: float")
		self.addChild("a3dl:color", v)

	def setTexture(self, texture):
		self.addChild("a3dl:texture", texture)

	def setTransparency(self, t):
		if(t != 0):
			p = Property("transparency")
			p.setPropValue( ("%f" % (t)), "float")
			self.addChild("a3dl:transparency", p)

	def setAlpha(self, a):
		self.setTransparency(1.0 - a)

	
class Object3D(Vobject):
	def __init__(self, name):
		Vobject.__init__(self, name)
		self.addType("a3dl:object3D")
		
	def setPosition(self, pos):
		v = Property("position")
		v.setPropValue(pos, "list: float")
		if(OutputHardTransform):
			self.addChild("a3dl:hardposition", v)
		else:
			self.addChild("a3dl:position", v)

	def setOrientation(self, ori):
		v = Property("orientation")
		v.setPropValue(ori, "list: float")
		if(OutputHardTransform):
			self.addChild("a3dl:hardorientation", v)
		else:
			self.addChild("a3dl:orientation", v)

	def setScaling(self, scale):
		v = Property("scaling")
		v.setPropValue(scale, "list: float")
		#if(OutputHardTransform):
		self.addChild("a3dl:hardscaling", v)
		#else:
		#self.addChild("a3dl:scaling", v)

	def addMaterial(self, material):
		self.addChild("a3dl:material", material)

class PolygonMesh(Object3D):
	def __init__(self, name):
		Object3D.__init__(self, name)
		self._types = []
		self.addType("a3dl:object3D.polygonmesh")
		if (name[0] == '$') or ( (name[0] != '!') and OutputStaticGeom ):
			print " (static mesh)"
			self.addType("a3dl:static")
		else:
			print " (dynamic mesh)"

	def setVertices(self, verts):
		vertstr = ""
		i = 0
		for v in verts:
			#print "setVertices: packing vertex %d into string:" % (i)
			#print v
			vertstr += struct.pack(">fff", v[0], v[2], v[1])
			i += 1
		p = Property("vertices")
		p.setPropValue(vertstr)
		self.addChild("a3dl:vertices", p)

	def setNormals(self, normals):
		str = ""
		for v in normals:
			str += struct.pack(">fff", v[0], v[2], v[1])
		p = Property("normals")
		p.setPropValue(str)
		self.addChild("a3dl:normals", p)

	def setTexels(self, texels):
		vertstr = ""
		for t in texels:
			for u in t:
				vertstr += struct.pack(">f", u)
		p = Property("texels")
		p.setPropValue(vertstr)
		self.addChild("a3dl:texels", p)

	def setPolygons(self, polys):
		str = ""
		polycount = 0
		for p in polys:
			#print "packing polygon #%d with %d vertices into string:" % (polycount, len(p))
			#print p
			polycount += 1
			str += struct.pack(">B", len(p))
			for c in p:
				str += struct.pack(">l", c)
		p = Property("polygons")
		p.setPropValue(str, "packed/polygon-list")  
			#note: datatype is important here! a3dl behaves differently if
			# "packed/polygon-list" or "packed/triangle-array".
		self.addChild("a3dl:polygons", p)
	
	def setDoubleSided(self):
		p = Property("doublesided")
		p.setPropValue("true", "boolean")
		self.addChild("a3dl:doublesided", p)

#	def setPolygons(self, tris):
		#tristr = ""
		#for t in tris:
		#	tristr += struct.pack(">lll", t[0], t[1], t[2])
		#p = Property("polygons")
		#p.setPropValue(tristr, "packed/triangle-array")
		#self.addChild("a3dl:polygons", p)



class Sector(Vobject):
	def __init__(self, name):
		Vobject.__init__(self, name)
		self.addType("a3dl:sector")
		print "created sector named \"" + name + "\"."

	# Sector is different. It adds it's properties individually
	# to the COD.  Then you can add the sector to the COD yourself,
	# when it's ready.
	def exportSectorProperties(self, worldObj, cod):
		global NoLights
		try:
			amb = worldObj.getAmb()
			if( (amb[0] != 0) or (amb[1] != 0) or (amb[2] != 0) ):
				p = Property("ambient-light")
				p.setPropValue(("%f %f %f" % (amb[0], amb[1], amb[2])), "float-vector")
				self.addChild("a3dl:ambient-light", p)
				cod.addVobject(p)
				NoLights = False
		except:
			print "Error getting ambient light!"

		try:
			mist = worldObj.getMist()[0]
			if(mist != 0):
				c = Property("mist-color")
				c.setPropValue(("0.0 0.0 0.0"), "float-vector")
				self.addChild("a3dl:fog-color", c)
				cod.addVobject(c)
				d = Property("mist-density")
				d.setPropValue(("%f" % mist), "float")
				self.addChild("a3dl:fog-density", d)
				cod.addVobject(d)
		except:
			print "Error getting mist!"


def matrixToQuaternion(m):
	tr = m[0][0] + m[1][1] + m[2][2] + 1
	if tr > 0:
		s = 0.5 / math.sqrt (tr)
		w = 0.25 / s
		x = (m[2][1] - m[1][2]) * s
		y = (m[0][2] - m[2][0]) * s
		z = (m[1][0] - m[0][1]) * s
	else:
		i = 0
		if m[1][1] > m[0][0]: i = 1
		if m[2][2] > m[i][i]: i = 2

 		if i == 0:
			s = math.sqrt ((m[0][0] - (m[1][1] + m[2][2])) + 1.0)
			x = s * 0.5
			if s != 0.0: s = 0.5 / s
			y = (m[1][2] + m[1][0]) * s
			z = (m[0][2] + m[2][0]) * s
			w = (m[1][2] - m[2][1]) * s

 		elif i == 1:
			s = math.sqrt ((m[1][1] - (m[2][2] + m[0][0])) + 1.0)
			y = 0.5 * s
			if s != 0.0: s = 0.5 / s
			x = (m[0][1] + m[1][0]) * s
			z = (m[1][2] + m[2][1]) * s
			w = (m[0][2] - m[2][0]) * s

 		elif i == 2:
			s = math.sqrt ((m[2][2] - (m[0][0] + m[1][1])) + 1.0)
			z = 0.5 * s
			if s != 0.0: s = 0.5 / s
			x = (m[0][2] + m[2][0]) * s
			y = (m[1][2] + m[2][1]) * s
			w = (m[0][1] - m[1][0]) * s

	square= x*x + y*y + z*z

	if square > 0.0: dist = 1.0 / math.sqrt(square)
	else: dist = 1
	
	x *= dist;
	y *= dist;
	z *= dist;
	if w > 1: w = w - 2
	elif w < -1: w = w + 2
	
	return [x, y, z, w]


def quaternionToAxisAngle(q):
	x, y, z, w = q[0], q[1], q[2], q[3]
	wtmp = ((w + 1) % 2) - 1
	w = 2.0 * math.acos(wtmp)
	sp = math.sin(w/2.0)
	if sp > .0000000001:
		x = x / sp
		y = y / sp
		z = z / sp
		w *= 180.0/math.pi
	else:
		x = y = 0
		z = 1
		w = 0

# 	d = math.sqrt(x*x + y*y + z*z)
# 	if d > 0:
# 		x = x / d
# 		y = y / d
# 		z = z / d
# 	else:
# 		x = y = 0
# 		z = 1
	
	return [x, y, z, w]


def invertQuat(q):
	d = 1.0 / (q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3])
	q[0] = -q[0] * d
	q[1] = -q[1] * d
	q[2] = -q[2] * d
	q[3] = -q[3] * d
	return q

def normalizeRotation(matrix, scale):
	return matrixMult([[1/scale[0], 0, 0], [0, 1/scale[1], 0], [0, 0, 1/scale[2]]], matrix)
	
def matrixToAxisAngle(m):
	return quaternionToAxisAngle(matrixToQuaternion(m))

def matrixToInvAxisAngle(m):
	return quaternionToAxisAngle(invertQuat(matrixToQuaternion(m)))

def compareVertices(a, b):
	return cmp(a[1:4], b[1:4])
	


# Extracts geometry information from the mesh suitable for use in A3DL.
# Arguments:
#   mesh	A Blender NMesh object
# Returns:
#   A tuple contatining four lists: vertices, texels, normals and polygons
#	   vertices	A list of triples defining vertex positions
#	   texels	  A list of UV coordinates each corresponding to a vertex in 
#				   'vertices' mapping the texture to the vertices
#	   normals	 A list of normal vectors (each corresponding to a polygon)
#	   polygons	A list of arrays, each with three or four indices into
#				   the vertices list, indicating mesh faces.
#
# TODO: avoid duplicates: Keep a list of unique combinations of 
#   vertex, exel and normal, then build properties from that.
#
def getGeometry(mesh):

	vertices = []
	texels = []
	normals = []
	polys = []

	vertex_count = 0
	face_count = 0
	for face in mesh.faces:
		if len(face.v) < 3:
			print "Warning: face %d only has %d vertices! skipping..." % (face_count, len(face.v))
			continue
		p = []
		face_vertex_count = 0

		# Collect vertex data for this face:
		# (reversed since a3dl and blender seem to have inverted conventions
		# for front/back of a polygon)
		reverse_vertex_list = face.v
		reverse_vertex_list.reverse()
		for v in reverse_vertex_list:
			vertices.append(v.co)  
			normals.append(v.no)   
			p.append(vertex_count) 
			vertex_count += 1
			face_vertex_count += 1
			polys.append(p)

		# Collect texture mapping for this face:
		# This ought to be polygon texturespace for each face, but for now, 
		# We'll just add a uv coord for each vertex.
		# TODO: check if mesh.hasVertexUV().
		if mesh.hasFaceUV():

			# Reverse UV coords since we reversed vertex indices 
			reverse_face_uv = face.uv
			reverse_face_uv.reverse()
			for uv in reverse_face_uv:
				t = (uv[0], 1-uv[1])
				texels.append(t)
		else:
			print "  (This mesh has no face UV texture mapping!)" 
		face_count += 1
#		if((face.mode & Blender.NMesh.FaceModes.TWOSIDE) && !(mesh.getMode() & Blender.NMesh.Modes.TWOSIDED)):
#		   TODO: double this face by adding an extra polygon

	return (vertices, texels, normals, polys)
	

def OLD_getGeometry(mesh):
	tmpvertices = []
	triangles = []
	count = 0
	for face in mesh.faces:
		firstvert = len(tmpvertices)
		for f in range(len(face)):
			uv = (0, 0)
			if len(face.uv) > f: uv = (face.uv[f][0], face.uv[f][1])
			tmpvertices.append((len(tmpvertices), (face[f][0], face[f][1], face[f][2]),
								uv, (face[f].no[0], face[f].no[1], face[f].no[2])))
		for f in range(2, len(face)):
			triangles.append([firstvert, firstvert + f, firstvert + f - 1])

	tmpvertices.sort(compareVertices)
	vertexmap = range(len(tmpvertices))
	cur = []
	vertices = []
	texels = []
	normals = []
	for tv in tmpvertices:
		if compareVertices(tv, cur):
			vertices.append(tv[1])
			texels.append(tv[2])
			normals.append(tv[3])
			cur = tv
		vertexmap[tv[0]] = len(vertices) - 1
	for t in triangles:
		(t[0], t[1], t[2]) = (vertexmap[t[0]], vertexmap[t[1]], vertexmap[t[2]])
	return (vertices, texels, normals, triangles)

def exportMesh(obj, cod):
	mesh = obj.data
	if(mesh == 0):
		print " Error: no object data"
		return 0
	trimesh = PolygonMesh(obj.getName())
	loc = obj.getLocation()
	trimesh.setPosition("%f %f %f" % (loc[0], loc[2], loc[1]))
	aa = matrixToAxisAngle(normalizeRotation(obj.getMatrix(), obj.size))
	trimesh.setOrientation("%f %f %f %f" % (aa[0], aa[2], aa[1], -aa[3]))
	trimesh.setScaling("%f %f %f" % (obj.size[0], obj.size[2], obj.size[1]))


	#print "max smooth angle = "  
	#print mesh.maxSmoothAngle
	#if(mesh.getMode() & Blender.NMesh.Modes.AUTOSMOOTH):
	#	print "autosmooth."

	if(mesh.getMode() & Blender.NMesh.Modes.TWOSIDED):
		print " (doublesided.)"
		trimesh.setDoubleSided()
	
	#if(material = MaterialsCache.find(mesh.materials[0].name)):
	if(False):
		print " (using previously created material vobject...)"
	else:
		print " (creating new material)..."
		material = Material("meshmaterial")
		hastexture = False
		if (len(mesh.materials) > 0):
			# Set base color
			material.setColor(mesh.materials[0].R, mesh.materials[0].G, mesh.materials[0].B)
			material.setAlpha(mesh.materials[0].getAlpha())
		
			#TODO: iterate over all materials and collect image layers
			textures = mesh.materials[0].getTextures()   
			# returns list of MTex objects

			if (textures[0]):
				tex = textures[0].tex
				mapfrom = textures[0].texco
				mapto = textures[0].mapto

				if(mesh.hasFaceUV() == False):
					print " Warning: mesh does not have face UV coordinates!"

				image = tex.getImage()
				if image != None:
					codtex = Texture("meshtexture")

					# XXX TODO deal with packed images
					filename = image.getFilename()
					if int(Blender.Get("version")) >= 233:
						if (filename[0:2] == "//"):
							blendfile = Blender.Get("filename")
							if sys.platform == 'win32':
								if blendfile.rfind("\\") != -1:
									filename = blendfile[0 : blendfile.rfind("\\")] + filename[1:]
								else:
									filename = filename[2:]
							else:
								if blendfile.rfind("/") != -1:
									filename = blendfile[0 : blendfile.rfind("/")] + filename[1:]
								else:
									filename = filename[2:]
					try:
						f = file(filename, "rb")
						codtex.setImage(f.read())
						material.setTexture(codtex)
						f.close()
						hastexture = True
						print "  (added texture from image file \"%s\")" % (filename)
					except IOError, (errno, strerror):
						print "ERROR opening or reading file \"%s\": (%s) %s " % (filename, errno, strerror)
				else:
					print " (material 0 has no textures.)"
			else:
				print " (mesh has no material, using white)"
				material.setColor(1, 1, 1)

	print " (setting a3dl material...)"
	trimesh.addMaterial(material)
	
	print " (getting blender geometry...)"
	(vertices, texels, normals, polys) = getGeometry(mesh)
	print " (setting a3dl geometry...)"
	trimesh.setVertices(vertices)
	trimesh.setNormals(normals)
	if (hastexture):
		trimesh.setTexels(texels)
	trimesh.setPolygons(polys)

	cod.addVobjectTree(trimesh)
	return trimesh


def exportLight(obj, cod):
	global NoLights
	lamp = obj.data
	light = Vobject(obj.name)
	light.addType("a3dl:light")	
	pos = Property("lightposition")
	loc = obj.getLocation()
	pos.setPropValue("%f %f %f" % (loc[0], loc[2], loc[1]), "list: float")
	light.addChild("a3dl:position", pos)
	radius = Property("lightradius")
	radius.setPropValue("%f" % (lamp.dist), "float");
	light.addChild("a3dl:radius", radius)
	static = Property("staticlight")
	static.setPropValue("true", "boolean")
	light.addChild("a3dl:static", static)
	color = Property("lightcolor")
	color.setPropValue("%f %f %f" % (lamp.R, lamp.G, lamp.B), "list: float");
	light.addChild("a3dl:color", color)
	cod.addVobjectTree(light)
	NoLights = False
	return light


def matrixMult(a, b) :
	return [ [ a[0][0]*b[0][0]+a[0][1]*b[1][0]+a[0][2]*b[2][0], a[1][0]*b[0][0]+a[1][1]*b[1][0]+a[1][2]*b[2][0], a[2][0]*b[0][0]+a[2][1]*b[1][0]+a[2][2]*b[2][0] ], [ a[0][0]*b[0][1]+a[0][1]*b[1][1]+a[0][2]*b[2][1], a[1][0]*b[0][1]+a[1][1]*b[1][1]+a[1][2]*b[2][1], a[2][0]*b[0][1]+a[2][1]*b[1][1]+a[2][2]*b[2][1] ], [ a[0][0]*b[0][2]+a[0][1]*b[1][2]+a[0][2]*b[2][2], a[1][0]*b[0][2]+a[1][1]*b[1][2]+a[1][2]*b[2][2], a[2][0]*b[0][2]+a[2][1]*b[1][2]+a[2][2]*b[2][2] ] ]



def exportCamera(obj, cod):
	viewpoint = Vobject(obj.getName())
	viewpoint.addType("a3dl:viewpoint")

	cam = obj.getData()

	pos = Property(obj.getName()+"_pos")
	loc = obj.getLocation()
	pos.setPropValue("%f %f %f" % (loc[0], loc[2], loc[1]), "list: float")
	viewpoint.addChild("a3dl:position", pos)

	ori = Property(obj.getName()+"_ori")

	m = obj.getMatrix()

	########## XXXXXXXXXXX BROKEN -- FIX XXXXXXXXXXXXXXXXX ###################3


	# Blender camera rotation is up vector and VOS is pointing vector?
	# So rotate 90 deg on local X axis.
	a = (math.pi / 2.0)
	c = math.cos(a)
	s = math.sin(a)
 	rx = [ [ 1.0, 0.0, 0.0 ], [0.0, c, s], [0.0, -s, c] ]   
	ry = [ [ c, 0.0, -s ], [ 0.0, 1.0, 0.0 ], [ s, 0.0, c ] ]
	rz = [ [ c, s, 0.0 ], [-s, c, 0.0], [0.0, 0.0, 1.0] ]
	mm = matrixMult( m, rx) 
	
	aa = matrixToAxisAngle(mm)
	ori.setPropValue("%f %f %f %f" % (aa[0], aa[2], aa[1], aa[3]), "list: float")
	viewpoint.addChild("a3dl:orientation", ori)

	fov = Property(obj.getName()+"_fov")
	fov.setPropValue("%f" % cam.getLens(), "float")
	viewpoint.addChild("a3dl:fov", fov);

	cod.addVobjectTree(viewpoint)
	return viewpoint




def exportObject(obj, cod):
	print "exporting object type="+obj.getType()
	if obj.getType() == "Mesh":
		if obj.data: return exportMesh(obj, cod)
	elif obj.getType() == "Lamp":
		if obj.data: return exportLight(obj, cod)
	elif obj.getType() == "Camera":
		if obj.data: return exportCamera(obj, cod)




### Main begins here

def doExport(filename):
	print ""
	print("-- Begin blender COD export --")
	cod = COD()

	# Find a "World" object to use for a sector
	try:
		world = Blender.World.Get(WorldName)
		if(WorldName == "World"):
			# Use "world" for the object name (this makes the Blender and VOS 
			# default names line up)
			sector = Sector("world")
		else:
			sector = Sector(WorldName)
		sector.exportSectorProperties(world, cod)

	except NameError:
		print "No World object named \"" + WorldName + "\" was found: will not write a sector object."
		sector = 0

	# Now get all objects in the current scene and put them
	# in the COD. If we have a "World"/sector, also link
	# the to that.
	scene = Blender.Scene.getCurrent()
	print "got scene."
	n = len(scene.getChildren())
	i = 0
	for obj in scene.getChildren():
		print "adding \"" + obj.name + "\" to COD..."
		i = i + 1
		Blender.Window.DrawProgressBar(n/i, "Adding "+obj.name+" to COD...")
		vobj = exportObject(obj, cod) 
		if(sector != 0): 
			print " (adding to sector)"
			sector.addChild(obj.name, vobj)

	if(sector != 0): 
		print "(adding sector to cod...)"
		cod.addVobject(sector)

	# Hints about common problems:
	if (NoLights == True):
		print "WARNING: Scene has no lamps and no ambient light; how will you see it?"

	cod.writeCOD(filename)
	print("-- Wrote COD: "+filename)
	print ""

if __script__['arg'] == 'saveToFile':
	if(OutputFilename == ""):
		if int(Blender.Get("version")) >= 233:
			# If the Blender filename is "masterpiece.blend", the COD file should be
			# named "masterpiece.cod":
			filename = Blender.Get("filename");
			filename = filename[0 : filename.rfind(".blend")] + ".cod";		
			Blender.Window.FileSelector(doExport, "Write COD File", filename)		
		else:
			Blender.Window.FileSelector(doExport, "Write COD File")
	else:
		doExport(OutputFilename)
elif __script__['arg'] == 'preview':
	(handle, path) = tempfile.mkstemp('.cod')
	doExport(path)
	if (sys.platform == "darwin"):
		os.spawnlp(os.P_NOWAIT, 'open', 'open', path)
	elif (sys.platform == "win32"):
		import _winreg
		vosui = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\interreality3d')
		exe = _winreg.QueryValue(vosui, None)
		_winreg.CloseKey(vosui)
		os.spawnl(os.P_NOWAIT, exe, exe, path)
	else:
		os.spawnlp(os.P_NOWAIT, 'interreality3d', 'interreality3d', path)

sys.stdout.flush()
